Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Parameter "SenderSubID" missing during login #69

Open
dawadam opened this issue Aug 27, 2023 · 6 comments
Open

Parameter "SenderSubID" missing during login #69

dawadam opened this issue Aug 27, 2023 · 6 comments

Comments

@dawadam
Copy link

dawadam commented Aug 27, 2023

I am trying to login to cTrader broker and "SenderSubID" parametter is needed.
I'm using a ctrader custom dictionary and adding this parameter on the json file, but it's not being sent.
And no response from the server.

json file :

{
  "application": {
    "reconnectSeconds": 10,
    "type": "initiator",
    "name": "test_ctrader",
    "tcp": {
      "host": "h51.p.ctrader.com",
      "port": 5201
    },
    "protocol": "ascii",
    "dictionary": "../../resources/FIX44-CSERVER.xml"
  },
  "BeginString": "FIX4.4",
  "Username": "123456",
  "Password": "azerty",
  "EncryptMethod": 0,
  "ResetSeqNumFlag": true,
  "HeartBtInt": 30,
  "SenderCompId": "demo.aaa.123456",
  "TargetCompID": "cServer",
  "SenderSubID": "QUOTE"
}

Application code :

import "reflect-metadata"

import {
    EngineFactory,
    SessionLauncher,
    AsciiSession,
    MsgView,
    IJsFixLogger,
    IJsFixConfig
} from 'jspurefix'

/**
 * 
 */
class FixTest extends AsciiSession {

    private readonly logger: IJsFixLogger
    private readonly fixLog: IJsFixLogger

    constructor(public readonly config: IJsFixConfig) {
        super(config)
        this.logReceivedMsgs = true

        this.fixLog = config.logFactory.plain(`jsfix.${config.description.application.name}.txt`)
        this.logger = config.logFactory.logger(`${this.me}:TradeCaptureClient`)
    }

    /**
     * 
     * @param msgType 
     * @param txt 
     */

    protected onDecoded(msgType: string, txt: string): void {
        console.log('onDecoded', msgType, txt)
        this.fixLog.info(txt)
    }

    /**
     * 
     */
    protected onEncoded(msgType: string, txt: string): void {
        console.log('onEncoded', msgType, txt)
        this.logger.info('test')
        this.fixLog.info(txt)
    }

    /**
     * 
     * @param msgType 
     * @param view 
     */
    protected onApplicationMsg(msgType: string, view: MsgView): void {
        console.log('onApplicationMsg', msgType)
        switch (msgType) {

        }
    }

    /**
     * 
     * @param view 
     */
    protected onReady(view: MsgView): void {
        console.log('READY')
    }

    /**
     * 
     * @param error 
     */
    protected onStopped(error?: Error): void {
        console.log('STOPPPED')
    }

    /**
     * 
     */
    protected onLogon(view: MsgView, user: string, password: string): boolean {
        console.log('LOGON')

        return true
    }

}


/**
 * 
 */
class AppLauncher extends SessionLauncher {
    public constructor(client: string = '../../jspurefix-test-initiator.json') {
        super(client)
        this.root = __dirname
    }

    protected override makeFactory(config: IJsFixConfig): EngineFactory {
        return {
            makeSession: () => new FixTest(config)
        } as EngineFactory
    }
}

const l = new AppLauncher()
l.exec()

Logon fix message :

8=FIX4.4|9=0000105|35=A|49=demo.aaa.123456|56=cServer|34=1|52=20230827-16:38:22.081|98=0|108=30|141=Y|553=123456|554=azerty|10=176|

No SenderSubID parameter, identifier is 50

How add it in login message ?

@TimelordUK
Copy link
Owner

please see jspf-md-demo project

you need to install a custom msg factory

class MySessionContainer extends SessionContainer {
  protected makeSessionFactory (description: ISessionDescription): ISessionMsgFactory {
    return new MsgFact(description)
  }
}

