Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

期货回测新增时序交易参数功能 #835

Merged
merged 30 commits into from
Jan 15, 2024
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
8c90ed1
Merge pull request #744 from ricequant/develop
Zhou-JiaJun Nov 10, 2022
42c6067
Merge pull request #747 from ricequant/develop
Cuizi7 Dec 20, 2022
1b50d26
Merge pull request #749 from ricequant/develop
Cuizi7 Jan 5, 2023
45c367b
Merge pull request #756 from ricequant/develop
Zhou-JiaJun Jan 15, 2023
80f14e1
Merge pull request #771 from ricequant/develop
Cuizi7 Mar 2, 2023
2fda3d4
Merge pull request #781 from ricequant/develop
Zhou-JiaJun Mar 15, 2023
dcec2c1
Merge pull request #788 from ricequant/develop
Zhou-JiaJun Apr 13, 2023
2f3a480
Merge pull request #794 from ricequant/develop
Zhou-JiaJun Jun 1, 2023
2f24e33
Merge pull request #800 from ricequant/develop
Zhou-JiaJun Jun 16, 2023
e336fcf
Merge pull request #801 from ricequant/develop
Zhou-JiaJun Jul 7, 2023
8befe5b
Merge pull request #803 from ricequant/develop
Zhou-JiaJun Jul 21, 2023
9506c3c
Merge pull request #807 from ricequant/develop
Zhou-JiaJun Aug 7, 2023
09ef170
Merge pull request #811 from ricequant/develop
Zhou-JiaJun Aug 31, 2023
0fd8f89
Merge pull request #813 from ricequant/develop
Zhou-JiaJun Sep 15, 2023
3fd540e
Merge pull request #825 from ricequant/develop
Zhou-JiaJun Nov 29, 2023
88358c8
modify .readthedocs.yml
Zhou-JiaJun Nov 30, 2023
985c2df
Merge pull request #830 from ricequant/develop
Cuizi7 Dec 7, 2023
1525ecf
期货回测新增时序交易参数功能
Lin-Dongzhao Dec 27, 2023
8ed7285
调整future_info更新方法
Lin-Dongzhao Dec 28, 2023
4d1da10
pr update
Lin-Dongzhao Jan 2, 2024
ea23c50
pr update
Lin-Dongzhao Jan 3, 2024
048595e
pr update
Lin-Dongzhao Jan 4, 2024
2d974b7
pr update
Lin-Dongzhao Jan 5, 2024
5ad70f1
增加函数参数及返回值类型说明
Lin-Dongzhao Jan 9, 2024
4136909
pr update
Lin-Dongzhao Jan 9, 2024
af38772
pr update
Lin-Dongzhao Jan 11, 2024
345382b
修改提示语翻译
Lin-Dongzhao Jan 12, 2024
d5f1ade
Merge branch 'develop' of https://github.com/ricequant/rqalpha into d…
Lin-Dongzhao Jan 12, 2024
16283c4
merge from develop
Lin-Dongzhao Jan 12, 2024
bb1ba72
pr update
Lin-Dongzhao Jan 15, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
498 changes: 309 additions & 189 deletions messages.pot

Large diffs are not rendered by default.

36 changes: 28 additions & 8 deletions rqalpha/data/base_data_source/data_source.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,16 @@
from rqalpha.utils.functools import lru_cache
from rqalpha.utils.typing import DateLike
from rqalpha.environment import Environment
from rqalpha.data.bundle import update_futures_trading_parameters
from rqalpha.utils.logger import user_system_log

from rqalpha.data.base_data_source.adjust import FIELDS_REQUIRE_ADJUSTMENT, adjust_bars
from rqalpha.data.base_data_source.storage_interface import (AbstractCalendarStore, AbstractDateSet,
AbstractDayBarStore, AbstractDividendStore,
AbstractInstrumentStore)
from rqalpha.data.base_data_source.storages import (DateSet, DayBarStore, DividendStore,
ExchangeTradingCalendarStore, FutureDayBarStore,
FutureInfoStore, InstrumentStore,
FutureInfoStore, FuturesTradingParametersStore,InstrumentStore,
ShareTransformationStore, SimpleFactorStore,
YieldCurveStore)

