def updatePortfolio(self, contract: Contract, posSize: float, marketPrice: float, marketValue: float, averageCost: float, unrealizedPNL: float, realizedPNL: float, account: str): contract = Contract.create(**dataclassAsDict(contract)) portfItem = PortfolioItem(contract, posSize, marketPrice, marketValue, averageCost, unrealizedPNL, realizedPNL, account) portfolioItems = self.portfolio[account] if posSize == 0: portfolioItems.pop(contract.conId, None) else: portfolioItems[contract.conId] = portfItem self._logger.info(f'updatePortfolio: {portfItem}') self.ib.updatePortfolioEvent.emit(portfItem)
def position( self, account: str, contract: Contract, posSize: float, avgCost: float): contract = Contract.create(**dataclassAsDict(contract)) position = Position(account, contract, posSize, avgCost) positions = self.positions[account] if posSize == 0: positions.pop(contract.conId, None) else: positions[contract.conId] = position self._logger.info(f'position: {position}') results = self._results.get('positions') if results is not None: results.append(position) self.ib.positionEvent.emit(position)
def openOrder( self, orderId: int, contract: Contract, order: Order, orderState: OrderState): """ This wrapper is called to: * feed in open orders at startup; * feed in open orders or order updates from other clients and TWS if clientId=master id; * feed in manual orders and order updates from TWS if clientId=0; * handle openOrders and allOpenOrders responses. """ if order.whatIf: # response to whatIfOrder if orderState.commissionCurrency: self._endReq(order.orderId, orderState) else: key = self.orderKey(order.clientId, order.orderId, order.permId) trade = self.trades.get(key) if trade: trade.order.permId = order.permId trade.order.totalQuantity = order.totalQuantity trade.order.lmtPrice = order.lmtPrice trade.order.auxPrice = order.auxPrice trade.order.orderType = order.orderType else: # ignore '?' values in the order order = Order(**{ k: v for k, v in dataclassAsDict(order).items() if v != '?'}) contract = Contract.create(**dataclassAsDict(contract)) orderStatus = OrderStatus( orderId=orderId, status=orderState.status) trade = Trade(contract, order, orderStatus, [], []) self.trades[key] = trade self._logger.info(f'openOrder: {trade}') self.permId2Trade.setdefault(order.permId, trade) results = self._results.get('openOrders') if results is None: self.ib.openOrderEvent.emit(trade) else: # response to reqOpenOrders or reqAllOpenOrders results.append(order) # make sure that the client issues order ids larger then any # order id encountered (even from other clients) to avoid # "Duplicate order id" error self.ib.client.updateReqId(orderId + 1)
def openOrder(self, orderId, contract, order, orderState): if order.whatIf: # response to whatIfOrder orderState = OrderState(**orderState.__dict__) self._endReq(orderId, orderState) else: contract = Contract(**contract.__dict__) order = Order(**order.__dict__) orderStatus = OrderStatus(status=orderState.status) if order.softDollarTier: order.softDollarTier = SoftDollarTier( **order.softDollarTier.__dict__) trade = Trade(contract, order, orderStatus, [], []) if order.clientId == self.clientId and orderId not in self.trades: self.trades[orderId] = trade _logger.info(f'openOrder: {trade}') results = self._results.get('openOrders') if results is not None: # response to reqOpenOrders results.append(order)
def execDetails( self, reqId: int, contract: Contract, execution: Execution): """ This wrapper handles both live fills and responses to reqExecutions. """ self._logger.info(f'execDetails {execution}') if execution.orderId == UNSET_INTEGER: # bug in TWS: executions of manual orders have unset value execution.orderId = 0 trade = self.permId2Trade.get(execution.permId) if not trade: key = self.orderKey( execution.clientId, execution.orderId, execution.permId) trade = self.trades.get(key) if trade and contract == trade.contract: contract = trade.contract else: contract = Contract.create(**dataclassAsDict(contract)) execId = execution.execId 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 is_open_today(contracthours: Contract): # a return of NONE is when the market is not opern for the given day """ Parse contract Trading Hours to Check if Valid Trading Day""" #rint("contract hours",contracthours) date_re = compile_re(r"([0-9]{8}):([0-9]+)-([0-9]{8}):([0-9]+)") #rint("date_re: ",date_re) days = contracthours.split(";") #parse the list today = datetime.today().strftime("%Y%m%d") #today in fomat matching the list yesterday = (datetime.today() - timedelta(days=1)).strftime("%Y%m%d") tomorrow = (datetime.today() + timedelta(days=1)).strftime("%Y%m%d") hours = [] tradingDayType, tradingDayRules, currentTimeFrame = checkDayType(today) log.debug("Trading date: {td} day type: {dt}".format(td=today,dt=tradingDayType)) for day in days: match = date_re.match(day) #rint("date re: ",date_re.match(day)) if not match: continue if match.group(1) not in [today, yesterday]: continue if match.group(3) not in [today, yesterday]: continue hours += ["{0}-{1}".format(match.group(2), match.group(4))] today_hours = ",".join(hours) log.debug("todays trading hours are: {th}".format(th=today_hours)) #csv_file = csv.reader(open('data/tradinghours.csv', "rt"), delimiter = ",") #hours_found = False #for row in csv_file: # if today_hours == row[0]: # hours_found = True # break #if not hours_found: # with open('data/tradinghours.csv', mode='a') as tradehours: # histwriter = csv.writer(tradehours, delimiter=',', quotechar='"', quoting=csv.QUOTE_MINIMAL) # histwriter.writerow([today_hours]) if today_hours == config.NORMAL_TRADING_HOURS: return True, tradingDayRules, currentTimeFrame return False, tradingDayRules, currentTimeFrame
def openOrder(self, orderId: int, contract: Contract, order: Order, orderState: OrderState): """ This wrapper is called to: * feed in open orders at startup; * feed in open orders or order updates from other clients and TWS if clientId=master id; * feed in manual orders and order updates from TWS if clientId=0; * handle openOrders and allOpenOrders responses. """ if order.whatIf: # response to whatIfOrder self._endReq(order.orderId, orderState) else: key = self.orderKey(order.clientId, order.orderId, order.permId) trade = self.trades.get(key) if trade: trade.order.permId = order.permId else: # ignore '?' values in the order order = Order(**{ k: v for k, v in dataclassAsDict(order).items() if v != '?' }) contract = Contract.create(**dataclassAsDict(contract)) orderStatus = OrderStatus(orderId=orderId, status=orderState.status) trade = Trade(contract, order, orderStatus, [], []) self.trades[key] = trade self._logger.info(f'openOrder: {trade}') self.permId2Trade.setdefault(order.permId, trade) results = self._results.get('openOrders') if results is None: self.ib.openOrderEvent.emit(trade) else: # response to reqOpenOrders or reqAllOpenOrders results.append(order)
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 key = self.orderKey(execution.clientId, execution.orderId, execution.permId) trade = self.trades.get(key) if trade and contract.conId == trade.contract.conId: contract = trade.contract else: contract = Contract(**contract.__dict__) 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 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 run(self): """ Execute the algorithm """ key_arr = [ 'blank', 'ATR15', 'ATR1', 'ATRD', 'CCI15', 'CCIA15', 'CCIA1h', 'CCIA1d', 'BBW15', 'BBb15', 'BBW1h', 'BBb1h', 'BBW1d', 'BBb1d' ] tradenow = False not_finished = True tradenow = False cci_trade = False ccibb_trade = False while not_finished: print("top of algo run self") crossed = False self.app.crossover.update(crossed) contContract = get_contract(self) dataContract = Contract(exchange=config.EXCHANGE, secType="FUT", localSymbol=contContract.localSymbol) log.info("Got Contract: {}".format(dataContract.localSymbol)) self.app.contract.update(dataContract.localSymbol) # my add nextday = datetime.now().day nexthour = datetime.now().hour # endDateTime=datetime(int(date.today().year),int(date.today().month),int(nextday),int(nexthour),int(nextqtr),int(0)), if datetime.now().minute < 15: nextqtr = 15 getqtr = 30 elif datetime.now().minute < 30: nextqtr = 30 getqtr = 45 elif datetime.now().minute < 45: nextqtr = 45 getqtr = 0 else: nexthour = nexthour + 1 nextqtr = 0 getqtr = 15 test = (datetime(int(date.today().year), int(date.today().month), int(nextday), int(nexthour), int(getqtr), int(0))) print("nextqtr and getqtr {} {}".format(nextqtr, getqtr)) log.info( "next datetime for 15 minutes - should be 15 minutes ahead of desired nextqtr{}" .format(test)) # #nextqtr = datetime.now().minute + 1 #print ("we manually overwrote start time") #print (nextqtr) # self.ib.waitUntil(time(hour=nexthour, minute=nextqtr)) # 15 Minute Data self.app.qtrhour.update(datetime.now()) print(datetime.now()) log.info( "requesting info for the following timeframe today: {} nexthour: {} minutes: {}" .format(date.today().day, nexthour + 1, getqtr)) bars_15m = self.get_bars_data(dataContract, "2 D", "15 mins", date.today().day, nexthour + 1, getqtr) x = np.array(bars_15m) log.debug("15 min bars {}".format(bars_15m[-1])) #sef.app. barupdateEvent_15m(baas_15m, True) #aars_15m.updateEvent += self.app.barupdateEvent_15m log.info("Got 15m data subscription") cci, avg, cci_prior, averageh, cci3 = calculate_cci(bars_15m) atr, atrprior = calculate_atr(bars_15m) bband_width, bband_b, bband_width_prior, bband_b_prior = calculate_bbands( bars_15m) log.info("starting 15 minutes".format(datetime.now())) log.info("CCI: {} ".format(cci)) log.info("CCIA {} ".format(avg)) log.info("CCIP {} ".format(cci_prior)) log.info("CCIPA: {} ".format(averageh)) log.info("ATR: {} ".format(atr)) log.info("bband w: {} ".format(bband_width)) log.info("bband p: {} ".format(bband_b)) qtrtime = datetime.now() #key_arr = [] if (cci > cci_prior and cci_prior < cci3) or (cci < cci_prior and cci_prior > cci3): crossed = True tradenow = True csv_row = "'" + str(datetime.now()) + ",'long'" key_arr[0] = "long" else: crossed = True tradenow = True csv_row = "'" + str(datetime.now()) + ",'short'" key_arr[0] = "short" csv_row += ",'" + str(crossed) + "'," + str(cci) + "," + str( avg) + "," + str(cci_prior) + "," + str(averageh) + "," + str( atr) + "," + str(bband_width) + "," + str(bband_b) print(key_arr) key_arr[1] = categories.categorize_atr15(atr) key_arr[4] = categories.categorize_cci_15(cci) key_arr[5] = categories.categorize_cci_15_avg(avg) key_arr[8] = categories.categorize_BBW15(bband_width) key_arr[9] = categories.categorize_BBb15(bband_b) stat = "check crossed status" #print("printing self app ********************************************") #print(Indicator.data) self.app.status1.update(stat) self.app.crossover.update(crossed) self.app.cci15.update(f"{cci:.02f}") self.app.cci15_av.update(f"{avg:.02f}") self.app.cci15p_av.update(f"{averageh:.02f}") self.app.cci15p.update(f"{cci_prior:.02f}") self.app.atr15.update(f"{atr:.02f}") self.app.bband15_width.update(f"{bband_width:.04f}") self.app.bband15_b.update(f"{bband_b:.04f}") self.app.qtrhour.update(qtrtime) #1 hour data test = (datetime(int(date.today().year), int(date.today().month), int(nextday), int(nexthour), int(getqtr), int(0))) #log.info("next datetime for 1 hour - should be 1 hour behind current hour {}".format(test)) log.info( "requesting info for the following timeframe today: {} nexthour: {} minutes: {} " .format(date.today().day, nexthour, 0)) bars_1h = self.get_bars_data(dataContract, "5 D", "1 hour", date.today().day, nexthour, 0) cci, avg, cci_prior, averageh, cci3 = calculate_cci(bars_1h) log.debug("bars_1h {}".format(bars_1h[-1])) atr, atrprior = calculate_atr(bars_1h) bband_width, bband_b, bband_width_prior, bband_b_prior = calculate_bbands( bars_1h) csv_row += "," + str(cci) + "," + str(avg) + "," + str( atr) + "," + str(bband_width) + "," + str(bband_b) key_arr[2] = categories.categorize_atr1h(atr) key_arr[6] = categories.categorize_cci_1h(avg) key_arr[10] = categories.categorize_BBW1h(bband_width) key_arr[11] = categories.categorize_BBb1h(bband_b) log.info("starting 1H ") log.info("CCI {} ".format(cci)) log.info("CCIA: {} ".format(avg)) log.info("ATR: {} ".format(atr)) log.info("bband w: {} ".format(bband_width)) log.info("bband p: {} ".format(bband_b)) qtrtime = datetime.now() self.app.cci1h.update(f"{cci:.02f}") self.app.cci1h_av.update(f"{avg:.02f}") self.app.bband1h_b.update(f"{bband_b:.04f}") self.app.bband1h_width.update(f"{bband_width:.04f}") self.app.atr1h.update(f"{atr:.02f}") log.info( "requesting info for the following timeframe today: nextday: {} hour: {} minute: {} " .format((nextday - 1), 0, 0)) bars_1d = self.get_bars_data(dataContract, "75 D", "1 day", nextday - 1, 0, 0) log.debug("1d min bars {}".format(bars_1d[-1])) cci, avg, cci_prior, averageh, cci3 = calculate_cci(bars_1d) atr, atrprior = calculate_atr(bars_1d) bband_width, bband_b, bband_width_prior, bband_b_prior = calculate_bbands( bars_1d) csv_row += "," + str(cci) + "," + str(avg) + "," + str( atr) + "," + str(bband_width) + "," + str(bband_b) key_arr[3] = categories.categorize_atr1d(atr) key_arr[7] = categories.categorize_cci_1d(avg) key_arr[12] = categories.categorize_BBW1d(bband_width) key_arr[13] = categories.categorize_BBb1d(bband_b) log.info("starting 1H ") log.info("starting 1D ") log.info("CCIP {} ".format(cci)) log.info("CCIPA: {} ".format(avg)) log.info("ATR: {} ".format(atr)) log.info("bband w: {} ".format(bband_width)) log.info("bband p: {} ".format(bband_b)) qtrtime = datetime.now() qtrtime = datetime.now() self.app.cci1d.update(f"{cci:.02f}") self.app.cci1d_av.update(f"{avg:.02f}") self.app.bband1d_b.update(f"{bband_b:.04f}") self.app.bband1d_width.update(f"{bband_width:.04f}") self.app.atr1d.update(f"{atr:.02f}") if tradenow: csv_file = csv.reader(open('data/ccibb.csv', "rt"), delimiter="'") for row in csv_file: if (''.join(key_arr)) == row[0]: print("we have a match in ccibb.csv") print(row) ccibb_trade = True csv_file = csv.reader(open('data/cci.csv', "rt"), delimiter="'") for row in csv_file: if (''.join(key_arr[0:7])) == row[0]: print("we have a match in cci.csv") print(row) cci_trade = True csv_row += "," + (''.join(key_arr)) + "," + str( ccibb_trade) + "," + str(ccibb_trade) with open('data/hist15.csv', mode='a') as hist15: histwriter = csv.writer(hist15, delimiter=',', quotechar='"', quoting=csv.QUOTE_MINIMAL) histwriter.writerow([csv_row]) print("key array comming *************************") print(''.join(key_arr)) log.info("key array {}".format(key_arr)) #key_arr.clear() tradenow = False cci_trade = False ccibb_trade = False
def symbolSamples(self, reqId, contractDescriptions): cds = [ContractDescription( **cd.__dict__) for cd in contractDescriptions] for cd in cds: cd.contract = Contract(**cd.contract.__dict__) self._endReq(reqId, cds)
def contractDetails(self, reqId, contractDetails): cd = ContractDetails(**contractDetails.__dict__) cd.summary = Contract(**cd.summary.__dict__) if cd.secIdList: cd.secIdList = [TagValue(s.tag, s.value) for s in cd.secIdList] self._results[reqId].append(cd)
def roll_positions(self, positions, right): for position in positions: symbol = position.contract.symbol sell_ticker = self.find_eligible_contracts( symbol, right, excluded_expirations=[ position.contract.lastTradeDateOrContractMonth ], ) self.wait_for_midpoint_price(sell_ticker) quantity = abs(position.position) position.contract.exchange = "SMART" [buy_ticker] = self.ib.reqTickers(position.contract) self.wait_for_midpoint_price(buy_ticker) price = midpoint_or_market_price( buy_ticker) - midpoint_or_market_price(sell_ticker) # Create combo legs comboLegs = [ ComboLeg( conId=position.contract.conId, ratio=1, exchange="SMART", action="BUY", ), ComboLeg( conId=sell_ticker.contract.conId, ratio=1, exchange="SMART", action="SELL", ), ] # Create contract combo = Contract( secType="BAG", symbol=symbol, currency="USD", exchange="SMART", comboLegs=comboLegs, ) # Create order order = LimitOrder( "BUY", quantity, round(price, 2), algoStrategy="Adaptive", algoParams=[TagValue("adaptivePriority", "Patient")], tif="DAY", ) # Submit order trade = self.wait_for_trade_submitted( self.ib.placeOrder(combo, order)) click.secho("Order submitted", fg="green") click.secho(f"{trade}", fg="green")
def lookup_contracts(symbols): return [Contract(**s) for s in symbols]
def run(self): """ Execute the algorithm """ key_arr = [ 'blank', 'ATR15', 'ATR1', 'ATRD', 'CCI15', 'CCIA15', 'CCIA1h', 'CCIA1d', 'BBW15', 'BBb15', 'BBW1h', 'BBb1h', 'BBW1d', 'BBb1d' ] tradenow = False not_finished = True tradenow = False cci_trade = False ccibb_trade = False while not_finished: print("top of algo run self") crossed = False self.app.crossover.update(crossed) contContract = get_contract(self) #print(contract.tradingHours.split(";")) #open_today = helpers.is_open_today(contract) #print("open today ",open_today) dataContract = Contract(exchange=config.EXCHANGE, secType="FUT", localSymbol=contContract.localSymbol) log.info("Got Contract: {}".format(dataContract.localSymbol)) self.app.contract.update(dataContract.localSymbol) wait_time, datetime_15, datetime_1h, datetime_1d = self.define_times( ) log.info( "next datetime for 15 minutes - should be 15 minutes ahead of desired nextqtr{}" .format(wait_time)) # #nextqtr = datetime.now().minute + 1 #print ("we manually overwrote start time") #print (nextqtr) # self.ib.waitUntil(wait_time) # 15 Minute Data self.app.qtrhour.update(wait_time) log.info("requesting info for the following timeframe today: {} ". format(wait_time)) bars_15m = self.get_bars_data(dataContract, "2 D", "15 mins", datetime_15) x = np.array(bars_15m) log.info("15 min bars {}".format(bars_15m[-1])) #sef.app. barupdateEvent_15m(baas_15m, True) #aars_15m.updateEvent += self.app.barupdateEvent_15m log.info("Got 15m data subscription") cci, avg, cci_prior, averageh, cci3 = calculate_cci(bars_15m) atr, atrprior = calculate_atr(bars_15m) bband_width, bband_b, bband_width_prior, bband_b_prior = calculate_bbands( bars_15m) log.info("starting 15 minutes".format(datetime.now())) log.info("CCI: {} ".format(cci)) log.info("CCIA {} ".format(avg)) log.info("CCIP {} ".format(cci_prior)) log.info("CCIPA: {} ".format(averageh)) log.info("ATR: {} ".format(atr)) log.info("bband w: {} ".format(bband_width)) log.info("bband p: {} ".format(bband_b)) qtrtime = datetime.now() #key_arr = [] if cci > cci_prior and cci_prior < cci3: crossed = True tradenow = True csv_row = "'" + str(datetime.now()) + ",'long'" key_arr[0] = "long" elif cci < cci_prior and cci_prior > cci3: crossed = True tradenow = True csv_row = "'" + str(datetime.now()) + ",'short'" key_arr[0] = "short" else: csv_row = "'" + str(datetime.now()) + ",'flat'" crossed = False tradenow = False csv_row += ",'" + str(crossed) + "'," + str(cci) + "," + str( avg) + "," + str(cci_prior) + "," + str(averageh) + "," + str( atr) + "," + str(bband_width) + "," + str(bband_b) print(key_arr) key_arr[1] = categories.categorize_atr15(atr) key_arr[4] = categories.categorize_cci_15(cci) key_arr[5] = categories.categorize_cci_15_avg(avg) key_arr[8] = categories.categorize_BBW15(bband_width) key_arr[9] = categories.categorize_BBb15(bband_b) stat = "check crossed status" #print("printing self app ********************************************") #print(Indicator.data) self.app.status1.update(stat) self.app.crossover.update(crossed) self.app.cci15.update(f"{cci:.02f}") self.app.cci15_av.update(f"{avg:.02f}") self.app.cci15p_av.update(f"{averageh:.02f}") self.app.cci15p.update(f"{cci_prior:.02f}") self.app.atr15.update(f"{atr:.02f}") self.app.bband15_width.update(f"{bband_width:.04f}") self.app.bband15_b.update(f"{bband_b:.04f}") self.app.qtrhour.update(qtrtime) #1 hour data log.info( "next datetime for 1 hour - should be 1 hour behind current hour {}" .format(datetime_1h)) bars_1h = self.get_bars_data(dataContract, "5 D", "1 hour", datetime_1h) cci, avg, cci_prior, averageh, cci3 = calculate_cci(bars_1h) log.info("bars_1h {}".format(bars_1h[-1])) atr, atrprior = calculate_atr(bars_1h) bband_width, bband_b, bband_width_prior, bband_b_prior = calculate_bbands( bars_1h) csv_row += "," + str(cci) + "," + str(avg) + "," + str( atr) + "," + str(bband_width) + "," + str(bband_b) key_arr[2] = categories.categorize_atr1h(atr) key_arr[6] = categories.categorize_cci_1h(avg) key_arr[10] = categories.categorize_BBW1h(bband_width) key_arr[11] = categories.categorize_BBb1h(bband_b) log.info("starting 1H ") log.info("CCI {} ".format(cci)) log.info("CCIA: {} ".format(avg)) log.info("ATR: {} ".format(atr)) log.info("bband w: {} ".format(bband_width)) log.info("bband p: {} ".format(bband_b)) qtrtime = datetime.now() self.app.cci1h.update(f"{cci:.02f}") self.app.cci1h_av.update(f"{avg:.02f}") self.app.bband1h_b.update(f"{bband_b:.04f}") self.app.bband1h_width.update(f"{bband_width:.04f}") self.app.atr1h.update(f"{atr:.02f}") log.info( "requesting info for the following timeframe today: nextday: ". format(datetime_1d)) bars_1d = self.get_bars_data(dataContract, "75 D", "1 day", datetime_1d) log.info("1d min bars {}".format(bars_1d[-1])) cci, avg, cci_prior, averageh, cci3 = calculate_cci(bars_1d) atr, atrprior = calculate_atr(bars_1d) bband_width, bband_b, bband_width_prior, bband_b_prior = calculate_bbands( bars_1d) csv_row += "," + str(cci) + "," + str(avg) + "," + str( atr) + "," + str(bband_width) + "," + str(bband_b) key_arr[3] = categories.categorize_atr1d(atr) key_arr[7] = categories.categorize_cci_1d(avg) key_arr[12] = categories.categorize_BBW1d(bband_width) key_arr[13] = categories.categorize_BBb1d(bband_b) log.info("starting 1H ") log.info("starting 1D ") log.info("CCIP {} ".format(cci)) log.info("CCIPA: {} ".format(avg)) log.info("ATR: {} ".format(atr)) log.info("bband w: {} ".format(bband_width)) log.info("bband p: {} ".format(bband_b)) qtrtime = datetime.now() qtrtime = datetime.now() self.app.cci1d.update(f"{cci:.02f}") self.app.cci1d_av.update(f"{avg:.02f}") self.app.bband1d_b.update(f"{bband_b:.04f}") self.app.bband1d_width.update(f"{bband_width:.04f}") self.app.atr1d.update(f"{atr:.02f}") if tradenow: csv_file = csv.reader(open('data/ccibb.csv', "rt"), delimiter="'") for row in csv_file: if (''.join(key_arr)) == row[0]: print("we have a match in ccibb.csv") print("found a match in CCIBB ", row) status_done = self.row_results(self, row) ccibb_trade = True csv_file = csv.reader(open('data/cci.csv', "rt"), delimiter="'") for row in csv_file: if (''.join(key_arr[0:8])) == row[0]: print("we have a match in cci.csv") print("found a math in CCI ", row) status_done = self.row_results(self, row) cci_trade = True csv_row += "," + (''.join(key_arr)) + "," + (''.join( key_arr[0:8])) + "," + str(ccibb_trade) + "," + str( ccibb_trade) with open('data/hist15.csv', mode='a') as hist15: histwriter = csv.writer(hist15, delimiter=',', quotechar='"', quoting=csv.QUOTE_MINIMAL) histwriter.writerow([csv_row]) tradenow = False cci_trade = False ccibb_trade = False print( "end of run self **********************************************************************************************" )
def start(config): import toml import thetagang.config_defaults as config_defaults with open(config, "r") as f: config = toml.load(f) config = normalize_config(config) validate_config(config) click.secho(f"Config:", fg="green") click.echo() click.secho(f" Account details:", fg="green") click.secho( f" Number = {config['account']['number']}", fg="cyan") click.secho( f" Cancel existing orders = {config['account']['cancel_orders']}", fg="cyan", ) click.secho( f" Margin usage = {config['account']['margin_usage']} ({config['account']['margin_usage'] * 100}%)", fg="cyan", ) click.secho( f" Market data type = {config['account']['market_data_type']}", fg="cyan", ) click.echo() click.secho(f" Roll options when either condition is true:", fg="green") click.secho( f" Days to expiry <= {config['roll_when']['dte']} and P&L >= {config['roll_when']['min_pnl']} ({config['roll_when']['min_pnl'] * 100}%)", fg="cyan", ) click.secho( f" P&L >= {config['roll_when']['pnl']} ({config['roll_when']['pnl'] * 100}%)", fg="cyan", ) click.echo() click.secho(f" When contracts are ITM:", fg="green") click.secho( f" Roll puts = {config['roll_when']['puts']['itm']}", fg="cyan", ) click.secho( f" Roll calls = {config['roll_when']['calls']['itm']}", fg="cyan", ) click.echo() click.secho(f" Write options with targets of:", fg="green") click.secho(f" Days to expiry >= {config['target']['dte']}", fg="cyan") click.secho(f" Default delta <= {config['target']['delta']}", fg="cyan") if "puts" in config["target"]: click.secho( f" Delta for puts <= {config['target']['puts']['delta']}", fg="cyan", ) if "calls" in config["target"]: click.secho( f" Delta for calls <= {config['target']['calls']['delta']}", fg="cyan", ) click.secho( f" Maximum new contracts = {config['target']['maximum_new_contracts']}", fg="cyan", ) click.secho( f" Minimum open interest = {config['target']['minimum_open_interest']}", fg="cyan", ) click.echo() click.secho(f" Symbols:", fg="green") for s in config["symbols"].keys(): c = config["symbols"][s] c_delta = f"{get_target_delta(config, s, 'C'):.2f}".rjust(4) p_delta = f"{get_target_delta(config, s, 'P'):.2f}".rjust(4) weight = f"{c['weight']:.2f}".rjust(4) weight_p = f"{(c['weight'] * 100):.1f}".rjust(4) click.secho( f" {s.rjust(5)} weight = {weight} ({weight_p}%), delta = {p_delta}p, {c_delta}c", fg="cyan", ) assert (sum([ config["symbols"][s]["weight"] for s in config["symbols"].keys() ]) == 1.0) click.echo() if config.get("ib_insync", {}).get("logfile"): util.logToFile(config["ib_insync"]["logfile"]) # TWS version is pinned to current stable ibc = IBC(978, **config["ibc"]) def onConnected(): portfolio_manager.manage() ib = IB() ib.connectedEvent += onConnected completion_future = asyncio.Future() portfolio_manager = PortfolioManager(config, ib, completion_future) probeContractConfig = config["watchdog"]["probeContract"] watchdogConfig = config.get("watchdog") del watchdogConfig["probeContract"] probeContract = Contract( secType=probeContractConfig["secType"], symbol=probeContractConfig["symbol"], currency=probeContractConfig["currency"], exchange=probeContractConfig["exchange"], ) watchdog = Watchdog(ibc, ib, probeContract=probeContract, **watchdogConfig) watchdog.start() ib.run(completion_future) watchdog.stop() ibc.terminate()
def run(self): """ Execute the algorithm """ key_arr = ['blank','ATR15','ATR1','ATRD','CCI15','CCIA15','CCIA1h','CCIA1d','BBW15','BBb15','BBW1h','BBb1h','BBW1d','BBb1d'] tradenow = False not_finished = True pendingshort = False pendinglong = False tradenow = False cci_trade = False ccibb_trade = False while not_finished: print ("top of algo run self") #top of logic - want to check status as we enter a new bar/hour/day/contract crossed = False self.app.crossover.update(crossed) contContract, contracthours = get_contract(self) # NEW tradeContract = self.ib.qualifyContracts(contContract) # gives all the details of a contract so we can trade it #print("fully qualified trading Contract: ",tradeContract) positions = self.ib.positions() open = self.have_position(positions) open_today = helpers.is_open_today(contracthours) dataContract = Contract(exchange=config.EXCHANGE, secType="FUT", localSymbol=contContract.localSymbol) log.info("Got Contract: {}".format(dataContract.localSymbol)) self.app.contract.update(dataContract.localSymbol) wait_time, datetime_15, datetime_1h, datetime_1d = self.define_times() log.info("next datetime for 15 minutes - should be 15 minutes ahead of desired nextqtr{}".format(wait_time)) # # starting the analysis section # self.ib.waitUntil(wait_time) # # 15 Minute Data # self.app.qtrhour.update(wait_time) log.info("requesting info for the following timeframe today: {} ".format(wait_time)) bars_15m = self.get_bars_data(dataContract,"2 D","15 mins",datetime_15) x = np.array(bars_15m) log.info("15 min bars {}".format(bars_15m[-1])) cci, avg, cci_prior, averageh, cci3 = calculate_cci(bars_15m) atr, atrprior = calculate_atr(bars_15m) bband_width, bband_b,bband_width_prior, bband_b_prior = calculate_bbands(bars_15m) log.info("starting 15 minutes".format(datetime.now())) log.info("CCI: {} ".format(cci)) log.info("CCIA {} ".format(avg)) log.info("CCIP {} ".format(cci_prior)) log.info("CCIPA: {} ".format(averageh)) log.info("ATR: {} ".format(atr)) log.info("bband w: {} ".format(bband_width)) log.info("bband p: {} ".format(bband_b)) qtrtime = datetime.now() if cci > avg and cci_prior < averageh: crossed = True print("crossed long",crossed) tradenow = True csv_row = "'"+str(datetime.now())+",'long'" key_arr[0] = "long" elif cci < avg and cci_prior > averageh: crossed = True tradenow = True print("crossed short",crossed) csv_row = "'"+str(datetime.now())+",'short'" key_arr[0] = "short" else: csv_row = "'"+str(datetime.now())+",'cash'" crossed = False tradenow = False print("NOT CROSSED ",crossed) print("csv row crossed ",csv_row) #if abs(cci - avg) > config.SPREAD: # pending = True csv_header = "Date,Status,Crossed,CCI15,CCIA15,CCI15P,CCIA15P,ATR15,BBw15,BBB15" csv_row += ",'"+str(crossed)+"',"+str(cci)+","+str(avg)+","+str(cci_prior)+","+str(averageh)+","+str(atr)+","+str(bband_width)+","+str(bband_b) print("csv row 15",csv_row) key_arr[1] = categories.categorize_atr15(atr) key_arr[4] = categories.categorize_cci_15(cci) key_arr[5] = categories.categorize_cci_15_avg(avg) key_arr[8] = categories.categorize_BBW15(bband_width) key_arr[9] = categories.categorize_BBb15(bband_b) stat = "check crossed status" self.app.status1.update(stat) self.app.crossover.update(crossed) self.app.cci15.update(f"{cci:.02f}") self.app.cci15_av.update(f"{avg:.02f}") self.app.cci15p_av.update(f"{averageh:.02f}") self.app.cci15p.update(f"{cci_prior:.02f}") self.app.atr15.update(f"{atr:.02f}") self.app.bband15_width.update(f"{bband_width:.04f}") self.app.bband15_b.update(f"{bband_b:.04f}") self.app.qtrhour.update(qtrtime) #1 hour data log.info("next datetime for 1 hour - should be 1 hour behind current hour {}".format(datetime_1h)) bars_1h = self.get_bars_data(dataContract,"5 D","1 hour",datetime_1h) cci, avg, cci_prior, averageh, cci3 = calculate_cci(bars_1h) log.info("bars_1h {}".format(bars_1h[-1])) atr,atrprior = calculate_atr(bars_1h) bband_width, bband_b,bband_width_prior, bband_b_prior = calculate_bbands(bars_1h) csv_row += ","+str(cci)+","+str(avg)+","+str(atr)+","+str(bband_width)+","+str(bband_b) print("csv row 1 hour ",csv_row) csv_header += ",CCI1h,CCIA1h,ATR1h,BBW1h,BBB1h" key_arr[2] = categories.categorize_atr1h(atr) key_arr[6] = categories.categorize_cci_1h(avg) key_arr[10] = categories.categorize_BBW1h(bband_width) key_arr[11] = categories.categorize_BBb1h(bband_b) log.info("starting 1H ") log.info("CCI {} ".format(cci)) log.info("CCIA: {} ".format(avg)) log.info("ATR: {} ".format(atr)) log.info("bband w: {} ".format(bband_width)) log.info("bband p: {} ".format(bband_b)) qtrtime = datetime.now() self.app.cci1h.update(f"{cci:.02f}") self.app.cci1h_av.update(f"{avg:.02f}") self.app.bband1h_b.update(f"{bband_b:.04f}") self.app.bband1h_width.update(f"{bband_width:.04f}") self.app.atr1h.update(f"{atr:.02f}") log.info("requesting info for the following timeframe today: nextday: ".format(datetime_1d)) bars_1d = self.get_bars_data(dataContract,"75 D","1 day",datetime_1d) log.info("1d min bars {}".format(bars_1d[-1])) cci, avg, cci_prior, averageh, cci3 = calculate_cci(bars_1d) atr, atrprior = calculate_atr(bars_1d) bband_width, bband_b,bband_width_prior, bband_b_prior = calculate_bbands(bars_1d) csv_row += ","+str(cci)+","+str(avg)+","+str(atr)+","+str(bband_width)+","+str(bband_b) print("csv row daily ",csv_row) csv_header += ",CCI1d,CCIA1d,ATR1d,BBB1d,BBW1d" key_arr[3] = categories.categorize_atr1d(atr) key_arr[7] = categories.categorize_cci_1d(avg) key_arr[12] = categories.categorize_BBW1d(bband_width) key_arr[13] = categories.categorize_BBb1d(bband_b) log.info("starting 1D ") log.info("CCIP {} ".format(cci)) log.info("CCIPA: {} ".format(avg)) log.info("ATR: {} ".format(atr)) log.info("bband w: {} ".format(bband_width)) log.info("bband p: {} ".format(bband_b)) qtrtime = datetime.now() qtrtime = datetime.now() self.app.cci1d.update(f"{cci:.02f}") self.app.cci1d_av.update(f"{avg:.02f}") self.app.bband1d_b.update(f"{bband_b:.04f}") self.app.bband1d_width.update(f"{bband_width:.04f}") self.app.atr1d.update(f"{atr:.02f}") print("tradenow: ",tradenow) if tradenow: print("Tradeing this bar ",(''.join(key_arr))," - ",''.join(key_arr[0:8])) csv_file1 = csv.reader(open('data/ccibb.csv', "rt"), delimiter = ",") for row1 in csv_file1: print("ccibb row: ",row1[0]) if ((''.join(key_arr)) == row1[0]): log.info("we have a match in ccibb.csv") print("found a match in CCIBB ",row1[0]) ccibb_trade = True tradenow = False status_done = self.row_results(row1,cci_trade,ccibb_trade) break csv_file2 = csv.reader(open('data/cci.csv', "rt"), delimiter = ",") for row2 in csv_file2: print("cci row: ",row2[0]) if ((''.join(key_arr[0:8])) == row2[0]): print("we have a match in cci.csv") print("found a math in CCI ",row2[0]) cci_trade = True tradenow = False status_done = self.row_results(row2,cci_trade,ccibb_trade) break print("did we find a match? If true than no ",tradenow) csv_row += ","+(''.join(key_arr))+","+(''.join(key_arr[0:8]))+","+str(cci_trade)+","+str(ccibb_trade)+","+str(pendinglong)+","+str(pendingshort) csv_header += ",CCIbbKey,CCIKey,CCI Trade,CCIbbTrade,Pending Long, Pending Short" print("csv row right before csv write ",csv_row) with open('data/hist15.csv', mode='a') as hist15: histwriter = csv.writer(hist15, delimiter=',', quotechar='"', quoting=csv.QUOTE_MINIMAL) #histwriter.writerow([csv_header]) #histwriter.writerow([csv_row]) tradenow = False cci_trade = False ccibb_trade = False print ("end of run self **********************************************************************************************")
def roll_positions(self, positions, right, account_summary, portfolio_positions={}): for position in positions: try: symbol = position.contract.symbol strike_limit = get_strike_limit(self.config, symbol, right) if right.startswith("C"): strike_limit = math.ceil( max([strike_limit or 0] + [ p.averageCost for p in portfolio_positions[symbol] if isinstance(p.contract, Stock) ])) sell_ticker = self.find_eligible_contracts( symbol, self.get_primary_exchange(symbol), right, strike_limit, exclude_expirations_before=position.contract. lastTradeDateOrContractMonth, exclude_first_exp_strike=position.contract.strike, ) qty_to_roll = abs(position.position) maximum_new_contracts = self.get_maximum_new_contracts_for( symbol, self.get_primary_exchange(symbol), account_summary, ) from_dte = option_dte( position.contract.lastTradeDateOrContractMonth) roll_when_dte = self.config["roll_when"]["dte"] if from_dte > roll_when_dte: qty_to_roll = min([qty_to_roll, maximum_new_contracts]) position.contract.exchange = "SMART" buy_ticker = self.get_ticker_for(position.contract, midpoint=True) price = midpoint_or_market_price( buy_ticker) - midpoint_or_market_price(sell_ticker) # Create combo legs comboLegs = [ ComboLeg( conId=position.contract.conId, ratio=1, exchange="SMART", action="BUY", ), ComboLeg( conId=sell_ticker.contract.conId, ratio=1, exchange="SMART", action="SELL", ), ] # Create contract combo = Contract( secType="BAG", symbol=symbol, currency="USD", exchange="SMART", comboLegs=comboLegs, ) # Create order order = LimitOrder( "BUY", qty_to_roll, round(price, 2), algoStrategy="Adaptive", algoParams=[TagValue("adaptivePriority", "Patient")], tif="DAY", ) to_dte = option_dte( sell_ticker.contract.lastTradeDateOrContractMonth) from_strike = position.contract.strike to_strike = sell_ticker.contract.strike # Submit order trade = self.ib.placeOrder(combo, order) self.orders.append(trade) click.echo() click.secho( f"Order submitted, current position={abs(position.position)}, qty_to_roll={qty_to_roll}, from_dte={from_dte}, to_dte={to_dte}, from_strike={from_strike}, to_strike={to_strike}, price={round(price,2)}, trade={trade}", fg="green", ) except RuntimeError as e: click.echo() click.secho(str(e), fg="red") click.secho( "Error occurred when trying to roll position. Continuing anyway...", fg="yellow", )
def run(self): """ Execute the algorithm """ # check for command line arguments # key_arr = ['blank','ATR15','ATR1','ATRD','CCI15','CCIA15','CCIA1h','CCIA1d','BBW15','BBb15','BBW1h','BBb1h','BBW1d','BBb1d'] #log.info("Account values: {av}".format(av=self.ib.accountValues())) tradeNow, not_finished, pendingShort, pendingLong, pendingSkip, cci_trade, ccibb_trade, crossed, justStartedApp = False, True, False, False, False, False, False, False, True pendingCnt = 0 # any variable that is used within the class will be defined with self if justStartedApp: pendingLong, pendingShort, pendingCnt = self.justStartedAppDirectionCheck( ) #if not pendingLong and not pendingShort: # pendingLong, pendingShort, pendingCnt = self.justStartedAppPendingCheck() justStartedApp = False # do we need to reset pending reviewIBTrades = orders.getListOfTrades(self.ib) while not_finished: log.info( "top of algo run self*************************************************" ) #top of logic - want to check status as we enter a new bar/hour/day/contract contContract, contracthours = get_contract( self) #basic information on continuious contact tradeContract = self.ib.qualifyContracts(contContract)[ 0] # gives all the details of a contract so we can trade it open_long, open_short, long_position_qty, short_position_qty, account_qty = orders.countOpenPositions( self.ib, "") # do we have an open position? self.app.shares.update(long_position_qty + short_position_qty) dataContract = Contract(exchange=config.EXCHANGE, secType="FUT", localSymbol=contContract.localSymbol) log.debug("Got Contract:{dc} local symbol {ls}".format( dc=dataContract, ls=dataContract.localSymbol)) self.app.contract.update(dataContract.localSymbol) wait_time, self.datetime_15, self.datetime_1h, self.datetime_1d, self.log_time = self.define_times( self.ib) log.info( "next datetime for 15 minutes - should be 15 minutes ahead of desired nextqtr{wt} and current time {ct}" .format(wt=wait_time, ct=datetime.today())) # need to determine if this is normal trading hours or not dayNightProfileCCI, dayNightProfileCCIBB = self.duringOrAfterHours( self.ib, contracthours) # # debug #current_time = datetime.now() #wait_time = wait_time = current_time.replace(minute = 1,second=0) # #self.ib.disconnect() self.ib.waitUntil(wait_time) #self.ib.connect(config.HOST, config.PORT, clientId=config.CLIENTID,timeout=0) #log.debug("before loop start:{ls}".format(ls=datetime.now())) #self.ib.loopUntil(condition=self.ib.isConnected()) # rying to fix 1100 error on nightly reset # # first attempt at fix #try: # logger.getLogger().info("Connecting...") # self.ib.connect(config.HOST, config.PORT, clientId=config.CLIENTID,timeout=0) # self.ib.reqMarketDataType(config.DATATYPE.value) #except NameError: # got this block from https://groups.io/g/insync/message/4045 # self.num_disconnects += 1 # print(datetime.now(), 'Connection error exception', self.num_disconnects) # self.ib.cancelHistoricalData(bars) # log.info('Sleeping for 10sec...') # self.ib.disconnect # self.ib.sleep(10) # self.ib.connect(config.HOST, config.PORT, clientId=config.CLIENTID,timeout=0) # if not self.backTest: stpSell, stpBuy = orders.countOpenOrders( self.ib) # don't want to execute covering log.info( "we have the follow number of open stp orders for Sell: {sell} and Buy: {buy} " .format(sell=stpSell, buy=stpBuy)) #if datetime.now().hour == 0: # log.info("0 hour and disconnecting".format(datetime.now(),datetime.now().hour)) # self.ib.disconnect() # self.ib.sleep(500) # self.ib.connect(config.HOST, config.PORT, clientId=config.CLIENTID) # log.info("0 hour and re-connecting".format(datetime.now(),datetime.now().hour)) #log.debug("after loop start:{ls}".format(ls=datetime.now())) #log.debug("requesting info for the following timeframe today: {} ".format(wait_time)) bars_15m = calculations.Calculations(self.ib, dataContract, "2 D", "15 mins", self.datetime_15, False, 0) #rint("bars15m ",bars_15m) if bars_15m.atr < config.ATR_STOP_MIN: bars_1h = calculations.Calculations(self.ib, dataContract, "5 D", "1 hour", self.datetime_1h, True, bars_15m.closePrice) modBuyStopLossPrice = bars_1h.buyStopLossPrice modSellStopLossPrice = bars_1h.sellStopLossPrice modTrailStopLoss = (bars_1h.sellStopLossPrice - bars_1h.buyStopLossPrice) / 2 else: bars_1h = calculations.Calculations(self.ib, dataContract, "5 D", "1 hour", self.datetime_1h, False, 0) modBuyStopLossPrice = bars_15m.buyStopLossPrice modSellStopLossPrice = bars_15m.sellStopLossPrice modTrailStopLoss = (bars_1h.sellStopLossPrice - bars_1h.buyStopLossPrice) / 2 bars_1d = calculations.Calculations(self.ib, dataContract, "75 D", "1 day", self.datetime_1d, False, 0) updated = self.update_tk(bars_15m, bars_1h, bars_1d) pendingLong, pendingShort, pendingCnt, pendingSkip, tradeNow, tradeAction, crossed = self.crossoverPending( bars_15m, pendingLong, pendingShort, pendingSkip, pendingCnt) cci_key, ccibb_key, summ_key = build_key_array( tradeAction, bars_15m, bars_1h, bars_1d) #setsum = self.setupsummary(summ_key) log.debug("tradeNow: {trade} pendingSkip {skip}".format( trade=tradeNow, skip=pendingSkip)) log.debug( "going into tradenow: {tn}, backtest: {bt}, open long: {ol} and short: {os}" .format(tn=tradeNow, bt=self.backTest, ol=open_long, os=open_long)) #handeling existing position if crossed and (open_long or open_short) and not ( pendingLong or pendingShort): # need to close stp and open positions log.info( "crossed and not pending so lets close stp and open positions. Open Long: {ol} open short: {os} pending long: {pl} pending short: {ps}" .format(ol=open_long, os=open_short, pl=pendingLong, ps=pendingShort)) allClosed = orders.closeOutMain( self.ib, tradeContract, False) # we don't worry about whether we are long or short elif (not (pendingLong or pendingShort)) and open_long and tradeAction == "Sell": log.info( "Not pending we are open_long and tradeaction is sell so lets close out stp and open positions Open Long: {ol} open short: {os} pending long: {pl} pending short: {ps}" .format(ol=open_long, os=open_short, pl=pendingLong, ps=pendingShort)) allClosed = orders.closeOutMain( self.ib, tradeContract, False) # we don't worry about whether we are long or short elif (not (pendingLong or pendingShort)) and open_short and tradeAction == "Buy": log.info( "Not pending we are open_short and tradeaction is buy so lets close out stp and open positions Open Long: {ol} open short: {os} pending long: {pl} pending short: {ps}" .format(ol=open_long, os=open_short, pl=pendingLong, ps=pendingShort)) allClosed = orders.closeOutMain( self.ib, tradeContract, False) # we don't worry about whether we are long or short if tradeNow: log.info("tradeNow - Tradeing this bar {cci} - {ccibb}".format( cci=cci_key, ccibb=ccibb_key)) csv_file1 = csv.reader(open('data/ccibb.csv', "rt"), delimiter=",") #cci_key, ccibb_key = build_key_array(self, tradeAction, bars_15m, bars_1h, bars_1d) for row1 in csv_file1: if ccibb_key == row1[0] and row1[ 13] == "Y": #13 is winrisk - whether we trade or not #log.info("we have a match in ccibb.csv") log.info( "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" ) log.info( " +++++++++++++++++++++++++++++++++++++++++++++++++ found a match in CCIBB " .format(str(row1[0]))) log.info( "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" ) ccibb_trade = True quantity = 1 # do we need to close out current order # do we need to close out current stop loss orders? if not self.backTest: fillStatus = orders.createOrdersMain( self.ib, tradeContract, tradeAction, quantity, dayNightProfileCCI, modBuyStopLossPrice, modSellStopLossPrice, False, modTrailStopLoss, bars_15m.closePrice) log.info( "logic.CCIbb: order placed, fillStatus: {fs}". format(fs=fillStatus)) open_long, open_short, tradenow = False, False, False status_done = self.row_results(row1, cci_trade, ccibb_trade) break elif ccibb_key == row1[0] and row1[13] == "N": log.info( "Entry found in CCIBB but not traded. See if this changes" ) self.app.status1.update( "Entry found in CCIBB but not traded.") log.info("Profit : {p}".format(p=row1[6])) log.info("Orders : {p}".format(p=row1[7])) log.info("Wins $ : {p}".format(p=row1[10])) log.info("Losses $: {p}".format(p=row1[11])) log.info("Wins # : {p}".format(p=row1[8])) log.info("Losses #: {p}".format(p=row1[9])) ccibb_trade = False csv_file2 = csv.reader(open('data/cci.csv', "rt"), delimiter=",") for row2 in csv_file2: #rint("cci row: ",row2[0],row2[13]) if cci_key == row2[0] and row2[13] == "Y": log.info( "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" ) log.info( "+++++++++++++++++++++++++++++++++++++++++++++++++ we have a match in cci.csv - tradeAction" .format(tradeAction)) log.info( "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" ) cci_trade = True quantity = 1 if not self.backTest: fillStatus = orders.createOrdersMain( self.ib, tradeContract, tradeAction, quantity, dayNightProfileCCIBB, modBuyStopLossPrice, modSellStopLossPrice, False, modTrailStopLoss, bars_15m.closePrice) open_long, open_short, tradenow = False, False, False status_done = self.row_results(row2, cci_trade, ccibb_trade) break elif cci_key == row2[0] and row2[13] == "N": log.info( "Entry found in CCI but not traded. See if this changes" ) self.app.status1.update( "Entry found in CCI but not traded.") log.info("Profit : {p}".format(p=row2[6])) log.info("Orders : {p}".format(p=row2[7])) log.info("Wins $ : {p}".format(p=row2[10])) log.info("Losses $: {p}".format(p=row2[11])) log.info("Wins # : {p}".format(p=row2[8])) log.info("Losses #: {p}".format(p=row2[9])) cci_trade = True if tradeNow: log.info( "we did not find a match in CCI: {cci} or CCI BB: {ccib}" .format(cci=cci_trade, ccib=ccibb_trade)) #csv_row_add = helpers.build_csv_bars_row(","+(''.join(key_arr))+","+(''.join(key_arr[0:8]))+","+str(cci_trade)+","+str(ccibb_trade)+","+str(pendingLong)+","+str(pendingShort),True) wrote_bar_to_csv = helpers.build_csv_bars_row( self.log_time, tradeAction, bars_15m, bars_1h, bars_1d, pendingLong, pendingShort, pendingCnt, tradeNow, ccibb_trade, cci_trade, ccibb_key, cci_key) tradenow, cci_trade, ccibb_trade = False, False, False changed = orders.modifySTPOrder(self.ib, modBuyStopLossPrice, modSellStopLossPrice, bars_15m.closePrice)
def roll_positions(self, positions, right, portfolio_positions={}): for position in positions: symbol = position.contract.symbol strike_limit = get_strike_limit(self.config, symbol, right) if right.startswith("C"): strike_limit = math.ceil( max([strike_limit or 0] + [ p.averageCost for p in portfolio_positions[symbol] if isinstance(p.contract, Stock) ])) sell_ticker = self.find_eligible_contracts( symbol, self.get_primary_exchange(symbol), right, strike_limit, excluded_expirations=[ position.contract.lastTradeDateOrContractMonth ], ) self.wait_for_midpoint_price(sell_ticker) quantity = abs(position.position) position.contract.exchange = "SMART" [buy_ticker] = self.ib.reqTickers(position.contract) self.wait_for_midpoint_price(buy_ticker) price = midpoint_or_market_price( buy_ticker) - midpoint_or_market_price(sell_ticker) # Create combo legs comboLegs = [ ComboLeg( conId=position.contract.conId, ratio=1, exchange="SMART", action="BUY", ), ComboLeg( conId=sell_ticker.contract.conId, ratio=1, exchange="SMART", action="SELL", ), ] # Create contract combo = Contract( secType="BAG", symbol=symbol, currency="USD", exchange="SMART", comboLegs=comboLegs, ) # Create order order = LimitOrder( "BUY", quantity, round(price, 2), algoStrategy="Adaptive", algoParams=[TagValue("adaptivePriority", "Patient")], tif="DAY", ) # Submit order trade = self.ib.placeOrder(combo, order) self.orders.append(trade) click.echo() click.secho("Order submitted", fg="green") click.secho(f"{trade}", fg="green")
def justStartedAppDirectionCheck(self): log.debug( "justStartedAppDirectionCheck: Application just restarted. Going through our checks" ) # do we need to reverse positions? # first check to see if we have positions or open orders. If not exit otherwise continue # Are we positioned in the wrong direction (i.e. long when we should be short?) If so, we need to close STP and open open trades. # not going to take a position at this time. # the bars data is the current, not completed, bar so we have to go back to get closed bars. contContract, contracthours = logic.get_contract( self) #basic information on continuious contact tradeContract = self.ib.qualifyContracts(contContract)[ 0] # gives all the details of a contract so we can trade it open_long, open_short, long_position_qty, short_position_qty, account_qty = orders.countOpenPositions( self.ib, "") # do we have an open position - not orders but positions? open_today, tradingDayRules = helpers.is_open_today(contracthours) wait_time, self.datetime_15, self.datetime_1h, self.datetime_1d, self.log_time = self.define_times( self.ib) dataContract = Contract(exchange=config.EXCHANGE, secType="FUT", localSymbol=contContract.localSymbol) bars_15m = calculations.Calculations(self.ib, dataContract, "2 D", "15 mins", self.datetime_15, False, 0) #rint("bars15 cci_third, ccia_third, cci_prior, ccia_prior, cci, ccia",bars_15m.cci_third,bars_15m.ccia_third,bars_15m.cci_prior, bars_15m.ccia_prior, bars_15m.cci, bars_15m.ccia) if (bars_15m.cci_prior > bars_15m.ccia_prior and open_short) or (bars_15m.cci_prior < bars_15m.ccia_prior and open_long): log.debug( "justStartedAppDirectionCheck: we are in app start up and we need to reverse due to wrong direction" ) allClosed = orders.closeOutMain( self.ib, tradeContract, True ) # we don't worry about whether we are long or short. just passing the contract, need to add order. Second false is whether this is an opening order. it is not log.debug( "justStartedAppDirectionCheck: crossed but not tradeNow so lets close stp and open positions" ) else: log.debug( "justStartedAppDirectionCheck: we are in app start up and we DO NOT need to reverse due to wrong direction" ) # now check if we should be pending on restart if (bars_15m.cci_four > bars_15m.ccia_four and bars_15m.cci_third < bars_15m.ccia_third and bars_15m.cci_prior < bars_15m.ccia_prior and \ abs(bars_15m.cci_third - bars_15m.ccia_third) < config.SPREAD and abs(bars_15m.cci_prior - bars_15m.ccia_prior) < config.SPREAD) or \ (bars_15m.cci_four < bars_15m.ccia_four and bars_15m.cci_third > bars_15m.ccia_third and bars_15m.cci_prior > bars_15m.ccia_prior and \ abs(bars_15m.cci_third - bars_15m.ccia_third) < config.SPREAD and abs(bars_15m.cci_prior - bars_15m.ccia_prior) < config.SPREAD): log.debug( "justStartedAppDirectionCheck: we are in a second leg pending situation on start up" ) if bars_15m.cci_prior > bars_15m.ccia_prior: return True, False, 2 else: return False, True, 2 elif (bars_15m.cci_third > bars_15m.ccia_third and bars_15m.cci_prior < bars_15m.ccia_prior and abs(bars_15m.cci_prior - bars_15m.ccia_prior) < config.SPREAD) or \ (bars_15m.cci_third < bars_15m.ccia_third and bars_15m.cci_prior > bars_15m.ccia_prior and abs(bars_15m.cci_prior - bars_15m.ccia_prior) < config.SPREAD): log.debug( "justStartedAppDirectionCheck: we are in a first leg pending situation on start up" ) if bars_15m.cci_prior > bars_15m.ccia_prior: return True, False, 1 else: return False, True, 1 else: log.debug( "justStartedAppDirectionCheck: we are not in an exiting pending pattern" ) return False, False, 0
def run(self): """ Execute the algorithm """ key_arr = ['blank','ATR15','ATR1','ATRD','CCI15','CCIA15','CCIA1h','CCIA1d','BBW15','BBb15','BBW1h','BBb1h','BBW1d','BBb1d'] tradenow = False not_finished = True pendingshort = False pendinglong = False # this is when the cross over is not wide enough PendingLongCnt = 0 PendingShortCnt = 0 tradenow = False cci_trade = False ccibb_trade = False while not_finished: print ("top of algo run self*************************************************") #top of logic - want to check status as we enter a new bar/hour/day/contract crossed = False contContract, contracthours = get_contract(self) #basic information on continuious contact #i NEW tradeContract = self.ib.qualifyContracts(contContract)[0] # gives all the details of a contract so we can trade it open_long, open_short, position_qty = self.have_position(self.ib.positions()) # do we have an open position? open_today = helpers.is_open_today(contracthours) dataContract = Contract(exchange=config.EXCHANGE, secType="FUT", localSymbol=contContract.localSymbol) log.debug("Got Contract: {}".format(dataContract.localSymbol)) #pnl = self.ib.pnl() log.debug("account names: {}".format(self.ib.managedAccounts())) log.info("PNL : {PNL} ".format(PNL=self.ib.pnl("all"))) self.app.contract.update(dataContract.localSymbol) wait_time, datetime_15, datetime_1h, datetime_1d = self.define_times() log.debug("next datetime for 15 minutes - should be 15 minutes ahead of desired nextqtr{}".format(wait_time)) self.ib.waitUntil(wait_time) self.app.qtrhour.update(wait_time) log.debug("requesting info for the following timeframe today: {} ".format(wait_time)) # #start of study # bars_15m = self.get_bars_data(dataContract,"2 D","15 mins",datetime_15) print("bar data close: ",bars_15m[-1].close) x = np.array(bars_15m) log.debug("15 min bars {}".format(str(bars_15m[-1]))) cci, avg, cci_prior, averageh, cci3 = calculate_cci(bars_15m) atr, atrprior = calculate_atr(bars_15m) bband_width, bband_b,bband_width_prior, bband_b_prior = calculate_bbands(bars_15m) logged_it = self.log_value("Starting 15 minutes", cci,avg,cci_prior, averageh,atr,bband_width,bband_b) qtrtime = datetime.now() print("stop loss = ",round((bars_15m[-1].close + (atr *2))*4,0)/4) if cci > avg and cci_prior < averageh: crossed = True tradenow = True csv_row = "'"+str(datetime.now())+",'long'" key_arr[0] = "long" tradeAction = "BUY" stoplossprice = round((bars_15m[-1].close - (atr * 2))*4,0)/4 elif cci < avg and cci_prior > averageh: crossed = True tradenow = True csv_row = "'"+str(datetime.now())+",'short'" key_arr[0] = "short" tradeAction = "SELL" stoplossprice = round((bars_15m[-1].close + (atr * 2))*4,0)/4 else: csv_row = "'"+str(datetime.now())+",'cash'" crossed = False tradenow = False stoplossprice = 0 stoploss = 0 if abs(cci - avg) > config.SPREAD: log.info("Pending ".format(cci-avg)) pendinglong = True pendingshort = True csv_header = "Date,Status,Crossed,CCI15,CCIA15,CCI15P,CCIA15P,ATR15,BBw15,BBB15" csv_row += ",'"+str(crossed)+"',"+str(cci)+","+str(avg)+","+str(cci_prior)+","+str(averageh)+","+str(atr)+","+str(bband_width)+","+str(bband_b) key_arr[1] = categories.categorize_atr15(atr) key_arr[4] = categories.categorize_cci_15(cci) key_arr[5] = categories.categorize_cci_15_avg(avg) key_arr[8] = categories.categorize_BBW15(bband_width) key_arr[9] = categories.categorize_BBb15(bband_b) # #1 hour data # log.debug("next datetime for 1 hour - should be 1 hour behind current hour {}".format(datetime_1h)) bars_1h = self.get_bars_data(dataContract,"5 D","1 hour",datetime_1h) cci, avg, cci_prior, averageh, cci3 = calculate_cci(bars_1h) log.debug("bars_1h {}".format(str(bars_1h[-1]))) atr,atrprior = calculate_atr(bars_1h) bband_width, bband_b,bband_width_prior, bband_b_prior = calculate_bbands(bars_1h) csv_row += ","+str(cci)+","+str(avg)+","+str(atr)+","+str(bband_width)+","+str(bband_b) csv_header += ",CCI1h,CCIA1h,ATR1h,BBW1h,BBB1h" key_arr[2] = categories.categorize_atr1h(atr) key_arr[6] = categories.categorize_cci_1h(avg) key_arr[10] = categories.categorize_BBW1h(bband_width) key_arr[11] = categories.categorize_BBb1h(bband_b) #logged_it = self.log_value("Starting 1 hour", cci,avg,cci_prior, averageh,atr,bband_width,bband_b) qtrtime = datetime.now() log.debug("requesting info for the following timeframe today: nextday: ".format(datetime_1d)) bars_1d = self.get_bars_data(dataContract,"75 D","1 day",datetime_1d) log.debug("1d min bars {}".format(str(bars_1d[-1]))) cci, avg, cci_prior, averageh, cci3 = calculate_cci(bars_1d) atr, atrprior = calculate_atr(bars_1d) bband_width, bband_b,bband_width_prior, bband_b_prior = calculate_bbands(bars_1d) csv_row += ","+str(cci)+","+str(avg)+","+str(atr)+","+str(bband_width)+","+str(bband_b) csv_header += ",CCI1d,CCIA1d,ATR1d,BBB1d,BBW1d" key_arr[3] = categories.categorize_atr1d(atr) key_arr[7] = categories.categorize_cci_1d(avg) key_arr[12] = categories.categorize_BBW1d(bband_width) key_arr[13] = categories.categorize_BBb1d(bband_b) #logged_it = self.log_value("Starting 1 Day", cci,avg,cci_prior, averageh,atr,bband_width,bband_b) qtrtime = datetime.now() setsum = self.setupsummary(key_arr) log.info("tradenow: {trade}".format(trade = tradenow)) # # starting trade logic # # test buy if tradenow: log.info("Tradeing this bar {}".format(str(''.join(key_arr))," - ",''.join(key_arr[0:8]))) csv_file1 = csv.reader(open('data/ccibb.csv', "rt"), delimiter = ",") for row1 in csv_file1: print("ccibb row: ",row1[0],row1[13]) if ((''.join(key_arr)) == row1[0]) and row1[13] == "Y": #13 is winrisk - whether we trade or not quantity = 2 log.info("we have a match in ccibb.csv") log.info("found a match in CCIBB ".format(str(row1[0]))) ccibb_trade = True if open_long or open_short: quantity = 4 ParentOrderID = orders.buildOrders(self.ib,tradeContract,tradeAction,quantity,"ccibb_day",stoplossprice) log.info("order placed, parentID: {}".format(ParentOrderID)) open_long = False open_short = False tradenow = False status_done = self.row_results(row1,cci_trade,ccibb_trade) break csv_file2 = csv.reader(open('data/cci.csv', "rt"), delimiter = ",") for row2 in csv_file2: print("cci row: ",row2[0],row2[13]) if ((''.join(key_arr[0:8])) == row2[0]) and row2[13] == "Y": quantity = 2 log.info("we have a match in cci.csv - tradeAction".format(tradeAction)) log.info("found a math in CCI {}".format(str(row2[0]))) if open_long or open_short: quantity = 4 ParentOrderID = orders.buildOrders(self.ib,tradeContract,tradeAction,quantity,"cci_day",stoplossprice) open_long = False open_short = False tradenow = False status_done = self.row_results(row2,cci_trade,ccibb_trade) break log.info("did we find a match? If true than no {match}".format(match = tradenow)) if open_long or open_short: quantity = 2 ParentOrderID = orders.buildOrders(self.ib,tradeContract,tradeAction,quantity,"ccibb_day",stoplossprice) open_long =False open_short = False csv_row += ","+(''.join(key_arr))+","+(''.join(key_arr[0:8]))+","+str(cci_trade)+","+str(ccibb_trade)+","+str(pendinglong)+","+str(pendingshort) csv_header += ",CCIbbKey,CCIKey,CCI Trade,CCIbbTrade,PendingLong, PendingShort" with open('data/hist15.csv', mode='a') as hist15: histwriter = csv.writer(hist15, delimiter=',', quotechar='"', quoting=csv.QUOTE_MINIMAL) histwriter.writerow([csv_header]) histwriter.writerow([csv_row]) tradenow = False cci_trade = False ccibb_trade = False
def start(config): import toml with open(config, "r") as f: config = toml.load(f) validate_config(config) click.secho(f"Config:", fg="green") click.echo() click.secho(f" Account details:", fg="green") click.secho( f" Number = {config['account']['number']}", fg="cyan") click.secho( f" Cancel existing orders = {config['account']['cancel_orders']}", fg="cyan", ) click.secho( f" Margin usage = {config['account']['margin_usage']} ({config['account']['margin_usage'] * 100}%)", fg="cyan", ) click.secho( f" Market data type = {config['account']['market_data_type']}", fg="cyan", ) click.echo() click.secho(f" Roll options when either condition is true:", fg="green") click.secho(f" Days to expiry <= {config['roll_when']['dte']}", fg="cyan") click.secho( f" P&L >= {config['roll_when']['pnl']} ({config['roll_when']['pnl'] * 100}%)", fg="cyan", ) click.echo() click.secho(f" Write options with targets of:", fg="green") click.secho(f" Days to expiry >= {config['target']['dte']}", fg="cyan") click.secho(f" Delta <= {config['target']['delta']}", fg="cyan") click.secho( f" Minimum open interest >= {config['target']['minimum_open_interest']}", fg="cyan", ) click.echo() click.secho(f" Symbols:", fg="green") for s in config["symbols"].keys(): click.secho( f" {s}, weight = {config['symbols'][s]['weight']} ({config['symbols'][s]['weight'] * 100}%)", fg="cyan", ) assert (sum([ config["symbols"][s]["weight"] for s in config["symbols"].keys() ]) == 1.0) click.echo() if config.get("ib_insync", {}).get("logfile"): util.logToFile(config["ib_insync"]["logfile"]) ibc = IBC(**config["ibc"]) def onConnected(): portfolio_manager.manage() ib = IB() ib.connectedEvent += onConnected completion_future = asyncio.Future() portfolio_manager = PortfolioManager(config, ib, completion_future) probeContractConfig = config["watchdog"]["probeContract"] watchdogConfig = config.get("watchdog") del watchdogConfig["probeContract"] probeContract = Contract( secType=probeContractConfig["secType"], symbol=probeContractConfig["symbol"], currency=probeContractConfig["currency"], exchange=probeContractConfig["exchange"], ) watchdog = Watchdog(ibc, ib, probeContract=probeContract, **watchdogConfig) watchdog.start() ib.run(completion_future) watchdog.stop() ibc.terminate()
def justStartedAppDirectionCheck(self): log.debug( "justStartedAppDirectionCheck: Application just restarted. Going through our checks" ) # do we need to reverse positions? # first check to see if we have positions or open orders. If not exit otherwise continue # Are we positioned in the wrong direction (i.e. long when we should be short?) If so, we need to close STP and open open trades. # not going to take a position at this time. # the bars data is the current, not completed, bar so we have to go back to get closed bars. # # checking for wrong direction contContract, contracthours = get_contract( self) #basic information on continuious contact tradeContract = self.ib.qualifyContracts(contContract)[ 0] # gives all the details of a contract so we can trade it open_long, open_short, long_position_qty, short_position_qty, account_qty = orders.countOpenPositions( self.ib, "") # do we have an open position - not orders but positions? open_today, tradingDayRules, currentTimeFrame = helpers.is_open_today( contracthours) wait_time, self.datetime_15, self.datetime_1h, self.datetime_1d, self.log_time = self.define_times( self.ib) dataContract = Contract(exchange=config.EXCHANGE, secType="FUT", localSymbol=contContract.localSymbol) bars_15m = calculations.Calculations(self.ib, dataContract, "2 D", "15 mins", self.datetime_15, False, 0) # bars_1h = calculations.Calculations(self.ib, dataContract, "5 D", "1 hour", self.datetime_1h, True, bars_15m.closePrice) bars_1d = calculations.Calculations(self.ib, dataContract, "75 D", "1 day", self.datetime_1d, False, 0) updated = self.update_tk(bars_15m, bars_1h, bars_1d) # #rint("bars15 cci_third, ccia_third, cci_prior, ccia_prior, cci, ccia",bars_15m.cci_third,bars_15m.ccia_third,bars_15m.cci_prior, bars_15m.ccia_prior, bars_15m.cci, bars_15m.ccia) if bars_15m.cci_prior > bars_15m.ccia_prior and open_short and abs( bars_15m.cci - bars_15m.ccia) > config.SPREAD: log.info( "justStartedAppDirectionCheck: we are in app start up and we need to reverse due to wrong direction" ) allClosed = orders.closeOutMain( self.ib, tradeContract, True ) # we don't worry about whether we are long or short. just passing the contract, need to add order. Second false is whether this is an opening order. it is not log.info( "justStartedAppDirectionCheck: crossed but not tradeNow so lets close stp and open positions" ) elif bars_15m.cci_prior > bars_15m.ccia_prior and open_short and abs( bars_15m.cci - bars_15m.ccia) <= config.SPREAD: return True, False, 1 log.info( "justStartedAppDirectionCheck: we are in app start up and we crossed but not exceed the spread - going pending" ) elif bars_15m.cci_prior < bars_15m.ccia_prior and open_long and abs( bars_15m.cci - bars_15m.ccia) > config.SPREAD: log.info( "justStartedAppDirectionCheck: we are in app start up and we need to reverse due to wrong direction. Came in long and crossed down > spread" ) allClosed = orders.closeOutMain( self.ib, tradeContract, True ) # we don't worry about whether we are long or short. just passing the contract, need to add order. Second false is whether this is an opening order. it is not log.info( "justStartedAppDirectionCheck: crossed but not tradeNow so lets close stp and open positions" ) elif bars_15m.cci_prior < bars_15m.ccia_prior and open_long and abs( bars_15m.cci - bars_15m.ccia) <= config.SPREAD: return False, True, 1 log.info( "justStartedAppDirectionCheck: we are in app start up and we crossed but not exceed the spread - going pending" ) else: log.info( "justStartedAppDirectionCheck: we are in app start up and we DO NOT need to reverse due to wrong direction - checking pending" ) log.info( "justStartedAppPendingCheck:: bars_15m.prior {ccip} {cciap}, third {ccip3} {cciap3} open long and short {ol} {os}" .format(ccip=bars_15m.cci_prior, cciap=bars_15m.ccia_prior, ccip3=bars_15m.cci_third, cciap3=bars_15m.ccia_third, ol=open_long, os=open_short)) if bars_15m.cci_prior > bars_15m.ccia_prior and abs(bars_15m.cci_prior - bars_15m.ccia_prior) <= config.SPREAD and \ bars_15m.cci_third < bars_15m.ccia_third and abs(bars_15m.cci_third - bars_15m.ccia_third) > config.SPREAD and \ not open_long and not open_short: return True, False, 1 elif bars_15m.cci_prior < bars_15m.ccia_prior and abs(bars_15m.cci_prior - bars_15m.ccia_prior) <= config.SPREAD and \ bars_15m.cci_third > bars_15m.ccia_third and abs(bars_15m.cci_third - bars_15m.ccia_third) > config.SPREAD and \ not open_long and not open_short: return False, True, 1 else: log.info("not going into this session pending") return False, False, 0
def start(config): import toml import thetagang.config_defaults as config_defaults # NOQA with open(config, "r") as f: config = toml.load(f) config = normalize_config(config) validate_config(config) click.secho("Config:", fg="green") click.echo() click.secho(" Account details:", fg="green") click.secho( f" Number = {config['account']['number']}", fg="cyan") click.secho( f" Cancel existing orders = {config['account']['cancel_orders']}", fg="cyan", ) click.secho( f" Margin usage = {config['account']['margin_usage']} ({config['account']['margin_usage'] * 100}%)", fg="cyan", ) click.secho( f" Market data type = {config['account']['market_data_type']}", fg="cyan", ) click.echo() click.secho(" Roll options when either condition is true:", fg="green") click.secho( f" Days to expiry <= {config['roll_when']['dte']} and P&L >= {config['roll_when']['min_pnl']} ({config['roll_when']['min_pnl'] * 100}%)", fg="cyan", ) if "max_dte" in config["roll_when"]: click.secho( f" P&L >= {config['roll_when']['pnl']} ({config['roll_when']['pnl'] * 100}%) and DTE < {config['roll_when']['max_dte']}", fg="cyan", ) else: click.secho( f" P&L >= {config['roll_when']['pnl']} ({config['roll_when']['pnl'] * 100}%)", fg="cyan", ) click.echo() click.secho(" When contracts are ITM:", fg="green") click.secho( f" Roll puts = {config['roll_when']['puts']['itm']}", fg="cyan", ) click.secho( f" Roll calls = {config['roll_when']['calls']['itm']}", fg="cyan", ) click.echo() click.secho(" Write options with targets of:", fg="green") click.secho(f" Days to expiry >= {config['target']['dte']}", fg="cyan") click.secho(f" Default delta <= {config['target']['delta']}", fg="cyan") if "puts" in config["target"]: click.secho( f" Delta for puts <= {config['target']['puts']['delta']}", fg="cyan", ) if "calls" in config["target"]: click.secho( f" Delta for calls <= {config['target']['calls']['delta']}", fg="cyan", ) click.secho( f" Maximum new contracts = {config['target']['maximum_new_contracts_percent'] * 100}% of buying power", fg="cyan", ) click.secho( f" Minimum open interest = {config['target']['minimum_open_interest']}", fg="cyan", ) click.echo() click.secho(" Symbols:", fg="green") for s in config["symbols"].keys(): c = config["symbols"][s] c_delta = f"{get_target_delta(config, s, 'C'):.2f}".rjust(4) p_delta = f"{get_target_delta(config, s, 'P'):.2f}".rjust(4) weight_p = f"{(c['weight'] * 100):.2f}".rjust(4) strike_limits = "" c_limit = get_strike_limit(config, s, "C") p_limit = get_strike_limit(config, s, "P") if c_limit: strike_limits += f", call strike >= ${c_limit:.2f}" if p_limit: strike_limits += f", put strike <= ${p_limit:.2f}" click.secho( f" {s.rjust(5)} weight = {weight_p}%, delta = {p_delta}p, {c_delta}c{strike_limits}", fg="cyan", ) assert (round( sum([config["symbols"][s]["weight"] for s in config["symbols"].keys()]), 5) == 1.00000) click.echo() if config.get("ib_insync", {}).get("logfile"): util.logToFile(config["ib_insync"]["logfile"]) # TWS version is pinned to current stable ibc_config = config.get("ibc", {}) # Remove any config params that aren't valid keywords for IBC ibc_keywords = { k: ibc_config[k] for k in ibc_config if k not in ["RaiseRequestErrors"] } ibc = IBC(981, **ibc_keywords) def onConnected(): portfolio_manager.manage() ib = IB() ib.RaiseRequestErrors = ibc_config.get("RaiseRequestErrors", False) ib.connectedEvent += onConnected completion_future = asyncio.Future() portfolio_manager = PortfolioManager(config, ib, completion_future) probeContractConfig = config["watchdog"]["probeContract"] watchdogConfig = config.get("watchdog") del watchdogConfig["probeContract"] probeContract = Contract( secType=probeContractConfig["secType"], symbol=probeContractConfig["symbol"], currency=probeContractConfig["currency"], exchange=probeContractConfig["exchange"], ) watchdog = Watchdog(ibc, ib, probeContract=probeContract, **watchdogConfig) watchdog.start() ib.run(completion_future) watchdog.stop() ibc.terminate()