|
| 1 | +def tearsheet_custom_metrics |
| 2 | +============================ |
| 3 | + |
| 4 | +``tearsheet_custom_metrics`` is a strategy lifecycle hook that runs during backtest |
| 5 | +analysis, immediately before LumiBot writes: |
| 6 | + |
| 7 | +- ``*_tearsheet.html`` |
| 8 | +- ``*_tearsheet_metrics.json`` |
| 9 | + |
| 10 | +Use this hook when you want strategy-defined metrics in both artifacts. |
| 11 | + |
| 12 | +When it runs |
| 13 | +------------ |
| 14 | + |
| 15 | +1. Backtest trading completes. |
| 16 | +2. LumiBot computes strategy/benchmark return series and drawdown context. |
| 17 | +3. LumiBot calls ``tearsheet_custom_metrics(...)``. |
| 18 | +4. Returned metrics are appended to the tearsheet metrics table and JSON scalar metrics. |
| 19 | + |
| 20 | +Method signature |
| 21 | +---------------- |
| 22 | + |
| 23 | +.. code-block:: python |
| 24 | +
|
| 25 | + def tearsheet_custom_metrics( |
| 26 | + self, |
| 27 | + stats_df: pd.DataFrame | None, |
| 28 | + strategy_returns: pd.Series, |
| 29 | + benchmark_returns: pd.Series | None, |
| 30 | + drawdown: pd.Series, |
| 31 | + drawdown_details: pd.DataFrame, |
| 32 | + risk_free_rate: float, |
| 33 | + ) -> dict: |
| 34 | + ... |
| 35 | +
|
| 36 | +Parameter structure |
| 37 | +------------------- |
| 38 | + |
| 39 | +``stats_df`` (``pd.DataFrame | None``) |
| 40 | + Backtest stats dataframe (same data used for ``*_stats.csv/parquet``). |
| 41 | + |
| 42 | +``strategy_returns`` (``pd.Series``) |
| 43 | + Strategy return series used for tearsheet metric calculations. |
| 44 | + |
| 45 | +``benchmark_returns`` (``pd.Series | None``) |
| 46 | + Benchmark return series when a benchmark is available; otherwise ``None``. |
| 47 | + |
| 48 | +``drawdown`` (``pd.Series``) |
| 49 | + Strategy drawdown series derived from cumulative returns. |
| 50 | + |
| 51 | +``drawdown_details`` (``pd.DataFrame``) |
| 52 | + Drawdown periods table (columns such as start/end/valley/days/max drawdown when available). |
| 53 | + |
| 54 | +``risk_free_rate`` (``float``) |
| 55 | + Effective risk-free rate used by tearsheet metrics. |
| 56 | + |
| 57 | +Return format |
| 58 | +------------- |
| 59 | + |
| 60 | +Return a ``dict`` mapping metric names to values. |
| 61 | + |
| 62 | +Supported formats: |
| 63 | + |
| 64 | +.. code-block:: python |
| 65 | +
|
| 66 | + {"Custom Metric A": 1.23} |
| 67 | + {"Custom Metric B": {"strategy": 1.23, "benchmark": 0.91}} |
| 68 | +
|
| 69 | +Behavior rules: |
| 70 | + |
| 71 | +1. Return ``{}`` if no custom metrics apply. |
| 72 | +2. Returning ``None`` is treated as no custom metrics. |
| 73 | +3. Returning a non-dict is ignored (with a warning). |
| 74 | +4. Exceptions inside the hook are caught; tearsheet generation continues. |
| 75 | + |
| 76 | +Example |
| 77 | +------- |
| 78 | + |
| 79 | +.. code-block:: python |
| 80 | +
|
| 81 | + class MyStrategy(Strategy): |
| 82 | + def tearsheet_custom_metrics( |
| 83 | + self, |
| 84 | + stats_df, |
| 85 | + strategy_returns, |
| 86 | + benchmark_returns, |
| 87 | + drawdown, |
| 88 | + drawdown_details, |
| 89 | + risk_free_rate, |
| 90 | + ): |
| 91 | + if strategy_returns.empty: |
| 92 | + return {} |
| 93 | +
|
| 94 | + p95 = float(strategy_returns.quantile(0.95)) |
| 95 | + avg_dd_days = ( |
| 96 | + float(drawdown_details["days"].mean()) |
| 97 | + if not drawdown_details.empty and "days" in drawdown_details.columns |
| 98 | + else 0.0 |
| 99 | + ) |
| 100 | +
|
| 101 | + return { |
| 102 | + "95th Percentile Daily Return": p95, |
| 103 | + "Average Drawdown Days": avg_dd_days, |
| 104 | + } |
| 105 | +
|
| 106 | +API reference (source of truth) |
| 107 | +------------------------------- |
| 108 | + |
| 109 | +The method docstring below is auto-loaded from the Strategy class and should be |
| 110 | +treated as the canonical API reference. |
| 111 | + |
| 112 | +.. automethod:: lumibot.strategies.strategy.Strategy.tearsheet_custom_metrics |
0 commit comments