Skip to content

Releases: TimelordUK/jspurefix

4.0.0

26 Aug 11:08
83b1cb6
Compare
Choose a tag to compare

Note we are receiving increasing numbers of production style support questions normally which resolve to missing fields within the dictionary file. This is particularly problematic when these fields belong to components such as Instrument as the message cannot be parsed correctly. Please check carefully before raising such issues as they take considerable time to check and fix. In particular a good start point is to fetch tokens such as below from the fix log and see if any fields are unknown. All missing fields have to be added into the correct messages and defined properly else you will experience problems which may seem like bugs in this library. If you are working against a commercial vendor check the specification they provide carefully and ensure missing fields are all present. see the jspf-cserver example for a bespoke set of messages working with this library.

We will not provide support unless it is clear these basic checks have been done and that full examples are available to demonstrate the problem along with the corresponding dictionary file being used.

example demo applications written with this library

description link
a good start point showing FIX 4 and 5 market data request/snapshot and custom generated types. jspf-md-demo
simple example with a custom dictionary that targets cserver jspf-cserver
example trade capture applicaton jspf-demo

What's Changed

Full Changelog: 3.5.0...4.0.0

refactor to remove internal circular dependencies

  1. PR #88
  2. latest packages

changes made to this release may cause some simple compile errors in your application, please see example applications and below :-

// internally Dictionary has now been removed and replaced with typescript Map<>
readonly groups: Map<string, IContainedSet>

// stores on FixDefinitions are now based on Map - you now need, has, get, set methods. size is a property
public readonly simple: Map<string, SimpleFieldDefinition> = new Map<string, SimpleFieldDefinition>()

// in msgView the getTyped no longer returns any, when using will need cast with as 
public getTyped (tagOrName: number | string): (boolean | string | number | Date | Buffer | null) 
// e.g.
return new FixMsgStoreRecord(v.getString(MsgTag.MsgType) ?? '', v.getTyped(MsgTag.SendingTime) as Date, v.getTyped(MsgTag.MsgSeqNum) as number, v.toObject() as ILooseObject)

// to solve internal circular references in the data dictionary, an interface IContainedSet has been introduced and ContainedFieldSet now implements the interface and remains private to the library.

// i.e. an instance of interface may fetch a contained instance of the same interface (recursive symantics vs circular dependency)
getSet: (path: string) => IContainedSet | null

example command line usage parse FIX log

fetch trade capure messages from a log and display in object notation

node node_modules/jspurefix/dist/jsfix-cmd --fix=../../jsfix.test_client.txt --delimiter="|"  --session=../../data/session/test-initiator.json --type=AE --objects
AE [TradeCaptureReport] = {
    "StandardHeader": {
        "BeginString": "FIX4.4",
        "BodyLength": 194,
        "MsgType": "AE",
        "SenderCompID": "accept-comp",
        "TargetCompID": "init-comp",
        "MsgSeqNum": 11,
        "TargetSubID": "fix",
        "SendingTime": "2023-11-11T14:45:19.705Z"
    },
    "TradeReportID": "100007",
    "TradeReportTransType": 0,
    "TradeReportType": 0,
    "TrdType": 0,
    "ExecID": "600007",
    "OrdStatus": "2",
    "PreviouslyReported": false,
    "Instrument": {
        "Symbol": "Gold",
        "SecurityID": "Gold.INC"
    },
    "LastQty": 118,
    "LastPx": 4.07,
    "TradeDate": "2023-11-11T00:00:00.000Z",
    "TransactTime": "2023-11-11T14:45:19.705Z",
    "StandardTrailer": {
        "CheckSum": "016"
    }
}

see tokenised output from log

node .\node_modules\jspurefix\dist\jsfix-cmd --fix=..\..\jsfix.test_client.txt --delimiter="|"  --type=AE --session=..\..\data\session\test-initiator.json --tokens
[0] 8 (BeginString) = FIX4.4, [1] 9 (BodyLength) = 0000193
[2] 35 (MsgType) = AE[TradeCaptureReport], [3] 49 (SenderCompID) = accept-comp
[4] 56 (TargetCompID) = init-comp, [5] 34 (MsgSeqNum) = 3
[6] 57 (TargetSubID) = fix, [7] 52 (SendingTime) = 20231111-14:44:59.672
[8] 571 (TradeReportID) = 100000, [9] 487 (TradeReportTransType) = 0
[10] 856 (TradeReportType) = 0[Submit], [11] 828 (TrdType) = 0[RegularTrade]
[12] 17 (ExecID) = 600000, [13] 39 (OrdStatus) = 2[Filled]
[14] 570 (PreviouslyReported) = N[NotReportedToCounterparty], [15] 55 (Symbol) = Gold
[16] 48 (SecurityID) = Gold.INC, [17] 32 (LastQty) = 171
[18] 31 (LastPx) = 6.28, [19] 75 (TradeDate) = 20231111
[20] 60 (TransactTime) = 20231111-14:44:59.672, [21] 10 (CheckSum) = 226

FIX 5 message types generated for quickfix and repo based definitions as below.

