mirror of
https://github.com/microsoft/qlib.git
synced 2026-07-13 15:56:57 +08:00
exchange support expression buy sell limit
This commit is contained in:
@@ -26,7 +26,7 @@ class Exchange:
|
|||||||
codes="all",
|
codes="all",
|
||||||
deal_price: Union[str, Tuple[str], List[str]] = None,
|
deal_price: Union[str, Tuple[str], List[str]] = None,
|
||||||
subscribe_fields=[],
|
subscribe_fields=[],
|
||||||
limit_threshold=None,
|
limit_threshold: Union[Tuple[str, str], float, None] = None,
|
||||||
volume_threshold=None,
|
volume_threshold=None,
|
||||||
open_cost=0.0015,
|
open_cost=0.0015,
|
||||||
close_cost=0.0025,
|
close_cost=0.0025,
|
||||||
@@ -41,7 +41,7 @@ class Exchange:
|
|||||||
:param end_time: closed end time for backtest
|
:param end_time: closed end time for backtest
|
||||||
:param codes: list stock_id list or a string of instruments(i.e. all, csi500, sse50)
|
:param codes: list stock_id list or a string of instruments(i.e. all, csi500, sse50)
|
||||||
|
|
||||||
:param deal_price: Union[str, Tuple[str], List[str]]
|
:param deal_price: Union[str, Tuple[str, str], List[str]]
|
||||||
The `deal_price` supports following two types of input
|
The `deal_price` supports following two types of input
|
||||||
- <deal_price> : str
|
- <deal_price> : str
|
||||||
- (<buy_price>, <sell_price>): Tuple[str] or List[str]
|
- (<buy_price>, <sell_price>): Tuple[str] or List[str]
|
||||||
@@ -51,8 +51,16 @@ class Exchange:
|
|||||||
- for example '$close', '$open', '$vwap' ("close" is OK. `Exchange` will help to prepend
|
- for example '$close', '$open', '$vwap' ("close" is OK. `Exchange` will help to prepend
|
||||||
"$" to the expression)
|
"$" to the expression)
|
||||||
|
|
||||||
:param subscribe_fields: list, subscribe fields
|
:param subscribe_fields: list, subscribe fields. This expressions will be added to the query and `self.quote`.
|
||||||
:param limit_threshold: float, 0.1 for example, default None
|
It is useful when users want more fields to be queried
|
||||||
|
|
||||||
|
:param limit_threshold: Union[Tuple[str, str], float, None]
|
||||||
|
1) `None`: no limitation
|
||||||
|
2) float, 0.1 for example, default None
|
||||||
|
3) Tuple[str, str]: (<the expression for buying stock limitation>,
|
||||||
|
<the expression for sell stock limitation>)
|
||||||
|
`False` value indicates the stock is tradable
|
||||||
|
`True` value indicates the stock is limited and not tradable
|
||||||
:param volume_threshold: float, 0.1 for example, default None
|
:param volume_threshold: float, 0.1 for example, default None
|
||||||
:param open_cost: cost rate for open, default 0.0015
|
:param open_cost: cost rate for open, default 0.0015
|
||||||
:param close_cost: cost rate for close, default 0.0025
|
:param close_cost: cost rate for close, default 0.0025
|
||||||
@@ -97,7 +105,7 @@ class Exchange:
|
|||||||
if limit_threshold is None:
|
if limit_threshold is None:
|
||||||
if C.region == REG_CN:
|
if C.region == REG_CN:
|
||||||
self.logger.warning(f"limit_threshold not set. The stocks hit the limit may be bought/sold")
|
self.logger.warning(f"limit_threshold not set. The stocks hit the limit may be bought/sold")
|
||||||
elif abs(limit_threshold) > 0.1:
|
elif self._get_limit_type(limit_threshold) == self.LT_FLT and abs(limit_threshold) > 0.1:
|
||||||
if C.region == REG_CN:
|
if C.region == REG_CN:
|
||||||
self.logger.warning(f"limit_threshold may not be set to a reasonable value")
|
self.logger.warning(f"limit_threshold may not be set to a reasonable value")
|
||||||
|
|
||||||
@@ -119,13 +127,17 @@ class Exchange:
|
|||||||
# $change is for calculating the limit of the stock
|
# $change is for calculating the limit of the stock
|
||||||
|
|
||||||
necessary_fields = {self.buy_price, self.sell_price, "$close", "$change", "$factor", "$volume"}
|
necessary_fields = {self.buy_price, self.sell_price, "$close", "$change", "$factor", "$volume"}
|
||||||
|
if self._get_limit_type(limit_threshold) == self.LT_TP_EXP:
|
||||||
|
for exp in limit_threshold:
|
||||||
|
necessary_fields.add(exp)
|
||||||
subscribe_fields = list(necessary_fields | set(subscribe_fields))
|
subscribe_fields = list(necessary_fields | set(subscribe_fields))
|
||||||
all_fields = list(necessary_fields | set(subscribe_fields))
|
all_fields = list(necessary_fields | set(subscribe_fields))
|
||||||
|
|
||||||
self.all_fields = all_fields
|
self.all_fields = all_fields
|
||||||
self.open_cost = open_cost
|
self.open_cost = open_cost
|
||||||
self.close_cost = close_cost
|
self.close_cost = close_cost
|
||||||
self.min_cost = min_cost
|
self.min_cost = min_cost
|
||||||
self.limit_threshold = limit_threshold
|
self.limit_threshold: Union[Tuple[str, str], float, None] = limit_threshold
|
||||||
self.volume_threshold = volume_threshold
|
self.volume_threshold = volume_threshold
|
||||||
self.extra_quote = extra_quote
|
self.extra_quote = extra_quote
|
||||||
self.set_quote(codes, start_time, end_time)
|
self.set_quote(codes, start_time, end_time)
|
||||||
@@ -133,6 +145,7 @@ class Exchange:
|
|||||||
def set_quote(self, codes, start_time, end_time):
|
def set_quote(self, codes, start_time, end_time):
|
||||||
if len(codes) == 0:
|
if len(codes) == 0:
|
||||||
codes = D.instruments()
|
codes = D.instruments()
|
||||||
|
|
||||||
self.quote = D.features(codes, self.all_fields, start_time, end_time, freq=self.freq, disk_cache=True).dropna(
|
self.quote = D.features(codes, self.all_fields, start_time, end_time, freq=self.freq, disk_cache=True).dropna(
|
||||||
subset=["$close"]
|
subset=["$close"]
|
||||||
)
|
)
|
||||||
@@ -157,13 +170,7 @@ class Exchange:
|
|||||||
self.trade_w_adj_price = False
|
self.trade_w_adj_price = False
|
||||||
|
|
||||||
# update limit
|
# update limit
|
||||||
# check limit_threshold
|
self._update_limit()
|
||||||
if self.limit_threshold is None:
|
|
||||||
self.quote["limit_buy"] = False
|
|
||||||
self.quote["limit_sell"] = False
|
|
||||||
else:
|
|
||||||
# set limit
|
|
||||||
self._update_limit(buy_limit=self.limit_threshold, sell_limit=self.limit_threshold)
|
|
||||||
|
|
||||||
quote_df = self.quote
|
quote_df = self.quote
|
||||||
if self.extra_quote is not None:
|
if self.extra_quote is not None:
|
||||||
@@ -194,9 +201,33 @@ class Exchange:
|
|||||||
|
|
||||||
self.quote = quote_dict
|
self.quote = quote_dict
|
||||||
|
|
||||||
def _update_limit(self, buy_limit, sell_limit):
|
LT_TP_EXP = "(exp)" # Tuple[str, str]
|
||||||
self.quote["limit_buy"] = self.quote["$change"].ge(buy_limit)
|
LT_FLT = "float" # float
|
||||||
self.quote["limit_sell"] = self.quote["$change"].le(-sell_limit)
|
LT_NONE = "none" # none
|
||||||
|
|
||||||
|
def _get_limit_type(self, limit_threshold):
|
||||||
|
if isinstance(limit_threshold, Tuple):
|
||||||
|
return self.LT_TP_EXP
|
||||||
|
elif isinstance(limit_threshold, float):
|
||||||
|
return self.LT_FLT
|
||||||
|
elif limit_threshold is None:
|
||||||
|
return self.LT_NONE
|
||||||
|
else:
|
||||||
|
raise NotImplementedError(f"This type of `limit_threshold` is not supported")
|
||||||
|
|
||||||
|
def _update_limit(self):
|
||||||
|
# check limit_threshold
|
||||||
|
lt_type = self._get_limit_type(self.limit_threshold)
|
||||||
|
if lt_type == self.LT_NONE:
|
||||||
|
self.quote["limit_buy"] = False
|
||||||
|
self.quote["limit_sell"] = False
|
||||||
|
elif lt_type == self.LT_TP_EXP:
|
||||||
|
# set limit
|
||||||
|
self.quote["limit_buy"] = self.quote[self.limit_threshold[0]]
|
||||||
|
self.quote["limit_sell"] = self.quote[self.limit_threshold[1]]
|
||||||
|
elif lt_type == self.LT_FLT:
|
||||||
|
self.quote["limit_buy"] = self.quote["$change"].ge(self.limit_threshold)
|
||||||
|
self.quote["limit_sell"] = self.quote["$change"].le(-self.limit_threshold) # pylint: disable=E1130
|
||||||
|
|
||||||
def check_stock_limit(self, stock_id, start_time, end_time, direction=None):
|
def check_stock_limit(self, stock_id, start_time, end_time, direction=None):
|
||||||
"""
|
"""
|
||||||
|
|||||||
Reference in New Issue
Block a user