From f17431b6ff5ff6962f7c08dc8bc14a1af0ca55c8 Mon Sep 17 00:00:00 2001 From: Bryan Ingle Date: Mon, 23 May 2022 21:57:52 -0600 Subject: [PATCH] Adjust inference logic for two-digit expirations of futures (and options) --- docs/content/releases/5.19.1.md | 3 + lib/utilities/parsers/SymbolParser.js | 8 +- .../utilities/parsers/SymbolParserSpec.js | 89 ++++++++++++++++++- 3 files changed, 91 insertions(+), 9 deletions(-) create mode 100644 docs/content/releases/5.19.1.md diff --git a/docs/content/releases/5.19.1.md b/docs/content/releases/5.19.1.md new file mode 100644 index 0000000..80a2ffc --- /dev/null +++ b/docs/content/releases/5.19.1.md @@ -0,0 +1,3 @@ +**Breaking Changes** + +* Adjusted the logic used by the `SymbolParser.parseInstrumentType` function to infer the expiration year of futures contracts and futures options based solely on a symbol (e.g. ZCZ19). In previous versions, given a two-digit number (e.g. 19), a year in the current century or the next century would be returned (e.g. 2019 or 2119). In this version, a 100-year window is used, interpreting a two-digit number as a year up to 25 years in the future, or alternatively up to 75 years in the past. Assuming the current year is 2022, the 100-year window will range from 1948 to 2047. \ No newline at end of file diff --git a/lib/utilities/parsers/SymbolParser.js b/lib/utilities/parsers/SymbolParser.js index 60adf6d..b35b9f5 100644 --- a/lib/utilities/parsers/SymbolParser.js +++ b/lib/utilities/parsers/SymbolParser.js @@ -743,12 +743,8 @@ module.exports = (() => { } else if (year < 100) { year = Math.floor(currentYear / 100) * 100 + year; - if (year < currentYear) { - const alternateYear = year + 100; - - if (currentYear - year > alternateYear - currentYear) { - year = alternateYear; - } + if (currentYear + 25 < year) { + year = year - 100; } } diff --git a/test/specs/utilities/parsers/SymbolParserSpec.js b/test/specs/utilities/parsers/SymbolParserSpec.js index ddfc091..3d7faae 100644 --- a/test/specs/utilities/parsers/SymbolParserSpec.js +++ b/test/specs/utilities/parsers/SymbolParserSpec.js @@ -854,6 +854,90 @@ describe('When parsing a symbol for instrument type', () => { }); }); +describe('When parsing a symbol for a futures contract', () => { + describe('and the year is 2022', () => { + let getFullYear; + + beforeEach(() => { + getFullYear = Date.prototype.getFullYear; + + Date.prototype.getFullYear = () => { return 2022; }; + }); + + it('the expiration year of "ZCN19" should parse to 2019', () => { + expect(SymbolParser.parseInstrumentType('ZCN19').year).toEqual(2019); + }); + + it('the expiration year of "ZCN21" should parse to 2021', () => { + expect(SymbolParser.parseInstrumentType('ZCN21').year).toEqual(2021); + }); + + it('the expiration year of "ZCN22" should parse to 2022', () => { + expect(SymbolParser.parseInstrumentType('ZCN22').year).toEqual(2022); + }); + + it('the expiration year of "ZCN32" should parse to 2032', () => { + expect(SymbolParser.parseInstrumentType('ZCN32').year).toEqual(2032); + }); + + it('the expiration year of "ZCN42" should parse to 2042', () => { + expect(SymbolParser.parseInstrumentType('ZCN42').year).toEqual(2042); + }); + + it('the expiration year of "ZCN47" should parse to 2047', () => { + expect(SymbolParser.parseInstrumentType('ZCN47').year).toEqual(2047); + }); + + it('the expiration year of "ZCN48" should parse to 1948', () => { + expect(SymbolParser.parseInstrumentType('ZCN48').year).toEqual(1948); + }); + + it('the expiration year of "ZCN49" should parse to 1949', () => { + expect(SymbolParser.parseInstrumentType('ZCN49').year).toEqual(1949); + }); + + it('the expiration year of "ZCN99" should parse to 1999', () => { + expect(SymbolParser.parseInstrumentType('ZCM99').year).toEqual(1999); + }); + + afterEach(() => { + Date.prototype.getFullYear = getFullYear; + }); + }); +}); + +describe('When parsing a symbol for a futures option', () => { + describe('and the year is 2022', () => { + let getFullYear; + + beforeEach(() => { + getFullYear = Date.prototype.getFullYear; + + Date.prototype.getFullYear = () => { return 2022; }; + }); + + it('the expiration year of "ZWK18465C" should parse to 2018', () => { + expect(SymbolParser.parseInstrumentType('ZWK18465C').year).toEqual(2018); + }); + + it('the expiration year of "ZWK22465C" should parse to 2022', () => { + expect(SymbolParser.parseInstrumentType('ZWK22465C').year).toEqual(2022); + }); + + it('the expiration year of "ZWK47465C" should parse to 2047', () => { + expect(SymbolParser.parseInstrumentType('ZWK47465C').year).toEqual(2047); + }); + + it('the expiration year of "ZWK48465C" should parse to 2048', () => { + expect(SymbolParser.parseInstrumentType('ZWK48465C').year).toEqual(1948); + }); + + afterEach(() => { + Date.prototype.getFullYear = getFullYear; + }); + }); +}); + describe('When checking to see if a symbol is a future', () => { it('the symbol "ES*1" should return true', () => { expect(SymbolParser.getIsFuture('ES*1')).toEqual(true); @@ -1332,7 +1416,6 @@ describe('When checking to see if a symbol is forex', () => { }); }); - describe('When checking to see if a symbol is crypto', () => { it('the symbol "ES*1" should return false', () => { expect(SymbolParser.getIsCrypto('ES*1')).toEqual(false); @@ -1479,7 +1562,7 @@ describe('When checking to see if a symbol is crypto', () => { }); }); -describe('When checking to see if a symbol is a future spread', () => { +describe('When checking to see if a symbol is a futures spread', () => { it('the symbol "ES*1" should return false', () => { expect(SymbolParser.getIsFutureSpread('ES*1')).toEqual(false); }); @@ -1625,7 +1708,7 @@ describe('When checking to see if a symbol is a future spread', () => { }); }); -describe('When checking to see if a symbol is a future option', () => { +describe('When checking to see if a symbol is a futures option', () => { it('the symbol "ES*1" should return false', () => { expect(SymbolParser.getIsFutureOption('ES*1')).toEqual(false); });