Ejemplo n.º 1
0
def get_dividend(order_book_id, start_date, *args, **kwargs):
    # adjusted 参数在不复权数据回测时不再提供
    env = Environment.get_instance()
    dt = env.trading_dt.date() - datetime.timedelta(days=1)
    start_date = to_date(start_date)
    if start_date > dt:
        raise RQInvalidArgument(
            _(u"in get_dividend, start_date {} is later than the previous test day {}").format(
                start_date, dt
            ))
    order_book_id = assure_order_book_id(order_book_id)
    array = env.data_proxy.get_dividend(order_book_id)
    if array is None:
        return None

    sd = start_date.year * 10000 + start_date.month * 100 + start_date.day
    ed = dt.year * 10000 + dt.month * 100 + dt.day
    return array[(array['announcement_date'] >= sd) & (array['announcement_date'] <= ed)]
Ejemplo n.º 2
0
def get_yield_curve(date=None, tenor=None):
    """
    获取某个国家市场指定日期的收益率曲线水平。

    数据为2002年至今的中债国债收益率曲线,来源于中央国债登记结算有限责任公司。

    :param date: 查询日期,默认为策略当前日期前一天
    :type date: `str` | `date` | `datetime` | `pandas.Timestamp`

    :param str tenor: 标准期限,'0S' - 隔夜,'1M' - 1个月,'1Y' - 1年,默认为全部期限

    :return: `pandas.DataFrame` - 查询时间段内无风险收益率曲线

    :example:

    ..  code-block:: python3
        :linenos:

        [In]
        get_yield_curve('20130104')

        [Out]
                        0S      1M      2M      3M      6M      9M      1Y      2Y  \
        2013-01-04  0.0196  0.0253  0.0288  0.0279  0.0280  0.0283  0.0292  0.0310

                        3Y      4Y   ...        6Y      7Y      8Y      9Y     10Y  \
        2013-01-04  0.0314  0.0318   ...    0.0342  0.0350  0.0353  0.0357  0.0361
        ...
    """
    env = Environment.get_instance()
    trading_date = env.trading_dt.date()
    yesterday = env.data_proxy.get_previous_trading_date(trading_date)

    if date is None:
        date = yesterday
    else:
        date = pd.Timestamp(date)
        if date > yesterday:
            raise RQInvalidArgument('get_yield_curve: {} >= now({})'.format(
                date, yesterday))

    return env.data_proxy.get_yield_curve(start_date=date,
                                          end_date=date,
                                          tenor=tenor)
Ejemplo n.º 3
0
def get_pit_financials(fields, quarter=None, interval=None, order_book_ids=None, if_adjusted='all'):
    if quarter is None:
        valid = True
    else:
        valid = isinstance(quarter, six.string_types) and quarter[-2] == 'q'
        if valid:
            try:
                valid = 1990 <= int(quarter[:-2]) <= 2050 and 1 <= int(quarter[-1]) <= 4
            except ValueError:
                valid = False
    if not valid:
        raise RQInvalidArgument(
            _(u"function {}: invalid {} argument, quarter should be in form of '2012q3', "
              u"got {} (type: {})").format(
                'get_pit_financials', 'quarter', quarter, type(quarter)
            ))

    env = Environment.get_instance()
    dt = env.calendar_dt.date()
    year = dt.year
    mon = dt.month
    day = dt.day
    int_date = year * 10000 + mon * 100 + day
    q = (mon - 4) // 3 + 1
    y = year
    if q <= 0:
        y -= 1
        q = 4
    default_quarter = str(y) + 'q' + str(q)
    if quarter is None or quarter > default_quarter:
        quarter = default_quarter
    result = rqdatac.get_pit_financials(fields, quarter, interval, order_book_ids, if_adjusted,
                                        max_info_date=int_date, market='cn')
    if result is None:
        return pd.DataFrame()

    if if_adjusted == 'ignore':
        result = result.reset_index().sort_values('info_date')
        result = result.groupby(['order_book_id', 'end_date'], as_index=False).fillna(method='ffill')
        result = result.drop(['info_date', 'if_adjusted'], axis=1)
        result = result.drop_duplicates(['order_book_id', 'end_date'], keep='last')
        result = result.set_index(['order_book_id', 'end_date']).sort_index()
    return result
Ejemplo n.º 4
0
def index_weights(order_book_id, date=None):
    # type: (str, Optional[Union[str, datetime.date]]) -> pd.Series
    """
    获取T-1日的指数权重

    :param order_book_id: 指数
    :param date: 可选,默认为T-1日
    :return: 每只股票在指数中的构成权重

    :example:

    获取上证50指数上个交易日的指数构成

    .. code-block:: python3
        :linenos:

        index_weights('000016.XSHG')
        # [Out]
        # Order_book_id
        # 600000.XSHG    0.03750
        # 600010.XSHG    0.00761
        # 600016.XSHG    0.05981
        # 600028.XSHG    0.01391
        # 600029.XSHG    0.00822
        # 600030.XSHG    0.03526
        # 600036.XSHG    0.04889
        # 600050.XSHG    0.00998
        # 600104.XSHG    0.02122

    """
    env = Environment.get_instance()
    data_proxy = env.data_proxy
    dt = to_date(data_proxy.get_previous_trading_date(env.trading_dt.date()))
    if date is None:
        date = dt
    else:
        date = to_date(date)
        if date > dt:
            raise RQInvalidArgument(
                _('in index_weights, date {} is no earlier than previous test date {}'
                  ).format(date, dt))
    order_book_id = assure_order_book_id(order_book_id)
    return rqdatac.index_weights(order_book_id, date)
