Golden SkyGolden SkyGuía Técnica

Referencia técnica · Paso 8b · Backtest — métricas

Equity y ratios

Las métricas que produce el backtest a partir de la lista de trades: la curva de capital, el profit factor con cap, la expectativa, los ratios ajustados por riesgo (Sharpe, Sortino, Calmar) desde la equity diaria, y las medidas de drawdown.

§0Notación

pnliP&L neto del trade i
gp / glgross profit / gross loss
rdretorno diario de la equity
DDidrawdown en la barra i
MARretorno mínimo aceptable (0)
peakmáximo acumulado de la equity

§1Curva de capital

La equity parte del capital inicial y salta con el P&L de cada trade en su barra de salida — plana entre trades.

equity[ · ] = initial_capital
∀ trade: equity[ exit_rel : ] += pnl  // escalón en cada exit_idx

§2Rentabilidad

Profit factor (capeado), expectativa y tasa de acierto sobre el conjunto de trades.

win_rate = winnerstotal  ·  expectancy = WR · avg_win + (1−WR) · avg_loss
gp = Σ(pnl > 0)  ·  gl = |Σ(pnl ≤ 0)|
PF = min( gpgl , pf_cap )  // pf_cap = 10

Si gl = 0 y hay ganancia, PF es → se reporta el cap (con flag pf_capped). Si no hay ganancia, PF = 0. El cap evita que unos pocos trades sin pérdidas produzcan un PF engañosamente enorme.

§3Ratios ajustados por riesgo

Sharpe y Sortino se calculan sobre los retornos diarios de la equity (daily bins con fence-post fix), anualizados a 252 días.

daily returns rd = Δ equity_diariaequity_diaria[:−1]
Sharpe = mean(rd)std(rd) · √252  // std ddof=0 (poblacional)
Sortino = mean(rd) − MARDDdown · √252    DDdown = √ mean( min(0, rd−MAR)² )

Sortino castiga solo la volatilidad a la baja: el denominador promedia min(0, r−MAR)² sobre todas las N observaciones (no solo las negativas). Con MAR=0, garantiza Sortino ≥ Sharpe cuando la media es positiva. Si no hay ningún retorno bajo MAR → None (honesto), no el sharpe×1.5 inventado del legacy.

§4Drawdown y derivados

El drawdown y el Ulcer se calculan sobre la equity barra a barra (más granular que los bins diarios de los ratios).

bar-a-bar peak = cummax(equity)  ·  DDi = peaki − equityipeaki
max_drawdown = max( DDi )
Ulcer = √ mean( DDi² )  // penaliza la DURACIÓN del drawdown
Recovery = total_pnlmax_dd_abs  ·  Calmar = retorno_anual_compuestomax_dd_pct

El Ulcer Index distingue un DD de 20% que dura 5 barras de uno que dura 50 (mismo max_dd, distinto Ulcer) — mide el "dolor" sostenido, no solo el pico. La granularidad barra-a-barra (vs diaria de Sharpe) tiene impacto bajo porque la equity es plana entre salidas.

§5R-multiples y motivos de salida

Complementos diagnósticos del resultado.

Ri = pnliriesgoi  →  mean · std · min · max · %positivos
exit_reasons: agrupados por familia  // tp_garch, tp_trailing → cuentan como "tp"

Los motivos de salida agrupados por familia permiten leer la distribución (cuántos TP, SL, timeout) sin que las variantes de exit policy (GARCH, trailing) fragmenten el conteo.

Viene de 8a: entrada al next-open, resolución de salida, costos, sizing y bancarrota.

Fuentes:
· domain/backtester/metrics.py — compute_metrics, PF (cap), _sharpe_from_daily_returns, _sortino_from_daily_returns, _max_drawdown, _ulcer_index, _daily_bin_indices