Expand Down Expand Up @@ -71,7 +73,7 @@ class BaseDataSource(AbstractDataSource):
INSTRUMENT_TYPE.PUBLIC_FUND,
)

def __init__(self, path, custom_future_info):
def __init__(self, path, custom_future_info, update_parameters_end_date=None):
if not os.path.exists(path):
raise RuntimeError('bundle path {} not exist'.format(os.path.abspath(path)))

Expand All @@ -86,20 +88,30 @@ def _p(name):
INSTRUMENT_TYPE.ETF: funds_day_bar_store,
INSTRUMENT_TYPE.LOF: funds_day_bar_store
} # type: Dict[INSTRUMENT_TYPE, AbstractDayBarStore]


self._futures_trading_parameters_store = None
if update_parameters_end_date:
if update_futures_trading_parameters(path, update_parameters_end_date):
self._futures_trading_parameters_store = FuturesTradingParametersStore(_p("futures_trading_parameters.h5"))
self._future_info_store = FutureInfoStore(_p("future_info.json"), custom_future_info)

self._instruments_stores = {} # type: Dict[INSTRUMENT_TYPE, AbstractInstrumentStore]
self._ins_id_or_sym_type_map = {} # type: Dict[str, INSTRUMENT_TYPE]
instruments = []

with open(_p('instruments.pk'), 'rb') as f:
for i in pickle.load(f):
if i["type"] == "Future" and Instrument.is_future_continuous_contract(i["order_book_id"]):
i["listed_date"] = datetime(1990, 1, 1)
instruments.append(Instrument(i, lambda i: self._future_info_store.get_future_info(i)["tick_size"]))
instruments.append(Instrument(
i,
lambda i: self._future_info_store.get_future_info(i).tick_size,
lambda i: self.get_futures_trading_parameters(i).long_margin_ratio,
lambda i: self.get_futures_trading_parameters(i).short_margin_ratio
))
for ins_type in self.DEFAULT_INS_TYPES:
self.register_instruments_store(InstrumentStore(instruments, ins_type))

self._future_info_store.data_compatible(self._instruments_stores[INSTRUMENT_TYPE.FUTURE])
dividend_store = DividendStore(_p('dividends.h5'))
self._dividends = {
INSTRUMENT_TYPE.CS: dividend_store,
Expand Down Expand Up @@ -359,8 +371,16 @@ def available_data_range(self, frequency):
def get_yield_curve(self, start_date, end_date, tenor=None):
return self._yield_curve.get_yield_curve(start_date, end_date, tenor=tenor)

def get_commission_info(self, instrument):
return self._future_info_store.get_future_info(instrument)
def get_futures_trading_parameters(self, instrument):
Cuizi7 marked this conversation as resolved.
Show resolved Hide resolved
# type: (Instrument) -> FuturesTradingParameters
if self._futures_trading_parameters_store:
trading_parameters = self._futures_trading_parameters_store.get_futures_trading_parameters(instrument)
if trading_parameters is None:
id_or_syms = instrument.order_book_id or instrument.underlying_symbol
return self._future_info_store.get_future_info(instrument.order_book_id, instrument.underlying_symbol)
return trading_parameters
else:
return self._future_info_store.get_future_info(instrument.order_book_id, instrument.underlying_symbol)

def get_merge_ticks(self, order_book_id_list, trading_date, last_dt=None):
raise NotImplementedError
Expand Down
150 changes: 134 additions & 16 deletions rqalpha/data/base_data_source/storages.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
from copy import copy
from itertools import chain
from contextlib import contextmanager
from typing import Dict, Iterable, Optional
from typing import Dict, Iterable, Optional, NamedTuple

import h5py
import numpy as np
Expand All @@ -34,12 +34,28 @@
from rqalpha.model.instrument import Instrument
from rqalpha.utils.datetime_func import convert_date_to_date_int
from rqalpha.utils.i18n import gettext as _
from rqalpha.utils.logger import user_system_log
from rqalpha.environment import Environment

from .storage_interface import (AbstractCalendarStore, AbstractDateSet,
AbstractDayBarStore, AbstractDividendStore,
AbstractInstrumentStore,
AbstractSimpleFactorStore)

class FuturesTradingParameters(NamedTuple):
"""
数据类,用以存储期货交易参数数据
"""
close_commission_ratio: float
close_commission_today_ratio: float
commission_type: str
open_commission_ratio: float
long_margin_ratio: float
short_margin_ratio: float
order_book_id: str = None
Cuizi7 marked this conversation as resolved.
Show resolved Hide resolved
underlying_symbol: str = None
tick_size: float = None
Cuizi7 marked this conversation as resolved.
Show resolved Hide resolved


class ExchangeTradingCalendarStore(AbstractCalendarStore):
def __init__(self, f):
Expand All @@ -64,27 +80,69 @@ def __init__(self, f, custom_future_info):
) for item in json.load(json_file)
}
self._custom_data = custom_future_info
self._future_info = {}