export interface ILogon {
  StandardHeader: IStandardHeader// [1] BeginString.8, BodyLength.9 .. HopRefID.630
  EncryptMethod: number// [2] 98 (Int)
  HeartBtInt: number// [3] 108 (Int)
  RawDataLength?: number// [4] 95 (Length)
  RawData?: Buffer// [5] 96 (RawData)
  ResetSeqNumFlag?: boolean// [6] 141 (Boolean)
  NextExpectedMsgSeqNum?: number// [7] 789 (Int)
  MaxMessageSize?: number// [8] 383 (Length)
  NoMsgTypes?: ILogonNoMsgTypes[]// [9] RefMsgType.372, MsgDirection.385
  TestMessageIndicator?: boolean// [10] 464 (Boolean)
  Username?: string// [11] 553 (String)
  Password?: string// [12] 554 (String)
  NewPassword?: string// [13] 925 (String)
  EncryptedPasswordMethod?: number// [14] 1400 (Int)
  EncryptedPasswordLen?: number// [15] 1401 (Length)
  EncryptedPassword?: Buffer// [16] 1402 (RawData)
  EncryptedNewPasswordLen?: number// [17] 1403 (Length)
  EncryptedNewPassword?: Buffer// [18] 1404 (RawData)
  SessionStatus?: number// [19] 1409 (Int)
  DefaultApplVerID?: string// [20] 1137 (String)
  DefaultApplExtID?: number// [21] 1407 (Int)
  DefaultCstmApplVerID?: string// [22] 1408 (String)
  Text?: string// [23] 58 (String)
  EncodedTextLen?: number// [24] 354 (Length)
  EncodedText?: Buffer// [25] 355 (RawData)
  StandardTrailer: IStandardTrailer// [26] SignatureLength.93, Signature.89, CheckSum.10
}

you can benchmark the library with something like

npm run qf50sp2-bench-md

> jspurefix@3.2.0 qf50sp2-bench-md
> node dist/jsfix-cmd  --dict=qf50sp2 --fix=data/examples/FIX.5.0SP2/quickfix/md-data-snapshot/fix.txt --benchmark --delimiter="|" --repeats=80000

{
    "content": "8=FIX.5.0SP2|9=0000430|35=W|49=init-comp|56=accept-comp|115=DTS|34=3187|57=fix|145=NCFX1_FIX50|52=20231031-20:51:03.893|122=20231030-10:18:33.507|262=EURJPY10Y=TDS|48=EURJPY10Y=TDS|22=8|167=FXFWD|762=NONE|15=EUR|20460=EUZZ02X3F8ZYPHZZZZ|268=3|269=0|278=2971|270=-3657.05|271=0|272=20231030|273=10:18:33.507|336=1|269=1|278=2972|270=-3590.35|271=0|272=20231030|273=10:18:33.507|336=1|269=H|278=2973|270=-3623.7|272=20231030|273=10:18:33.507|336=1|10=052|",
    "view": "[0] 8 (BeginString) = FIX.5.0SP2[FIX.5.0SP2], [1] 9 (BodyLength) = 0000430\r\n[2] 35 (MsgType) = W[MarketDataSnapshotFullRefresh], [3] 49 (SenderCompID) = init-comp\r\n[4] 56 (TargetCompID) = accept-comp, [5] 115 (OnBehalfOfCompID) = DTS\r\n[6] 34 (MsgSeqNum) = 3187, [7] 57 (TargetSubID) = fix\r\n[8] 145 (DeliverToLocationID) = NCFX1_FIX50, [9] 52 (SendingTime) = 20231031-20:51:03.893\r\n[10] 122 (OrigSendingTime) = 20231030-10:18:33.507, [11] 262 (MDReqID) = EURJPY10Y=TDS\r\n[12] 48 (SecurityID) = EURJPY10Y=TDS, [13] 22 (SecurityIDSource) = 8[ExchangeSymbol]\r\n[14] 167 (SecurityType) = FXFWD[FxForward], [15] 762 (SecuritySubType) = NONE\r\n[16] 15 (Currency) = EUR, [17] 20460 (InstrumentCustom) = EUZZ02X3F8ZYPHZZZZ\r\n[18] 268 (NoMDEntries) = 3, [19] 269 (MDEntryType) = 0[Bid]\r\n[20] 278 (MDEntryID) = 2971, [21] 270 (MDEntryPx) = -3657.05\r\n[22] 271 (MDEntrySize) = 0, [23] 272 (MDEntryDate) = 20231030\r\n[24] 273 (MDEntryTime) = 10:18:33.507, [25] 336 (TradingSessionID) = 1[Day]\r\n[26] 269 (MDEntryType) = 1[Offer], [27] 278 (MDEntryID) = 2972\r\n[28] 270 (MDEntryPx) = -3590.35, [29] 271 (MDEntrySize) = 0\r\n[30] 272 (MDEntryDate) = 20231030, [31] 273 (MDEntryTime) = 10:18:33.507\r\n[32] 336 (TradingSessionID) = 1[Day], [33] 269 (MDEntryType) = H[MidPrice]\r\n[34] 278 (MDEntryID) = 2973, [35] 270 (MDEntryPx) = -3623.7\r\n[36] 272 (MDEntryDate) = 20231030, [37] 273 (MDEntryTime) = 10:18:33.507\r\n[38] 336 (TradingSessionID) = 1[Day], [39] 10 (CheckSum) = 052",
    "msg_type": "W",
    "iterations": 80000,
    "elapsed_ms": 590,
    "fields": 40,
    "content_length": 453,
    "micros_per_msg": 7.375,
    "chars_per_second": 61423729,
    "fields_per_second": 5423729
}

Quickfix XML Trim

produce a version of quickfix from src file only including the specified messages and supporting fields. Will also decorate fields with tag ids for easier readability. All components, fields and groups included to support just those messages.

node dist/jsfix-cmd  --dict=qf44 --trim --type="0,1,2,3,4,5,AE"
    <message name='Reject' msgcat='admin' msgtype='3'>
       <!-- 45 SEQNUM -->
      <field name='RefSeqNum' required='Y'/>
       <!-- 371 INT -->
      <field name='RefTagID' required='N'/>
       <!-- 372 STRING -->
      <field name='RefMsgType' required='N'/>
       <!-- 373 INT -->
      <field name='SessionRejectReason' required='N'/>
       <!-- 58 STRING -->
      <field name='Text' ...
Read more

3.5.0

20 Jan 10:04
Compare
Choose a tag to compare

