Пример #1
0
    def test_get_trading_period(self):
        from datetime import time
        from rqalpha.utils import TimeRange

        rb_time_range = self.data_proxy.get_trading_period(["RB1912"])
        self.assertSetEqual(
            set(rb_time_range), {
                TimeRange(start=time(21, 1), end=time(23, 0)),
                TimeRange(start=time(9, 1), end=time(10, 15)),
                TimeRange(start=time(10, 31), end=time(11, 30)),
                TimeRange(start=time(13, 31), end=time(15, 0))
            })

        merged_time_range = self.data_proxy.get_trading_period(
            ["AG1912", "TF1912"], [
                TimeRange(start=time(9, 31), end=time(11, 30)),
                TimeRange(start=time(13, 1), end=time(15, 0)),
            ])
        self.assertSetEqual(
            set(merged_time_range), {
                TimeRange(start=time(21, 1), end=time(23, 59)),
                TimeRange(start=time(0, 0), end=time(2, 30)),
                TimeRange(start=time(9, 1), end=time(11, 30)),
                TimeRange(start=time(13, 1), end=time(15, 15)),
            })
Пример #2
0
 def trading_hours(self):
     # trading_hours='09:31-11:30,13:01-15:00'
     try:
         trading_hours = self.__dict__["trading_hours"]
     except KeyError:
         if self.type in INST_TYPE_IN_STOCK_ACCOUNT:
             return self.STOCK_TRADING_PERIOD
         return None
     trading_period = []
     trading_hours = trading_hours.replace("-", ":")
     for time_range_str in trading_hours.split(","):
         start_h, start_m, end_h, end_m = (int(i) for i in time_range_str.split(":"))
         start, end = datetime.time(start_h, start_m), datetime.time(end_h, end_m)
         if start > end:
             trading_period.append(TimeRange(start, datetime.time(23, 59)))
             trading_period.append(TimeRange(datetime.time(0, 0), end))
         else:
             trading_period.append(TimeRange(start, end))
     return trading_period
