def update_contract_position_table_with_contract_order( self, contract_order_before_fills: contractOrder, fill_list: tradeQuantity): """ Alter the strategy position table according to contract order fill value :param contract_order_before_fills: :return: """ futures_contract_entire_order = contract_order_before_fills.futures_contract list_of_individual_contracts = ( futures_contract_entire_order.as_list_of_individual_contracts()) time_date = datetime.datetime.now() log = contract_order_before_fills.log_with_attributes(self.log) for contract, trade_done in zip(list_of_individual_contracts, fill_list): self._update_positions_for_individual_contract_leg( contract=contract, trade_done=trade_done, time_date=time_date) log.msg( "Updated position of %s because of trade %s ID:%d with fills %d" % ( str(contract), str(contract_order_before_fills), contract_order_before_fills.order_id, trade_done, ))
def send_to_algo( self, contract_order_to_trade: contractOrder ) -> (Algo, orderWithControls): log = contract_order_to_trade.log_with_attributes(self.log) instrument_order = self.get_parent_of_contract_order( contract_order_to_trade) contract_order_to_trade_with_algo_set = check_and_if_required_allocate_algo_to_single_contract_order( data=self.data, contract_order=contract_order_to_trade, instrument_order=instrument_order) log.msg("Sending order %s to algo %s" % (str(contract_order_to_trade_with_algo_set), contract_order_to_trade_with_algo_set.algo_to_use)) algo_class_to_call = self.add_controlling_algo_to_order( contract_order_to_trade_with_algo_set) algo_instance = algo_class_to_call( self.data, contract_order_to_trade_with_algo_set) # THIS LINE ACTUALLY SENDS THE ORDER TO THE ALGO placed_broker_order_with_controls = algo_instance.submit_trade() if placed_broker_order_with_controls is missing_order: # important we do this or order will never execute # if no issue here will be released once order filled self.contract_stack.release_order_from_algo_control( contract_order_to_trade_with_algo_set.order_id) return missing_order return algo_instance, placed_broker_order_with_controls
def liquidity_size_contract_order( self, contract_order_after_trade_limits: contractOrder) -> contractOrder: data_broker = dataBroker(self.data) log = contract_order_after_trade_limits.log_with_attributes(self.log) # check liquidity, and if neccessary carve up order # Note for spread orders we check liquidity in the component markets liquid_qty = ( data_broker. get_largest_offside_liquid_size_for_contract_order_by_leg( contract_order_after_trade_limits)) if liquid_qty != contract_order_after_trade_limits.trade: log.msg("Cut down order to size %s from %s because of liquidity" % (str(liquid_qty), str(contract_order_after_trade_limits.trade))) if liquid_qty.equals_zero(): return missing_order contract_order_to_trade = contract_order_after_trade_limits.replace_required_trade_size_only_use_for_unsubmitted_trades( liquid_qty) return contract_order_to_trade
def apply_trade_limits_to_contract_order( self, proposed_order: contractOrder) -> contractOrder: log = proposed_order.log_with_attributes(self.log) data_trade_limits = dataTradeLimits(self.data) instrument_strategy = proposed_order.instrument_strategy # proposed_order.trade.total_abs_qty() is a scalar, returns a scalar maximum_abs_qty = ( data_trade_limits.what_trade_is_possible_for_strategy_instrument( instrument_strategy, proposed_order.trade)) contract_order_after_trade_limits = ( proposed_order. change_trade_size_proportionally_to_meet_abs_qty_limit( maximum_abs_qty)) if contract_order_after_trade_limits.trade != proposed_order.trade: log.msg("%s trade change from %s to %s because of trade limits" % ( proposed_order.key, str(proposed_order.trade), str(contract_order_after_trade_limits.trade), )) return contract_order_after_trade_limits
def round_limit_price_to_tick_size(self, contract_order: contractOrder, limit_price: float) -> float: contract = contract_order.futures_contract min_tick = self.data_broker.get_min_tick_size_for_contract(contract) if min_tick is missing_contract: log = contract_order.log_with_attributes(self.data.log) log.warn("Couldn't find min tick size for %s, not rounding limit price %f" % (str(contract), limit_price)) return limit_price rounded_limit_price = min_tick * round(limit_price / min_tick) return rounded_limit_price
def allocate_for_best_execution_no_limit( data: dataBlob, contract_order: contractOrder) -> contractOrder: # in the future could be randomized... log = contract_order.log_with_attributes(data.log) data_broker = dataBroker(data) short_of_time = data_broker.less_than_one_hour_of_trading_leg_for_contract( contract_order.futures_contract) if short_of_time: log.warn("Short of time, so allocating to algo_market") contract_order.algo_to_use = MARKET_ALGO else: log.msg("'Best' order so allocating to original_best") contract_order.algo_to_use = ORIGINAL_BEST return contract_order
def check_and_if_required_allocate_algo_to_single_contract_order( data: dataBlob, contract_order: contractOrder, instrument_order: instrumentOrder) -> contractOrder: """ :param data: dataBlog :param instrument_order: parent instrument order :param list_of_contract_orders: :return: list of contract orders with algo added """ log = contract_order.log_with_attributes(data.log) if contract_order.algo_to_use != "": # Already done return contract_order instrument_order_type = instrument_order.order_type # not used yet, but maybe in the future is_roll_order = instrument_order.roll_order if instrument_order_type == market_order_type: log.msg("Market order type, so allocating to algo_market") contract_order.algo_to_use = MARKET_ALGO elif (instrument_order_type == best_order_type or instrument_order_type == zero_roll_order_type): contract_order = allocate_for_best_execution_no_limit( data=data, contract_order=contract_order) elif instrument_order_type == limit_order_type: log.critical( "Don't have an algo for instrument level limit orders yet!") return missing_order elif instrument_order_type == balance_order_type: log.critical("Balance orders aren't executed, shouldn't even be here!") return missing_order else: log.warn( "Don't recognise order type %s so allocating to default algo_market" % instrument_order_type) contract_order.algo_to_use = DEFAULT_ALGO return contract_order
def get_ticker_object_for_order(self, order: contractOrder) -> tickerObject: contract_object = order.futures_contract trade_list_for_multiple_legs = order.trade new_log = order.log_with_attributes(self.log) contract_object_with_ib_data = ( self.futures_contract_data.get_contract_object_with_IB_data(contract_object) ) if contract_object_with_ib_data is missing_contract: new_log.warn("Can't get data for %s" % str(contract_object)) return futuresContractPrices.create_empty() ticker_with_bs = self.ib_client.get_ticker_object( contract_object_with_ib_data, trade_list_for_multiple_legs=trade_list_for_multiple_legs, ) ticker_object = ibTickerObject(ticker_with_bs, self.ib_client) return ticker_object
def get_market_data_for_order_modifies_ticker_object(self, ticker_object: tickerObject, contract_order: contractOrder) -> benchmarkPriceCollection: # We use prices for a couple of reasons: # to provide a benchmark for execution purposes # (optionally) to set limit prices ## log = contract_order.log_with_attributes(self.data.log) # Get the first 'reference' tick reference_tick = ( ticker_object.wait_for_valid_bid_and_ask_and_return_current_tick( wait_time_seconds=10 ) ) tick_analysis = ticker_object.analyse_for_tick(reference_tick) if tick_analysis is missing_data: log.warn( "Can't get market data for %s so not trading with limit order %s" % (contract_order.instrument_code, str(contract_order))) return missing_data ticker_object.clear_and_add_reference_as_first_tick(reference_tick) # These prices will be used for limit price purposes # They are scalars benchmark_side_prices = tick_analysis.side_price offside_price = tick_analysis.offside_price mid_price = tick_analysis.mid_price collected_prices = benchmarkPriceCollection( offside_price=tick_analysis.offside_price, side_price=tick_analysis.side_price, mid_price = tick_analysis.mid_price) return collected_prices
def get_and_submit_broker_order_for_contract_order( self, contract_order: contractOrder, input_limit_price: float=None, order_type: brokerOrderType=market_order_type, limit_price_from: str = limit_price_from_input, ticker_object: tickerObject=None, broker_account: str = arg_not_supplied ): log = contract_order.log_with_attributes(self.data.log) broker = self.data_broker.get_broker_name() if broker_account is arg_not_supplied: broker_account = self.data_broker.get_broker_account() broker_clientid = self.data_broker.get_broker_clientid() if ticker_object is None: ticker_object = self.data_broker.get_ticker_object_for_order(contract_order) collected_prices = self.get_market_data_for_order_modifies_ticker_object( ticker_object, contract_order ) if collected_prices is missing_data: # no data available, no can do return missing_order ## We want to preserve these otherwise there is a danger they will dynamically change collected_prices = copy(collected_prices) if order_type == limit_order_type: limit_price = self.set_limit_price( contract_order=contract_order, collected_prices=collected_prices, limit_price_from=limit_price_from, input_limit_price=input_limit_price, ) elif order_type == market_order_type: limit_price = None else: error_msg = "Order type %s not valid for broker orders" % str (order_type) log.critical(error_msg) return missing_order broker_order = create_new_broker_order_from_contract_order( contract_order, order_type=order_type, side_price=collected_prices.side_price, mid_price=collected_prices.mid_price, offside_price = collected_prices.offside_price, broker=broker, broker_account=broker_account, broker_clientid=broker_clientid, limit_price=limit_price, ) log.msg( "Created a broker order %s (not yet submitted or written to local DB)" % str(broker_order)) placed_broker_order_with_controls = self.data_broker.submit_broker_order( broker_order) if placed_broker_order_with_controls is missing_order: log.warn("Order could not be submitted") return missing_order log = placed_broker_order_with_controls.order.log_with_attributes(log) log.msg("Submitted order to IB %s" % str(placed_broker_order_with_controls.order)) placed_broker_order_with_controls.add_or_replace_ticker(ticker_object) return placed_broker_order_with_controls