示例#1
0
    def on_order(self, raw: dict, action: str):
        # DEV NOTE: this is the only method who can delete orders from self.all_orders
        # DEV NOTE: this is the only method who broadcast orders to tactics

        order_id = raw.get('clOrdID')

        if not order_id:
            self.logger.error(
                'Got an order without "clOrdID", probably a bitmex order (e.g., liquidation)'
            )
            self.logger.error('order: {}'.format(raw))
            raise AttributeError(
                'If we were liquidated, we should change our tactics')

        if OrderStatus[raw['ordStatus']] == OrderStatus.Rejected:
            raise ValueError(
                'A order should never be rejected, please fix the tactic. Order: {}'
                .format(raw))

        order = self.all_orders.get(order_id)

        if order:
            if action != 'update':
                raise AttributeError(
                    'Got action "{}". This should be an "update", since this order already '
                    'exist in our data and we don\'t expect a "delete"'.format(
                        action))

            if not order.is_open():
                # This is the case when a closed (e.g. Canceled) order where inserted, now we clean it up in the insert
                # We also assume that bitmex will not update a canceled/closed order twice
                del self.all_orders[order.client_id]
                return

            order.update_from_bitmex(raw)  # type: OrderCommon

            if not order.is_open():
                del self.all_orders[order.client_id]

                self._notify_cancel_if_the_case(order)

        else:
            symbol = Symbol[raw['symbol']]
            type_ = OrderType[raw['ordType']]
            order = OrderCommon(symbol=symbol,
                                type=type_).update_from_bitmex(raw)

            if action != 'insert' and order.is_open():
                raise AttributeError(
                    'Got action "{}". This should be an "insert", since this data does\'nt exist '
                    'in memory and is open. Order: {}'.format(action, raw))

            # yes, always add an order to the list, even if it is closed. If so, it will be removed in the "update" feed
            self.all_orders[order_id] = order

            self._notify_cancel_if_the_case(order)
示例#2
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