you will need to add new property here which constructs a header object based on json properties

 public header (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
    const o: IStandardHeader = {
      BeginString: description.BeginString,
      BodyLength: placeHolder,
      MsgType: msgType,
      SenderCompID: description.SenderCompId,
     // e.g.  SenderSubID: 'QUOTE', 
      MsgSeqNum: seqNum,
      SendingTime: time,
      TargetCompID: description.TargetCompID,
      TargetSubID: description.TargetSubID,
      ...overrideData
    }
    return this.mutate(o, 'StandardHeader')
  }

@TimelordUK
Copy link
Owner

it is a bug in sense the base class should send this field but its missing and this is only way to override at the moment - i will patch at some point soon such that the default implementtion will take care of it

@dawadam
Copy link
Author

dawadam commented Aug 27, 2023

Ok, but ILogon don't include SenderSubID property.

export interface ILogon {
  StandardHeader: IStandardHeader
  EncryptMethod: number// 98
  HeartBtInt: number// 108
  RawDataLength?: number// 95
  RawData?: Buffer// 96
  ResetSeqNumFlag?: boolean// 141
  NextExpectedMsgSeqNum?: number// 789
  MaxMessageSize?: number// 383
  MsgTypeGrp?: IMsgTypeGrp[]
  TestMessageIndicator?: boolean// 464
  Username?: string// 553
  Password?: string// 554
  NewPassword?: string// 925
  EncryptedPasswordMethod?: number// 1400
  EncryptedPasswordLen?: number// 1401
  EncryptedPassword?: Buffer// 1402
  EncryptedNewPasswordLen?: number// 1403
  EncryptedNewPassword?: Buffer// 1404
  SessionStatus?: number// 1409
  DefaultApplVerID: string// 1137
  DefaultApplExtID?: number// 1407
  DefaultCstmApplVerID?: string// 1408
  Text?: string// 58
  EncodedTextLen?: number// 354
  EncodedText?: Buffer// 355
  StandardTrailer: IStandardTrailer
}

@dawadam
Copy link
Author

dawadam commented Aug 27, 2023

It doesn't seem to work, here is the complete code.

jspurefix_msg-fact.ts

import { ISessionDescription, MsgType, ASessionMsgFactory } from 'jspurefix'
import { ILogout } from 'jspurefix/dist/types/FIX4.4/cserver/logout'
import { ILooseObject } from 'jspurefix/dist/collections/collection'
import { EncryptMethod, ILogon, IStandardHeader } from 'jspurefix/dist/types/FIX4.4/cserver'


/**
 * 
 */
export class MsgFact extends ASessionMsgFactory {
  constructor(readonly description: ISessionDescription) {
    super(description, (_description: ISessionDescription, _type: string, o: ILooseObject) => o)
    this.isAscii = description?.application?.protocol === 'ascii'
  }

  /**
   * 
   */
  public logon(): ILooseObject {
    const description = this.description

    const o: ILogon = {
      Username: description.Username,
      Password: description.Password,
      HeartBtInt: description.HeartBtInt,
      ResetSeqNumFlag: description.ResetSeqNumFlag,
      // @ts-ignore
      SenderSubID: description.SenderSubID,
      EncryptMethod: EncryptMethod.NoneOther,
      StandardHeader: undefined,
      StandardTrailer: undefined
    }
    return this.mutate(o, MsgType.Logon)
  }


  /**
   * 
   */
  public logout(text: string): ILooseObject {
    const o: ILogout = {
      Text: text,
      StandardHeader: undefined,
      StandardTrailer: undefined
    }
    return this.mutate(o, MsgType.Logout)
  }


  /**
   * 
   */
  public header(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
    const o: IStandardHeader = {
      BeginString: description.BeginString,
      BodyLength: placeHolder,
      MsgType: msgType,
      SenderCompID: description.SenderCompId,
      MsgSeqNum: seqNum,
      SendingTime: time,
      TargetCompID: description.TargetCompID,
      TargetSubID: description.TargetSubID,
      ...overrideData
    }
    return this.mutate(o, 'StandardHeader')
  }
}

jspurefix.ts

import "reflect-metadata"

