Пример #1
0
    def _fill_order(self, order: OrderCommon, qty_to_fill, price_fill, fee=0.):
        assert price_fill >= 0, "Price has to be positive"
        assert abs(order.leaves_qty
                   ) >= 1, "Order with leaves_qty < 1 should be closed"
        assert abs(order.signed_qty
                   ) >= 1, "Order signed_qty should not be less than 1"
        assert abs(qty_to_fill) >= 1, "Can not fill order with qty less than 1"

        fully_filled = order.fill(qty_to_fill)
        side = order.side()
        fill = Fill(
            symbol=order.symbol,
            side=order.side_str(),
            qty_filled=qty_to_fill,
            price_fill=price_fill,
            fill_time=self.current_timestamp,
            fill_type=FillType.complete if fully_filled else FillType.partial,
            order_id=order.client_id)
        self._log_fill(fill)
        self.volume[order.symbol] += abs(qty_to_fill) * price_fill
        self.n_fills[order.symbol] += 1

        position = self.positions[order.symbol]
        position.update(signed_qty=qty_to_fill * side,
                        price=price_fill,
                        leverage=self.leverage[order.symbol],
                        current_timestamp=self.current_timestamp,
                        fee=fee)

        method = self._get_tactic(order.client_id).handle_fill
        self._queue_append(self.current_timestamp + WS_DELAY, method, fill)

        return fully_filled
Пример #2
0
    def _exec_market_order(self, order: OrderCommon):
        assert order.type == OrderType.Market
        assert order.status == OrderStatus.Pending
        order.status = OrderStatus.New
        tick_size = order.symbol.tick_size

        # TODO: watch for self-cross

        # We only fill at two price levels: best price and second best price
        if order.side() > 0:
            best_price = self.current_quote.ask_price
            second_best = best_price + tick_size
            size = self.current_quote.ask_size
        else:
            best_price = self.current_quote.bid_price
            second_best = best_price - tick_size
            size = self.current_quote.bid_size

        qty_fill1 = min(size, order.leaves_qty)
        qty_fill2 = order.leaves_qty - qty_fill1

        fully_filled = self._fill_order(order=order,
                                        qty_to_fill=qty_fill1,
                                        price_fill=best_price)
        if not fully_filled:
            assert qty_fill2 > 0
            self._fill_order(order=order,
                             qty_to_fill=qty_fill2,
                             price_fill=second_best)

        return
Пример #3
0
    def _exec_limit_order(self, order: OrderCommon):
        assert order.type == OrderType.Limit

        bid = self.current_quote.bid_price
        ask = self.current_quote.ask_price
        order_side = order.side()

        self._check_price_sanity(order)

        violated_post_only = order_side > 0 and order.price >= ask or order_side < 0 and order.price <= bid
        if violated_post_only:
            order.status = OrderStatus.Canceled
            order.status_msg = OrderCancelReason.cross_during_post_only
            self._exec_order_cancels([order])
            return

        order.status = OrderStatus.New
        self.active_orders[order.client_id] = order
        order._made_spread = order_side > 0 and order.price > bid or order_side < 0 and order.price < ask
Пример #4
0
    def _execute_order(self, order: OrderCommon) -> Fill:
        assert self.tick_num < len(self.ohlcv)

        if order.status != OrderStatus.Pending and not order.is_open():
            raise ValueError("expected order to be opened, but got " + str(order.status) + ". Order = \n"
                             + order.get_header() + "\n" + str(order))
        current_candle = self.current_candle()
        current_time = current_candle.name  # pd.Timestamp
        high = current_candle.high
        low = current_candle.low
        open = current_candle.open
        close = current_candle.close

        position = self.positions[order.symbol]  # type: PositionSim
        current_qty = position.signed_qty
        qty_fill = qty_to_close = outstanding_qty = None
        crossed = False

        if order.type is OrderType.Market:
            crossed = True
            price_fill = self._estimate_price()
            qty_fill = order.signed_qty
        elif order.type is OrderType.Limit:
            price_fill = order.price
            max_qty_fill = order.leaves_qty * order.side()
            # clip fill
            if (open <= order.price <= close) or (close <= order.price <= open):
                qty_fill = max_qty_fill
            elif high == low == order.price:
                qty_fill = round(0.5 * max_qty_fill)
            else:
                if low < order.price < high:
                    if order.is_sell():
                        factor = max((high - order.price) / (high - low), 0.50001)
                        assert factor >= 0
                    else:
                        factor = max((low - order.price) / (low - high), 0.50001)
                        assert factor >= 0
                    qty_fill = round(factor * max_qty_fill)
            if qty_fill is not None:
                crossed = True
        else:
            raise ValueError("order type " + str(order.type) + " not supported")

        if not crossed:
            return None  # type: Fill

        if position.is_open and position.would_change_side(qty_fill):
            qty_to_close = float(sign(qty_fill)) * min(abs(current_qty), abs(qty_fill))
            outstanding_qty = qty_fill - qty_to_close

        if order.fill(qty_fill) or order.type == OrderType.Market:
            order.status = OrderStatus.Filled
            order.fill_price = price_fill

        if order.price is not None and ((open <= order.price <= close) or (close <= order.price <= open)):
            assert order.status == OrderStatus.Filled

        fee = self.FEE[order.type]

        if outstanding_qty:
            position = self._update_position(order.symbol,
                                             qty=qty_to_close,
                                             price=price_fill,
                                             leverage=self.leverage[order.symbol],
                                             current_timestamp=current_time,
                                             fee=fee)
            assert not position.is_open

            position = self._update_position(order.symbol,
                                             qty=outstanding_qty,
                                             price=price_fill,
                                             leverage=self.leverage[order.symbol],
                                             current_timestamp=current_time,
                                             fee=fee)
            assert position.is_open
        else:
            self._update_position(order.symbol,
                                  qty=qty_fill,
                                  price=price_fill,
                                  leverage=self.leverage[order.symbol],
                                  current_timestamp=current_time,
                                  fee=fee)

        fill = Fill(order=order,
                    qty_filled=qty_fill,
                    price_fill=price_fill,
                    fill_time=current_time,
                    fill_type=FillType.complete if (order.status == OrderStatus.Filled) else FillType.partial)
        self.fills_hist += [fill]
        self.active_orders = drop_closed_orders_dict(self.active_orders)
        if self.can_call_handles:
            order.tactic.handle_fill(fill)
        return fill