예제 #1
0
        def check_are_valid_fields(func_name, fields):
            if isinstance(fields, six.string_types):
                if fields not in valid_fields:
                    raise RQInvalidArgument(
                        _(u"function {}: invalid {} argument, valid fields are {}, got {} (type: {})"
                          ).format(func_name, self._arg_name,
                                   repr(valid_fields), fields, type(fields)))
                return

            if fields is None and ignore_none:
                return

            if isinstance(fields, list):
                invalid_fields = [
                    field for field in fields if field not in valid_fields
                ]
                if invalid_fields:
                    raise RQInvalidArgument(
                        _(u"function {}: invalid field {}, valid fields are {}, got {} (type: {})"
                          ).format(func_name, invalid_fields,
                                   repr(valid_fields), fields, type(fields)))
                return

            raise RQInvalidArgument(
                _(u"function {}: invalid {} argument, expect a string or a list of string, got {} (type: {})"
                  ).format(func_name, self._arg_name, repr(fields),
                           type(fields)))
예제 #2
0
def _validate_benchmark(config, data_proxy):
    benchmark = config.base.benchmark
    if benchmark is None:
        return
    instrument = data_proxy.instruments(benchmark)
    if instrument is None:
        raise patch_user_exc(
            ValueError(_(u"invalid benchmark {}").format(benchmark)))

    if instrument.order_book_id == "000300.XSHG":
        # 000300.XSHG 数据进行了补齐,因此认为只要benchmark设置了000300.XSHG,就存在数据,不受限于上市日期。
        return
    config = Environment.get_instance().config
    start_date = config.base.start_date
    end_date = config.base.end_date
    if instrument.listed_date.date() > start_date:
        raise patch_user_exc(
            ValueError(
                _(u"benchmark {benchmark} has not been listed on {start_date}"
                  ).format(benchmark=benchmark, start_date=start_date)))
    if instrument.de_listed_date.date() < end_date:
        raise patch_user_exc(
            ValueError(
                _(u"benchmark {benchmark} has been de_listed on {end_date}").
                format(benchmark=benchmark, end_date=end_date)))
예제 #3
0
def init_portfolio(env):
    accounts = {}
    config = env.config
    start_date = datetime.datetime.combine(config.base.start_date,
                                           datetime.time.min)
    units = 0

    if config.base.init_positions or (DEFAULT_ACCOUNT_TYPE.FUTURE.name
                                      in config.base.accounts
                                      and config.base.frequency != '1d'):
        BaseAccount.AGGRESSIVE_UPDATE_LAST_PRICE = True

    for account_type, starting_cash in six.iteritems(config.base.accounts):
        if starting_cash == 0:
            raise RuntimeError(
                _(u"{} starting cash can not be 0, using `--account {} 100000`"
                  ).format(account_type, account_type))

        account_model = env.get_account_model(account_type)
        position_model = env.get_position_model(account_type)
        positions = Positions(position_model)

        for order_book_id, quantity in _filter_positions(env, account_type):
            instrument = env.get_instrument(order_book_id)
            if instrument is None:
                raise RuntimeError(
                    _(u'invalid order book id {} in initial positions').format(
                        order_book_id))
            if not instrument.listing:
                raise RuntimeError(
                    _(u'instrument {} in initial positions is not listing').
                    format(order_book_id))

            bars = env.data_proxy.history_bars(
                order_book_id,
                1,
                '1d',
                'close',
                env.data_proxy.get_previous_trading_date(start_date),
                adjust_type='none')
            if bars is None:
                raise RuntimeError(
                    _(u'the close price of {} in initial positions is not available'
                      ).format(order_book_id))

            price = bars[0]
            trade = _fake_trade(order_book_id, quantity, price)
            if order_book_id not in positions:
                positions[order_book_id] = position_model(order_book_id)
            positions[order_book_id].apply_trade(trade)
            # FIXME
            positions[order_book_id]._last_price = price

        account = account_model(starting_cash, positions)
        units += account.total_value
        accounts[account_type] = account

    return Portfolio(config.base.start_date, 1, units, accounts)
예제 #4
0
 def tear_down(self, *args):
     result = {}
     for mod_name, __ in reversed(self._mod_list):
         try:
             system_log.debug(
                 _(u"mod tear_down [START] {}").format(mod_name))
             ret = self._mod_dict[mod_name].tear_down(*args)
             system_log.debug(
                 _(u"mod tear_down [END]   {}").format(mod_name))
         except Exception as e:
             system_log.exception("tear down fail for {}", mod_name)
             continue
         if ret is not None:
             result[mod_name] = ret
     return result
예제 #5
0
    def get_bars(self, order_book_id, fields=None):
        try:
            s, e = self._index[order_book_id]
        except KeyError:
            six.print_(_(u"No data for {}").format(order_book_id))
            return

        if fields is None:
            # the first is date
            fields = self._table.names[1:]

        if len(fields) == 1:
            return self._converter.convert(fields[0], self._table.cols[fields[0]][s:e])

        # remove datetime if exist in fields
        self._remove_(fields, 'datetime')

        dtype = np.dtype([('datetime', np.uint64)] +
                         [(f, self._converter.field_type(f, self._table.cols[f].dtype))
                          for f in fields])
        result = np.empty(shape=(e - s, ), dtype=dtype)
        for f in fields:
            result[f] = self._converter.convert(f, self._table.cols[f][s:e])
        result['datetime'] = self._table.cols['date'][s:e]
        result['datetime'] *= 1000000

        return result
예제 #6
0
def assure_stock_order_book_id(id_or_symbols):
    if isinstance(id_or_symbols, Instrument):
        return id_or_symbols.order_book_id
    elif isinstance(id_or_symbols, six.string_types):
        return assure_stock_order_book_id(instruments(id_or_symbols))
    else:
        raise RQInvalidArgument(_(u"unsupported order_book_id type"))
예제 #7
0
 def register_account_type(self, account_type, value):
     for k, v in six.iteritems(self.account_type_dict):
         if v == value:
             raise RuntimeError(
                 _(u"value {value} has been used for {original_key}").
                 format(value=value, original_key=k))
     self.account_type_dict[account_type] = value
예제 #8
0
 def _is_number(self, func_name, value):
     try:
         v = float(value)
     except ValueError:
         raise RQInvalidArgument(
             _(u"function {}: invalid {} argument, expect a number, got {} (type: {})"
               ).format(func_name, self._arg_name, value, type(value)))
예제 #9
0
    def set_env(self, environment):
        self._env = environment

        config = environment.config

        for mod_name in config.mod.__dict__:
            mod_config = getattr(config.mod, mod_name)
            if not mod_config.enabled:
                continue
            self._mod_list.append((mod_name, mod_config))

        for idx, (mod_name, user_mod_config) in enumerate(self._mod_list):
            if hasattr(user_mod_config, 'lib'):
                lib_name = user_mod_config.lib
            elif mod_name in SYSTEM_MOD_LIST:
                lib_name = "bwtougu.mod.bwtougu_mod_" + mod_name
            else:
                lib_name = "bwtougu_mod_" + mod_name
            system_log.debug(_(u"loading mod {}").format(lib_name))
            mod_module = import_mod(lib_name)
            if mod_module is None:
                del self._mod_list[idx]
                return
            mod = mod_module.load_mod()

            mod_config = RqAttrDict(
                copy.deepcopy(getattr(mod_module, "__config__", {})))
            mod_config.update(user_mod_config)
            setattr(config.mod, mod_name, mod_config)
            self._mod_list[idx] = (mod_name, mod_config)
            self._mod_dict[mod_name] = mod

        self._mod_list.sort(key=lambda item: getattr(item[1], "priority", 100))
        environment.mod_dict = self._mod_dict
예제 #10
0
 def wrapper(*args, **kwargs):
     if not Environment.get_instance().config.extra.is_hold:
         return func(*args, **kwargs)
     else:
         system_log.debug(
             _(u"not run {}({}, {}) because strategy is hold").format(
                 func, args, kwargs))
예제 #11
0
    def __getitem__(self, key):
        if not isinstance(key, six.string_types):
            raise patch_user_exc(
                ValueError(
                    'invalid key {} (use order_book_id please)'.format(key)))

        instrument = self._data_proxy.instruments(key)
        if instrument is None:
            raise patch_user_exc(
                ValueError('invalid order book id or symbol: {}'.format(key)))
        order_book_id = instrument.order_book_id

        try:
            return self._cache[order_book_id]
        except KeyError:
            try:
                bar = self._data_proxy.get_bar(order_book_id, self._dt,
                                               self._frequency)
            except Exception as e:
                system_log.exception(e)
                raise patch_user_exc(
                    KeyError(
                        _(u"id_or_symbols {} does not exist").format(key)))
            if bar is None:
                return BarObject(instrument, NANDict, self._dt)
            else:
                self._cache[order_book_id] = bar
                return bar
예제 #12
0
def order_percent(id_or_ins, percent, price=None, style=None):
    """
    发送一个等于目前投资组合价值(市场价值和目前现金的总和)一定百分比的买/卖单,正数代表买,负数代表卖。股票的股数总是会被调整成对应的一手的股票数的倍数(1手是100股)。百分比是一个小数,并且小于或等于1(<=100%),0.5表示的是50%.需要注意,如果资金不足,该API将不会创建发送订单。

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

    :param float percent: 占有现有的投资组合价值的百分比。正数表示买入,负数表示卖出。

    :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

        #买入等于现有投资组合50%价值的平安银行股票。如果现在平安银行的股价是¥10/股并且现在的投资组合总价值是¥2000,那么将会买入200股的平安银行股票。(不包含交易成本和滑点的损失):
        order_percent('000001.XSHG', 0.5)
    """
    if percent < -1 or percent > 1:
        raise RQInvalidArgument(_(u"percent should between -1 and 1"))

    style = cal_style(price, style)
    account = Environment.get_instance().portfolio.accounts[
        DEFAULT_ACCOUNT_TYPE.STOCK.name]
    return order_value(id_or_ins, account.total_value * percent, style=style)
예제 #13
0
 def _are_valid_query_entities(self, func_name, entities):
     from sqlalchemy.orm.attributes import InstrumentedAttribute
     for e in entities:
         if not isinstance(e, InstrumentedAttribute):
             raise RQInvalidArgument(
                 _(u"function {}: invalid {} argument, should be entity like "
                   u"Fundamentals.balance_sheet.total_equity, got {} (type: {})"
                   ).format(func_name, self.arg_name, e, type(e)))
예제 #14
0
 def wrapper(*args, **kwargs):
     phase = cls.stack.top.phase
     if phase not in phases:
         raise patch_user_exc(
             RuntimeError(
                 _(u"You cannot call %s when executing %s") %
                 (func.__name__, phase.value)))
     return func(*args, **kwargs)
예제 #15
0
        def check_is_valid_date(func_name, value):
            if ignore_none and value is None:
                return None
            if isinstance(value, (datetime.date, pd.Timestamp)):
                return
            if isinstance(value, six.string_types):
                try:
                    v = parse_date(value)
                    return
                except ValueError:
                    raise RQInvalidArgument(
                        _(u"function {}: invalid {} argument, expect a valid date, got {} (type: {})"
                          ).format(func_name, self._arg_name, value,
                                   type(value)))

            raise RQInvalidArgument(
                _(u"function {}: invalid {} argument, expect a valid date, got {} (type: {})"
                  ).format(func_name, self._arg_name, value, type(value)))
예제 #16
0
 def get_instance(cls):
     """
     返回已经创建的 Environment 对象
     """
     if Environment._env is None:
         raise RuntimeError(
             _(u"Environment has not been created. Please Use `Environment.get_instance()` after RQAlpha init"
               ))
     return Environment._env
예제 #17
0
        def check_is_in(func_name, value):
            if ignore_none and value is None:
                return

            if value not in valid_values:
                raise RQInvalidArgument(
                    _(u"function {}: invalid {} argument, valid: {}, got {} (type: {})"
                      ).format(func_name, self._arg_name, repr(valid_values),
                               value, type(value)))
예제 #18
0
    def start_up(self, env, mod_config):

        if env.config.base.run_type == RUN_TYPE.LIVE_TRADING:
            return

        mod_config.matching_type = self.parse_matching_type(
            mod_config.matching_type)
        if mod_config.commission_multiplier < 0:
            raise patch_user_exc(
                ValueError(
                    _(u"invalid commission multiplier value: value range is [0, +∞)"
                      )))
        if env.config.base.margin_multiplier <= 0:
            raise patch_user_exc(
                ValueError(
                    _(u"invalid margin multiplier value: value range is (0, +∞]"
                      )))

        if env.config.base.frequency == "tick":
            mod_config.volume_limit = False
            if mod_config.matching_type not in [
                    MATCHING_TYPE.NEXT_TICK_LAST,
                    MATCHING_TYPE.NEXT_TICK_BEST_OWN,
                    MATCHING_TYPE.NEXT_TICK_BEST_COUNTERPARTY,
            ]:
                raise RuntimeError(
                    _("Not supported matching type {}").format(
                        mod_config.matching_type))
        else:
            if mod_config.matching_type not in [
                    MATCHING_TYPE.NEXT_BAR_OPEN,
                    MATCHING_TYPE.CURRENT_BAR_CLOSE,
            ]:
                raise RuntimeError(
                    _("Not supported matching type {}").format(
                        mod_config.matching_type))

        if mod_config.signal:
            env.set_broker(SignalBroker(env, mod_config))
        else:
            env.set_broker(SimulationBroker(env, mod_config))

        event_source = SimulationEventSource(env)
        env.set_event_source(event_source)
예제 #19
0
 def _get_universe(self):
     universe = self._env.get_universe()
     if len(
             universe
     ) == 0 and DEFAULT_ACCOUNT_TYPE.STOCK.name not in self._config.base.accounts:
         raise patch_user_exc(RuntimeError(
             _("Current universe is empty. Please use subscribe function before trade"
               )),
                              force=True)
     return universe
예제 #20
0
def assure_order_book_id(id_or_ins):
    if isinstance(id_or_ins, Instrument):
        order_book_id = id_or_ins.order_book_id
    elif isinstance(id_or_ins, six.string_types):
        order_book_id = Environment.get_instance().data_proxy.instruments(
            id_or_ins).order_book_id
    else:
        raise RQInvalidArgument(_(u"unsupported order_book_id type"))

    return order_book_id
예제 #21
0
 def after_trading(self, event):
     for account, order in self._open_orders:
         order.mark_rejected(
             _(u"Order Rejected: {order_book_id} can not match. Market close."
               ).format(order_book_id=order.order_book_id))
         self._env.event_bus.publish_event(
             Event(EVENT.ORDER_UNSOLICITED_UPDATE,
                   account=account,
                   order=order))
     self._open_orders = self._delayed_orders
     self._delayed_orders = []
예제 #22
0
def parse_run_type(rt_str):
    assert isinstance(rt_str, six.string_types)
    mapping = {
        "b": RUN_TYPE.BACKTEST,
        "p": RUN_TYPE.PAPER_TRADING,
        "r": RUN_TYPE.LIVE_TRADING,
    }
    try:
        return mapping[rt_str]
    except KeyError:
        raise RuntimeError(_(u"unknown run type: {}").format(rt_str))
예제 #23
0
def _sell_all_stock(order_book_id, amount, style):
    env = Environment.get_instance()
    order = Order.__from_create__(order_book_id, amount, SIDE.SELL, style,
                                  None)
    if amount == 0:
        order.mark_rejected(_(u"Order Creation Failed: 0 order quantity"))
        return order

    if env.can_submit_order(order):
        env.broker.submit_order(order)
    return order
예제 #24
0
def cancel_order(order):
    """
    撤单

    :param order: 需要撤销的order对象
    :type order: :class:`~Order` object
    """
    if order is None:
        patch_user_exc(KeyError(_(u"Cancel order fail: invalid order id")))
    env = Environment.get_instance()
    if env.can_cancel_order(order):
        env.broker.cancel_order(order)
    return order
예제 #25
0
    def _is_valid_frequency(self, func_name, value):
        valid = isinstance(value, six.string_types) and value[-1] in ("d", "m")
        if valid:
            try:
                valid = int(value[:-1]) > 0
            except ValueError:
                valid = False

        if not valid:
            raise RQInvalidArgument(
                _(u"function {}: invalid {} argument, frequency should be in form of "
                  u"'1m', '5m', '1d', got {} (type: {})").format(
                      func_name, self.arg_name, value, type(value)))
예제 #26
0
def parse_accounts(accounts):
    a = {}
    if isinstance(accounts, tuple):
        accounts = {account_type: starting_cash for account_type, starting_cash in accounts}

    for account_type, starting_cash in six.iteritems(accounts):
        if starting_cash is None:
            continue
        starting_cash = float(starting_cash)
        a[account_type.upper()] = starting_cash
    if len(a) == 0:
        raise RuntimeError(_(u"None account type has been selected."))
    return a
