TimelordUK / jspurefix

native typescript FIX engine
MIT License
61 stars 28 forks source link

URGENT: Multiple FIX Sessions: One SenderCompID to Multiple TargetCompIDs #87

Open helloissac opened 4 months ago

helloissac commented 4 months ago

Hi @TimelordUK, we are running multiple FIX sessions, connecting multiple clients to a server.

The server would need to handle multiple TargetCompIDs (clients' SenderCompIDs).

I'm currently using a single TargetCompID from the FIX server's ISessionDescription.

However, I want my server to send to different TargetCompIDs in multiple FIX sessions and I'm not sure how to work around it.

Could you suggest a way to work around this situation using your jspurefix package?

Thank you very much for you attention.

TimelordUK commented 4 months ago

i do not think there is a workaround solution without some changes to the jspurefix library itself.

you could try the branch https://github.com/TimelordUK/jspurefix/tree/dynamic.comp/data/session - but this is experimental.

please note i do not have time to go through many iterations to produce something precisely for your requirement, the branch shows that something like you suggest can be done. You would have to take this and adapt it to your purposes which eventually could be pulled into master branch.

if you look at the server test-dynamic-acceptor, the targetCompId of the server is marked '*'

{
  "application": {
    "type": "acceptor",
    "name": "test_server",
    "tcp": {
      "host" : "localhost",
      "port": 2344
    },
    "protocol": "ascii",
    "dictionary": "repo44"
  },
  "Username": "js-server",
  "Password": "pwd-server",
  "EncryptMethod": 0,
  "ResetSeqNumFlag": true,
  "HeartBtInt": 30,
  "SenderCompId": "accept-comp",
  "TargetCompID": "*",
  "TargetSubID": "fix",
  "BeginString": "FIX.4.4"
}

next, in the server applicaton on the login message from client we add some logic to override the template parameter based on client senderCompId

  protected onLogon (view: MsgView, user: string, password: string): boolean {
    const senderCompId = view.getTyped(MsgTag.SenderCompID) as string
    const id = this.transport?.id ?? -1
    this.logger.info(`transport id ${id} will use targetCompId ${senderCompId} from client login`)
    this.config.factory?.addCompIdMapping(id, senderCompId)
    return true
  }

if we run the test applicaton on this branch

 node .\dist\sample\tcp\trade-capture\app.js | tee ./log.txt

and look in log

rg "from client" .\log.txt 52:2024-06-30T13:28:29.457Z [test_server:TradeCaptureServer] info: transport id 1 will use targetCompId init-comp from client login

and in fix log the server sends back the clients sender sub id.

8=FIX.4.4|9=0000117|35=A|49=init-comp|56=accept-comp|34=1|57=fix|52=20240630-13:44:32.776|98=0|108=30|141=Y|553=js-client|554=pwd-client|10=038|
8=FIX.4.4|9=0000117|35=A|49=accept-comp|56=init-comp|34=1|57=fix|52=20240630-13:44:32.787|98=0|108=30|141=Y|553=js-server|554=pwd-server|10=088|

this is being done in the factory class - if you have your own overriden version of this then this would need to be updated with something similar

see ascii-session-msg-factory.ts

public header (transportId: number, msgType: string, seqNum: number, time: Date, overrideData?: Partial<IStandardHeader>): ILooseObject {
    const description = this.description
    const bodyLength: number = Math.max(4, description.BodyLengthChars ?? 7)
    const placeHolder = Math.pow(10, bodyLength - 1) + 1
    let targetCompId: string | undefined = description.TargetCompID
    if (targetCompId === '*') {
      if (this.compMap.has(transportId)) {
        targetCompId = this.compMap.get(transportId)
      }
    }
    const o: IStandardHeader = {
      BeginString: description.BeginString,
      BodyLength: placeHolder,
      MsgType: msgType,
      SenderCompID: description.SenderCompId,
      MsgSeqNum: seqNum,
      SendingTime: time,
      TargetCompID: targetCompId ?? description.TargetCompID,
      TargetSubID: description.TargetSubID,
      SenderSubID: description.SenderSubID,
      ...overrideData
    }

    return this.mutate(o, 'StandardHeader')
  }
}