Niveles de capacidad: descubierto, implementado, verificado, aprobado¶
Un catálogo que mezcla «el motor lo tiene», «el proyecto lo calcula», «hay tests que lo prueban» y «alguien lo ha aprobado para un uso» es un catálogo que miente sin querer. El banco de pruebas separa esas cuatro afirmaciones en una escalera de niveles y obliga a que cada indicador declare exactamente uno: el más alto que ha alcanzado.
Qué vas a aprender¶
- Los cuatro niveles:
discovered → implemented → verified → approved. - Qué evidencia exige cada peldaño y dónde vive en el código.
- El checklist de promoción de un indicador.
- Por qué no hay auto-promoción y qué significa
production: false. - Dónde aparecen los niveles (API, tabla, fichas de ayuda).
1. La escalera¶
flowchart LR
D["discovered<br/>el motor lo expone<br/>347"] -->|contrato del proyecto| I["implemented<br/>primitiva registrada<br/>39"]
I -->|tests de fidelidad<br/>+ paridad por carril| V["verified<br/>evidencia automática<br/>39"]
V -->|registro de aprobación<br/>con alcance| A["approved<br/>benchmark + research<br/>39"]
| Nivel | Afirmación | ¿Contrato del proyecto? | ¿POST /v1/indicators? |
Evidencia |
|---|---|---|---|---|
discovered |
Un motor instalado lo expone en al menos un carril | No | Solo por paso directo al motor (342 de 347), sin garantías | el comportamiento del propio motor |
implemented |
Es una primitiva registrada: fórmula canónica, parámetros, warmup, clave de caché, rol | Sí | Sí, canónico (33 de 39; las otras 6 leen series derivadas o paneles) | tests de registro/contrato |
verified |
Implementada y su fidelidad y la paridad de sus carriles están probadas | Sí | Sí | verified_test_id + streaming_verified_test_id |
approved |
Verificada y un registro declarado le concede un alcance | Sí | Sí | finazbench/features/approval.py |
La escalera es monótona: approved implica verified, que implica implemented. Una entrada que
solo es discovered nunca se presenta como ninguna de las otras tres.
Analogía: el carné de conducir
discovered es saber que existe el coche. implemented, haber estudiado el código de circulación
(hay reglas escritas). verified, haber aprobado el examen práctico (hay evidencia). approved,
tener el carné para una categoría concreta: aquí, «benchmark» e «investigación», nunca
«producción».
2. Cada peldaño con detalle¶
2.1 discovered (347 entradas)¶
finazbench/features/catalog.py recorre los wheels instalados (_vectorta_records,
_nautilus_records): una función batch o una clase *Stream en VectorTA; una subclase concreta de
Indicator en Nautilus. La sonda ejecuta 100 puntos antes de declarar un carril disponible.
Un indicador descubierto no tiene contrato: ni formula_version, ni parámetros canónicos, ni
first_valid, ni warmup, ni clave de caché. «Descubierto» no significa roto ni de segunda: significa
que se usa con la semántica de su motor.
El paso directo al motor NO promueve el nivel
Desde el paquete de la API de 2026-09-22, POST /v1/indicators calcula 342 de estas 347
entradas por paso directo al motor (execution_mode: engine_passthrough). La respuesta
lo declara: capability.level sigue siendo discovered, guarantees.canonical_formula es
false, first_valid_rule es observed y no hay paridad. Poder calcular un indicador no
es tener un contrato: la única vía para subir de nivel es el checklist de promoción de este
capítulo. Las 5 entradas no ejecutables (rsmk, spearman_correlation,
decisionpoint_breadth_swenlin_trading_oscillator, SpreadAnalyzer, half_causal_estimator)
explican su motivo en capability.not_computable_reason.
2.2 implemented (39)¶
La primitiva se registra en finazbench/features/ con:
- fórmula con
formula_version(registry.build_speces el único constructor legítimo); - parámetros canónicos, validados y de orden estable;
inputs/outputsy regla defirst_valid(por salida si difieren);- warmup (
warmup.required_history) con reglas de cadena y paralelismo; - clave de caché determinista (
FeatureSpec.cache_key_parts); - rol en el catálogo de estrategias.
Las 39 se registran en tres fases: 11 de fase A, 23 de fase B y 5 de fase C. De ellas,
33 son ejecutables por POST /v1/indicators; las otras 6 (bandwidth, rolling_quantile,
linreg_endpoint, sma_of, session_position, cross_sectional_rank) leen series derivadas o
paneles y solo se usan dentro de estrategias (422 con not_computable_reason).
2.3 verified (39)¶
Dos pruebas independientes:
- Fidelidad del kernel canónico a la fórmula (ventanas, semillas,
first_valid, NaN, bordes):tests/features/test_canonical.py(fase A), la suitetest_ext_*(fases B/C) ytests/features/test_streaming_ext.py. - Paridad de cada carril declarado: batch y stream de cada motor contra la canónica desde el
first_validcomún, con la tolerancia registrada (y los streams de Nautilus contra su propio batch, exactos). Evidencia:tests/features/test_parity.pyytest_streaming_ext.py::TestParidadStreamBatch.
2.4 approved (39)¶
Una entrada explícita y revisable en finazbench/features/approval.py con su alcance:
| Alcance | Valor |
|---|---|
benchmark |
true: se puede usar en campañas y resultados publicados |
research |
true: barridos, estudios, derivaciones |
production |
false |
production: false no es una tarea pendiente
Este repositorio es un banco de pruebas para comparar motores y medir paridad. Ningún
indicador ni ninguna estrategia se declara aprobada para operar con dinero real, ni aquí ni en el
registro. Un resultado approved es válido para benchmark e investigación, y para nada más.
3. Checklist de promoción¶
Para llevar un indicador de discovered a approved (cambio manual y revisable):
| # | Paso | Qué se entrega |
|---|---|---|
| 1 | Fórmula canónica | Definición exacta: semillas, ventana inclusiva o no, escala, política de NaN. Si el motor difiere, se dice. |
| 2 | Parámetros | Nombres, tipos, límites y defaults; cuáles afectan al número (entran en la clave de caché). |
| 3 | first_valid |
Regla por salida; first_valid_by_output si las salidas calientan distinto. |
| 4 | Warmup | warmup.required_history con dependencias paralelas y encadenadas (R1–R3). Un warmup mal declarado convierte NaN en señales falsas. |
| 5 | Test de fidelidad | Valores calculados a mano o fixture congelado; debe fallar si cambia la fórmula. |
| 6 | Carriles de paridad | Proveedores soportados, tolerancias con motivo, test stream/batch por carril. Un carril que calcula otra variante queda variant/unsupported con nota. |
| 7 | Registro de aprobación | Solo con 1–6 en verde: entrada en approval.py con su alcance (por defecto benchmark + research). |
flowchart TD
S[indicador descubierto] --> F{¿fórmula canónica<br/>decidida y escrita?}
F -- no --> S
F -- sí --> T{¿test de fidelidad<br/>y paridad en verde?}
T -- no --> I[implemented<br/>no verificado]
T -- sí --> R{¿registro de aprobación<br/>revisado?}
R -- no --> V[verified]
R -- sí --> A[approved<br/>benchmark + research]
4. Por qué no hay auto-promoción¶
La introspección puede probar que un nombre existe y corre. No puede probar:
- cuál es la fórmula canónica;
- cómo siembra el motor;
- por qué diverge de un indicador hermano del otro motor.
Esas son decisiones que exigen leer el motor y escribir un test. Por eso quedan registradas como un diff revisado, no como efecto secundario de actualizar un wheel.
Un caso real: supertrend de VectorTA
vector_ta.supertrend existe y corre. Pero su segunda salida es una bandera {0, 1} que
coincide con la dirección canónica en solo 2.229 de 4.991 barras (44,7 %, menos que una moneda).
Si el catálogo se hubiera auto-promovido, supertrend habría quedado «verificado» con la fórmula
equivocada. El proyecto sirve en su lugar la recursión canónica sobre el ATR nativo de VectorTA
(custom_reference_on_vta_atr), que sí reproduce la dirección en 4.991 de 4.991.
La segunda regla es simétrica: no se degrada evidencia en silencio. Un carril unsupported o
variant se publica con su motivo; si falta el test id, la afirmación no se hace; si lo declarado y lo
observado difieren, se guardan ambos en runs/env/capabilities.json.
5. Dónde se ven los niveles¶
| Lugar | Qué muestra |
|---|---|
GET /v1/indicators |
capability en cada entrada y capability_counts en el payload; filtros verified y approved |
docs/INDICATORS.md |
columna Level por fila |
GET /v1/help/{name} |
bloque capability con level, computable_via_api, evidence y approval |
runs/env/capabilities.json |
matriz de capacidad (102 filas, fases A/B × 3 proveedores): «¿el carril existe y corre?» |
runs/parity/summary.json |
paridad de estrategias: «¿se reproducen los trades entre motores?» |
Capacidad no es paridad, y ninguna es aprobación
capabilities.json responde si un carril existe y corre; summary.json, si una estrategia
reproduce trades equivalentes. Ninguno de los dos es una aprobación: la aprobación es el registro
declarado.
6. Lo que los niveles NO significan¶
discoveredno significa roto ni no soportado: se usa con su motor.implementedno significa verificado;verifiedno significa rentable ni mejor.approvedno significa aprobado para producción.- Ningún nivel implica que una estrategia que use el indicador esté aprobada, ni validación contra el mercado: la paridad es equivalencia entre motores de la fórmula canónica, no una afirmación sobre retornos.
Resumen¶
- Cuatro niveles monótonos: discovered (347), implemented (39), verified (39), approved (39).
- 386 descubiertos; 375 ejecutables por la API: 33 con fórmula canónica y paridad verificada y 342 por paso directo al motor sin garantías canónicas; 11 no ejecutables con motivo; el paso directo no promueve el nivel.
- Cada peldaño añade una evidencia concreta: contrato, tests de fidelidad y paridad, registro de aprobación.
- La promoción es un cambio manual revisado con siete pasos; no hay auto-promoción.
- El alcance de la aprobación es
benchmark+research;production: falsesiempre.
Para practicar¶
- Elige un indicador
discovereddedocs/INDICATORS.mdy redacta los pasos 1–3 del checklist para él (fórmula, parámetros,first_valid). - ¿Qué pasaría si un indicador se promoviera con el warmup mal declarado? Pon un ejemplo con una EMA.
- Explica por qué
tresapprovedaunque ningún motor lo exponga. - Pide
GET /v1/indicators?verified=truey?approved=true: ¿por qué hoy devuelven el mismo número?