stoqey / ib

Interactive Brokers TWS/IB Gateway API client library for Node.js (TS)
https://stoqey.github.io/ib-doc/
MIT License
210 stars 48 forks source link

Bug when using cancelPositionsMulti #219

Closed mafianekcek closed 4 months ago

mafianekcek commented 4 months ago

Hello, I just found a bug when using cancelPositionsMulti (with Advisory demo account). I am not sure where exactly the problem is (thats why I am writing this topic) but basically it doesnt work and after using this command the whole API breaks (sending random messages to the API etc..)

Here is the script which you can use to reproduce this bug: test.js:

const { IBApi, EventName } = require("@stoqey/ib")

const ib = new IBApi({
    clientId: 1,
    host: "127.0.0.1",
    port: 7497,
})

ib.on(EventName.info, (msg, code) => {
    console.log("INFO", code, msg)
})

ib.on(EventName.error, (err, code, reqId) => {
    console.error("ERROR", reqId, code, err.message)
})

ib.on(EventName.nextValidId, (id) => {
    console.log("nextValidId", id)
})

ib.on(EventName.positionMulti, (reqId, account, modelCode, contract, pos, avgCost) => {
    console.log("positionMulti", reqId, account, modelCode, JSON.stringify(contract), pos)
})

ib.on(EventName.positionMultiEnd, (reqId) => {
    console.log("positionMultiEnd", reqId)

})

ib.connect()

async function delay(ms) {
    const res = await new Promise((resolve, reject) => { setTimeout(() => { return resolve(true) }, ms) })
    return res
}

setTimeout(async() => {
    console.log("reqPositionsMulti 1")
    ib.reqPositionsMulti(1, "DU010", "")

    console.log("Waiting 5 sec...")
    await delay(5000)

    console.log("cancelPositionsMulti 1")
    ib.cancelPositionsMulti(1)

    console.log("reqPositionsMulti 2")
    ib.reqPositionsMulti(2, "DU010", "")

    console.log("Exiting in 5 sec...")
    await delay(5000)
    ib.disconnect()
}, 1000)

test.js output:

nextValidId 1
INFO 2104 Market data farm connection is OK:usfuture
INFO 2104 Market data farm connection is OK:eufarm
INFO 2107 HMDS data farm connection is inactive but should be available upon demand.euhmds
INFO 2158 Sec-def data farm connection is OK:secdefeu
reqPositionsMulti 1
Waiting 5 sec...
positionMulti 1 DU010  {"conId":637533398,"symbol":"MES","secType":"FUT","lastTradeDateOrContractMonth":"20240920","strike":0,"multiplier":5,"exchange":"","currency":"USD","localSymbol":"MESU4","tradingClass":"MES"} 2
positionMultiEnd 1
cancelPositionsMulti 1
reqPositionsMulti 2
Exiting in 5 sec...
ERROR 1 321 Error validating request.-'bj' : cause - Account/Group '74' is incorrect. Valid accounts: ''; 'DF01A'; 'DF01'; 'DU010'; 'DU011'; 'DU012';  Valid groups: ''; 'All'; 'test_group_1';
INFO 320 Error reading request. Unable to parse data. java.lang.NumberFormatException: For input string: "DU010"

The behavior when using the official Python API client is normal, here you can see the script used and the output.

test.py:

import asyncio
from ibapi.client import EClient
from ibapi.wrapper import EWrapper

class IBApiWrapper(EWrapper):
    def __init__(self):
        super().__init__()

class IBApiClient(EClient):
    def __init__(self, wrapper):
        EClient.__init__(self, wrapper)

class IBApiApp(IBApiWrapper, IBApiClient):
    def __init__(self):
        IBApiWrapper.__init__(self)
        IBApiClient.__init__(self, wrapper=self)

    def nextValidId(self, orderId: int):
        super().nextValidId(orderId)
        self.nextOrderId = orderId
        print(f"Next valid order ID: {orderId}")

    def positionMulti(self, reqId, account, modelCode, contract, pos, avgCost):
       print("PositionMulti. reqId:", reqId, "Account:", account, "ModelCode:", modelCode, "Contract:", contract, ",Position:", pos, "AvgCost:", avgCost)         

    def positionMultiEnd(self, reqId):
        print("PositionMultiEnd. reqId:", reqId)  

async def run_loop(app):
    while True:
        app.run()

async def main():
    app = IBApiApp()
    app.connect('127.0.0.1', 7497, clientId=1)

    loop = asyncio.get_event_loop()
    loop.run_in_executor(None, app.run)

    await asyncio.sleep(1)

    print("reqPositionsMulti 1")
    app.reqPositionsMulti(1, "DU010", "")

    print("Waiting 5 sec...")
    await asyncio.sleep(5)

    print("cancelPositionsMulti 1")
    app.cancelPositionsMulti(1)

    print("reqPositionsMulti 2")
    app.reqPositionsMulti(2, "DU010", "")

    print("Exiting in 5 sec...")
    await asyncio.sleep(5)
    app.disconnect()

asyncio.run(main())

test.py output:

Next valid order ID: 1
ERROR -1 2104 Market data farm connection is OK:usfuture
ERROR -1 2104 Market data farm connection is OK:eufarm
ERROR -1 2107 HMDS data farm connection is inactive but should be available upon demand.euhmds
ERROR -1 2158 Sec-def data farm connection is OK:secdefeu
reqPositionsMulti 1
Waiting 5 sec...
PositionMulti. reqId: 1 Account: DU010 ModelCode:  Contract: 637533398,MES,FUT,20240920,0,,5,,,USD,MESU4,MES,False,,,,combo: ,Position: 2 AvgCost: 28216.87
PositionMultiEnd. reqId: 1
cancelPositionsMulti 1
reqPositionsMulti 2
Exiting in 5 sec...
PositionMulti. reqId: 2 Account: DU010 ModelCode:  Contract: 637533398,MES,FUT,20240920,0,,5,,,USD,MESU4,MES,False,,,,combo: ,Position: 2 AvgCost: 28216.87
PositionMultiEnd. reqId: 2

Could you please check this issue? Thank you <3

rylorin commented 4 months ago

Hello, Thanks for your documented bug report. I've been able to reproduce the problem; but at the moment I don't see any reason for this as those API calls implementation are quite basic. I will look at this deeper when I have some free time. Regards

rylorin commented 4 months ago

src/tests/unit/api/positions.test.ts

  console.log
    positionMultiEnd 45

      at IBApi.<anonymous> (src/tests/unit/api/positions.test.ts:96:17)
          at Array.forEach (<anonymous>)

  console.log
    cancelPositionsMulti sent 46

      at IBApi.<anonymous> (src/tests/unit/api/positions.test.ts:99:17)
          at Array.forEach (<anonymous>)

  console.log
    reqPositionsMulti sent 47

      at IBApi.<anonymous> (src/tests/unit/api/positions.test.ts:102:17)
          at Array.forEach (<anonymous>)

 FAIL  src/tests/unit/api/positions.test.ts
  IBApi Tests
    ✓ Test reqPositions / cancelPositions (86 ms)
    ✕ Test reqPositionsMulti / cancelPositionsMulti (62 ms)

  ● IBApi Tests › Test reqPositionsMulti / cancelPositionsMulti

    Failed: "[46] Error validating request.-'bj' : cause - Model name '1' is incorrect. Valid model name: ''; 'Core';  (#321)"
rylorin commented 4 months ago

I got it! :) I will publish a fix in the next few days

rylorin commented 4 months ago

Fixed in 1.3.26