Example #1
0
while x < len(openOrdersList):
    #symbol, orderId, orderType, action, quantity, status, date_order, faProfile, parentId, avgFillPrice, account = parseTradeString(ib,openOrdersList[x])
    #validatedOpenOrders = validateOpenOrdersCSV(ib, orderId, status)
    #log.info("closeOpenOrder: - we have open order records: opendOrderId: {ooi} orderType: {ot} ".format(ooi=orderId, ot=orderType))
    print(" blank ")
    #print("closeOpenOrder: closing all open orders - currently working on: ",openOrdersList[x])
    #trademkt = ib.cancelOrder(openOrdersList[x])            # don't need to place order when cancelling
    #print("\n----------------------- openOrdersList ---------------\n",openOrdersList[x])
    print("----------------------- TRADEMKT ---------------: ", openOrdersList)
    #validatedOpenOrders = validateOpenOrdersCSV(ib, orderId, status)
    #orderId, orderType, action, quantity = orders.parseOrderString(ib,openOrdersList)
    #checkOrderStatus = updateCanceledOpenOrders(ib, orderId, trademkt)   # update each order that we cancelled
    print("action ", openOrdersList[x].action)
    if openOrdersList[x].action == "BUY" and openOrdersList[
            x].orderType == "STP":
        openOrdersList[x].auxPrice = 2787.50
        print("new auxPrice", openOrdersList[x].auxPrice)
        openOrder = openOrdersList[x]
        print("type openOrder ", type(openOrder))
        print("")
        #print("type contract ",type(contract))
        print("")
        print("open Order ", openOrder)
        ib.placeOrder(tradeContract, openOrdersList[x])
        #openOrdersList
        print("open orders", openOrdersList)
        print("open orders", ib.openTrades())
        print("open trades ", ib.trades())
        print("orders ", ib.orders())
    x += 1
    print(" x is ", x)