import {
    EngineFactory,
    SessionLauncher,
    AsciiSession,
    MsgView,
    IJsFixLogger,
    IJsFixConfig,
    SessionContainer,
    ISessionDescription,
    ISessionMsgFactory
} from 'jspurefix'

import { MsgFact } from './jspurefix_msg-fact'




/**
 * 
 */
class FixTest extends AsciiSession {

    private readonly logger: IJsFixLogger
    private readonly fixLog: IJsFixLogger




    constructor(public readonly config: IJsFixConfig) {
        super(config)

        this.logReceivedMsgs = true

        this.fixLog = config.logFactory.plain(`jsfix.${config.description.application.name}.txt`)
        this.logger = config.logFactory.logger(`${this.me}:TradeCaptureClient`)
    }



    /**
     * 
     * @param msgType 
     * @param txt 
     */

    protected onDecoded(msgType: string, txt: string): void {
        console.log('onDecoded', msgType, txt)
        this.fixLog.info(txt)
    }

    /**
     * 
     */
    protected onEncoded(msgType: string, txt: string): void {
        console.log('onEncoded', msgType, txt)
        this.logger.info('test')
        this.fixLog.info(txt)
    }

    /**
     * 
     * @param msgType 
     * @param view 
     */
    protected onApplicationMsg(msgType: string, view: MsgView): void {
        console.log('onApplicationMsg', msgType)
        switch (msgType) {

        }
    }

    /**
     * 
     * @param view 
     */
    protected onReady(view: MsgView): void {
        console.log('READY')
    }

    /**
     * 
     * @param error 
     */
    protected onStopped(error?: Error): void {
        console.log('STOPPPED')
    }

    /**
     * 
     */
    protected onLogon(view: MsgView, user: string, password: string): boolean {
        console.log('LOGON')

        return true
    }



}


/**
 * 
 */
class MySessionContainer extends SessionContainer {
    protected makeSessionFactory(description: ISessionDescription): ISessionMsgFactory {
        return new MsgFact(description)
    }
}

/**
 * 
 */
class AppLauncher extends SessionLauncher {
    public constructor(client: string = '../../jspurefix-test-initiator.json') {
        super(client)
        this.sessionContainer = new MySessionContainer()
        this.root = __dirname
    }



    protected override makeFactory(config: IJsFixConfig): EngineFactory {
        return {
            makeSession: () => new FixTest(config)
        } as EngineFactory
    }
}

const l = new AppLauncher()
l.exec()

jspurefix-test-initiator.json

{
  "application": {
    "reconnectSeconds": 10,
    "type": "initiator",
    "name": "test_ctrader",
    "tcp": {
      "host": "h51.p.ctrader.com",
      "port": 5201
    },
    "protocol": "ascii",
    "dictionary": "../../resources/FIX44-CSERVER.xml"
  },
  "BeginString": "FIX4.4",
  "Username": "123",
  "Password": "abc",
  "EncryptMethod": 0,
  "ResetSeqNumFlag": true,
  "HeartBtInt": 30,
  "SenderCompId": "demo.icmarkets.123",
  "TargetCompID": "cServer",
  "SenderSubID": "QUOTE"
}

I think i will be wait the patch.
Code is big for just connection.
Do you have free time for that right now? :)

@TimelordUK
Copy link
Owner

the field does not go in the logon message - it should be in the header

 public header(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
    const o: IStandardHeader = {
      BeginString: description.BeginString,
      BodyLength: placeHolder,
      MsgType: msgType,
      SenderCompID: description.SenderCompId,
      MsgSeqNum: seqNum,
      SendingTime: time,
      TargetCompID: description.TargetCompID,
      TargetSubID: description.TargetSubID,
      ...overrideData
    }
    return this.mutate(o, 'StandardHeader')
  }

@dawadam
Copy link
Author

dawadam commented Aug 27, 2023

Thanks, it works, the FIX message is correct.

8=FIX4.4|9=0000122|35=A|49=demo.icmarkets.123|56=cServer|34=1|50=QUOTE|52=20230827-20:51:43.527|98=0|108=30|141=Y|553=8665823|554=abc|10=000|

No response from the server, but that must be another issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants