Skip to content

fix: benchmark name shows as BENCHMARK in tearsheet#1014

Open
someposer wants to merge 2 commits into
Lumiwealth:devfrom
someposer:fix/benchmark-label-tearsheet
Open

fix: benchmark name shows as BENCHMARK in tearsheet#1014
someposer wants to merge 2 commits into
Lumiwealth:devfrom
someposer:fix/benchmark-label-tearsheet

Conversation

@someposer

@someposer someposer commented May 8, 2026

Copy link
Copy Markdown

Summary

  • df_final["benchmark"] returns a new Series on each access, so assigning to .name on that throwaway object is a no-op — QuantStats never saw the asset name and fell back to uppercasing the column label, showing BENCHMARK in the tearsheet header instead of the actual symbol (e.g. SPY).
  • Fix captures the series once with .rename() and passes it to both the primary and KDE-retry qs.reports.html call sites.
  • Adds tests/test_tearsheet_quantstats_args.py with two regression tests that assert the correct name reaches qs.reports.html on both paths. Tests were verified to fail against the buggy code and pass with the fix.

Test plan

  • uv run pytest tests/test_tearsheet_quantstats_args.py -v — both tests pass
  • Confirmed tests fail on the pre-fix code ('benchmark' instead of symbol name)
  • Existing tearsheet tests unaffected (test_tearsheet.py, test_tearsheet_quantstats_retry_unit.py)

Summary by CodeRabbit

  • Bug Fixes

    • Ensure benchmark series retain the intended asset label when passed to tearsheet report generation, keeping HTML and metrics outputs consistent.
  • Tests

    • Added regression tests that validate benchmark labeling is preserved during report generation, including retry paths triggered by KDE-related failures.

df_final["benchmark"] returns a new Series on each access, so
assigning to .name on that throwaway object is a no-op. QuantStats
never saw the asset name and fell back to uppercasing the column
label, producing "BENCHMARK" in the report header instead of the
actual symbol (e.g. "SPY").

Fix: capture the series once with .rename() before the qs.reports.html
call, and pass that named series to both the primary and KDE-retry
call sites.

Adds test_tearsheet_quantstats_args.py with two regression tests
that assert the correct name reaches qs.reports.html on both paths.
@someposer someposer requested a review from grzesir as a code owner May 8, 2026 05:12
@coderabbitai

coderabbitai Bot commented May 8, 2026

Copy link
Copy Markdown

Review Change Stack
No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 427e521d-696d-48c3-b1bc-bf7fe0138ca6

📥 Commits

Reviewing files that changed from the base of the PR and between c685cce and 045aee0.

📒 Files selected for processing (1)
  • lumibot/tools/indicators.py

📝 Walkthrough

Walkthrough

This PR refactors how benchmark series names are established in the create_tearsheet() function. Instead of mutating the benchmark Series' name in-place, the function now explicitly renames it before passing to QuantStats. Test coverage validates correct naming in both the primary flow and KDE failure retry scenarios.

Changes

Benchmark Series Naming and Validation

Layer / File(s) Summary
Benchmark Series Naming Implementation
lumibot/tools/indicators.py
create_tearsheet() now derives _benchmark_series by renaming df_final["benchmark"] to str(benchmark_asset) before passing to qs.reports.html, replacing prior in-place name mutation. QuantStats HTML generation and retry logic remain unchanged.
Test Scaffolding and Helpers
tests/test_tearsheet_quantstats_args.py
Adds _make_inputs() for deterministic test DataFrames, _base_kwargs() for common create_tearsheet arguments, and _fake_html_writer() for monkeypatching QuantStats.
Regression Tests
tests/test_tearsheet_quantstats_args.py
test_benchmark_series_name_passed_to_quantstats() validates the benchmark Series name is correctly passed to QuantStats. test_benchmark_series_name_preserved_on_kde_retry() confirms the KDE failure retry path receives the same correctly named Series.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐰 I renamed a Series, tidy and bright,
No side-effect shadows left in sight,
QuantStats now sees the benchmark's name,
On first try or retry, it's still the same,
Hop, nibble, and celebrate this tiny delight!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title clearly and concisely summarizes the main fix: correcting the benchmark name display in the tearsheet from a generic 'BENCHMARK' to the actual asset symbol.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 Pylint (4.0.5)
lumibot/tools/indicators.py

************* Module pylintrc
pylintrc:1:0: F0011: error while parsing the configuration: File contains no section headers.
file: 'pylintrc', line: 1
'known-third-party=lumibot' (config-parse-error)
[
{
"type": "convention",
"module": "lumibot.tools.indicators",
"obj": "",
"line": 289,
"column": 0,
"endLine": null,
"endColumn": null,
"path": "lumibot/tools/indicators.py",
"symbol": "line-too-long",
"message": "Line too long (102/100)",
"message-id": "C0301"
},
{
"type": "convention",
"module": "lumibot.tools.indicators",
"obj": "",
"line": 314,
"column": 0,
"endLine": null,
"endColumn": null,
"path": "lumibot/tools/indicators.py",
"symbol": "line-too-long",
"message": "Line too long (118/100)",
"message-id": "C0301"
},
{
"type": "convention",
"module": "lumibot.tools.indicators

... [truncated 67401 characters] ...

   "symbol": "wrong-import-order",
    "message": "first party import \"lumibot.tools.lumibot_logger.get_logger\" should be placed before local imports \"constants.LUMIBOT_DEFAULT_TIMEZONE\", \"yahoo_helper.YahooHelper\"",
    "message-id": "C0411"
},
{
    "type": "convention",
    "module": "lumibot.tools.indicators",
    "obj": "",
    "line": 22,
    "column": 0,
    "endLine": 26,
    "endColumn": 1,
    "path": "lumibot/tools/indicators.py",
    "symbol": "wrong-import-order",
    "message": "first party import \"lumibot.tools.parquet_utils.coerce_object_columns_to_json_strings\" should be placed before local imports \"constants.LUMIBOT_DEFAULT_TIMEZONE\", \"yahoo_helper.YahooHelper\"",
    "message-id": "C0411"
}

]

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
lumibot/tools/indicators.py (1)

1944-1950: ⚡ Quick win

_benchmark_series not propagated to the metrics generation paths.

Both the _write_tearsheet_metrics_json_fallback fallback (line 1944) and the metrics_json_fn call (line 1996) still pass df_final["benchmark"] — a Series with name="benchmark". If QuantStats uses the Series name to label the benchmark column in those outputs, those artifacts will still show "benchmark" / "BENCHMARK" instead of the asset symbol, inconsistent with the now-correct HTML tearsheet.

_benchmark_series is already in scope; the fix is mechanical.

♻️ Proposed fix
-        metrics_df = qs.reports.metrics(
-            df_final["strategy"],
-            df_final["benchmark"],
+        metrics_df = qs.reports.metrics(
+            df_final["strategy"],
+            _benchmark_series,
-                    metrics_json_fn(
-                        df_final["strategy"],
-                        df_final["benchmark"],
+                    metrics_json_fn(
+                        df_final["strategy"],
+                        _benchmark_series,

Also applies to: 1994-2002

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lumibot/tools/indicators.py` around lines 1944 - 1950, The metrics generation
paths still pass df_final["benchmark"] (a Series named "benchmark") to
qs.reports.metrics and metrics_json_fn causing outputs to be labeled
"benchmark"; replace those calls to use the already-in-scope _benchmark_series
instead. Update the call in the _write_tearsheet_metrics_json_fallback path
where metrics_df is created (qs.reports.metrics(..., df_final["benchmark"],
...)) and the metrics_json_fn invocation to pass _benchmark_series so QuantStats
receives the correct benchmark Series name; leave all other args
(risk_free_rate, custom_metrics, display flags) unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@lumibot/tools/indicators.py`:
- Around line 1944-1950: The metrics generation paths still pass
df_final["benchmark"] (a Series named "benchmark") to qs.reports.metrics and
metrics_json_fn causing outputs to be labeled "benchmark"; replace those calls
to use the already-in-scope _benchmark_series instead. Update the call in the
_write_tearsheet_metrics_json_fallback path where metrics_df is created
(qs.reports.metrics(..., df_final["benchmark"], ...)) and the metrics_json_fn
invocation to pass _benchmark_series so QuantStats receives the correct
benchmark Series name; leave all other args (risk_free_rate, custom_metrics,
display flags) unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7f939403-9bd4-4d5c-84da-1d6113328497

📥 Commits

Reviewing files that changed from the base of the PR and between 07d280b and c685cce.

📒 Files selected for processing (2)
  • lumibot/tools/indicators.py
  • tests/test_tearsheet_quantstats_args.py

df_final["benchmark"] is a Series named "benchmark", causing QuantStats
to label tearsheet benchmark columns as "benchmark" instead of the actual
asset name. Both the metrics_json_fn path and the fallback
_write_tearsheet_metrics_json_fallback now use _benchmark_series, which
is already renamed to the benchmark asset name at the call site.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants