def bracketOrder(self, action: str, quantity: float, limitPrice:float, takeProfitPrice: float, stopLossPrice: float) -> BracketOrder: """ Create a limit order that is bracketed by a take-profit order and a stop-loss order. Submit the bracket like: .. code-block:: python for o in bracket: ib.placeOrder(contract, o) https://interactivebrokers.github.io/tws-api/bracket_order.html """ assert action in ('BUY', 'SELL') reverseAction = 'BUY' if action == 'SELL' else 'SELL' parent = LimitOrder( action, quantity, limitPrice, orderId=self.client.getReqId(), transmit=False) takeProfit = LimitOrder( reverseAction, quantity, takeProfitPrice, orderId=self.client.getReqId(), transmit=False, parentId=parent.orderId) stopLoss = StopOrder( reverseAction, quantity, stopLossPrice, orderId=self.client.getReqId(), transmit=True, parentId=parent.orderId) return BracketOrder(parent, takeProfit, stopLoss)
def exit_positions(self): portfolio = self.get_portfolio() trades = self.get_trades() curr_date, curr_dt = get_datetime_for_logging() if self._nope_value > self.config["nope"]["long_exit"]: held_calls = self.get_held_contracts(portfolio, 'C') existing_call_order_ids = self.get_existing_order_ids(trades, 'C', 'SELL') remaining_calls = list(filter(lambda c: c['contract'].conId not in existing_call_order_ids, held_calls)) if len(remaining_calls) > 0: remaining_calls.sort(key=lambda c: c['contract'].conId) remaining_call_contracts = [c['contract'] for c in remaining_calls] qualified_contracts = self.ib.qualifyContracts(*remaining_call_contracts) tickers = self.ib.reqTickers(*qualified_contracts) tickers.sort(key=lambda t: t.contract.conId) for idx, ticker in enumerate(tickers): price = midpoint_or_market_price(ticker) if not util.isNan(price): quantity = remaining_calls[idx]['position'] order = LimitOrder("SELL", quantity, price, algoStrategy="Adaptive", algoParams=[TagValue(tag='adaptivePriority', value='Normal')], tif="DAY") call_contract = ticker.contract self.wait_for_trade_submitted(self.ib.placeOrder(call_contract, order)) with open(f"logs/{curr_date}-trade.txt", "a") as f: f.write(f'Sold {quantity} {call_contract.strike}C{call_contract.lastTradeDateOrContractMonth} ({remaining_calls[idx]["avg"]} average) for {price * 100} each, {self._nope_value} | {self._underlying_price} | {curr_dt}\n') else: with open("logs/errors.txt", "a") as f: f.write(f'Error selling call at {self._nope_value} | {self._underlying_price} | {curr_dt}\n') if self._nope_value < self.config["nope"]["short_exit"]: held_puts = self.get_held_contracts(portfolio, 'P') existing_put_order_ids = self.get_existing_order_ids(trades, 'P', 'SELL') remaining_puts = list(filter(lambda c: c['contract'].conId not in existing_put_order_ids, held_puts)) if len(remaining_puts) > 0: remaining_puts.sort(key=lambda c: c['contract'].conId) remaining_put_contracts = [c['contract'] for c in remaining_puts] qualified_contracts = self.ib.qualifyContracts(*remaining_put_contracts) tickers = self.ib.reqTickers(*qualified_contracts) tickers.sort(key=lambda t: t.contract.conId) for idx, ticker in enumerate(tickers): price = midpoint_or_market_price(ticker) if not util.isNan(price): quantity = remaining_puts[idx]['position'] order = LimitOrder("SELL", quantity, price, algoStrategy="Adaptive", algoParams=[TagValue(tag='adaptivePriority', value='Normal')], tif="DAY") put_contract = ticker.contract self.wait_for_trade_submitted(self.ib.placeOrder(put_contract, order)) with open(f"logs/{curr_date}-trade.txt", "a") as f: f.write(f'Sold {quantity} {put_contract.strike}P{put_contract.lastTradeDateOrContractMonth} ({remaining_puts[idx]["avg"]} average) for {price * 100} each, {self._nope_value} | {self._underlying_price} | {curr_dt}\n') else: with open("logs/errors.txt", "a") as f: f.write(f'Error selling put at {self._nope_value} | {self._underlying_price} | {curr_dt}\n')
def enter_positions(self): portfolio = self.get_portfolio() trades = self.get_trades() curr_date, curr_dt = get_datetime_for_logging() if self._nope_value < self.config["nope"]["long_enter"]: held_calls = self.get_total_position(portfolio, 'C') existing_order_quantity = self.get_total_buys(trades, 'C') total_buys = held_calls + existing_order_quantity if total_buys < self.config["nope"]["call_limit"]: contracts = self.find_eligible_contracts(self.SYMBOL, 'C') # TODO: Implement contract selection from eligible candidiates contract_to_buy = contracts[self.config["nope"]["call_strike_offset"]] qualified_contracts = self.ib.qualifyContracts(contract_to_buy) tickers = self.ib.reqTickers(*qualified_contracts) if len(tickers) > 0: price = midpoint_or_market_price(tickers[0]) call_contract = qualified_contracts[0] if not util.isNan(price): quantity = self.config["nope"]["call_quantity"] order = LimitOrder('BUY', quantity, price, algoStrategy="Adaptive", algoParams=[TagValue(tag='adaptivePriority', value='Normal')], tif="DAY") self.wait_for_trade_submitted(self.ib.placeOrder(call_contract, order)) with open(f"logs/{curr_date}-trade.txt", "a") as f: f.write(f'Bought {quantity} {call_contract.strike}C{call_contract.lastTradeDateOrContractMonth} for {price * 100} each, {self._nope_value} | {self._underlying_price} | {curr_dt}\n') else: with open("logs/errors.txt", "a") as f: f.write(f'Error buying call at {self._nope_value} | {self._underlying_price} | {curr_dt}\n') elif self._nope_value > self.config["nope"]["short_enter"]: held_puts = self.get_total_position(portfolio, 'P') existing_order_quantity = self.get_total_buys(trades, 'P') total_buys = held_puts + existing_order_quantity if total_buys < self.config["nope"]["put_limit"]: contracts = self.find_eligible_contracts(self.SYMBOL, 'P') # TODO: Implement contract selection from eligible candidates contract_to_buy = contracts[-self.config["nope"]["put_strike_offset"] - 1] qualified_contracts = self.ib.qualifyContracts(contract_to_buy) tickers = self.ib.reqTickers(*qualified_contracts) if len(tickers) > 0: price = midpoint_or_market_price(tickers[0]) put_contract = qualified_contracts[0] if not util.isNan(price): quantity = self.config["nope"]["put_quantity"] order = LimitOrder('BUY', quantity, price, algoStrategy="Adaptive", algoParams=[TagValue(tag='adaptivePriority', value='Normal')], tif="DAY") self.wait_for_trade_submitted(self.ib.placeOrder(put_contract, order)) with open(f"logs/{curr_date}-trade.txt", "a") as f: f.write(f'Bought {quantity} {put_contract.strike}P{put_contract.lastTradeDateOrContractMonth} for {price * 100} each, {self._nope_value} | {self._underlying_price} | {curr_dt}\n') else: with open("logs/errors.txt", "a") as f: f.write(f'Error buying put at {self._nope_value} | {self._underlying_price} | {curr_dt}\n')
def write_puts(self, symbol, quantity): sell_ticker = self.find_eligible_contracts(symbol, "P") if not self.wait_for_midpoint_price(sell_ticker): click.secho( "Couldn't get midpoint price for contract={sell_ticker}, skipping for now", fg="red", ) return # Create order order = LimitOrder( "SELL", quantity, round(midpoint_or_market_price(sell_ticker), 2), algoStrategy="Adaptive", algoParams=[TagValue("adaptivePriority", "Patient")], tif="DAY", ) # Submit order trade = self.wait_for_trade_submitted( self.ib.placeOrder(sell_ticker.contract, order)) click.echo() click.secho("Order submitted", fg="green") click.secho(f"{trade}", fg="green")
def buy_contracts(self, right): action = "BUY" contracts = self.find_eligible_contracts(self.SYMBOL, right) ticker = self.select_contract(contracts, right) if ticker is not None: price = midpoint_or_market_price(ticker) quantity = (self.config["nope"]["call_quantity"] if right == "C" else self.config["nope"]["put_quantity"]) if not util.isNan(price) and self.check_acc_balance( price, quantity): contract = ticker.contract order = LimitOrder( action, quantity, price, algoStrategy="Adaptive", algoParams=[ TagValue(tag="adaptivePriority", value="Normal") ], tif="DAY", ) self.cancel_order_type("SELL", "STP") trade = self.ib.placeOrder(contract, order) trade.filledEvent += log_fill trade.filledEvent += self.on_buy_fill self.log_order(contract, quantity, price, action) else: with open("logs/errors.txt", "a") as f: f.write( f"Error buying {right} at {self._nope_value} | {self._underlying_price}\n" )
def buy_contracts(self, right): action = "BUY" contracts = self.find_eligible_contracts(self.SYMBOL, right) # TODO: Improve contract selection https://github.com/ajhpark/ib_nope/issues/21 offset = (self.config["nope"]["call_strike_offset"] if right == "C" else -self.config["nope"]["put_strike_offset"] - 1) contract_to_buy = contracts[offset] qualified_contracts = self.ib.qualifyContracts(contract_to_buy) tickers = self.ib.reqTickers(*qualified_contracts) if len(tickers) > 0: price = midpoint_or_market_price(tickers[0]) if not util.isNan(price): contract = qualified_contracts[0] quantity = (self.config["nope"]["call_quantity"] if right == "C" else self.config["nope"]["put_quantity"]) order = LimitOrder( action, quantity, price, algoStrategy="Adaptive", algoParams=[ TagValue(tag="adaptivePriority", value="Normal") ], tif="DAY", ) trade = self.ib.placeOrder(contract, order) trade.filledEvent += log_fill self.log_order(contract, quantity, price, action) else: with open("logs/errors.txt", "a") as f: f.write( f"Error buying {right} at {self._nope_value} | {self._underlying_price}\n" )
def write_calls(self, symbol, primary_exchange, quantity, strike_limit): sell_ticker = self.find_eligible_contracts(symbol, primary_exchange, "C", strike_limit) if not self.wait_for_midpoint_price(sell_ticker): click.secho( "Couldn't get midpoint price for contract={sell_ticker}, skipping for now", fg="red", ) return # Create order order = LimitOrder( "SELL", quantity, round(get_highest_price(sell_ticker), 2), algoStrategy="Adaptive", algoParams=[TagValue("adaptivePriority", "Patient")], tif="DAY", ) # Submit order try: trade = self.ib.placeOrder(sell_ticker.contract, order) self.orders.append(trade) click.echo() click.secho("Order submitted", fg="green") click.secho(f"{trade}", fg="green") except: click.echo() click.secho( "Order trade submission seems to have failed, or a response wasn't received in time. Continuing anyway...", fg="yellow", )
def roll_positions(self, positions, right): for position in positions: symbol = position.contract.symbol sell_ticker = self.find_eligible_contracts(symbol, right) 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 sell_held_contracts(self, right): portfolio = self.get_portfolio() trades = self.get_trades() action = "SELL" held_contracts_info = self.get_held_contracts_info(portfolio, right) existing_contract_order_ids = self.get_existing_order_ids( trades, right, action) remaining_contracts_info = list( filter( lambda c: c["contract"].conId not in existing_contract_order_ids, held_contracts_info, )) if len(remaining_contracts_info) > 0: remaining_contracts_info.sort(key=lambda c: c["contract"].conId) remaining_contracts = [ c["contract"] for c in remaining_contracts_info ] qualified_contracts = self.ib.qualifyContracts( *remaining_contracts) tickers = self.ib.reqTickers(*qualified_contracts) tickers.sort(key=lambda t: t.contract.conId) for idx, ticker in enumerate(tickers): price = midpoint_or_market_price(ticker) avg = remaining_contracts_info[idx]["avg"] if not util.isNan(price): quantity = remaining_contracts_info[idx]["position"] order = LimitOrder( action, quantity, price, algoStrategy="Adaptive", algoParams=[ TagValue(tag="adaptivePriority", value="Normal") ], tif="DAY", ) contract = ticker.contract trade = self.ib.placeOrder(contract, order) trade.filledEvent += log_fill self.log_order(contract, quantity, price, action, avg) else: with open("logs/errors.txt", "a") as f: f.write( f"Error selling {right} at {self._nope_value} | {self._underlying_price}\n" )
def ib_submit_order( self, contract_object_with_ib_data, trade_list, account="", order_type="market", limit_price=None, ): if contract_object_with_ib_data.is_spread_contract(): ibcontract_with_legs = self.ib_futures_contract( contract_object_with_ib_data, trade_list_for_multiple_legs=trade_list, return_leg_data=True, ) ibcontract = ibcontract_with_legs.ibcontract else: ibcontract = self.ib_futures_contract(contract_object_with_ib_data) ibcontract_with_legs = ibcontractWithLegs(ibcontract) if ibcontract is missing_contract: return missing_order ib_BS_str, ib_qty = resolveBS_for_list(trade_list) if order_type == "market": ib_order = MarketOrder(ib_BS_str, ib_qty) elif order_type == "limit": if limit_price is None: self.log.critical("Need to have limit price with limit order!") return missing_order else: ib_order = LimitOrder(ib_BS_str, ib_qty, limit_price) else: self.log.critical("Order type %s not recognised!" % order_type) return missing_order if account != "": ib_order.account = account order_object = self.ib.placeOrder(ibcontract, ib_order) # for consistency with spread orders trade_with_contract = tradeWithContract(ibcontract_with_legs, order_object) return trade_with_contract
def write_puts(self, symbol, quantity): sell_ticker = self.find_eligible_contracts(symbol, "P") # Create order order = LimitOrder( "SELL", quantity, round(sell_ticker.marketPrice(), 2), algoStrategy="Adaptive", algoParams=[TagValue("adaptivePriority", "Patient")], tif="DAY", ) # Submit order trade = self.ib.placeOrder(sell_ticker.contract, order) click.echo() click.secho("Order submitted", fg="green") click.secho(f"{trade}", fg="green")
def write_calls(self, symbol, quantity, min_strike): sell_ticker = self.find_eligible_contracts(symbol, "C", min_strike) self.wait_for_midpoint_price(sell_ticker) # Create order order = LimitOrder( "SELL", quantity, round(midpoint_or_market_price(sell_ticker), 2), algoStrategy="Adaptive", algoParams=[TagValue("adaptivePriority", "Patient")], tif="DAY", ) # Submit order trade = self.wait_for_trade_submitted( self.ib.placeOrder(sell_ticker.contract, order)) click.echo() click.secho("Order submitted", fg="green") click.secho(f"{trade}", fg="green")
def enter_positions(self): portfolio = self.get_portfolio() trades = self.get_trades() action = 'BUY' if self._nope_value < self.config["nope"]["long_enter"]: held_calls = self.get_total_position(portfolio, 'C') existing_order_quantity = self.get_total_buys(trades, 'C') total_buys = held_calls + existing_order_quantity if total_buys < self.config["nope"]["call_limit"]: contracts = self.find_eligible_contracts(self.SYMBOL, 'C') # TODO: Implement contract selection from eligible candidiates contract_to_buy = contracts[self.config["nope"] ["call_strike_offset"]] qualified_contracts = self.ib.qualifyContracts(contract_to_buy) tickers = self.ib.reqTickers(*qualified_contracts) if len(tickers) > 0: price = midpoint_or_market_price(tickers[0]) call_contract = qualified_contracts[0] if not util.isNan(price): quantity = self.config["nope"]["call_quantity"] order = LimitOrder(action, quantity, price, algoStrategy="Adaptive", algoParams=[ TagValue(tag='adaptivePriority', value='Normal') ], tif="DAY") trade = self.ib.placeOrder(call_contract, order) trade.filledEvent += log_fill self.log_order(call_contract, quantity, price, action) else: with open("logs/errors.txt", "a") as f: f.write( f'Error buying call at {self._nope_value} | {self._underlying_price}\n' ) elif self._nope_value > self.config["nope"]["short_enter"]: held_puts = self.get_total_position(portfolio, 'P') existing_order_quantity = self.get_total_buys(trades, 'P') total_buys = held_puts + existing_order_quantity if total_buys < self.config["nope"]["put_limit"]: contracts = self.find_eligible_contracts(self.SYMBOL, 'P') # TODO: Implement contract selection from eligible candidates contract_to_buy = contracts[ -self.config["nope"]["put_strike_offset"] - 1] qualified_contracts = self.ib.qualifyContracts(contract_to_buy) tickers = self.ib.reqTickers(*qualified_contracts) if len(tickers) > 0: price = midpoint_or_market_price(tickers[0]) put_contract = qualified_contracts[0] if not util.isNan(price): quantity = self.config["nope"]["put_quantity"] order = LimitOrder(action, quantity, price, algoStrategy="Adaptive", algoParams=[ TagValue(tag='adaptivePriority', value='Normal') ], tif="DAY") trade = self.ib.placeOrder(put_contract, order) trade.filledEvent += log_fill self.log_order(put_contract, quantity, price, action) else: with open("logs/errors.txt", "a") as f: f.write( f'Error buying put at {self._nope_value} | {self._underlying_price}\n' )
def exit_positions(self): portfolio = self.get_portfolio() trades = self.get_trades() action = 'SELL' if self._nope_value > self.config["nope"]["long_exit"]: held_calls = self.get_held_contracts(portfolio, 'C') existing_call_order_ids = self.get_existing_order_ids( trades, 'C', 'SELL') remaining_calls = list( filter( lambda c: c['contract'].conId not in existing_call_order_ids, held_calls)) if len(remaining_calls) > 0: remaining_calls.sort(key=lambda c: c['contract'].conId) remaining_call_contracts = [ c['contract'] for c in remaining_calls ] qualified_contracts = self.ib.qualifyContracts( *remaining_call_contracts) tickers = self.ib.reqTickers(*qualified_contracts) tickers.sort(key=lambda t: t.contract.conId) for idx, ticker in enumerate(tickers): price = midpoint_or_market_price(ticker) if not util.isNan(price): quantity = remaining_calls[idx]['position'] order = LimitOrder(action, quantity, price, algoStrategy="Adaptive", algoParams=[ TagValue(tag='adaptivePriority', value='Normal') ], tif="DAY") call_contract = ticker.contract trade = self.ib.placeOrder(call_contract, order) trade.filledEvent += log_fill self.log_order(call_contract, quantity, price, action, remaining_calls[idx]['avg']) else: with open("logs/errors.txt", "a") as f: f.write( f'Error selling call at {self._nope_value} | {self._underlying_price}\n' ) if self._nope_value < self.config["nope"]["short_exit"]: held_puts = self.get_held_contracts(portfolio, 'P') existing_put_order_ids = self.get_existing_order_ids( trades, 'P', 'SELL') remaining_puts = list( filter( lambda c: c['contract'].conId not in existing_put_order_ids, held_puts)) if len(remaining_puts) > 0: remaining_puts.sort(key=lambda c: c['contract'].conId) remaining_put_contracts = [ c['contract'] for c in remaining_puts ] qualified_contracts = self.ib.qualifyContracts( *remaining_put_contracts) tickers = self.ib.reqTickers(*qualified_contracts) tickers.sort(key=lambda t: t.contract.conId) for idx, ticker in enumerate(tickers): price = midpoint_or_market_price(ticker) if not util.isNan(price): quantity = remaining_puts[idx]['position'] order = LimitOrder(action, quantity, price, algoStrategy="Adaptive", algoParams=[ TagValue(tag='adaptivePriority', value='Normal') ], tif="DAY") put_contract = ticker.contract trade = self.ib.placeOrder(put_contract, order) trade.filledEvent += log_fill self.log_order(put_contract, quantity, price, action, remaining_puts[idx]['avg']) else: with open("logs/errors.txt", "a") as f: f.write( f'Error selling put at {self._nope_value} | {self._underlying_price}\n' )
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 open_strategy(self, strategy: Strategy) -> None: """Place a new strategy at the market """ ownership = "BUY" if strategy.ownership == OwnershipType.Buyer else "SELL" reverse_ownership = "BUY" if ownership == "SELL" else "SELL" contract = Contract() contract.symbol = strategy.code contract.secType = "BAG" contract.exchange = "SMART" contract.currency = strategy.currency.value order_comboLegs = [] tp_sl_comboLegs = [] for leg in strategy.legs.values(): leg_order = ComboLeg() leg_order.conId = leg.option.contract.conId leg_order.ratio = leg.ratio leg_order.action = "BUY" if leg.ownership == OwnershipType.Buyer else "SELL" # contract.comboLegs.append(leg_order) order_comboLegs.append(leg_order) for leg in strategy.legs.values(): leg_tp_sl = ComboLeg() leg_tp_sl.conId = leg.option.contract.conId leg_tp_sl.ratio = leg.ratio leg_tp_sl.action = ( "SELL" if leg.ownership == OwnershipType.Buyer else "BUY" ) # reverse # contract.comboLegs.append(leg_order) tp_sl_comboLegs.append(leg_tp_sl) contract.comboLegs = order_comboLegs order = LimitOrder( action="BUY" if strategy.ownership == OwnershipType.Buyer else "SELL", totalQuantity=strategy.quantity, lmtPrice=strategy.entry_price, orderRef=strategy.strategy_id, orderId=self._broker.client.getReqId(), tif="GTC", transmit=True, ) # Must be False print("ORDER SENDED") self._broker.placeOrder(contract, order) # contract.comboLegs = tp_sl_comboLegs # print('take_profit_order', strategy.take_profit_price) take_profit = LimitOrder( action="SELL" if strategy.ownership == OwnershipType.Buyer else "BUY", # reverse totalQuantity=strategy.quantity, lmtPrice=strategy.take_profit_price, # lmtPrice = -0.1, orderRef=strategy.strategy_id + "_TP", orderId=self._broker.client.getReqId(), tif="GTC", transmit=True, parentId=order.orderId, ) self._broker.placeOrder(contract, take_profit)
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", )