1
0
mirror of https://github.com/microsoft/qlib.git synced 2026-07-07 21:11:50 +08:00

Qlib simulator refinement (redo of PR 1244) (#1262)

* Use dict-like configuration

* Rename from_neutrader to integration

* SAOE strategy

* Optimize file structure

* Optimize code

* Format code

* create_state_maintainer_recursive

* Remove explicit time_per_step

* CI test passed

* Resolve PR comments

* Pass all CI

* Minor test issue

* Refine SAOE adapter logic

* Minor bugfix

* Cherry pick updates

* Resolve PR comments

* CI issues

* Refine adapter & saoe_data logic

* Resolve PR comments

* Resolve PR comments

* Rename ONE_SEC to EPS_T; complete backtest loop

* CI issue

* Resolve Yuge's PR comments
This commit is contained in:
Huoran Li
2022-08-24 14:09:45 +08:00
committed by GitHub
parent e78fe48a26
commit 1d65d28b28
26 changed files with 1009 additions and 772 deletions

View File

@@ -345,4 +345,4 @@ def format_decisions(
return res
__all__ = ["Order", "backtest"]
__all__ = ["Order", "backtest", "get_strategy_executor"]

View File

@@ -83,7 +83,9 @@ def collect_data_loop(
while not trade_executor.finished():
_trade_decision: BaseTradeDecision = trade_strategy.generate_trade_decision(_execute_result)
_execute_result = yield from trade_executor.collect_data(_trade_decision, level=0)
trade_strategy.post_exe_step(_execute_result)
bar.update(1)
trade_strategy.post_upper_level_exe_step()
if return_value is not None:
all_executors = trade_executor.get_all_executors()

View File

@@ -135,6 +135,21 @@ class Order:
else:
raise NotImplementedError(f"This type of input is not supported")
@property
def key_by_day(self) -> tuple:
"""A hashable & unique key to identify this order, under the granularity in day."""
return self.stock_id, self.date, self.direction
@property
def key(self) -> tuple:
"""A hashable & unique key to identify this order."""
return self.stock_id, self.start_time, self.end_time, self.direction
@property
def date(self) -> pd.Timestamp:
"""Date of the order."""
return pd.Timestamp(self.start_time.replace(hour=0, minute=0, second=0))
class OrderHelper:
"""

View File

@@ -114,7 +114,7 @@ class BaseExecutor:
self.track_data = track_data
self._trade_exchange = trade_exchange
self.level_infra = LevelInfrastructure()
self.level_infra.reset_infra(common_infra=common_infra)
self.level_infra.reset_infra(common_infra=common_infra, executor=self)
self._settle_type = settle_type
self.reset(start_time=start_time, end_time=end_time, common_infra=common_infra)
if common_infra is None:
@@ -134,6 +134,8 @@ class BaseExecutor:
else:
self.common_infra.update(common_infra)
self.level_infra.reset_infra(common_infra=self.common_infra)
if common_infra.has("trade_account"):
# NOTE: there is a trick in the code.
# shallow copy is used instead of deepcopy.
@@ -256,6 +258,7 @@ class BaseExecutor:
object
trade decision
"""
if self.track_data:
yield trade_decision
@@ -296,6 +299,7 @@ class BaseExecutor:
if return_value is not None:
return_value.update({"execute_result": res})
return res
def get_all_executors(self) -> List[BaseExecutor]:
@@ -396,7 +400,7 @@ class NestedExecutor(BaseExecutor):
trade_decision = updated_trade_decision
# NEW UPDATE
# create a hook for inner strategy to update outer decision
self.inner_strategy.alter_outer_trade_decision(trade_decision)
trade_decision = self.inner_strategy.alter_outer_trade_decision(trade_decision)
return trade_decision
def _collect_data(
@@ -473,6 +477,9 @@ class NestedExecutor(BaseExecutor):
# do nothing and just step forward
sub_cal.step()
# Let inner strategy know that the outer level execution is done.
self.inner_strategy.post_upper_level_exe_step()
return execute_result, {"inner_order_indicators": inner_order_indicators, "decision_list": decision_list}
def post_inner_exe_step(self, inner_exe_res: List[object]) -> None:

View File

@@ -3,9 +3,8 @@
from __future__ import annotations
import bisect
from abc import abstractmethod
from typing import TYPE_CHECKING, Any, Set, Tuple, Union
from typing import Any, Set, Tuple, TYPE_CHECKING, Union
import numpy as np
@@ -184,8 +183,8 @@ class TradeCalendarManager:
Tuple[int, int]:
the index of the range. **the left and right are closed**
"""
left = bisect.bisect_right(list(self._calendar), start_time) - 1
right = bisect.bisect_right(list(self._calendar), end_time) - 1
left = np.searchsorted(self._calendar, start_time, side="right") - 1
right = np.searchsorted(self._calendar, end_time, side="right") - 1
left -= self.start_index
right -= self.start_index
@@ -248,7 +247,7 @@ class LevelInfrastructure(BaseInfrastructure):
sub_level_infra:
- **NOTE**: this will only work after _init_sub_trading !!!
"""
return {"trade_calendar", "sub_level_infra", "common_infra"}
return {"trade_calendar", "sub_level_infra", "common_infra", "executor"}
def reset_cal(
self,