예제 #27
0
    def _are_valid_instruments(self, func_name, values):
        if isinstance(values, (six.string_types, Instrument)):
            self._is_valid_instrument(func_name, values)
            return

        if isinstance(values, list):
            for v in values:
                self._is_valid_instrument(func_name, v)
            return

        raise RQInvalidArgument(
            _(u"function {}: invalid {} argument, expect a string or a list of string, got {} (type: {})"
              ).format(func_name, self._arg_name, repr(values), type(values)))
예제 #28
0
def order_target_percent(id_or_ins, percent, price=None, style=None):
    """
    买入/卖出证券以自动调整该证券的仓位到占有一个指定的投资组合的目标百分比。

    *   如果投资组合中没有任何该证券的仓位,那么会买入等于现在投资组合总价值的目标百分比的数目的证券。
    *   如果投资组合中已经拥有该证券的仓位,那么会买入/卖出目标百分比和现有百分比的差额数目的证券,最终调整该证券的仓位占据投资组合的比例至目标百分比。

    其实我们需要计算一个position_to_adjust (即应该调整的仓位)

    `position_to_adjust = target_position - current_position`

    投资组合价值等于所有已有仓位的价值和剩余现金的总和。买/卖单会被下舍入一手股数(A股是100的倍数)的倍数。目标百分比应该是一个小数,并且最大值应该<=1,比如0.5表示50%。

    如果position_to_adjust 计算之后是正的,那么会买入该证券,否则会卖出该证券。 需要注意,如果资金不足,该API将不会创建发送订单。

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

    :param float percent: 仓位最终所占投资组合总价值的目标百分比。

    :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

        #如果投资组合中已经有了平安银行股票的仓位,并且占据目前投资组合的10%的价值,那么以下代码会买入平安银行股票最终使其占据投资组合价值的15%:
        order_target_percent('000001.XSHE', 0.15)
    """
    if percent < 0 or percent > 1:
        raise RQInvalidArgument(_(u"percent should between 0 and 1"))
    order_book_id = assure_stock_order_book_id(id_or_ins)

    style = cal_style(price, style)

    account = Environment.get_instance().portfolio.accounts[
        DEFAULT_ACCOUNT_TYPE.STOCK.name]
    position = account.positions[order_book_id]

    if percent == 0:
        return _sell_all_stock(order_book_id, position.sellable, style)

    return order_value(order_book_id,
                       account.total_value * percent - position.market_value,
                       style=style)
예제 #29
0
    def _is_valid_interval(self, func_name, value):
        valid = isinstance(
            value, six.string_types) and value[-1] in {'d', 'm', 'q', 'y'}
        if valid:
            try:
                valid = int(value[:-1]) > 0
            except ValueError:
                valid = False

        if not valid:
            raise RQInvalidArgument(
                _(u"function {}: invalid {} argument, interval should be in form of '1d', '3m', '4q', '2y', "
                  u"got {} (type: {})").format(func_name, self.arg_name, value,
                                               type(value)))
예제 #30
0
    def __init__(self, event_bus, scope, ucontext):
        self._user_context = ucontext
        self._current_universe = set()

        self._init = scope.get('init', None)
        self._handle_bar = scope.get('handle_bar', None)
        self._handle_tick = scope.get('handle_tick', None)
        func_before_trading = scope.get('before_trading', None)
        if func_before_trading is not None and func_before_trading.__code__.co_argcount > 1:
            self._before_trading = lambda context: func_before_trading(
                context, None)
            system_log.warn(
                _(u"deprecated parameter[bar_dict] in before_trading function."
                  ))
        else:
            self._before_trading = func_before_trading
        self._after_trading = scope.get('after_trading', None)

        if self._before_trading is not None:
            event_bus.add_listener(EVENT.BEFORE_TRADING, self.before_trading)
        if self._handle_bar is not None:
            event_bus.add_listener(EVENT.BAR, self.handle_bar)
        if self._handle_tick is not None:
            event_bus.add_listener(EVENT.TICK, self.handle_tick)
        if self._after_trading is not None:
            event_bus.add_listener(EVENT.AFTER_TRADING, self.after_trading)

        self._before_day_trading = scope.get('before_day_trading', None)
        self._before_night_trading = scope.get('before_night_trading', None)
        if self._before_day_trading is not None:
            system_log.warn(
                _(u"[deprecated] before_day_trading is no longer used. use before_trading instead."
                  ))
        if self._before_night_trading is not None:
            system_log.warn(
                _(u"[deprecated] before_night_trading is no longer used. use before_trading instead."
                  ))