erdewit / ib_insync

Python sync/async framework for Interactive Brokers API
BSD 2-Clause "Simplified" License
2.76k stars 726 forks source link

Commission repost is not emitted #155

Closed eric0470 closed 5 years ago

eric0470 commented 5 years ago

Hi, I found problem with commission report.

Connector class:

import os
import logging
import asyncio
import traceback

from ib_insync import IB, Future, Stock, Option, FuturesOption
from ib_insync import util as ib_util

class IBConnection:
    MAX_RETRY_INTERVAL = 20

    def __init__(self):
        self.host = '192.168.1.5'
        self.port = 4002
        self.client_id = 1
        self.ib = IB()
        self.append_event_handlers()
        self._retry_interval = 1
        self._is_connected = False
        self._is_reconnecting = False
        self._is_run = False

        # Patch async
        ib_util.patchAsyncio()

    async def close(self):
        self._is_run = False
        await self.disconnect()

    def is_connected(self):
        return self._is_connected

    def place_order(self, contract, order):
        trade = self.ib.placeOrder(contract, order)
        return trade

    async def disconnect(self):
        if self.is_connected():
            self._is_connected = False
            self.ib.disconnect()
            await asyncio.sleep(5)
            print('TWS: Disconnect form TWS')

    async def connect(self):
        try:
            self._is_run = True

            if not self.is_connected():
                print(f'TWS: Begin to connect with IB on "{self.host}:{self.port}" with CLIENT ID {self.client_id}')
                await self.ib.connectAsync(self.host, self.port, self.client_id, 10)
                print(f'TWS: Exit From connectAsync func')

            return True
        except:  # noqa
            print(traceback.format_exc())
            self.on_tws_disconnect()
            return False

    async def reconnect(self):
        try:
            self._is_reconnecting = True
            print(f'TWS: Reconnect start "{self.host}:{self.port}" with CLIENT ID {self.client_id}')
            await self.disconnect()
            await asyncio.sleep(60)

            while not self.is_connected():
                is_connected = await self.connect()

                if not is_connected:
                    print(f'TWS: Connecting to TWS failed. Retrying in {self._retry_interval} seconds')
                    await asyncio.sleep(self._retry_interval)
                    self._increase_retry_interval()
        except:  # noqa
            print(traceback.format_exc())

    def on_tws_connected(self):
        self._is_connected = True
        self._is_reconnecting = False
        self._reset_retry_interval()
        print(f'Connected to TWS')

    def on_tws_disconnect(self):
        if not self._is_reconnecting and self._is_run:
            asyncio.ensure_future(self.reconnect())

    def append_event_handlers(self):
        self.ib.connectedEvent += self.on_tws_connected
        self.ib.disconnectedEvent += self.on_tws_disconnect
        self.ib.commissionReportEvent += self.on_commission

    def on_commission(self, trade, fill, report):
        print(fill, end='\n\n')

    def _reset_retry_interval(self):
        self._retry_interval = 1

    def _increase_retry_interval(self):
        self._retry_interval = min(self.MAX_RETRY_INTERVAL, 2 * self._retry_interval)

main.py

import asyncio
import traceback
from ib_insync import IB, Future, MarketOrder
from ib_connections import IBConnection

async def main_test():
    try:
        ib = IBConnection()
        await ib.connect()

        # Make orders
        contract = Future('ES', '20190621', 'GLOBEX')
        buy_order = MarketOrder('BUY', 5)
        ib.place_order(contract, buy_order)
        await asyncio.sleep(5)

        sell_order = MarketOrder('SELL', 5)
        ib.place_order(contract, sell_order)
        await asyncio.sleep(5)

        await ib.close()
    except:  # noqa
        print(traceback.format_exc())

if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main_test())
    loop.close()

New order created in main_test were not caught in on_commission. What i am doing wrong here?

ib_insync 0.9.52 TWS Build 976.2k

hadrianl commented 5 years ago

r u sure about order being submitted? it seems that u did not qualifyContracts?

eric0470 commented 5 years ago

r u sure about order being submitted? it seems that u did not qualifyContracts?

Yep, orders were submitted and filled. You can try to run the code by yourself to see the problem here.

eric0470 commented 5 years ago

Simpler version with same problem:

import asyncio
import traceback
from ib_insync import IB, Future, MarketOrder

def on_commission(trade, fill, report):
    print('Commission', fill, end='\n\n')

def order_exec(trade, fill):
    print('Order Exec:', fill, end='\n\n')

async def default_test():
    try:
        ib = IB()
        ib.commissionReportEvent += on_commission
        ib.execDetailsEvent += order_exec
        await ib.connectAsync('192.168.1.5', 4002, 1)
        print('CONNECTED')

        # Make orders
        contract = Future('ES', '20190621', 'GLOBEX')
        buy_order = MarketOrder('BUY', 5)
        ib.placeOrder(contract, buy_order)
        await asyncio.sleep(3)

        sell_order = MarketOrder('SELL', 5)
        ib.placeOrder(contract, sell_order)
        await asyncio.sleep(3)

        ib.disconnect()
        await asyncio.sleep(2)
    except:  # noqa
        print(traceback.format_exc())

if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    loop.run_until_complete(default_test())
    loop.close()
erdewit commented 5 years ago

Thank you for the bug report and the test case to reproduce it. The problem was a regression introduced one month ago with the addition of reqCompletedOrders.

It has been fixed in v0.9.53.