Note we are receiving increasing numbers of production style support questions normally which resolve to missing fields within the dictionary file. This is particularly problematic when these fields belong to components such as Instrument as the message cannot be parsed correctly. Please check carefully before raising such issues as they take considerable time to check and fix. In particular a good start point is to fetch tokens such as below from the fix log and see if any fields are unknown. All missing fields have to be added into the correct messages and defined properly else you will experience problems which may seem like bugs in this library. If you are working against a commercial vendor check the specification they provide carefully and ensure missing fields are all present. see the jspf-cserver example for a bespoke set of messages working with this library.

We will not provide support unless it is clear these basic checks have been done and that full examples are available to demonstrate the problem along with the corresponding dictionary file being used.

example demo applications written with this library

description link
a good start point showing FIX 4 and 5 market data request/snapshot and custom generated types. jspf-md-demo
simple example with a custom dictionary that targets cserver jspf-cserver
example trade capture applicaton jspf-demo

What's Changed

  • Set PossDupFlag and OrigSendingTime when resending application messages by @douglasmuraoka in #82
  • Gap fill with wrong seq num & not inflating correctly if end seq is 0 by @douglasmuraoka in #83

Full Changelog: 3.4.0...3.5.0

Change Summary

some great contribution improvements with replay

  1. PR #82
  2. PR #83
  3. latest packages

example command line usage parse FIX log

fetch trade capure messages from a log and display in object notation

node node_modules/jspurefix/dist/jsfix-cmd --fix=../../jsfix.test_client.txt --delimiter="|"  --session=../../data/session/test-initiator.json --type=AE --objects
AE [TradeCaptureReport] = {
    "StandardHeader": {
        "BeginString": "FIX4.4",
        "BodyLength": 194,
        "MsgType": "AE",
        "SenderCompID": "accept-comp",
        "TargetCompID": "init-comp",
        "MsgSeqNum": 11,
        "TargetSubID": "fix",
        "SendingTime": "2023-11-11T14:45:19.705Z"
    },
    "TradeReportID": "100007",
    "TradeReportTransType": 0,
    "TradeReportType": 0,
    "TrdType": 0,
    "ExecID": "600007",
    "OrdStatus": "2",
    "PreviouslyReported": false,
    "Instrument": {
        "Symbol": "Gold",
        "SecurityID": "Gold.INC"
    },
    "LastQty": 118,
    "LastPx": 4.07,
    "TradeDate": "2023-11-11T00:00:00.000Z",
    "TransactTime": "2023-11-11T14:45:19.705Z",
    "StandardTrailer": {
        "CheckSum": "016"
    }
}

see tokenised output from log

node .\node_modules\jspurefix\dist\jsfix-cmd --fix=..\..\jsfix.test_client.txt --delimiter="|"  --type=AE --session=..\..\data\session\test-initiator.json --tokens
[0] 8 (BeginString) = FIX4.4, [1] 9 (BodyLength) = 0000193
[2] 35 (MsgType) = AE[TradeCaptureReport], [3] 49 (SenderCompID) = accept-comp
[4] 56 (TargetCompID) = init-comp, [5] 34 (MsgSeqNum) = 3
[6] 57 (TargetSubID) = fix, [7] 52 (SendingTime) = 20231111-14:44:59.672
[8] 571 (TradeReportID) = 100000, [9] 487 (TradeReportTransType) = 0
[10] 856 (TradeReportType) = 0[Submit], [11] 828 (TrdType) = 0[RegularTrade]
[12] 17 (ExecID) = 600000, [13] 39 (OrdStatus) = 2[Filled]
[14] 570 (PreviouslyReported) = N[NotReportedToCounterparty], [15] 55 (Symbol) = Gold
[16] 48 (SecurityID) = Gold.INC, [17] 32 (LastQty) = 171
[18] 31 (LastPx) = 6.28, [19] 75 (TradeDate) = 20231111
[20] 60 (TransactTime) = 20231111-14:44:59.672, [21] 10 (CheckSum) = 226

FIX 5 message types generated for quickfix and repo based definitions as below.

export interface ILogon {
  StandardHeader: IStandardHeader// [1] BeginString.8, BodyLength.9 .. HopRefID.630
  EncryptMethod: number// [2] 98 (Int)
  HeartBtInt: number// [3] 108 (Int)
  RawDataLength?: number// [4] 95 (Length)
  RawData?: Buffer// [5] 96 (RawData)
  ResetSeqNumFlag?: boolean// [6] 141 (Boolean)
  NextExpectedMsgSeqNum?: number// [7] 789 (Int)
  MaxMessageSize?: number// [8] 383 (Length)
  NoMsgTypes?: ILogonNoMsgTypes[]// [9] RefMsgType.372, MsgDirection.385
  TestMessageIndicator?: boolean// [10] 464 (Boolean)
  Username?: string// [11] 553 (String)
  Password?: string// [12] 554 (String)
  NewPassword?: string// [13] 925 (String)
  EncryptedPasswordMethod?: number// [14] 1400 (Int)
  EncryptedPasswordLen?: number// [15] 1401 (Length)
  EncryptedPassword?: Buffer// [16] 1402 (RawData)
  EncryptedNewPasswordLen?: number// [17] 1403 (Length)
  EncryptedNewPassword?: Buffer// [18] 1404 (RawData)
  SessionStatus?: number// [19] 1409 (Int)
  DefaultApplVerID?: string// [20] 1137 (String)
  DefaultApplExtID?: number// [21] 1407 (Int)
  DefaultCstmApplVerID?: string// [22] 1408 (String)
  Text?: string// [23] 58 (String)
  EncodedTextLen?: number// [24] 354 (Length)
  EncodedText?: Buffer// [25] 355 (RawData)
  StandardTrailer: IStandardTrailer// [26] SignatureLength.93, Signature.89, CheckSum.10
}

