diff --git a/docs/content/releases/5.12.0.md b/docs/content/releases/5.12.0.md new file mode 100644 index 0000000..a0ae028 --- /dev/null +++ b/docs/content/releases/5.12.0.md @@ -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. \ No newline at end of file diff --git a/example/browser/js/startup.js b/example/browser/js/startup.js index d8b6670..032c47b 100644 --- a/example/browser/js/startup.js +++ b/example/browser/js/startup.js @@ -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; @@ -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) { diff --git a/lib/marketState/.meta.js b/lib/marketState/.meta.js index 09baf2f..f70e8b0 100644 --- a/lib/marketState/.meta.js +++ b/lib/marketState/.meta.js @@ -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} */ \ No newline at end of file diff --git a/lib/marketState/Profile.js b/lib/marketState/Profile.js index f60dbf2..d9b8525 100644 --- a/lib/marketState/Profile.js +++ b/lib/marketState/Profile.js @@ -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'; @@ -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). */ @@ -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]; @@ -115,7 +129,7 @@ module.exports = (() => { * @returns {string} */ formatPrice(price) { - return formatter(price, this.unitCode); + return formatter(price, this.unitCode, this); } /** @@ -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. * diff --git a/lib/utilities/data/AssetClass.js b/lib/utilities/data/AssetClass.js new file mode 100644 index 0000000..a526df2 --- /dev/null +++ b/lib/utilities/data/AssetClass.js @@ -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; +})(); diff --git a/lib/utilities/data/UnitCode.js b/lib/utilities/data/UnitCode.js index b02f694..12752fb 100644 --- a/lib/utilities/data/UnitCode.js +++ b/lib/utilities/data/UnitCode.js @@ -3,9 +3,13 @@ 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 @@ -13,12 +17,12 @@ module.exports = (() => { * @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) { @@ -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} @@ -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} @@ -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 diff --git a/lib/utilities/parsers/SymbolParser.js b/lib/utilities/parsers/SymbolParser.js index 39da37f..1d8dd50 100644 --- a/lib/utilities/parsers/SymbolParser.js +++ b/lib/utilities/parsers/SymbolParser.js @@ -1,6 +1,8 @@ const is = require('@barchart/common-js/lang/is'), string = require('@barchart/common-js/lang/string'); +const AssetClass = require('./../data/AssetClass'); + module.exports = (() => { 'use strict'; @@ -460,7 +462,9 @@ module.exports = (() => { definition = {}; definition.symbol = symbol; + definition.type = 'future'; + definition.asset = AssetClass.FUTURE; definition.dynamic = false; definition.root = match[1]; @@ -480,7 +484,9 @@ module.exports = (() => { definition = {}; definition.symbol = symbol; + definition.type = 'future'; + definition.asset = AssetClass.FUTURE; definition.dynamic = true; definition.root = match[1]; @@ -497,7 +503,9 @@ module.exports = (() => { definition = {}; definition.symbol = symbol; + definition.type = 'forex'; + definition.asset = AssetClass.FOREX; } return definition; @@ -514,7 +522,9 @@ module.exports = (() => { definition = {}; definition.symbol = symbol; + definition.type = 'equity_option'; + definition.asset = AssetClass.STOCK_OPTION; definition.option_type = match[9] === 'C' ? 'call' : 'put'; definition.strike = parseFloat(match[8]); @@ -580,7 +590,9 @@ module.exports = (() => { } definition.symbol = symbol; + definition.type = 'future_option'; + definition.asset = AssetClass.FUTURE_OPTION; definition.option_type = optionType; definition.strike = parseInt(match[3]); @@ -602,7 +614,9 @@ module.exports = (() => { definition = {}; definition.symbol = symbol; + definition.type = 'future_option'; + definition.asset = AssetClass.FUTURE_OPTION; definition.option_type = match[5] === 'C' ? 'call' : 'put'; definition.strike = parseInt(match[4]);