Ejemplo n.º 5
0
def add_indicator(order_book_id, frequency, field_name, field_list):
    """
        Description : 给数据添加指标的。
        Arg :
            @order_book_id : 股票id
            @frequency : 频率
            @field_list : 列名
            @field_list : 列数据
        Returns :
        Raises	 :
    """
    order_book_id = assure_order_book_id(order_book_id)
    env = Environment.get_instance()

    if frequency[-1] == 'm' and env.config.base.frequency == '1d':
        raise RQInvalidArgument('can not get minute history in day back test')

    return env.data_proxy.add_indicator(order_book_id, frequency, field_name,
                                        field_list)
Ejemplo n.º 6
0
def get_split(order_book_ids, start_date=None):
    # type: (Union[str, List[str]], Optional[Union[str, datetime.date]]) -> pd.DataFrame
    """
    获取某只股票到策略当前日期前一天的拆分情况(包含起止日期)。

    :param order_book_ids: 证券代码,证券的独特的标识符,例如:'000001.XSHE'
    :param start_date: 开始日期,用户必须指定,需要早于策略当前日期

    :return: 查询时间段内的某个股票的拆分数据

        *   ex_dividend_date: 除权除息日,该天股票的价格会因为拆分而进行调整
        *   book_closure_date: 股权登记日
        *   split_coefficient_from: 拆分因子(拆分前)
        *   split_coefficient_to: 拆分因子(拆分后)

        例如:每10股转增2股,则split_coefficient_from = 10, split_coefficient_to = 12.

    :example:

    ..  code-block:: python3
        :linenos:

        get_split('000001.XSHE', start_date='2010-01-04')
        #[Out]
        #                 book_closure_date payable_date  split_coefficient_from  \
        #ex_dividend_date
        #2013-06-20              2013-06-19   2013-06-20                      10
        #                  split_coefficient_to
        #ex_dividend_date
        #2013-06-20                        16.0
    """
    # order_book_id 支持list类型
    env = Environment.get_instance()
    dt = env.trading_dt.date() - datetime.timedelta(days=1)
    start_date = to_date(start_date)
    if start_date > dt:
        raise RQInvalidArgument(
            _('in get_split, start_date {} is no earlier than the previous test day {}'
              ).format(start_date, dt))
    if isinstance(order_book_ids, six.string_types):
        order_book_ids = [order_book_ids]
    order_book_ids = [assure_order_book_id(i) for i in order_book_ids]
    return rqdatac.get_split(order_book_ids, start_date, dt)
Ejemplo n.º 7
0
def get_dividend(order_book_id, start_date):
    """
    获取某只股票到策略当前日期前一天的分红情况(包含起止日期)。

    :param order_book_id: 股票代码
    :type order_book_id: str
    :param start_date: 开始日期,需要早于策略当前日期
    :type start_date: `str` | `date` | `datetime` | `pandas.Timestamp`

    :return: ndarray

    =========================   ===================================================
    fields                      字段名
    =========================   ===================================================
    announcement_date           分红宣布日
    book_closure_date           股权登记日
    dividend_cash_before_tax    税前分红
    ex_dividend_date            除权除息日
    payable_date                分红到帐日
    round_lot                   分红最小单位
    =========================   ===================================================
    """
    # adjusted 参数在不复权数据回测时不再提供
    env = Environment.get_instance()
    dt = env.trading_dt.date() - datetime.timedelta(days=1)
    start_date = to_date(start_date)
    if start_date > dt:
        raise RQInvalidArgument(
            _(
                u"in get_dividend, start_date {} is later than the previous test day {}"
            ).format(start_date, dt)
        )
    order_book_id = assure_order_book_id(order_book_id)
    array = env.data_proxy.get_dividend(order_book_id)
    if array is None:
        return None

    sd = start_date.year * 10000 + start_date.month * 100 + start_date.day
    ed = dt.year * 10000 + dt.month * 100 + dt.day
    return array[
        (array["announcement_date"] >= sd) & (array["announcement_date"] <= ed)
    ]
Ejemplo n.º 8
0
def _submit_order(ins, amount, side, position_effect, style, quantity,
                  auto_switch_order_value):
    env = Environment.get_instance()
    if isinstance(style, LimitOrder):
        if style.get_limit_price() <= 0:
            raise RQInvalidArgument(_(u"Limit order price should be positive"))
    price = env.data_proxy.get_last_price(ins.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=ins.order_book_id))
        return
    round_lot = int(ins.round_lot)

    if side in [SIDE.BUY, side.SELL]:
        if not (side == SIDE.SELL and quantity == abs(amount)):
            if _is_ksh(ins):
                # KSH can buy(sell) 201, 202 shares
                amount = _get_ksh_amount(amount)
            else:
                amount = int(Decimal(amount) / Decimal(round_lot)) * round_lot

    if amount == 0:
        user_system_log.warn(
            _(u"Order Creation Failed: 0 order quantity, order_book_id={order_book_id}"
              ).format(order_book_id=ins.order_book_id))
        return
    order = Order.__from_create__(ins.order_book_id, abs(amount), side, style,
                                  position_effect)
    if order.type == ORDER_TYPE.MARKET:
        order.set_frozen_price(price)
    if side == SIDE.BUY and auto_switch_order_value:
        account, position, ins = _get_account_position_ins(ins)
        if not is_cash_enough(env, order, account):
            user_system_log.warn(
                _("insufficient cash, use all remaining cash({}) to create order"
                  ).format(account.cash))
            return _order_value(account, position, ins, account.cash, style)
    if env.can_submit_order(order):
        env.broker.submit_order(order)
        return order
Ejemplo n.º 9
0
def unsubscribe(id_or_symbols):
    """
    取消订阅合约行情。取消订阅会导致合约池内合约的减少,如果当前合约池中没有任何合约,则策略直接退出。

    :param id_or_ins: 标的物
    :type id_or_ins: :class:`~Instrument` object | `str` | List[:class:`~Instrument`] | List[`str`]
    """
    current_universe = Environment.get_instance().get_universe()
    if isinstance(id_or_symbols, six.string_types):
        order_book_id = instruments(id_or_symbols).order_book_id
        current_universe.discard(order_book_id)
    elif isinstance(id_or_symbols, Instrument):
        current_universe.discard(id_or_symbols.order_book_id)
    elif isinstance(id_or_symbols, Iterable):
        for item in id_or_symbols:
            i = assure_order_book_id(item)
            current_universe.discard(i)
    else:
        raise RQInvalidArgument(_(u"unsupported order_book_id type"))

    Environment.get_instance().update_universe(current_universe)
Ejemplo n.º 10
0
def subscribe(id_or_symbols):
    """
    订阅合约行情。该操作会导致合约池内合约的增加,从而影响handle_bar中处理bar数据的数量。

    需要注意,用户在初次编写策略时候需要首先订阅合约行情,否则handle_bar不会被触发。

    :param id_or_ins: 标的物
    :type id_or_ins: :class:`~Instrument` object | `str` | List[:class:`~Instrument`] | List[`str`]
    """
    current_universe = Environment.get_instance().get_universe()
    if isinstance(id_or_symbols, six.string_types):
        order_book_id = instruments(id_or_symbols).order_book_id
        current_universe.add(order_book_id)
    elif isinstance(id_or_symbols, Instrument):
        current_universe.add(id_or_symbols.order_book_id)
    elif isinstance(id_or_symbols, Iterable):
        for item in id_or_symbols:
            current_universe.add(assure_order_book_id(item))
    else:
        raise RQInvalidArgument(_(u"unsupported order_book_id type"))
    verify_that('id_or_symbols')._are_valid_instruments("subscribe", id_or_symbols)
    Environment.get_instance().update_universe(current_universe)
Ejemplo n.º 11
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)
    return _order_value(order_book_id, cash_amount, style)
Ejemplo n.º 12
0
def unsubscribe(id_or_symbols):
    # type: (Union[str, Instrument, Iterable[str], Iterable[Instrument]]) -> None
    """
    取消订阅合约行情。取消订阅会导致合约池内合约的减少,如果当前合约池中没有任何合约,则策略直接退出。

    :param id_or_symbols: 标的物

    """
    current_universe = Environment.get_instance().get_universe()
    if isinstance(id_or_symbols, six.string_types):
        order_book_id = instruments(id_or_symbols).order_book_id
        current_universe.discard(order_book_id)
    elif isinstance(id_or_symbols, Instrument):
        current_universe.discard(id_or_symbols.order_book_id)
    elif isinstance(id_or_symbols, Iterable):
        for item in id_or_symbols:
            i = assure_order_book_id(item)
            current_universe.discard(i)
    else:
        raise RQInvalidArgument(_(u"unsupported order_book_id type"))

    Environment.get_instance().update_universe(current_universe)
Ejemplo n.º 13
0
    def history_bars(self,
                     instrument,
                     bar_count,
                     frequency,
                     fields,
                     dt,
                     skip_suspended=True,
                     include_now=False,
                     adjust_type='pre',
                     adjust_orig=None):
        if frequency != '1d':
            raise NotImplementedError

        if skip_suspended and instrument.type == 'CS':
            bars = self._filtered_day_bars(instrument)
        else:
            bars = self._all_day_bars_of(instrument)

        if not self._are_fields_valid(fields, bars.dtype.names):
            raise RQInvalidArgument("invalid fileds: {}".format(fields))

        if len(bars) <= 0:
            return bars

        dt = np.uint64(convert_date_to_int(dt))
        i = bars['datetime'].searchsorted(dt, side='right')
        left = i - bar_count if i >= bar_count else 0
        bars = bars[left:i]
        if adjust_type == 'none' or instrument.type in {'Future', 'INDX'}:
            # 期货及指数无需复权
            return bars if fields is None else bars[fields]

        if isinstance(fields, str) and fields not in FIELDS_REQUIRE_ADJUSTMENT:
            return bars if fields is None else bars[fields]

        return adjust_bars(bars,
                           self.get_ex_cum_factor(instrument.order_book_id),
                           fields, adjust_type, adjust_orig)
Ejemplo n.º 14
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)
    auto_switch_order_value = Environment.get_instance().config.mod.sys_accounts.auto_switch_order_value
    return _order_shares(order_book_id, amount, style, auto_switch_order_value)
Ejemplo n.º 15
0
def subscribe(id_or_symbols):
    # type: (Union[str, Instrument, Iterable[str], Iterable[Instrument]]) -> None

    """
    订阅合约行情。该操作会导致合约池内合约的增加,从而影响handle_bar中处理bar数据的数量。

    需要注意,用户在初次编写策略时候需要首先订阅合约行情,否则handle_bar不会被触发。

    :param id_or_symbols: 标的物

    """
    current_universe = Environment.get_instance().get_universe()
    if isinstance(id_or_symbols, six.string_types):
        order_book_id = instruments(id_or_symbols).order_book_id
        current_universe.add(order_book_id)
    elif isinstance(id_or_symbols, Instrument):
        current_universe.add(id_or_symbols.order_book_id)
    elif isinstance(id_or_symbols, Iterable):
        for item in id_or_symbols:
            current_universe.add(assure_order_book_id(item))
    else:
        raise RQInvalidArgument(_(u"unsupported order_book_id type"))
    verify_that("id_or_symbols")._are_valid_instruments("subscribe", id_or_symbols)
    Environment.get_instance().update_universe(current_universe)
Ejemplo n.º 16
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
        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
Ejemplo n.º 17
0
def history_bars(
    order_book_id,
    bar_count,
    frequency,
    fields=None,
    skip_suspended=True,
    include_now=False,
    adjust_type="pre",
):
    # type:(str, int, str, Optional[Union[str, List[str]]], Optional[bool], Optional[bool], Optional[str]) -> np.ndarray
    """
    获取指定合约的历史 k 线行情,同时支持日以及分钟历史数据。不能在init中调用。

    日回测获取分钟历史数据:不支持

    日回测获取日历史数据

    =========================   ===================================================
    调用时间                      返回数据
    =========================   ===================================================
    T日before_trading            T-1日day bar
    T日handle_bar                T日day bar
    =========================   ===================================================

    分钟回测获取日历史数据

    =========================   ===================================================
    调用时间                      返回数据
    =========================   ===================================================
    T日before_trading            T-1日day bar
    T日handle_bar                T-1日day bar
    =========================   ===================================================

    分钟回测获取分钟历史数据

    =========================   ===================================================
    调用时间                      返回数据
    =========================   ===================================================
    T日before_trading            T-1日最后一个minute bar
    T日handle_bar                T日当前minute bar
    =========================   ===================================================

    :param order_book_id: 合约代码
    :param bar_count: 获取的历史数据数量,必填项
    :param frequency: 获取数据什么样的频率进行。'1d'或'1m'分别表示每日和每分钟,必填项
    :param fields: 返回数据字段。必填项。见下方列表。
    :param skip_suspended: 是否跳过停牌数据
    :param include_now: 是否包含当前数据
    :param adjust_type: 复权类型,默认为前复权 pre;可选 pre, none, post

    =========================   ===================================================
    fields                      字段名
    =========================   ===================================================
    datetime                    时间戳
    open                        开盘价
    high                        最高价
    low                         最低价
    close                       收盘价
    volume                      成交量
    total_turnover              成交额
    open_interest               持仓量(期货专用)
    basis_spread                期现差(股指期货专用)
    settlement                  结算价(期货日线专用)
    prev_settlement             结算价(期货日线专用)
    =========================   ===================================================

    :example:

    获取最近5天的日线收盘价序列(策略当前日期为20160706):

    ..  code-block:: python3
        :linenos:

        [In]
        logger.info(history_bars('000002.XSHE', 5, '1d', 'close'))
        [Out]
        [ 8.69  8.7   8.71  8.81  8.81]
    """
    order_book_id = assure_order_book_id(order_book_id)
    env = Environment.get_instance()
    dt = env.calendar_dt

    if frequency[-1] not in {"m", "d"}:
        raise RQInvalidArgument("invalid frequency {}".format(frequency))

    if frequency[-1] == "m" and env.config.base.frequency == "1d":
        raise RQInvalidArgument("can not get minute history in day back test")

    if frequency[-1] == "d" and frequency != "1d":
        raise RQInvalidArgument("invalid frequency")

    if adjust_type not in {"pre", "post", "none"}:
        raise RuntimeError("invalid adjust_type")

    if frequency == "1d":
        sys_frequency = Environment.get_instance().config.base.frequency
        if (
            sys_frequency in ["1m", "tick"]
            and not include_now
            and ExecutionContext.phase() != EXECUTION_PHASE.AFTER_TRADING
        ) or (ExecutionContext.phase() in (EXECUTION_PHASE.BEFORE_TRADING, EXECUTION_PHASE.OPEN_AUCTION)):
            dt = env.data_proxy.get_previous_trading_date(env.trading_dt.date())
            # 当 EXECUTION_PHASE.BEFORE_TRADING 的时候,强制 include_now 为 False
            include_now = False
        if sys_frequency == "1d":
            # 日回测不支持 include_now
            include_now = False

    if fields is None:
        fields = ["datetime", "open", "high", "low", "close", "volume"]

    return env.data_proxy.history_bars(
        order_book_id,
        bar_count,
        frequency,
        fields,
        dt,
        skip_suspended=skip_suspended,
        include_now=include_now,
        adjust_type=adjust_type,
        adjust_orig=env.trading_dt,
    )