you can benchmark the library with something like

npm run qf50sp2-bench-md

> jspurefix@3.2.0 qf50sp2-bench-md
> node dist/jsfix-cmd  --dict=qf50sp2 --fix=data/examples/FIX.5.0SP2/quickfix/md-data-snapshot/fix.txt --benchmark --delimiter="|" --repeats=80000

{
    "content": "8=FIX.5.0SP2|9=0000430|35=W|49=init-comp|56=accept-comp|115=DTS|34=3187|57=fix|145=NCFX1_FIX50|52=20231031-20:51:03.893|122=20231030-10:18:33.507|262=EURJPY10Y=TDS|48=EURJPY10Y=TDS|22=8|167=FXFWD|762=NONE|15=EUR|20460=EUZZ02X3F8ZYPHZZZZ|268=3|269=0|278=2971|270=-3657.05|271=0|272=20231030|273=10:18:33.507|336=1|269=1|278=2972|270=-3590.35|271=0|272=20231030|273=10:18:33.507|336=1|269=H|278=2973|270=-3623.7|272=20231030|273=10:18:33.507|336=1|10=052|",
    "view": "[0] 8 (BeginString) = FIX.5.0SP2[FIX.5.0SP2], [1] 9 (BodyLength) = 0000430\r\n[2] 35 (MsgType) = W[MarketDataSnapshotFullRefresh], [3] 49 (SenderCompID) = init-comp\r\n[4] 56 (TargetCompID) = accept-comp, [5] 115 (OnBehalfOfCompID) = DTS\r\n[6] 34 (MsgSeqNum) = 3187, [7] 57 (TargetSubID) = fix\r\n[8] 145 (DeliverToLocationID) = NCFX1_FIX50, [9] 52 (SendingTime) = 20231031-20:51:03.893\r\n[10] 122 (OrigSendingTime) = 20231030-10:18:33.507, [11] 262 (MDReqID) = EURJPY10Y=TDS\r\n[12] 48 (SecurityID) = EURJPY10Y=TDS, [13] 22 (SecurityIDSource) = 8[ExchangeSymbol]\r\n[14] 167 (SecurityType) = FXFWD[FxForward], [15] 762 (SecuritySubType) = NONE\r\n[16] 15 (Currency) = EUR, [17] 20460 (InstrumentCustom) = EUZZ02X3F8ZYPHZZZZ\r\n[18] 268 (NoMDEntries) = 3, [19] 269 (MDEntryType) = 0[Bid]\r\n[20] 278 (MDEntryID) = 2971, [21] 270 (MDEntryPx) = -3657.05\r\n[22] 271 (MDEntrySize) = 0, [23] 272 (MDEntryDate) = 20231030\r\n[24] 273 (MDEntryTime) = 10:18:33.507, [25] 336 (TradingSessionID) = 1[Day]\r\n[26] 269 (MDEntryType) = 1[Offer], [27] 278 (MDEntryID) = 2972\r\n[28] 270 (MDEntryPx) = -3590.35, [29] 271 (MDEntrySize) = 0\r\n[30] 272 (MDEntryDate) = 20231030, [31] 273 (MDEntryTime) = 10:18:33.507\r\n[32] 336 (TradingSessionID) = 1[Day], [33] 269 (MDEntryType) = H[MidPrice]\r\n[34] 278 (MDEntryID) = 2973, [35] 270 (MDEntryPx) = -3623.7\r\n[36] 272 (MDEntryDate) = 20231030, [37] 273 (MDEntryTime) = 10:18:33.507\r\n[38] 336 (TradingSessionID) = 1[Day], [39] 10 (CheckSum) = 052",
    "msg_type": "W",
    "iterations": 80000,
    "elapsed_ms": 590,
    "fields": 40,
    "content_length": 453,
    "micros_per_msg": 7.375,
    "chars_per_second": 61423729,
    "fields_per_second": 5423729
}

Quickfix XML Trim

produce a version of quickfix from src file only including the specified messages and supporting fields. Will also decorate fields with tag ids for easier readability. All components, fields and groups included to support just those messages.

node dist/jsfix-cmd  --dict=qf44 --trim --type="0,1,2,3,4,5,AE"
    <message name='Reject' msgcat='admin' msgtype='3'>
       <!-- 45 SEQNUM -->
      <field name='RefSeqNum' required='Y'/>
       <!-- 371 INT -->
      <field name='RefTagID' required='N'/>
       <!-- 372 STRING -->
      <field name='RefMsgType' required='N'/>
       <!-- 373 INT -->
      <field name='SessionRejectReason' required='N'/>
       <!-- 58 STRING -->
      <field name='Text' required='N'/>
       <!-- 354 LENGTH -->
      <field name='EncodedTextLen' required='N'/>
       <!-- 355 DATA -->
      <field name='EncodedText' required='N'/>
    </message>

note programatically

async function getTrimDefinitions (types: string[]): Promise<FixDefinitions> {
  const builder = new QuickFixXmlFileBuilder(definitions)
  builder.write(types)
  const d = builder.elasticBuffer.toString()
  const parser = new QuickFixXmlFileParser(() => new StringDuplex(d), () => new EmptyLogger())
  return await parser.parse()
}

est('check builder', async () => {
  const msgTypes = ['0', '1', '2', '3', '4', '5', 'AE']
  const newdDefinitions = await getTrimDefinitions(msgTypes)
  expect(newdDefinitions).toBeTruthy()
  msgTypes.forEach(mt => {
    const m = newdDefinitions.message.get(mt)
    expect(m).toBeTruthy()
  })
})

3.4.0

03 Dec 20:47
a56ca84
Compare
Choose a tag to compare

