trader.performance.benchmark module¶
Account ROI vs. a market benchmark (requirements §12.1, GitLab issue #19).
Not the same feature as the backtest’s buy-and-hold baseline (MR !41, trader.backtest.metrics.buy_and_hold + trader.backtest.engine). That one answers “did this strategy beat holding the SAME symbol it traded”, computed from simulated bars over a backtest window. This module answers a different question: “did the whole real ACCOUNT, over its real account_snapshots history (requirements §10), beat a market INDEX” — QQQ/VOO by default, configurable via config/reporting.yaml. It is a portfolio-level report over live data, not a per-symbol simulation.
It deliberately REUSES total_return_pct and buy_and_hold from trader.backtest.metrics rather than reimplementing them: both are pure functions over two Decimal numbers with no backtest-specific assumption baked in (no slippage, no whole-share constraint), so there is nothing here to duplicate — only to call.
- class trader.performance.benchmark.AccountReturn(start_at, end_at, starting_value, ending_value, return_pct, snapshot_count)[source]¶
Bases:
objectThe account’s real return between the earliest and latest snapshot in a window.
- Parameters:
start_at (datetime)
end_at (datetime)
starting_value (Decimal)
ending_value (Decimal)
return_pct (Decimal)
snapshot_count (int)
- start_at: datetime¶
- end_at: datetime¶
- starting_value: Decimal¶
- ending_value: Decimal¶
- return_pct: Decimal¶
- snapshot_count: int¶
How many snapshots the window actually spanned — surfaced so a report built from exactly 2 sparse snapshots can be told apart from one built from a dense history, even though both produce one first/last pair.
- class trader.performance.benchmark.BenchmarkReturn(ticker, start_at, end_at, starting_price, ending_price, ending_value, return_pct)[source]¶
Bases:
objectA buy-and-hold return for one ticker over the SAME window as an AccountReturn.
- Parameters:
ticker (str)
start_at (datetime)
end_at (datetime)
starting_price (Decimal)
ending_price (Decimal)
ending_value (Decimal)
return_pct (Decimal)
- ticker: str¶
- start_at: datetime¶
- end_at: datetime¶
- starting_price: Decimal¶
- ending_price: Decimal¶
- ending_value: Decimal¶
What starting_value (an AccountReturn’s) would be worth today had it bought this ticker instead, fractional shares — see buy_and_hold’s own docstring for why fractional, not whole, shares are used here too.
- return_pct: Decimal¶
- trader.performance.benchmark.account_return(snapshots)[source]¶
The account’s return from its earliest to its latest snapshot in snapshots.
Ordered by (captured_at, id) before taking first/last — the same tie-break SnapshotRepository itself uses — so a caller that already queried in order is not silently trusted, and one that did not is corrected rather than producing a return over the wrong pair.
- Raises:
PerformanceError – fewer than 2 snapshots. A single point cannot show a return, and reporting 0.00% would read as “flat” rather than “unmeasurable” — the same reasoning run_backtest uses for a too-short window.
- Parameters:
snapshots (Sequence[AccountSnapshot])
- Return type:
- trader.performance.benchmark.benchmark_return(ticker, bars, capital)[source]¶
The buy-and-hold return ticker would have produced over bars.
Marked close-to-close, not open-to-open or with slippage applied: this is a REPORT of what a lump-sum buy-and-hold would have returned over the account’s snapshot window, not an executable order, so there is no fill price to model.
- Raises:
PerformanceError – fewer than 2 bars. Same reasoning as account_return — a single bar cannot show a return.
- Parameters:
ticker (str)
bars (Sequence[Bar])
capital (Decimal)
- Return type:
- trader.performance.benchmark.excess_return_pct(account, benchmark)[source]¶
How much the account beat (or lagged) benchmark. Negative is a real answer.
Both operands are already display-rounded Decimal`s (via `_pct), so this subtraction loses no precision worth quantizing again — unlike BacktestResult.excess_return_pct (MR !41), which is a @property on one object because both figures live together there; here the two figures come from two different functions, so this is a plain function instead.
- Parameters:
account (AccountReturn)
benchmark (BenchmarkReturn)
- Return type:
Decimal