jspurefix
Version:
pure node js fix engine
500 lines • 18.1 kB
JavaScript
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
require("reflect-metadata");
const buffer_1 = require("../../buffer");
const ascii_1 = require("../../buffer/ascii");
const quickfix_1 = require("../../types/FIX4.4/quickfix");
const index_1 = require("../../index");
const encoder_test_1 = require("../env/encoder-test");
const encoderTester = new encoder_test_1.EncoderTest();
beforeAll(() => __awaiter(void 0, void 0, void 0, function* () {
yield encoderTester.init('session/qf-fix44.json');
}), 45000);
test('expect a definition ', () => {
expect(encoderTester.nos).toBeTruthy();
});
test('encode heartbeat', () => __awaiter(void 0, void 0, void 0, function* () {
var _a;
const session = encoderTester.session;
const factory = session.config.factory;
expect(factory).toBeTruthy();
if (!factory)
return;
const hb = factory.heartbeat('test01');
const hbd = encoderTester.definitions.message.get('Heartbeat');
expect(hbd).toBeTruthy();
if (!hbd)
return;
const fix = encoderTester.toFixMessage(hb, hbd);
expect(fix).toBeTruthy();
const res = yield encoderTester.setup.client.parseText(fix);
expect(res.event).toEqual('msg');
expect(res.msgType).toEqual('0');
const len = (_a = res.view) === null || _a === void 0 ? void 0 : _a.getTyped(buffer_1.Tags.BodyLengthTag);
const expected = fix.length - '8=FIX.4.4|9=0000081|'.length - '10=159|'.length;
expect(len).toEqual(expected);
}));
test('encode custom header PossDupFlag', () => {
const no = {
StandardHeader: {
PossDupFlag: true
}
};
const hb = encoderTester.definitions.message.get('Heartbeat');
const fix = hb ? encoderTester.toFixMessage(no, hb) : '';
expect(fix).toMatch('43=Y|');
});
test('encode custom header PossDupFlag', () => {
const no = {
StandardHeader: {
MsgSeqNum: 9999
}
};
const hb = encoderTester.definitions.message.get('Heartbeat');
const fix = hb ? encoderTester.toFixMessage(no, hb) : '';
expect(fix).toMatch('34=9999|');
});
test('encode string ClOrdID ', () => {
const no = {};
no.ClOrdID = 'Order-a';
const fix = encoderTester.toFix(no);
expect(fix).toEqual('11=Order-a|');
});
test('should not encode empty string', () => {
const no = {};
no.ClOrdID = '';
const fix = encoderTester.toFix(no);
expect(fix).toEqual('');
});
test('should not encode null string', () => {
const no = {};
no.ClOrdID = null;
const fix = encoderTester.toFix(no);
expect(fix).toEqual('');
});
test('encode +ve numeric (int) Price ', () => {
const no = {};
no.Price = 100;
const fix = encoderTester.toFix(no);
expect(fix).toEqual('44=100|');
});
test('encode -ve numeric (int) Price ', () => {
const no = {};
no.Price = -100;
const fix = encoderTester.toFix(no);
expect(fix).toEqual('44=-100|');
});
test('encode +ve numeric (double 8dp) Price ', () => {
const no = {};
no.Price = 123.12345678;
const fix = encoderTester.toFix(no);
expect(fix).toEqual('44=123.12345678|');
});
test('encode +ve numeric (double 14dp) Price ', () => {
const no = {};
no.Price = 123.12345678901234;
const fix = encoderTester.toFix(no);
expect(fix).toEqual('44=123.12345678901234|');
});
test('encode -ve numeric (double 14dp) Price ', () => {
const no = {};
no.Price = -123.12345678901234;
const fix = encoderTester.toFix(no);
expect(fix).toEqual('44=-123.12345678901234|');
});
test('encode +ve string Price ', () => {
const no = {};
no.Price = '123.12345678901234';
const fix = encoderTester.toFix(no);
expect(fix).toEqual('44=123.12345678901234|');
});
test('encode LocalDate TradeDate ', () => {
const no = {};
no.TradeDate = encoderTester.localDate;
const fix = encoderTester.toFix(no);
expect(fix).toEqual('75=20180725|');
});
test('encode UTCTIMESTAMP ExpireTime ', () => {
const no = {};
no.ExpireTime = encoderTester.utcTimeStamp;
const fix = encoderTester.toFix(no);
expect(fix).toEqual('126=20180610-16:35:00.246|');
});
test('encode UTCTIMESTAMP ExpireTime - check padding', () => {
const no = {};
no.ExpireTime = new Date(Date.UTC(2018, 0, 1, 0, 0, 0, 1));
const fix = encoderTester.toFix(no);
expect(fix).toEqual('126=20180101-00:00:00.001|');
});
test('encode UTCDATEONLY MDEntryDate', () => {
const mdGrp = encoderTester.definitions.component.get('MDFullGrp');
expect(mdGrp).toBeTruthy();
if (!mdGrp)
return;
const grp = {
NoMDEntries: [
{
MDEntryType: '0',
MDEntryDate: encoderTester.utcDate
}
]
};
const fix = encoderTester.toFix(grp, mdGrp);
expect(fix).toEqual('268=1|269=0|272=20180610|');
});
test('encode UTCTIMEONLY MDEntryTime', () => {
const mdGrp = encoderTester.definitions.component.get('MDFullGrp');
expect(mdGrp).toBeTruthy();
const grp = {
NoMDEntries: [
{
MDEntryType: '0',
MDEntryTime: encoderTester.utcTime
}
]
};
if (!mdGrp)
return;
const fix = encoderTester.toFix(grp, mdGrp);
expect(fix).toEqual('268=1|269=0|273=16:35:00.246|');
});
function getTCR1() {
const d0 = new Date(Date.UTC(2018, 11, 1, 0, 0, 0));
const d1 = new Date(Date.UTC(2018, 11, 2, 0, 0, 0));
return {
TradeRequestID: 'all-trades',
TradeRequestType: quickfix_1.TradeRequestType.AllTrades,
SubscriptionRequestType: quickfix_1.SubscriptionRequestType.SnapshotPlusUpdates,
TrdCapDtGrp: {
NoDates: [
{
TransactTime: d0
},
{
TransactTime: d1
}
]
}
};
}
test('encode TradeCaptureReportRequest with TransactTime', () => {
const tcr = getTCR1();
const d = encoderTester.definitions.message.get('TradeCaptureReportRequest');
if (!d)
return;
const fix = encoderTester.toFix(tcr, d);
expect(fix).toEqual('568=all-trades|569=0|263=1|580=2|60=20181201-00:00:00.000|60=20181202-00:00:00.000|');
});
test('encode BOOLEAN (true) ReportToExch', () => {
expect(encoderTester.er).toBeTruthy();
const e = {};
e.ReportToExch = true;
const fix = encoderTester.toFix(e, encoderTester.er);
expect(fix).toEqual('113=Y|');
});
test('encode BOOLEAN (truthy) ReportToExch', () => {
expect(encoderTester.er).toBeTruthy();
const e = {};
e.ReportToExch = 1;
const fix = encoderTester.toFix(e, encoderTester.er);
expect(fix).toEqual('113=Y|');
});
test('encode BOOLEAN (string) ReportToExch', () => {
expect(encoderTester.er).toBeTruthy();
const e = {};
e.ReportToExch = 'TRUE';
const fix = encoderTester.toFix(e, encoderTester.er);
expect(fix).toEqual('113=Y|');
});
test('encode BOOLEAN (false) ReportToExch', () => {
expect(encoderTester.er).toBeTruthy();
const e = {};
e.ReportToExch = false;
const fix = encoderTester.toFix(e, encoderTester.er);
expect(fix).toEqual('113=N|');
});
test('encode BOOLEAN (falsy) ReportToExch', () => {
expect(encoderTester.er).toBeTruthy();
const e = {};
e.ReportToExch = 0;
const fix = encoderTester.toFix(e, encoderTester.er);
expect(fix).toEqual('113=N|');
});
test('encode RawData EncodedText', () => {
expect(encoderTester.er).toBeTruthy();
const toEncode = 'this is fix';
const e = {
EncodedText: Buffer.alloc(toEncode.length, toEncode, 'utf8')
};
const fix = encoderTester.toFix(e, encoderTester.er);
expect(fix).toEqual('354=11|355=this is fix|');
});
test('encode empty RawData EncodedText', () => {
expect(encoderTester.er).toBeTruthy();
const toEncode = '';
const e = {
EncodedText: Buffer.alloc(toEncode.length, toEncode, 'utf8')
};
const fix = encoderTester.toFix(e, encoderTester.er);
expect(fix).toEqual('354=0|355=|');
});
function getParties() {
return {
Parties: {
NoPartyIDs: [
{
PartyID: 'magna.',
PartyIDSource: '9',
PartyRole: 28
},
{
PartyID: 'iaculis',
PartyIDSource: 'F',
PartyRole: 2
}
]
}
};
}
function getPartiesNoPartyID() {
return {
Parties: {
NoPartyIDs: [
{
PartyIDSource: '9',
PartyRole: 28
}
]
}
};
}
test('encode repeated group of simple repository Parties', () => {
expect(encoderTester.er).toBeTruthy();
const e = getParties();
const fix = encoderTester.toFix(e, encoderTester.er);
expect(fix).toEqual('453=2|448=magna.|447=9|452=28|448=iaculis|447=F|452=2|');
});
test('use a carat as log delimiter', () => {
expect(encoderTester.er).toBeTruthy();
const caratEncoder = new ascii_1.AsciiEncoder(encoderTester.session.buffer, encoderTester.definitions, new ascii_1.TimeFormatter(encoderTester.session.buffer), ascii_1.AsciiChars.Soh, ascii_1.AsciiChars.Carat);
const e = getParties();
const fix = encoderTester.toFix(e, encoderTester.er, caratEncoder);
expect(fix).toEqual('453=2^448=magna.^447=9^452=28^448=iaculis^447=F^452=2^');
});
test('use a carat as log delimiter with Soh in buffer to show encoding still works', () => {
expect(encoderTester.er).toBeTruthy();
const caratEncoder = new ascii_1.AsciiEncoder(encoderTester.session.buffer, encoderTester.definitions, new ascii_1.TimeFormatter(encoderTester.session.buffer), ascii_1.AsciiChars.Soh, ascii_1.AsciiChars.Carat);
const e = getParties();
const fix = encoderTester.toFix(e, encoderTester.er, caratEncoder);
expect(fix).toEqual('453=2^448=magna.^447=9^452=28^448=iaculis^447=F^452=2^');
const trimmed = caratEncoder.trim().toString();
expect(trimmed).toEqual('453=2448=magna.447=9452=28448=iaculis447=F452=2');
});
test('encode repeated group with no PartyID - should encode', () => {
expect(encoderTester.er).toBeTruthy();
const e = getPartiesNoPartyID();
const fix = encoderTester.toFix(e, encoderTester.er);
expect(fix).toEqual('453=1|447=9|452=28|');
});
test('encode repeated group with no array - should throw', () => {
expect(encoderTester.er).toBeTruthy();
const e = {
Parties: {
NoPartyIDs: 'should be an array'
}
};
function run() {
encoderTester.toFix(e, encoderTester.er);
}
expect(run).toThrow(/expected array/);
});
test('encode repeated group with empty array', () => {
expect(encoderTester.er).toBeTruthy();
const e = {
Parties: {
NoPartyIDs: []
}
};
expect(encoderTester.toFix(e, encoderTester.er)).toEqual('453=0|');
});
function getInstrument() {
return {
Instrument: {
Symbol: 'ac,',
SymbolSfx: 'non',
SecurityID: 'Pellentesque',
SecurityIDSource: 'B',
Product: 2
}
};
}
function getInstrumentNestedGroup() {
return {
Instrument: {
Symbol: 'ac,',
SymbolSfx: 'non',
SecurityID: 'Pellentesque',
SecurityIDSource: 'B',
SecAltIDGrp: {
NoSecurityAltID: [
{
SecurityAltID: 'lorem',
SecurityAltIDSource: 'consequat'
},
{
SecurityAltID: 'sapien',
SecurityAltIDSource: 'tempor'
}
]
},
Product: 2
}
};
}
test('encode component', () => {
expect(encoderTester.er).toBeTruthy();
const e = getInstrument();
expect(encoderTester.toFix(e, encoderTester.er)).toEqual('55=ac,|65=non|48=Pellentesque|22=B|460=2|');
});
test('encode component nested group', () => {
expect(encoderTester.er).toBeTruthy();
const e = getInstrumentNestedGroup();
expect(encoderTester.toFix(e, encoderTester.er)).toEqual('55=ac,|65=non|48=Pellentesque|22=B|454=2|455=lorem|456=consequat|455=sapien|456=tempor|460=2|');
});
test('encode group missing delimiter', () => {
expect(encoderTester.er).toBeTruthy();
const e = getInstrumentNestedGroup();
delete e.Instrument.SecAltIDGrp.NoSecurityAltID[0].SecurityAltID;
function run() {
encoderTester.toFix(e, encoderTester.er);
}
expect(run).toThrow(/group instance \[1] inconsistent delimiter 455 expected tag 456/);
});
test('encode group not an array of', () => {
expect(encoderTester.er).toBeTruthy();
const e = {
Instrument: {
Symbol: 'ac,',
SymbolSfx: 'non',
SecurityID: 'Pellentesque',
SecurityIDSource: 'B',
SecAltIDGrp: {
NoSecurityAltID: {
elements: []
}
},
Product: 2
}
};
function run() {
encoderTester.toFix(e, encoderTester.er);
}
expect(run).toThrow(/expected array instance for group NoSecurityAltID/);
});
function getCompID(securityType) {
switch (securityType) {
case quickfix_1.SecurityType.CommonStock: {
return 'DepA';
}
case quickfix_1.SecurityType.CorporateBond: {
return 'DepB';
}
case quickfix_1.SecurityType.ConvertibleBond: {
return 'DepC';
}
default:
return 'DepD';
}
}
function createOrder(id, symbol, securityType, side, qty, price) {
return {
StandardHeader: {
DeliverToCompID: getCompID(securityType)
},
ClOrdID: `Cli${id}`,
Account: 'MyAcc',
Side: side,
Price: price,
OrdType: quickfix_1.OrdType.Limit,
OrderQtyData: {
OrderQty: qty
},
Instrument: {
SecurityType: securityType,
Symbol: symbol,
SecurityID: '459200101',
SecurityIDSource: quickfix_1.SecurityIDSource.IsinNumber
},
TimeInForce: quickfix_1.TimeInForce.Day
};
}
function getNewOrderSingle(o1) {
var _a;
return __awaiter(this, void 0, void 0, function* () {
const nosd = encoderTester.definitions.message.get('NewOrderSingle');
expect(nosd).toBeTruthy();
if (!nosd)
return;
const fix = encoderTester.toFixMessage(o1, nosd);
expect(fix).toBeTruthy();
const res = yield encoderTester.setup.client.parseText(fix);
const tag = (_a = res.view) === null || _a === void 0 ? void 0 : _a.getTyped('DeliverToCompID');
return { res, tag };
});
}
test('encode custom header 1 - expect DeliverToCompID DepA', () => __awaiter(void 0, void 0, void 0, function* () {
var _b;
const type = quickfix_1.SecurityType.CommonStock;
const o1 = createOrder(1, 'MS', type, quickfix_1.Side.Buy, 100, 1000.0);
const { res, tag } = yield getNewOrderSingle(o1);
expect(tag).toEqual('DepA');
expect(res.event).toEqual('msg');
expect(res.msgType).toEqual(index_1.MsgType.NewOrderSingle);
const parsed = (_b = res.view) === null || _b === void 0 ? void 0 : _b.toObject();
expect(parsed.StandardHeader.DeliverToCompID).toEqual('DepA');
}));
test('encode custom header 2 - expect DeliverToCompID DepC', () => __awaiter(void 0, void 0, void 0, function* () {
var _c;
const type = quickfix_1.SecurityType.ConvertibleBond;
const o1 = createOrder(1, 'MSCb', type, quickfix_1.Side.Buy, 100, 1000.0);
const { res, tag } = yield getNewOrderSingle(o1);
expect(tag).toEqual('DepC');
expect(res.event).toEqual('msg');
expect(res.msgType).toEqual(index_1.MsgType.NewOrderSingle);
const parsed = (_c = res.view) === null || _c === void 0 ? void 0 : _c.toObject();
expect(parsed.StandardHeader.DeliverToCompID).toEqual('DepC');
}));
test('encode custom header - include MsgSeqNum (for resends we do not want to overwrite)', () => __awaiter(void 0, void 0, void 0, function* () {
var _d;
const type = quickfix_1.SecurityType.ConvertibleBond;
const seqNum = 10;
const o1 = createOrder(1, 'MSCb', type, quickfix_1.Side.Buy, 100, 1000.0);
o1.StandardHeader.MsgSeqNum = seqNum;
o1.StandardHeader.PossDupFlag = true;
const nosd = encoderTester.definitions.message.get('NewOrderSingle');
expect(nosd).toBeTruthy();
if (!nosd)
return;
const fix = encoderTester.toFixMessage(o1, nosd);
expect(fix).toBeTruthy();
const res = yield encoderTester.setup.client.parseText(fix);
expect(res.event).toEqual('msg');
expect(res.msgType).toEqual(index_1.MsgType.NewOrderSingle);
const parsed = (_d = res.view) === null || _d === void 0 ? void 0 : _d.toObject();
const h = parsed.StandardHeader;
expect(h.DeliverToCompID).toEqual('DepC');
expect(h.MsgSeqNum).toEqual(seqNum);
expect(h.BeginString).toEqual('FIX.4.4');
expect(h.PossDupFlag).toEqual(true);
expect(h.MsgType).toEqual(index_1.MsgType.NewOrderSingle);
}));
//# sourceMappingURL=ascii-encoder.test.js.map