Note we are receiving increasing numbers of production style support questions normally which resolve to missing fields within the dictionary file. This is particularly problematic when these fields belong to components such as Instrument as the message cannot be parsed correctly. Please check carefully before raising such issues as they take considerable time to check and fix. In particular a good start point is to fetch tokens such as below from the fix log and see if any fields are unknown. All missing fields have to be added into the correct messages and defined properly else you will experience problems which may seem like bugs in this library. If you are working against a commercial vendor check the specification they provide carefully and ensure missing fields are all present. see the jspf-cserver example for a bespoke set of messages working with this library.

We will not provide support unless it is clear these basic checks have been done and that full examples are available to demonstrate the problem along with the corresponding dictionary file being used.

example demo applications written with this library

description link
a good start point showing FIX 4 and 5 market data request/snapshot and custom generated types. jspf-md-demo
simple example with a custom dictionary that targets cserver jspf-cserver
example trade capture applicaton jspf-demo

Full Changelog: 3.2.0...3.4.0

What's Changed

New Contributors

Full Changelog: 3.2.0...3.4.0

Change Summary

  1. produce a version of quickfix from src file only including the specified messages and supporting fields. Will also decorate fields with tag ids for easier readability. All components, fields and groups included to support just those messages.
node dist/jsfix-cmd  --dict=qf44 --trim --type="0,1,2,3,4,5,AE"
    <message name='Reject' msgcat='admin' msgtype='3'>
       <!-- 45 SEQNUM -->
      <field name='RefSeqNum' required='Y'/>
       <!-- 371 INT -->
      <field name='RefTagID' required='N'/>
       <!-- 372 STRING -->
      <field name='RefMsgType' required='N'/>
       <!-- 373 INT -->
      <field name='SessionRejectReason' required='N'/>
       <!-- 58 STRING -->
      <field name='Text' required='N'/>
       <!-- 354 LENGTH -->
      <field name='EncodedTextLen' required='N'/>
       <!-- 355 DATA -->
      <field name='EncodedText' required='N'/>
    </message>

note programatically

async function getTrimDefinitions (types: string[]): Promise<FixDefinitions> {
  const builder = new QuickFixXmlFileBuilder(definitions)
  builder.write(types)
  const d = builder.elasticBuffer.toString()
  const parser = new QuickFixXmlFileParser(() => new StringDuplex(d), () => new EmptyLogger())
  return await parser.parse()
}

est('check builder', async () => {
  const msgTypes = ['0', '1', '2', '3', '4', '5', 'AE']
  const newdDefinitions = await getTrimDefinitions(msgTypes)
  expect(newdDefinitions).toBeTruthy()
  msgTypes.forEach(mt => {
    const m = newdDefinitions.message.get(mt)
    expect(m).toBeTruthy()
  })
})
  1. segment parser now throws an internal state summary if it fails to parse a given message which shows in log.
  2. user contribution PR #79 allowing just an acceptor to be launched.
  3. user contribution PR #78 include FIX 5 session description.
  4. one less parse of quick fix XML required to improve initialisation speed.
  5. more unit testing around parsing data dictionary. Now 300 unit tests providing very good code coverage.
  6. bug for repo based dictionary (not quickfix) relating to binary based fields not parsing correctly.
  7. refactor StringDuplex (used for testing and can be used to parse a quick fix file).
  8. bug fixing the version attribute of the fix definitions being used.
  9. some lint fixes.

example command line usage parse FIX log

fetch trade capure messages from a log and display in object notation

node node_modules/jspurefix/dist/jsfix-cmd --fix=../../jsfix.test_client.txt --delimiter="|"  --session=../../data/session/test-initiator.json --type=AE --objects
AE [TradeCaptureReport] = {
    "StandardHeader": {
        "BeginString": "FIX4.4",
        "BodyLength": 194,
        "MsgType": "AE",
        "SenderCompID": "accept-comp",
        "TargetCompID": "init-comp",
        "MsgSeqNum": 11,
        "TargetSubID": "fix",
        "SendingTime": "2023-11-11T14:45:19.705Z"
    },
    "TradeReportID": "100007",
    "TradeReportTransType": 0,
    "TradeReportType": 0,
    "TrdType": 0,
    "ExecID": "600007",
    "OrdStatus": "2",
    "PreviouslyReported": false,
    "Instrument": {
        "Symbol": "Gold",
        "SecurityID": "Gold.INC"
    },
    "LastQty": 118,
    "LastPx": 4.07,
    "TradeDate": "2023-11-11T00:00:00.000Z",
    "TransactTime": "2023-11-11T14:45:19.705Z",
    "StandardTrailer": {
        "CheckSum": "016"
    }
}

see tokenised output from log

node .\node_modules\jspurefix\dist\jsfix-cmd --fix=..\..\jsfix.test_client.txt --delimiter="|"  --type=AE --session=..\..\data\session\test-initiator.json --tokens
[0] 8 (BeginString) = FIX4.4, [1] 9 (BodyLength) = 0000193
[2] 35 (MsgType) = AE[TradeCaptureReport], [3] 49 (SenderCompID) = accept-comp
[4] 56 (TargetCompID) = init-comp, [5] 34 (MsgSeqNum) = 3
[6] 57 (TargetSubID) = fix, [7] 52 (SendingTime) = 20231111-14:44:59.672
[8] 571 (TradeReportID) = 100000, [9] 487 (TradeReportTransType) = 0
[10] 856 (TradeReportType) = 0[Submit], [11] 828 (TrdType) = 0[RegularTrade]
[12] 17 (ExecID) = 600000, [13] 39 (OrdStatus) = 2[Filled]
[14] 570 (PreviouslyReported) = N[NotReportedToCounterparty], [15] 55 (Symbol) = Gold
[16] 48 (SecurityID) = Gold.INC, [17] 32 (LastQty) = 171
[18] 31 (LastPx) = 6.28, [19] 75 (TradeDate) = 20231111
[20] 60 (TransactTime) = 20231111-14:44:59.672, [21] 10 (CheckSum) = 226

