Closed DominikStefancik closed 1 year ago
hello,
if you use the quckfix style XML data dictionary you can look at
https://github.com/TimelordUK/jspf-md-demo
where you would add your new fields into the definitions within the XML and then run the generator
"generate": "cd node_modules/jspurefix/dist && node jsfix-cmd \"--dict=../../data/FIX44-MD.xml\" \"--compile\" \"--output=../../src/types/\"",
this will regenerate the messages so. intellisense works with new fields. The data dictionary is needed to correctly send and parse messages.
let us know how it goes - perhaps create a sinple repo with just the additional fields required which we can help get working
Hi,
As far as I understand, the generator is for creating custom types or extending existing ones with fields which are not there yet. That is not my problem.
The IStandardHeader
interface already has the fields DeliverToCompID
and SenderSubID
defined in it. My problem is the following:
I create an order message, e.g. NewOrderSingle
with fields required by the client’s OMS. When the message is sent, it has the StandardHeader
with the required fields (BeginString
, BodyLength
, MsgType
, SenderCompID
, …). What I need, is that the header contains additional (optional) fields on top of that. So if I printed such a message out, its StandardHeader
would look similar to this:
"StandardHeader": {
"SenderCompID": “sender-id",
"TargetCompID": “target-id",
... (other required fields),
...,
“DeliverToCompID”: "some_string",
“SenderSubID”: "some_string",
...
}
I looked at the source code of the repo and debugged the samples provided in it. This is what I found:
StandardHeader
yet.FixSession
, which uses transport.transmitter
(of type MsgTransmitter) to do that.public encodeMessage (msgType: string, obj: ILooseObject)
method which adds the StandardHeader to the object before it encodes it.There are two concrete classes of the MsgTransmitter. I’m using the AsciiMsgTransmitter in my code. In its encodeMessage
implementation, it creates the header with headerProps
, like this
const { StandardHeader, ...bodyProps } = obj
const headerProps: Partial<IStandardHeader> = {
...(StandardHeader?.PossDupFlag ? { PossDupFlag: StandardHeader?.PossDupFlag } : {}),
...(StandardHeader?.MsgSeqNum ? { MsgSeqNum: StandardHeader?.MsgSeqNum } : {})
}
const hdr: ILooseObject = factory.header(msgType, this.msgSeqNum,this.time || new Date(), headerProps)
This means that even if I manually set the header and add it to the object before sending it like this
const header: ILooseObject = this.config.factory.header(
msgType,
(this.transport.transmitter as AsciiMsgTransmitter).msgSeqNum,
new Date(),
{
DeliverToCompID: 'some_string',
SenderSubID: 'some_string'
}
);
fixMessage.StandardHeader = header;
this.send(msgType, fixMessage)
The encodeMessage
will not use the added fields and create a header without them.
Why is it implemented like this? Why are only PossDupFlag
and MsgSeqNum
used in the overrideData
parameter of the ISessionMsgFactory.header
method?
Is there any other way to add the additional fields into the header and make sure they are encoded?
hello,
it is not ideal but for now can you use the mutator within the factory - if you look at unit test session.test.js
somewhere in your app assign the mutator and then the object is gven to you after it is created.
hopefully this will work
clientFactory.mutator = mutateSeqNo
function mutateSeqNo (description: ISessionDescription, type: string, o: ILooseObject): ILooseObject {
switch (type) {
case 'StandardHeader': {
const hdr = o as IStandardHeader
// set your fields here
if (hdr.MsgSeqNum === 2) {
hdr.MsgSeqNum = 0
}
break
}
}
return o
}
It would be better for us to change the methods non private and then an app could extend factory
and create extended factory - but this will not work as code stands.
clientFactory = new SessionMsgFactoryExt (clientDescription)
serverFactory = new SessionMsgFactory(serverDescription)
const clientConfig = new JsFixConfig(clientFactory, definitions, clientDescription, AsciiChars.Pipe)
export class SessionMsgFactoryExt extends SessionMsgFactory {
asciiHeader (msgType: string, seqNum: number, time: Date, overrideData?: Partial<IStandardHeader>): ILooseObject {
}
}
Hi,
Thank you for the hint! Using custom mutator helped. The optional tags are in the header now.
The situation got a bit more complicated, though :) The value for the DeliverToCompID
tag has to be different for each of the client’s department.
The use case is this:
If I send an order for a certain asset type (e.g. Stock), it has to route to a department A. If I send an order for another type, it has to route to a different department. In order to say to which department the message should be sent, I have to set a different value for the tag DeliverToCompID
.
That means, that every time I call the mutator, I have to know the asset type. Something like this
function myMutator (description: ISessionDescription, type: string, o: ILooseObject): ILooseObject {
switch (type) {
case 'StandardHeader': {
const hdr = o as IStandardHeader
const assetType = await asynCallToGetAssetType()
if (assetType === ‘Stock’) {
hdr.DeliverToCompID = ‘DepA’
} else if(…) {
hdr.DeliverToCompID = ‘DepB’
} else {
hdr.DeliverToCompID = ‘DepC’
}
break
}
}
return o
}
The problem is, I know the asset type when an order message (e.g. IOrderSingle) is being constructed. Otherwise, I have to us an async call to get the asset type. If I wanted to use this async call in the mutator, I would have to change the function definition to be async and return a promise.
function async myMutator (description: ISessionDescription, type: string, o: ILooseObject): Promise<ILooseObject>{ … }
When I tested it, the messages are not sent.
Is there any other way that mutator could use async calls in its body that would work?
If that doesn’t work, is it possible to set the tag DeliverToCompID
in the StandardHeader
of a message in the time when it is being constructed, but also make sure that it is not overwritten in the encodeMessage
as I described in my previous comment?
ok having a look will report back shortly.
are you able to test the current master git banch
if you look at ascii encode tests
"8=FIX4.4|9=0000159|35=D|49=init-comp|56=accept-comp|128=DepC|34=10|57=fix|52=20210926-18:43:48.413|11=Cli1|1=MyAcc|55=MSCb|48=459200101|22=4|167=CB|54=1|38=100|40=2|44=1000|59=0|10=107|"
function createOrder (id: number, symbol: string, securityType: SecurityType, side: Side, qty: number, price: number): INewOrderSingle {
return {
ClOrdID: `Cli${id}`,
Account: 'MyAcc',
Side: side,
Price: price,
OrdType: OrdType.Limit,
OrderQtyData: {
OrderQty: qty
} as IOrderQtyData,
Instrument: {
SecurityType: securityType,
Symbol: symbol,
SecurityID: '459200101',
SecurityIDSource: SecurityIDSource.IsinNumber
} as IInstrument,
TimeInForce: TimeInForce.Day
} as INewOrderSingle
}
function getCompID(securityType: SecurityType): string {
switch (securityType) {
case SecurityType.CommonStock: {
return 'DepA'
}
case SecurityType.CorporateBond: {
return 'DepB'
}
case SecurityType.ConvertibleBond: {
return 'DepC'
}
default:
return 'DepD'
}
}
test('encode custom header 1 - expect DeliverToCompID DepA', async () => {
const type = SecurityType.CommonStock
const o1 = createOrder(1, 'MS', type, Side.Buy, 100, 1000.0)
const h: ILooseObject = {
DeliverToCompID: getCompID(type)
}
o1.StandardHeader = h as IStandardHeader
const nosd = definitions.message.get('NewOrderSingle')
const fix = toFixMessage(o1, nosd)
expect(fix).toBeTruthy()
const res: ParsingResult = await toParse(fix)
const tag = res.view.getTyped('DeliverToCompID')
expect(tag).toEqual('DepA')
expect(res.event).toEqual('msg')
expect(res.msgType).toEqual(MsgType.NewOrderSingle)
const parsed: INewOrderSingle = res.view.toObject()
expect(parsed.StandardHeader.DeliverToCompID).toEqual('DepA')
})
test('encode custom header 2 - expect DeliverToCompID DepC', async () => {
const type = SecurityType.ConvertibleBond
const o1 = createOrder(1, 'MSCb', type, Side.Buy, 100, 1000.0)
const h: ILooseObject = {
DeliverToCompID: getCompID(type)
}
o1.StandardHeader = h as IStandardHeader
const nosd = definitions.message.get('NewOrderSingle')
const fix = toFixMessage(o1, nosd)
expect(fix).toBeTruthy()
const res: ParsingResult = await toParse(fix)
const tag = res.view.getTyped('DeliverToCompID')
expect(tag).toEqual('DepC')
expect(res.event).toEqual('msg')
expect(res.msgType).toEqual(MsgType.NewOrderSingle)
const parsed: INewOrderSingle = res.view.toObject()
expect(parsed.StandardHeader.DeliverToCompID).toEqual('DepC')
})
Thanks for the changes! Looking at the code this looks very promising, I've run into a similar problem with OrigSendingTime
yesterday.
Related question - is there a reason why SendingTime
is not considered and does not take precedence before this.time || new Date()
? We have a scenario where we need to track the SendingTime
and pack it back to OrigSendingTime
<122> on replay. Is this something you would consider?
I had a go at adding this but as yet have not added tests for it - in latest release
I can see that the issue is not closed yet, so the question is, is the mutator still the preferred way of adding field to the header?
https://github.com/TimelordUK/jspurefix/issues/39
take a look here - the better solution is to install your own message factory for total control
On Mon, 13 Jun 2022 at 15:25, Vadym Kurachevskyi @.***> wrote:
I can see that the issue is not closed yet, so the question is, is the mutator still the preferred way of adding field to the header?
— Reply to this email directly, view it on GitHub https://github.com/TimelordUK/jspurefix/issues/22#issuecomment-1153990543, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABXWJG64UBEKQDKXQJ6E443VO5AEDANCNFSM5DJLGLVA . You are receiving this because you commented.Message ID: @.***>
specifically here https://github.com/TimelordUK/jspf-md-demo/blob/master/src/app/app.ts
this whole sample is a good start point note locally installed factory which you can the change
Yay. It worked. Thanks for the super-fast response!
closing think this is resolved
Hi,
I'm using jspurefix to communicate with our client's FIX OMS. When sending application messages, the client requires optional tags DeliverToCompID and SenderSubID to be provided in the standard header, otherwise the messages are not processed and I don't get any response from them. I have tried to "inject" the fields into the header manually and then send the message, like this
After I do that, the
fixMessage
object contains the header with the additional fields, but when I send it, the fields are not present. Neither they are seen in the logs when theonEncoded
method is called, nor the client doesn't see them when the message is received.How can I add the additional fields in the header and be sure they are included in the message when sent? Is my approach a correct one?
Thank you for your advice!