Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 3 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -123,10 +123,8 @@ black:
# C0103: invalid-name
# C0209: consider-using-f-string
# R0402: consider-using-from-import
# R1705: no-else-return
# R1710: inconsistent-return-statements
# R1725: super-with-arguments
# R1735: use-dict-literal
# R1730: consider-using-ternary
# W0102: dangerous-default-value
# W0212: protected-access
# W0221: arguments-differ
Expand All @@ -148,8 +146,8 @@ black:
# We use sys.setrecursionlimit(2000) to make the recursion depth larger to ensure that pylint works properly (the default recursion depth is 1000).
# References for parameters: https://github.com/PyCQA/pylint/issues/4577#issuecomment-1000245962
pylint:
pylint --disable=C0104,C0114,C0115,C0116,C0301,C0302,C0411,C0413,C1802,R0401,R0801,R0902,R0903,R0911,R0912,R0913,R0914,R0915,R0917,R1720,W0105,W0123,W0201,W0511,W0613,W1113,W1514,W4904,E0401,E1121,C0103,C0209,R0402,R1705,R1710,R1725,R1730,R1735,W0102,W0212,W0221,W0223,W0231,W0237,W0612,W0621,W0622,W0703,W1309,E1102,E1136 --const-rgx='[a-z_][a-z0-9_]{2,30}' qlib --init-hook="import astroid; astroid.context.InferenceContext.max_inferred = 500; import sys; sys.setrecursionlimit(2000)"
pylint --disable=C0104,C0114,C0115,C0116,C0301,C0302,C0411,C0413,C1802,R0401,R0801,R0902,R0903,R0911,R0912,R0913,R0914,R0915,R0917,R1720,W0105,W0123,W0201,W0511,W0613,W1113,W1514,E0401,E1121,E1123,C0103,C0209,R0402,R1705,R1710,R1725,R1735,W0102,W0212,W0221,W0223,W0231,W0237,W0246,W0612,W0621,W0622,W0703,W1309,E1102,E1136 --const-rgx='[a-z_][a-z0-9_]{2,30}' scripts --init-hook="import astroid; astroid.context.InferenceContext.max_inferred = 500; import sys; sys.setrecursionlimit(2000)"
pylint --disable=C0104,C0114,C0115,C0116,C0301,C0302,C0411,C0413,C1802,R0401,R0801,R0902,R0903,R0911,R0912,R0913,R0914,R0915,R0917,R1720,W0105,W0123,W0201,W0511,W0613,W1113,W1514,W4904,E0401,E1121,C0103,C0209,R0402,R1710,R1730,W0102,W0212,W0221,W0223,W0231,W0237,W0612,W0621,W0622,W0703,W1309,E1102,E1136 --const-rgx='[a-z_][a-z0-9_]{2,30}' qlib --init-hook="import astroid; astroid.context.InferenceContext.max_inferred = 500; import sys; sys.setrecursionlimit(2000)"
pylint --disable=C0104,C0114,C0115,C0116,C0301,C0302,C0411,C0413,C1802,R0401,R0801,R0902,R0903,R0911,R0912,R0913,R0914,R0915,R0917,R1720,W0105,W0123,W0201,W0511,W0613,W1113,W1514,E0401,E1121,E1123,C0103,C0209,R0402,R1710,R1730,W0102,W0212,W0221,W0223,W0231,W0237,W0246,W0612,W0621,W0622,W0703,W1309,E1102,E1136 --const-rgx='[a-z_][a-z0-9_]{2,30}' scripts --init-hook="import astroid; astroid.context.InferenceContext.max_inferred = 500; import sys; sys.setrecursionlimit(2000)"

# Check code with flake8.
# The following flake8 error codes were ignored:
Expand Down
3 changes: 1 addition & 2 deletions qlib/backtest/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,7 @@ def get_exchange(
**kwargs,
)
return exchange
else:
return init_instance_by_config(exchange, accept_types=Exchange)
return init_instance_by_config(exchange, accept_types=Exchange)