Ejemplo n.º 18
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, LimitOrder(price), 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)
Ejemplo n.º 19
0
def history_bars(order_book_id,
                 bar_count,
                 frequency,
                 fields=None,
                 skip_suspended=True,
                 include_now=False,
                 adjust_type='pre'):
    """
    获取指定合约的历史行情,同时支持日以及分钟历史数据。不能在init中调用。 注意,该API会自动跳过停牌数据。

    日回测获取分钟历史数据:不支持

    日回测获取日历史数据

    =========================   ===================================================
    调用时间                      返回数据
    =========================   ===================================================
    T日before_trading            T-1日day bar
    T日handle_bar                T日day bar
    =========================   ===================================================

    分钟回测获取日历史数据

    =========================   ===================================================
    调用时间                      返回数据
    =========================   ===================================================
    T日before_trading            T-1日day bar
    T日handle_bar                T-1日day bar
    =========================   ===================================================

    分钟回测获取分钟历史数据

    =========================   ===================================================
    调用时间                      返回数据
    =========================   ===================================================
    T日before_trading            T-1日最后一个minute bar
    T日handle_bar                T日当前minute bar
    =========================   ===================================================

    :param order_book_id: 合约代码
    :type order_book_id: `str`

    :param int bar_count: 获取的历史数据数量,必填项
    :param str frequency: 获取数据什么样的频率进行。'1d'或'1m'分别表示每日和每分钟,必填项
    :param str fields: 返回数据字段。必填项。见下方列表。

    =========================   ===================================================
    fields                      字段名
    =========================   ===================================================
    datetime                    时间戳
    open                        开盘价
    high                        最高价
    low                         最低价
    close                       收盘价
    volume                      成交量
    total_turnover              成交额
    open_interest               持仓量(期货专用)
    basis_spread                期现差(股指期货专用)
    settlement                  结算价(期货日线专用)
    prev_settlement             结算价(期货日线专用)
    =========================   ===================================================

    :param bool skip_suspended: 是否跳过停牌数据
    :param bool include_now: 是否包含当前数据
    :param str adjust_type: 复权类型,默认为前复权 pre;可选 pre, none, post

    :return: `ndarray`, 方便直接与talib等计算库对接,效率较history返回的DataFrame更高。

    :example:

    获取最近5天的日线收盘价序列(策略当前日期为20160706):

    ..  code-block:: python3
        :linenos:

        [In]
        logger.info(history_bars('000002.XSHE', 5, '1d', 'close'))
        [Out]
        [ 8.69  8.7   8.71  8.81  8.81]
    """
    order_book_id = assure_order_book_id(order_book_id)
    env = Environment.get_instance()
    dt = env.calendar_dt

    if frequency[-1] == 'm' and env.config.base.frequency == '1d':
        raise RQInvalidArgument('can not get minute history in day back test')

    if adjust_type not in {'pre', 'post', 'none'}:
        raise RuntimeError('invalid adjust_type')

    if frequency == '1d':
        sys_frequency = Environment.get_instance().config.base.frequency
        if ((sys_frequency in ['1m', 'tick'] and not include_now)
                or ExecutionContext.phase() == EXECUTION_PHASE.BEFORE_TRADING):
            dt = env.data_proxy.get_previous_trading_date(
                env.trading_dt.date())
            # 当 EXECUTION_PHASE.BEFORE_TRADING 的时候,强制 include_now 为 False
            include_now = False
        if sys_frequency == "1d":
            # 日回测不支持 include_now
            include_now = False

    if fields is None:
        fields = ["datetime", "open", "high", "low", "close", "volume"]

    return env.data_proxy.history_bars(order_book_id,
                                       bar_count,
                                       frequency,
                                       fields,
                                       dt,
                                       skip_suspended=skip_suspended,
                                       include_now=include_now,
                                       adjust_type=adjust_type,
                                       adjust_orig=env.trading_dt)
