BacktestReport#
Description#
BacktestReport turns a backtest engine's per-step [equity, pnl, position, cost]
into the running report columns. It carries the aggregation that a report needs so
that pure-C++ callers get it too, and the Python backtest_report
helper is a thin wrapper that labels these columns and reads the last row for the
summary.
The six outputs, in order, are:
drawdown:equity - running peak equity, the dollar drawdown (<= 0). This is the dollar form, not the percentageDrawdownop, because an equity curve starts at zero and can go negative, where a percentage of the peak is undefined.cum_cost: the running sum ofcost.turnover: the running sum of|position change|; the first step counts the move from flat.trades: the running count of steps that changed the position.max_drawdown: the running minimum ofdrawdown, the worst so far (<= 0).sharpe: the runningmean(pnl) / sample std(pnl),NaNuntil two finitepnlvalues with positive dispersion.
Every column is a causal accumulator, so a full-sample statistic is the last
finite value: total_pnl is the last equity, max_drawdown the last
max_drawdown, and so on. nan_policy: ignore: a row with any NaN field is a
skipped bar, so the state holds and the output row is NaN, recovering at the
next finite bar, exactly as CumSum and the other accumulators do.
Examples#
Usage plot#
import numpy as np
import plotly.graph_objects as go
from plotly.subplots import make_subplots
from screamer import BacktestPriceTarget, BacktestReport
rng = np.random.default_rng(0)
n = 500
price = 100 + np.cumsum(rng.standard_normal(n) * 0.3)
signal = np.sign(rng.standard_normal(n))
equity, pnl, position, cost = (BacktestPriceTarget(spread=0.0005)(signal, price).T)
rep = BacktestReport()(equity, pnl, position, cost) # (n, 7)
drawdown, trades = rep[:, 0], rep[:, 3]
fig = make_subplots(rows=3, cols=1, shared_xaxes=True, row_heights=[0.4, 0.3, 0.3],
vertical_spacing=0.06)
fig.add_trace(go.Scatter(y=equity, name='equity', line=dict(color='steelblue')), row=1, col=1)
fig.add_trace(go.Scatter(y=drawdown, name='drawdown', line=dict(color='crimson'),
fill='tozeroy'), row=2, col=1)
fig.add_trace(go.Scatter(y=trades, name='trades', line=dict(color='seagreen')), row=3, col=1)
fig.update_layout(title='BacktestReport: running equity, drawdown, and trade count',
yaxis=dict(title='equity ($)'), yaxis2=dict(title='drawdown ($)'),
yaxis3=dict(title='trades'),
margin=dict(l=20, r=20, t=60, b=20),
legend=dict(orientation='h', yanchor='bottom', y=1.02, xanchor='right', x=1))
fig.show()