-
Notifications
You must be signed in to change notification settings - Fork 2.7k
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
Migrate backtest logic from NT #1263
Conversation
@@ -146,3 +156,177 @@ def reset(self, outer_trade_decision: BaseTradeDecision = None, **kwargs: Any) - | |||
order_list = outer_trade_decision.order_list | |||
assert len(order_list) == 1 | |||
self._order = order_list[0] | |||
|
|||
|
|||
class SAOEIntStrategy(SAOEStrategy): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the difference between this and SAOEStrategy? Why do we need another class?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SAOEStrategy
is the strategy that maintains SAOEState
internally (by SAOEStateAdapter
). SAOEIntStrategy
is a subclass of SAOEStrategy
, but it contains interpreters. For now, SAOEIntStrategy
is the only implementation of SAOEStrategy
but I think it is useful to leave this two-level structure for future scalability.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Better to explain that in docstring.
@lihuoran Will there be a related internal PR in neutrader? |
qlib/rl/data/pickle_styled.py
Outdated
def _drop_stock_id(df: pd.DataFrame) -> pd.DataFrame: | ||
return df.reset_index().drop(columns=["instrument"]).set_index(["datetime"]) | ||
|
||
self.today = _drop_stock_id(fetch_features(stock_id, date)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it will be better to add more docs about the reason that today
and yesterday
are different with its base IntradayProcessedData
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NTIntradayProcessedData
's base is BaseIntradayProcessedData
, not IntradayProcessedData
. Both NTIntradayProcessedData
and IntradayProcessedData
are implementations of BaseIntradayProcessedData
and that is why they have different data formats.
qlib/rl/utils/env_wrapper.py
Outdated
@@ -249,3 +249,24 @@ def step(self, policy_action: PolicyActType, **kwargs: Any) -> Tuple[ObsType, fl | |||
|
|||
def render(self, mode: str = "human") -> None: | |||
raise NotImplementedError("Render is not implemented in EnvWrapper.") | |||
|
|||
|
|||
class CollectDataEnvWrapper: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is it necessary to create a new class?
Will it be simpler to create a superclass of EnvWrapper
if we want to simplify the interface further?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
""" | ||
|
||
def __init__(self, data_dir: Path, max_step: int, data_ticks: int, data_dim: int) -> None: | ||
def __init__(self, max_step: int, data_ticks: int, data_dim: int, data_dir: Path = None) -> None: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think all implementations related to data_dir
is coupled with the specific data format for that specific case.
So it should not be a general class as a part of the framework.
So it should be redesigned after the data interface is well-designed.
We could leave a TODO here in this PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TODO left.
warnings.filterwarnings("ignore", category=RuntimeWarning) | ||
|
||
path = sys.argv[1] | ||
backtest(get_backtest_config_fromfile(path)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a test
or script
to run the code (maybe just a script in internal nuetrader)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes we have it now.
qlib/rl/data/pickle_styled.py
Outdated
@@ -233,6 +238,25 @@ def __repr__(self) -> str: | |||
return f"{self.__class__.__name__}({self.today}, {self.yesterday})" | |||
|
|||
|
|||
class NTIntradayProcessedData(BaseIntradayProcessedData): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this should be put into another file, because it's not pickle_styled
. See the headers of this file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure how should we re-organize related structures. If we need to move NTIntradayProcessedData
to a separate file, where should we put NTIntradayProcessedData
and load_intraday_processed_data()
?
qlib/rl/data/pickle_styled.py
Outdated
) -> BaseIntradayProcessedData: | ||
from qlib.rl.data.integration import dataset # pylint: disable=C0415 | ||
|
||
if dataset is None: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here. This should be separated.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
if self._policy is not None: | ||
self._policy.eval() | ||
|
||
def set_env(self, env: BaseEnvWrapper) -> None: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BaseEnvWrapper
is for aligning the interface of Executor to RL Environment.
It is weird to rely on BaseEnvWrapper
when running backtesting.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
* Backtest migration * Minor bug fix in test * Reorganize file to avoid loop import * Fix test SAOE bug * Remove unnecessary names * Resolve PR comments; remove private classes; * Fix CI error * Resolve PR comments * Refactor data interfaces * Remove convert_instance_config and change config * Pylint issue * Pylint issue * Fix tempfile warning * Resolve PR comments * Add more comments
Description
Migrate experiment backtest logic from NT.
Motivation and Context
How Has This Been Tested?
pytest qlib/tests/test_all_pipeline.py
under upper directory ofqlib
.Screenshots of Test Results (if appropriate):
Types of changes