def calculate_limit_prices_for_direct_child_orders( data: dataBlob, instrument_order: instrumentOrder, list_of_contract_orders: listOfOrders, ) -> listOfOrders: """ A direct child order only contains one contract id i.e. not an intramarket spread :param data: :param instrument_order: :param list_of_contract_orders: :return: list of contract orders """ list_of_contract_orders = [ add_limit_price_to_a_direct_child_order(data, instrument_order, child_order) for child_order in list_of_contract_orders ] flag_missing_orders = [ child_order is missing_order for child_order in list_of_contract_orders ] if any(flag_missing_orders): log = instrument_order.log_with_attributes(data.log) log.critical( "Couldn't adjust limit price for at least one child order %s: can't execute any child orders" % str(instrument_order) ) return listOfOrders([]) list_of_contract_orders = listOfOrders(list_of_contract_orders) return list_of_contract_orders
def apply_contract_fill_to_parent_order_multiple_children( self, list_of_contract_order_ids: list, instrument_order: instrumentOrder): ## Three cases for multiple children (as normally one to one) # - Inter market spread: we can't handle these yet and we'll break # - Leg by leg flat spread eg forced roll order: do nothing since doesn't change instrument positions # Distributed roll order eg if we are short -2 front, want to buy 3, will do +2 front +1 next log = instrument_order.log_with_attributes(self.log) distributed_order = self.check_to_see_if_distributed_instrument_order( list_of_contract_order_ids, instrument_order) flat_order = instrument_order.is_zero_trade() if flat_order: # An inter-market flat spread # Meaningless to do this return None elif distributed_order: # a distributed order, all orders have the same sign as the # instrument order self.apply_contract_fill_to_parent_order_distributed_children( list_of_contract_order_ids, instrument_order) else: # A proper spread trade across markets can't do this log.critical( "Can't handle inter-market spread orders! Instrument order %s %s" % (str(instrument_order), str(instrument_order.order_id)))
def create_contract_roll_orders(data: dataBlob, roll_spread_info: rollSpreadInformation, instrument_order: instrumentOrder) -> listOfOrders: diag_positions = diagPositions(data) instrument_code = instrument_order.instrument_code if roll_spread_info.position_in_priced == 0: return missing_order if diag_positions.is_roll_state_force(instrument_code): contract_orders = create_contract_orders_spread(roll_spread_info) elif diag_positions.is_roll_state_force_outright(instrument_code): contract_orders = create_contract_orders_outright(roll_spread_info) else: log = instrument_order.log_with_attributes(data.log) roll_state = diag_positions.get_roll_state(instrument_code) log.warn("Roll state %s is unexpected, might have changed" % str(roll_state)) return missing_order contract_orders = allocate_algo_to_list_of_contract_orders( data, contract_orders, instrument_order ) return contract_orders
def _put_adjusting_order_on_stack(self, new_order: instrumentOrder, existing_order_id_list: list, allow_zero_orders: bool = False) -> int: """ Considering the unfilled orders already on the stack place an additional adjusting order :param new_order: :return: """ log = new_order.log_with_attributes(self.log) existing_orders = listOfOrders([ self.get_order_with_id_from_stack(order_id) for order_id in existing_order_id_list ]) adjusted_order = calculate_adjusted_order_given_existing_orders( new_order, existing_orders, log) if adjusted_order.is_zero_trade() and not allow_zero_orders: # Trade we want is already in the system error_msg = "Adjusted order %s is zero, zero orders not allowed" % str( adjusted_order) log.warn(error_msg) raise zeroOrderException(error_msg) order_id = self._put_order_on_stack_and_get_order_id(adjusted_order) return order_id
def apply_overrides_for_instrument_and_strategy(self, proposed_order: instrumentOrder)\ -> instrumentOrder: """ Apply an override to a trade :param strategy_name: str :param instrument_code: str :return: int, updated position """ diag_overrides = diagOverrides(self.data) diag_positions = diagPositions(self.data) instrument_strategy = proposed_order.instrument_strategy original_position = diag_positions.get_current_position_for_instrument_strategy( instrument_strategy) override = diag_overrides.get_cumulative_override_for_instrument_strategy( instrument_strategy) revised_order = override.apply_override(original_position, proposed_order) if revised_order.trade != proposed_order.trade: log = proposed_order.log_with_attributes(self.log) log.msg("%s trade change from %s to %s because of override %s" % ( instrument_strategy.key, str(revised_order.trade), str(proposed_order.trade), str(override), )) return revised_order
def passive_roll_child_order( data: dataBlob, trade: int, instrument_order: instrumentOrder, ) -> list: log = instrument_order.log_with_attributes(data.log) diag_positions = diagPositions(data) instrument_code = instrument_order.instrument_code diag_contracts = diagContracts(data) current_contract = diag_contracts.get_priced_contract_id(instrument_code) next_contract = diag_contracts.get_forward_contract_id(instrument_code) position_current_contract = ( diag_positions.get_position_for_instrument_and_contract_date( instrument_code, current_contract)) # Break out because so darn complicated if position_current_contract == 0: # Passive roll and no position in the current contract, start trading # the next contract log.msg( "Passive roll handling order %s, no position in current contract, entire trade in next contract %s" % (str(instrument_order), next_contract)) return [contractIdAndTrade(next_contract, trade)] # ok still have a position in the current contract increasing_trade = sign(trade) == sign(position_current_contract) if increasing_trade: # Passive roll and increasing trade # Do it all in next contract log.msg( "Passive roll handling order %s, increasing trade, entire trade in next contract %s" % (str(instrument_order), next_contract)) return [contractIdAndTrade(next_contract, trade)] # ok a reducing trade new_position = position_current_contract + trade sign_of_position_is_unchanged = sign(position_current_contract) == sign( new_position) if new_position == 0 or sign_of_position_is_unchanged: # A reducing trade that we can do entirely in the current contract log.msg( "Passive roll handling order %s, reducing trade, entire trade in next contract %s" % (str(instrument_order), next_contract)) return [contractIdAndTrade(current_contract, trade)] # OKAY to recap: it's a passive roll, but the trade will be split between # current and next list_of_child_contract_dates_and_trades = \ passive_trade_split_over_two_contracts(trade=trade, current_contract=current_contract, next_contract=next_contract, position_current_contract=position_current_contract) log.msg( "Passive roll handling order %s, reducing trade, split trade between contract %s and %s" % (str(instrument_order), current_contract, next_contract)) return list_of_child_contract_dates_and_trades
def put_balance_trades_on_stack( self, instrument_order: instrumentOrder, contract_order: contractOrder, broker_order: brokerOrder, ): log = instrument_order.log_with_attributes(self.log) log.msg("Putting balancing trades on stacks") try: instrument_order_id = ( self.instrument_stack. put_manual_order_on_stack_and_return_order_id(instrument_order) ) except Exception as e: log.error( "Couldn't add balancing instrument trade error condition %s" % str(e)) return failure, missing_order, missing_order, missing_order try: contract_order.parent = instrument_order_id contract_order_id = self.contract_stack.put_order_on_stack( contract_order) except Exception as e: log.error( "Couldn't add balancing contract trade error condition %s " % str(e)) return failure, instrument_order_id, missing_order, missing_order try: self.instrument_stack.add_children_to_order_without_existing_children( instrument_order_id, [contract_order_id]) except Exception as e: log.error("Couldn't add children to instrument order error %s" % str(e)) return failure, instrument_order_id, contract_order_id, missing_order broker_order.parent = contract_order_id try: broker_order_id = self.broker_stack.put_order_on_stack( broker_order) except Exception as e: log.error( "Couldn't add balancing broker trade error condition %s" % str(e)) return failure, instrument_order_id, contract_order_id, missing_order try: self.contract_stack.add_children_to_order_without_existing_children( contract_order_id, [broker_order_id]) except Exception as e: log.error("Couldn't add children to contract order exception %s" % str(e)) return failure, instrument_order_id, contract_order_id, broker_order_id log.msg("All balancing trades added to stacks") return success, instrument_order_id, contract_order_id, broker_order_id
def update_strategy_position_table_with_instrument_order( self, original_instrument_order: instrumentOrder, new_fill: tradeQuantity): """ Alter the strategy position table according to new_fill value :param original_instrument_order: :return: """ instrument_strategy = original_instrument_order.instrument_strategy current_position_as_int = self.diag_positions.get_current_position_for_instrument_strategy( instrument_strategy) trade_done_as_int = new_fill.as_single_trade_qty_or_error() if trade_done_as_int is missing_order: self.log.critical("Instrument orders can't be spread orders!") return failure new_position_as_int = current_position_as_int + trade_done_as_int self.db_strategy_position_data.update_position_for_instrument_strategy_object( instrument_strategy, new_position_as_int) log = original_instrument_order.log_with_attributes(self.log) log.msg( "Updated position of %s from %d to %d because of trade %s %d fill %s" % (str(instrument_strategy), current_position_as_int, new_position_as_int, str(original_instrument_order), original_instrument_order.order_id, str(new_fill))) return success
def get_required_contract_trade_for_instrument(data: dataBlob, instrument_order: instrumentOrder) -> list: """ Return the contract to trade for a given instrument Depends on roll status and trade vs position: - roll_states = ['No_Roll', 'Passive', 'Force', 'Force_Outright', 'Roll_Adjusted'] If 'No Roll' then trade current contract If 'Passive', and no position in current contract: trade next contract If 'Passive', and reducing trade which leaves zero or something in current contract: trade current contract If 'Passive', and reducing trade which is larger than current contract position: trade current and next contract If 'Passive', and increasing trade: trade next contract If 'Force' or 'Force Outright' or 'Roll_Adjusted': don't trade :param instrument_order: :param data: dataBlog :return: tuple: list of child orders: each is a tuple: contract str or missing_contract, trade int """ instrument_code = instrument_order.instrument_code log = instrument_order.log_with_attributes(data.log) trade = instrument_order.as_single_trade_qty_or_error() if trade is missing_order: log.critical("Instrument order can't be a spread order") return [] diag_positions = diagPositions(data) if diag_positions.is_roll_state_no_roll(instrument_code): diag_contracts = dataContracts(data) current_contract = diag_contracts.get_priced_contract_id(instrument_code) log.msg( "No roll, allocating entire order %s to current contract %s" % (str(instrument_order), current_contract) ) return [contractIdAndTrade(current_contract, trade)] elif diag_positions.is_roll_state_passive(instrument_code): # no log as function does it list_of_child_contract_dates_and_trades = passive_roll_child_order(data=data, instrument_order = instrument_order, trade = trade ) return list_of_child_contract_dates_and_trades elif diag_positions.is_type_of_active_rolling_roll_state(instrument_code): log.msg( "Roll state is active rolling, not going to generate trade for order %s" % (str(instrument_order))) return [] else: log.critical( "Roll state %s not understood: can't generate trade for %s" % (diag_positions.get_name_of_roll_state(instrument_code), str(instrument_order)) ) return []
def create_balance_instrument_trade(self, instrument_order: instrumentOrder): log = instrument_order.log_with_attributes(self.log) log.msg("Putting balancing order on instrument stack") instrument_order_id = self.instrument_stack.put_manual_order_on_stack_and_return_order_id( instrument_order) instrument_order.order_id = instrument_order_id log.msg( "Marking balancing trades as completed and updating positions and historic order data" ) self.apply_position_change_to_instrument(instrument_order, instrument_order.fill, apply_entire_trade=True) self.handle_completed_instrument_order(instrument_order_id)
def adjust_order_for_position_limits(self, order: instrumentOrder) -> instrumentOrder: log = order.log_with_attributes(self.log) data_position_limits = dataPositionLimits(self.data) cut_down_order = data_position_limits.cut_down_proposed_instrument_trade_for_position_limits(order) if cut_down_order.trade != order.trade: if cut_down_order.is_zero_trade(): ## at position limit, can't do anything log.warn("Can't trade at all because of position limits %s" % str(order)) else: log.warn("Can't do full trade of %s because of position limits, can only do %s" % (str(order), str(cut_down_order.trade))) return cut_down_order
def submit_order(self, order: instrumentOrder): log = order.log_with_attributes(self.log) cut_down_order = self.adjust_order_for_position_limits(order) try: order_id = self.order_stack.put_order_on_stack(cut_down_order) except zeroOrderException: # we checked for zero already, which means that there is an existing order on the stack # An existing order of the same size log.warn("Ignoring new order as either zero size or it replicates an existing order on the stack") else: log.msg( "Added order %s to instrument order stack with order id %d" % (str(order), order_id), instrument_order_id=order_id, )
def _put_new_order_on_stack_when_no_existing_order( self, new_order: instrumentOrder, allow_zero_orders: bool = False ) -> int: # no current order for this instrument/strategy log = new_order.log_with_attributes(self.log) if new_order.is_zero_trade() and not allow_zero_orders: log_msg = "Zero orders not allowed" log.msg(log_msg) raise zeroOrderException(log_msg) log.msg("New order %s putting on %s" % (str(new_order), str(self))) order_id = self._put_order_on_stack_and_get_order_id(new_order) return order_id
def add_limit_price_to_a_direct_child_order( data: dataBlob, instrument_order: instrumentOrder, child_order: contractOrder ) -> contractOrder: """ :param data: dataBlob :param instrument_order: :param child_order: will be modified :return: float """ contract_to_match = instrument_order.limit_contract price_to_adjust = instrument_order.limit_price if contract_to_match is None or price_to_adjust is None: # No limit price so don't bother return child_order new_limit_price = calculate_adjusted_price_for_a_direct_child_order( data, child_order, contract_to_match, price_to_adjust ) if new_limit_price is missing_data: # This is a serious problem # We can't possibly execute any part of the parent order log = instrument_order.log_with_attributes(data.log) log.critical( "Couldn't adjust limit price for order %s child %s going from %s to %s" % ( str(instrument_order), str(child_order), contract_to_match, child_order.contract_date, ) ) return missing_order child_order.limit_price = new_limit_price return child_order
def add_reference_price_to_a_direct_child_order( data: dataBlob, instrument_order: instrumentOrder, child_order: contractOrder ): """ :param data: dataBlob :param instrument_order: :param child_order: will be modified :return: child order """ contract_to_match = instrument_order.reference_contract price_to_adjust = instrument_order.reference_price if contract_to_match is None or price_to_adjust is None: # No reference price so don't bother return child_order new_reference_price = calculate_adjusted_price_for_a_direct_child_order( data, child_order, contract_to_match, price_to_adjust ) if new_reference_price is missing_data: log = instrument_order.log_with_attributes(data.log) log.warn( "Couldn't adjust reference price for order %s child %s going from %s to %s, can't do TCA" % ( str(instrument_order), str(child_order), contract_to_match, child_order.contract_date, ) ) return child_order child_order.reference_price = new_reference_price return child_order
def add_instrument_and_list_of_contract_orders_to_stack( self, instrument_order: instrumentOrder, list_of_contract_orders: listOfOrders ): instrument_stack = self.instrument_stack contract_stack = self.contract_stack parent_log = instrument_order.log_with_attributes(self.log) # Do as a transaction: if everything doesn't go to plan can roll back # We lock now, and instrument_order.lock_order() try: parent_order_id = instrument_stack.put_order_on_stack( instrument_order, allow_zero_orders = True) except Exception as parent_order_error: parent_log.warn( "Couldn't put parent order %s on instrument order stack error %s" % (str(instrument_order), str(parent_order_error))) instrument_order.unlock_order() return None ## Parent order is now on stack in locked state ## We will unlock at the end, or during a rollback # Do as a transaction: if everything doesn't go to plan can roll back # if this try fails we will roll back the instrument commit list_of_child_order_ids =[] try: # Add parent order to children # This will only throw an error if the orders already have parents, which they shouldn't for child_order in list_of_contract_orders: child_order.parent = parent_order_id # this will return eithier - # - a list of order IDS if all went well # - an empty list if error and rolled back, # - or an error something went wrong and couldn't rollback (the outer catch will try and rollback) list_of_child_order_ids = put_children_on_stack(child_stack = contract_stack, parent_log=parent_log, list_of_child_orders=list_of_contract_orders, parent_order = instrument_order) if len(list_of_child_order_ids)==0: ## We had an error, but manged to roll back the children. Still need to throw an error so the parent ## will be rolledback. But because the list_of_child_order_ids is probably zero ## we won't try and rollback children in the catch statement raise Exception("Couldn't put child orders on stack, children were rolled back okay") ## All seems to have worked # still locked remember instrument_stack.unlock_order_on_stack(parent_order_id) instrument_stack.add_children_to_order_without_existing_children( parent_order_id, list_of_child_order_ids ) except Exception as error_from_adding_child_orders: # okay it's gone wrong # Roll back parent order and possibly children # At this point list_of_child_order_ids will eithier be empty (if succesful rollback) or contain child ids rollback_parents_and_children_and_handle_exceptions(child_stack=contract_stack, parent_stack=instrument_stack, list_of_child_order_ids=list_of_child_order_ids, parent_order_id=parent_order_id, error_from_adding_child_orders=error_from_adding_child_orders, parent_log=parent_log) # phew got there parent_log.msg("Added parent order with ID %d %s to stack" % (parent_order_id, str(instrument_order))) log_successful_adding(list_of_child_orders=list_of_contract_orders, list_of_child_ids=list_of_child_order_ids, parent_order=instrument_order, parent_log=parent_log)