TimelordUK / jspurefix

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

Help With Sending Raw Fix Messages #53

Open fw-vince opened 2 years ago

fw-vince commented 2 years ago

@TimelordUK I am trying to pass raw FIX messages from client to server. Is there a function or pattern that will take a string like 8=FIX.4.49=9035=A34=19749=TRADER52=20220927-15:38:19.00056=OMS98=0108=3010=181 and send this exact message without mutation?

TimelordUK commented 2 years ago

if you checkout

https://github.com/TimelordUK/jspf-md-demo

we have

  1. added a custom message into FIX44-MD.xml

    <message name='UserFixArchive' msgcat='app' msgtype='UFA'>
    <group name='NoEvents' required='Y'>
    <field name='Subject' required='N' />
    <field name='RawDataLength' required='Y' />
    <field name='RawData' required='Y' />
    </group>
    </message>
  2. npm run generate

> jspf-md-demo@1.0.3 generate
> cd node_modules/jspurefix/dist && node jsfix-cmd "--dict=../../data/FIX44-MD.xml" "--compile" "--output=../../src/types/"
  1. added as an example a call to a new method sendArchivist when we receive a news flash
  case MsgType.News: {
        const news: INews = view.toObject()
        this.logger.info(news.Headline)
        // send the news to 'archive' service as a user defined message
        this.sendArchivist(msgType, view)
      }
        break

this method builds a raw message and sends it

  private sendArchivist (msgType: string, view: MsgView): void {
    const viewBuffer = this.asRaw(view)
    const asTxt = viewBuffer.toString()
    this.logger.info(asTxt)
    // @ts-expect-error ts2307
    const archive: IUserFixArchive = {
      NoEvents: [
        {
          Subject: msgType,
          RawData: viewBuffer,
          RawDataLength: viewBuffer.length
        }
      ]
    }
    this.send(MsgType.UserFixArchive, archive)
  }
  1. the server receives the message and decodes it to txt

2022-10-22T13:44:20.004Z [test_server:MDServer] info: event count for archive 1 2022-10-22T13:44:20.004Z [test_server:MDServer] info: request to archive this message 8=FIX4.4☺9=0000095☺35=B☺49=accept-comp☺56=init-comp☺34=3☺57=fix☺52=20221022-13:44:19.989☺148=mash the buy button!☺10=186☺

  case MsgType.UserFixArchive: {
        const msg: IUserFixArchive = view.toObject()
        const events: IUserFixArchiveNoEvents[] = msg.NoEvents
        this.logger.info(`event count for archive ${events.length}`)
        for (let i = 0; i < events.length; ++i) {
          const ev = events[i]
          const txt = ev.RawData.toString('utf8')
          this.logger.info(`request to archive this message ${txt}`)
        }
      }
fw-vince commented 2 years ago

@TimelordUK thank you for the quick response and example added to https://github.com/TimelordUK/jspf-demo/. Although, I think this does not completely solve my problem.

What I mean by "Sending Raw Fix Messages" is I need to send any already prepared FIX message (string), regardless of correct header, messageType or trailer, from my client to the server. For example, say I want to use AsciiSession to send any FIX message found in jspurefix/data/examples/FIX.4.4/fix.txt to a FIX server (not necessarily jspurefix), is this possible with jspurefix?

TimelordUK commented 2 years ago

The problem surely you have is if you wish to communicate say with a third party fix engine the session protocol is important. That is to say you need to maintain sequence numbers and keep alive heartbeat and so on.

That being said if you are asking can I read a set of messages say from fix file strip out existing header and trailer and re encode those messages so third party engine will receive them then yes that should be possible. But it would be easier I would think to store and replay messages in json format but working in raw format is also possible.

You can’t just take a set of messages and send them without valid headers else a commercial engine will just likely drop session immediately

fw-vince commented 2 years ago

@TimelordUK thank you for the response and I understand what you are saying about valid session protocol and messaging, perhaps stripping out the header and trailer would be necessary. I was also thinking to create a unique session based on the message selected from the fix file to remedy these problems.

I'm needing to send messages one at a time from a fix file. Although, I'm not sure if this is possible since FixSession.send requires a parameters MsgType and ILooseObject and I would be providing a string. Would I need to somehow call MsgTransmitter.encoderStream directly to send a message found from a fix file?

TimelordUK commented 2 years ago

there is a lot of test code alfready that can help which is a good starting point

for example in src/test/fix-log-replay.tests

we see a helper method :--

beforeAll(async () => {
  setup = new Setup('session/test-initiator.json',null)
  await setup.init()
  definitions = setup.client.config.definitions
  expected = require(path.join(root, 'examples/FIX.4.4/fix.json'))
  views = await setup.client.getViews()   // look to see how this is done
}, 45000)
  async getViews (fix: string = 'examples/FIX.4.4/fix.txt'): Promise<MsgView[]> {
    return this.replayer.replayFixFile(path.join(root, fix))
  }
replayFixFile (replayFile: string): Promise<MsgView[]> {
    return new Promise<MsgView[]>((accept, reject) => {
      try {
        const arr: MsgView[] = []
        const transport: MsgTransport = new MsgTransport(1, this.config, new FileDuplex(replayFile))
        transport.receiver.on('msg', (msgType: string, m: MsgView) => {
          // note must clone if a view is to be saved after this call
          arr.push(m.clone())
        })
        transport.receiver.on('end', () => {
          accept(arr)
        })
        transport.receiver.on('error', (e) => {
          reject(e)
        })
      } catch (e) {
        reject(e)
      }
    })
  }

so you would

  1. add code more or less from test env to read a txt fix file and turn it into views - you probably wont be inerested in heartbeats etc so these you may filter out.
  2. each view can be turned back into an object - view.ToObject()
  3. remove the standardHeader and standardTrailer
  4. you can then just send this object down your session and it will be re-encoded with a new header and trailer
  5. you may want to add things like throttle where you only send N messages per second and filters to only send certain types of message which could be on command line

i can dip in and out for little pieces of help but at moment I am very time constrained - perhaps later on i will add to git a skeleton of the above to show how it can be done