Ejemplo n.º 20
0
 def raise_not_valid_future_error(self, func_name, arg_name, value):
     raise RQInvalidArgument(
         _(u"function {}: invalid {} argument, expect a valid future instrument/order_book_id/symbol, "
           u"got {} (type: {})").format(
             func_name, self._arg_name, value, type(value)
         ))
Ejemplo n.º 21
0
def get_price(
    order_book_ids,  # type: Union[str, Iterable[str]]
    start_date,  # type: Union[datetime.date, str]
    end_date=None,  # type: Optional[Union[datetime.date, datetime.datetime, str]]
    frequency='1d',  # type: Optional[str]
    fields=None,  # type: Optional[Iterable[str]]
    adjust_type='pre',  # type: Optional[str]
    skip_suspended=False,  # type: Optional[bool]
    expect_df=False  # type: Optional[bool]
):  # type: (...) -> Union[pd.DataFrame, pd.Panel, pd.Series]
    """
    获取指定合约或合约列表的历史行情(包含起止日期,日线或分钟线),不能在'handle_bar'函数中进行调用。

    注意,这一函数主要是为满足在研究平台编写策略习惯而引入。在编写策略中,使用history_bars进行数据获取会更方便。

    :param order_book_ids: 合约代码,合约代码,可传入order_book_id, order_book_id list, symbol, symbol list
    :param start_date: 开始日期,用户必须指定
    :param end_date: 结束日期,默认为策略当前日期前一天
    :param frequency: 历史数据的频率。 现在支持日/分钟级别的历史数据,默认为'1d'。使用者可自由选取不同频率,例如'5m'代表5分钟线
    :param fields: 期望返回的字段名称,如 open,close 等
    :param adjust_type: 权息修复方案。前复权 - pre,后复权 - post,不复权 - none
    :param skip_suspended: 是否跳过停牌数据。默认为False,不跳过,用停牌前数据进行补齐。True则为跳过停牌期。注意,当设置为True时,函数order_book_id只支持单个合约传入
    :param expect_df: 是否期望始终返回 DataFrame。pandas 0.25.0 以上该参数应设为 True,以避免因试图构建 Panel 产生异常

    当 expect_df 为 False 时,返回值的类型如下

        *   传入一个order_book_id,多个fields,函数会返回一个pandas DataFrame
        *   传入一个order_book_id,一个field,函数会返回pandas Series
        *   传入多个order_book_id,一个field,函数会返回一个pandas DataFrame
        *   传入多个order_book_id,函数会返回一个pandas Panel


        =========================   =========================   ==============================================================================
        参数                         类型                        说明
        =========================   =========================   ==============================================================================
        open                        float                       开盘价
        close                       float                       收盘价
        high                        float                       最高价
        low                         float                       最低价
        limit_up                    float                       涨停价
        limit_down                  float                       跌停价
        total_turnover              float                       总成交额
        volume                      float                       总成交量
        acc_net_value               float                       累计净值(仅限基金日线数据)
        unit_net_value              float                       单位净值(仅限基金日线数据)
        discount_rate               float                       折价率(仅限基金日线数据)
        settlement                  float                       结算价 (仅限期货日线数据)
        prev_settlement             float                       昨日结算价(仅限期货日线数据)
        open_interest               float                       累计持仓量(期货专用)
        basis_spread                float                       基差点数(股指期货专用,股指期货收盘价-标的指数收盘价)
        trading_date                pandas.TimeStamp             交易日期(仅限期货分钟线数据),对应期货夜盘的情况
        =========================   =========================   ==============================================================================

    :example:

    获取单一股票历史日线行情:

    ..  code-block:: python3
        :linenos:

        get_price('000001.XSHE', start_date='2015-04-01', end_date='2015-04-12')
        #[Out]
        #open    close    high    low    total_turnover    volume    limit_up    limit_down
        #2015-04-01    10.7300    10.8249    10.9470    10.5469    2.608977e+09    236637563.0    11.7542    9.6177
        #2015-04-02    10.9131    10.7164    10.9470    10.5943    2.222671e+09    202440588.0    11.9102    9.7397
        #2015-04-03    10.6486    10.7503    10.8114    10.5876    2.262844e+09    206631550.0    11.7881    9.6448
        #2015-04-07    10.9538    11.4015    11.5032    10.9538    4.898119e+09    426308008.0    11.8288    9.6787
        #2015-04-08    11.4829    12.1543    12.2628    11.2929    5.784459e+09    485517069.0    12.5409    10.2620
        #2015-04-09    12.1747    12.2086    12.9208    12.0255    5.794632e+09    456921108.0    13.3684    10.9403
        #2015-04-10    12.2086    13.4294    13.4294    12.1069    6.339649e+09    480990210.0    13.4294    10.9877
        #...
    """
    env = Environment.get_instance()
    yesterday = env.trading_dt.date() - datetime.timedelta(days=1)
    if end_date is not None:
        end_date = to_date(end_date)
        if end_date > yesterday:
            raise RQInvalidArgument(
                _('in get_price, end_date {} is no earlier than the previous test day {}'
                  ).format(end_date, yesterday))
    else:
        end_date = yesterday

    start_date = to_date(start_date)
    if start_date > yesterday:
        raise RQInvalidArgument(
            _('in get_price, start_date {} is no earlier than the previous test day {}'
              ).format(start_date, yesterday))

    if end_date < start_date:
        raise RQInvalidArgument(
            _('in get_price, start_date {} > end_date {}').format(
                start_date, end_date))

    if isinstance(order_book_ids, six.string_types):
        order_book_ids = assure_order_book_id(order_book_ids)
    else:
        order_book_ids = [assure_order_book_id(i) for i in order_book_ids]

    return rqdatac.get_price(order_book_ids,
                             start_date,
                             end_date,
                             frequency=frequency,
                             fields=fields,
                             adjust_type=adjust_type,
                             skip_suspended=skip_suspended,
                             expect_df=expect_df)
