diff --git a/src/fxhnt/adapters/web/templates/cockpit.html b/src/fxhnt/adapters/web/templates/cockpit.html
index 52f1663..e588220 100644
--- a/src/fxhnt/adapters/web/templates/cockpit.html
+++ b/src/fxhnt/adapters/web/templates/cockpit.html
@@ -18,7 +18,7 @@
{% if f.bt_status %}
{{ f.bt_status }}
- Sh {{ '%.2f'|format(f.bt_sharpe) }} / DSR {{ '%.2f'|format(f.bt_dsr) }}
+ Sh {{ '%.2f'|format(f.bt_sharpe) if f.bt_sharpe is not none else '—' }} / DSR {{ '%.2f'|format(f.bt_dsr) if f.bt_dsr is not none else '—' }}
{% else %}
—
{% endif %}
diff --git a/src/fxhnt/adapters/web/templates/strategy.html b/src/fxhnt/adapters/web/templates/strategy.html
index 93c5d37..afe589b 100644
--- a/src/fxhnt/adapters/web/templates/strategy.html
+++ b/src/fxhnt/adapters/web/templates/strategy.html
@@ -12,10 +12,10 @@
{% if d.bt_status %}
backtest verdict {{ d.bt_status }}
as of {{ d.bt_as_of or '—' }}
-CAGR {{ '%+.1f'|format(100*d.bt_cagr) }}%
- · vol {{ '%.1f'|format(100*d.bt_ann_vol) }}% · Sharpe {{ '%.2f'|format(d.bt_sharpe) }}
- · maxDD {{ '%.1f'|format(100*d.bt_maxdd) }}% · DSR {{ '%.2f'|format(d.bt_dsr) }}
- · OOS Sharpe {{ '%.2f'|format(d.bt_oos_sharpe) }}
+CAGR {{ '%+.1f'|format(100*d.bt_cagr) if d.bt_cagr is not none else '—' }}%
+ · vol {{ '%.1f'|format(100*d.bt_ann_vol) if d.bt_ann_vol is not none else '—' }}% · Sharpe {{ '%.2f'|format(d.bt_sharpe) if d.bt_sharpe is not none else '—' }}
+ · maxDD {{ '%.1f'|format(100*d.bt_maxdd) if d.bt_maxdd is not none else '—' }}% · DSR {{ '%.2f'|format(d.bt_dsr) if d.bt_dsr is not none else '—' }}
+ · OOS Sharpe {{ '%.2f'|format(d.bt_oos_sharpe) if d.bt_oos_sharpe is not none else '—' }}
{% endif %}
per-period returns & rolling mean (raw — no normalization)
diff --git a/tests/integration/test_web.py b/tests/integration/test_web.py
index 9b2880d..c8874ba 100644
--- a/tests/integration/test_web.py
+++ b/tests/integration/test_web.py
@@ -44,6 +44,14 @@ def test_cockpit_shows_backtest_verdict_column() -> None:
assert "DSR" in r.text
+def test_strategy_detail_renders_backtest_verdict() -> None:
+ r = _client().get("/strategy/eqfactor_long")
+ assert r.status_code == 200
+ assert "backtest verdict" in r.text # verdict block header present
+ assert "badge PASS" in r.text # PASS badge rendered
+ assert "DSR" in r.text # backtest metric label present
+
+
def test_strategy_detail_renders() -> None:
r = _client().get("/strategy/combined")
assert r.status_code == 200 and "2026-06-06" in r.text
|