示例#1
0
    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
示例#2
0
    def preprocess_contract_order(
            self, original_contract_order: contractOrder) -> contractOrder:

        if original_contract_order is missing_order:
            # weird race condition
            return missing_order

        if original_contract_order.fill_equals_desired_trade():
            return missing_order

        if original_contract_order.is_order_controlled_by_algo():
            # already being traded by an active algo
            return missing_order

        data_broker = dataBroker(self.data)

        # CHECK FOR LOCKS
        data_locks = dataLocks(self.data)
        instrument_locked = data_locks.is_instrument_locked(
            original_contract_order.instrument_code)

        market_closed = not (data_broker.is_contract_okay_to_trade(
            original_contract_order.futures_contract))
        if instrument_locked or market_closed:
            # we don't log to avoid spamming
            #print("market is closed for order %s" % str(original_contract_order))
            return missing_order

        # RESIZE
        contract_order_to_trade = self.size_contract_order(
            original_contract_order)

        return contract_order_to_trade
示例#3
0
    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,
                ))
示例#4
0
    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 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 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
示例#7
0
    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
示例#8
0
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
示例#9
0
    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
示例#10
0
    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 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
示例#12
0
    def size_contract_order(
            self, original_contract_order: contractOrder) -> contractOrder:
        # We can deal with partially filled contract orders: that's how hard we
        # are!
        remaining_contract_order = original_contract_order.create_order_with_unfilled_qty(
        )

        # Check the order doesn't breach trade limits
        contract_order_after_trade_limits = self.apply_trade_limits_to_contract_order(
            remaining_contract_order)

        contract_order_to_trade = self.liquidity_size_contract_order(
            contract_order_after_trade_limits)

        if contract_order_to_trade is missing_order:
            return missing_order

        if contract_order_to_trade.fill_equals_desired_trade():
            # Nothing left to trade
            return missing_order

        return contract_order_to_trade
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
示例#14
0
    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
示例#15
0
    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