Some checks failed
ci/woodpecker/push/woodpecker Pipeline was canceled
In-place extension (no fork). Existing tests still pass; new fields are optional and None when no benchmark is supplied.
99 lines
2.9 KiB
Python
99 lines
2.9 KiB
Python
"""Kevin-extension tests for compute_metrics (alpha/beta/winners/losers/best/worst)."""
|
|
|
|
from datetime import datetime, timedelta, timezone
|
|
from decimal import Decimal
|
|
|
|
import pandas as pd
|
|
|
|
from backtester.metrics import compute_metrics
|
|
|
|
|
|
def _ts(day):
|
|
return datetime(2026, 5, 15, tzinfo=timezone.utc) + timedelta(days=day)
|
|
|
|
|
|
def test_compute_metrics_returns_alpha_and_beta_with_benchmark():
|
|
spy = pd.DataFrame(
|
|
{
|
|
"open": [500, 500, 510, 510, 520],
|
|
"high": [500] * 5,
|
|
"low": [500] * 5,
|
|
"close": [500, 500, 510, 510, 520],
|
|
"volume": [0] * 5,
|
|
},
|
|
index=pd.date_range("2026-05-15", periods=5, freq="D", tz="UTC"),
|
|
)
|
|
trades = [
|
|
{
|
|
"symbol": "NVDA",
|
|
"source_mention_id": 1,
|
|
"entry_at": _ts(0),
|
|
"entry_price": Decimal("100"),
|
|
"exit_at": _ts(4),
|
|
"exit_price": Decimal("110"),
|
|
"qty": Decimal("10"),
|
|
"pnl_usd": Decimal("100"),
|
|
"pnl_pct": Decimal("10"),
|
|
"holding_days_actual": 4,
|
|
}
|
|
]
|
|
curve = [(_ts(i), Decimal(str(100000 + 25 * i))) for i in range(5)]
|
|
|
|
result = compute_metrics(
|
|
trade_log=trades,
|
|
equity_curve=curve,
|
|
initial_capital=Decimal("100000"),
|
|
benchmark_bars=spy,
|
|
)
|
|
|
|
assert result.alpha_vs_spy_pct is not None
|
|
assert result.beta_vs_spy is not None
|
|
|
|
|
|
def test_compute_metrics_returns_winners_losers_best_worst():
|
|
trades = [
|
|
{
|
|
"symbol": "NVDA",
|
|
"source_mention_id": 1,
|
|
"entry_at": _ts(0),
|
|
"entry_price": Decimal("100"),
|
|
"exit_at": _ts(5),
|
|
"exit_price": Decimal("110"),
|
|
"qty": Decimal("10"),
|
|
"pnl_usd": Decimal("100"),
|
|
"pnl_pct": Decimal("10"),
|
|
"holding_days_actual": 5,
|
|
},
|
|
{
|
|
"symbol": "INTC",
|
|
"source_mention_id": 2,
|
|
"entry_at": _ts(0),
|
|
"entry_price": Decimal("50"),
|
|
"exit_at": _ts(5),
|
|
"exit_price": Decimal("45"),
|
|
"qty": Decimal("20"),
|
|
"pnl_usd": Decimal("-100"),
|
|
"pnl_pct": Decimal("-10"),
|
|
"holding_days_actual": 5,
|
|
},
|
|
]
|
|
result = compute_metrics(
|
|
trade_log=trades,
|
|
equity_curve=[(_ts(0), Decimal("100000"))],
|
|
initial_capital=Decimal("100000"),
|
|
)
|
|
|
|
assert result.avg_winner_pct == Decimal("10")
|
|
assert result.avg_loser_pct == Decimal("-10")
|
|
assert result.best_trade == {"symbol": "NVDA", "pnl_pct": Decimal("10")}
|
|
assert result.worst_trade == {"symbol": "INTC", "pnl_pct": Decimal("-10")}
|
|
|
|
|
|
def test_compute_metrics_no_benchmark_omits_alpha_beta():
|
|
result = compute_metrics(
|
|
trade_log=[],
|
|
equity_curve=[(_ts(0), Decimal("100000"))],
|
|
initial_capital=Decimal("100000"),
|
|
)
|
|
assert result.alpha_vs_spy_pct is None
|
|
assert result.beta_vs_spy is None
|