Ejemplo n.º 22
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 np.isnan(price):
        user_system_log.warn(
            _(u"Order Creation Failed: [{order_book_id}] No market data").
            format(order_book_id=order_book_id))
        return

    if price == 0:
        return order_shares(order_book_id, 0, style)

    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)
Ejemplo n.º 23
0
 def check_greater_than(func_name, value):
     if value <= low:
         raise RQInvalidArgument(
             _(u"function {}: invalid {} argument, expect a value > {}, got {} (type: {})").format(
                 func_name, self._arg_name, low, value, type(value)
             ))
Ejemplo n.º 24
0
    def history_bars(self,
                     instrument,
                     bar_count,
                     frequency,
                     fields,
                     dt,
                     skip_suspended=True,
                     include_now=False,
                     adjust_type='pre',
                     adjust_orig=None):
        if frequency not in ['1m', '5m', '15m', '1d']:
            raise NotImplementedError

        if skip_suspended and instrument.type == 'CS':
            bars = self._filtered_day_bars(instrument)
        elif frequency == '1d':
            bars = self._all_day_bars_of(instrument)
        elif frequency in ['1m', '5m', '15m']:
            dt_end = np.uint64(convert_date_to_int(dt + timedelta(days=1)))
            dt_start = np.uint(
                convert_date_to_int(dt - timedelta(days=int(bar_count / 180) +
                                                   30)))  # @TODO
            bars = self._all_min_bars_of(instrument, dt_start, dt_end)

        if not self._are_fields_valid(fields, bars.dtype.names):
            raise RQInvalidArgument("invalid fileds: {}".format(fields))

        if len(bars) <= 0:
            return bars

        if frequency == '1d':
            dt = np.uint64(convert_date_to_int(dt))
        elif frequency in ['1m', '5m', '15m']:
            dt = np.uint64(convert_date_min_to_int(dt))

        count = int(frequency[:-1])
        i = bars['datetime'].searchsorted(dt, side='right')
        if i <= 0:
            return None
        left = i - bar_count * count if i >= bar_count * count else 0
        if count > 1:
            merge_bars = []
            for j in range(0, bar_count):
                if left + (j + 1) * count > i:
                    break
                merge_bar = self._history_merge_min_bar(
                    bars[left + j * count:left + (j + 1) * count])
                merge_bars.append(merge_bar)
            bars = np.array(merge_bars)
        else:
            bars = bars[left:i]
        if adjust_type == 'none' or instrument.type in {'Future', 'INDX'}:
            # 期货及指数无需复权
            return bars if fields is None else bars[fields]

        if isinstance(fields, str) and fields not in FIELDS_REQUIRE_ADJUSTMENT:
            return bars if fields is None else bars[fields]

        return adjust_bars(bars,
                           self.get_ex_cum_factor(instrument.order_book_id),
                           fields, adjust_type, adjust_orig)
Ejemplo n.º 25
0
def get_financials(query, quarter=None, interval='4q', expect_df=False):
    if quarter is None:
        valid = True
    else:
        valid = isinstance(quarter, six.string_types) and quarter[-2] == 'q'
        if valid:
            try:
                valid = 1990 <= int(quarter[:-2]) <= 2050 and 1 <= int(
                    quarter[-1]) <= 4
            except ValueError:
                valid = False
    if not valid:
        raise RQInvalidArgument(
            _(u"function {}: invalid {} argument, quarter should be in form of '2012q3', "
              u"got {} (type: {})").format('get_financials', 'quarter',
                                           quarter, type(quarter)))
    env = Environment.get_instance()
    dt = env.calendar_dt.date() - datetime.timedelta(
        days=1)  # Take yesterday's data as default
    year = dt.year
    mon = dt.month
    day = dt.day
    int_date = year * 10000 + mon * 100 + day
    q = (mon - 4) // 3 + 1
    y = year
    if q <= 0:
        y -= 1
        q = 4
    default_quarter = str(y) + 'q' + str(q)
    if quarter is None or quarter > default_quarter:
        quarter = default_quarter

    include_date = False
    for d in query.column_descriptions:
        if d['name'] == 'announce_date':
            include_date = True
    if not include_date:
        query = query.add_column(rqdatac.fundamentals.announce_date)

    result = rqdatac.get_financials(query,
                                    quarter,
                                    interval,
                                    expect_df=expect_df)
    if result is None:
        return pd.DataFrame()
    if isinstance(result, pd.Series):
        return result
    elif isinstance(result, pd.DataFrame):
        result = result[(result['announce_date'] <= int_date)
                        | pd.isnull(result['announce_date'])]
        if not include_date:
            del result['announce_date']
    else:
        d = dict()
        for order_book_id in result.minor_axis:
            df = result.minor_xs(order_book_id)
            df = df[(df.announce_date < int_date) |
                    (pd.isnull(df.announce_date))]
            d[order_book_id] = df
        pl = pd.Panel.from_dict(d, orient='minor')
        if not include_date:
            pl.drop('announce_date', axis=0, inplace=True)
            if len(pl.items) == 1:
                pl = pl[pl.items[0]]
        return pl

    return result
