diff --git a/qlib/backtest/high_performance_ds.py b/qlib/backtest/high_performance_ds.py index d556f303c..123725832 100644 --- a/qlib/backtest/high_performance_ds.py +++ b/qlib/backtest/high_performance_ds.py @@ -82,7 +82,7 @@ class BaseQuote: the columns of data to fetch method : Union[str, Callable] the method apply to data. - e.g ["None", "last", "all", "sum", "mean", "any", qlib/utils/resam.py/ts_data_last] + e.g [None, "last", "all", "sum", "mean", "any", qlib/utils/resam.py/ts_data_last] Return ---------- @@ -177,19 +177,19 @@ class BaseSingleMetric: def add(self, other: "BaseSingleMetric", fill_value: float = None) -> "BaseSingleMetric": """Replace np.NaN with fill_value in two metrics and add them.""" - + raise NotImplementedError(f"Please implement the `add` method") def replace(self, replace_dict: dict) -> "BaseSingleMetric": """Replace the value of metric according to replace_dict.""" - + raise NotImplementedError(f"Please implement the `replace` method") def apply(self, func: dict) -> "BaseSingleMetric": """Replace the value of metric with func(metric). - Currently, the func is only qlib/backtest/order/Order.parse_dir. + Currently, the func is only qlib/backtest/order/Order.parse_dir. """ - + raise NotImplementedError(f"Please implement the 'apply' method") @@ -426,11 +426,6 @@ class PandasOrderIndicator(BaseOrderIndicator): for metric in metrics: tmp_metric = PandasSingleMetric({}) for indicator in indicators: - if(metric == "trade_price"): - tmp_metric = tmp_metric.add( - indicator.data["trade_price"] * indicator.data["deal_amount"], fill_value - ) - else: - tmp_metric = tmp_metric.add(indicator.data[metric], fill_value) + tmp_metric = tmp_metric.add(indicator.data[metric], fill_value) metric_dict[metric] = tmp_metric.metric return metric_dict diff --git a/qlib/backtest/report.py b/qlib/backtest/report.py index 64d00b436..e37642244 100644 --- a/qlib/backtest/report.py +++ b/qlib/backtest/report.py @@ -308,8 +308,9 @@ class Indicator: def _update_order_fulfill_rate(self): def func(deal_amount, amount): + # deal_amount is np.NaN when there is no inner decision. So full fill rate is 0. tmp_deal_amount = deal_amount.replace({np.NaN: 0}) - return deal_amount / tmp_deal_amount + return tmp_deal_amount / amount self.order_indicator.transfer(func, "ffr") @@ -318,12 +319,21 @@ class Indicator: self._update_order_fulfill_rate() def _agg_order_trade_info(self, inner_order_indicators: List[Dict[str, pd.Series]]): + # calculate total trade amount with each inner order indicator. + def trade_amount_func(deal_amount, trade_price): + return deal_amount * trade_price + + for indicator in inner_order_indicators: + indicator.transfer(trade_amount_func, "trade_price") + + # sum inner order indicators with same metric. all_metric = ["inner_amount", "deal_amount", "trade_price", "trade_value", "trade_cost", "trade_dir"] metric_dict = self.order_indicator_cls.sum_all_indicators(inner_order_indicators, all_metric, fill_value=0) for metric in metric_dict: self.order_indicator.assign(metric, metric_dict[metric]) def func(trade_price, deal_amount): + # trade_price is np.NaN instead of inf when deal_amount is zero. tmp_deal_amount = deal_amount.replace({0: np.NaN}) return trade_price / tmp_deal_amount