Example #2
0
class IBStore(with_metaclass(MetaSingleton, object)):
    '''Singleton class wrapping an ibpy ibConnection instance.

    The parameters can also be specified in the classes which use this store,
    like ``IBData`` and ``IBBroker``

    Params:

      - ``host`` (default:``127.0.0.1``): where IB TWS or IB Gateway are
        actually running. And although this will usually be the localhost, it
        must not be

      - ``port`` (default: ``7496``): port to connect to. The demo system uses
        ``7497``

      - ``clientId`` (default: ``None``): which clientId to use to connect to
        TWS.

        ``None``: generates a random id between 1 and 65535
        An ``integer``: will be passed as the value to use.

      - ``notifyall`` (default: ``False``)

        If ``False`` only ``error`` messages will be sent to the
        ``notify_store`` methods of ``Cerebro`` and ``Strategy``.

        If ``True``, each and every message received from TWS will be notified

      - ``_debug`` (default: ``False``)

        Print all messages received from TWS to standard output

      - ``reconnect`` (default: ``3``)

        Number of attempts to try to reconnect after the 1st connection attempt
        fails

        Set it to a ``-1`` value to keep on reconnecting forever

      - ``timeout`` (default: ``3.0``)

        Time in seconds between reconnection attemps

      - ``timeoffset`` (default: ``True``)

        If True, the time obtained from ``reqCurrentTime`` (IB Server time)
        will be used to calculate the offset to localtime and this offset will
        be used for the price notifications (tickPrice events, for example for
        CASH markets) to modify the locally calculated timestamp.

        The time offset will propagate to other parts of the ``backtrader``
        ecosystem like the **resampling** to align resampling timestamps using
        the calculated offset.

      - ``timerefresh`` (default: ``60.0``)

        Time in seconds: how often the time offset has to be refreshed

      - ``indcash`` (default: ``True``)

        Manage IND codes as if they were cash for price retrieval
    '''

    # Set a base for the data requests (historical/realtime) to distinguish the
    # id in the error notifications from orders, where the basis (usually
    # starting at 1) is set by TWS
    REQIDBASE = 0x01000000

    BrokerCls = None #getattr(sys.modules["cerebro.strategies." +classname.split('.')[0]], classname.split('.')[1])IBBroker  #None  # broker class will autoregister
    DataCls = None  # data class will auto register

    params = (
        ('host', '127.0.0.1'),
        ('port', 7496),
        ('clientId', None),  # None generates a random clientid 1 -> 2^16
        ('notifyall', False), # NOT IMPLEMENTED
        ('_debug', False),
        ('reconnect', 3),  # -1 forever, 0 No, > 0 number of retries
        ('timeout', 3.0),  # timeout between reconnections
        ('timeoffset', True),  # Use offset to server for timestamps if needed
        ('timerefresh', 60.0),  # How often to refresh the timeoffset
        ('indcash', True),  # Treat IND codes as CASH elements
        ('readonly', False),  # Set to True when IB API is in read-only mode
        ('account', ''),  # Main account to receive updates for
     
    )

    @classmethod
    def getdata(cls, *args, **kwargs):
        '''Returns ``DataCls`` with args, kwargs'''
        return cls.DataCls(*args, **kwargs)

    @classmethod
    def getbroker(cls, *args, **kwargs):
        '''Returns broker with *args, **kwargs from registered ``BrokerCls``'''
        return cls.BrokerCls(*args, **kwargs)

    def __init__(self):
        super(IBStore, self).__init__()

        self._env = None  # reference to cerebro for general notifications
        self.broker = None  # broker instance
        self.datas = list()  # datas that have registered over start
        # self.ccount = 0  # requests to start (from cerebro or datas)

        # self._lock_tmoffset = threading.Lock()
        # self.tmoffset = timedelta()  # to control time difference with server

        # # Structures to hold datas requests
        # self.qs = collections.OrderedDict()  # key: tickerId -> queues
        # self.ts = collections.OrderedDict()  # key: queue -> tickerId
        self.iscash = dict()  # tickerIds from cash products (for ex: EUR.JPY)

        self.acc_cash = AutoDict()  # current total cash per account
        self.acc_value = AutoDict()  # current total value per account
        self.acc_upds = AutoDict()  # current account valueinfos per account

        self.positions = collections.defaultdict(Position)  # actual positions

        self.orderid = None  # next possible orderid (will be itertools.count)

        self.managed_accounts = list()  # received via managedAccounts
        self.notifs = queue.Queue()  # store notifications for cerebro
        self.orders = collections.OrderedDict()  # orders by order ided

        self.opending = collections.defaultdict(list)  # pending transmission
        self.brackets = dict()  # confirmed brackets
        
        self.last_tick = None

        # Use the provided clientId or a random one
        if self.p.clientId is None:
            self.clientId = random.randint(1, pow(2, 16) - 1)
        else:
            self.clientId = self.p.clientId

        if self.p.timeout is None:
            self.timeout = 2
        else:
            self.timeout = self.p.timeout

        if self.p.readonly is None:
            self.readonly = False
        else:
            self.readonly = self.p.readonly

        if self.p.account is None:
            self.account = ""
        else:
            self.account = self.p.account
                    
        if self.p._debug:
            util.logToConsole(level=logging.DEBUG)
        
        util.patchAsyncio()
        util.startLoop()
        
        self.ib = IB()
        
        self.ib.connect(
                    host=self.p.host, port=self.p.port, 
                    clientId=self.clientId,
                    timeout=self.timeout,
                    readonly=self.readonly,
                    account=self.account,
                    )
        
        # This utility key function transforms a barsize into a:
        #   (Timeframe, Compression) tuple which can be sorted
        def keyfn(x):
            n, t = x.split()
            tf, comp = self._sizes[t]
            return (tf, int(n) * comp)

        # This utility key function transforms a duration into a:
        #   (Timeframe, Compression) tuple which can be sorted
        def key2fn(x):
            n, d = x.split()
            tf = self._dur2tf[d]
            return (tf, int(n))

        # Generate a table of reverse durations
        self.revdur = collections.defaultdict(list)
        # The table (dict) is a ONE to MANY relation of
        #   duration -> barsizes
        # Here it is reversed to get a ONE to MANY relation of
        #   barsize -> durations
        for duration, barsizes in self._durations.items():
            for barsize in barsizes:
                self.revdur[keyfn(barsize)].append(duration)

        # Once managed, sort the durations according to real duration and not
        # to the text form using the utility key above
        for barsize in self.revdur:
            self.revdur[barsize].sort(key=key2fn)

    def start(self, data=None, broker=None):
        #self.reconnect(fromstart=True)  # reconnect should be an invariant

        # Datas require some processing to kickstart data reception
        if data is not None:
            self._env = data._env
            # For datas simulate a queue with None to kickstart co
            self.datas.append(data)

            # if connection fails, get a fakeation that will force the
            # datas to try to reconnect or else bail out
            return self.getTickerQueue(start=True)

        elif broker is not None:
            self.broker = broker

    def stop(self):
        try:
            self.ib.disconnect()  # disconnect should be an invariant
        except AttributeError:
            pass    # conn may have never been connected and lack "disconnect"

    def get_notifications(self):
        '''Return the pending "store" notifications'''
        # The background thread could keep on adding notifications. The None
        # mark allows to identify which is the last notification to deliver
        self.notifs.put(None)  # put a mark
        notifs = list()
        while True:
            notif = self.notifs.get()
            if notif is None:  # mark is reached
                break
            notifs.append(notif)

        return notifs

    def managedAccounts(self):
        # 1st message in the stream
        self.managed_accounts = self.ib.managedAccounts()

        # Request time to avoid synchronization issues
        self.reqCurrentTime()

    def currentTime(self,msg):
        if not self.p.timeoffset:  # only if requested ... apply timeoffset
            return
        curtime = datetime.fromtimestamp(float(msg.time))
        with self._lock_tmoffset:
            self.tmoffset = curtime - datetime.now()

        threading.Timer(self.p.timerefresh, self.reqCurrentTime).start()

    def timeoffset(self):
        with self._lock_tmoffset:
            return self.tmoffset

    def reqCurrentTime(self):
        self.ib.reqCurrentTime()

    def nextOrderId(self):
        # Get the next ticker using a new request value from TWS
        self.orderid = self.ib.client.getReqId()
        return self.orderid

    def getTickerQueue(self, start=False):
        '''Creates ticker/Queue for data delivery to a data feed'''
        q = queue.Queue()
        if start:
            q.put(None)
            return q
            
        return q
    
    def getContractDetails(self, contract, maxcount=None):
        #cds = list()
        cds = self.ib.reqContractDetails(contract)
 
        #cds.append(cd)

        if not cds or (maxcount and len(cds) > maxcount):
            err = 'Ambiguous contract: none/multiple answers received'
            self.notifs.put((err, cds, {}))
            return None

        return cds

    def reqHistoricalDataEx(self, contract, enddate, begindate,
                            timeframe, compression,
                            what=None, useRTH=False, tz='', 
                            sessionend=None,
                            #tickerId=None
                            ):
        '''
        Extension of the raw reqHistoricalData proxy, which takes two dates
        rather than a duration, barsize and date

        It uses the IB published valid duration/barsizes to make a mapping and
        spread a historical request over several historical requests if needed
        '''
        # Keep a copy for error reporting purposes
        kwargs = locals().copy()
        kwargs.pop('self', None)  # remove self, no need to report it

        if timeframe < TimeFrame.Seconds:
            # Ticks are not supported
            return self.getTickerQueue(start=True)

        if enddate is None:
            enddate = datetime.now()

        if begindate is None:
            duration = self.getmaxduration(timeframe, compression)
            if duration is None:
                err = ('No duration for historical data request for '
                       'timeframe/compresison')
                self.notifs.put((err, (), kwargs))
                return self.getTickerQueue(start=True)
            barsize = self.tfcomp_to_size(timeframe, compression)
            if barsize is None:
                err = ('No supported barsize for historical data request for '
                       'timeframe/compresison')
                self.notifs.put((err, (), kwargs))
                return self.getTickerQueue(start=True)

            return self.reqHistoricalData(contract=contract, enddate=enddate,
                                          duration=duration, barsize=barsize,
                                          what=what, useRTH=useRTH, tz=tz,
                                          sessionend=sessionend)

        # Check if the requested timeframe/compression is supported by IB
        durations = self.getdurations(timeframe, compression)
        # if not durations:  # return a queue and put a None in it
        #     return self.getTickerQueue(start=True)

        # Get or reuse a queue
        # if tickerId is None:
        #     tickerId, q = self.getTickerQueue()
        # else:
        #     tickerId, q = self.reuseQueue(tickerId)  # reuse q for old tickerId

        # Get the best possible duration to reduce number of requests
        duration = None
        # for dur in durations:
        #     intdate = self.dt_plus_duration(begindate, dur)
        #     if intdate >= enddate:
        #         intdate = enddate
        #         duration = dur  # begin -> end fits in single request
        #         break
        intdate = begindate

        if duration is None:  # no duration large enough to fit the request
            duration = durations[-1]

            # Store the calculated data
            # self.histexreq[tickerId] = dict(
            #     contract=contract, enddate=enddate, begindate=intdate,
            #     timeframe=timeframe, compression=compression,
            #     what=what, useRTH=useRTH, tz=tz, sessionend=sessionend)

        barsize = self.tfcomp_to_size(timeframe, compression)

        if contract.secType in ['CASH', 'CFD']:
            #self.iscash[tickerId] = 1  # msg.field code
            if not what:
                what = 'BID'  # default for cash unless otherwise specified

        elif contract.secType in ['IND'] and self.p.indcash:
            #self.iscash[tickerId] = 4  # msg.field code
            pass

        what = what or 'TRADES'
        
        q = self.getTickerQueue()

        histdata = self.ib.reqHistoricalData(
                                contract,
                                intdate.strftime('%Y%m%d %H:%M:%S') + ' GMT',
                                duration,
                                barsize,
                                what,
                                useRTH,
                                2) # dateformat 1 for string, 2 for unix time in seconds
        for msg in histdata:
            q.put(msg)
        
        return q

    def reqHistoricalData(self, contract, enddate, duration, barsize,
                          what=None, useRTH=False, tz='', sessionend=None):
        '''Proxy to reqHistorical Data'''

        # get a ticker/queue for identification/data delivery
        q = self.getTickerQueue()

        if contract.secType in ['CASH', 'CFD']:
            #self.iscash[tickerId] = True
            if not what:
                what = 'BID'  # TRADES doesn't work
            elif what == 'ASK':
                #self.iscash[tickerId] = 2
                pass
        else:
            what = what or 'TRADES'

        # split barsize "x time", look in sizes for (tf, comp) get tf
        #tframe = self._sizes[barsize.split()[1]][0]
        # self.histfmt[tickerId] = tframe >= TimeFrame.Days
        # self.histsend[tickerId] = sessionend
        # self.histtz[tickerId] = tz

        histdata = self.ib.reqHistoricalData(
                            contract,
                            enddate.strftime('%Y%m%d %H:%M:%S') + ' GMT',
                            duration,
                            barsize,
                            what,
                            useRTH,
                            2) # dateformat 1 for string, 2 for unix time in seconds
        for msg in histdata:
            q.put(msg)

        return q

    def reqRealTimeBars(self, contract, useRTH=False, duration=5):
        '''Creates a request for (5 seconds) Real Time Bars

        Params:
          - contract: a ib.ext.Contract.Contract intance
          - useRTH: (default: False) passed to TWS
          - duration: (default: 5) passed to TWS

        Returns:
          - a Queue the client can wait on to receive a RTVolume instance
        '''
        # get a ticker/queue for identification/data delivery
        q = self.getTickerQueue()

        rtb = self.ib.reqRealTimeBars(contract, duration, 
                                      'MIDPOINT', useRTH=useRTH)
        self.ib.sleep(duration)
        for bar in rtb:
            q.put(bar)
        return q

    def reqMktData(self, contract, what=None):
        '''Creates a MarketData subscription

        Params:
          - contract: a ib.ext.Contract.Contract intance

        Returns:
          - a Queue the client can wait on to receive a RTVolume instance
        '''
        # get a ticker/queue for identification/data delivery
        q = self.getTickerQueue()
        ticks = '233'  # request RTVOLUME tick delivered over tickString

        if contract.secType in ['CASH', 'CFD']:
            #self.iscash[tickerId] = True
            ticks = ''  # cash markets do not get RTVOLUME
            if what == 'ASK':
                #self.iscash[tickerId] = 2
                pass

        # q.put(None)  # to kickstart backfilling
        # Can request 233 also for cash ... nothing will arrive
        
        md = MktData()
        q_ticks = queue.Queue()
        
        util.run(md.update_ticks(self.ib, contract, ticks, q_ticks))
                  
        while not q_ticks.empty():
            ticker = q_ticks.get()
            for tick in ticker.ticks:
                # https://interactivebrokers.github.io/tws-api/tick_types.html
                if tick != self.last_tick: #last price
                    #print(str(tick.time) +" >> " + str(tick.price))
                    self.last_tick = tick
                    q.put(tick)

        return q

    # The _durations are meant to calculate the needed historical data to
    # perform backfilling at the start of a connetion or a connection is lost.
    # Using a timedelta as a key allows to quickly find out which bar size
    # bar size (values in the tuples int the dict) can be used.

    _durations = dict([
        # 60 seconds - 1 min
        ('60 S',
         ('1 secs', '5 secs', '10 secs', '15 secs', '30 secs',
          '1 min')),

        # 120 seconds - 2 mins
        ('120 S',
         ('1 secs', '5 secs', '10 secs', '15 secs', '30 secs',
          '1 min', '2 mins')),

        # 180 seconds - 3 mins
        ('180 S',
         ('1 secs', '5 secs', '10 secs', '15 secs', '30 secs',
          '1 min', '2 mins', '3 mins')),

        # 300 seconds - 5 mins
        ('300 S',
         ('1 secs', '5 secs', '10 secs', '15 secs', '30 secs',
          '1 min', '2 mins', '3 mins', '5 mins')),

        # 600 seconds - 10 mins
        ('600 S',
         ('1 secs', '5 secs', '10 secs', '15 secs', '30 secs',
          '1 min', '2 mins', '3 mins', '5 mins', '10 mins')),

        # 900 seconds - 15 mins
        ('900 S',
         ('1 secs', '5 secs', '10 secs', '15 secs', '30 secs',
          '1 min', '2 mins', '3 mins', '5 mins', '10 mins', '15 mins')),

        # 1200 seconds - 20 mins
        ('1200 S',
         ('1 secs', '5 secs', '10 secs', '15 secs', '30 secs',
          '1 min', '2 mins', '3 mins', '5 mins', '10 mins', '15 mins',
          '20 mins')),

        # 1800 seconds - 30 mins
        ('1800 S',
         ('1 secs', '5 secs', '10 secs', '15 secs', '30 secs',
          '1 min', '2 mins', '3 mins', '5 mins', '10 mins', '15 mins',
          '20 mins', '30 mins')),

        # 3600 seconds - 1 hour
        ('3600 S',
         ('5 secs', '10 secs', '15 secs', '30 secs',
          '1 min', '2 mins', '3 mins', '5 mins', '10 mins', '15 mins',
          '20 mins', '30 mins',
          '1 hour')),

        # 7200 seconds - 2 hours
        ('7200 S',
         ('5 secs', '10 secs', '15 secs', '30 secs',
          '1 min', '2 mins', '3 mins', '5 mins', '10 mins', '15 mins',
          '20 mins', '30 mins',
          '1 hour', '2 hours')),

        # 10800 seconds - 3 hours
        ('10800 S',
         ('10 secs', '15 secs', '30 secs',
          '1 min', '2 mins', '3 mins', '5 mins', '10 mins', '15 mins',
          '20 mins', '30 mins',
          '1 hour', '2 hours', '3 hours')),

        # 14400 seconds - 4 hours
        ('14400 S',
         ('15 secs', '30 secs',
          '1 min', '2 mins', '3 mins', '5 mins', '10 mins', '15 mins',
          '20 mins', '30 mins',
          '1 hour', '2 hours', '3 hours', '4 hours')),

        # 28800 seconds - 8 hours
        ('28800 S',
         ('30 secs',
          '1 min', '2 mins', '3 mins', '5 mins', '10 mins', '15 mins',
          '20 mins', '30 mins',
          '1 hour', '2 hours', '3 hours', '4 hours', '8 hours')),

        # 1 days
        ('1 D',
         ('1 min', '2 mins', '3 mins', '5 mins', '10 mins', '15 mins',
          '20 mins', '30 mins',
          '1 hour', '2 hours', '3 hours', '4 hours', '8 hours',
          '1 day')),

        # 2 days
        ('2 D',
         ('2 mins', '3 mins', '5 mins', '10 mins', '15 mins',
          '20 mins', '30 mins',
          '1 hour', '2 hours', '3 hours', '4 hours', '8 hours',
          '1 day')),

        # 1 weeks
        ('1 W',
         ('3 mins', '5 mins', '10 mins', '15 mins',
          '20 mins', '30 mins',
          '1 hour', '2 hours', '3 hours', '4 hours', '8 hours',
          '1 day', '1 W')),

        # 2 weeks
        ('2 W',
         ('15 mins', '20 mins', '30 mins',
          '1 hour', '2 hours', '3 hours', '4 hours', '8 hours',
          '1 day', '1 W')),

        # 1 months
        ('1 M',
         ('30 mins',
          '1 hour', '2 hours', '3 hours', '4 hours', '8 hours',
          '1 day', '1 W', '1 M')),

        # 2+ months
        ('2 M', ('1 day', '1 W', '1 M')),
        ('3 M', ('1 day', '1 W', '1 M')),
        ('4 M', ('1 day', '1 W', '1 M')),
        ('5 M', ('1 day', '1 W', '1 M')),
        ('6 M', ('1 day', '1 W', '1 M')),
        ('7 M', ('1 day', '1 W', '1 M')),
        ('8 M', ('1 day', '1 W', '1 M')),
        ('9 M', ('1 day', '1 W', '1 M')),
        ('10 M', ('1 day', '1 W', '1 M')),
        ('11 M', ('1 day', '1 W', '1 M')),

        # 1+ years
        ('1 Y',  ('1 day', '1 W', '1 M')),
    ])

    # Sizes allow for quick translation from bar sizes above to actual
    # timeframes to make a comparison with the actual data
    _sizes = {
        'secs': (TimeFrame.Seconds, 1),
        'min': (TimeFrame.Minutes, 1),
        'mins': (TimeFrame.Minutes, 1),
        'hour': (TimeFrame.Minutes, 60),
        'hours': (TimeFrame.Minutes, 60),
        'day': (TimeFrame.Days, 1),
        'W': (TimeFrame.Weeks, 1),
        'M': (TimeFrame.Months, 1),
    }

    _dur2tf = {
        'S': TimeFrame.Seconds,
        'D': TimeFrame.Days,
        'W': TimeFrame.Weeks,
        'M': TimeFrame.Months,
        'Y': TimeFrame.Years,
    }

    def getdurations(self,  timeframe, compression):
        key = (timeframe, compression)
        if key not in self.revdur:
            return []

        return self.revdur[key]

    def getmaxduration(self, timeframe, compression):
        key = (timeframe, compression)
        try:
            return self.revdur[key][-1]
        except (KeyError, IndexError):
            pass

        return None

    def tfcomp_to_size(self, timeframe, compression):
        if timeframe == TimeFrame.Months:
            return '{} M'.format(compression)

        if timeframe == TimeFrame.Weeks:
            return '{} W'.format(compression)

        if timeframe == TimeFrame.Days:
            if not compression % 7:
                return '{} W'.format(compression // 7)

            return '{} day'.format(compression)

        if timeframe == TimeFrame.Minutes:
            if not compression % 60:
                hours = compression // 60
                return ('{} hour'.format(hours)) + ('s' * (hours > 1))

            return ('{} min'.format(compression)) + ('s' * (compression > 1))

        if timeframe == TimeFrame.Seconds:
            return '{} secs'.format(compression)

        # Microseconds or ticks
        return None

    def dt_plus_duration(self, dt, duration):
        size, dim = duration.split()
        size = int(size)
        if dim == 'S':
            return dt + timedelta(seconds=size)

        if dim == 'D':
            return dt + timedelta(days=size)

        if dim == 'W':
            return dt + timedelta(days=size * 7)

        if dim == 'M':
            month = dt.month - 1 + size  # -1 to make it 0 based, readd below
            years, month = divmod(month, 12)
            return dt.replace(year=dt.year + years, month=month + 1)

        if dim == 'Y':
            return dt.replace(year=dt.year + size)

        return dt  # could do nothing with it ... return it intact


    # def histduration(self, dt1, dt2):
    #     # Given two dates calculates the smallest possible duration according
    #     # to the table from the Historical Data API limitations provided by IB
    #     #
    #     # Seconds: 'x S' (x: [60, 120, 180, 300, 600, 900, 1200, 1800, 3600,
    #     #                     7200, 10800, 14400, 28800])
    #     # Days: 'x D' (x: [1, 2]
    #     # Weeks: 'x W' (x: [1, 2])
    #     # Months: 'x M' (x: [1, 11])
    #     # Years: 'x Y' (x: [1])

    #     td = dt2 - dt1  # get a timedelta for calculations

    #     # First: array of secs
    #     tsecs = td.total_seconds()
    #     secs = [60, 120, 180, 300, 600, 900, 1200, 1800, 3600, 7200, 10800,
    #             14400, 28800]

    #     idxsec = bisect.bisect_left(secs, tsecs)
    #     if idxsec < len(secs):
    #         return '{} S'.format(secs[idxsec])

    #     tdextra = bool(td.seconds or td.microseconds)  # over days/weeks

    #     # Next: 1 or 2 days
    #     days = td.days + tdextra
    #     if td.days <= 2:
    #         return '{} D'.format(days)

    #     # Next: 1 or 2 weeks
    #     weeks, d = divmod(td.days, 7)
    #     weeks += bool(d or tdextra)
    #     if weeks <= 2:
    #         return '{} W'.format(weeks)

    #     # Get references to dt components
    #     y2, m2, d2 = dt2.year, dt2.month, dt2.day
    #     y1, m1, d1 = dt1.year, dt1.month, dt2.day

    #     H2, M2, S2, US2 = dt2.hour, dt2.minute, dt2.second, dt2.microsecond
    #     H1, M1, S1, US1 = dt1.hour, dt1.minute, dt1.second, dt1.microsecond

    #     # Next: 1 -> 11 months (11 incl)
    #     months = (y2 * 12 + m2) - (y1 * 12 + m1) + (
    #         (d2, H2, M2, S2, US2) > (d1, H1, M1, S1, US1))
    #     if months <= 1:  # months <= 11
    #         return '1 M'  # return '{} M'.format(months)
    #     elif months <= 11:
    #         return '2 M'  # cap at 2 months to keep the table clean

    #     # Next: years
    #     # y = y2 - y1 + (m2, d2, H2, M2, S2, US2) > (m1, d1, H1, M1, S1, US1)
    #     # return '{} Y'.format(y)

    #     return '1 Y'  # to keep the table clean

    def makecontract(self, symbol, sectype, exch, curr,
                     expiry='', strike=0.0, right='', mult=1):
        '''returns a contract from the parameters without check'''

        contract = Contract()

        contract.symbol = symbol
        contract.secType = sectype
        contract.exchange = exch
        if curr:
            contract.currency = curr
        if sectype in ['FUT', 'OPT', 'FOP']:
            contract.lastTradeDateOrContractMonth = expiry
        if sectype in ['OPT', 'FOP']:
            contract.strike = strike
            contract.right = right
        if mult:
            contract.multiplier = mult

        return contract

    def cancelOrder(self, orderid):
        '''Proxy to cancelOrder'''
        self.ib.cancelOrder(orderid)


    def placeOrder(self, orderid, contract, order):
        '''Proxy to placeOrder'''        
        trade = self.ib.placeOrder(contract, order)  
        while not trade.isDone():
            self.ib.waitOnUpdate()
        return trade
        
    def reqTrades(self):
        '''Proxy to Trades'''
        return self.ib.trades()

    def reqPositions(self):
        '''Proxy to reqPositions'''
        return self.ib.reqPositions()
    
    def getposition(self, contract, clone=False):
        # Lock access to the position dicts. This is called from main thread
        # and updates could be happening in the background
        #with self._lock_pos:
        position = self.positions[contract.conId]
        if clone:
            return copy(position)

        return position

    def reqAccountUpdates(self, subscribe=True, account=None):
        '''Proxy to reqAccountUpdates

        If ``account`` is ``None``, wait for the ``managedAccounts`` message to
        set the account codes
        '''
        if account is None:
            #self._event_managed_accounts.wait()
            self.managedAccounts()
            account = self.managed_accounts[0]

        #self.ib.reqAccountUpdates(subscribe, bytes(account))
        self.updateAccountValue()


    def updateAccountValue(self):
        # Lock access to the dicts where values are updated. This happens in a
        # sub-thread and could kick it at anytime
        #with self._lock_accupd:
        #if self.connected():
        ret = self.ib.accountValues()
        
        for msg in ret:
            try:
                value = float(msg.value)   
            except ValueError:
                value = msg.value

            self.acc_upds[msg.account][msg.tag][msg.currency] = value

            if msg.tag == 'NetLiquidation':
                # NetLiquidationByCurrency and currency == 'BASE' is the same
                self.acc_value[msg.account] = value
            elif msg.tag == 'TotalCashBalance' and msg.currency == 'BASE':
                self.acc_cash[msg.account] = value

    def get_acc_values(self, account=None):
        '''Returns all account value infos sent by TWS during regular updates
        Waits for at least 1 successful download

        If ``account`` is ``None`` then a dictionary with accounts as keys will
        be returned containing all accounts

        If account is specified or the system has only 1 account the dictionary
        corresponding to that account is returned
        '''
        # Wait for at least 1 account update download to have been finished
        # before the account infos can be returned to the calling client
        # if self.connected():
        #     self._event_accdownload.wait()
        # Lock access to acc_cash to avoid an event intefering
        #with self._updacclock:
        if account is None:
            # wait for the managedAccount Messages
            # if self.connected():
            #     self._event_managed_accounts.wait()

            if not self.managed_accounts:
                return self.acc_upds.copy()

            elif len(self.managed_accounts) > 1:
                return self.acc_upds.copy()

            # Only 1 account, fall through to return only 1
            account = self.managed_accounts[0]

        try:
            return self.acc_upds[account].copy()
        except KeyError:
            pass

        return self.acc_upds.copy()

    def get_acc_value(self, account=None):
        '''Returns the net liquidation value sent by TWS during regular updates
        Waits for at least 1 successful download

        If ``account`` is ``None`` then a dictionary with accounts as keys will
        be returned containing all accounts

        If account is specified or the system has only 1 account the dictionary
        corresponding to that account is returned
        '''
        # Wait for at least 1 account update download to have been finished
        # before the value can be returned to the calling client
        # if self.connected():
        #     self._event_accdownload.wait()
        # Lock access to acc_cash to avoid an event intefering
        #with self._lock_accupd:
        if account is None:
            # wait for the managedAccount Messages
            # if self.connected():
            #     self._event_managed_accounts.wait()

            if not self.managed_accounts:
                return float()

            elif len(self.managed_accounts) > 1:
                return sum(self.acc_value.values())

            # Only 1 account, fall through to return only 1
            account = self.managed_accounts[0]

        try:
            return self.acc_value[account]
        except KeyError:
            pass

        return float()

    def get_acc_cash(self, account=None):
        '''Returns the total cash value sent by TWS during regular updates
        Waits for at least 1 successful download

        If ``account`` is ``None`` then a dictionary with accounts as keys will
        be returned containing all accounts

        If account is specified or the system has only 1 account the dictionary
        corresponding to that account is returned
        '''
        # Wait for at least 1 account update download to have been finished
        # before the cash can be returned to the calling client'
        # if self.connected():
        #     self._event_accdownload.wait()
            # result = [v for v in self.ib.accountValues() \
            #           if v.tag == 'TotalCashBalance' and v.currency == 'BASE']
        # Lock access to acc_cash to avoid an event intefering
            
        #with self._lock_accupd:
        if account is None:
            #wait for the managedAccount Messages
            # if self.connected():
            #     self._event_managed_accounts.wait()

            if not self.managed_accounts:
                return float()

            elif len(self.managed_accounts) > 1:
                return sum(self.acc_cash.values())

            # Only 1 account, fall through to return only 1
            account = self.managed_accounts[0]

        try:
            return self.acc_cash[account]
        except KeyError:
            pass
Example #3
0
class Live(IB):
    def __init__(self,
                 symbol,
                 temp,
                 client,
                 verbose=False,
                 notification=False):
        self.symbol = symbol
        instruments = pd.read_csv('instruments.csv').set_index('symbol')
        params = instruments.loc[self.symbol]
        self.market = str(params.market)
        self.exchange = str(params.exchange)
        self.temp = temp
        self.tick_size = float(params.tick_size)
        self.digits = int(params.digits)
        self.leverage = int(params.leverage)
        self.client = client
        self.verbose = verbose
        self.notification = notification
        self.ib = IB()
        print(self.ib.connect('127.0.0.1', 7497, client))
        self.get_contract()
        self.data = self.download_data(tempo=self.temp, duration='1 D')
        self.current_date()
        self.pool = pd.DataFrame(columns=[
            'date', 'id', 'type', 'lots', 'price', 'S/L', 'T/P', 'commission',
            'comment', 'profit'
        ])
        self.history = pd.DataFrame(columns=[
            'date', 'id', 'type', 'lots', 'price', 'S/L', 'T/P', 'commission',
            'comment', 'profit'
        ])
        self.pending = pd.DataFrame(columns=[
            'date', 'id', 'type', 'lots', 'price', 'S/L', 'T/P', 'commission',
            'comment', 'profit'
        ])
        self.position = 0
        self.number = 0

    def get_contract(self):
        if self.market == 'futures':
            expiration = self.ib.reqContractDetails(
                Future(self.symbol,
                       self.exchange))[0].contract.lastTradeDateOrContractMonth
            self.contract = Future(symbol=self.symbol,
                                   exchange=self.exchange,
                                   lastTradeDateOrContractMonth=expiration)
        elif self.market == 'forex':
            self.contract = Forex(self.symbol)
        elif self.market == 'stocks':
            self.contract = Stock(symbol=self.symbol,
                                  exchange=self.exchange,
                                  currency='USD')

    def download_data(self, tempo, duration):
        pr = (lambda mark: 'TRADES' if mark == 'futures' else
              ('TRADES' if mark == 'stocks' else 'MIDPOINT'))(self.market)
        historical = self.ib.reqHistoricalData(self.contract,
                                               endDateTime='',
                                               durationStr=duration,
                                               barSizeSetting=tempo,
                                               whatToShow=pr,
                                               useRTH=True,
                                               keepUpToDate=True)
        return historical

    def data_to_df(self, data):
        df = util.df(data)[['date', 'open', 'high', 'low', 'close',
                            'volume']].set_index('date')
        df.index = pd.to_datetime(df.index)
        return df

    def send_telegram_message(self, msg):
        '''Sends a telegram message
        '''
        requests.post(
            'https://api.telegram.org/bot804823606:AAFq-YMKr4hIjQ4N5M8GYCGa5w9JJ1kIunk/sendMessage',
            data={
                'chat_id': '@ranguito_channel',
                'text': msg
            })

    def current_date(self):
        self.date = datetime.now().strftime('%Y-%m-%d')
        self.weekday = datetime.now().weekday()
        self.hour = datetime.now().strftime('%H:%M:%S')

    def pool_check(self):
        '''Check pool trades'''
        if self.position == 0:
            self.pool = pd.DataFrame(columns=[
                'date', 'type', 'lots', 'price', 'S/L', 'T/P', 'commission',
                'comment', 'profit'
            ])

    def calculate_profit(self, type, price, lots):
        '''Calculates profit'''
        if type == 'BUY':
            profit = (lambda pos: 0
                      if pos >= 0 else (self.pool[self.pool.type == 'SELL'])[
                          'price'].iloc[0] - price)(self.position)
        else:
            profit = (lambda pos: 0 if pos <= 0 else price -
                      (self.pool[self.pool.type == 'BUY'])['price'].iloc[0])(
                          self.position)
        return profit * self.leverage * lots

    def order_values(self, order_id):
        price = 0
        commission = 0
        if len(self.ib.fills()) > 0:
            for trade in util.tree(self.ib.fills()):
                if ('OrderId' and 'clientId') in trade[1]['Execution']:
                    if ((nested_lookup('orderId', trade)[0] == order_id) and
                        (nested_lookup('clientId', trade)[0] == self.client)):
                        commission = nested_lookup('commission', trade)[0]
                        price = nested_lookup('price', trade)[0]
        return (price, commission)

    def order_send(self, type, lots, sl=0, tp=0, comment=''):
        market_order = MarketOrder(type, lots)
        #initial_margin, maintenance_margin = self.get_margins(market_order)
        self.ib.placeOrder(self.contract, market_order)
        id = market_order.orderId

        self.number += 1
        price = 0
        while price == 0:
            self.ib.sleep(1)
            price, commission = self.order_values(id)
        profit = self.calculate_profit(type, price, lots)
        trade = {
            'date': str(self.date) + ' ' + str(self.hour),
            'id': id,
            'type': type,
            'lots': lots,
            'price': price,
            'S/L': sl,
            'T/P': tp,
            'commission': commission,
            'comment': comment,
            'profit': profit
        }
        self.save_trade(trade)
        self.pool = pd.concat(
            [self.pool, pd.DataFrame(trade, index=[self.number])], sort=False)
        self.history = pd.concat(
            [self.history,
             pd.DataFrame(trade, index=[self.number])],
            sort=False)
        mult = (lambda dir: 1 if dir == 'BUY' else -1)(type)
        self.position += (mult * lots)
        self.pool_check()
        if self.verbose:
            print('%s %s | %sING %d units at %5.2f in %s' % (str(
                self.date), str(self.hour), type, lots, price, self.symbol))
        if self.notification:
            if self.position != 0:
                self.send_message_in(type, price, sl, tp, lots)
            else:
                self.send_message_out(type, price, lots, profit, commission,
                                      commission)

    def bracket_stop_order(self,
                           type,
                           lots,
                           entry_price,
                           sl=0,
                           tp=0,
                           comment=''):
        bracket_order = self.ib.bracketStopOrder(type, lots, entry_price, tp,
                                                 sl)
        #initial_margin, maintenance_margin = self.get_margins(bracket_order[0])
        for order in bracket_order:
            self.ib.placeOrder(self.contract, order)
        id_entry = bracket_order[0].orderId
        id_tp = bracket_order[1].orderId
        id_sl = bracket_order[2].orderId

        trade = {
            'date': str(self.date) + ' ' + str(self.hour),
            'id': id_entry,
            'type': bracket_order[0].action,
            'lots': lots,
            'price': entry_price,
            'S/L': sl,
            'T/P': tp,
            'commission': 0,
            'comment': comment,
            'profit': 0
        }
        self.pending = pd.concat(
            [self.pending, pd.DataFrame(trade, index=[id_entry])], sort=False)
        trade = {
            'date': str(self.date) + ' ' + str(self.hour),
            'id': id_tp,
            'type': bracket_order[1].action,
            'lots': lots,
            'price': tp,
            'S/L': 0,
            'T/P': 0,
            'commission': 0,
            'comment': comment,
            'profit': 0
        }
        self.pending = pd.concat(
            [self.pending, pd.DataFrame(trade, index=[id_entry])], sort=False)
        trade = {
            'date': str(self.date) + ' ' + str(self.hour),
            'id': id_sl,
            'type': bracket_order[2].action,
            'lots': lots,
            'price': sl,
            'S/L': 0,
            'T/P': 0,
            'commission': 0,
            'comment': comment,
            'profit': 0
        }
        self.pending = pd.concat(
            [self.pending, pd.DataFrame(trade, index=[id_entry])], sort=False)
        return (bracket_order[0], bracket_order[1], bracket_order[2])

    def pending_check(self, order):
        id = order.orderId
        if len(self.pending) > 0:
            price, commission = self.order_values(id)
            if price > 0:
                self.number += 1
                order_select = self.pending[self.pending.id == id]
                profit = self.calculate_profit(order_select.type.iloc[0],
                                               price,
                                               order_select.lots.iloc[0])
                trade = {
                    'date': str(self.date) + ' ' + str(self.hour),
                    'id': id,
                    'type': order_select.type.iloc[0],
                    'lots': order_select.lots.iloc[0],
                    'price': price,
                    'S/L': order_select['S/L'].iloc[0],
                    'T/P': order_select['T/P'].iloc[0],
                    'commission': commission,
                    'comment': '',
                    'profit': profit
                }
                self.save_trade(trade)
                self.pool = pd.concat(
                    [self.pool,
                     pd.DataFrame(trade, index=[self.number])],
                    sort=False)
                self.history = pd.concat(
                    [self.history,
                     pd.DataFrame(trade, index=[self.number])],
                    sort=False)
                mult = (lambda dir: 1
                        if dir == 'BUY' else -1)(order_select.type.iloc[0])
                self.position += (mult * order_select.lots.iloc[0])
                self.pool_check()
                if self.verbose:
                    print('%s %s | %sING %d units at %5.2f in %s' %
                          (str(self.date), str(
                              self.hour), order_select.type.iloc[0],
                           order_select.lots.iloc[0], price, self.symbol))
                if self.notification:
                    if self.position != 0:
                        self.send_message_in(order_select.type.iloc[0], price,
                                             order_select['S/L'].iloc[0],
                                             order_select['T/P'].iloc[0],
                                             order_select.lots.iloc[0])
                    else:
                        self.send_message_out(order_select.type.iloc[0], price,
                                              order_select.lots.iloc[0],
                                              profit, commission, commission)
                return True
            else:
                return False

    def get_margins(self, order):
        init_margin = float(
            self.ib.whatIfOrder(self.contract, order).initMarginChange)
        maint_margin = float(
            self.ib.whatIfOrder(self.contract, order).maintMarginChange)
        return (init_margin, maint_margin)

    def send_message_in(self, type, price_in, sl, tp, lots):
        msg_in = '%s Opened in %s \nPrice: %5.2f \nS/L: %5.2f \nT/P: %5.2f \nLots: %d \nAt: %s' % (
            type, self.symbol, price_in, sl, tp, lots, self.hour)
        self.send_telegram_message(msg_in)

    def send_message_out(self, type, price_out, lots, profit, comm_in,
                         comm_out):
        msg_out = '%s Closed in %s \nPrice: %5.2f \nProfit(USD): %5.2f \nCommissions(USD): %5.2f \nAt: %s' % \
                    (type, self.symbol, price_out, profit, (comm_in+comm_out),self.hour)
        self.send_telegram_message(msg_out)

    def save_trade(self, trade):
        if not path.exists('history_trades_%s.csv' % self.symbol):
            initial = pd.DataFrame(columns=[
                'date', 'id', 'type', 'lots', 'price', 'S/L', 'T/P',
                'commission', 'comment', 'profit'
            ]).set_index('date')
            initial.to_csv('history_trades_%s.csv' % self.symbol)
        history = pd.read_csv('history_trades_%s.csv' % self.symbol)
        trade = pd.DataFrame(trade, index=[0])
        history = pd.concat([history, trade], sort=False)
        history['net profit'] = history['profit'] - history['commission']
        history['accumulated profit'] = history['net profit'].cumsum()
        history['max profit'] = history['accumulated profit'].cummax()
        history.set_index('date').to_csv('history_trades_%s.csv' % self.symbol)
Example #4
0
class LiveTrading(MethodManager_):
    def __init__(self, strat_name, client_id, runtime_tm, debugging, manager_,
                 heartbeat_q, traded_instr):
        self.strat_name = strat_name
        self.client_id = client_id
        self.runtime_tm = runtime_tm
        self.debugging = debugging
        self.manager_ = manager_
        self.heartbeat_q = heartbeat_q
        self.traded_instr = traded_instr

        self.account_curr = ACCOUNT_CURR
        self.tax_rate_account_country = TAX_RATE_ACCOUNT_COUNTRY
        self.simult_reqs_interval = SIMULT_REQS_INTERVAL
        self.potential_mkt_data_lines_already_in_use = POTENTIAL_MKT_DATA_LINES_ALREADY_IN_USE

        self.ib = IB()
        # self.ib.setCallback('error', self.on_ib_error)
        self.ib.errorEvent += self.on_ib_error
        self.err_ = None

        self.report = Reporting(self)
        self.cash = CashManagement(debugging)
        self.hlprs = ClientSharedMemory()

        self.access_type = 'tws'
        if self.access_type == 'gateway':
            self.__port = 4001
        elif self.access_type == 'tws':
            self.__port = 7497
        self.__host = '127.0.0.1'
        self.ib.connect(self.__host, self.__port, clientId=self.client_id)

        self.portfolio = []
        self.portfolio_instr = []
        self.cnt_trades_per_instr_per_day = {}

        self.req_tracker = {
            'total': 0,
            'open_market_data_reqs': 0,
            'open_market_data_lines': 0,
            'hist_data_prior_time': None
        }

        self.start_cet = datetime.datetime.now()

        self.manager_[
            'active_mkt_data_lines'] = POTENTIAL_MKT_DATA_LINES_ALREADY_IN_USE
        self.funda_data_req_max = IB_PACING['funda_data_reqs']['reqs']

        if self.debugging.get_meths:
            self.print_meths(str(self.__class__.__name__), dir(self))

        _inst_ = self
        self.fin_ratios = FinRatios(_inst_)

    @staticmethod
    def us_tz_op(dt_obj):
        """
        Converting time
        :param dt_obj: datetime obj
        :return: Eastern datetime
        """
        # log_start = datetime.datetime.now()
        if not isinstance(dt_obj, datetime.datetime):
            raise TypeError
        if dt_obj.year == 1900:
            curr_year = datetime.datetime.now().year
        else:
            curr_year = dt_obj.year
        # http://www.webexhibits.org/daylightsaving/b2.html
        dst = {
            2018: [[3, 11], [11, 4]],
            2019: [[3, 10], [11, 3]],
            2020: [[3, 8], [11, 1]]
        }
        window = dst[curr_year]

        #disclaimer
        kind_of_difference = HOUR_DAY_BOUNDARY_FAIL_CHECK
        if dt_obj.hour < kind_of_difference:
            raise Exception("determination difficult, try later in the day")

        dst_start = datetime.datetime(curr_year, window[0][0], window[0][1])
        dst_end = datetime.datetime(curr_year, window[1][0], window[1][1])
        if dt_obj > dst_start and dt_obj < dst_end:
            utc2est = -4
        else:
            utc2est = -5
        this_tz_from_uts = '+0100'
        if time.strftime("%z", time.gmtime()) != this_tz_from_uts:
            raise exceptions_.WrongTimeZone(
                'Wrong time zone, code is fixed to Danish winter time, might have to be adjusted'
            )

        cet2utc = -2
        dt_obj_utc = dt_obj + datetime.timedelta(
            hours=cet2utc)  #.replace(tzinfo = datetime.timezone.utc)
        dt_obj_est = dt_obj_utc + datetime.timedelta(hours=utc2est)
        # logging = rk_logging.Logging()
        # logging.log(
        #         runtime_tm, log_start, str('' + ',' + sys._getframe().f_code.co_name)
        #         ,dt_obj_utc
        #         ,dt_obj_est
        #     )
        return dt_obj_est

    def _manipulated_time(self, cet_in):
        delta = cet_in - self.start_cet
        current_manipulated_time = datetime.datetime(
            self.start_cet.year, self.start_cet.month, self.start_cet.day,
            self.debugging.dummy_hour, self.debugging.dummy_minute,
            self.debugging.dummy_second)
        current_manipulated_time += delta
        return current_manipulated_time

    def buy(self, ticker, rank, size, order_type):
        log_start = datetime.datetime.now()
        log = logging_.Logging(
            self.runtime_tm, log_start,
            str(self.__class__.__name__ + ',' +
                sys._getframe().f_code.co_name))
        self.hlprs.add_to_manager(self.strat_name, *log.monitor())
        order_log_msg = None
        symbol = ticker.contract.symbol
        if self._ticker_len_n_type_check(ticker.domBids) > 1:
            offer_price = ticker.domBids[rank].price
            # offer_size = ticker.domBids[rank].size
        else:
            offer_price = ticker.domBids.price
            # offer_size = ticker.domBids.size
        cnt = 0
        max_order_filled_cnts = int(ORDER_CANCEL_IF_NOT_FILLED_SECS /
                                    ORDER_FILLED_CHECKED_CYCLE_SECS)
        if self.debugging.dummy_data:
            order_success = True
        else:
            if order_type == 'MKT':
                order = MarketOrder('BUY', size)
                trade = self.ib.placeOrder(ticker.contract, order)
                while cnt < max_order_filled_cnts:  # a bit complicated but it does actually make sense
                    order_log_msg = trade.log
                    self.ib.sleep(ORDER_FILLED_CHECKED_CYCLE_SECS)
                    cnt += 1
                    if not trade.orderStatus.status != 'Filled':
                        # PendingSubmit = 'PendingSubmit'
                        # PendingCancel = 'PendingCancel'
                        # PreSubmitted = 'PreSubmitted'
                        # Submitted = 'Submitted'
                        # ApiPending = 'ApiPending'  # undocumented, can be returned from req(All)OpenOrders
                        # ApiCancelled = 'ApiCancelled'
                        # Cancelled = 'Cancelled'
                        # Filled = 'Filled'
                        # Inactive = 'Inactive'
                        order_success = True
                        break
                else:
                    self.ib.cancelOrder(trade)
                # while not trade.isDone():
                #     print("not sure if this makes sense")
                #     self.ib.waitOnUpdate()
            elif order_type == 'LMT':
                raise Exception("order type not defined")
            else:
                raise Exception("order type not defined")
        if order_success:
            if self.debugging.dummy_time:
                _now = self._manipulated_time(datetime.datetime.now())
            else:
                _now = datetime.datetime.now()

            filled_tm = self.us_tz_op(_now)
            status = 0
            self.portfolio.append([symbol, filled_tm, offer_price, size])
            self.hlprs.add_to_manager(self.strat_name, 'portfolio',
                                      self.portfolio)

            self.portfolio_instr.append(symbol)
            # self.add_to_monitor('portfolio_instr', 'self.portfolio_instr')

            print('not sure if this works')
            self.cnt_trades_per_instr_per_day[ticker.contract.symbol] += 1
            self.hlprs.add_to_manager(self.strat_name,
                                      'cnt_trades_per_instr_per_day',
                                      self.cnt_trades_per_instr_per_day)
            self.heartbeat_q.put([
                self.strat_name, 'cnt_trades_per_instr_per_day',
                self.cnt_trades_per_instr_per_day
            ])
            self.hlprs.add_to_manager(
                self.strat_name, 'cap_usd',
                self.cash.available_funds(self.report.pnl(),
                                          self.account_curr))
        else:
            status = 1
        log.log(self.portfolio, self.portfolio_instr,
                self.cnt_trades_per_instr_per_day, order_log_msg)
        return status

    def sell(self, ticker, rank, order_type):
        log_start = datetime.datetime.now()
        log = logging_.Logging(
            self.runtime_tm, log_start,
            str(self.__class__.__name__ + ',' +
                sys._getframe().f_code.co_name))
        self.hlprs.add_to_manager(self.strat_name, *log.monitor())
        order_log_msg = None
        symbol = ticker.contract.symbol
        try:
            ix = self.portfolio_instr.index(symbol)
        except ValueError:
            status = -1
            print(
                'not quite sure why this error sometimes occurs and if this is maybe to early to leave?'
            )
            return status

        # if isinstance(ticker.domBids, str) == True:
        #     offer_price = ticker.domAsks.price
        #     offer_size = ticker.domAsks.size
        # else:
        #     offer_price = ticker.domAsks[rank].price
        #     offer_size = ticker.domAsks[rank].size
        price_ix = 2
        size_ix = 3
        # held_price = self.held[ix][price_ix]
        held_size = self.portfolio[ix][size_ix]

        cnt = 0
        max_order_filled_cnts = int(ORDER_CANCEL_IF_NOT_FILLED_SECS /
                                    ORDER_FILLED_CHECKED_CYCLE_SECS)
        if not self.debugging.dummy_data:
            if order_type == 'MKT':
                order = MarketOrder('SELL', held_size)
                # https://github.com/erdewit/ib_insync/blob/master/notebooks/ordering.ipynb
                trade = self.ib.placeOrder(ticker.contract, order)
                while cnt < max_order_filled_cnts:
                    order_log_msg = trade.log
                    self.ib.sleep(ORDER_FILLED_CHECKED_CYCLE_SECS)
                    cnt += 1
                    if trade.orderStatus.status == 'Filled':
                        order_success = True
                        break
                self.ib.cancelOrder(trade)
                while not trade.isDone():
                    print("not sure if this makes sense")
                    self.ib.waitOnUpdate()
            elif order_type == 'LMT':
                raise Exception("order type not defined")
            else:
                raise Exception("order type not defined")
        else:
            order_success = True
        order_success = True
        if order_success:
            status = 0
            del self.portfolio[ix]
            self.hlprs.add_to_manager(self.strat_name, 'portfolio',
                                      self.portfolio)
            del self.portfolio_instr[ix]
            # self.add_to_monitor('portfolio_instr', self.portfolio_instr)
            print('not sure if this works')
            self.cnt_trades_per_instr_per_day[ticker.contract.symbol] += 1
            self.hlprs.add_to_manager(self.strat_name,
                                      'cnt_trades_per_instr_per_day',
                                      self.cnt_trades_per_instr_per_day)
            self.heartbeat_q.put([
                self.strat_name, 'cnt_trades_per_instr_per_day',
                self.cnt_trades_per_instr_per_day
            ])
            self.hlprs.add_to_manager(
                self.strat_name, 'cap_usd',
                self.cash.available_funds(self.report.pnl(),
                                          self.account_curr))
        else:
            status = 1
        log.log(self.portfolio, self.portfolio_instr,
                self.cnt_trades_per_instr_per_day, order_log_msg)
        return status

    @staticmethod
    def _ticker_len_n_type_check(bid_or_ask_li):
        # log_start = datetime.datetime.now()
        n_ticks = len(bid_or_ask_li) if isinstance(bid_or_ask_li,
                                                   str) == False else 1
        # log = rk_logging.Logging()
        # log.log(
        #     self.runtime_tm, log_start, str('' + ',' + sys._getframe().f_code.co_name)
        # )
        return n_ticks

    def req_handling(self, ticker, type_):
        log_start = datetime.datetime.now()
        log = logging_.Logging(
            self.runtime_tm, log_start,
            str(self.__class__.__name__ + ',' +
                sys._getframe().f_code.co_name))
        self.hlprs.add_to_manager(self.strat_name, *log.monitor())
        # unix_ts = int(time.time())
        self.req_tracker['total'] += 1
        self.req_tracker['open_market_data_reqs'] += 1
        self.req_tracker['open_market_data_lines'] += 1

        # status = -1 means immediately kill connections
        status = self.pacing_violations(type_)
        log.log()
        if status == -1:
            print('get connections here')
            # do sth
            return
        elif status == 0:
            return

    def req_handler(self, toggle):
        if toggle == 'increase':
            self.manager_['active_mkt_data_lines'] += 1
        elif toggle == 'decrease':
            self.manager_['active_mkt_data_lines'] -= 1
        else:
            raise Exception('not defined')

    def req_mkt_dpt_ticker_(self, contract_):
        _cnt_ = 0
        status = 1
        while _cnt_ < N_TRIES_IF_SIMULT_REQS:
            if self.manager_['active_mkt_data_lines'] < IB_PACING[
                    'mkt_data_lines']:
                self.req_handler('increase')
                ticker = self.ib.reqMktDepth(contract_)
                self.ib.sleep(
                    SIMULT_REQS_INTERVAL
                )  # wait here because ticker needs updateEvent might not be fast enough
            else:
                self.ib.sleep(
                    SIMULT_REQS_INTERVAL
                )  # wait here because ticker needs updateEvent might not be fast enough
                _cnt_ += 1
                continue
            if self.err_ != exceptions_.IbPacingError:
                status = 0
                break
        return ticker, status

    def cancel_mkt_dpt_ticker_(self, contract_):
        self.ib.cancelMktDepth(contract_)
        self.req_handler('decrease')

    @staticmethod
    def _get_ib_pacing():
        return IB_PACING

    def pacing_violations(self, type_):
        log_start = datetime.datetime.now()
        log = logging_.Logging(
            self.runtime_tm, log_start,
            str(self.__class__.__name__ + ',' +
                sys._getframe().f_code.co_name))
        self.hlprs.add_to_manager(self.strat_name, *log.monitor())
        # histData, mktDepth, scannerData

        # TODO: far from done here, continue at some point
        status = 0
        if type_ == 'histData':
            if self.req_tracker[
                    'total'] == 1:  #first time is already incremented
                self.req_tracker['hist_data_prior_time'] = int(time.time())
                status = 0
            elif self.req_tracker['total'] > 1 and self.req_tracker[
                    'open_market_data_lines'] < MARKET_DATA_LINES:
                status = 0
            elif self.req_tracker['total'] > 1 \
                    and self.req_tracker['open_market_data_lines'] == MARKET_DATA_LINES \
                    and int(time.time()) - self.req_tracker['hist_data_prior_time'] >= IB_PACING['hist_data_similar']['secs']:
                print('CRITICAL: market data lines limit reached')
                status = -1
        elif type_ == 'mktDepth':
            status = 0
        elif type_ == 'scannerData':
            status = 0
        log.log(status, self.req_tracker)
        self.hlprs.add_to_manager(self.strat_name, *log.monitor())
        return status

    def check_network_requirements(self):
        # s = socket.socket()
        # #e.g.
        # address, port = '10.8.8.19', '53141'
        # address, port = '208.245.107.3', '4000'
        # try:
        #     s.connect((address, port))
        #     return True
        # except socket.error:
        #     return False
        # #or
        # rows = []
        # lc = psutil.net_connections('inet')
        # for c in lc:
        #     (ip, port) = c.laddr  # 0.0.0.0
        #     if ip == '10.8.8.19':  # or ip == '::'
        #         if c.type == socket.SOCK_STREAM and c.status == psutil.CONN_LISTEN:
        #             proto_s = 'tcp'
        #         elif c.type == socket.SOCK_DGRAM:
        #             proto_s = 'udp'
        #         else:
        #             continue
        #         pid_s = str(c.pid) if c.pid else '(unknown)'
        #         msg = 'PID {} is listening on port {}/{} for all IPs.'
        #         msg = msg.format(pid_s, port, proto_s)
        #         print(msg)
        status = True
        if not status:
            raise Exception('cannot connect to all necessary servers')
#

    def on_ib_error(self, reqId, errorCode, errorString, errorSomething):
        """
        https://groups.io/g/insync/topic/how_to_capture_error_trapped/7718294?p=,,,20,0,0,0::recentpostdate%2Fsticky,,,20,1,80,7718294
        """
        max_n_mkt_depth_reqs = 309  # ERROR:ib_insync.wrapper:Error 309, reqId 39: Max number (3) of market depth requests has been reached, contract: Stock(symbol='PETZ', exchange='ISLAND', currency='USD')
        if errorCode == max_n_mkt_depth_reqs:
            self.err_ = exceptions_.IbPacingError
Example #5
0
class trade_ES():
    def __init__(self):

        self.ib = IB()
        self.ib.connect('127.0.0.1',
                        7497,
                        clientId=np.random.randint(10, 1000))
        self.tickers_ret = {}
        self.endDateTime = ''
        self.No_days = '43200 S'
        self.interval = '30 secs'
        self.tickers_signal = "Hold"
        self.ES = Future(symbol='ES',
                         lastTradeDateOrContractMonth='20200619',
                         exchange='GLOBEX',
                         currency='USD')
        self.ib.qualifyContracts(self.ES)
        self.ES_df = self.ib.reqHistoricalData(contract=self.ES,
                                               endDateTime=self.endDateTime,
                                               durationStr=self.No_days,
                                               barSizeSetting=self.interval,
                                               whatToShow='TRADES',
                                               useRTH=False,
                                               keepUpToDate=True)
        self.tickers_ret = []
        self.options_ret = []
        self.option = {'call': FuturesOption, 'put': FuturesOption}
        self.options_history = {}
        self.trade_options = {'call': [], 'put': []}
        self.price = 0
        self.i = -1
        self.ES_df.updateEvent += self.make_clean_df
        self.Buy = True
        self.Sell = False
        self.ib.positionEvent += self.order_verify
        self.waitTimeInSeconds = 220
        self.tradeTime = 0
        self.mySemaphore = asyncio.Semaphore(1)

    def run(self):

        self.make_clean_df(self.ES_df)

    def next_exp_weekday(self):
        weekdays = {2: [6, 0], 4: [0, 1, 2], 0: [3, 4]}
        today = datetime.date.today().weekday()
        for exp, day in weekdays.items():
            if today in day:
                return exp

    def next_weekday(self, d, weekday):

        days_ahead = weekday - d.weekday()
        if days_ahead <= 0:  # Target day already happened this week
            days_ahead += 7
        date_to_return = d + datetime.timedelta(
            days_ahead)  # 0 = Monday, 1=Tuself.ESday, 2=Wednself.ESday...
        return date_to_return.strftime('%Y%m%d')

    def get_strikes_and_expiration(self):

        expiration = self.next_weekday(datetime.date.today(),
                                       self.next_exp_weekday())
        chains = self.ib.reqSecDefOptParams(underlyingSymbol='ES',
                                            futFopExchange='GLOBEX',
                                            underlyingSecType='FUT',
                                            underlyingConId=self.ES.conId)
        chain = util.df(chains)
        strikes = chain[chain['expirations'].astype(str).str.contains(
            expiration)].loc[:, 'strikes'].values[0]
        [ESValue] = self.ib.reqTickers(self.ES)
        ES_price = ESValue.marketPrice()
        strikes = [
            strike for strike in strikes
            if strike % 5 == 0 and ES_price - 10 < strike < ES_price + 10
        ]
        return strikes, expiration

    def get_contract(self, right, net_liquidation):
        strikes, expiration = self.get_strikes_and_expiration()
        for strike in strikes:
            contract = FuturesOption(symbol='ES',
                                     lastTradeDateOrContractMonth=expiration,
                                     strike=strike,
                                     right=right,
                                     exchange='GLOBEX')
            self.ib.qualifyContracts(contract)
            self.price = self.ib.reqMktData(contract, "", False, False)
            if float(self.price.last) * 50 >= net_liquidation:
                continue
            else:
                return contract

    def make_clean_df(self, ES_df, hashbar=None):

        ES_df = util.df(ES_df)
        ES_df['RSI'] = ta.RSI(ES_df['close'])
        ES_df['macd'], ES_df['macdsignal'], ES_df['macdhist'] = ta.MACD(
            ES_df['close'], fastperiod=12, slowperiod=26, signalperiod=9)
        ES_df['MA_9'] = ta.MA(ES_df['close'], timeperiod=9)
        ES_df['MA_21'] = ta.MA(ES_df['close'], timeperiod=21)
        ES_df['MA_200'] = ta.MA(ES_df['close'], timeperiod=200)
        ES_df['EMA_9'] = ta.EMA(ES_df['close'], timeperiod=9)
        ES_df['EMA_21'] = ta.EMA(ES_df['close'], timeperiod=21)
        ES_df['EMA_200'] = ta.EMA(ES_df['close'], timeperiod=200)
        ES_df['ATR'] = ta.ATR(ES_df['high'], ES_df['low'], ES_df['close'])
        ES_df['roll_max_cp'] = ES_df['high'].rolling(20).max()
        ES_df['roll_min_cp'] = ES_df['low'].rolling(20).min()
        ES_df['roll_max_vol'] = ES_df['volume'].rolling(20).max()
        ES_df.dropna(inplace=True)
        self.loop_function(ES_df)

    def placeOrder(self, contract, order):

        trade = self.ib.placeOrder(contract, order)
        tradeTime = datetime.datetime.now()
        return ([trade, contract, tradeTime])

    def sell(self, contract, position):
        self.ib.qualifyContracts(contract)
        if position.position > 0:
            order = 'Sell'
        else:
            order = 'Buy'

        marketorder = MarketOrder(order, abs(position.position))
        marketTrade, contract, tradeTime = self.placeOrder(
            contract, marketorder)
        while self.ib.position.position != 0:
            self.ib.sleep(1)
        self.mySemaphore.release()

    async def buy(self, contract):
        await self.semaphore.acquire()
        self.ib.qualifyContracts(contract)
        marketorder = MarketOrder('Buy', 1)
        marketTrade = self.ib.placeOrder(contract, marketorder)

    def order_verify(self, order):
        if order.position == 0.0 or order.position < 0:
            self.Buy = True
            self.Sell = False
        elif order.position > 0:
            self.Buy = False
            self.Sell = True

        else:
            self.Buy = False
            self.Sell = False
        print(f'Buy= {self.Buy}, sell = {self.Sell}')

    def loop_function(self, ES_df):

        df = ES_df[[
            'high', 'low', 'volume', 'close', 'RSI', 'ATR', 'roll_max_cp',
            'roll_min_cp', 'roll_max_vol', 'EMA_9', 'EMA_21', 'macd',
            'macdsignal'
        ]]

        if self.tickers_signal == "Hold":
            print('Hold')
            if df["high"].iloc[self.i] >= df["roll_max_cp"].iloc[self.i] and \
                    df["volume"].iloc[self.i] > df["roll_max_vol"].iloc[self.i - 1] and df['RSI'].iloc[self.i] > 30 \
                    and df['macd'].iloc[self.i] > df['macdsignal'].iloc[self.i] :
                self.tickers_signal = "Buy"
                return


            elif df["low"].iloc[self.i] <= df["roll_min_cp"].iloc[self.i] and \
                    df["volume"].iloc[self.i] > df["roll_max_vol"].iloc[self.i - 1] and df['RSI'].iloc[self.i] < 70 \
                    and df['macd'].iloc[self.i] < df['macdsignal'].iloc[self.i]:
                self.tickers_signal = "Sell"
                return

            else:
                self.tickers_signal = "Hold"
                return

        elif self.tickers_signal == "Buy":
            print('BUY SIGNAL')
            if df["close"].iloc[self.i] > df["close"].iloc[self.i - 1] - (
                    0.75 * df["ATR"].iloc[self.i - 1]) and len(
                        self.ib.positions()) != 0:
                print(
                    f'{df["close"].iloc[self.i]} > {df["close"].iloc[self.i - 1] - (0.75 * df["ATR"].iloc[self.i - 1])}'
                )
                print('first buy condition')
                positions = self.ib.positions()
                for position in positions:
                    if position.contract.right == 'C':
                        self.sell(position.contract, position)
                        self.tickers_signal = "Hold"
                        return



            elif df["low"].iloc[self.i] <= df["roll_min_cp"].iloc[self.i] and \
                    df["volume"].iloc[self.i] > df["roll_max_vol"].iloc[self.i - 1] and df['RSI'].iloc[self.i] < 70 \
                    and df['macd'].iloc[self.i] < df['macdsignal'].iloc[self.i] and len(self.ib.positions())!=0:
                self.tickers_signal = "Sell"
                print('sell')
                positions = self.ib.positions()
                for position in positions:
                    if position.contract.right == 'C':
                        self.sell(position.contract, position)
                        self.tickers_signal == "Sell"
                        return

            else:
                if len(self.ib.positions()) == 0:
                    self.option['call'] = self.get_contract(
                        right="C", net_liquidation=2000)
                    self.buy(self.option['call'])
                    self.tickers_signal = "Hold"
                else:
                    self.tickers_signal = "Hold"

        elif self.tickers_signal == "Sell":
            print('SELL SIGNAL')
            if df["close"].iloc[self.i] < df["close"].iloc[self.i - 1] + (
                    0.75 * df["ATR"].iloc[self.i - 1]) and len(
                        self.ib.positions()) != 0:
                print('first sell condition')
                print(
                    f'{df["close"].iloc[self.i]} < {df["close"].iloc[self.i - 1] - (0.75 * df["ATR"].iloc[self.i - 1])}'
                )
                print('sell')
                positions = self.ib.positions()
                for position in positions:
                    if position.contract.right == 'P':
                        self.sell(position.contract, position)
                        self.tickers_signal = "Hold"
                        return



            elif df["high"].iloc[self.i] >= df["roll_max_cp"].iloc[self.i] and \
                    df["volume"].iloc[self.i] > df["roll_max_vol"].iloc[self.i - 1] and df['RSI'].iloc[self.i] > 30 \
                    and df['macd'].iloc[self.i] > df['macdsignal'].iloc[self.i] and len(self.ib.positions())!=0:
                self.tickers_signal = "Buy"
                print('sell')
                positions = self.ib.positions()
                for position in positions:
                    if position.contract.right == 'P':
                        self.sell(position.contract, position)
                        self.tickers_signal == "Buy"
                        return

            else:
                if len(self.ib.positions()) == 0:
                    self.option['put'] = self.get_contract(
                        right="P", net_liquidation=2000)
                    self.buy(self.option['put'])
                    self.tickers_signal = "Hold"
                else:
                    self.tickers_signal = "Hold"

    def checkError(self, errCode, errString):
        print('Error Callback', errCode, errString)
        if errCode == 2104:
            print('re-connect after 5 secs')
            self.ib.sleep(5)
            self.ib.disconnect()
            self.ib.connect('127.0.0.1',
                            7497,
                            clientId=np.random.randint(10, 1000))
            self.make_clean_df(self.ES)
Example #6
0
class IbManager(object):
    log_file = 'ib_manager'

    def __init__(self, ip: str, port: int, client_id: int):
        self._ib = IB()
        self._ib_ip: str = ip
        self._ib_port: int = port
        self._client_id: int = client_id
        self._subscribed_mkt_contracts: List[str] = []
        self._subscribed_mkt_depth_contracts: List[str] = []
        self._log: Log = Log.create(Log.path(self.log_file))
        self._logger = self._log.get_logger('ibmanager')
        self._recorder: Recorder = Recorder(self._log)
        self._market_recorder: MarketRecorder = MarketRecorder(
            self._ib, self._recorder)
        self._account_recorder: AccountRecorder = AccountRecorder(
            self._ib, self._recorder)
        self._keep_connection_task: asyncio.Task = None
        self._ib.connectedEvent += self.on_ib_connected
        self._ib.disconnectedEvent += self.on_ib_disconnected
        self._reconnect_flag: bool = False

    def on_ib_connected(self) -> None:
        self._logger.info('connected with ib')
        self._reconnect_flag = False
        self._recover_subscriptions()

    def on_ib_disconnected(self) -> None:
        self._logger.warning('disconnected with ib')
        self._reconnect_flag = True
        if self._keep_connection_task is None:
            self._keep_connection_task = asyncio.create_task(self._reconnect())

    async def _reconnect(self) -> None:
        while self._reconnect_flag:
            await asyncio.sleep(20)
            self._logger.info('try to reconnect ib gateway')
            await self.initialize()
        self._keep_connection_task = None

    def _recover_subscriptions(self) -> None:
        for contract in self._subscribed_mkt_contracts:
            self._logger.info(f'recover subscribe {str(contract)}')
            self._ib.reqMktData(contract)
        for contract in self._subscribed_mkt_depth_contracts:
            self._logger.info(f'recover subscribe depth {str(contract)}')
            self._ib.reqMktDepth(contract)

    async def initialize(self):
        if self._ib.isConnected():
            return
        try:
            await self._ib.connectAsync(self._ib_ip,
                                        self._ib_port,
                                        clientId=self._client_id)
            accounts = self._ib.managedAccounts()
            if len(accounts) > 0:
                self._account_recorder.update_account(accounts[0])
                self.update_account()
        except Exception:
            pass

    def update_account(self):
        self._ib.reqAccountSummaryAsync()

    async def find_symbols(self, pattern: str) -> List[str]:
        symbols = await self._ib.reqMatchingSymbolsAsync(pattern)
        contracts = [symbol.contract.nonDefaults() for symbol in symbols]
        return contracts

    def make_contract(self, **kwargs) -> Contract:
        return Contract.create(**kwargs)

    def sub_market(self, contract: Contract) -> str:
        if contract in self._subscribed_mkt_contracts:
            return 'already subscribe {}'.format(str(contract))
        self._subscribed_mkt_contracts.append(contract)
        self._ib.reqMktData(contract)
        return 'subscribe {} success'.format(str(contract))

    def unsub_market(self, contract: Contract) -> str:
        if contract not in self._subscribed_mkt_contracts:
            return 'not ever subscribe {}'.format(str(contract))
        self._subscribed_mkt_contracts.append(contract)
        self._ib.cancelMktData(contract)
        return 'unsubscribe {} success'.format(str(contract))

    def sub_market_depth(self, contract: Contract) -> str:
        if contract in self._subscribed_mkt_depth_contracts:
            return 'already subscribe depth {}'.format(str(contract))
        self._subscribed_mkt_depth_contracts.append(contract)
        self._ib.reqMktDepth(contract)
        return 'subscribe depth {} success'.format(str(contract))

    def unsub_market_depth(self, contract: Contract) -> str:
        if contract not in self._subscribed_mkt_depth_contracts:
            return 'not ever subscribe depth {}'.format(str(contract))
        self._subscribed_mkt_contracts.remove(contract)
        self._ib.cancelMktDepth(contract)
        return 'unsubscribe depth {} success'.format(str(contract))

    def place_order(self, contract: Contract, side: str, size: int,
                    price: float) -> str:
        trade = self._place_order(contract, side, size, price)
        return str(trade)

    def _place_order(self, contract: Contract, side: str, size: int,
                     price: float) -> Trade:
        side = side.upper()
        if side not in ('SELL', 'BUY'):
            return [f'invalid order type: {side}']
        price = float(f'{round(float(price), 3):.3f}')
        order = LimitOrder(side, size, price, tif='GTC')
        trade = self._ib.placeOrder(contract, order)
        return trade

    def cancel_order(self, order_id: int) -> str:
        order_id = int(order_id)
        order = Order(orderId=order_id)
        trade = self._ib.cancelOrder(order)
        return str(trade)

    async def orders(self) -> List[str]:
        orders = await self._ib.reqOpenOrdersAsync()
        return [str(order) for order in orders]

    def portfolio(self) -> List[str]:
        results = self._ib.portfolio()
        return [str(value) for value in results]
Example #7
0
class Window(qt.QWidget):
    def __init__(self, host, port, clientId):
        qt.QWidget.__init__(self)
        self.setWindowTitle("Giulio's App")
        self.canvas = MplCanvas()
        # self.edit = qt.QLineEdit('', self)
        # self.edit.editingFinished.connect(self.add)
        self.table = HistoricalTable()
        self.MAList = []
        self.MADict = {}

        self.connectButton = qt.QPushButton('Connect')
        self.connectButton.setStyleSheet(
            "border: 1px solid black; background: white")
        self.connectButton.resize(100, 32)
        self.connectButton.setGeometry(200, 150, 100, 40)
        self.connectButton.clicked.connect(self.onConnectButtonClicked)
        self.displayButton = qt.QPushButton('Display values')
        self.displayButton.setStyleSheet(
            "border: 1px solid black; background: white")
        self.displayButton.resize(100, 32)
        self.displayButton.clicked.connect(self.onDisplayButtonClicked)
        self.cancelAllButton = qt.QPushButton('CancelAll')
        self.cancelAllButton.setStyleSheet(
            "border: 1px solid black; background: white")
        self.cancelAllButton.resize(100, 32)
        self.cancelAllButton.setGeometry(200, 150, 100, 40)
        self.cancelAllButton.clicked.connect(self.onCancelAllButtonClicked)

        layout = qt.QVBoxLayout(self)
        # layout.addWidget(self.edit)
        layout.addWidget(self.table)
        #layout.addWidget(self.canvas)
        layout.addWidget(self.connectButton)
        layout.addWidget(self.cancelAllButton)
        # layout.addStretch(1)
        # self.fig = plt.figure()
        # self.ax = self.fig.add_subplot(1, 1, 1)
        self.xs = []
        self.ys = []
        # layout.addWidget(self.fig)
        self.connectInfo = (host, port, clientId)
        self.ib = IB()
        self.headers = [
            'symbol', 'bidSize', 'bid', 'ask', 'askSize', 'last', 'lastSize',
            'close'
        ]
        self.id = 1
        self.firstSignal = True
        self.isConnectionBroken = False
        self.firstma50 = 0
        self.firstma200 = 0
        self.availableCash = 0
        self.ib.orderStatusEvent += self.order_status_cb
        self.ib.execDetailsEvent += self.exec_details_cb
        self.ib.errorEvent += self.error_cb
        self.ib.accountSummaryEvent += self.accountSummary
        self.ib.pendingTickersEvent += self.onPendingTickers

        # self.ib.pendingTickersEvent += self.table.onPendingTickers

    async def accountSummaryAsync(self, account: str = '') -> \
            List[AccountValue]:
        if not self.wrapper.acctSummary:
            # loaded on demand since it takes ca. 250 ms
            await self.reqAccountSummaryAsync()
        if account:
            return [
                v for v in self.wrapper.acctSummary.values()
                if v.account == account
            ]
        else:
            return list(self.wrapper.acctSummary.values())

    def accountSummary(self, account: str = '') -> List[AccountValue]:
        if (account.tag == 'BuyingPower'):
            logging.info('account buying power - ' + account.value)
            accVal: float = 0.0
            accVal = account.value
            self.availableCash = float(accVal)
            self.availableCash = round(self.availableCash, 2)
            logging.info('available cash - ' + str(self.availableCash))
        logging.info("account summary:: " + str(account.account) + " " +
                     account.tag + " " + account.value)

        return []  #self._run(self.accountSummaryAsync(account))

    def error_cb(self, reqId, errorCode, errorString, contract):
        logging.error("error: " + str(reqId) + " , " + str(errorCode) + " , " +
                      str(errorString))
        logging.error("string - " + str(errorString))
        """if(errorCode == 1100):
            logging.error("Connectivity between IB and TWS has been lost")
            self.isConnectionBroken = True
        if (errorCode == 1300):
            logging.error("socket connection dropped")
            self.isConnectionBroken = True
        if(errorCode == 2105):
            logging.error("HMDS data farm connection is broken")
        if ((errorCode == 2104 or errorCode == 2106) and self.isConnectionBroken == True):
            logging.info("HMDS data farm connection has been restored")
            self.reqData()"""

    def reqGlobalCancel(self):
        """
        Cancel all active trades including those placed by other
        clients or TWS/IB gateway.
        """
        self.ib.reqGlobalCancel()
        logging.info('reqGlobalCancel')

    def order_status_cb(self, trade):
        logging.info("order status for " + str(trade.order.orderId))
        logging.info("Status filled and remaining - " +
                     trade.orderStatus.status + " " +
                     str(trade.orderStatus.filled) + " " +
                     str(trade.orderStatus.remaining))

    def exec_details_cb(self, trade, fill):
        logging.info("exec details for " + fill.contract.symbol +
                     " with orderid " + str(fill.execution.orderId))
        if (fill.execution.side == "Sell"):
            self.availableCash += fill.execution.price

    def onPendingTickers(self, tickers):
        for ticker in tickers:
            logging.info("ticker - " + str(ticker.contract.conId) + " " +
                         str(ticker.contract.secType) + " " +
                         ticker.contract.symbol + " " +
                         ticker.contract.currency)
            for col, header in enumerate(self.headers):
                if col == 0:
                    continue
                val = getattr(ticker, header)
                symbol = ticker.contract.symbol + (ticker.contract.currency
                                                   if ticker.contract.secType
                                                   == 'CASH' else '')
                ma = self.MADict[symbol]
                logging.info("Values - " + str(ticker.contract.secType) + " " +
                             str(ticker.contract.conId) + " " + symbol + " " +
                             str(header) + " " + str(col) + " val- " +
                             str(val))
                if (str(header) == 'bid'):
                    ma.bid = val
                if (str(header) == 'ask'):
                    ma.ask = val

    def onBarUpdate(self, bars, hasNewBar):
        self.xs.append(dt.datetime.now().strftime('%H:%M:%S.%f'))
        # logging.debug("bar update " + str(hasNewBar) + " for " + str(bars.reqId))
        logging.info(bars[-1])
        symbol = bars.contract.symbol + (
            bars.contract.currency if bars.contract.secType == 'CASH' else '')
        ma = self.MADict[symbol]
        logging.info("update for " + ma.symbol)
        df = util.df(bars)
        # logging.debug(df)
        ma.setMAs(df)
        ma50 = ta.MA(df['close'], 50)
        ma200 = ta.MA(df['close'], 200)
        self.ys.append(ma50)

        self.xs = self.xs[-50:]
        self.ys = self.ys[-50:]

        # self.ax.clear()
        # self.ax.plot(self.xs, self.ys)
        plt.xticks(rotation=45, ha='right')
        plt.subplots_adjust(bottom=0.30)
        plt.title('50MA')
        plt.ylabel('MA')
        """logging.debug("ma50")
        logging.debug(ma50)
        logging.debug("ma200")
        logging.debug(ma200)
        logging.debug("last items")
        logging.debug(ma50.tail(1).item())
        logging.debug(ma200.tail(1).item())"""
        orderList = ma.checkGCDC()
        if (orderList is not None):
            orderQuantity = 0
            for order in orderList:
                if (order.orderType == "LMT"):
                    if (order.action == "Buy"):
                        order.totalQuantity = 1000  #(self.availableCash/ma.bid) * .01
                        self.availableCash -= (order.totalQuantity *
                                               order.trailStopPrice)
                        logging.info("Placing buy order for " + ma.symbol +
                                     " " + str(ma.bid) + " with orderId " +
                                     str(order.orderId))
                    else:
                        order.totalQuantity = 1000  #(self.availableCash/ma.ask) * .01
                        logging.info("Placing sell order for " + ma.symbol +
                                     " at " + str(ma.ask) + " with orderId " +
                                     str(order.orderId))
                    orderQuantity = order.totalQuantity
                else:
                    if (order.orderType == "TRAIL"):
                        order.totalQuantity = orderQuantity
                        if (order.action == "Buy"):
                            #order.totalQuantity = (self.availableCash / ma.bid) * .01
                            self.availableCash -= (order.totalQuantity *
                                                   order.trailStopPrice)
                            logging.info("Placing buy order for " + ma.symbol +
                                         " " + str(ma.bid) + " with orderId " +
                                         str(order.orderId))
                        else:
                            #order.totalQuantity = (self.availableCash / ma.ask) * .01
                            logging.info("Placing sell order for " +
                                         ma.symbol + " at " + str(ma.ask) +
                                         " with orderId " + str(order.orderId))

                        logging.info("Placing " + order.action +
                                     " order for " + ma.symbol + " at " +
                                     str(order.trailStopPrice) + " " +
                                     str(ma.ask) + " with orderId " +
                                     str(order.orderId) + " " +
                                     str(trade.order.orderId))
                trade = self.ib.placeOrder(bars.contract, order)

        if (ma.isOrderActive == False and ma.GCCheck == True):
            logging.info("order is not active and gccheck is true")
        self.MADict[symbol] = ma
        """if (ma.firstSignal == True):
            ma.firstma50 = round(ma50.tail(1).item(), 6)
            ma.firstma200 = round(ma200.tail(1).item(), 6)
            ma.firstSignal = False
            if (ma.firstma50 < ma.firstma200):
                logging.info("checking golden cross for " + ma.symbol + " : mas - " + str(ma.firstma50) + " " + str(ma.firstma200))
            else:
                logging.info("checking death cross for " + ma.symbol + " : mas - " + str(ma.firstma50) + " " + str(ma.firstma200))
                ma.GCCheck = False
                self.MADict[symbol] = ma
        else:
            prevma50 = ma.getMa50()
            prevma200 = ma.getMa200()
            currma50 = round(ma50.tail(1).item(), 6)
            currma200 = round(ma200.tail(1).item(), 6)
            if(ma.isOrderActive == False):
                if(ma.GCCheck == True):
                    logging.info("golden cross check for " + ma.symbol)
                    logging.info("prev mas - " + str(prevma50) + " " + str(prevma200))
                    logging.info("curr mas - " + str(currma50) + " " + str(currma200))
                    logging.info("curr bid and ask vals - " + str(ma.bid) + " " + str(ma.ask))
                    if((prevma50 <= prevma200) and (currma50 > currma200)):
                        logging.info(("golden cross occured for " + ma.symbol))
                        ma.GCCheck = False
                        if(ma.isOrderActive == False):
                            ma.isOrderActive = True
                            order = TrailOrder("Buy", 1000, ma.ask, 20)
                            trade = self.ib.placeOrder(bars.contract, order)
                            logging.info("Placing buy order for " + ma.symbol + " at " + str(order.trailStopPrice) + " " + str(ma.ask) + " with orderId " + str(order.orderId) + " " + str(trade.order.orderId))
                        self.MADict[symbol] = ma

                else:
                    logging.info("death cross check for " + ma.symbol)
                    logging.info("prev mas - " + str(prevma50) + " " + str(prevma200))
                    logging.info("curr mas - " + str(currma50) + " " + str(currma200))
                    if ((prevma50 >= prevma200) and (currma50 < currma200)):
                        logging.info(("death cross occured for " + ma.symbol))
                        ma.GCCheck = True
                        if (ma.isOrderActive == False):
                            ma.isOrderActive = True
                            order = TrailOrder("Sell", 1000, ma.bid, 20)
                            trade = self.ib.placeOrder(bars.contract, order)
                            logging.info("Placing sell order for " + ma.symbol + " at " + str(ma.bid) + " with orderId " + str(trade.order.orderId))
                        self.MADict[symbol] = ma """

        ma.setMa50(round(ma50.tail(1).item(), 6))
        ma.setMa200(round(ma200.tail(1).item(), 6))
        self.MADict[symbol] = ma

        logging.debug("MAs for " + str(bars.contract.secType) + " " +
                      str(bars.contract.symbol) + " " +
                      bars.contract.currency + " , reqid: " + str(bars.reqId) +
                      " " + str(ma50.values[-1]) + " " +
                      str(ma200.values[-1]) + " : " +
                      str(ma50.tail(1).item()) + " " +
                      str(ma200.tail(1).item()))
        self.table.updateData(bars.reqId, round(ma50.tail(1).item(), 6),
                              round(ma200.tail(1).item(), 6))
        # logging.debug(ma50.values[-1])
        # plt.close()
        # plot = util.barplot(bars)
        # clear_output(wait=True)
        # display(plot)

    def add_historical(self, text=''):
        logging.debug("text - " + text)
        logger.debug("logging")
        text = text or self.edit.text()
        if text:
            logging.debug('eval text ')  # + eval(text))
            contract = eval(text)
            logging.debug("requesting historical and mkt data for " + text)
            bars = self.ib.reqHistoricalData(contract,
                                             endDateTime='',
                                             durationStr='2000 S',
                                             barSizeSetting='10 secs',
                                             whatToShow='MIDPOINT',
                                             useRTH=True,
                                             formatDate=1,
                                             keepUpToDate=True)
            ticker = self.ib.reqMktData(contract, '', False, False, None)
            logging.info(bars[-1])
            logging.debug("sectype " + str(bars.reqId) + " " +
                          str(bars.contract.conId) + " " +
                          bars.contract.secType + " " + bars.contract.symbol +
                          " " + bars.contract.currency)
            self.table.addHistoricalData(bars.reqId, contract)
            df = util.df(bars)
            # with pd.option_context('display.max_rows', None, 'display.max_columns',
            #                       None):  # more options can be specified also
            #    logging.debug(df)
            close = pd.DataFrame(df, columns=['close'])
            logging.debug("close ")
            logging.debug(close)
            # df['pandas_SMA_3'] = df.iloc[:, 1].rolling(window=3).mean()
            # df.head()

            #ma50 = ta.MA(df['close'], 50)
            #ma200 = ta.MA(df['close'], 200)
            symbol = bars.contract.symbol + (bars.contract.currency
                                             if bars.contract.secType == 'CASH'
                                             else '')
            logging.info("symbol - " + symbol)
            ma = MovingAverages(
                self.ib, symbol, bars.reqId
            )  #, round(ma50.tail(1).item(), 6), round(ma200.tail(1).item(), 6))
            ma.setMAs(df)
            self.MAList.append(ma)
            self.MADict[symbol] = ma
            """logging.debug("ma50")
            logging.debug(ma50)
            logging.debug("ma200")
            logging.debug(ma200)
            logging.debug("initial ma vals for " + symbol)
            logging.debug(ma50.tail(1).item())
            logging.debug(ma200.tail(1).item())"""
            self.table.updateData(bars.reqId, round(ma.ma50.tail(1).item(), 6),
                                  round(ma.ma200.tail(1).item(), 6))
            # sma = pd.SMA(df['close'].values, timeperiod=4)
            """portfolio = self.ib.portfolio()#.wrapper.portfolio.cash = 10000
            logging.debug("portfolio")
            logging.debug(portfolio)
            positions = self.ib.positions()
            logging.debug("positions")
            for x in range(len(positions)):
                logging.debug(positions[x].contract.symbol)
                logging.debug(positions[x].position)"""
            # logging.debug(positions)
            bars.updateEvent += self.onBarUpdate
            logging.debug("reqid is " + str(bars.reqId) + " for " +
                          bars.contract.symbol + " " + bars.contract.currency +
                          " , sectype - " + bars.contract.secType)

    def onDisplayButtonClicked(self, _):
        logging.debug("MA values")
        for ma in self.MAList:
            logging.debug("symbol - " + " " + ma.symbol)
            logging.debug(
                str(ma.firstma50) + " " + str(ma.firstma200) + " " +
                str(ma.firstSignal) + " " + str(ma.ma50) + " " + str(ma.ma200))
        for x in self.MADict:
            logging.debug(x)
        for x in self.MADict.values():
            logging.debug("dict values - " + str(x.firstSignal) + " " +
                          x.symbol + " " + str(x.firstma50) + " " +
                          str(x.firstma200) + " " + str(x.ma50) + " " +
                          str(x.ma200))

    def onConnectButtonClicked(self, _):
        logging.debug("isconnected: " + str(self.ib.isConnected()))
        if self.ib.isConnected():
            self.ib.disconnect()
            logging.debug("clearing data")
            self.table.clearData()
            self.connectButton.setText('Connect')
            logging.debug("done")
        else:
            logging.debug("trying to connect")
            # ib = IB()
            # ib.connect('127.0.0.1', 7497, clientId=3)
            self.ib.connect('127.0.0.1', 7497,
                            clientId=2)  # *self.connectInfo)
            logging.debug("connected - ")  # + self.ib.isConnected())
            # self.ib.reqMarketDataType(2)
            self.connectButton.setText('Disconnect')
            self.ib.reqAccountSummary()
            self.reqData()

    def onCancelAllButtonClicked(self):
        logging.info("Cancelling all open orders")
        #self.ib.connect('127.0.0.1', 7497, clientId=2)  # *self.connectInfo)
        self.reqGlobalCancel()

    def reqData(self):
        #self.reqGlobalCancel()
        """for symbol in ('EURUSD', 'USDJPY', 'EURGBP', 'USDCAD',
                       'EURCHF', 'AUDUSD', 'NZDUSD'):
            logging.debug("requesting for " + symbol)
            self.add_historical(f"Forex('{symbol}')")"""
        #self.add_historical("Stock('TSLA', 'SMART', 'USD')")
        #self.add_historical("Stock('IBM', 'SMART', 'USD')")
        #self.add_historical("Stock('MSFT', 'SMART', 'USD')")
        self.add_historical("Stock('FB', 'SMART', 'USD')")

    def closeEvent(self, ev):
        logging.debug("closing")
        asyncio.get_event_loop().stop()