pythonpython-asynciointeractive-brokerssanic

Keeping an always-running ib_insync connection with Sanic


I'm developing an API (using Sanic) which is a gateway to IB, using ib_insync This API exposes endpoints to place a new order and getting live positions, but also is in charge to update order statuses in a DB using the events of ib_insync.

My question is - is it possible to have the API connect only once to IB when it goes up, and re-using the same connection for all requests?

I'm currently connecting to IB using connectAsync on every request. And while this is working - the API will not receive events if not currently handling a request.

This is one endpoint code, for reference

@app.post("/order/<symbol>")
async def post_order(request, symbol):
    order = jsonpickle.decode(request.body)
    with await IB().connectAsync("127.0.0.1", 7496, clientId=100) as ib:
        ib.orderStatusEvent += onOrderStatus
        ib.errorEvent += onTWSError
        ib.newOrderEvent += onNewOrderEvent

        contract = await ib.qualifyContractsAsync(contract)
        trade = ib.placeOrder(contract[0], order)

    return text(trade.order.orderId)

So I wish to not use the with statement, and just use a global ib connection.

When I'm connecting on the module init (using connectAsync), every later call that is async, like qualifyContractsAsync, just hangs. Debugging showed me that it hangs on asyncio.gather which means I'm doing something wrong with the event loops.


Solution

  • I'm not familiar with this particular connection, but yes it should be possible. Presumably the with statement is opening and closing the connection.

    Instead, open and close it with a listener.

    Docs re: listeners

    @app.before_server_start
    async def connect(app,_):
        app.ctx.foo = await Foobar()
    
    @app.route("/")
    async def handler(request):
        await request.app.ctx.foo.do_something()
    
    @app.after_server_stop
    async def close(app,_):
        app.ctx.foo.close()