Skip to content
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

Merged
merged 16 commits into from
Sep 19, 2022
Merged

Migrate backtest logic from NT #1263

merged 16 commits into from
Sep 19, 2022

Conversation

lihuoran
Copy link
Contributor

Description

Migrate experiment backtest logic from NT.

Motivation and Context

How Has This Been Tested?

  • Pass the test by running: pytest qlib/tests/test_all_pipeline.py under upper directory of qlib.
  • If you are adding a new feature, test on your own test scripts.

Screenshots of Test Results (if appropriate):

  1. Pipeline test:
  2. Your own tests:

Types of changes

  • Fix bugs
  • Add new feature
  • Update documentation

qlib/rl/neural_trading_migration/backtest.py Outdated Show resolved Hide resolved
qlib/rl/order_execution/interpreter.py Outdated Show resolved Hide resolved
qlib/rl/order_execution/interpreter.py Show resolved Hide resolved
qlib/rl/order_execution/network.py Outdated Show resolved Hide resolved
qlib/rl/order_execution/strategy.py Outdated Show resolved Hide resolved
@@ -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):
Copy link
Collaborator

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?

Copy link
Contributor Author

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.

Copy link
Collaborator

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.

@you-n-g
Copy link
Collaborator

you-n-g commented Sep 5, 2022

@lihuoran Will there be a related internal PR in neutrader?

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))
Copy link
Collaborator

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.

Copy link
Contributor Author

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.

@@ -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:
Copy link
Collaborator

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?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree.

Copy link
Contributor Author

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:
Copy link
Collaborator

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.

Copy link
Contributor Author

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))
Copy link
Collaborator

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)?

Copy link
Contributor Author

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.

@@ -233,6 +238,25 @@ def __repr__(self) -> str:
return f"{self.__class__.__name__}({self.today}, {self.yesterday})"


class NTIntradayProcessedData(BaseIntradayProcessedData):
Copy link
Collaborator

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.

Copy link
Contributor Author

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()?

) -> BaseIntradayProcessedData:
from qlib.rl.data.integration import dataset # pylint: disable=C0415

if dataset is None:
Copy link
Collaborator

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.

Copy link
Collaborator

@ultmaster ultmaster left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

qlib/rl/order_execution/strategy.py Outdated Show resolved Hide resolved
if self._policy is not None:
self._policy.eval()

def set_env(self, env: BaseEnvWrapper) -> None:
Copy link
Collaborator

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.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found only env.status["cur_step"] is used.
If we get that value from TradeCalendarManager.get_trade_step,
the following code can be removed.

  1. All def set_env related code.
  2. CollectDataEnvWrapper
  3. Following code in qlib/rl/order_execution/strategy.py

image

@you-n-g you-n-g merged commit bee05f5 into main Sep 19, 2022
@you-n-g you-n-g deleted the huoran/migrate_amc4th branch September 19, 2022 06:54
@you-n-g you-n-g added the enhancement New feature or request label Dec 9, 2022
qianyun210603 pushed a commit to qianyun210603/qlib that referenced this pull request Mar 23, 2023
* 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
This pull request was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants