mirror of
https://github.com/microsoft/qlib.git
synced 2026-07-17 17:34:35 +08:00
fix bugs
This commit is contained in:
@@ -7,8 +7,7 @@ from pathlib import Path
|
|||||||
import qlib
|
import qlib
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
from qlib.config import REG_CN
|
from qlib.config import REG_CN
|
||||||
from qlib.contrib.strategy import TopkDropoutStrategy
|
|
||||||
from qlib.contrib.backtest import backtest
|
|
||||||
from qlib.utils import exists_qlib_data, init_instance_by_config, flatten_dict
|
from qlib.utils import exists_qlib_data, init_instance_by_config, flatten_dict
|
||||||
from qlib.workflow import R
|
from qlib.workflow import R
|
||||||
from qlib.workflow.record_temp import PortAnaRecord
|
from qlib.workflow.record_temp import PortAnaRecord
|
||||||
@@ -130,20 +129,9 @@ if __name__ == "__main__":
|
|||||||
"min_cost": 5,
|
"min_cost": 5,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#report_dict = backtest(
|
|
||||||
# start_time=trade_start_time,
|
|
||||||
# end_time=trade_end_time,
|
|
||||||
# **backtest_config,
|
|
||||||
# account=1e8,
|
|
||||||
# benchmark=benchmark,
|
|
||||||
# deal_price="$close",
|
|
||||||
# verbose=False,
|
|
||||||
#)
|
|
||||||
|
|
||||||
with R.start(experiment_name="highfreq_backtest"):
|
with R.start(experiment_name="highfreq_backtest"):
|
||||||
# backtest. If users want to use backtest based on their own prediction,
|
# backtest. If users want to use backtest based on their own prediction,
|
||||||
# please refer to https://qlib.readthedocs.io/en/latest/component/recorder.html#record-template.
|
# please refer to https://qlib.readthedocs.io/en/latest/component/recorder.html#record-template.
|
||||||
recorder = R.get_recorder()
|
recorder = R.get_recorder()
|
||||||
par = PortAnaRecord(recorder, port_analysis_config, 1)
|
par = PortAnaRecord(recorder, port_analysis_config, "day")
|
||||||
par.generate()
|
par.generate()
|
||||||
|
|||||||
@@ -179,7 +179,7 @@ class Account:
|
|||||||
bar_close = trade_exchange.get_close(code, trade_start_time, trade_end_time)
|
bar_close = trade_exchange.get_close(code, trade_start_time, trade_end_time)
|
||||||
self.current.update_stock_price(stock_id=code, price=bar_close)
|
self.current.update_stock_price(stock_id=code, price=bar_close)
|
||||||
# update holding day count
|
# update holding day count
|
||||||
|
|
||||||
# update value
|
# update value
|
||||||
self.val = self.current.calculate_value()
|
self.val = self.current.calculate_value()
|
||||||
# update earning
|
# update earning
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import pathlib
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
from ...data.data import Cal
|
from ...data.data import Cal
|
||||||
from ...utils import get_sample_freq_calendar
|
from ...utils import get_sample_freq_calendar, parse_freq
|
||||||
from .position import Position
|
from .position import Position
|
||||||
from .report import Report
|
from .report import Report
|
||||||
from .order import Order
|
from .order import Order
|
||||||
@@ -151,16 +151,25 @@ class SplitEnv(BaseEnv):
|
|||||||
trade_state, trade_info = self.sub_env.execute(order_list=_order_list)
|
trade_state, trade_info = self.sub_env.execute(order_list=_order_list)
|
||||||
|
|
||||||
self.trade_account.update_bar_end(
|
self.trade_account.update_bar_end(
|
||||||
trade_start_time=trade_start_time, trade_end_time=trade_end_time, trade_exchange=self.trade_exchange, update_report=self.generate_report
|
trade_start_time=trade_start_time,
|
||||||
|
trade_end_time=trade_end_time,
|
||||||
|
trade_exchange=self.trade_exchange,
|
||||||
|
update_report=self.generate_report,
|
||||||
)
|
)
|
||||||
_obs = {"current": self.trade_account.current}
|
_obs = {"current": self.trade_account.current}
|
||||||
_info = {}
|
_info = {}
|
||||||
return _obs, _info
|
return _obs, _info
|
||||||
|
|
||||||
def get_report(self):
|
def get_report(self):
|
||||||
_report = self.trade_account.report.generate_report_dataframe() if self.generate_report else None
|
sub_env_report_dict = self.sub_env.get_report()
|
||||||
_positions = self.trade_account.get_positions() if self.generate_report else None
|
if self.generate_report:
|
||||||
return [(_report, _positions), *self.sub_env.get_report()]
|
_report = self.trade_account.report.generate_report_dataframe()
|
||||||
|
_positions = self.trade_account.get_positions()
|
||||||
|
_count, _freq = parse_freq(self.step_bar)
|
||||||
|
sub_env_report_dict.update({f"{_count}{_freq}": (_report, _positions)})
|
||||||
|
return sub_env_report_dict
|
||||||
|
else:
|
||||||
|
return sub_env_report_dict
|
||||||
|
|
||||||
|
|
||||||
class SimulatorEnv(BaseEnv):
|
class SimulatorEnv(BaseEnv):
|
||||||
@@ -235,13 +244,20 @@ class SimulatorEnv(BaseEnv):
|
|||||||
# do nothing
|
# do nothing
|
||||||
pass
|
pass
|
||||||
self.trade_account.update_bar_end(
|
self.trade_account.update_bar_end(
|
||||||
trade_start_time=trade_start_time, trade_end_time=trade_end_time, trade_exchange=self.trade_exchange, update_report=self.generate_report
|
trade_start_time=trade_start_time,
|
||||||
|
trade_end_time=trade_end_time,
|
||||||
|
trade_exchange=self.trade_exchange,
|
||||||
|
update_report=self.generate_report,
|
||||||
)
|
)
|
||||||
_obs = {"current": self.trade_account.current}
|
_obs = {"current": self.trade_account.current}
|
||||||
_info = {"trade_info": trade_info}
|
_info = {"trade_info": trade_info}
|
||||||
return _obs, _info
|
return _obs, _info
|
||||||
|
|
||||||
def get_report(self):
|
def get_report(self):
|
||||||
_report = self.trade_account.report.generate_report_dataframe() if self.generate_report else None
|
if self.generate_report:
|
||||||
_positions = self.trade_account.get_positions() if self.generate_report else None
|
_report = self.trade_account.report.generate_report_dataframe()
|
||||||
return [(_report, _positions)]
|
_positions = self.trade_account.get_positions()
|
||||||
|
_count, _freq = parse_freq(self.step_bar)
|
||||||
|
return {f"{_count}{_freq}": (_report, _positions)}
|
||||||
|
else:
|
||||||
|
return {}
|
||||||
|
|||||||
@@ -163,7 +163,7 @@ class TopkDropoutStrategy(ModelStrategy):
|
|||||||
|
|
||||||
# Get the stock list we really want to buy
|
# Get the stock list we really want to buy
|
||||||
buy = today[: len(sell) + self.topk - len(last)]
|
buy = today[: len(sell) + self.topk - len(last)]
|
||||||
#print("flag", len(sell), len(buy), self.topk, len(last))
|
# print("flag", len(sell), len(buy), self.topk, len(last))
|
||||||
for code in current_stock_list:
|
for code in current_stock_list:
|
||||||
if not self.trade_exchange.is_stock_tradable(
|
if not self.trade_exchange.is_stock_tradable(
|
||||||
stock_id=code, start_time=trade_start_time, end_time=trade_end_time
|
stock_id=code, start_time=trade_start_time, end_time=trade_end_time
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ from ..data.dataset import DatasetH
|
|||||||
from ..data.dataset.handler import DataHandlerLP
|
from ..data.dataset.handler import DataHandlerLP
|
||||||
from ..utils import init_instance_by_config, get_module_by_module_path
|
from ..utils import init_instance_by_config, get_module_by_module_path
|
||||||
from ..log import get_module_logger
|
from ..log import get_module_logger
|
||||||
from ..utils import flatten_dict
|
from ..utils import flatten_dict, parse_freq
|
||||||
from ..strategy.base import BaseStrategy
|
from ..strategy.base import BaseStrategy
|
||||||
from ..contrib.eva.alpha import calc_ic, calc_long_short_return
|
from ..contrib.eva.alpha import calc_ic, calc_long_short_return
|
||||||
|
|
||||||
@@ -225,7 +225,7 @@ class PortAnaRecord(RecordTemp):
|
|||||||
|
|
||||||
artifact_path = "portfolio_analysis"
|
artifact_path = "portfolio_analysis"
|
||||||
|
|
||||||
def __init__(self, recorder, config, risk_analysis_dep, **kwargs):
|
def __init__(self, recorder, config, risk_analysis_freq, **kwargs):
|
||||||
"""
|
"""
|
||||||
config["strategy"] : dict
|
config["strategy"] : dict
|
||||||
define the strategy class as well as the kwargs.
|
define the strategy class as well as the kwargs.
|
||||||
@@ -233,59 +233,87 @@ class PortAnaRecord(RecordTemp):
|
|||||||
define the env class as well as the kwargs.
|
define the env class as well as the kwargs.
|
||||||
config["backtest"] : dict
|
config["backtest"] : dict
|
||||||
define the backtest kwargs.
|
define the backtest kwargs.
|
||||||
risk_analysis_dep : int
|
risk_analysis_freq : int
|
||||||
risk analyze the dep'th env report
|
risk analysis freq of report
|
||||||
"""
|
"""
|
||||||
super().__init__(recorder=recorder, **kwargs)
|
super().__init__(recorder=recorder, **kwargs)
|
||||||
|
|
||||||
self.strategy_config = config["strategy"]
|
self.strategy_config = config["strategy"]
|
||||||
self.env_config = config["env"]
|
self.env_config = config["env"]
|
||||||
self.backtest_config = config["backtest"]
|
self.backtest_config = config["backtest"]
|
||||||
self.risk_analysis_dep = risk_analysis_dep
|
_count, _freq = parse_freq(risk_analysis_freq)
|
||||||
|
self.risk_analysis_freq = f"{_count}{_freq}"
|
||||||
|
self.report_freq = self._get_report_freq(self.env_config)
|
||||||
|
|
||||||
|
def _get_report_freq(self, env_config):
|
||||||
|
ret_freq = []
|
||||||
|
if env_config["kwargs"].get("generate_report", False):
|
||||||
|
_count, _freq = parse_freq(env_config["kwargs"]["step_bar"])
|
||||||
|
ret_freq.append(f"{_count}{_freq}")
|
||||||
|
if "sub_env" in env_config["kwargs"]:
|
||||||
|
ret_freq.extend(self._get_report_freq(env_config["kwargs"]["sub_env"]))
|
||||||
|
return ret_freq
|
||||||
|
|
||||||
|
def _cal_risk_analysis_scaler(self, freq):
|
||||||
|
_count, _freq = parse_freq(freq)
|
||||||
|
_freq_scaler = {
|
||||||
|
"minute": 240 * 250,
|
||||||
|
"day": 250,
|
||||||
|
"week": 50,
|
||||||
|
"month": 12,
|
||||||
|
}
|
||||||
|
return _count * _freq_scaler[_freq]
|
||||||
|
|
||||||
def generate(self, **kwargs):
|
def generate(self, **kwargs):
|
||||||
# custom strategy and get backtest
|
# custom strategy and get backtest
|
||||||
report_list = normal_backtest(env=self.env_config, strategy=self.strategy_config, **self.backtest_config)
|
report_dict = normal_backtest(env=self.env_config, strategy=self.strategy_config, **self.backtest_config)
|
||||||
for report_dep, (report_normal, positions_normal) in enumerate(report_list):
|
for report_freq, (report_normal, positions_normal) in report_dict.items():
|
||||||
if report_normal is None:
|
self.recorder.save_objects(
|
||||||
if self.risk_analysis_dep == report_dep:
|
**{f"report_normal_{report_freq}.pkl": report_normal}, artifact_path=PortAnaRecord.get_path()
|
||||||
warnings.warn(
|
)
|
||||||
f"the report in dep {risk_analysis_dep} is None, please set the corresponding env with `generate_report==True`"
|
self.recorder.save_objects(
|
||||||
)
|
**{f"positions_normal_{report_freq}.pkl": positions_normal}, artifact_path=PortAnaRecord.get_path()
|
||||||
continue
|
)
|
||||||
|
|
||||||
self.recorder.save_objects(
|
if self.risk_analysis_freq not in report_dict:
|
||||||
**{f"report_normal_{report_dep}.pkl": report_normal}, artifact_path=PortAnaRecord.get_path()
|
warnings.warn(
|
||||||
|
f"the freq {self.risk_analysis_freq} report is not found, please set the corresponding env with `generate_report==True`"
|
||||||
)
|
)
|
||||||
self.recorder.save_objects(
|
else:
|
||||||
**{f"positions_norma_{report_dep}l.pkl": positions_normal}, artifact_path=PortAnaRecord.get_path()
|
report_normal, _ = report_dict.get(self.risk_analysis_freq)
|
||||||
|
analysis = dict()
|
||||||
|
risk_analysis_scaler = self._cal_risk_analysis_scaler(self.risk_analysis_freq)
|
||||||
|
analysis["excess_return_without_cost"] = risk_analysis(
|
||||||
|
report_normal["return"] - report_normal["bench"], risk_analysis_scaler
|
||||||
)
|
)
|
||||||
# analysis
|
analysis["excess_return_with_cost"] = risk_analysis(
|
||||||
if self.risk_analysis_dep == report_dep:
|
report_normal["return"] - report_normal["bench"] - report_normal["cost"], risk_analysis_scaler
|
||||||
analysis = dict()
|
)
|
||||||
analysis["excess_return_without_cost"] = risk_analysis(report_normal["return"] - report_normal["bench"])
|
analysis_df = pd.concat(analysis) # type: pd.DataFrame
|
||||||
analysis["excess_return_with_cost"] = risk_analysis(
|
# log metrics
|
||||||
report_normal["return"] - report_normal["bench"] - report_normal["cost"]
|
self.recorder.log_metrics(**flatten_dict(analysis_df["risk"].unstack().T.to_dict()))
|
||||||
)
|
# save results
|
||||||
analysis_df = pd.concat(analysis) # type: pd.DataFrame
|
self.recorder.save_objects(
|
||||||
# log metrics
|
**{f"port_analysis_{report_freq}.pkl": analysis_df}, artifact_path=PortAnaRecord.get_path()
|
||||||
self.recorder.log_metrics(**flatten_dict(analysis_df["risk"].unstack().T.to_dict()))
|
)
|
||||||
# save results
|
logger.info(
|
||||||
self.recorder.save_objects(
|
f"Portfolio analysis record 'port_analysis_{report_freq}.pkl' has been saved as the artifact of the Experiment {self.recorder.experiment_id}"
|
||||||
**{f"port_analysis.pkl_{report_dep}": analysis_df}, artifact_path=PortAnaRecord.get_path()
|
)
|
||||||
)
|
# print out results
|
||||||
logger.info(
|
pprint("The following are analysis results of the excess return without cost.")
|
||||||
f"Portfolio analysis record 'port_analysis_{report_dep}.pkl' has been saved as the artifact of the Experiment {self.recorder.experiment_id}"
|
pprint(analysis["excess_return_without_cost"])
|
||||||
)
|
pprint("The following are analysis results of the excess return with cost.")
|
||||||
# print out results
|
pprint(analysis["excess_return_with_cost"])
|
||||||
pprint("The following are analysis results of the excess return without cost.")
|
|
||||||
pprint(analysis["excess_return_without_cost"])
|
|
||||||
pprint("The following are analysis results of the excess return with cost.")
|
|
||||||
pprint(analysis["excess_return_with_cost"])
|
|
||||||
|
|
||||||
def list(self):
|
def list(self):
|
||||||
return [
|
list_path = []
|
||||||
PortAnaRecord.get_path("report_normal.pkl"),
|
for _freq in self.report_freq:
|
||||||
PortAnaRecord.get_path("positions_normal.pkl"),
|
list_path.extend(
|
||||||
PortAnaRecord.get_path("port_analysis.pkl"),
|
[
|
||||||
]
|
PortAnaRecord.get_path(f"report_normal_{_freq}.pkl"),
|
||||||
|
PortAnaRecord.get_path(f"positions_normal_{_freq}.pkl"),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
if _freq == self.risk_analysis_freq:
|
||||||
|
list_path.append(PortAnaRecord.get_path(f"port_analysis_{_freq}.pkl"))
|
||||||
|
return list_path
|
||||||
|
|||||||
Reference in New Issue
Block a user