Пример #1
0
 def get_trade_price(self, order, price):
     side = order.side
     temp_price = price + price * self.rate * (1 if side == SIDE.BUY else -1)
     try:
         temp_bar = Environment.get_instance().bar_dict[order.order_book_id]
     except KeyError:
         pass
     else:
         limit_up, limit_down = temp_bar.limit_up, temp_bar.limit_down
         if is_valid_price(limit_up):
             temp_price = min(temp_price, limit_up)
         if is_valid_price(limit_down):
             temp_price = max(temp_price, limit_down)
     return temp_price
Пример #2
0
def order_value(id_or_ins, cash_amount, price=None, style=None):
    """
    使用想要花费的金钱买入/卖出股票,而不是买入/卖出想要的股数,正数代表买入,负数代表卖出。股票的股数总是会被调整成对应的100的倍数(在A中国A股市场1手是100股)。当您提交一个卖单时,该方法代表的意义是您希望通过卖出该股票套现的金额。如果金额超出了您所持有股票的价值,那么您将卖出所有股票。需要注意,如果资金不足,该API将不会创建发送订单。

    :param id_or_ins: 下单标的物
    :type id_or_ins: :class:`~Instrument` object | `str`

    :param float cash_amount: 需要花费现金购买/卖出证券的数目。正数代表买入,负数代表卖出。

    :param float price: 下单价格,默认为None,表示 :class:`~MarketOrder`, 此参数主要用于简化 `style` 参数。

    :param style: 下单类型, 默认是市价单。目前支持的订单类型有 :class:`~LimitOrder` 和 :class:`~MarketOrder`
    :type style: `OrderStyle` object

    :return: :class:`~Order` object

    :example:

    .. code-block:: python

        #买入价值¥10000的平安银行股票,并以市价单发送。如果现在平安银行股票的价格是¥7.5,那么下面的代码会买入1300股的平安银行,因为少于100股的数目将会被自动删除掉:
        order_value('000001.XSHE', 10000)
        #卖出价值¥10000的现在持有的平安银行:
        order_value('000001.XSHE', -10000)

    """

    style = cal_style(price, style)

    if isinstance(style, LimitOrder):
        if style.get_limit_price() <= 0:
            raise RQInvalidArgument(_(u"Limit order price should be positive"))

    order_book_id = assure_stock_order_book_id(id_or_ins)
    env = Environment.get_instance()

    price = env.get_last_price(order_book_id)
    if not is_valid_price(price):
        user_system_log.warn(
            _(u"Order Creation Failed: [{order_book_id}] No market data").format(order_book_id=order_book_id))
        return

    account = env.portfolio.accounts[DEFAULT_ACCOUNT_TYPE.STOCK.name]
    round_lot = int(env.get_instrument(order_book_id).round_lot)

    if cash_amount > 0:
        cash_amount = min(cash_amount, account.cash)

    if isinstance(style, MarketOrder):
        amount = int(Decimal(cash_amount) / Decimal(price) / Decimal(round_lot)) * round_lot
    else:
        amount = int(Decimal(cash_amount) / Decimal(style.get_limit_price()) / Decimal(round_lot)) * round_lot

    # if the cash_amount is larger than you current security’s position,
    # then it will sell all shares of this security.

    position = account.positions[order_book_id]
    amount = downsize_amount(amount, position)

    return order_shares(order_book_id, amount, style=style)
Пример #3
0
def submit_order(id_or_ins, amount, side, price=None, position_effect=None):

    order_book_id = assure_order_book_id(id_or_ins)
    env = Environment.get_instance()
    if env.config.base.run_type != RUN_TYPE.BACKTEST:
        if "88" in order_book_id:
            raise RQInvalidArgument(_(u"Main Future contracts[88] are not supported in paper trading."))
        if "99" in order_book_id:
            raise RQInvalidArgument(_(u"Index Future contracts[99] are not supported in paper trading."))
    style = cal_style(price, None)
    market_price = env.get_last_price(order_book_id)
    if not is_valid_price(market_price):
        user_system_log.warn(
            _(u"Order Creation Failed: [{order_book_id}] No market data").format(order_book_id=order_book_id)
        )
        return

    order = Order.__from_create__(
        order_book_id=order_book_id,
        quantity=amount,
        side=side,
        style=style,
        position_effect=position_effect
    )

    if order.type == ORDER_TYPE.MARKET:
        order.set_frozen_price(market_price)
    if env.can_submit_order(order):
        env.broker.submit_order(order)
    return order
Пример #4
0
 def _on_bar(self, event):
     # run once
     if len(self._positions) == 0:
         price = event.bar_dict[self.benchmark].close
         if not is_valid_price(price):
             return
         position = self._positions.get_or_create(self.benchmark)
         quantity = int(self._total_cash / price)
         position._quantity = quantity
         position._avg_price = price
         self._total_cash -= quantity * price
Пример #5
0
    def match(self, open_orders):
        price_board = self._env.price_board
        for account, order in open_orders:
            order_book_id = order.order_book_id
            instrument = self._env.get_instrument(order_book_id)

            deal_price = self._deal_price_decider(order_book_id, order.side)
            if not is_valid_price(deal_price):
                listed_date = instrument.listed_date.date()
                if listed_date == self._trading_dt.date():
                    reason = _(
                        u"Order Cancelled: current security [{order_book_id}] can not be traded in listed date [{listed_date}]").format(
                        order_book_id=order.order_book_id,
                        listed_date=listed_date,
                    )
                else:
                    reason = _(u"Order Cancelled: current bar [{order_book_id}] miss market data.").format(
                        order_book_id=order.order_book_id)
                order.mark_rejected(reason)
                continue

            if order.type == ORDER_TYPE.LIMIT:
                if order.side == SIDE.BUY and order.price < deal_price:
                    continue
                if order.side == SIDE.SELL and order.price > deal_price:
                    continue
                # 是否限制涨跌停不成交
                if self._price_limit:
                    if order.side == SIDE.BUY and deal_price >= price_board.get_limit_up(order_book_id):
                        continue
                    if order.side == SIDE.SELL and deal_price <= price_board.get_limit_down(order_book_id):
                        continue
                if self._liquidity_limit:
                    if order.side == SIDE.BUY and price_board.get_a1(order_book_id) == 0:
                        continue
                    if order.side == SIDE.SELL and price_board.get_b1(order_book_id) == 0:
                        continue
            else:
                if self._price_limit:
                    if order.side == SIDE.BUY and deal_price >= price_board.get_limit_up(order_book_id):
                        reason = _(
                            "Order Cancelled: current bar [{order_book_id}] reach the limit_up price."
                        ).format(order_book_id=order.order_book_id)
                        order.mark_rejected(reason)
                        continue
                    if order.side == SIDE.SELL and deal_price <= price_board.get_limit_down(order_book_id):
                        reason = _(
                            "Order Cancelled: current bar [{order_book_id}] reach the limit_down price."
                        ).format(order_book_id=order.order_book_id)
                        order.mark_rejected(reason)
                        continue
                if self._liquidity_limit:
                    if order.side == SIDE.BUY and price_board.get_a1(order_book_id) == 0:
                        reason = _(
                            "Order Cancelled: [{order_book_id}] has no liquidity."
                        ).format(order_book_id=order.order_book_id)
                        order.mark_rejected(reason)
                        continue
                    if order.side == SIDE.SELL and price_board.get_b1(order_book_id) == 0:
                        reason = _(
                            "Order Cancelled: [{order_book_id}] has no liquidity."
                        ).format(order_book_id=order.order_book_id)
                        order.mark_rejected(reason)
                        continue

            if self._volume_limit:
                bar = self._env.bar_dict[order_book_id]
                volume_limit = round(bar.volume * self._volume_percent) - self._turnover[order.order_book_id]
                round_lot = instrument.round_lot
                volume_limit = (volume_limit // round_lot) * round_lot
                if volume_limit <= 0:
                    if order.type == ORDER_TYPE.MARKET:
                        reason = _(u"Order Cancelled: market order {order_book_id} volume {order_volume}"
                                   u" due to volume limit").format(
                            order_book_id=order.order_book_id,
                            order_volume=order.quantity
                        )
                        order.mark_cancelled(reason)
                    continue

                unfilled = order.unfilled_quantity
                fill = min(unfilled, volume_limit)
            else:
                fill = order.unfilled_quantity

            ct_amount = account.positions.get_or_create(order.order_book_id).cal_close_today_amount(fill, order.side)
            price = self._slippage_decider.get_trade_price(order, deal_price)

            trade = Trade.__from_create__(
                order_id=order.order_id,
                price=price,
                amount=fill,
                side=order.side,
                position_effect=order.position_effect,
                order_book_id=order.order_book_id,
                frozen_price=order.frozen_price,
                close_today_amount=ct_amount
            )
            trade._commission = self._env.get_trade_commission(account_type_str2enum(account.type), trade)
            trade._tax = self._env.get_trade_tax(account_type_str2enum(account.type), trade)
            order.fill(trade)
            self._turnover[order.order_book_id] += fill

            self._env.event_bus.publish_event(Event(EVENT.TRADE, account=account, trade=trade, order=order))

            if order.type == ORDER_TYPE.MARKET and order.unfilled_quantity != 0:
                reason = _(
                    u"Order Cancelled: market order {order_book_id} volume {order_volume} is"
                    u" larger than {volume_percent_limit} percent of current bar volume, fill {filled_volume} actually"
                ).format(
                    order_book_id=order.order_book_id,
                    order_volume=order.quantity,
                    filled_volume=order.filled_quantity,
                    volume_percent_limit=self._volume_percent * 100.0
                )
                order.mark_cancelled(reason)
Пример #6
0
def order_shares(id_or_ins, amount, price=None, style=None):
    """
    落指定股数的买/卖单,最常见的落单方式之一。如有需要落单类型当做一个参量传入,如果忽略掉落单类型,那么默认是市价单(market order)。

    :param id_or_ins: 下单标的物
    :type id_or_ins: :class:`~Instrument` object | `str`

    :param int amount: 下单量, 正数代表买入,负数代表卖出。将会根据一手xx股来向下调整到一手的倍数,比如中国A股就是调整成100股的倍数。

    :param float price: 下单价格,默认为None,表示 :class:`~MarketOrder`, 此参数主要用于简化 `style` 参数。

    :param style: 下单类型, 默认是市价单。目前支持的订单类型有 :class:`~LimitOrder` 和 :class:`~MarketOrder`
    :type style: `OrderStyle` object

    :return: :class:`~Order` object

    :example:

    .. code-block:: python

        #购买Buy 2000 股的平安银行股票,并以市价单发送:
        order_shares('000001.XSHE', 2000)
        #卖出2000股的平安银行股票,并以市价单发送:
        order_shares('000001.XSHE', -2000)
        #购买1000股的平安银行股票,并以限价单发送,价格为¥10:
        order_shares('000001.XSHG', 1000, style=LimitOrder(10))
    """
    if amount == 0:
        # 如果下单量为0,则认为其并没有发单,则直接返回None
        return None
    style = cal_style(price, style)
    if isinstance(style, LimitOrder):
        if style.get_limit_price() <= 0:
            raise RQInvalidArgument(_(u"Limit order price should be positive"))
    order_book_id = assure_stock_order_book_id(id_or_ins)
    env = Environment.get_instance()

    price = env.get_last_price(order_book_id)
    if not is_valid_price(price):
        user_system_log.warn(
            _(u"Order Creation Failed: [{order_book_id}] No market data").format(order_book_id=order_book_id))
        return

    if amount > 0:
        side = SIDE.BUY
    else:
        amount = abs(amount)
        side = SIDE.SELL

    round_lot = int(env.get_instrument(order_book_id).round_lot)

    try:
        amount = int(Decimal(amount) / Decimal(round_lot)) * round_lot
    except ValueError:
        amount = 0

    r_order = Order.__from_create__(order_book_id, amount, side, style, None)

    if price == 0:
        user_system_log.warn(
            _(u"Order Creation Failed: [{order_book_id}] No market data").format(order_book_id=order_book_id))
        r_order.mark_rejected(
            _(u"Order Creation Failed: [{order_book_id}] No market data").format(order_book_id=order_book_id))
        return r_order

    if amount == 0:
        # 如果计算出来的下单量为0, 则不生成Order, 直接返回None
        # 因为很多策略会直接在handle_bar里面执行order_target_percent之类的函数,经常会出现下一个量为0的订单,如果这些订单都生成是没有意义的。
        r_order.mark_rejected(_(u"Order Creation Failed: 0 order quantity"))
        return r_order
    if r_order.type == ORDER_TYPE.MARKET:
        r_order.set_frozen_price(price)
    if env.can_submit_order(r_order):
        env.broker.submit_order(r_order)

    return r_order
Пример #7
0
def order(id_or_ins, amount, side, position_effect, style):
    if not isinstance(style, OrderStyle):
        raise RuntimeError
    if amount < 0:
        raise RuntimeError
    if amount == 0:
        user_system_log.warn(_(u"Order Creation Failed: Order amount is 0."))
        return None
    if isinstance(style, LimitOrder) and style.get_limit_price() <= 0:
        raise RQInvalidArgument(_(u"Limit order price should be positive"))

    order_book_id = assure_future_order_book_id(id_or_ins)
    env = Environment.get_instance()
    if env.config.base.run_type != RUN_TYPE.BACKTEST:
        if "88" in order_book_id:
            raise RQInvalidArgument(_(u"Main Future contracts[88] are not supported in paper trading."))
        if "99" in order_book_id:
            raise RQInvalidArgument(_(u"Index Future contracts[99] are not supported in paper trading."))

    price = env.get_last_price(order_book_id)
    if not is_valid_price(price):
        user_system_log.warn(
            _(u"Order Creation Failed: [{order_book_id}] No market data").format(order_book_id=order_book_id)
        )
        return

    amount = int(amount)

    env = Environment.get_instance()

    orders = []
    if position_effect == POSITION_EFFECT.CLOSE:
        if side == SIDE.BUY:
            if env.portfolio:
                position = env.portfolio.positions[order_book_id]
                sell_quantity, sell_old_quantity = position.sell_quantity, position.sell_old_quantity
            else:
                position = env.booking.get_position(order_book_id, POSITION_DIRECTION.SHORT)
                sell_quantity, sell_old_quantity = position.quantity, position.old_quantity

            # 如果平仓量大于持仓量,则 Warning 并 取消订单创建
            if amount > sell_quantity:
                user_system_log.warn(
                    _(u"Order Creation Failed: close amount {amount} is larger than position "
                      u"quantity {quantity}").format(amount=amount, quantity=sell_quantity)
                )
                return []
            sell_old_quantity = sell_old_quantity
            if amount > sell_old_quantity:
                if sell_old_quantity != 0:
                    # 如果有昨仓,则创建一个 POSITION_EFFECT.CLOSE 的平仓单
                    orders.append(Order.__from_create__(
                        order_book_id,
                        sell_old_quantity,
                        side,
                        style,
                        POSITION_EFFECT.CLOSE
                    ))
                # 剩下还有仓位,则创建一个 POSITION_EFFECT.CLOSE_TODAY 的平今单
                orders.append(Order.__from_create__(
                    order_book_id,
                    amount - sell_old_quantity,
                    side,
                    style,
                    POSITION_EFFECT.CLOSE_TODAY
                ))
            else:
                # 创建 POSITION_EFFECT.CLOSE 的平仓单
                orders.append(Order.__from_create__(
                    order_book_id,
                    amount,
                    side,
                    style,
                    POSITION_EFFECT.CLOSE
                ))
        else:
            if env.portfolio:
                position = env.portfolio.positions[order_book_id]
                buy_quantity, buy_old_quantity = position.buy_quantity, position.buy_old_quantity
            else:
                position = env.booking.get_position(order_book_id, POSITION_DIRECTION.LONG)
                buy_quantity, buy_old_quantity = position.quantity, position.old_quantity

            if amount > buy_quantity:
                user_system_log.warn(
                    _(u"Order Creation Failed: close amount {amount} is larger than position "
                      u"quantity {quantity}").format(amount=amount, quantity=buy_quantity)
                )
                return []
            buy_old_quantity = buy_old_quantity
            if amount > buy_old_quantity:
                if buy_old_quantity != 0:
                    orders.append(Order.__from_create__(
                        order_book_id,
                        buy_old_quantity,
                        side,
                        style,
                        POSITION_EFFECT.CLOSE
                    ))
                orders.append(Order.__from_create__(
                    order_book_id,
                    amount - buy_old_quantity,
                    side,
                    style,
                    POSITION_EFFECT.CLOSE_TODAY
                ))
            else:
                orders.append(Order.__from_create__(
                    order_book_id,
                    amount,
                    side,
                    style,
                    POSITION_EFFECT.CLOSE
                ))
    else:
        orders.append(Order.__from_create__(
            order_book_id,
            amount,
            side,
            style,
            position_effect
        ))

    if not is_valid_price(price):
        user_system_log.warn(
            _(u"Order Creation Failed: [{order_book_id}] No market data").format(order_book_id=order_book_id))
        return []

    if len(orders) > 1:
        user_system_log.warn(_(
            "Order was separated, original order: {original_order_repr}, new orders: [{new_orders_repr}]".format(
                original_order_repr="Order(order_book_id={}, quantity={}, side={}, position_effect={})".format(
                    order_book_id, amount, side, position_effect
                ), new_orders_repr=", ".join(["Order({}, {}, {}, {})".format(
                    o.order_book_id, o.quantity, o.side, o.position_effect
                ) for o in orders])
            )
        ))

    for o in orders:
        if o.type == ORDER_TYPE.MARKET:
            o.set_frozen_price(price)
        if env.can_submit_order(o):
            env.broker.submit_order(o)
        else:
            orders.remove(o)

    # 向前兼容,如果创建的order_list 只包含一个订单的话,直接返回对应的订单,否则返回列表
    if len(orders) == 1:
        return orders[0]
    else:
        return orders
Пример #8
0
def order_value(id_or_ins, cash_amount, price=None, style=None):
    """
    使用想要花费的金钱买入/卖出股票,而不是买入/卖出想要的股数,正数代表买入,负数代表卖出。股票的股数总是会被调整成对应的100的倍数(在A中国A股市场1手是100股)。如果资金不足,该API将不会创建发送订单。

    需要注意:
    当您提交一个买单时,cash_amount 代表的含义是您希望买入股票消耗的金额(包含税费),最终买入的股数不仅和发单的价格有关,还和税费相关的参数设置有关。
    当您提交一个卖单时,cash_amount 代表的意义是您希望卖出股票的总价值。如果金额超出了您所持有股票的价值,那么您将卖出所有股票。

    :param id_or_ins: 下单标的物
    :type id_or_ins: :class:`~Instrument` object | `str`

    :param float cash_amount: 需要花费现金购买/卖出证券的数目。正数代表买入,负数代表卖出。

    :param float price: 下单价格,默认为None,表示 :class:`~MarketOrder`, 此参数主要用于简化 `style` 参数。

    :param style: 下单类型, 默认是市价单。目前支持的订单类型有 :class:`~LimitOrder` 和 :class:`~MarketOrder`
    :type style: `OrderStyle` object

    :return: :class:`~Order` object | None

    :example:

    .. code-block:: python

        #花费最多¥10000买入平安银行股票,并以市价单发送。具体下单的数量与您策略税费相关的配置有关。
        order_value('000001.XSHE', 10000)
        #卖出价值¥10000的现在持有的平安银行:
        order_value('000001.XSHE', -10000)

    """

    style = cal_style(price, style)

    if isinstance(style, LimitOrder):
        if style.get_limit_price() <= 0:
            raise RQInvalidArgument(_(u"Limit order price should be positive"))

    order_book_id = assure_stock_order_book_id(id_or_ins)
    env = Environment.get_instance()

    price = env.get_last_price(order_book_id)
    if not is_valid_price(price):
        user_system_log.warn(
            _(u"Order Creation Failed: [{order_book_id}] No market data").format(order_book_id=order_book_id))
        return

    account = env.portfolio.accounts[DEFAULT_ACCOUNT_TYPE.STOCK.name]

    if cash_amount > 0:
        cash_amount = min(cash_amount, account.cash)

    price = price if isinstance(style, MarketOrder) else style.get_limit_price()
    amount = int(Decimal(cash_amount) / Decimal(price))

    if cash_amount > 0:
        round_lot = int(env.get_instrument(order_book_id).round_lot)

        # FIXME: logic duplicate with order_shares
        amount = int(Decimal(amount) / Decimal(round_lot)) * round_lot

        while amount > 0:
            dummy_order = Order.__from_create__(order_book_id, amount, SIDE.BUY, style, POSITION_EFFECT.OPEN)
            expected_transaction_cost = env.get_order_transaction_cost(DEFAULT_ACCOUNT_TYPE.STOCK, dummy_order)
            if amount * price + expected_transaction_cost <= cash_amount:
                break
            amount -= round_lot
        else:
            user_system_log.warn(_(u"Order Creation Failed: 0 order quantity"))
            return

    # if the cash_amount is larger than you current security’s position,
    # then it will sell all shares of this security.

    position = account.positions[order_book_id]
    amount = downsize_amount(amount, position)

    return order_shares(order_book_id, amount, style=style)
Пример #9
0
    def match(self, account, order, open_auction):
        # type: (Account, Order, bool) -> None
        if not (order.position_effect in self.SUPPORT_POSITION_EFFECTS
                and order.side in self.SUPPORT_SIDES):
            raise NotImplementedError
        order_book_id = order.order_book_id
        instrument = self._env.get_instrument(order_book_id)

        if open_auction:
            deal_price = self._open_auction_deal_price_decider(
                order_book_id, order.side)
        else:
            deal_price = self._deal_price_decider(order_book_id, order.side)

        if not is_valid_price(deal_price):
            listed_date = instrument.listed_date.date()
            if listed_date == self._env.trading_dt.date():
                reason = _(
                    u"Order Cancelled: current security [{order_book_id}] can not be traded"
                    u" in listed date [{listed_date}]").format(
                        order_book_id=order.order_book_id,
                        listed_date=listed_date,
                    )
            else:
                reason = _(
                    u"Order Cancelled: current bar [{order_book_id}] miss market data."
                ).format(order_book_id=order.order_book_id)
            order.mark_rejected(reason)
            return

        price_board = self._env.price_board
        if order.type == ORDER_TYPE.LIMIT:
            if order.side == SIDE.BUY and order.price < deal_price:
                return
            if order.side == SIDE.SELL and order.price > deal_price:
                return
            # 是否限制涨跌停不成交
            if self._price_limit:
                if order.side == SIDE.BUY and deal_price >= price_board.get_limit_up(
                        order_book_id):
                    return
                if order.side == SIDE.SELL and deal_price <= price_board.get_limit_down(
                        order_book_id):
                    return
            if self._liquidity_limit:
                if order.side == SIDE.BUY and price_board.get_a1(
                        order_book_id) == 0:
                    return
                if order.side == SIDE.SELL and price_board.get_b1(
                        order_book_id) == 0:
                    return
        else:
            if self._price_limit:
                if order.side == SIDE.BUY and deal_price >= price_board.get_limit_up(
                        order_book_id):
                    reason = _(
                        "Order Cancelled: current bar [{order_book_id}] reach the limit_up price."
                    ).format(order_book_id=order.order_book_id)
                    order.mark_rejected(reason)
                    return
                if order.side == SIDE.SELL and deal_price <= price_board.get_limit_down(
                        order_book_id):
                    reason = _(
                        "Order Cancelled: current bar [{order_book_id}] reach the limit_down price."
                    ).format(order_book_id=order.order_book_id)
                    order.mark_rejected(reason)
                    return
            if self._liquidity_limit:
                if order.side == SIDE.BUY and price_board.get_a1(
                        order_book_id) == 0:
                    reason = _(
                        "Order Cancelled: [{order_book_id}] has no liquidity."
                    ).format(order_book_id=order.order_book_id)
                    order.mark_rejected(reason)
                    return
                if order.side == SIDE.SELL and price_board.get_b1(
                        order_book_id) == 0:
                    reason = _(
                        "Order Cancelled: [{order_book_id}] has no liquidity."
                    ).format(order_book_id=order.order_book_id)
                    order.mark_rejected(reason)
                    return

        if self._inactive_limit:
            bar_volume = self._env.get_bar(order_book_id).volume
            if bar_volume == 0:
                reason = _(u"Order Cancelled: {order_book_id} bar no volume"
                           ).format(order_book_id=order.order_book_id)
                order.mark_cancelled(reason)
                return

        if self._volume_limit:
            if open_auction:
                volume = self._env.data_proxy.get_open_auction_bar(
                    order_book_id, self._env.calendar_dt).volume
            else:
                volume = self._env.get_bar(order_book_id).volume
            if volume == volume:
                volume_limit = round(
                    volume *
                    self._volume_percent) - self._turnover[order.order_book_id]

                round_lot = instrument.round_lot
                volume_limit = (volume_limit // round_lot) * round_lot
                if volume_limit <= 0:
                    if order.type == ORDER_TYPE.MARKET:
                        reason = _(
                            u"Order Cancelled: market order {order_book_id} volume {order_volume}"
                            u" due to volume limit").format(
                                order_book_id=order.order_book_id,
                                order_volume=order.quantity)
                        order.mark_cancelled(reason)
                    return

                fill = min(order.unfilled_quantity, volume_limit)
            else:
                fill = order.unfilled_quantity
        else:
            fill = order.unfilled_quantity

        ct_amount = account.calc_close_today_amount(order_book_id, fill,
                                                    order.position_direction)
        price = self._slippage_decider.get_trade_price(order, deal_price)

        trade = Trade.__from_create__(order_id=order.order_id,
                                      price=price,
                                      amount=fill,
                                      side=order.side,
                                      position_effect=order.position_effect,
                                      order_book_id=order.order_book_id,
                                      frozen_price=order.frozen_price,
                                      close_today_amount=ct_amount)
        trade._commission = self._env.get_trade_commission(trade)
        trade._tax = self._env.get_trade_tax(trade)
        order.fill(trade)
        self._turnover[order.order_book_id] += fill

        self._env.event_bus.publish_event(
            Event(EVENT.TRADE, account=account, trade=trade, order=order))

        if order.type == ORDER_TYPE.MARKET and order.unfilled_quantity != 0:
            reason = _(
                u"Order Cancelled: market order {order_book_id} volume {order_volume} is"
                u" larger than {volume_percent_limit} percent of current bar volume, fill {filled_volume} actually"
            ).format(order_book_id=order.order_book_id,
                     order_volume=order.quantity,
                     filled_volume=order.filled_quantity,
                     volume_percent_limit=self._volume_percent * 100.0)
            order.mark_cancelled(reason)
Пример #10
0
    def match(self, open_orders):
        price_board = self._env.price_board
        for account, order in open_orders:
            order_book_id = order.order_book_id
            instrument = self._env.get_instrument(order_book_id)

            if not is_valid_price(price_board.get_last_price(order_book_id)):
                listed_date = instrument.listed_date.date()
                if listed_date == self._trading_dt.date():
                    reason = _(
                        u"Order Cancelled: current security [{order_book_id}] can not be traded in listed date [{listed_date}]"
                    ).format(
                        order_book_id=order.order_book_id,
                        listed_date=listed_date,
                    )
                else:
                    reason = _(
                        u"Order Cancelled: current bar [{order_book_id}] miss market data."
                    ).format(order_book_id=order.order_book_id)
                order.mark_rejected(reason)
                continue

            deal_price = self._deal_price_decider(order_book_id, order.side)
            if order.type == ORDER_TYPE.LIMIT:
                if order.side == SIDE.BUY and order.price < deal_price:
                    continue
                if order.side == SIDE.SELL and order.price > deal_price:
                    continue
                # 是否限制涨跌停不成交
                if self._price_limit:
                    if order.side == SIDE.BUY and deal_price >= price_board.get_limit_up(
                            order_book_id):
                        continue
                    if order.side == SIDE.SELL and deal_price <= price_board.get_limit_down(
                            order_book_id):
                        continue
                if self._liquidity_limit:
                    if order.side == SIDE.BUY and price_board.get_a1(
                            order_book_id) == 0:
                        continue
                    if order.side == SIDE.SELL and price_board.get_b1(
                            order_book_id) == 0:
                        continue
            else:
                if self._price_limit:
                    if order.side == SIDE.BUY and deal_price >= price_board.get_limit_up(
                            order_book_id):
                        reason = _(
                            "Order Cancelled: current bar [{order_book_id}] reach the limit_up price."
                        ).format(order_book_id=order.order_book_id)
                        order.mark_rejected(reason)
                        continue
                    if order.side == SIDE.SELL and deal_price <= price_board.get_limit_down(
                            order_book_id):
                        reason = _(
                            "Order Cancelled: current bar [{order_book_id}] reach the limit_down price."
                        ).format(order_book_id=order.order_book_id)
                        order.mark_rejected(reason)
                        continue
                if self._liquidity_limit:
                    if order.side == SIDE.BUY and price_board.get_a1(
                            order_book_id) == 0:
                        reason = _(
                            "Order Cancelled: [{order_book_id}] has no liquidity."
                        ).format(order_book_id=order.order_book_id)
                        order.mark_rejected(reason)
                        continue
                    if order.side == SIDE.SELL and price_board.get_b1(
                            order_book_id) == 0:
                        reason = _(
                            "Order Cancelled: [{order_book_id}] has no liquidity."
                        ).format(order_book_id=order.order_book_id)
                        order.mark_rejected(reason)
                        continue

            if self._volume_limit:
                bar = self._env.bar_dict[order_book_id]
                volume_limit = round(
                    bar.volume *
                    self._volume_percent) - self._turnover[order.order_book_id]
                round_lot = instrument.round_lot
                volume_limit = (volume_limit // round_lot) * round_lot
                if volume_limit <= 0:
                    if order.type == ORDER_TYPE.MARKET:
                        reason = _(
                            u"Order Cancelled: market order {order_book_id} volume {order_volume}"
                            u" due to volume limit").format(
                                order_book_id=order.order_book_id,
                                order_volume=order.quantity)
                        order.mark_cancelled(reason)
                    continue

                unfilled = order.unfilled_quantity
                fill = min(unfilled, volume_limit)
            else:
                fill = order.unfilled_quantity

            ct_amount = account.positions.get_or_create(
                order.order_book_id).cal_close_today_amount(fill, order.side)
            price = self._slippage_decider.get_trade_price(order, deal_price)
            trade = Trade.__from_create__(
                order_id=order.order_id,
                price=price,
                amount=fill,
                side=order.side,
                position_effect=order.position_effect,
                order_book_id=order.order_book_id,
                frozen_price=order.frozen_price,
                close_today_amount=ct_amount)
            trade._commission = self._commission_decider.get_commission(
                account.type, trade)
            trade._tax = self._tax_decider.get_tax(account.type, trade)
            order.fill(trade)
            self._turnover[order.order_book_id] += fill

            self._env.event_bus.publish_event(
                Event(EVENT.TRADE, account=account, trade=trade, order=order))

            if order.type == ORDER_TYPE.MARKET and order.unfilled_quantity != 0:
                reason = _(
                    u"Order Cancelled: market order {order_book_id} volume {order_volume} is"
                    u" larger than {volume_percent_limit} percent of current bar volume, fill {filled_volume} actually"
                ).format(order_book_id=order.order_book_id,
                         order_volume=order.quantity,
                         filled_volume=order.filled_quantity,
                         volume_percent_limit=self._volume_percent * 100.0)
                order.mark_cancelled(reason)
Пример #11
0
def submit_order(id_or_ins, amount, side, price=None, position_effect=None):
    """
    通用下单函数,策略可以通过该函数自由选择参数下单。

    :param id_or_ins: 下单标的物
    :type id_or_ins: :class:`~Instrument` object | `str`

    :param float amount: 下单量,需为正数

    :param side: 多空方向,多(SIDE.BUY)或空(SIDE.SELL)
    :type side: :class:`~SIDE` enum

    :param float price: 下单价格,默认为None,表示市价单

    :param position_effect: 开平方向,开仓(POSITION_EFFECT.OPEN),平仓(POSITION.CLOSE)或平今(POSITION_EFFECT.CLOSE_TODAY),交易股票不需要该参数
    :type position_effect: :class:`~POSITION_EFFECT` enum

    :return: :class:`~Order` object | None

    :example:

    .. code-block:: python

        # 购买 2000 股的平安银行股票,并以市价单发送:
        submit_order('000001.XSHE', 2000, SIDE.BUY)
        # 平 10 份 RB1812 多方向的今仓,并以 4000 的价格发送限价单
        submit_order('RB1812', 10, SIDE.SELL, price=4000, position_effect=POSITION_EFFECT.CLOSE_TODAY)

    """
    order_book_id = assure_order_book_id(id_or_ins)
    env = Environment.get_instance()
    if (env.config.base.run_type != RUN_TYPE.BACKTEST
            and env.get_instrument(order_book_id).type == "Future"):
        if "88" in order_book_id:
            raise RQInvalidArgument(
                _(u"Main Future contracts[88] are not supported in paper trading."
                  ))
        if "99" in order_book_id:
            raise RQInvalidArgument(
                _(u"Index Future contracts[99] are not supported in paper trading."
                  ))
    style = cal_style(price, None)
    market_price = env.get_last_price(order_book_id)
    if not is_valid_price(market_price):
        user_system_log.warn(
            _(u"Order Creation Failed: [{order_book_id}] No market data").
            format(order_book_id=order_book_id))
        return

    amount = int(amount)

    order = Order.__from_create__(
        order_book_id=order_book_id,
        quantity=amount,
        side=side,
        style=style,
        position_effect=position_effect,
    )

    if order.type == ORDER_TYPE.MARKET:
        order.set_frozen_price(market_price)
    if env.can_submit_order(order):
        env.broker.submit_order(order)
        return order
Пример #12
0
def order_target_portfolio(target_portfolio):
    # type: (Dict[Union[str, Instrument], float]) -> List[Order]
    """
    买入/卖出证券以批量调整证券的仓位,以期使其持仓市值占账户总权益的比重达到指定值。

    :param target_portfolio: 下单标的物及其目标市值占比的字典

    :example:

    .. code-block:: python

        # 调整仓位,以使平安银行和万科 A 的持仓占比分别达到 10% 和 15%
        order_target_portfolio({
            '000001.XSHE': 0.1
            '000002.XSHE': 0.15
        })
    """

    if isinstance(target_portfolio, pd.Series):
        # FIXME: kind of dirty
        total_percent = sum(target_portfolio)
    else:
        total_percent = sum(six.itervalues(target_portfolio))
    if total_percent > 1 and not np.isclose(total_percent, 1):
        raise RQInvalidArgument(
            _(u"total percent should be lower than 1, current: {}").format(
                total_percent))

    env = Environment.get_instance()
    account = env.portfolio.accounts[DEFAULT_ACCOUNT_TYPE.STOCK]
    account_value = account.total_value
    target_quantities = {}
    for id_or_ins, target_percent in target_portfolio.items():
        order_book_id = assure_order_book_id(id_or_ins)
        if target_percent < 0:
            raise RQInvalidArgument(
                _(u"target percent of {} should between 0 and 1, current: {}").
                format(order_book_id, target_percent))
        price = env.data_proxy.get_last_price(order_book_id)
        if not is_valid_price(price):
            user_system_log.warn(
                _(u"Order Creation Failed: [{order_book_id}] No market data").
                format(order_book_id=order_book_id))
            continue
        target_quantities[order_book_id] = (account_value * target_percent /
                                            price, price)

    close_orders, open_orders = [], []
    current_quantities = {
        p.order_book_id: p.quantity
        for p in account.get_positions()
        if p.direction == POSITION_DIRECTION.LONG
    }
    for order_book_id, quantity in current_quantities.items():
        if order_book_id not in target_portfolio:
            close_orders.append(
                Order.__from_create__(order_book_id, quantity, SIDE.SELL,
                                      MarketOrder(), POSITION_EFFECT.CLOSE))

    round_lot = 100
    for order_book_id, (target_quantity, price) in target_quantities.items():
        if order_book_id in current_quantities:
            delta_quantity = target_quantity - current_quantities[order_book_id]
        else:
            delta_quantity = target_quantity

        if delta_quantity >= round_lot:
            delta_quantity = math.floor(delta_quantity / round_lot) * round_lot
            open_order = Order.__from_create__(order_book_id, delta_quantity,
                                               SIDE.BUY, MarketOrder(),
                                               POSITION_EFFECT.OPEN)
            open_order.set_frozen_price(price)
            open_orders.append(open_order)
        elif delta_quantity < -1:
            delta_quantity = math.floor(delta_quantity)
            close_order = Order.__from_create__(order_book_id,
                                                abs(delta_quantity), SIDE.SELL,
                                                MarketOrder(),
                                                POSITION_EFFECT.CLOSE)
            close_order.set_frozen_price(price)
            close_orders.append(close_order)

    submit_orders = []
    for order in chain(close_orders, open_orders):
        if env.can_submit_order(order):
            submit_orders.append(order)
            env.broker.submit_order(order)
    return submit_orders
Пример #13
0
    def _match(self, account, order):
        order_book_id = order.order_book_id
        price_board = self._env.price_board

        last_price = price_board.get_last_price(order_book_id)

        if not is_valid_price(last_price):
            instrument = self._env.get_instrument(order_book_id)
            listed_date = instrument.listed_date.date()
            if listed_date == self._env.trading_dt.date():
                reason = _(
                    "Order Cancelled: current security [{order_book_id}] can not be traded in listed date [{listed_date}]"
                ).format(
                    order_book_id=order_book_id,
                    listed_date=listed_date,
                )
            else:
                reason = _(
                    u"Order Cancelled: current bar [{order_book_id}] miss market data."
                ).format(order_book_id=order_book_id)
            order.mark_rejected(reason)
            self._env.event_bus.publish_event(
                Event(EVENT.ORDER_UNSOLICITED_UPDATE,
                      account=account,
                      order=copy(order)))
            return

        if order.type == ORDER_TYPE.LIMIT:
            deal_price = order.frozen_price
        else:
            deal_price = last_price

        if self._price_limit:
            """
            在 Signal 模式下,不再阻止涨跌停是否买进,price_limit 参数表示是否给出警告提示。
            """
            if order.position_effect != POSITION_EFFECT.EXERCISE:
                if order.side == SIDE.BUY and deal_price >= price_board.get_limit_up(
                        order_book_id):
                    user_system_log.warning(
                        _(u"You have traded {order_book_id} with {quantity} lots in limit up status"
                          ).format(order_book_id=order_book_id,
                                   quantity=order.quantity))

                if order.side == SIDE.SELL and deal_price <= price_board.get_limit_down(
                        order_book_id):
                    user_system_log.warning(
                        _(u"You have traded {order_book_id} with {quantity} lots in limit down status"
                          ).format(order_book_id=order_book_id,
                                   quantity=order.quantity))

        ct_amount = account.calc_close_today_amount(order_book_id,
                                                    order.quantity,
                                                    order.position_direction)
        trade_price = self._slippage_decider.get_trade_price(order, deal_price)
        trade = Trade.__from_create__(order_id=order.order_id,
                                      price=trade_price,
                                      amount=order.quantity,
                                      side=order.side,
                                      position_effect=order.position_effect,
                                      order_book_id=order_book_id,
                                      frozen_price=order.frozen_price,
                                      close_today_amount=ct_amount)
        trade._commission = self._env.get_trade_commission(trade)
        trade._tax = self._env.get_trade_tax(trade)
        order.fill(trade)

        self._env.event_bus.publish_event(
            Event(EVENT.TRADE, account=account, trade=trade,
                  order=copy(order)))
Пример #14
0
def order(id_or_ins, amount, side, position_effect, style):
    if not isinstance(style, OrderStyle):
        raise RuntimeError
    if amount < 0:
        raise RuntimeError
    if amount == 0:
        user_system_log.warn(_(u"Order Creation Failed: Order amount is 0."))
        return None
    if isinstance(style, LimitOrder) and style.get_limit_price() <= 0:
        raise RQInvalidArgument(_(u"Limit order price should be positive"))

    order_book_id = assure_future_order_book_id(id_or_ins)
    env = Environment.get_instance()
    if env.config.base.run_type != RUN_TYPE.BACKTEST:
        if "88" in order_book_id:
            raise RQInvalidArgument(
                _(u"Main Future contracts[88] are not supported in paper trading."
                  ))
        if "99" in order_book_id:
            raise RQInvalidArgument(
                _(u"Index Future contracts[99] are not supported in paper trading."
                  ))

    price = env.get_last_price(order_book_id)
    if not is_valid_price(price):
        user_system_log.warn(
            _(u"Order Creation Failed: [{order_book_id}] No market data").
            format(order_book_id=order_book_id))
        return

    amount = int(amount)

    position = Environment.get_instance().portfolio.positions[order_book_id]

    orders = []
    if position_effect == POSITION_EFFECT.CLOSE:
        if side == SIDE.BUY:
            # 如果平仓量大于持仓量,则 Warning 并 取消订单创建
            if amount > position.sell_quantity:
                user_system_log.warn(
                    _(u"Order Creation Failed: close amount {amount} is larger than position "
                      u"quantity {quantity}").format(
                          amount=amount, quantity=position.sell_quantity))
                return []
            sell_old_quantity = position.sell_old_quantity
            if amount > sell_old_quantity:
                if sell_old_quantity != 0:
                    # 如果有昨仓,则创建一个 POSITION_EFFECT.CLOSE 的平仓单
                    orders.append(
                        Order.__from_create__(order_book_id, sell_old_quantity,
                                              side, style,
                                              POSITION_EFFECT.CLOSE))
                # 剩下还有仓位,则创建一个 POSITION_EFFECT.CLOSE_TODAY 的平今单
                orders.append(
                    Order.__from_create__(order_book_id,
                                          amount - sell_old_quantity, side,
                                          style, POSITION_EFFECT.CLOSE_TODAY))
            else:
                # 创建 POSITION_EFFECT.CLOSE 的平仓单
                orders.append(
                    Order.__from_create__(order_book_id, amount, side, style,
                                          POSITION_EFFECT.CLOSE))
        else:
            if amount > position.buy_quantity:
                user_system_log.warn(
                    _(u"Order Creation Failed: close amount {amount} is larger than position "
                      u"quantity {quantity}").format(
                          amount=amount, quantity=position.buy_quantity))
                return []
            buy_old_quantity = position.buy_old_quantity
            if amount > buy_old_quantity:
                if buy_old_quantity != 0:
                    orders.append(
                        Order.__from_create__(order_book_id, buy_old_quantity,
                                              side, style,
                                              POSITION_EFFECT.CLOSE))
                orders.append(
                    Order.__from_create__(order_book_id,
                                          amount - buy_old_quantity, side,
                                          style, POSITION_EFFECT.CLOSE_TODAY))
            else:
                orders.append(
                    Order.__from_create__(order_book_id, amount, side, style,
                                          POSITION_EFFECT.CLOSE))
    else:
        orders.append(
            Order.__from_create__(order_book_id, amount, side, style,
                                  position_effect))

    if not is_valid_price(price):
        user_system_log.warn(
            _(u"Order Creation Failed: [{order_book_id}] No market data").
            format(order_book_id=order_book_id))
        return []

    for o in orders:
        if o.type == ORDER_TYPE.MARKET:
            o.set_frozen_price(price)
        if env.can_submit_order(o):
            env.broker.submit_order(o)
        else:
            orders.remove(o)

    # 向前兼容,如果创建的order_list 只包含一个订单的话,直接返回对应的订单,否则返回列表
    if len(orders) == 1:
        return orders[0]
    else:
        return orders
Пример #15
0
def submit_order(id_or_ins, amount, side, price=None, position_effect=None):
    """
    通用下单函数,策略可以通过该函数自由选择参数下单。

    :param id_or_ins: 下单标的物
    :type id_or_ins: :class:`~Instrument` object | `str`

    :param float amount: 下单量,需为正数

    :param side: 多空方向,多(SIDE.BUY)或空(SIDE.SELL)
    :type side: :class:`~SIDE` enum

    :param float price: 下单价格,默认为None,表示市价单

    :param position_effect: 开平方向,开仓(POSITION_EFFECT.OPEN),平仓(POSITION.CLOSE)或平今(POSITION_EFFECT.CLOSE_TODAY),交易股票不需要该参数
    :type position_effect: :class:`~POSITION_EFFECT` enum

    :return: :class:`~Order` object | None

    :example:

    .. code-block:: python

        # 购买 2000 股的平安银行股票,并以市价单发送:
        submit_order('000001.XSHE', 2000, SIDE.BUY)
        # 平 10 份 RB1812 多方向的今仓,并以 4000 的价格发送限价单
        submit_order('RB1812', 10, SIDE.SELL, price=4000, position_effect=POSITION_EFFECT.CLOSE_TODAY)

    """
    order_book_id = assure_order_book_id(id_or_ins)
    env = Environment.get_instance()
    if (
        env.config.base.run_type != RUN_TYPE.BACKTEST
        and env.get_instrument(order_book_id).type == "Future"
    ):
        if "88" in order_book_id:
            raise RQInvalidArgument(
                _(u"Main Future contracts[88] are not supported in paper trading.")
            )
        if "99" in order_book_id:
            raise RQInvalidArgument(
                _(u"Index Future contracts[99] are not supported in paper trading.")
            )
    style = cal_style(price, None)
    market_price = env.get_last_price(order_book_id)
    if not is_valid_price(market_price):
        user_system_log.warn(
            _(u"Order Creation Failed: [{order_book_id}] No market data").format(
                order_book_id=order_book_id
            )
        )
        return

    amount = int(amount)

    order = Order.__from_create__(
        order_book_id=order_book_id,
        quantity=amount,
        side=side,
        style=style,
        position_effect=position_effect,
    )

    if order.type == ORDER_TYPE.MARKET:
        order.set_frozen_price(market_price)
    if env.can_submit_order(order):
        env.broker.submit_order(order)
        return order
Пример #16
0
    def _match(self, account, order):
        order_book_id = order.order_book_id
        price_board = self._env.price_board

        last_price = price_board.get_last_price(order_book_id)

        if not is_valid_price(last_price):
            instrument = self._env.get_instrument(order_book_id)
            listed_date = instrument.listed_date.date()
            if listed_date == self._env.trading_dt.date():
                reason = _(
                    "Order Cancelled: current security [{order_book_id}] can not be traded in listed date [{listed_date}]").format(
                    order_book_id=order_book_id,
                    listed_date=listed_date,
                )
            else:
                reason = _(u"Order Cancelled: current bar [{order_book_id}] miss market data.").format(
                    order_book_id=order_book_id)
            order.mark_rejected(reason)
            self._env.event_bus.publish_event(Event(EVENT.ORDER_UNSOLICITED_UPDATE, account=account, order=copy(order)))
            return

        if order.type == ORDER_TYPE.LIMIT:
            deal_price = order.frozen_price
        else:
            deal_price = last_price

        if self._price_limit:
            """
            在 Signal 模式下,不再阻止涨跌停是否买进,price_limit 参数表示是否给出警告提示。
            """
            if order.side == SIDE.BUY and deal_price >= price_board.get_limit_up(order_book_id):
                user_system_log.warning(_(u"You have traded {order_book_id} with {quantity} lots in {bar_status}").format(
                    order_book_id=order_book_id,
                    quantity=order.quantity,
                    bar_status=BAR_STATUS.LIMIT_UP
                ))
                return
            if order.side == SIDE.SELL and deal_price <= price_board.get_limit_down(order_book_id):
                user_system_log.warning(_(u"You have traded {order_book_id} with {quantity} lots in {bar_status}").format(
                    order_book_id=order_book_id,
                    quantity=order.quantity,
                    bar_status=BAR_STATUS.LIMIT_DOWN
                ))
                return

        ct_amount = account.positions.get_or_create(order_book_id).cal_close_today_amount(order.quantity, order.side)
        trade_price = self._slippage_decider.get_trade_price(order.side, deal_price)
        trade = Trade.__from_create__(
            order_id=order.order_id,
            price=trade_price,
            amount=order.quantity,
            side=order.side,
            position_effect=order.position_effect,
            order_book_id=order_book_id,
            frozen_price=order.frozen_price,
            close_today_amount=ct_amount
        )
        trade._commission = self._commission_decider.get_commission(account.type, trade)
        trade._tax = self._tax_decider.get_tax(account.type, trade)
        order.fill(trade)

        self._env.event_bus.publish_event(Event(EVENT.TRADE, account=account, trade=trade, order=copy(order)))
Пример #17
0
 def prev_close(self):
     if not is_valid_price(self._prev_close):
         env = Environment.get_instance()
         self._prev_close = env.data_proxy.get_prev_close(
             self._order_book_id, env.trading_dt)
     return self._prev_close