Golden SkyGolden SkyGuía Técnica

Referencia técnica · Paso 8c · Backtest — salidas

Políticas de salida

Cómo se cierra un trade — fija, GARCH o trailing — y por qué la salida vive en el refinamiento, nunca en el descubrimiento. Meter la salida en el target sería co-optimizar señal y regla de cierre: irreproducible.

§0Notación

σtvolatilidad condicional GARCH en t
mfemax favorable excursion (fracción)
maemax adverse excursion (fracción)
peakprecio favorable máximo del trade
ATRtaverage true range en la barra t
Knº de variantes de exit policy

§1El scope: descubrimiento vs. refinamiento

Regla dura del pipeline: el Target que consumen lift y combinaciones se calcula SIEMPRE con TP/SL fijos del data_config. Las exit policies alternativas se evalúan solo sobre el top-K ya seleccionado.

FASE A — Discovery (barato, target FIJO)
  Target = TP/SL fijos → lift (FDR) → combinaciones (FDR) → top-K

FASE B — Refinamiento (caro, sobre top-K)
  re-backtestear top-K con distintas ExitPolicy → comparar Sharpe/DD/Recovery
  la que mejora consistentemente el conjunto va a producción

Violar esto se llama co-optimización de features y stopping rules — una de las fuentes principales de resultados irreproducibles en descubrimiento cuantitativo.

Los 3 riesgos de meter la salida en el target

RiesgoQué pasa
Data snooping ×KCada variante define un Target distinto → otro ranking → otro set de "ganadores". El FDR habría que hacerlo sobre 10⁹ × K hipótesis. Sin corregir por K, se eligen reglas que solo matchean el ruido de esa exit policy.
Path-dependence / circularidadSi el target depende de un modelo (GARCH, trailing) ajustado a los mismos datos, el edge aparente no viene de la regla — viene de que el target se ajustó al ruido del período. Colapsa silencioso si cambia el régimen de volatilidad.
Costo ×KEl discovery recalcula targets K veces. En el timeframe M5 (velas de 5 min) + K=4 + GARCH rolling, un run de ~30 min pasa a ~8 h. Ese costo no compra edge, compra sesgo.

Única excepción legítima: cuando la salida es estructural al edge (hipótesis documentada ANTES, UNA variante, sensibilidad post-selección). Ya migrada como los targets Target_*_Direction (edge direccional puro, agnóstico de magnitud) — un solo target_column por run.

§2El contrato ExitPolicy

Toda policy se enchufa al motor sin tocarlo. Dos rutas de ejecución, estado del trade en fracción del entry, sin memoria entre trades.

is_vectorizable = True → fast-path: resolve_vectorized resuelve el trade entero con NumPy
is_vectorizable = False → slow-path: evaluate se llama bar-by-bar hasta cerrar o timeout
TradeState mfe_pct = max pnl% favorable observado (piso 0)
mae_pct = max pnl% adverso observado (magnitud, piso 0)
current_pnl_pct = pnl al close de la barra (puede ser negativo)

Las policies son frozen y funciones puras: no mutan arrays ni acumulan estado — cada trade es una llamada independiente. Para validación por folds, windowed(start, end) slicea los arrays puntuales (σ, ATR, extremos) a la ventana, sin truncar el look-behind.

§3Resolución base y Fixed

La política por defecto: TP/SL fijos, primer toque gana. Dos detalles de realismo que la separan del legacy: gap conservador y desempate intrabar.

find_exit primer toque de TP o SL dentro de [entry_idx, entry_idx + H]
ninguno → timeout al close de min(entry_idx + H, n−1)

Gap conservador (mejora vs. legacy)

TP con gap a favor → se captura solo hasta el strike // no asumir venta al gap
SL con gap adverso (open pasó el strike) → exit = open (peor) // gap_adjusted = True

El legacy usaba siempre exit = strike, subestimando el slippage en equities con gaps overnight. El modelo conservador nunca infla ganancias con gaps favorables.

Empate intrabar (TP y SL en la misma barra)

Sin datos intrabar, la dirección de la vela es el proxy de la trayectoria O→H→L→C:

DirecciónVelaTrayectoriaPrimer toque
longalcistaO→L→H→CLow → SL
longbajistaO→H→L→CHigh → TP
shortalcistaO→L→H→CLow → TP
shortbajistaO→H→L→CHigh → SL

Heurística validada por paridad con el create_target del legacy. Cambiarla rompe la reproducibilidad histórica.

§4GARCH — TP/SL escalado por volatilidad

La regla no cambia; lo que se adapta es la magnitud del target a la volatilidad del momento de entrar. Fijos durante el trade — no es un trailing.

GarchDynamicExitPolicy tp_pct = σt · tp_multiplier  // default 2.0
sl_pct = σt · tp_multiplier · sl_ratio  // default 0.5 → R:R nominal 2:1

En una barra volátil (σ=0.008): TP=1.6%, SL=0.8%. En una tranquila (σ=0.003): TP=0.6%, SL=0.3%. Se calculan UNA vez en la barra de entrada y no se recalculan. Fallback: si σt es NaN o ≤ 0 (warm-up del GARCH), cae al TP/SL fijo del data_config — nunca falla en runtime. Reason tags: tp_garch / sl_garch, o tp/sl si usó fallback.

§5Trailing — tres variantes

El SL persigue al precio favorable: solo se acerca, nunca se aleja. Las tres son iterativas (necesitan mfe_pct por barra) y comparten use_fixed_tp (default True → sale por lo primero que toque).

VarianteSL vigenteCuándo
Percentpeak · (1 − trailing_pct)Volatilidad estable. Variante A: activo desde la barra 1. Variante B: SL fijo hasta mfe ≥ activation_pct, luego trailing.
ATR (Chandelier)peak − mult · ATRtVolatilidad variable (crypto, forex exóticos). Se autoajusta sin re-calibrar por activo. No dispara mientras ATR sea NaN (warm-up).
Bar Extrememin-low de N barras previasNiveles estructurales (swings, soportes). La ventana excluye la barra actual — evita path-dependence intrabar.

El peak deriva del mfe_pct post-update (incluye el high/low de la propia barra) y el disparo compara contra el low (long) / high (short) — paridad con Fixed. Empate TP + trailing SL: misma heurística de vela. Los extremos de Bar Extreme se pre-computan causalmente sobre la serie completa (fold-safe).

§6La regla de uso

La elección del trailing NO se optimiza sobre OOS. Se prueba en el IS post-discovery, se congela, y se valida en WFV/OOS/transfer sin re-tunear.

1. baseline Fixed → anotar Sharpe, PF, MaxDD, distribución de exit_reasons
2. elegir UNA variante según la naturaleza del combo // no probar las 3 en paralelo
3. parámetros medios (no extremos) → comparar contra baseline
4. si mejora, fijar y correr WFV + OOS + transfer. Sin más tuning.
Anti-pattern. Probar 20 combinaciones de trailing_pct × activation_pct × initial_sl_pct y quedarse con la mejor es data snooping sobre parámetros de exit — contamina la validación downstream aunque no sea sobre el target. Misma regla que en discovery: hipótesis primero, parámetros según la hipótesis, no búsqueda.

Viene de 8a (motor) y 8b (métricas). Sigue en 8d: el filtro de régimen.

Fuentes:
· domain/backtester/exit_policies/ — base, fixed, garch, trailing_percent, trailing_atr, trailing_bar_extreme
· domain/backtester/exit_resolution.py — find_exit (gap conservador, empate intrabar)
· webapp/docs/EXIT_POLICY_ARCHITECTURE.md — scope discovery/refinamiento, los 3 riesgos, guía de parámetros