1
0
mirror of https://github.com/microsoft/qlib.git synced 2026-07-16 01:06:56 +08:00

support empty benchmark

Empty benchmark could accelerate the learning process
This commit is contained in:
Young
2021-07-26 06:14:57 +00:00
parent 9bf8c999e6
commit bdebe12cf2
6 changed files with 45 additions and 39 deletions

View File

@@ -8,9 +8,9 @@ from .account import Account
if TYPE_CHECKING: if TYPE_CHECKING:
from ..strategy.base import BaseStrategy from ..strategy.base import BaseStrategy
from .executor import BaseExecutor
from .position import Position from .position import Position
from .exchange import Exchange from .exchange import Exchange
from .executor import BaseExecutor
from .backtest import backtest_loop from .backtest import backtest_loop
from .backtest import collect_data_loop from .backtest import collect_data_loop
from .order import Order from .order import Order
@@ -155,6 +155,7 @@ def get_strategy_executor(
# - for avoiding recursive import # - for avoiding recursive import
# - typing annotations is not reliable # - typing annotations is not reliable
from ..strategy.base import BaseStrategy from ..strategy.base import BaseStrategy
from .executor import BaseExecutor
trade_account = create_account_instance( trade_account = create_account_instance(
start_time=start_time, end_time=end_time, benchmark=benchmark, account=account, pos_type=pos_type start_time=start_time, end_time=end_time, benchmark=benchmark, account=account, pos_type=pos_type

View File

@@ -75,17 +75,7 @@ class Account:
): ):
self._pos_type = pos_type self._pos_type = pos_type
self._port_metr_enabled = port_metr_enabled self._port_metr_enabled = port_metr_enabled
self.init_vars(init_cash, position_dict, freq, benchmark_config)
def is_port_metr_enabled(self):
"""
Is portfolio-based metrics enabled.
"""
return self._port_metr_enabled and not self.current.skip_update()
def init_vars(self, init_cash, position_dict, freq: str, benchmark_config: dict):
# init cash
self.init_cash = init_cash self.init_cash = init_cash
self.current: BasePosition = init_instance_by_config( self.current: BasePosition = init_instance_by_config(
{ {
@@ -100,8 +90,19 @@ class Account:
self.accum_info = AccumulatedInfo() self.accum_info = AccumulatedInfo()
self.report = None self.report = None
self.positions = {} self.positions = {}
# in of reset ignore None values
self.benchmark_config = benchmark_config
self.freq = freq
self.reset(freq=freq, benchmark_config=benchmark_config, init_report=True) self.reset(freq=freq, benchmark_config=benchmark_config, init_report=True)
def is_port_metr_enabled(self):
"""
Is portfolio-based metrics enabled.
"""
return self._port_metr_enabled and not self.current.skip_update()
def reset_report(self, freq, benchmark_config): def reset_report(self, freq, benchmark_config):
# portfolio related metrics # portfolio related metrics
if self.is_port_metr_enabled(): if self.is_port_metr_enabled():

View File

@@ -512,7 +512,7 @@ class Exchange:
def _get_factor_or_raise_erorr(self, factor: float = None, stock_id: str = None, start_time=None, end_time=None): def _get_factor_or_raise_erorr(self, factor: float = None, stock_id: str = None, start_time=None, end_time=None):
"""Please refer to the docs of get_amount_of_trade_unit""" """Please refer to the docs of get_amount_of_trade_unit"""
if factor is None: if factor is None:
if stock_id is not None and start_time is not None and end_time is not None : if stock_id is not None and start_time is not None and end_time is not None:
factor = self.get_factor(stock_id=stock_id, start_time=start_time, end_time=end_time) factor = self.get_factor(stock_id=stock_id, start_time=start_time, end_time=end_time)
else: else:
raise ValueError(f"`factor` and (`stock_id`, `start_time`, `end_time`) can't both be None") raise ValueError(f"`factor` and (`stock_id`, `start_time`, `end_time`) can't both be None")
@@ -537,15 +537,16 @@ class Exchange:
the end time of trading range the end time of trading range
""" """
if not self.trade_w_adj_price and self.trade_unit is not None: if not self.trade_w_adj_price and self.trade_unit is not None:
factor = self._get_factor_or_raise_erorr(factor=factor, factor = self._get_factor_or_raise_erorr(
stock_id=stock_id, factor=factor, stock_id=stock_id, start_time=start_time, end_time=end_time
start_time=start_time, )
end_time=end_time)
return self.trade_unit / factor return self.trade_unit / factor
else: else:
return None return None
def round_amount_by_trade_unit(self, deal_amount, factor: float = None, stock_id: str = None, start_time=None, end_time=None): def round_amount_by_trade_unit(
self, deal_amount, factor: float = None, stock_id: str = None, start_time=None, end_time=None
):
"""Parameter """Parameter
Please refer to the docs of get_amount_of_trade_unit Please refer to the docs of get_amount_of_trade_unit
@@ -555,10 +556,9 @@ class Exchange:
""" """
if not self.trade_w_adj_price and self.trade_unit is not None: if not self.trade_w_adj_price and self.trade_unit is not None:
# the minimal amount is 1. Add 0.1 for solving precision problem. # the minimal amount is 1. Add 0.1 for solving precision problem.
factor = self._get_factor_or_raise_erorr(factor=factor, factor = self._get_factor_or_raise_erorr(
stock_id=stock_id, factor=factor, stock_id=stock_id, start_time=start_time, end_time=end_time
start_time=start_time, )
end_time=end_time)
return (deal_amount * factor + 0.1) // self.trade_unit * self.trade_unit / factor return (deal_amount * factor + 0.1) // self.trade_unit * self.trade_unit / factor
return deal_amount return deal_amount

View File

@@ -80,11 +80,12 @@ class Report:
def init_bench(self, freq=None, benchmark_config=None): def init_bench(self, freq=None, benchmark_config=None):
if freq is not None: if freq is not None:
self.freq = freq self.freq = freq
if benchmark_config is not None: self.benchmark_config = benchmark_config
self.benchmark_config = benchmark_config
self.bench = self._cal_benchmark(self.benchmark_config, self.freq) self.bench = self._cal_benchmark(self.benchmark_config, self.freq)
def _cal_benchmark(self, benchmark_config, freq): def _cal_benchmark(self, benchmark_config, freq):
if benchmark_config is None:
return None
benchmark = benchmark_config.get("benchmark", CSI300_BENCH) benchmark = benchmark_config.get("benchmark", CSI300_BENCH)
if benchmark is None: if benchmark is None:
return None return None

View File

@@ -63,9 +63,9 @@ class TWAPStrategy(BaseStrategy):
stock_id=order.stock_id, start_time=trade_start_time, end_time=trade_end_time stock_id=order.stock_id, start_time=trade_start_time, end_time=trade_end_time
): ):
continue continue
_amount_trade_unit = self.trade_exchange.get_amount_of_trade_unit(stock_id=order.stock_id, _amount_trade_unit = self.trade_exchange.get_amount_of_trade_unit(
start_time=order.start_time, stock_id=order.stock_id, start_time=order.start_time, end_time=order.end_time
end_time=order.end_time) )
_order_amount = None _order_amount = None
# considering trade unit # considering trade unit
if _amount_trade_unit is None: if _amount_trade_unit is None:
@@ -169,9 +169,9 @@ class SBBStrategyBase(BaseStrategy):
self.trade_trend[order.stock_id] = _pred_trend self.trade_trend[order.stock_id] = _pred_trend
continue continue
# get amount of one trade unit # get amount of one trade unit
_amount_trade_unit = self.trade_exchange.get_amount_of_trade_unit(stock_id=order.stock_id, _amount_trade_unit = self.trade_exchange.get_amount_of_trade_unit(
start_time=order.start_time, stock_id=order.stock_id, start_time=order.start_time, end_time=order.end_time
end_time=order.end_time) )
if _pred_trend == self.TREND_MID: if _pred_trend == self.TREND_MID:
_order_amount = None _order_amount = None
# considering trade unit # considering trade unit
@@ -471,9 +471,9 @@ class ACStrategy(BaseStrategy):
if sig_sam is None or np.isnan(sig_sam): if sig_sam is None or np.isnan(sig_sam):
# no signal, TWAP # no signal, TWAP
_amount_trade_unit = self.trade_exchange.get_amount_of_trade_unit(stock_id=order.stock_id, _amount_trade_unit = self.trade_exchange.get_amount_of_trade_unit(
start_time=order.start_time, stock_id=order.stock_id, start_time=order.start_time, end_time=order.end_time
end_time=order.end_time) )
if _amount_trade_unit is None: if _amount_trade_unit is None:
# divide the order into equal parts, and trade one part # divide the order into equal parts, and trade one part
_order_amount = self.trade_amount[order.stock_id] / (trade_len - trade_step) _order_amount = self.trade_amount[order.stock_id] / (trade_len - trade_step)
@@ -494,10 +494,9 @@ class ACStrategy(BaseStrategy):
np.sinh(kappa * (trade_len - trade_step)) - np.sinh(kappa * (trade_len - trade_step - 1)) np.sinh(kappa * (trade_len - trade_step)) - np.sinh(kappa * (trade_len - trade_step - 1))
) / np.sinh(kappa * trade_len) ) / np.sinh(kappa * trade_len)
_order_amount = order.amount * amount_ratio _order_amount = order.amount * amount_ratio
_order_amount = self.trade_exchange.round_amount_by_trade_unit(_order_amount, _order_amount = self.trade_exchange.round_amount_by_trade_unit(
stock_id=order.stock_id, _order_amount, stock_id=order.stock_id, start_time=order.start_time, end_time=order.end_time
start_time=order.start_time, )
end_time=order.end_time)
if order.direction == order.SELL: if order.direction == order.SELL:
# sell all amount at last # sell all amount at last
@@ -584,8 +583,11 @@ class FileOrderStrategy(BaseStrategy):
""" """
def __init__( def __init__(
self, file: Union[IO, str, Path, pd.DataFrame], self,
trade_range: Union[Tuple[int, int], TradeRange] = None, *args, **kwargs file: Union[IO, str, Path, pd.DataFrame],
trade_range: Union[Tuple[int, int], TradeRange] = None,
*args,
**kwargs,
): ):
""" """

View File

@@ -2,9 +2,10 @@
# Licensed under the MIT License. # Licensed under the MIT License.
from __future__ import annotations from __future__ import annotations
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
if TYPE_CHECKING: if TYPE_CHECKING:
from qlib.backtest.exchange import Exchange from qlib.backtest.exchange import Exchange
from qlib.backtest.position import BasePosition from qlib.backtest.position import BasePosition
from typing import List, Tuple, Union from typing import List, Tuple, Union
from ..model.base import BaseModel from ..model.base import BaseModel