def execDetails(self, reqId, contract, execution): """ This wrapper handles both live fills and responses to reqExecutions. """ if execution.orderId == 2147483647: # bug in TWS: executions of manual orders have orderId=2**31 - 1 execution.orderId = 0 contract = self._getContract(contract) execId = execution.execId execution = Execution(**execution.__dict__) execution.time = util.parseIBDatetime(execution.time). \ astimezone(datetime.timezone.utc) isLive = reqId not in self._futures time = self.lastTime if isLive else execution.time fill = Fill(contract, execution, CommissionReport(), time) if execId not in self.fills: # first time we see this execution so add it self.fills[execId] = fill key = self.orderKey( execution.clientId, execution.orderId, execution.permId) trade = self.trades.get(key) if trade: trade.fills.append(fill) logEntry = TradeLogEntry(self.lastTime, trade.orderStatus.status, f'Fill {execution.shares}@{execution.price}') trade.log.append(logEntry) if isLive: self.handleEvent('execDetailsEvent', trade, fill) self._logger.info(f'execDetails: {fill}') trade.fillEvent(trade, fill) if not isLive: self._results[reqId].append(fill)
def execDetails(self, reqId, contract, execution): """ This wrapper handles both live fills and responses to reqExecutions. """ if execution.orderId == UNSET_INTEGER: # bug in TWS: executions of manual orders have unset value execution.orderId = 0 key = self.orderKey(execution.clientId, execution.orderId, execution.permId) trade = self.trades.get(key) or self.permId2Trade.get(execution.permId) if trade and contract == trade.contract: contract = trade.contract else: contract = Contract.create(**contract.dict()) execId = execution.execId execution.time = util.parseIBDatetime(execution.time). \ astimezone(datetime.timezone.utc) isLive = reqId not in self._futures time = self.lastTime if isLive else execution.time fill = Fill(contract, execution, CommissionReport(), time) if execId not in self.fills: # first time we see this execution so add it self.fills[execId] = fill if trade: trade.fills.append(fill) logEntry = TradeLogEntry( time, trade.orderStatus.status, f'Fill {execution.shares}@{execution.price}') trade.log.append(logEntry) if isLive: self._logger.info(f'execDetails: {fill}') self.ib.execDetailsEvent.emit(trade, fill) trade.fillEvent(trade, fill) if not isLive: self._results[reqId].append(fill)
def historicalDataUpdate(self, reqId, bar): bars = self.reqId2Bars[reqId] bar.date = util.parseIBDatetime(bar.date) if len(bars) == 0 or bar.date > bars[-1].date: bars.append(bar) else: bars[-1] = bar
def historicalNews( self, reqId: int, time: str, providerCode: str, articleId: str, headline: str): dt = parseIBDatetime(time) dt = cast(datetime, dt) article = HistoricalNews(dt, providerCode, articleId, headline) self._results[reqId].append(article)
def tickString(self, reqId: int, tickType: int, value: str): ticker = self.reqId2Ticker.get(reqId) if not ticker: return try: if tickType == 47: # https://interactivebrokers.github.io/tws-api/fundamental_ratios_tags.html d = dict( t.split('=') # type: ignore for t in value.split(';') if t) # type: ignore for k, v in d.items(): with suppress(ValueError): if v == '-99999.99': v = 'nan' d[k] = float(v) d[k] = int(v) ticker.fundamentalRatios = FundamentalRatios(**d) elif tickType in (48, 77): # RT Volume or RT Trade Volume string format: # price;size;ms since epoch;total volume;VWAP;single trade # example: # 701.28;1;1348075471534;67854;701.46918464;true priceStr, sizeStr, rtTime, volume, vwap, _ = value.split(';') if volume: if tickType == 48: ticker.rtVolume = int(volume) elif tickType == 77: ticker.rtTradeVolume = int(volume) if vwap: ticker.vwap = float(vwap) if rtTime: ticker.rtTime = datetime.fromtimestamp( int(rtTime) / 1000, timezone.utc) if priceStr == '': return price = float(priceStr) size = int(sizeStr) if price and size: if ticker.prevLast != ticker.last: ticker.prevLast = ticker.last ticker.last = price if ticker.prevLastSize != ticker.lastSize: ticker.prevLastSize = ticker.lastSize ticker.lastSize = size tick = TickData(self.lastTime, tickType, price, size) ticker.ticks.append(tick) elif tickType == 59: # Dividend tick: # https://interactivebrokers.github.io/tws-api/tick_types.html#ib_dividends # example value: '0.83,0.92,20130219,0.23' past12, next12, nextDate, nextAmount = value.split(',') ticker.dividends = Dividends( float(past12) if past12 else None, float(next12) if next12 else None, parseIBDatetime(nextDate) if nextDate else None, float(nextAmount) if nextAmount else None) self.pendingTickers.add(ticker) except ValueError: self._logger.error(f'tickString with tickType {tickType}: ' f'malformed value: {value!r}')
def historicalDataUpdate(self, reqId, bar): bars = self.reqId2Bars[reqId] bar.date = util.parseIBDatetime(bar.date) if len(bars) == 0 or bar.date > bars[-1].date: bars.append(bar) self._handleEvent('barUpdate', bars, True) elif bars[-1] != bar: bars[-1] = bar self._handleEvent('barUpdate', bars, False)
def get_dte(dt): '''Gets days to expiry Arg: (dt) as day in string format 'yyyymmdd' Returns: days to expiry as int''' return (util.parseIBDatetime(dt) - datetime.now().date() ).days + 1 # 1 day added to accommodate for US timezone
def historicalDataUpdate(self, reqId, bar): bar = BarData(**bar.__dict__) bars = self.reqId2Bars.get(reqId) if not bars: return bar.date = util.parseIBDatetime(bar.date) if len(bars) == 0 or bar.date > bars[-1].date: bars.append(bar) self.handleEvent('barUpdate', bars, True) elif bars[-1] != bar: bars[-1] = bar self.handleEvent('barUpdate', bars, False)
def historicalDataUpdate(self, reqId: int, bar: BarData): bars = self.reqId2Subscriber.get(reqId) if not bars: return bar.date = parseIBDatetime(bar.date) # type: ignore hasNewBar = len(bars) == 0 or bar.date > bars[-1].date if hasNewBar: bars.append(bar) elif bars[-1] != bar: bars[-1] = bar else: return self.ib.barUpdateEvent.emit(bars, hasNewBar) bars.updateEvent.emit(bars, hasNewBar)
def historicalDataUpdate(self, reqId, bar): bar = BarData(**bar.__dict__) bars = self.reqId2Subscriber.get(reqId) if not bars: return bar.date = util.parseIBDatetime(bar.date) hasNewBar = len(bars) == 0 or bar.date > bars[-1].date if hasNewBar: bars.append(bar) elif bars[-1] != bar: bars[-1] = bar else: return self._ib.barUpdateEvent.emit(bars, hasNewBar) bars.updateEvent.emit(bars, hasNewBar)
def tickString(self, reqId, tickType, value): ticker = self.reqId2Ticker.get(reqId) if not ticker: return try: if tickType == 47: # https://interactivebrokers.github.io/tws-api/fundamental_ratios_tags.html ticker.fundamentalRatios = value elif tickType == 48: # RTVolume string format: # price;size;ms since epoch;total volume;VWAP;single trade # example: # 701.28;1;1348075471534;67854;701.46918464;true price, size, _, rtVolume, vwap, _ = value.split(';') if rtVolume: ticker.rtVolume = int(rtVolume) if vwap: ticker.vwap = float(vwap) if price == '': return price = float(price) size = float(size) if price and size: if ticker.prevLast != ticker.last: ticker.prevLast = ticker.last ticker.last = price if ticker.prevLastSize != ticker.lastSize: ticker.prevLastSize = ticker.lastSize ticker.lastSize = size tick = TickData(self.lastTime, tickType, price, size) ticker.ticks.append(tick) self.pendingTickers.add(ticker) elif tickType == 59: # Dividend tick: # https://interactivebrokers.github.io/tws-api/tick_types.html#ib_dividends # example value: '0.83,0.92,20130219,0.23' past12, next12, nextDate, nextAmount = value.split(',') ticker.dividends = Dividends( float(past12) if past12 else None, float(next12) if next12 else None, util.parseIBDatetime(nextDate) if nextDate else None, float(nextAmount) if nextAmount else None) except ValueError: self._logger.error( f'tickString with tickType {tickType}: ' f'malformed value: {value!r}')
def historicalData(self, reqId, bar): bar = BarData(**bar.__dict__) bar.date = util.parseIBDatetime(bar.date) self._results[reqId].append(bar)
def historicalData(self, reqId: int, bar: BarData): bar.date = parseIBDatetime(bar.date) # type: ignore self._results[reqId].append(bar)
def headTimestamp(self, reqId, headTimestamp): dt = util.parseIBDatetime(headTimestamp) self._endReq(reqId, dt)
def realtimeBar(self, reqId, time, open_, high, low, close, volume, wap, count): dt = util.parseIBDatetime(time) bar = RealTimeBar(dt, -1, open_, high, low, close, volume, wap, count) self.reqId2Bars[reqId].append(bar)
async def get_dte(dt): return await (util.parseIBDatetime(dt) - datetime.datetime.now().date()).days
contracts = [c for cs in contracts for c in cs if c] for c in contracts: df_ohlc = ib.run(get_ohlc(ib, c)) undPrice = ib.run(get_tick(ib, c))[0].marketPrice() chains = {c.symbol: ib.run(get_chain(ib, c))[0]} sek = {b for a in [list(product([k], m.expirations, m.strikes)) for k, m in chains.items()] for b in a} dfc = pd.DataFrame(list(sek), columns=[ 'symbol', 'expiry', 'strike']) dfc = dfc.assign(dte=[(util.parseIBDatetime( dt)-datetime.datetime.now().date()).days for dt in dfc.expiry]) # Limit to max and min dte dfc = dfc[dfc.dte <= data['common']['maxdte']] dfc = dfc[dfc.dte >= data['common']['mindte']] # integrate rise, fall and stdev dfc = dfc.join(dfc.dte.apply( lambda x: df_ohlc.iloc[x][['rise', 'fall', 'sd']])) # remove the calls and puts whose strike is in the threshold of st dev dfc['undPrice'] = undPrice dfc = dfc.assign(right=np.where( dfc.strike >= dfc.undPrice, 'C', 'P')) c_mask = (dfc.right == 'C') & ( dfc.strike > dfc.undPrice + data['common']['callstdmult']*dfc.sd)
def historicalData(self, reqId: int, bar: BarData): results = self._results.get(reqId) if results is not None: bar.date = parseIBDatetime(bar.date) # type: ignore results.append(bar)
def headTimestamp(self, reqId, headTimestamp): try: dt = util.parseIBDatetime(headTimestamp) self._endReq(reqId, dt) except ValueError as exc: self._endReq(reqId, exc, False)
def get_dte(dt): '''Gets days to expiry Arg: (dt) as day in string format 'yyyymmdd' Returns: days to expiry as int''' return (util.parseIBDatetime(dt) - datetime.datetime.now().date()).days