def create_account_instance(
Expand Down
3 changes: 1 addition & 2 deletions qlib/backtest/account.py
Original file line number Diff line number Diff line change
Expand Up @@ -409,8 +409,7 @@ def get_portfolio_metrics(self) -> Tuple[pd.DataFrame, dict]:
_portfolio_metrics = self.portfolio_metrics.generate_portfolio_metrics_dataframe()
_positions = self.get_hist_positions()
return _portfolio_metrics, _positions
else:
raise ValueError("generate_portfolio_metrics should be True if you want to generate portfolio_metrics")
raise ValueError("generate_portfolio_metrics should be True if you want to generate portfolio_metrics")

def get_trade_indicator(self) -> Indicator:
"""get the trade indicator instance, which has pa/pos/ffr info."""
Expand Down
22 changes: 9 additions & 13 deletions qlib/backtest/decision.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,23 +117,21 @@ def sign(self) -> int:
def parse_dir(direction: Union[str, int, np.integer, OrderDir, np.ndarray]) -> Union[OrderDir, np.ndarray]:
if isinstance(direction, OrderDir):
return direction
elif isinstance(direction, (int, float, np.integer, np.floating)):
if isinstance(direction, (int, float, np.integer, np.floating)):
return Order.BUY if direction > 0 else Order.SELL
elif isinstance(direction, str):
if isinstance(direction, str):
dl = direction.lower().strip()
if dl == "sell":
return OrderDir.SELL
elif dl == "buy":
if dl == "buy":
return OrderDir.BUY
else:
raise NotImplementedError(f"This type of input is not supported")
elif isinstance(direction, np.ndarray):
raise NotImplementedError(f"This type of input is not supported")
if isinstance(direction, np.ndarray):
direction_array = direction.copy()
direction_array[direction_array > 0] = Order.BUY
direction_array[direction_array <= 0] = Order.SELL
return direction_array
else:
raise NotImplementedError(f"This type of input is not supported")
raise NotImplementedError(f"This type of input is not supported")

@property
def key_by_day(self) -> tuple:
Expand Down Expand Up @@ -385,8 +383,7 @@ def update(self, trade_calendar: TradeCalendarManager) -> Optional[BaseTradeDeci
def _get_range_limit(self, **kwargs: Any) -> Tuple[int, int]:
if self.trade_range is not None:
return self.trade_range(trade_calendar=cast(TradeCalendarManager, kwargs.get("inner_calendar")))
else:
raise NotImplementedError("The decision didn't provide an index range")
raise NotImplementedError("The decision didn't provide an index range")

def get_range_limit(self, **kwargs: Any) -> Tuple[int, int]:
"""
Expand Down Expand Up @@ -432,9 +429,8 @@ def get_range_limit(self, **kwargs: Any) -> Tuple[int, int]:
except NotImplementedError as e:
if "default_value" in kwargs:
return kwargs["default_value"]
else:
# Default to get full index
raise NotImplementedError(f"The decision didn't provide an index range") from e
# Default to get full index
raise NotImplementedError(f"The decision didn't provide an index range") from e

# clip index
if getattr(self, "total_step", None) is not None:
Expand Down
31 changes: 13 additions & 18 deletions qlib/backtest/exchange.py
Original file line number Diff line number Diff line change
Expand Up @@ -263,12 +263,11 @@ def _get_limit_type(self, limit_threshold: Union[tuple, float, None]) -> str:
"""get limit type"""
if isinstance(limit_threshold, tuple):
return self.LT_TP_EXP
elif isinstance(limit_threshold, float):
if isinstance(limit_threshold, float):
return self.LT_FLT
elif limit_threshold is None:
if limit_threshold is None:
return self.LT_NONE
else:
raise NotImplementedError(f"This type of `limit_threshold` is not supported")
raise NotImplementedError(f"This type of `limit_threshold` is not supported")

def _update_limit(self, limit_threshold: Union[Tuple, float, None]) -> None:
# $close may contain NaN, the nan indicates that the stock is not tradable at that timestamp
Expand Down Expand Up @@ -368,12 +367,11 @@ def check_stock_limit(
buy_limit = self.quote.get_data(stock_id, start_time, end_time, field="limit_buy", method="all")
sell_limit = self.quote.get_data(stock_id, start_time, end_time, field="limit_sell", method="all")
return bool(buy_limit or sell_limit)
elif direction == Order.BUY:
if direction == Order.BUY:
return cast(bool, self.quote.get_data(stock_id, start_time, end_time, field="limit_buy", method="all"))
elif direction == Order.SELL:
if direction == Order.SELL:
return cast(bool, self.quote.get_data(stock_id, start_time, end_time, field="limit_sell", method="all"))
else:
raise ValueError(f"direction {direction} is not supported!")
raise ValueError(f"direction {direction} is not supported!")

def check_stock_suspended(
self,
Expand Down Expand Up @@ -596,17 +594,15 @@ def get_real_deal_amount(self, current_amount: float, target_amount: float, fact
"""
if current_amount == target_amount:
return 0
elif current_amount < target_amount:
if current_amount < target_amount:
deal_amount = target_amount - current_amount
deal_amount = self.round_amount_by_trade_unit(deal_amount, factor)
return deal_amount
else:
if target_amount == 0:
return -current_amount
else:
deal_amount = current_amount - target_amount
deal_amount = self.round_amount_by_trade_unit(deal_amount, factor)
return -deal_amount
if target_amount == 0:
return -current_amount
deal_amount = current_amount - target_amount
deal_amount = self.round_amount_by_trade_unit(deal_amount, factor)
return -deal_amount

def generate_order_for_target_amount_position(
self,
Expand Down Expand Up @@ -755,8 +751,7 @@ def get_amount_of_trade_unit(
end_time=end_time,
)
return self.trade_unit / factor
else:
return None
return None

def round_amount_by_trade_unit(
self,
Expand Down
6 changes: 3 additions & 3 deletions qlib/backtest/executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,7 @@ def __init__(
self._skip_empty_decision = skip_empty_decision
self._align_range_limit = align_range_limit

super(NestedExecutor, self).__init__(
super().__init__(
time_per_step=time_per_step,
start_time=start_time,
end_time=end_time,
Expand All @@ -380,7 +380,7 @@ def reset_common_infra(self, common_infra: CommonInfrastructure, copy_trade_acco
# NOTE: please refer to the docs of BaseExecutor.reset_common_infra for the meaning of `copy_trade_account`

# The first level follow the `copy_trade_account` from the upper level
super(NestedExecutor, self).reset_common_infra(common_infra, copy_trade_account=copy_trade_account)
super().reset_common_infra(common_infra, copy_trade_account=copy_trade_account)

# The lower level have to copy the trade_account
self.inner_executor.reset_common_infra(common_infra, copy_trade_account=True)
Expand Down Expand Up @@ -544,7 +544,7 @@ def __init__(
trade_type: str
please refer to the doc of `TT_SERIAL` & `TT_PARAL`
"""
super(SimulatorExecutor, self).__init__(
super().__init__(
time_per_step=time_per_step,
start_time=start_time,
end_time=end_time,
Expand Down
11 changes: 5 additions & 6 deletions qlib/backtest/high_performance_ds.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,12 +117,11 @@ def get_data(self, stock_id, start_time, end_time, field, method=None):
stock_data = resam_ts_data(self.data[stock_id][field], start_time, end_time, method=method)
if stock_data is None:
return None
elif isinstance(stock_data, (bool, np.bool_, int, float, np.number)):
if isinstance(stock_data, (bool, np.bool_, int, float, np.number)):
return stock_data
elif isinstance(stock_data, pd.Series):
if isinstance(stock_data, pd.Series):
return idd.SingleData(stock_data)
else:
raise ValueError(f"stock data from resam_ts_data must be a number, pd.Series or pd.DataFrame")
raise ValueError(f"stock data from resam_ts_data must be a number, pd.Series or pd.DataFrame")


class NumpyQuote(BaseQuote):
Expand Down Expand Up @@ -561,7 +560,7 @@ class PandasOrderIndicator(BaseOrderIndicator):
"""

def __init__(self) -> None:
super(PandasOrderIndicator, self).__init__()
super().__init__()
self.data: Dict[str, PandasSingleMetric] = OrderedDict()

def assign(self, col: str, metric: Union[dict, pd.Series]) -> None:
Expand Down Expand Up @@ -609,7 +608,7 @@ class NumpyOrderIndicator(BaseOrderIndicator):
"""

def __init__(self) -> None:
super(NumpyOrderIndicator, self).__init__()
super().__init__()
self.data: Dict[str, SingleData] = OrderedDict()

def assign(self, col: str, metric: dict) -> None:
Expand Down
3 changes: 1 addition & 2 deletions qlib/backtest/position.py
Original file line number Diff line number Diff line change
Expand Up @@ -433,8 +433,7 @@ def get_stock_count(self, code: str, bar: str) -> float:
"""the days the account has been hold, it may be used in some special strategies"""
if f"count_{bar}" in self.position[code]:
return self.position[code][f"count_{bar}"]
else:
return 0
return 0

def get_stock_weight(self, code: str) -> float:
return self.position[code]["weight"]
Expand Down
59 changes: 28 additions & 31 deletions qlib/backtest/report.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,22 +103,21 @@ def _cal_benchmark(benchmark_config: Optional[dict], freq: str) -> Optional[pd.S

if isinstance(benchmark, pd.Series):
return benchmark
else:
start_time = benchmark_config.get("start_time", None)
end_time = benchmark_config.get("end_time", None)

if freq is None:
raise ValueError("benchmark freq can't be None!")
_codes = benchmark if isinstance(benchmark, (list, dict)) else [benchmark]
fields = ["$close/Ref($close,1)-1"]
_temp_result, _ = get_higher_eq_freq_feature(_codes, fields, start_time, end_time, freq=freq)
if len(_temp_result) == 0:
raise ValueError(f"The benchmark {_codes} does not exist. Please provide the right benchmark")
return (
_temp_result.groupby(level="datetime", group_keys=False)[_temp_result.columns.tolist()[0]]
.mean()
.fillna(0)
)
start_time = benchmark_config.get("start_time", None)
end_time = benchmark_config.get("end_time", None)

if freq is None:
raise ValueError("benchmark freq can't be None!")
_codes = benchmark if isinstance(benchmark, (list, dict)) else [benchmark]
fields = ["$close/Ref($close,1)-1"]
_temp_result, _ = get_higher_eq_freq_feature(_codes, fields, start_time, end_time, freq=freq)
if len(_temp_result) == 0:
raise ValueError(f"The benchmark {_codes} does not exist. Please provide the right benchmark")
return (
_temp_result.groupby(level="datetime", group_keys=False)[_temp_result.columns.tolist()[0]]
.mean()
.fillna(0)
)

def _sample_benchmark(
self,
Expand Down Expand Up @@ -299,13 +298,13 @@ def record(self, trade_start_time: Union[str, pd.Timestamp]) -> None:
self.trade_indicator_his[trade_start_time] = self.get_trade_indicator()

def _update_order_trade_info(self, trade_info: List[Tuple[Order, float, float, float]]) -> None:
amount = dict()
deal_amount = dict()
trade_price = dict()
trade_value = dict()
trade_cost = dict()
trade_dir = dict()
pa = dict()
amount = {}
deal_amount = {}
trade_price = {}
trade_value = {}
trade_cost = {}
trade_dir = {}
pa = {}

for order, _trade_val, _trade_cost, _trade_price in trade_info:
amount[order.stock_id] = order.amount_delta
Expand Down Expand Up @@ -556,30 +555,28 @@ def _cal_trade_fulfill_rate(self, method: str = "mean") -> Optional[BaseSingleMe
return self.order_indicator.transfer(
lambda ffr: ffr.mean(),
)
elif method == "amount_weighted":
if method == "amount_weighted":
return self.order_indicator.transfer(
lambda ffr, deal_amount: (ffr * deal_amount.abs()).sum() / (deal_amount.abs().sum()),
)
elif method == "value_weighted":
if method == "value_weighted":
return self.order_indicator.transfer(
lambda ffr, trade_value: (ffr * trade_value.abs()).sum() / (trade_value.abs().sum()),
)
else:
raise ValueError(f"method {method} is not supported!")
raise ValueError(f"method {method} is not supported!")

def _cal_trade_price_advantage(self, method: str = "mean") -> Optional[BaseSingleMetric]:
if method == "mean":
return self.order_indicator.transfer(lambda pa: pa.mean())
elif method == "amount_weighted":
if method == "amount_weighted":
return self.order_indicator.transfer(
lambda pa, deal_amount: (pa * deal_amount.abs()).sum() / (deal_amount.abs().sum()),
)
elif method == "value_weighted":
if method == "value_weighted":
return self.order_indicator.transfer(
lambda pa, trade_value: (pa * trade_value.abs()).sum() / (trade_value.abs().sum()),
)
else:
raise ValueError(f"method {method} is not supported!")
raise ValueError(f"method {method} is not supported!")

def _cal_trade_positive_rate(self) -> Optional[BaseSingleMetric]:
def func(pa):
Expand Down
8 changes: 3 additions & 5 deletions qlib/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -361,8 +361,7 @@ def get_uri_type(uri: Union[str, Path]):

if is_nfs_or_win and not is_win:
return QlibConfig.NFS_URI
else:
return QlibConfig.LOCAL_URI
return QlibConfig.LOCAL_URI

def get_data_uri(self, freq: Optional[Union[str, Freq]] = None) -> Path:
"""
Expand All @@ -375,14 +374,13 @@ def get_data_uri(self, freq: Optional[Union[str, Freq]] = None) -> Path:
_provider_uri = self.provider_uri[freq]
if self.get_uri_type(_provider_uri) == QlibConfig.LOCAL_URI:
return Path(_provider_uri)
elif self.get_uri_type(_provider_uri) == QlibConfig.NFS_URI:
if self.get_uri_type(_provider_uri) == QlibConfig.NFS_URI:
if "win" in platform.system().lower():
# windows, mount_path is the drive
_path = str(self.mount_path[freq])
return Path(f"{_path}:\\") if ":" not in _path else Path(_path)
return Path(self.mount_path[freq])
else:
raise NotImplementedError(f"This type of uri is not supported")
raise NotImplementedError(f"This type of uri is not supported")

def set_mode(self, mode):
# raise KeyError
Expand Down
2 changes: 1 addition & 1 deletion qlib/contrib/model/catboost_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def fit(
num_boost_round=1000,
early_stopping_rounds=50,
verbose_eval=20,
evals_result=dict(),
evals_result={},
reweighter=None,
**kwargs,
):
Expand Down
2 changes: 1 addition & 1 deletion qlib/contrib/model/double_ensemble.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ def fit(self, dataset: DatasetH):

def train_submodel(self, df_train, df_valid, weights, features):
dtrain, dvalid = self._prepare_data_gbm(df_train, df_valid, weights, features)
evals_result = dict()
evals_result = {}

callbacks = [lgb.log_evaluation(20), lgb.record_evaluation(evals_result)]
if self.early_stopping_rounds:
Expand Down
2 changes: 1 addition & 1 deletion qlib/contrib/model/highfreq_gdbt_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ def fit(
evals_result=None,
):
if evals_result is None:
evals_result = dict()
evals_result = {}
dtrain, dvalid = self._prepare_data(dataset)
early_stopping_callback = lgb.early_stopping(early_stopping_rounds)
verbose_eval_callback = lgb.log_evaluation(period=verbose_eval)
Expand Down
Loading