Skip to content

Commit

Permalink
Add ability to override Profile.format with custom delegate
Browse files Browse the repository at this point in the history
  • Loading branch information
bryaningl3 committed Jul 14, 2021
1 parent e814b2f commit d1b4b3c
Show file tree
Hide file tree
Showing 7 changed files with 244 additions and 28 deletions.
5 changes: 5 additions & 0 deletions docs/content/releases/5.12.0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
**New Features**

* Added the `AssetClass` enumeration to describe instrument types (e.g. stocks, futures, options, etc).
* Added the `Profile.asset` property which references an `AssetClass` item, assuming the asset class can be inferred from the instrument's symbol.
* Added the `Profile.setPriceFormatterCustom` function which allows the consumer to specify their own price format function, completely overriding the default price formatting logic.
18 changes: 17 additions & 1 deletion example/browser/js/startup.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,23 @@ const formatDecimal = require('./../../../lib/utilities/format/decimal'),
formatPrice = require('./../../../lib/utilities/format/price'),
formatQuote = require('./../../../lib/utilities/format/quote');

const AssetClass = require('./../../../lib/utilities/data/AssetClass');

const Profile = require('./../../../lib/marketState/Profile');

module.exports = (() => {
'use strict';

function customPriceFormatter(value, unitCode, profile) {
if (profile.asset === AssetClass.FUTURE_OPTION && (profile.root === 'ZB' || profile.root === 'ZN')) {
return formatPrice(value, unitCode, '-', false, ',');
} else {
return formatPrice(value, unitCode, '-', true, ',');
}
}

Profile.setPriceFormatterCustom(customPriceFormatter);

var PageModel = function() {
var that = this;
var connection = null;
Expand Down Expand Up @@ -550,7 +564,9 @@ module.exports = (() => {
};

that.formatPrice = function(price) {
return formatPrice(price, that.quote().profile.unitCode, '-', true);
return that.quote().profile.formatPrice(price);

//return formatPrice(price, that.quote().profile.unitCode, '-', true);
};

that.formatInteger = function(value) {
Expand Down
18 changes: 18 additions & 0 deletions lib/marketState/.meta.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,22 @@
* @memberOf Schema
* @property {number} price - The price level.
* @property {number} size - The aggregate quantity traded.
*/

/**
* A meta namespace containing signatures of anonymous functions.
*
* @namespace Callbacks
*/

/**
* The signature of a "custom" function used to format price values.
*
* @public
* @callback CustomPriceFormatterCallback
* @memberOf Callbacks
* @param {Number} value - The price value to format.
* @param {String} value - The price value to format.
* @param {Profile} profile - The instrument being priced.
* @returns {String}
*/
44 changes: 35 additions & 9 deletions lib/marketState/Profile.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
const SymbolParser = require('./../utilities/parsers/SymbolParser'),
buildPriceFormatter = require('../utilities/format/factories/price');

const AssetClass = require('./../utilities/data/AssetClass');

module.exports = (() => {
'use strict';

Expand Down Expand Up @@ -67,15 +69,18 @@ module.exports = (() => {

const info = SymbolParser.parseInstrumentType(this.symbol);

if (info) {
if (info.type === 'future') {
/**
* @property {string|undefined} root - Root symbol (futures only).
* @public
* @readonly
*/
this.root = info.root;
const future = info !== null && info.asset === AssetClass.FUTURE;
const option = info !== null && info.asset === AssetClass.FUTURE_OPTION;

if (future || option) {
/**
* @property {string|undefined} root - Root symbol (futures and futures options only).
* @public
* @readonly
*/
this.root = info.root;

if (future) {
/**
* @property {string|undefined} month - Month code (futures only).
*/
Expand All @@ -98,6 +103,15 @@ module.exports = (() => {
}
}

/**
* @property {AssetClass|null} type - The instrument type (a.k.a. asset class). This will only be present when inference based on the instrument symbol is possible.
*/
this.asset = null;

if (info && info.asset) {
this.asset = info.asset;
}

if (typeof(additional) === 'object' && additional !== null) {
for (let p in additional) {
this[p] = additional[p];
Expand All @@ -115,7 +129,7 @@ module.exports = (() => {
* @returns {string}
*/
formatPrice(price) {
return formatter(price, this.unitCode);
return formatter(price, this.unitCode, this);
}

/**
Expand All @@ -131,6 +145,18 @@ module.exports = (() => {
formatter = buildPriceFormatter(fractionSeparator, specialFractions, thousandsSeparator);
}

/**
* An alternative to {@link Profile.setPriceFormatter} which allows the consumer to specify
* a function to
*
* @public
* @static
* @param {Callbacks.CustomPriceFormatterCallback} fn - The function to use for price formatting (which replaces the default logic).
*/
static setPriceFormatterCustom(fn) {
formatter = fn;
}

/**
* Alias for {@link Profile.setPriceFormatter} function.
*
Expand Down
130 changes: 130 additions & 0 deletions lib/utilities/data/AssetClass.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
const Enum = require('@barchart/common-js/lang/Enum');

module.exports = (() => {
'use strict';

/**
* An enumeration for instrument types (e.g. stock, future, etc).
*
* @public
* @exported
* @extends {Enum}
* @param {String} code
* @param {String} description
* @param {Number} id
*/
class AssetClass extends Enum {
constructor(code, description, id) {
super(code, description);

this._id = id;
}

/**
* A unique numeric identifier assigned by Barchart.
*
* @public
* @returns {Number}
*/
get id() {
return this._id;
}

toJSON() {
return this._id;
}

/**
* Converts the string-based identifier into an enumeration item.
*
* @public
* @static
* @param {String} code
* @returns {AssetClass|null}
*/
static parse(code) {
return Enum.fromCode(UnitCode, code);
}

/**
* Converts the numeric identifier into an enumeration item.
*
* @public
* @static
* @param {Number} id
* @returns {AssetClass|null}
*/
static fromId(id) {
return Enum.getItems(AssetClass).find(x => x.id === id) || null;
}

/**
* A stock.
*
* @public
* @static
* @returns {AssetClass}
*/
static get STOCK() {
return STOCK;
}

/**
* A stock option.
*
* @public
* @static
* @returns {AssetClass}
*/
static get STOCK_OPTION() {
return STOCK_OPTION;
}

/**
* A future.
*
* @public
* @static
* @returns {AssetClass}
*/
static get FUTURE() {
return FUTURE;
}

/**
* A future option.
*
* @public
* @static
* @returns {AssetClass}
*/
static get FUTURE_OPTION() {
return FUTURE_OPTION;
}

/**
* A foreign exchange instrument.
*
* @public
* @static
* @returns {AssetClass}
*/
static get FOREX() {
return FOREX;
}

toString() {
return `[AssetClass (id=${this.id}, code=${this.code})]`;
}
}

const STOCK = new AssetClass('STK', 'U.S. Equity', 1);
const STOCK_OPTION = new AssetClass('STKOPT', 'Equity Option', 34);

const FUTURE = new AssetClass('FUT', 'Future', 2);
const FUTURE_OPTION = new AssetClass('FUTOPT', 'Future Option', 12);

const FOREX = new AssetClass('FOREX', 'FOREX', 10);

return AssetClass;
})();
43 changes: 25 additions & 18 deletions lib/utilities/data/UnitCode.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,26 @@ const Enum = require('@barchart/common-js/lang/Enum');
module.exports = (() => {
'use strict';

// 2021/07/14, For a more detailed on the "special" fractional formatting (i.e. CME
// tick notation), please refer to the detailed unit test suite written for CME
// notation (see the cmeSpec.js file).

/**
* Describes how an instrument's price is formatted. In most cases, unit codes are stored as a
* single character; however, this enumeration adds additional information. There are fourteen
* An enumeration that describes different styles for pricing instruments (and
* formatting instrument prices for display to humans). Barchart uses fourteen
* distinct unit codes.
*
* @public
* @exported
* @extends {Enum}
* @param {String} code
* @param {Number} baseCode
* @param {Number} decimalDigits
* @param {Boolean} supportsFractions
* @param {Number=} fractionFactor
* @param {Number=} fractionDigits
* @param {Number=} fractionFactorSpecial
* @param {Number=} fractionDigitsSpecial
* @param {Number} decimalDigits - When formatting a price as a decimal value, the number of decimal places to display.
* @param {Boolean} supportsFractions - As an alternative to decimal-formatted prices, some instruments support fractional representations.
* @param {Number=} fractionFactor - The count of discrete prices which a unit can be divided into (e.g. a US dollar can be divided into 100 cents). By default, this is also the implied denominator in fractional notation (e.g. 3.6875 equals 3 and 22/32 — which is represented in fractional notation as 3-22, where the denominator of 32 is implied).
* @param {Number=} fractionDigits - The number of digits of the fraction's numerator to display (e.g. two digits of the fraction 22/32 are shown in the fractional notation 3-22).
* @param {Number=} fractionFactorSpecial - Special fraction factors refer to the CME tick notation scheme (read more (here)[https://www.cmegroup.com/confluence/display/EPICSANDBOX/Fractional+Pricing+-+Tick+and+Decimal+Conversions]). For example, the CME notation for 0.51171875 (in 1/8ths of 1/32nds) is 0-163, where the numerator of 163 means 16 thirty-seconds and 3 eighths of a thirty-second, where the actual fraction is 16.3[75] / 32, which equals 0.51171875.
* @param {Number=} fractionDigitsSpecial - The number of digits of the fraction's numerator to display, when formatting in CME tick notation. For example, the 0-163 (in 1/8ths of 1/32nds) equates to the fraction of 16.375/32. This notation is limited to three digits (163) and omits the trailing two digits (75).
*/
class UnitCode extends Enum {
constructor(code, baseCode, decimalDigits, supportsFractions, fractionFactor, fractionDigits, fractionFactorSpecial, fractionDigitsSpecial) {
Expand Down Expand Up @@ -88,14 +92,10 @@ module.exports = (() => {
}

/**
* When formatting with fractional notation (instead of decimal notation), multiply the
* decimal part of the value by this factor to get the fractional (i.e. numerator) value.
* In other words, this factor is the denominator.
*
* For example, the value 9.5 will be formatted as "9-4" with a fractional factor of eight.
* This is because 8 * 0.5 = 4. In other words, the price is quoted in eighths and 0.5 is
* four eighths. Using the same logic, the value of 9.75 will be formatted as "9-6" with
* a fractional factor of eight.
* The count of discrete prices which a unit can be divided into (e.g. a US dollar can be divided
* into 100 cents). By default, this is also the implied denominator in fractional notation (e.g. 3.6875
* equals 3 and 22/32 — which is represented in fractional notation as 3-22, where the denominator of 32
* is implied).
*
* @public
* @returns {Number|undefined}
Expand All @@ -117,7 +117,11 @@ module.exports = (() => {
}

/**
* Same as {@link UnitCode#fractionFactor} for "special" fractions.
* Special fraction factors refer to the CME tick notation scheme (read more (here)[https://www.cmegroup.com/confluence/display/EPICSANDBOX/Fractional+Pricing+-+Tick+and+Decimal+Conversions]).
*
* For example, the CME notation for 0.51171875 (in 1/8ths of 1/32nds) is 0-163, where the
* numerator of 163 means 16 thirty-seconds and 3 eighths of a thirty-second, where the
* actual fraction is 16.3[75] / 32, which equals 0.51171875.
*
* @public
* @returns {Number|undefined}
Expand All @@ -137,7 +141,10 @@ module.exports = (() => {
}

/**
* Returns the {@link UnitCode#fractionFactor} or {@link UnitCode#fractionFactorSpecial} value.
* The number of digits of the fraction's numerator to display, when formatting
* in CME tick notation. For example, the 0-163 (in 1/8ths of 1/32nds) equates
* to the fraction of 16.375/32. This notation is limited to three digits (163)
* and omits the trailing two digits (75).
*
* @public
* @param {Boolean=} special
Expand Down
Loading

0 comments on commit d1b4b3c

Please sign in to comment.