def data_compatible(self, futures_instruments_store):
Cuizi7 marked this conversation as resolved.
Show resolved Hide resolved
"""
RQAlpha==5.3.5 后, margin_rate调整为从 future_info.json 获取,当用户的 bundle 数据未更新时,调用该函数进行兼容
"""
user_system_log.warn(_("Your bundle data is too old, please update it to lastest"))
hard_code = {"TC": 0.05, "ER": 0.05, "WS": 0.05, "RO": 0.05, "ME": 0.06, "WT": 0.05}
if "margin_rate" not in self._default_data[list(hard_code.keys())[0]]:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

list(...)[0] -> next(iter(...))

for id_or_syms in list(self._default_data.keys()):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for ... in self._default_data:

if len(id_or_syms) <= 2:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

最好用一个正则表达式

if id_or_syms in hard_code.keys():
self._default_data[id_or_syms]["margin_rate"] = hard_code[id_or_syms]
else:
order_book_id = futures_instruments_store._instruments[id_or_syms + "88"].trading_code
self._default_data[id_or_syms]["margin_rate"] = futures_instruments_store._instruments[order_book_id].margin_rate
else:
self._default_data[id_or_syms]["margin_rate"] = futures_instruments_store._instruments[id_or_syms].margin_rate

@classmethod
def _process_future_info_item(cls, item):
item["commission_type"] = cls.COMMISSION_TYPE_MAP[item["commission_type"]]
return item

def get_future_info(self, instrument):
# type: (Instrument) -> Dict[str, float]
order_book_id = instrument.order_book_id
try:
return self._future_info[order_book_id]
except KeyError:
custom_info = self._custom_data.get(order_book_id) or self._custom_data.get(instrument.underlying_symbol)
info = self._default_data.get(order_book_id) or self._default_data.get(instrument.underlying_symbol)
if custom_info:
info = copy(info) or {}
info.update(custom_info)
elif not info:
raise NotImplementedError(_("unsupported future instrument {}").format(order_book_id))
return self._future_info.setdefault(order_book_id, info)
@lru_cache(1024)
Lin-Dongzhao marked this conversation as resolved.
Show resolved Hide resolved
def get_future_info(self, order_book_id, underlying_symbol):
# type: (str, str) -> FuturesTradingParameters
custom_info = self._custom_data.get(order_book_id) or self._custom_data.get(underlying_symbol)
info = self._default_data.get(order_book_id) or self._default_data.get(underlying_symbol)
if custom_info:
info = copy(info) or {}
info.update(custom_info)
elif not info:
raise NotImplementedError(_("unsupported future instrument {}").format(order_book_id))
info = self._to_namedtuple(info)
return info

