Referencia técnica · Paso 11 · OOS Testing
La única data que ninguna fase anterior vio. El OOS no busca estrategias nuevas: confirma las que el walk-forward ya validó, sobre una ventana reservada, y mide cuánto se degrada su ventaja fuera de la muestra de descubrimiento.
El OOS solo scorea los combos que el walk-forward marcó como FDR-significativos. No testea todo el pool — hacerlo sería empezar una segunda búsqueda sobre datos frescos.
Fallback exploratorio: si ningún combo fue FDR-significativo (Monte Carlo apagado o nada pasó), el OOS testea los de PF pooleado > 1 con un warning explícito — "un 'pasa' acá no equivale a validación estadística". Es exploración, no confirmación.
El riesgo del OOS es que la preparación de features "vea" el futuro. Tres piezas data-dependientes se manejan fit-en-IS / apply-a-OOS.
| Pieza | Cómo se evita el leakage |
|---|---|
| Winsorización | Los bounds (clip de outliers) se aprenden solo con las filas IS y se aplican a toda la serie → el OOS se clipea con bounds del pasado, el IS queda idéntico. |
| σ GARCH | Único caso leakage-sensitive: no se fitea sobre el full. Se fitea en IS y se forecastea a OOS (extend_conditional_volatility_oos). Las demás policies (ATR, %, bar extreme, fija) son causales → computarlas sobre el full y windowear no filtra nada. |
| Targets | Los Target_* forward-looking de barras IS en la frontera miran barras OOS → "contaminados" por diseño. Inofensivos: el scoring OOS no usa targets, solo features causales + régimen + OHLC. Nunca se usan para re-seleccionar sobre OOS. |
El scoring OOS usa exactamente el mismo motor que el backtest y el walk-forward. La única diferencia es la ventana de datos — nunca la fórmula.
Las métricas se computan con el mismo compute_metrics (igual initial_capital, periods_per_day, pf_cap) que el ranking del Step 9. Invariante del pipeline: las métricas OOS e IS se calculan idénticas — lo único que cambia es la ventana de datos, no la definición. El excluded_regimes del combo se hereda inmutable y se aplica en el motor.
Se compara el PF OOS contra el del WFV. El gate exige que el rigor NO se afloje respecto del WFV — al contrario de lo que sería tentador para la "prueba final".
min_trades, min_profit_factor y max_pf_decay son parámetros de entrada (defaults abajo).
Audit fix #4 — la progresión de rigor no se invierte. Sería un error clásico que el gate final (OOS) fuera el más laxo de la cadena: dejaría pasar en la última prueba lo que las anteriores habrían frenado. Por eso min_profit_factor es ≥ el del WFV, no menor. El orden de salida pone primero los que pasan, luego por PF OOS (no por Δ PF, para no premiar a un perdedor que "mejora" mucho desde un PF bajo).
El OOS es, conceptualmente, la prueba más fuerte: es la única data que ningún paso del descubrimiento tocó. Pero es una herramienta más, no un veredicto absoluto.
Viene de 10 (WFO) y 9 (WFV, fuente de los combos). Sigue en 12: transfer a instrumentos emparentados.
Fuentes:
· services/oos_service.py — prepare_oos_features (fit-IS/apply-OOS), execute_oos, gate
· domain/validation_common/window_backtest.py — run_window_backtest (motor único, block_unknown)
· domain/backtester/garch.py — extend_conditional_volatility_oos
· domain/indicators_quality/winsorize.py — compute/apply_winsorization_bounds
· webapp/docs/STEPS_10_12_MIGRATION_PLAN.md §2.2 — data-prep leakage-safe, audit fix #4