Referencia técnica · Paso 10 · Walk-Forward Optimization
La prueba más exigente del descubrimiento: en vez de validar un combo fijo, el WFO re-corre todo el discovery dentro de cada fold y pregunta si el mismo concepto reaparece en varias ventanas. Un edge real se re-descubre solo; uno espurio, no.
La diferencia con el walk-forward (§9): allí un combo fijo se testea por folds. Acá, dentro del train de cada fold, se vuelve a correr el discovery completo (lift + combinaciones) desde cero.
La pregunta cambia de "¿este combo aguanta?" a "¿el proceso vuelve a encontrar esta idea cuando le doy otra ventana?". Reproducibilidad temporal del descubrimiento, no de un resultado puntual.
Dos combos con las mismas familias de indicador pero distintos umbrales son la misma idea. La signature semántica captura eso.
Se reusa semantic_signature del dedup de combinaciones — misma definición de "regla" que produjo el pool IS. n_folds_discovered cuenta folds distintos: un fold que halla el concepto dos veces cuenta 1.
Re-correr el discovery por fold es peligroso: si una sola fila de test se cuela en el train, el edge se fabrica. Cuatro guardas lo impiden.
| Guarda | Cómo |
|---|---|
| Discovery restringido al train | Se slicea df_indicators / df_binary a [train_start, train_end) antes de llamar al discovery. La restricción es estructural — físicamente no recibe filas de test. |
| Embargo | El fold se arma con gap = max_holding: los targets forward-looking de las últimas barras de train caen en el gap, nunca en la test-window. |
| Params idénticos | Se reusan los mismos params de lift/combinations del pipeline. WFO solo fuerza discovery_train_ratio=None (el fold YA es el train — no aplica otro split interno). |
| df_binary conservador | La frecuencia se calculó sobre el IS completo; el filtro técnico per-fold (min_signals sobre train) descarta las reglas raras. Residual conservador: a lo sumo omite alguna, nunca fabrica edge. |
El corazón anti-snooping del WFO: qué decide que un concepto pase, y qué se limita a informar.
n_folds_discovered — cuántas veces el discovery lo re-encontró en las ventanas de train. La performance en las test-windows se calcula y se REPORTA, pero nunca entra a la selección. Si el test decidiera qué pasa, estaríamos snoopeando el test — exactamente lo que WFO existe para evitar.Por eso match_signatures devuelve el set completo de conceptos evaluados (sin filtrar por min_folds) — el gate lo aplica el caller. Reportar todo lo evaluado, no solo los ganadores, es la defensa contra el selection bias.
Para cada concepto, se backtestean sus reglas concretas en la test-window de cada fold donde se re-descubrió, con el mismo motor que todo el resto (misma exit policy, costos, triple-swap, filtro de régimen).
El pooling suma las ganancias y pérdidas reales antes de dividir (mismo criterio que el walk-forward, audit #1 — promediar razones puede invertir el signo). wfo_efficiency cerca de 1 = el edge se sostiene fuera del train; muy por debajo = degradó.
Un concepto pasa si se re-descubrió lo suficiente Y su performance test pooleada supera el umbral. min_folds_discovered y min_profit_factor son parámetros de entrada — abajo se muestran sus valores por defecto (config completa en §7).
test_pf_per_fold, test_pf_std, test_pf_min y n_losing_folds (folds con trades donde PF<1). Un pooled bueno con pf_min ≪ 1 o muchos losing_folds = un fold outlier lo sostiene → edge no robusto. Es lectura del trader, no un veredicto automático.Un WFO que "no re-descubre" puede estar diciendo dos cosas muy distintas: no hay edge, o no hay datos suficientes para encontrarlo.
Debajo de ese piso, el discovery (lift + combinations con FDR) pierde poder estadístico: n_folds_discovered baja por falta de datos, no por ausencia de edge. El binding constraint del WFO es el train por fold — la salida es bajar n_folds o ampliar el IS, no aflojar el gate.
Viene de 9 (walk-forward / WFV). Sigue en 11: el test out-of-sample.
Fuentes:
· domain/walk_forward_optimization/rediscovery.py — match_signatures, RediscoveredConcept
· domain/combinations/dedup.py — semantic_signature (bases sin umbrales)
· services/walk_forward_optimization_service.py — discover_on_train, score_concepts, execute_wfo
· domain/validation_common/pooled_metrics.py — pool_fold_metrics
· webapp/docs/STEPS_10_12_MIGRATION_PLAN.md §2.1bis — diseño y puntos de aislamiento