Exemple #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)
        temp_bar = Environment.get_instance().bar_dict[order.order_book_id]
        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
Exemple #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 | None

    :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]

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

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

    # 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)
Exemple #3
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:
        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
 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 = self._total_cash / price
         position._quantity = quantity
         position._avg_price = price
         self._total_cash -= quantity * price
Exemple #5
0
    def apply_settlement(self):
        env = Environment.get_instance()
        settle_price = env.data_proxy.get_settle_price(self.order_book_id,
                                                       env.trading_dt)
        if not is_valid_price(settle_price):
            raise RuntimeError(
                _("Settlement price {settle_price} of {order_book_id} is invalid"
                  .format(settle_price=settle_price,
                          order_book_id=self.order_book_id)))
        self._buy_old_holding_list = [(settle_price, self.buy_quantity)]
        self._sell_old_holding_list = [(settle_price, self.sell_quantity)]
        self._buy_today_holding_list = []
        self._sell_today_holding_list = []

        self._buy_transaction_cost = 0.
        self._sell_transaction_cost = 0.
        self._buy_realized_pnl = 0.
        self._sell_realized_pnl = 0.
Exemple #6
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, 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(
            account_type_str2enum(account.type), trade)
        trade._tax = self._env.get_trade_tax(
            account_type_str2enum(account.type), trade)
        order.fill(trade)

        self._env.event_bus.publish_event(
            Event(EVENT.TRADE, account=account, trade=trade,
                  order=copy(order)))
Exemple #7
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 | None

    :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
        user_system_log.warn(_(u"Order Creation Failed: Order amount is 0."))
        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
        position_effect = POSITION_EFFECT.OPEN
    else:
        amount = abs(amount)
        side = SIDE.SELL
        position_effect = POSITION_EFFECT.CLOSE

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

    if side == SIDE.BUY or amount != env.portfolio.accounts[
        DEFAULT_ACCOUNT_TYPE.STOCK.name
    ].positions[order_book_id].sellable:
        # 一次性申报卖出时可以卖散股
        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, position_effect)

    if amount == 0:
        # 如果计算出来的下单量为0, 则不生成Order, 直接返回None
        # 因为很多策略会直接在handle_bar里面执行order_target_percent之类的函数,经常会出现下一个量为0的订单,如果这些订单都生成是没有意义的。
        user_system_log.warn(_(u"Order Creation Failed: 0 order quantity"))
        return
    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
Exemple #8
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
Exemple #9
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)