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