mirror of
https://github.com/microsoft/qlib.git
synced 2026-07-07 04:50:56 +08:00
RL backtest pipeline on 5-min data (#1417)
* Workflow runnable * CI * Slight changes to make the workflow runnable. The changes of handler/provider should be reverted before merging. * Train experiment successful * Refine handler & provider * test passed * Ready to test on server * Minor * Test passed * TWAP training * Add PPOReward * Add a FIXME * Refine PPO reward according to PR comments * Minor * Resolve PR comments * CI issues * CI issues * CI issues
This commit is contained in:
@@ -40,8 +40,8 @@ def get_exchange(
|
||||
open_cost: float = 0.0015,
|
||||
close_cost: float = 0.0025,
|
||||
min_cost: float = 5.0,
|
||||
limit_threshold: Union[Tuple[str, str], float, None] = None,
|
||||
deal_price: Union[str, Tuple[str, str], List[str]] = None,
|
||||
limit_threshold: Union[Tuple[str, str], float, None] | None = None,
|
||||
deal_price: Union[str, Tuple[str, str], List[str]] | None = None,
|
||||
**kwargs: Any,
|
||||
) -> Exchange:
|
||||
"""get_exchange
|
||||
@@ -284,7 +284,7 @@ def collect_data(
|
||||
account: Union[float, int, dict] = 1e9,
|
||||
exchange_kwargs: dict = {},
|
||||
pos_type: str = "Position",
|
||||
return_value: dict = None,
|
||||
return_value: dict | None = None,
|
||||
) -> Generator[object, None, None]:
|
||||
"""initialize the strategy and executor, then collect the trade decision data for rl training
|
||||
|
||||
|
||||
@@ -152,7 +152,9 @@ class Account:
|
||||
# trading related metrics(e.g. high-frequency trading)
|
||||
self.indicator = Indicator()
|
||||
|
||||
def reset(self, freq: str = None, benchmark_config: dict = None, port_metr_enabled: bool = None) -> None:
|
||||
def reset(
|
||||
self, freq: str | None = None, benchmark_config: dict | None = None, port_metr_enabled: bool | None = None
|
||||
) -> None:
|
||||
"""reset freq and report of account
|
||||
|
||||
Parameters
|
||||
|
||||
@@ -55,7 +55,7 @@ def collect_data_loop(
|
||||
end_time: Union[pd.Timestamp, str],
|
||||
trade_strategy: BaseStrategy,
|
||||
trade_executor: BaseExecutor,
|
||||
return_value: dict = None,
|
||||
return_value: dict | None = None,
|
||||
) -> Generator[BaseTradeDecision, Optional[BaseTradeDecision], None]:
|
||||
"""Generator for collecting the trade decision data for rl training
|
||||
|
||||
|
||||
@@ -254,7 +254,7 @@ class IdxTradeRange(TradeRange):
|
||||
self._start_idx = start_idx
|
||||
self._end_idx = end_idx
|
||||
|
||||
def __call__(self, trade_calendar: TradeCalendarManager = None) -> Tuple[int, int]:
|
||||
def __call__(self, trade_calendar: TradeCalendarManager | None = None) -> Tuple[int, int]:
|
||||
return self._start_idx, self._end_idx
|
||||
|
||||
def clip_time_range(self, start_time: pd.Timestamp, end_time: pd.Timestamp) -> Tuple[pd.Timestamp, pd.Timestamp]:
|
||||
@@ -315,7 +315,7 @@ class BaseTradeDecision(Generic[DecisionType]):
|
||||
2. Same as `case 1.3`
|
||||
"""
|
||||
|
||||
def __init__(self, strategy: BaseStrategy, trade_range: Union[Tuple[int, int], TradeRange] = None) -> None:
|
||||
def __init__(self, strategy: BaseStrategy, trade_range: Union[Tuple[int, int], TradeRange, None] = None) -> None:
|
||||
"""
|
||||
Parameters
|
||||
----------
|
||||
@@ -554,7 +554,7 @@ class TradeDecisionWO(BaseTradeDecision[Order]):
|
||||
self,
|
||||
order_list: List[Order],
|
||||
strategy: BaseStrategy,
|
||||
trade_range: Union[Tuple[int, int], TradeRange] = None,
|
||||
trade_range: Union[Tuple[int, int], TradeRange, None] = None,
|
||||
) -> None:
|
||||
super().__init__(strategy, trade_range=trade_range)
|
||||
self.order_list = cast(List[Order], order_list)
|
||||
|
||||
@@ -41,10 +41,10 @@ class Exchange:
|
||||
start_time: Union[pd.Timestamp, str] = None,
|
||||
end_time: Union[pd.Timestamp, str] = None,
|
||||
codes: Union[list, str] = "all",
|
||||
deal_price: Union[str, Tuple[str, str], List[str]] = None,
|
||||
deal_price: Union[str, Tuple[str, str], List[str], None] = None,
|
||||
subscribe_fields: list = [],
|
||||
limit_threshold: Union[Tuple[str, str], float, None] = None,
|
||||
volume_threshold: Union[tuple, dict] = None,
|
||||
volume_threshold: Union[tuple, dict, None] = None,
|
||||
open_cost: float = 0.0015,
|
||||
close_cost: float = 0.0025,
|
||||
min_cost: float = 5.0,
|
||||
@@ -340,7 +340,7 @@ class Exchange:
|
||||
stock_id: str,
|
||||
start_time: pd.Timestamp,
|
||||
end_time: pd.Timestamp,
|
||||
direction: int = None,
|
||||
direction: int | None = None,
|
||||
) -> bool:
|
||||
"""
|
||||
Parameters
|
||||
@@ -406,7 +406,7 @@ class Exchange:
|
||||
stock_id: str,
|
||||
start_time: pd.Timestamp,
|
||||
end_time: pd.Timestamp,
|
||||
direction: int = None,
|
||||
direction: int | None = None,
|
||||
) -> bool:
|
||||
# check if stock can be traded
|
||||
return not (
|
||||
@@ -421,8 +421,8 @@ class Exchange:
|
||||
def deal_order(
|
||||
self,
|
||||
order: Order,
|
||||
trade_account: Account = None,
|
||||
position: BasePosition = None,
|
||||
trade_account: Account | None = None,
|
||||
position: BasePosition | None = None,
|
||||
dealt_order_amount: Dict[str, float] = defaultdict(float),
|
||||
) -> Tuple[float, float, float]:
|
||||
"""
|
||||
@@ -586,7 +586,7 @@ class Exchange:
|
||||
)
|
||||
return amount_dict
|
||||
|
||||
def get_real_deal_amount(self, current_amount: float, target_amount: float, factor: float = None) -> float:
|
||||
def get_real_deal_amount(self, current_amount: float, target_amount: float, factor: float | None = None) -> float:
|
||||
"""
|
||||
Calculate the real adjust deal amount when considering the trading unit
|
||||
:param current_amount:
|
||||
@@ -712,8 +712,8 @@ class Exchange:
|
||||
|
||||
def _get_factor_or_raise_error(
|
||||
self,
|
||||
factor: float = None,
|
||||
stock_id: str = None,
|
||||
factor: float | None = None,
|
||||
stock_id: str | None = None,
|
||||
start_time: pd.Timestamp = None,
|
||||
end_time: pd.Timestamp = None,
|
||||
) -> float:
|
||||
@@ -728,8 +728,8 @@ class Exchange:
|
||||
|
||||
def get_amount_of_trade_unit(
|
||||
self,
|
||||
factor: float = None,
|
||||
stock_id: str = None,
|
||||
factor: float | None = None,
|
||||
stock_id: str | None = None,
|
||||
start_time: pd.Timestamp = None,
|
||||
end_time: pd.Timestamp = None,
|
||||
) -> Optional[float]:
|
||||
@@ -762,8 +762,8 @@ class Exchange:
|
||||
def round_amount_by_trade_unit(
|
||||
self,
|
||||
deal_amount: float,
|
||||
factor: float = None,
|
||||
stock_id: str = None,
|
||||
factor: float | None = None,
|
||||
stock_id: str | None = None,
|
||||
start_time: pd.Timestamp = None,
|
||||
end_time: pd.Timestamp = None,
|
||||
) -> float:
|
||||
|
||||
@@ -31,8 +31,8 @@ class BaseExecutor:
|
||||
generate_portfolio_metrics: bool = False,
|
||||
verbose: bool = False,
|
||||
track_data: bool = False,
|
||||
trade_exchange: Exchange = None,
|
||||
common_infra: CommonInfrastructure = None,
|
||||
trade_exchange: Exchange | None = None,
|
||||
common_infra: CommonInfrastructure | None = None,
|
||||
settle_type: str = BasePosition.ST_NO,
|
||||
**kwargs: Any,
|
||||
) -> None:
|
||||
@@ -161,7 +161,7 @@ class BaseExecutor:
|
||||
"""
|
||||
return self.level_infra.get("trade_calendar")
|
||||
|
||||
def reset(self, common_infra: CommonInfrastructure = None, **kwargs: Any) -> None:
|
||||
def reset(self, common_infra: CommonInfrastructure | None = None, **kwargs: Any) -> None:
|
||||
"""
|
||||
- reset `start_time` and `end_time`, used in trade calendar
|
||||
- reset `common_infra`, used to reset `trade_account`, `trade_exchange`, .etc
|
||||
@@ -227,7 +227,7 @@ class BaseExecutor:
|
||||
def collect_data(
|
||||
self,
|
||||
trade_decision: BaseTradeDecision,
|
||||
return_value: dict = None,
|
||||
return_value: dict | None = None,
|
||||
level: int = 0,
|
||||
) -> Generator[Any, Any, List[object]]:
|
||||
"""Generator for collecting the trade decision data for rl training
|
||||
@@ -327,7 +327,7 @@ class NestedExecutor(BaseExecutor):
|
||||
track_data: bool = False,
|
||||
skip_empty_decision: bool = True,
|
||||
align_range_limit: bool = True,
|
||||
common_infra: CommonInfrastructure = None,
|
||||
common_infra: CommonInfrastructure | None = None,
|
||||
**kwargs: Any,
|
||||
) -> None:
|
||||
"""
|
||||
@@ -534,7 +534,7 @@ class SimulatorExecutor(BaseExecutor):
|
||||
generate_portfolio_metrics: bool = False,
|
||||
verbose: bool = False,
|
||||
track_data: bool = False,
|
||||
common_infra: CommonInfrastructure = None,
|
||||
common_infra: CommonInfrastructure | None = None,
|
||||
trade_type: str = TT_SERIAL,
|
||||
**kwargs: Any,
|
||||
) -> None:
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import timedelta
|
||||
from typing import Any, Dict, List, Union
|
||||
@@ -320,7 +321,7 @@ class Position(BasePosition):
|
||||
self.position[stock]["price"] = price_dict[stock]
|
||||
self.position["now_account_value"] = self.calculate_value()
|
||||
|
||||
def _init_stock(self, stock_id: str, amount: float, price: float = None) -> None:
|
||||
def _init_stock(self, stock_id: str, amount: float, price: float | None = None) -> None:
|
||||
"""
|
||||
initialization the stock in current position
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import pathlib
|
||||
from collections import OrderedDict
|
||||
@@ -86,7 +87,7 @@ class PortfolioMetrics:
|
||||
self.benches: dict = OrderedDict()
|
||||
self.latest_pm_time: Optional[pd.TimeStamp] = None
|
||||
|
||||
def init_bench(self, freq: str = None, benchmark_config: dict = None) -> None:
|
||||
def init_bench(self, freq: str | None = None, benchmark_config: dict | None = None) -> None:
|
||||
if freq is not None:
|
||||
self.freq = freq
|
||||
self.benchmark_config = benchmark_config
|
||||
@@ -149,15 +150,15 @@ class PortfolioMetrics:
|
||||
self,
|
||||
trade_start_time: Union[str, pd.Timestamp] = None,
|
||||
trade_end_time: Union[str, pd.Timestamp] = None,
|
||||
account_value: float = None,
|
||||
cash: float = None,
|
||||
return_rate: float = None,
|
||||
total_turnover: float = None,
|
||||
turnover_rate: float = None,
|
||||
total_cost: float = None,
|
||||
cost_rate: float = None,
|
||||
stock_value: float = None,
|
||||
bench_value: float = None,
|
||||
account_value: float | None = None,
|
||||
cash: float | None = None,
|
||||
return_rate: float | None = None,
|
||||
total_turnover: float | None = None,
|
||||
turnover_rate: float | None = None,
|
||||
total_cost: float | None = None,
|
||||
cost_rate: float | None = None,
|
||||
stock_value: float | None = None,
|
||||
bench_value: float | None = None,
|
||||
) -> None:
|
||||
# check data
|
||||
if None in [
|
||||
|
||||
@@ -31,7 +31,7 @@ class TradeCalendarManager:
|
||||
freq: str,
|
||||
start_time: Union[str, pd.Timestamp] = None,
|
||||
end_time: Union[str, pd.Timestamp] = None,
|
||||
level_infra: LevelInfrastructure = None,
|
||||
level_infra: LevelInfrastructure | None = None,
|
||||
) -> None:
|
||||
"""
|
||||
Parameters
|
||||
@@ -99,7 +99,7 @@ class TradeCalendarManager:
|
||||
def get_trade_step(self) -> int:
|
||||
return self.trade_step
|
||||
|
||||
def get_step_time(self, trade_step: int = None, shift: int = 0) -> Tuple[pd.Timestamp, pd.Timestamp]:
|
||||
def get_step_time(self, trade_step: int | None = None, shift: int = 0) -> Tuple[pd.Timestamp, pd.Timestamp]:
|
||||
"""
|
||||
Get the left and right endpoints of the trade_step'th trading interval
|
||||
|
||||
|
||||
Reference in New Issue
Block a user