def _to_namedtuple(self, info):
# type: (dict) -> FuturesTradingParameters
if info.get("order_book_id"):
order_book_id = info['order_book_id']
underlying_symbol = None
else:
order_book_id = None
underlying_symbol = info['underlying_symbol']
close_commission_ratio = info['close_commission_ratio']
close_commission_today_ratio = info['close_commission_today_ratio']
commission_type = info['commission_type']
open_commission_ratio = info['open_commission_ratio']
long_margin_ratio = info['margin_rate']
short_margin_ratio = info['margin_rate']
tick_size = info['tick_size']
info = FuturesTradingParameters(
close_commission_ratio,
close_commission_today_ratio,
commission_type,
open_commission_ratio,
long_margin_ratio,
short_margin_ratio,
order_book_id,
underlying_symbol,
tick_size
)
return info


class InstrumentStore(AbstractInstrumentStore):
Expand Down Expand Up @@ -208,6 +266,66 @@ class FutureDayBarStore(DayBarStore):
DEFAULT_DTYPE = np.dtype(DayBarStore.DEFAULT_DTYPE.descr + [("open_interest", '<f8')])


class FuturesTradingParametersStore(object):
COMMISSION_TYPE_MAP = {
0: COMMISSION_TYPE.BY_MONEY,
1: COMMISSION_TYPE.BY_VOLUME
}

# 历史期货交易参数的数据在2010年4月之后才有
FUTURES_TRADING_PARAMETERS_START_DATE = 20100401

def __init__(self, path):
self._path = path

def get_futures_trading_parameters(self, instrument):
# type: (Instrument) -> FuturesTradingParameters or None
env = Environment.get_instance()
order_book_id = instrument.order_book_id
dt = convert_date_to_date_int(env.trading_dt)
if (dt < self.FUTURES_TRADING_PARAMETERS_START_DATE): return None
data = self.get_futures_trading_parameters_all_time(order_book_id)
if (data is None): return None
else:
arr = data[data['datetime'] == dt]
if len(arr) == 0:
if dt >= convert_date_to_date_int(instrument.listed_date) and dt <= convert_date_to_date_int(instrument.de_listed_date):
user_system_log.info("Historical futures trading parameters are abnormal, the lastst parameters will be used for calculations.\nPlease contract RiceQuant to repair: 0755-26569969")
return None
futures_trading_parameters = self._to_namedtuple(order_book_id, arr)
return futures_trading_parameters

@lru_cache(1024)
def get_futures_trading_parameters_all_time(self, order_book_id):
# type: (str) -> numpy.ndarray or None
with h5_file(self._path) as h5:
Lin-Dongzhao marked this conversation as resolved.
Show resolved Hide resolved
try:
data = h5[order_book_id][:]
except KeyError:
return None
return data

def _to_namedtuple(self, order_book_id, arr):
# type: (str, numpy.ndarray) -> FuturesTradingParameters
close_commission_ratio = float(arr["close_commission"][0])
close_commission_today_ratio = float(arr['close_commission_today'][0])
commission_type = self.COMMISSION_TYPE_MAP[int(arr['commission_type'][0])]
open_commission_ratio = float(arr['open_commission'][0])
long_margin_ratio = float(arr["long_margin_ratio"][0])
short_margin_ratio = float(arr["short_margin_ratio"][0])

futures_trading_parameters = FuturesTradingParameters(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FuturesTradingParameters(**arr[0])

close_commission_ratio,
close_commission_today_ratio,
commission_type,
open_commission_ratio,
long_margin_ratio,
short_margin_ratio,
order_book_id
)
return futures_trading_parameters


class DividendStore(AbstractDividendStore):
def __init__(self, path):
self._path = path
Expand Down