Пример #3
0
class Instrument(metaclass=PropertyReprMeta):
    DEFAULT_LISTED_DATE = datetime.datetime(1990, 1, 1)
    DEFAULT_DE_LISTED_DATE = datetime.datetime(2999, 12, 31)

    @staticmethod
    def _fix_date(ds, dflt):
        if isinstance(ds, datetime.datetime):
            return ds
        if ds == '0000-00-00':
            return dflt
        year, month, day = ds.split('-')
        return datetime.datetime(int(year), int(month), int(day))

    __repr__ = property_repr

    def __init__(self, dic, future_tick_size_getter=None):
        # type: (Dict, Optional[Callable[[Instrument], float]]) -> None
        self.__dict__ = copy.copy(dic)
        self._future_tick_size_getter = future_tick_size_getter

        if "listed_date" in dic:
            self.__dict__["listed_date"] = self._fix_date(
                dic["listed_date"], self.DEFAULT_LISTED_DATE)
        if "de_listed_date" in dic:
            self.__dict__["de_listed_date"] = self._fix_date(
                dic["de_listed_date"], self.DEFAULT_DE_LISTED_DATE)
        if "maturity_date" in self.__dict__:
            self.__dict__["maturity_date"] = self._fix_date(
                dic["maturity_date"], self.DEFAULT_DE_LISTED_DATE)

        if 'contract_multiplier' in dic:
            if np.isnan(self.contract_multiplier):
                raise RuntimeError(
                    "Contract multiplier of {} is not supposed to be nan".
                    format(self.order_book_id))

    @property
    def order_book_id(self):
        # type: () -> str
        """
        [str] 股票:证券代码,证券的独特的标识符。应以’.XSHG’或’.XSHE’结尾,前者代表上证,后者代表深证。
        期货:期货代码,期货的独特的标识符(郑商所期货合约数字部分进行了补齐。例如原有代码’ZC609’补齐之后变为’ZC1609’)。
        主力连续合约UnderlyingSymbol+88,例如’IF88’ ;指数连续合约命名规则为UnderlyingSymbol+99
        """
        return self.__dict__["order_book_id"]

    @property
    def symbol(self):
        # type: () -> str
        """
        [str] 股票:证券的简称,例如’平安银行’。期货:期货的简称,例如’沪深1005’。
        """
        return self.__dict__["symbol"]

    @property
    def round_lot(self):
        # type: () -> int
        """
        [int] 股票:一手对应多少股,中国A股一手是100股。期货:一律为1。
        """
        return self.__dict__["round_lot"]

    @property
    def listed_date(self):
        # type: () -> datetime.datetime
        """
        [datetime] 股票:该证券上市日期。期货:期货的上市日期,主力连续合约与指数连续合约都为 datetime(1990, 1, 1)。
        """
        return self.__dict__["listed_date"]

    @property
    def de_listed_date(self):
        # type: () -> datetime.datetime
        """
        [datetime] 股票:退市日期。期货:交割日期。
        """
        return self.__dict__["de_listed_date"]

    @property
    def type(self):
        # type: () -> str
        """
        [sty] 合约类型,目前支持的类型有: ‘CS’, ‘INDX’, ‘LOF’, ‘ETF’, ‘Future’
        """
        return INSTRUMENT_TYPE[self.__dict__["type"]]

    @property
    def exchange(self):
        # type: () -> EXCHANGE
        """
        [str] 交易所。股票:’XSHE’ - 深交所, ‘XSHG’ - 上交所。期货:’DCE’ - 大连商品交易所, ‘SHFE’ - 上海期货交易所,
        ’CFFEX’ - 中国金融期货交易所, ‘CZCE’- 郑州商品交易所
        """
        return self.__dict__["exchange"]

    @property
    def market_tplus(self):
        # type: () -> int
        """
        [int] 合约卖出和买入操作需要间隔的最小交易日数,如A股为 1
        公募基金的 market_tplus 默认0
        """
        return self.__dict__.get("market_tplus") or 0

    @property
    def sector_code(self):
        """
        [str] 板块缩写代码,全球通用标准定义(股票专用)
        """
        try:
            return self.__dict__["sector_code"]
        except (KeyError, ValueError):
            raise AttributeError(
                "Instrument(order_book_id={}) has no attribute 'sector_code' ".
                format(self.order_book_id))

    @property
    def sector_code_name(self):
        """
        [str] 以当地语言为标准的板块代码名(股票专用)
        """
        try:
            return self.__dict__["sector_code_name"]
        except (KeyError, ValueError):
            raise AttributeError(
                "Instrument(order_book_id={}) has no attribute 'sector_code_name' "
                .format(self.order_book_id))

    @property
    def industry_code(self):
        """
        [str] 国民经济行业分类代码,具体可参考“Industry列表” (股票专用)
        """
        try:
            return self.__dict__["industry_code"]
        except (KeyError, ValueError):
            raise AttributeError(
                "Instrument(order_book_id={}) has no attribute 'industry_code' "
                .format(self.order_book_id))

    @property
    def industry_name(self):
        """
        [str] 国民经济行业分类名称(股票专用)
        """
        try:
            return self.__dict__["industry_name"]
        except (KeyError, ValueError):
            raise AttributeError(
                "Instrument(order_book_id={}) has no attribute 'industry_name' "
                .format(self.order_book_id))

    @property
    def concept_names(self):
        """
        [str] 概念股分类,例如:’铁路基建’,’基金重仓’等(股票专用)
        """
        try:
            return self.__dict__["concept_names"]
        except (KeyError, ValueError):
            raise AttributeError(
                "Instrument(order_book_id={}) has no attribute 'concept_names' "
                .format(self.order_book_id))

    @property
    def board_type(self):
        """
        [str] 板块类别,’MainBoard’ - 主板,’GEM’ - 创业板(股票专用)
        """
        try:
            return self.__dict__["board_type"]
        except (KeyError, ValueError):
            raise AttributeError(
                "Instrument(order_book_id={}) has no attribute 'board_type' ".
                format(self.order_book_id))

    @property
    def status(self):
        """
        [str] 合约状态。’Active’ - 正常上市, ‘Delisted’ - 终止上市, ‘TemporarySuspended’ - 暂停上市,
        ‘PreIPO’ - 发行配售期间, ‘FailIPO’ - 发行失败(股票专用)
        """
        try:
            return self.__dict__["status"]
        except (KeyError, ValueError):
            raise AttributeError(
                "Instrument(order_book_id={}) has no attribute 'status' ".
                format(self.order_book_id))

    @property
    def special_type(self):
        """
        [str] 特别处理状态。’Normal’ - 正常上市, ‘ST’ - ST处理, ‘StarST’ - *ST代表该股票正在接受退市警告,
        ‘PT’ - 代表该股票连续3年收入为负,将被暂停交易, ‘Other’ - 其他(股票专用)
        """
        try:
            return self.__dict__["special_type"]
        except (KeyError, ValueError):
            raise AttributeError(
                "Instrument(order_book_id={}) has no attribute 'special_type' "
                .format(self.order_book_id))

    @property
    def contract_multiplier(self):
        """
        [float] 合约乘数,例如沪深300股指期货的乘数为300.0(期货专用)
        """
        return self.__dict__.get('contract_multiplier', 1)

    @property
    def margin_rate(self):
        """
        [float] 合约最低保证金率(期货专用)
        """
        return self.__dict__.get("margin_rate", 1)

    @property
    def underlying_order_book_id(self):
        """
        [str] 合约标的代码,目前除股指期货(IH, IF, IC)之外的期货合约,这一字段全部为’null’(期货专用)
        """
        try:
            return self.__dict__["underlying_order_book_id"]
        except (KeyError, ValueError):
            raise AttributeError(
                "Instrument(order_book_id={}) has no attribute 'underlying_order_book_id' "
                .format(self.order_book_id))

    @property
    def underlying_symbol(self):
        """
        [str] 合约标的代码,目前除股指期货(IH, IF, IC)之外的期货合约,这一字段全部为’null’(期货专用)
        """
        try:
            return self.__dict__["underlying_symbol"]
        except (KeyError, ValueError):
            raise AttributeError(
                "Instrument(order_book_id={}) has no attribute 'underlying_symbol' "
                .format(self.order_book_id))

    @property
    def maturity_date(self):
        # type: () -> datetime.datetime
        """
        [datetime] 到期日
        """
        try:
            return self.__dict__["maturity_date"]
        except (KeyError, ValueError):
            raise AttributeError(
                "Instrument(order_book_id={}) has no attribute 'maturity_date' "
                .format(self.order_book_id))

    @property
    def settlement_method(self):
        """
        [str] 交割方式,’CashSettlementRequired’ - 现金交割, ‘PhysicalSettlementRequired’ - 实物交割(期货专用)
        """
        try:
            return self.__dict__["settlement_method"]
        except (KeyError, ValueError):
            raise AttributeError(
                "Instrument(order_book_id={}) has no attribute 'settlement_method' "
                .format(self.order_book_id))

    @property
    def listing(self):
        """
        [bool] 该合约当前日期是否在交易
        """
        trading_dt = Environment.get_instance().trading_dt
        return self.listing_at(trading_dt)

    @property
    def listed(self):
        """
        [bool] 该合约当前交易日是否已上市
        """
        return self.listed_at(Environment.get_instance().trading_dt)

    @property
    def de_listed(self):
        """
        [bool] 该合约当前交易日是否已退市
        """
        return self.de_listed_at(Environment.get_instance().trading_dt)

    @property
    def account_type(self):
        if self.type in INST_TYPE_IN_STOCK_ACCOUNT:
            return DEFAULT_ACCOUNT_TYPE.STOCK
        elif self.type == INSTRUMENT_TYPE.FUTURE:
            return DEFAULT_ACCOUNT_TYPE.FUTURE
        else:
            raise NotImplementedError

    def listing_at(self, dt):
        """
        该合约在指定日期是否在交易
        :param dt: datetime.datetime
        :return: bool
        """
        return self.listed_at(dt) and not self.de_listed_at(dt)

    def listed_at(self, dt):
        """
        该合约在指定日期是否已上日
        :param dt: datetime.datetime
        :return: bool
        """
        return self.listed_date <= dt

    def de_listed_at(self, dt):
        """
        该合约在指定日期是否已退市
        :param dt: datetime.datetime
        :return: bool
        """
        if self.type in (INSTRUMENT_TYPE.FUTURE, INSTRUMENT_TYPE.OPTION):
            return dt.date() > self.de_listed_date.date()
        else:
            return dt >= self.de_listed_date

    STOCK_TRADING_PERIOD = [
        TimeRange(start=datetime.time(9, 31), end=datetime.time(11, 30)),
        TimeRange(start=datetime.time(13, 1), end=datetime.time(15, 0)),
    ]

    @property
    def trading_hours(self):
        # trading_hours='09:31-11:30,13:01-15:00'
        try:
            trading_hours = self.__dict__["trading_hours"]
        except KeyError:
            if self.type in INST_TYPE_IN_STOCK_ACCOUNT:
                return self.STOCK_TRADING_PERIOD
            return None
        trading_period = []
        trading_hours = trading_hours.replace("-", ":")
        for time_range_str in trading_hours.split(","):
            start_h, start_m, end_h, end_m = (
                int(i) for i in time_range_str.split(":"))
            start, end = datetime.time(start_h,
                                       start_m), datetime.time(end_h, end_m)
            if start > end:
                trading_period.append(TimeRange(start, datetime.time(23, 59)))
                trading_period.append(TimeRange(datetime.time(0, 0), end))
            else:
                trading_period.append(TimeRange(start, end))
        return trading_period

    @property
    def trading_code(self):
        # type: () -> str
        try:
            return self.__dict__["trading_code"]
        except (KeyError, ValueError):
            raise AttributeError(
                "Instrument(order_book_id={}) has no attribute 'trading_code' "
                .format(self.order_book_id))

    @property
    def trade_at_night(self):
        return any(
            r.start <= datetime.time(4, 0) or r.end >= datetime.time(19, 0)
            for r in (self.trading_hours or []))

    def days_from_listed(self):
        if self.listed_date == self.DEFAULT_LISTED_DATE:
            return -1

        date = Environment.get_instance().trading_dt.date()
        if self.de_listed_date.date() < date:
            return -1

        ipo_days = (date - self.listed_date.date()).days
        return ipo_days if ipo_days >= 0 else -1

    def days_to_expire(self):
        if self.type != 'Future' or self.order_book_id[
                -2:] == '88' or self.order_book_id[-2:] == '99':
            return -1

        date = Environment.get_instance().trading_dt.date()
        days = (self.maturity_date.date() - date).days
        return -1 if days < 0 else days

    def tick_size(self):
        # type: () -> float
        if self.type in (INSTRUMENT_TYPE.CS, INSTRUMENT_TYPE.INDX):
            return 0.01
        elif self.type in ("ETF", "LOF"):
            return 0.001
        elif self.type == INSTRUMENT_TYPE.FUTURE:
            return self._future_tick_size_getter(self)
        else:
            raise NotImplementedError

    def calc_cash_occupation(self, price, quantity, direction):
        # type: (float, float, POSITION_DIRECTION) -> float
        if self.type in INST_TYPE_IN_STOCK_ACCOUNT:
            return price * quantity
        elif self.type == INSTRUMENT_TYPE.FUTURE:
            margin_multiplier = Environment.get_instance(
            ).config.base.margin_multiplier
            return price * quantity * self.contract_multiplier * self.margin_rate * margin_multiplier
        else:
            raise NotImplementedError