FIX 5 message types generated for quickfix and repo based definitions as below.

export interface ILogon {
  StandardHeader: IStandardHeader// [1] BeginString.8, BodyLength.9 .. HopRefID.630
  EncryptMethod: number// [2] 98 (Int)
  HeartBtInt: number// [3] 108 (Int)
  RawDataLength?: number// [4] 95 (Length)
  RawData?: Buffer// [5] 96 (RawData)
  ResetSeqNumFlag?: boolean// [6] 141 (Boolean)
  NextExpectedMsgSeqNum?: number// [7] 789 (Int)
  MaxMessageSize?: number// [8] 383 (Length)
  NoMsgTypes?: ILogonNoMsgTypes[]// [9] RefMsgType.372, MsgDirection.385
  TestMessageIndicator?: boolean// [10] 464 (Boolean)
  Username?: string// [11] 553 (String)
  Password?: string// [12] 554 (String)
  NewPassword?: string// [13] 925 (String)
  EncryptedPasswordMethod?: number// [14] 1400 (Int)
  EncryptedPasswordLen?: number// [15] 1401 (Length)
  EncryptedPassword?: Buffer// [16] 1402 (RawData)
  EncryptedNewPasswordLen?: number// [17] 1403 (Length)
  EncryptedNewPassword?: Buffer// [18] 1404 (RawData)
  SessionStatus?: number// [19] 1409 (Int)
  DefaultApplVerID?: string// [20] 1137 (String)
  DefaultApplExtID?: number// [21] 1407 (Int)
  DefaultCstmApplVerID?: string// [22] 1408 (String)
  Text?: string// [23] 58 (String)
  EncodedTextLen?: number// [24] 354 (Length)
  EncodedText?: Buffer// [25] 355 (RawData)
  StandardTrailer: IStandardTrailer// [26] SignatureLength.93, Signature.89, CheckSum.10
}

you can benchmark the library with something like

npm run qf50sp2-bench-md

> jspurefix@3.2.0 qf50sp2-bench-md
> node dist/jsfix-cmd  --dict=qf50sp2 --fix=data/examples/FIX.5.0SP2/quickfix/md-data-snapshot/fix.txt --benchmark --delimiter="|" --repeats=80000