Ejemplo n.º 26
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:
        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 six.iteritems(target_portfolio):
        order_book_id = assure_stock_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

    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 six.iteritems(current_quantities):
        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 in six.iteritems(target_quantities):
        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_orders.append(Order.__from_create__(
                order_book_id, delta_quantity, SIDE.BUY, MarketOrder(), POSITION_EFFECT.OPEN
            ))
        elif delta_quantity < -1:
            delta_quantity = math.floor(delta_quantity)
            close_orders.append(Order.__from_create__(
                order_book_id, abs(delta_quantity), SIDE.SELL, MarketOrder(), POSITION_EFFECT.CLOSE
            ))

    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
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 np.isnan(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.sell_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 np.isnan(price) or price == 0:
        user_system_log.warn(
            _(u"Order Creation Failed: [{order_book_id}] No market data").
            format(order_book_id=order_book_id))
        for o in orders:
            o.mark_rejected(
                _(u"Order Creation Failed: [{order_book_id}] No market data").
                format(order_book_id=order_book_id))
        return 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)

    # 向前兼容,如果创建的order_list 只包含一个订单的话,直接返回对应的订单,否则返回列表
    if len(orders) == 1:
        return orders[0]
    else:
        return orders
Ejemplo n.º 28
0
 def check_less_than(func_name, value):
     if value >= high:
         raise RQInvalidArgument(
             _(u"function {}: invalid {} argument, expect a value < {}, got {} (type: {})").format(
                 func_name, self._arg_name, high, value, type(value)
             ))
Ejemplo n.º 29
0
def _submit_order(id_or_ins, amount, side, position_effect, style):
    amount = int(amount)
    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"))

    instrument = assure_instrument(id_or_ins)
    order_book_id = instrument.order_book_id
    env = Environment.get_instance()
    if env.config.base.run_type != RUN_TYPE.BACKTEST and instrument.type == INSTRUMENT_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."
                  ))

    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

    env = Environment.get_instance()

    orders = []
    if position_effect in (POSITION_EFFECT.CLOSE_TODAY, POSITION_EFFECT.CLOSE):
        direction = POSITION_DIRECTION.LONG if side == SIDE.SELL else POSITION_DIRECTION.SHORT
        position = env.portfolio.get_position(order_book_id,
                                              direction)  # type: Position
        if position_effect == POSITION_EFFECT.CLOSE_TODAY:
            if amount > position.today_closable:
                user_system_log.warning(
                    _("Order Creation Failed: "
                      "close today amount {amount} is larger than today closable quantity {quantity}"
                      ).format(amount=amount,
                               quantity=position.today_closable))
                return []
            orders.append(
                Order.__from_create__(order_book_id, amount, side, style,
                                      POSITION_EFFECT.CLOSE_TODAY))
        else:
            quantity, old_quantity = position.quantity, position.old_quantity
            if amount > quantity:
                user_system_log.warn(
                    _(u"Order Creation Failed: close amount {amount} is larger than position quantity {quantity}"
                      ).format(amount=amount, quantity=quantity))
                return []
            if amount > old_quantity:
                if old_quantity != 0:
                    # 如果有昨仓,则创建一个 POSITION_EFFECT.CLOSE 的平仓单
                    orders.append(
                        Order.__from_create__(order_book_id, old_quantity,
                                              side, style,
                                              POSITION_EFFECT.CLOSE))
                # 剩下还有仓位,则创建一个 POSITION_EFFECT.CLOSE_TODAY 的平今单
                orders.append(
                    Order.__from_create__(order_book_id, amount - 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))
    elif position_effect == POSITION_EFFECT.OPEN:
        orders.append(
            Order.__from_create__(order_book_id, amount, side, style,
                                  position_effect))
    else:
        raise NotImplementedError()

    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
Ejemplo n.º 30
0
 def check_is_instance_of(func_name, value):
     if not isinstance(value, types):
         raise RQInvalidArgument(
             _(u"function {}: invalid {} argument, expect a value of type {}, got {} (type: {})").format(
                 func_name, self._arg_name, types, value, type(value)
             ))