mirror of
https://github.com/microsoft/qlib.git
synced 2026-07-12 23:36:54 +08:00
black format & add comments & add randStrategy direction
This commit is contained in:
@@ -92,7 +92,9 @@ def get_exchange(
|
|||||||
return init_instance_by_config(exchange, accept_types=Exchange)
|
return init_instance_by_config(exchange, accept_types=Exchange)
|
||||||
|
|
||||||
|
|
||||||
def create_account_instance(start_time, end_time, benchmark: str, account: float, pos_type: str="Position") -> Account:
|
def create_account_instance(
|
||||||
|
start_time, end_time, benchmark: str, account: float, pos_type: str = "Position"
|
||||||
|
) -> Account:
|
||||||
"""
|
"""
|
||||||
# TODO: is very strange pass benchmark_config in the account(maybe for report)
|
# TODO: is very strange pass benchmark_config in the account(maybe for report)
|
||||||
# There should be a post-step to process the report.
|
# There should be a post-step to process the report.
|
||||||
@@ -119,26 +121,25 @@ def create_account_instance(start_time, end_time, benchmark: str, account: float
|
|||||||
"start_time": start_time,
|
"start_time": start_time,
|
||||||
"end_time": end_time,
|
"end_time": end_time,
|
||||||
},
|
},
|
||||||
"pos_type": pos_type
|
"pos_type": pos_type,
|
||||||
}
|
}
|
||||||
return Account(**kwargs)
|
return Account(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
def get_strategy_executor(start_time,
|
def get_strategy_executor(
|
||||||
end_time,
|
start_time,
|
||||||
strategy: BaseStrategy,
|
end_time,
|
||||||
executor: BaseExecutor,
|
strategy: BaseStrategy,
|
||||||
benchmark: str = "SH000300",
|
executor: BaseExecutor,
|
||||||
account: Union[float, str] = 1e9,
|
benchmark: str = "SH000300",
|
||||||
exchange_kwargs: dict = {},
|
account: Union[float, str] = 1e9,
|
||||||
pos_type: str = "Position",
|
exchange_kwargs: dict = {},
|
||||||
):
|
pos_type: str = "Position",
|
||||||
|
):
|
||||||
|
|
||||||
trade_account = create_account_instance(start_time=start_time,
|
trade_account = create_account_instance(
|
||||||
end_time=end_time,
|
start_time=start_time, end_time=end_time, benchmark=benchmark, account=account, pos_type=pos_type
|
||||||
benchmark=benchmark,
|
)
|
||||||
account=account,
|
|
||||||
pos_type=pos_type)
|
|
||||||
|
|
||||||
exchange_kwargs = copy.copy(exchange_kwargs)
|
exchange_kwargs = copy.copy(exchange_kwargs)
|
||||||
if "start_time" not in exchange_kwargs:
|
if "start_time" not in exchange_kwargs:
|
||||||
@@ -154,14 +155,16 @@ def get_strategy_executor(start_time,
|
|||||||
return trade_strategy, trade_executor
|
return trade_strategy, trade_executor
|
||||||
|
|
||||||
|
|
||||||
def backtest(start_time,
|
def backtest(
|
||||||
end_time,
|
start_time,
|
||||||
strategy,
|
end_time,
|
||||||
executor,
|
strategy,
|
||||||
benchmark="SH000300",
|
executor,
|
||||||
account=1e9,
|
benchmark="SH000300",
|
||||||
exchange_kwargs={},
|
account=1e9,
|
||||||
pos_type: str = "Position"):
|
exchange_kwargs={},
|
||||||
|
pos_type: str = "Position",
|
||||||
|
):
|
||||||
|
|
||||||
trade_strategy, trade_executor = get_strategy_executor(
|
trade_strategy, trade_executor = get_strategy_executor(
|
||||||
start_time,
|
start_time,
|
||||||
@@ -178,14 +181,16 @@ def backtest(start_time,
|
|||||||
return report_dict, indicator_dict
|
return report_dict, indicator_dict
|
||||||
|
|
||||||
|
|
||||||
def collect_data(start_time,
|
def collect_data(
|
||||||
end_time,
|
start_time,
|
||||||
strategy,
|
end_time,
|
||||||
executor,
|
strategy,
|
||||||
benchmark="SH000300",
|
executor,
|
||||||
account=1e9,
|
benchmark="SH000300",
|
||||||
exchange_kwargs={},
|
account=1e9,
|
||||||
pos_type: str = "Position"):
|
exchange_kwargs={},
|
||||||
|
pos_type: str = "Position",
|
||||||
|
):
|
||||||
|
|
||||||
trade_strategy, trade_executor = get_strategy_executor(
|
trade_strategy, trade_executor = get_strategy_executor(
|
||||||
start_time,
|
start_time,
|
||||||
|
|||||||
@@ -63,7 +63,9 @@ class AccumulatedInfo:
|
|||||||
|
|
||||||
|
|
||||||
class Account:
|
class Account:
|
||||||
def __init__(self, init_cash: float=1e9, freq: str = "day", benchmark_config: dict = {}, pos_type:str = "Position"):
|
def __init__(
|
||||||
|
self, init_cash: float = 1e9, freq: str = "day", benchmark_config: dict = {}, pos_type: str = "Position"
|
||||||
|
):
|
||||||
self.pos_type = pos_type
|
self.pos_type = pos_type
|
||||||
self.init_vars(init_cash, freq, benchmark_config)
|
self.init_vars(init_cash, freq, benchmark_config)
|
||||||
|
|
||||||
@@ -71,13 +73,13 @@ class Account:
|
|||||||
|
|
||||||
# init cash
|
# init cash
|
||||||
self.init_cash = init_cash
|
self.init_cash = init_cash
|
||||||
self.current: BasePosition = init_instance_by_config({
|
self.current: BasePosition = init_instance_by_config(
|
||||||
'class': self.pos_type,
|
{
|
||||||
'kwargs': {
|
"class": self.pos_type,
|
||||||
"cash": init_cash
|
"kwargs": {"cash": init_cash},
|
||||||
},
|
"module_path": "qlib.backtest.position",
|
||||||
'module_path': "qlib.backtest.position",
|
}
|
||||||
})
|
)
|
||||||
self.accum_info = AccumulatedInfo()
|
self.accum_info = AccumulatedInfo()
|
||||||
self.reset(freq=freq, benchmark_config=benchmark_config, init_report=True)
|
self.reset(freq=freq, benchmark_config=benchmark_config, init_report=True)
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,9 @@ def backtest_loop(start_time, end_time, trade_strategy: BaseStrategy, trade_exec
|
|||||||
return return_value.get("report"), return_value.get("indicator")
|
return return_value.get("report"), return_value.get("indicator")
|
||||||
|
|
||||||
|
|
||||||
def collect_data_loop(start_time, end_time, trade_strategy: BaseStrategy, trade_executor: BaseExecutor, return_value: dict = None):
|
def collect_data_loop(
|
||||||
|
start_time, end_time, trade_strategy: BaseStrategy, trade_executor: BaseExecutor, return_value: dict = None
|
||||||
|
):
|
||||||
"""Generator for collecting the trade decision data for rl training
|
"""Generator for collecting the trade decision data for rl training
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
@@ -68,7 +70,7 @@ def collect_data_loop(start_time, end_time, trade_strategy: BaseStrategy, trade_
|
|||||||
}
|
}
|
||||||
all_indicators = {}
|
all_indicators = {}
|
||||||
for _executor in all_executors:
|
for _executor in all_executors:
|
||||||
key = "{}{}".format( *Freq.parse(_executor.time_per_step))
|
key = "{}{}".format(*Freq.parse(_executor.time_per_step))
|
||||||
all_indicators[key] = _executor.get_trade_indicator().generate_trade_indicators_dataframe()
|
all_indicators[key] = _executor.get_trade_indicator().generate_trade_indicators_dataframe()
|
||||||
all_indicators[key + "_obj"] = _executor.get_trade_indicator()
|
all_indicators[key + "_obj"] = _executor.get_trade_indicator()
|
||||||
return_value.update({"report": all_reports, "indicator": all_indicators})
|
return_value.update({"report": all_reports, "indicator": all_indicators})
|
||||||
|
|||||||
@@ -2,8 +2,10 @@
|
|||||||
# Licensed under the MIT License.
|
# Licensed under the MIT License.
|
||||||
# TODO: rename it with decision.py
|
# TODO: rename it with decision.py
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
# try to fix circular imports when enabling type hints
|
# try to fix circular imports when enabling type hints
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from qlib.strategy.base import BaseStrategy
|
from qlib.strategy.base import BaseStrategy
|
||||||
from qlib.backtest.utils import TradeCalendarManager
|
from qlib.backtest.utils import TradeCalendarManager
|
||||||
@@ -59,6 +61,7 @@ class BaseTradeDecision:
|
|||||||
1. The outer strategy's decision is available at the start of the interval
|
1. The outer strategy's decision is available at the start of the interval
|
||||||
2. Same as `case 1.3`
|
2. Same as `case 1.3`
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, strategy: BaseStrategy):
|
def __init__(self, strategy: BaseStrategy):
|
||||||
"""
|
"""
|
||||||
Parameters
|
Parameters
|
||||||
@@ -125,7 +128,8 @@ class TradeDecisionWO(BaseTradeDecision):
|
|||||||
Trade Decision (W)ith (O)rder.
|
Trade Decision (W)ith (O)rder.
|
||||||
Besides, the time_range is also included.
|
Besides, the time_range is also included.
|
||||||
"""
|
"""
|
||||||
def __init__(self, order_list: List[Order], strategy: BaseStrategy, idx_range: Tuple=None):
|
|
||||||
|
def __init__(self, order_list: List[Order], strategy: BaseStrategy, idx_range: Tuple = None):
|
||||||
super().__init__(strategy)
|
super().__init__(strategy)
|
||||||
self.order_list = order_list
|
self.order_list = order_list
|
||||||
self.idx_range = idx_range
|
self.idx_range = idx_range
|
||||||
@@ -198,8 +202,7 @@ class TradeDecisionWithOrderPool:
|
|||||||
|
|
||||||
class BaseDecisionUpdater:
|
class BaseDecisionUpdater:
|
||||||
def update_decision(self, decision, trade_calendar) -> BaseTradeDecision:
|
def update_decision(self, decision, trade_calendar) -> BaseTradeDecision:
|
||||||
"""[summary]
|
"""
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
decision : BaseTradeDecision
|
decision : BaseTradeDecision
|
||||||
|
|||||||
@@ -15,7 +15,8 @@ class BasePosition:
|
|||||||
The Position want to maintain the position like a dictionary
|
The Position want to maintain the position like a dictionary
|
||||||
Please refer to the `Position` class for the position
|
Please refer to the `Position` class for the position
|
||||||
"""
|
"""
|
||||||
def __init__(self, cash=0., *args, **kwargs) -> None:
|
|
||||||
|
def __init__(self, cash=0.0, *args, **kwargs) -> None:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def skip_update(self) -> bool:
|
def skip_update(self) -> bool:
|
||||||
@@ -46,7 +47,6 @@ class BasePosition:
|
|||||||
"""
|
"""
|
||||||
raise NotImplementedError(f"Please implement the `check_stock` method")
|
raise NotImplementedError(f"Please implement the `check_stock` method")
|
||||||
|
|
||||||
|
|
||||||
def update_order(self, order: Order, trade_val: float, cost: float, trade_price: float):
|
def update_order(self, order: Order, trade_val: float, cost: float, trade_price: float):
|
||||||
"""
|
"""
|
||||||
Parameters
|
Parameters
|
||||||
@@ -86,6 +86,7 @@ class BasePosition:
|
|||||||
the value(money) of all the stock
|
the value(money) of all the stock
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError(f"Please implement the `calculate_stock_value` method")
|
raise NotImplementedError(f"Please implement the `calculate_stock_value` method")
|
||||||
|
|
||||||
def get_stock_list(self) -> List:
|
def get_stock_list(self) -> List:
|
||||||
"""
|
"""
|
||||||
Get the list of stocks in the position.
|
Get the list of stocks in the position.
|
||||||
@@ -140,7 +141,7 @@ class BasePosition:
|
|||||||
"""
|
"""
|
||||||
raise NotImplementedError(f"Please implement the `get_stock_amount_dict` method")
|
raise NotImplementedError(f"Please implement the `get_stock_amount_dict` method")
|
||||||
|
|
||||||
def get_stock_weight_dict(self, only_stock: bool=False) -> Dict:
|
def get_stock_weight_dict(self, only_stock: bool = False) -> Dict:
|
||||||
"""
|
"""
|
||||||
generate stock weight dict {stock_id : value weight of stock in the position}
|
generate stock weight dict {stock_id : value weight of stock in the position}
|
||||||
it is meaningful in the beginning or the end of each trade date
|
it is meaningful in the beginning or the end of each trade date
|
||||||
@@ -399,13 +400,13 @@ class Position(BasePosition):
|
|||||||
self.position["now_account_value"] = now_account_value
|
self.position["now_account_value"] = now_account_value
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class InfPosition(BasePosition):
|
class InfPosition(BasePosition):
|
||||||
"""
|
"""
|
||||||
Position with infinite cash and amount.
|
Position with infinite cash and amount.
|
||||||
|
|
||||||
This is useful for generating random orders.
|
This is useful for generating random orders.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def skip_update(self) -> bool:
|
def skip_update(self) -> bool:
|
||||||
""" Updating state is meaningless for InfPosition """
|
""" Updating state is meaningless for InfPosition """
|
||||||
return True
|
return True
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ from ..tests.config import CSI300_BENCH
|
|||||||
|
|
||||||
|
|
||||||
class Report:
|
class Report:
|
||||||
'''
|
"""
|
||||||
Motivation:
|
Motivation:
|
||||||
Report is for supporting portfolio related metrics.
|
Report is for supporting portfolio related metrics.
|
||||||
|
|
||||||
@@ -26,7 +26,8 @@ class Report:
|
|||||||
daily report of the account
|
daily report of the account
|
||||||
contain those followings: returns, costs turnovers, accounts, cash, bench, value
|
contain those followings: returns, costs turnovers, accounts, cash, bench, value
|
||||||
update report
|
update report
|
||||||
'''
|
"""
|
||||||
|
|
||||||
def __init__(self, freq: str = "day", benchmark_config: dict = {}):
|
def __init__(self, freq: str = "day", benchmark_config: dict = {}):
|
||||||
"""
|
"""
|
||||||
Parameters
|
Parameters
|
||||||
|
|||||||
@@ -140,7 +140,6 @@ class BaseInfrastructure:
|
|||||||
self.reset_infra(**infra_dict)
|
self.reset_infra(**infra_dict)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class CommonInfrastructure(BaseInfrastructure):
|
class CommonInfrastructure(BaseInfrastructure):
|
||||||
def get_support_infra(self):
|
def get_support_infra(self):
|
||||||
return ["trade_account", "trade_exchange"]
|
return ["trade_account", "trade_exchange"]
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ class TopkDropoutStrategy(ModelStrategy):
|
|||||||
# TODO:
|
# TODO:
|
||||||
# 1. Supporting leverage the get_range_limit result from the decision
|
# 1. Supporting leverage the get_range_limit result from the decision
|
||||||
# 2. Supporting alter_outer_trade_decision
|
# 2. Supporting alter_outer_trade_decision
|
||||||
|
# 3. Supporting checking the availability of trade decision
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
model,
|
model,
|
||||||
@@ -104,7 +105,7 @@ class TopkDropoutStrategy(ModelStrategy):
|
|||||||
pred_start_time, pred_end_time = self.trade_calendar.get_step_time(trade_step, shift=1)
|
pred_start_time, pred_end_time = self.trade_calendar.get_step_time(trade_step, shift=1)
|
||||||
pred_score = resam_ts_data(self.pred_scores, start_time=pred_start_time, end_time=pred_end_time, method="last")
|
pred_score = resam_ts_data(self.pred_scores, start_time=pred_start_time, end_time=pred_end_time, method="last")
|
||||||
if pred_score is None:
|
if pred_score is None:
|
||||||
return []
|
return TradeDecisionWO([], self)
|
||||||
if self.only_tradable:
|
if self.only_tradable:
|
||||||
# If The strategy only consider tradable stock when make decision
|
# If The strategy only consider tradable stock when make decision
|
||||||
# It needs following actions to filter stocks
|
# It needs following actions to filter stocks
|
||||||
@@ -256,6 +257,7 @@ class WeightStrategyBase(ModelStrategy):
|
|||||||
# TODO:
|
# TODO:
|
||||||
# 1. Supporting leverage the get_range_limit result from the decision
|
# 1. Supporting leverage the get_range_limit result from the decision
|
||||||
# 2. Supporting alter_outer_trade_decision
|
# 2. Supporting alter_outer_trade_decision
|
||||||
|
# 3. Supporting checking the availability of trade decision
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
model,
|
model,
|
||||||
@@ -332,9 +334,9 @@ class WeightStrategyBase(ModelStrategy):
|
|||||||
pred_start_time, pred_end_time = self.trade_calendar.get_step_time(trade_step, shift=1)
|
pred_start_time, pred_end_time = self.trade_calendar.get_step_time(trade_step, shift=1)
|
||||||
pred_score = resam_ts_data(self.pred_scores, start_time=pred_start_time, end_time=pred_end_time, method="last")
|
pred_score = resam_ts_data(self.pred_scores, start_time=pred_start_time, end_time=pred_end_time, method="last")
|
||||||
if pred_score is None:
|
if pred_score is None:
|
||||||
return []
|
return TradeDecisionWO([], self)
|
||||||
current_temp = copy.deepcopy(self.trade_position)
|
current_temp = copy.deepcopy(self.trade_position)
|
||||||
assert(isinstance(current_temp, Position)) # Avoid InfPosition
|
assert isinstance(current_temp, Position) # Avoid InfPosition
|
||||||
|
|
||||||
target_weight_position = self.generate_target_weight_position(
|
target_weight_position = self.generate_target_weight_position(
|
||||||
score=pred_score, current=current_temp, trade_start_time=trade_start_time, trade_end_time=trade_end_time
|
score=pred_score, current=current_temp, trade_start_time=trade_start_time, trade_end_time=trade_end_time
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ class TWAPStrategy(BaseStrategy):
|
|||||||
trade_step = self.trade_calendar.get_trade_step()
|
trade_step = self.trade_calendar.get_trade_step()
|
||||||
# get the total count of trading step
|
# get the total count of trading step
|
||||||
start_idx, end_idx = get_start_end_idx(self, self.outer_trade_decision)
|
start_idx, end_idx = get_start_end_idx(self, self.outer_trade_decision)
|
||||||
trade_len = end_idx - start_idx + 1
|
trade_len = end_idx - start_idx + 1
|
||||||
|
|
||||||
if trade_step < start_idx:
|
if trade_step < start_idx:
|
||||||
# It is not time to start trading
|
# It is not time to start trading
|
||||||
@@ -137,12 +137,16 @@ class TWAPStrategy(BaseStrategy):
|
|||||||
# calculate the amount of one part, ceil the amount
|
# calculate the amount of one part, ceil the amount
|
||||||
# floor((trade_unit_cnt + trade_len - rel_trade_step) / (trade_len - rel_trade_step + 1)) == ceil(trade_unit_cnt / (trade_len - rel_trade_step + 1))
|
# floor((trade_unit_cnt + trade_len - rel_trade_step) / (trade_len - rel_trade_step + 1)) == ceil(trade_unit_cnt / (trade_len - rel_trade_step + 1))
|
||||||
_order_amount = (
|
_order_amount = (
|
||||||
(trade_unit_cnt + trade_len - rel_trade_step - 1) // (trade_len - rel_trade_step) * _amount_trade_unit
|
(trade_unit_cnt + trade_len - rel_trade_step - 1)
|
||||||
|
// (trade_len - rel_trade_step)
|
||||||
|
* _amount_trade_unit
|
||||||
)
|
)
|
||||||
|
|
||||||
if order.direction == order.SELL:
|
if order.direction == order.SELL:
|
||||||
# sell all amount at last
|
# sell all amount at last
|
||||||
if self.trade_amount[order.stock_id] > 1e-5 and (_order_amount < 1e-5 or rel_trade_step == trade_len - 1):
|
if self.trade_amount[order.stock_id] > 1e-5 and (
|
||||||
|
_order_amount < 1e-5 or rel_trade_step == trade_len - 1
|
||||||
|
):
|
||||||
_order_amount = self.trade_amount[order.stock_id]
|
_order_amount = self.trade_amount[order.stock_id]
|
||||||
|
|
||||||
_order_amount = min(_order_amount, self.trade_amount[order.stock_id])
|
_order_amount = min(_order_amount, self.trade_amount[order.stock_id])
|
||||||
@@ -173,6 +177,7 @@ class SBBStrategyBase(BaseStrategy):
|
|||||||
# TODO:
|
# TODO:
|
||||||
# 1. Supporting leverage the get_range_limit result from the decision
|
# 1. Supporting leverage the get_range_limit result from the decision
|
||||||
# 2. Supporting alter_outer_trade_decision
|
# 2. Supporting alter_outer_trade_decision
|
||||||
|
# 3. Supporting checking the availability of trade decision
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
@@ -225,8 +230,7 @@ class SBBStrategyBase(BaseStrategy):
|
|||||||
self.trade_trend = {}
|
self.trade_trend = {}
|
||||||
self.trade_amount = {}
|
self.trade_amount = {}
|
||||||
# init the trade amount of order and predicted trade trend
|
# init the trade amount of order and predicted trade trend
|
||||||
outer_order_generator = outer_trade_decision.generator()
|
for order in outer_trade_decision.get_decision():
|
||||||
for order in outer_order_generator:
|
|
||||||
self.trade_trend[order.stock_id] = self.TREND_MID
|
self.trade_trend[order.stock_id] = self.TREND_MID
|
||||||
self.trade_amount[order.stock_id] = order.amount
|
self.trade_amount[order.stock_id] = order.amount
|
||||||
|
|
||||||
@@ -248,8 +252,7 @@ class SBBStrategyBase(BaseStrategy):
|
|||||||
pred_start_time, pred_end_time = self.trade_calendar.get_step_time(trade_step, shift=1)
|
pred_start_time, pred_end_time = self.trade_calendar.get_step_time(trade_step, shift=1)
|
||||||
order_list = []
|
order_list = []
|
||||||
# for each order in in self.outer_trade_decision
|
# for each order in in self.outer_trade_decision
|
||||||
outer_order_generator = self.outer_trade_decision.generator(only_enable=True)
|
for order in self.outer_trade_decision.get_decision():
|
||||||
for order in outer_order_generator:
|
|
||||||
# get the price trend
|
# get the price trend
|
||||||
if trade_step % 2 == 0:
|
if trade_step % 2 == 0:
|
||||||
# in the first of two adjacent bars, predict the price trend
|
# in the first of two adjacent bars, predict the price trend
|
||||||
@@ -379,9 +382,11 @@ class SBBStrategyEMA(SBBStrategyBase):
|
|||||||
"""
|
"""
|
||||||
(S)elect the (B)etter one among every two adjacent trading (B)ars to sell or buy with (EMA) signal.
|
(S)elect the (B)etter one among every two adjacent trading (B)ars to sell or buy with (EMA) signal.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# TODO:
|
# TODO:
|
||||||
# 1. Supporting leverage the get_range_limit result from the decision
|
# 1. Supporting leverage the get_range_limit result from the decision
|
||||||
# 2. Supporting alter_outer_trade_decision
|
# 2. Supporting alter_outer_trade_decision
|
||||||
|
# 3. Supporting checking the availability of trade decision
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
@@ -463,6 +468,7 @@ class ACStrategy(BaseStrategy):
|
|||||||
# TODO:
|
# TODO:
|
||||||
# 1. Supporting leverage the get_range_limit result from the decision
|
# 1. Supporting leverage the get_range_limit result from the decision
|
||||||
# 2. Supporting alter_outer_trade_decision
|
# 2. Supporting alter_outer_trade_decision
|
||||||
|
# 3. Supporting checking the availability of trade decision
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
lamb: float = 1e-6,
|
lamb: float = 1e-6,
|
||||||
@@ -555,8 +561,7 @@ class ACStrategy(BaseStrategy):
|
|||||||
if outer_trade_decision is not None:
|
if outer_trade_decision is not None:
|
||||||
self.trade_amount = {}
|
self.trade_amount = {}
|
||||||
# init the trade amount of order and predicted trade trend
|
# init the trade amount of order and predicted trade trend
|
||||||
outer_order_generator = outer_trade_decision.generator()
|
for order in outer_trade_decision.get_decision():
|
||||||
for order in outer_order_generator:
|
|
||||||
self.trade_amount[order.stock_id] = order.amount
|
self.trade_amount[order.stock_id] = order.amount
|
||||||
|
|
||||||
def generate_trade_decision(self, execute_result=None):
|
def generate_trade_decision(self, execute_result=None):
|
||||||
@@ -564,8 +569,6 @@ class ACStrategy(BaseStrategy):
|
|||||||
trade_step = self.trade_calendar.get_trade_step()
|
trade_step = self.trade_calendar.get_trade_step()
|
||||||
# get the total count of trading step
|
# get the total count of trading step
|
||||||
trade_len = self.trade_calendar.get_trade_len()
|
trade_len = self.trade_calendar.get_trade_len()
|
||||||
# update outer trade decision
|
|
||||||
self.outer_trade_decision.update(self.trade_calendar)
|
|
||||||
|
|
||||||
# update the order amount
|
# update the order amount
|
||||||
if execute_result is not None:
|
if execute_result is not None:
|
||||||
@@ -575,8 +578,7 @@ class ACStrategy(BaseStrategy):
|
|||||||
trade_start_time, trade_end_time = self.trade_calendar.get_step_time(trade_step)
|
trade_start_time, trade_end_time = self.trade_calendar.get_step_time(trade_step)
|
||||||
pred_start_time, pred_end_time = self.trade_calendar.get_step_time(trade_step, shift=1)
|
pred_start_time, pred_end_time = self.trade_calendar.get_step_time(trade_step, shift=1)
|
||||||
order_list = []
|
order_list = []
|
||||||
outer_order_generator = self.outer_trade_decision.generator(only_enable=True)
|
for order in self.outer_trade_decision.get_decision():
|
||||||
for order in outer_order_generator:
|
|
||||||
# if not tradable, continue
|
# if not tradable, continue
|
||||||
if not self.trade_exchange.is_stock_tradable(
|
if not self.trade_exchange.is_stock_tradable(
|
||||||
stock_id=order.stock_id, start_time=trade_start_time, end_time=trade_end_time
|
stock_id=order.stock_id, start_time=trade_start_time, end_time=trade_end_time
|
||||||
@@ -638,14 +640,16 @@ class ACStrategy(BaseStrategy):
|
|||||||
|
|
||||||
|
|
||||||
class RandomOrderStrategy(BaseStrategy):
|
class RandomOrderStrategy(BaseStrategy):
|
||||||
|
def __init__(
|
||||||
def __init__(self,
|
self,
|
||||||
index_range: Tuple[int, int], # The range is closed on both left and right.
|
index_range: Tuple[int, int], # The range is closed on both left and right.
|
||||||
sample_ratio: float = 1.,
|
sample_ratio: float = 1.0,
|
||||||
volume_ratio: float = 0.01,
|
volume_ratio: float = 0.01,
|
||||||
market: str = "all",
|
market: str = "all",
|
||||||
*args,
|
direction: int = Order.BUY,
|
||||||
**kwargs):
|
*args,
|
||||||
|
**kwargs,
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
@@ -667,9 +671,12 @@ class RandomOrderStrategy(BaseStrategy):
|
|||||||
self.sample_ratio = sample_ratio
|
self.sample_ratio = sample_ratio
|
||||||
self.volume_ratio = volume_ratio
|
self.volume_ratio = volume_ratio
|
||||||
self.market = market
|
self.market = market
|
||||||
|
self.direction = direction
|
||||||
exch: Exchange = self.common_infra.get("trade_exchange")
|
exch: Exchange = self.common_infra.get("trade_exchange")
|
||||||
# TODO: this can't be online
|
# TODO: this can't be online
|
||||||
self.volume = D.features(D.instruments(market), ["Mean(Ref($volume, 1), 10)"], start_time=exch.start_time, end_time=exch.end_time)
|
self.volume = D.features(
|
||||||
|
D.instruments(market), ["Mean(Ref($volume, 1), 10)"], start_time=exch.start_time, end_time=exch.end_time
|
||||||
|
)
|
||||||
self.volume_df = self.volume.iloc[:, 0].unstack()
|
self.volume_df = self.volume.iloc[:, 0].unstack()
|
||||||
|
|
||||||
def generate_trade_decision(self, execute_result=None):
|
def generate_trade_decision(self, execute_result=None):
|
||||||
@@ -677,15 +684,15 @@ class RandomOrderStrategy(BaseStrategy):
|
|||||||
step_time_start, step_time_end = self.trade_calendar.get_step_time(trade_step)
|
step_time_start, step_time_end = self.trade_calendar.get_step_time(trade_step)
|
||||||
|
|
||||||
order_list = []
|
order_list = []
|
||||||
for direction in Order.SELL, Order.BUY:
|
if step_time_start in self.volume_df:
|
||||||
if step_time_start in self.volume_df:
|
for stock_id, volume in self.volume_df[step_time_start].dropna().sample(frac=self.sample_ratio).items():
|
||||||
for stock_id, volume in self.volume_df[step_time_start].dropna().sample(frac=self.sample_ratio).items():
|
order_list.append(
|
||||||
order_list.append(
|
self.common_infra.get("trade_exchange").create_order(
|
||||||
self.common_infra.get("trade_exchange").create_order(
|
code=stock_id,
|
||||||
code=stock_id,
|
amount=volume * self.volume_ratio,
|
||||||
amount=volume * self.volume_ratio,
|
start_time=step_time_start,
|
||||||
start_time=step_time_start,
|
end_time=step_time_end,
|
||||||
end_time=step_time_end,
|
direction=self.direction,
|
||||||
direction=direction, # 1 for buy
|
)
|
||||||
))
|
)
|
||||||
return TradeDecisionWO(order_list, self, self.index_range)
|
return TradeDecisionWO(order_list, self, self.index_range)
|
||||||
|
|||||||
@@ -213,7 +213,7 @@ class InstrumentProvider(abc.ABC, ProviderBackendMixin):
|
|||||||
self.backend = kwargs.get("backend", {})
|
self.backend = kwargs.get("backend", {})
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def instruments(market: Union[List, str]="all", filter_pipe: Union[List, None]=None):
|
def instruments(market: Union[List, str] = "all", filter_pipe: Union[List, None] = None):
|
||||||
"""Get the general config dictionary for a base market adding several dynamic filters.
|
"""Get the general config dictionary for a base market adding several dynamic filters.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
|
|||||||
@@ -85,7 +85,9 @@ class BaseStrategy:
|
|||||||
"""
|
"""
|
||||||
raise NotImplementedError("generate_trade_decision is not implemented!")
|
raise NotImplementedError("generate_trade_decision is not implemented!")
|
||||||
|
|
||||||
def update_trade_decision(self, trade_decision: BaseTradeDecision, trade_calendar: TradeCalendarManager) -> Union[BaseTradeDecision, None]:
|
def update_trade_decision(
|
||||||
|
self, trade_decision: BaseTradeDecision, trade_calendar: TradeCalendarManager
|
||||||
|
) -> Union[BaseTradeDecision, None]:
|
||||||
"""
|
"""
|
||||||
update trade decision in each step of inner execution, this method enable all order
|
update trade decision in each step of inner execution, this method enable all order
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ from . import lazy_sort_index
|
|||||||
from ..config import C
|
from ..config import C
|
||||||
from .time import Freq, cal_sam_minute
|
from .time import Freq, cal_sam_minute
|
||||||
|
|
||||||
|
|
||||||
def resam_calendar(calendar_raw: np.ndarray, freq_raw: str, freq_sam: str) -> np.ndarray:
|
def resam_calendar(calendar_raw: np.ndarray, freq_raw: str, freq_sam: str) -> np.ndarray:
|
||||||
"""
|
"""
|
||||||
Resample the calendar with frequency freq_raw into the calendar with frequency freq_sam
|
Resample the calendar with frequency freq_raw into the calendar with frequency freq_sam
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import functools
|
|||||||
|
|
||||||
|
|
||||||
@functools.lru_cache(maxsize=240)
|
@functools.lru_cache(maxsize=240)
|
||||||
def get_min_cal(shift: int=0) -> List[time]:
|
def get_min_cal(shift: int = 0) -> List[time]:
|
||||||
"""
|
"""
|
||||||
get the minute level calendar in day period
|
get the minute level calendar in day period
|
||||||
|
|
||||||
@@ -30,8 +30,9 @@ def get_min_cal(shift: int=0) -> List[time]:
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
cal = []
|
cal = []
|
||||||
for ts in list(pd.date_range("9:30", "11:29", freq="1min") - pd.Timedelta(minutes=shift)) +\
|
for ts in list(pd.date_range("9:30", "11:29", freq="1min") - pd.Timedelta(minutes=shift)) + list(
|
||||||
list(pd.date_range("13:00", "14:59", freq="1min") - pd.Timedelta(minutes=shift)):
|
pd.date_range("13:00", "14:59", freq="1min") - pd.Timedelta(minutes=shift)
|
||||||
|
):
|
||||||
cal.append(ts.time())
|
cal.append(ts.time())
|
||||||
return cal
|
return cal
|
||||||
|
|
||||||
@@ -115,7 +116,7 @@ def get_day_min_idx_range(start: str, end: str, freq: str) -> Tuple[int, int]:
|
|||||||
start = pd.Timestamp(start).time()
|
start = pd.Timestamp(start).time()
|
||||||
end = pd.Timestamp(end).time()
|
end = pd.Timestamp(end).time()
|
||||||
freq = Freq(freq)
|
freq = Freq(freq)
|
||||||
in_day_cal = Freq.MIN_CAL[::freq.count]
|
in_day_cal = Freq.MIN_CAL[:: freq.count]
|
||||||
left_idx = bisect.bisect_left(in_day_cal, start)
|
left_idx = bisect.bisect_left(in_day_cal, start)
|
||||||
right_idx = bisect.bisect_right(in_day_cal, end) - 1
|
right_idx = bisect.bisect_right(in_day_cal, end) - 1
|
||||||
return left_idx, right_idx
|
return left_idx, right_idx
|
||||||
@@ -141,15 +142,19 @@ def cal_sam_minute(x: pd.Timestamp, sam_minutes: int) -> pd.Timestamp:
|
|||||||
"""
|
"""
|
||||||
cal = get_min_cal(C.min_data_shift)[::sam_minutes]
|
cal = get_min_cal(C.min_data_shift)[::sam_minutes]
|
||||||
idx = bisect.bisect_right(cal, x.time()) - 1
|
idx = bisect.bisect_right(cal, x.time()) - 1
|
||||||
date, new_time = x.date(), cal[idx]
|
date, new_time = x.date(), cal[idx]
|
||||||
return pd.Timestamp(
|
return pd.Timestamp(
|
||||||
datetime(date.year,
|
datetime(
|
||||||
month=date.month,
|
date.year,
|
||||||
day=date.day,
|
month=date.month,
|
||||||
hour=new_time.hour,
|
day=date.day,
|
||||||
minute=new_time.minute,
|
hour=new_time.hour,
|
||||||
second=new_time.second,
|
minute=new_time.minute,
|
||||||
microsecond=new_time.microsecond))
|
second=new_time.second,
|
||||||
|
microsecond=new_time.microsecond,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
print(get_day_min_idx_range("8:30", "14:59", "10min"))
|
print(get_day_min_idx_range("8:30", "14:59", "10min"))
|
||||||
|
|||||||
Reference in New Issue
Block a user