{
    "content": "8=FIX.5.0SP2|9=0000430|35=W|49=init-comp|56=accept-comp|115=DTS|34=3187|57=fix|145=NCFX1_FIX50|52=20231031-20:51:03.893|122=20231030-10:18:33.507|262=EURJPY10Y=TDS|48=EURJPY10Y=TDS|22=8|167=FXFWD|762=NONE|15=EUR|20460=EUZZ02X3F8ZYPHZZZZ|268=3|269=0|278=2971|270=-3657.05|271=0|272=20231030|273=10:18:33.507|336=1|269=1|278=2972|270=-3590.35|271=0|272=20231030|273=10:18:33.507|336=1|269=H|278=2973|270=-3623.7|272=20231030|273=10:18:33.507|336=1|10=052|",
    "view": "[0] 8 (BeginString) = FIX.5.0SP2[FIX.5.0SP2], [1] 9 (BodyLength) = 0000430\r\n[2] 35 (MsgType) = W[MarketDataSnapshotFullRefresh], [3] 49 (SenderCompID) = init-comp\r\n[4] 56 (TargetCompID) = accept-comp, [5] 115 (OnBehalfOfCompID) = DTS\r\n[6] 34 (MsgSeqNum) = 3187, [7] 57 (TargetSubID) = fix\r\n[8] 145 (DeliverToLocationID) = NCFX1_FIX50, [9] 52 (SendingTime) = 20231031-20:51:03.893\r\n[10] 122 (OrigSendingTime) = 20231030-10:18:33.507, [11] 262 (MDReqID) = EURJPY10Y=TDS\r\n[12] 48 (SecurityID) = EURJPY10Y=TDS, [13] 22 (SecurityIDSource) = 8[ExchangeSymbol]\r\n[14] 167 (SecurityType) = FXFWD[FxForward], [15] 762 (SecuritySubType) = NONE\r\n[16] 15 (Currency) = EUR, [17] 20460 (InstrumentCustom) = EUZZ02X3F8ZYPHZZZZ\r\n[18] 268 (NoMDEntries) = 3, [19] 269 (MDEntryType) = 0[Bid]\r\n[20] 278 (MDEntryID) = 2971, [21] 270 (MDEntryPx) = -3657.05\r\n[22] 271 (MDEntrySize) = 0, [23]...
Read more

3.2.0

11 Nov 15:07
Compare
Choose a tag to compare

Note we are receiving increasing numbers of production style support questions normally which resolve to missing fields within the dictionary file. This is particularly problematic when these fields belong to components such as Instrument as the message cannot be parsed correctly. Please check carefully before raising such issues as they take considerable time to check and fix. In particular a good start point is to fetch tokens such as below from the fix log and see if any fields are unknown. All missing fields have to be added into the correct messages and defined properly else you will experience problems which may seem like bugs in this library. If you are working against a commercial vendor check the specification they provide carefully and ensure missing fields are all present. see the jspf-cserver example for a bespoke set of messages working with this library.

We will not provide support unless it is clear these basic checks have been done and that full examples are available to demonstrate the problem along with the corresponding dictionary file being used.

example demo applications written with this library

description link
a good start point showing FIX 4 and 5 market data request/snapshot and custom generated types. jspf-md-demo
simple example with a custom dictionary that targets cserver jspf-cserver
example trade capture applicaton jspf-demo

change summary

  1. #62 the quickfix parser has been improved to cope with forward referenced components where previously groups wthin those components would not be included. This particularly impacts the FIX 5 SP2 specification. If you are using FIX 5 and quickfix notation you must move to this release.
  2. fewer memory allocations.
  3. include types for quickfix and repo based FIX5 SP2 - see the jspf-md-demo for both FIX.50SP2 and FIX.4.4 examples and an easy start point for a jspurefix based application.
  4. more unit tests added covering FIX 5 specifically.
  5. started documenting apis via doc notation, this is a work in progress.
  6. each message / component definition has a helper method allowing to reach recursively in and pull out a sub definition
  getSecListGrp (): (ContainedFieldSet | null) {
    return this.definitions.getSet('SecurityList.SecListGrp')
  }

  getNumRelatedSym (): (ContainedFieldSet | null) {
    return  return this.definitions.getSet('SecurityList.SecListGrp.NoRelatedSym')
  }

 getNoTickRules (): (ContainedFieldSet | null) {
      return  return this.definitions.getSet('SecurityList.SecListGrp.NoRelatedSym.SecurityTradingRules.BaseTradingRules.TickRules.NoTickRules')
  }
  1. linting against standard
  2. note the existing helper command can be useful when parsing logs. See jspf-demo package.json and examples below.
  3. add admin messages into FIX5 xml for quickfix and generate associated types.
  4. update to latest npm dependencies e.g. tsc compiler.
  5. new cserver example with generated types for this venue jspf-cserver
  6. repo unzipped as part of npm install

example command line usage parse FIX log

fetch trade capure messages from a log and display in object notation

node node_modules/jspurefix/dist/jsfix-cmd --fix=../../jsfix.test_client.txt --delimiter="|"  --session=../../data/session/test-initiator.json --type=AE --objects
AE [TradeCaptureReport] = {
    "StandardHeader": {
        "BeginString": "FIX4.4",
        "BodyLength": 194,
        "MsgType": "AE",
        "SenderCompID": "accept-comp",
        "TargetCompID": "init-comp",
        "MsgSeqNum": 11,
        "TargetSubID": "fix",
        "SendingTime": "2023-11-11T14:45:19.705Z"
    },
    "TradeReportID": "100007",
    "TradeReportTransType": 0,
    "TradeReportType": 0,
    "TrdType": 0,
    "ExecID": "600007",
    "OrdStatus": "2",
    "PreviouslyReported": false,
    "Instrument": {
        "Symbol": "Gold",
        "SecurityID": "Gold.INC"
    },
    "LastQty": 118,
    "LastPx": 4.07,
    "TradeDate": "2023-11-11T00:00:00.000Z",
    "TransactTime": "2023-11-11T14:45:19.705Z",
    "StandardTrailer": {
        "CheckSum": "016"
    }
}

see tokenised output from log

node .\node_modules\jspurefix\dist\jsfix-cmd --fix=..\..\jsfix.test_client.txt --delimiter="|"  --type=AE --session=..\..\data\session\test-initiator.json --tokens
[0] 8 (BeginString) = FIX4.4, [1] 9 (BodyLength) = 0000193
[2] 35 (MsgType) = AE[TradeCaptureReport], [3] 49 (SenderCompID) = accept-comp
[4] 56 (TargetCompID) = init-comp, [5] 34 (MsgSeqNum) = 3
[6] 57 (TargetSubID) = fix, [7] 52 (SendingTime) = 20231111-14:44:59.672
[8] 571 (TradeReportID) = 100000, [9] 487 (TradeReportTransType) = 0
[10] 856 (TradeReportType) = 0[Submit], [11] 828 (TrdType) = 0[RegularTrade]
[12] 17 (ExecID) = 600000, [13] 39 (OrdStatus) = 2[Filled]
[14] 570 (PreviouslyReported) = N[NotReportedToCounterparty], [15] 55 (Symbol) = Gold
[16] 48 (SecurityID) = Gold.INC, [17] 32 (LastQty) = 171
[18] 31 (LastPx) = 6.28, [19] 75 (TradeDate) = 20231111
[20] 60 (TransactTime) = 20231111-14:44:59.672, [21] 10 (CheckSum) = 226

FIX 5 message types generated for quickfix and repo based definitions as below.

export interface ILogon {
  StandardHeader: IStandardHeader// [1] BeginString.8, BodyLength.9 .. HopRefID.630
  EncryptMethod: number// [2] 98 (Int)
  HeartBtInt: number// [3] 108 (Int)
  RawDataLength?: number// [4] 95 (Length)
  RawData?: Buffer// [5] 96 (RawData)
  ResetSeqNumFlag?: boolean// [6] 141 (Boolean)
  NextExpectedMsgSeqNum?: number// [7] 789 (Int)
  MaxMessageSize?: number// [8] 383 (Length)
  NoMsgTypes?: ILogonNoMsgTypes[]// [9] RefMsgType.372, MsgDirection.385
  TestMessageIndicator?: boolean// [10] 464 (Boolean)
  Username?: string// [11] 553 (String)
  Password?: string// [12] 554 (String)
  NewPassword?: string// [13] 925 (String)
  EncryptedPasswordMethod?: number// [14] 1400 (Int)
  EncryptedPasswordLen?: number// [15] 1401 (Length)
  EncryptedPassword?: Buffer// [16] 1402 (RawData)
  EncryptedNewPasswordLen?: number// [17] 1403 (Length)
  EncryptedNewPassword?: Buffer// [18] 1404 (RawData)
  SessionStatus?: number// [19] 1409 (Int)
  DefaultApplVerID?: string// [20] 1137 (String)
  DefaultApplExtID?: number// [21] 1407 (Int)
  DefaultCstmApplVerID?: string// [22] 1408 (String)
  Text?: string// [23] 58 (String)
  EncodedTextLen?: number// [24] 354 (Length)
  EncodedText?: Buffer// [25] 355 (RawData)
  StandardTrailer: IStandardTrailer// [26] SignatureLength.93, Signature.89, CheckSum.10
}

you can benchmark the library with something like

npm run qf50sp2-bench-md

> jspurefix@3.2.0 qf50sp2-bench-md
> node dist/jsfix-cmd  --dict=qf50sp2 --fix=data/examples/FIX.5.0SP2/quickfix/md-data-snapshot/fix.txt --benchmark --delimiter="|" --repeats=80000

{
    "content": "8=FIX.5.0SP2|9=0000430|35=W|49=init-comp|56=accept-comp|115=DTS|34=3187|57=fix|145=NCFX1_FIX50|52=20231031-20:51:03.893|122=20231030-10:18:33.507|262=EURJPY10Y=TDS|48=EURJPY10Y=TDS|22=8|167=FXFWD|762=NONE|15=EUR|20460=EUZZ02X3F8ZYPHZZZZ|268=3|269=0|278=2971|270=-3657.05|271=0|272=20231030|273=10:18:33.507|336=1|269=1|278=2972|270=-3590.35|271=0|272=20231030|273=10:18:33.507|336=1|269=H|278=2973|270=-3623.7|272=20231030|273=10:18:33.507|336=1|10=052|",
    "view": "[0] 8 (BeginString) = FIX.5.0SP2[FIX.5.0SP2], [1] 9 (BodyLength) = 0000430\r\n[2] 35 (MsgType) = W[MarketDataSnapshotFullRefresh], [3] 49 (SenderCompID) = init-comp\r\n[4] 56 (TargetCompID) = accept-comp, [5] 115 (OnBehalfOfCompID) = DTS\r\n[6] 34 (MsgSeqNum) = 3187, [7] 57 (TargetSubID) = fix\r\n[8] 145 (DeliverToLocationID) = NCFX1_FIX50, [9] 52 (SendingTime) = 20231031-20:51:03.893\r\n[10] 122 (OrigSendingTime) = 20231030-10:18:33.507, [11] 262 (MDReqID) = EURJPY10Y=TDS\r\n[12] 48 (SecurityID) = EURJPY10Y=TDS, [13] 22 (SecurityIDSource) = 8[ExchangeSymbol]\r\n[14] 167 (SecurityType) = FXFWD[FxForward], [15] 762 (SecuritySubType) = NONE\r\n[16] 15 (Currency) = EUR, [17] 20460 (InstrumentCustom) = EUZZ02X3F8ZYPHZZZZ\r\n[18] 268 (NoMDEntries) = 3, [19] 269 (MDEntryType) = 0[Bid]\r\n[20] 278 (MDEntryID) = 2971, [21] 270 (MDEntryPx) = -3657.05\r\n[22] 271 (MDEntrySize) = 0, [23] 272 (MDEntryDate) = 20231030\r\n[24] 273 (MDEntryTime) = 10:18:33.507, [25] 336 (TradingSessionID) = 1[Day]\r\n[26] 269 (MDEntryType) = 1[Offer], [27] 278 (MDEntryID) = 2972\r\n[28] 270 (MDEntryPx) = -3590.35, [29] 271 (MDEntrySize) = 0\r\n[30] 272 (MDEntryDate) = 20231030, [31] 273 (MDEntryTime) = 10:18:33.507\r\n[32] 336 (TradingSessionID) = 1[Day], [33] 269 (MDEntryType) = H[MidPrice]\r\n[34] 278 (MDEntryID) = 2973, [35] 270 (MDEntryPx) = -3623.7\r\n[36] 272 (MDEntryDate) = 20231030, [37] 273 (MDEntryTime) = 10:18:33.507\r\n[38] 336 (TradingSessionID) = 1[Day], [39] 10 (CheckSum) = 052",
    "msg_type": "W",
    "iterations": 80000,
    "elapsed_ms": 590,
    "fields": 40,
    "content_length": 453,
    "micros_per_msg": 7.375,
    "chars_per_second": 61423729,
    "fields_per_second": 5423729
}

What's Changed

Full Changelog: 3.0.0...3.2.0

3.0.0

04 Jan 13:51
Compare
Choose a tag to compare

this version contains a lot of change and needs to be tested carefully before release.

  1. #56 - better handling of loss of connection where a logout attempt over that transport causes misaligned seq number on reconnect when not using resetSeqNo

  2. #54 remove deprecated npm libs replaced with axios.

  3. replace tslint (deprecated) with eslint and extended typescript support. Fix the significant number of linting errors reported which impacts many files in system. Please check carefully and see jspf-md-demo there may be some minor compilation breaks from previous version where for example you many also be returned null such as view.structure.

  4. #55 and #57 - PR see below

What's Changed

New Contributors

Full Changelog: 2.2.1...3.0.0

2.2.1

23 Oct 12:32
Compare
Choose a tag to compare
  1. add quickfix types for 4.2 and 4.3 (repo not currently including user/password on login) #52
  2. update to latest dependencies
  3. corrected issue with incorrect BeginString in session config. #50
  4. helper method for #53 - see https://github.com/TimelordUK/jspf-md-demo

2.2.0

14 Aug 09:38
Compare
Choose a tag to compare
  1. #47 Error Improvements

another great PR improving TCP error management where responsibility is handed back to client for logging etc

2.1.0

06 Aug 10:36
Compare
Choose a tag to compare
  1. Fixing lost promises while connecting

thanks for work done in improving the tcp initiator error recovery in this great PR

v2.0.4

02 Mar 21:39
Compare
Choose a tag to compare

v2.0.3

10 Jan 18:49
Compare
Choose a tag to compare