Saltar a contenido

Indicadores por API: POST /v1/indicators

Qué vas a aprender

  • Qué hace POST /v1/indicators y en qué se diferencia de un backtest.
  • Las 39 primitivas registradas (33 calculables en modo canónico), sus parámetros de ejemplo y su regla de primer valor válido (first_valid_rule).
  • El paso directo al motor: cómo la API calcula otros 342 indicadores sin garantías canónicas (375 ejecutables en total; 11 no ejecutables con motivo).
  • Cómo construir la petición: data, indicator (indicator, params, source, periods_batch), engine, engine_options.
  • Cómo leer la respuesta: provider, first_valid_index, n_valid, mask_summary, values_tail.
  • Por qué el RSI14 tiene first_valid_index 14 en VectorTA y 13 en Nautilus, y por qué sus valores no convergen (variante declarada, no error).
  • Cómo aprovechar el example_curl de cada ficha y cuáles son los errores típicos.

1. La idea: pedir una curva, no una estrategia

Un backtest responde «¿cuánto habría ganado?». POST /v1/indicators responde algo más modesto y muy útil: «dame esta curva sobre estos datos y dime desde qué barra es fiable». Es la herramienta para:

  • comprobar que un indicador se comporta como esperas antes de meterlo en una estrategia;
  • comparar la misma curva en los dos motores;
  • medir cuánto cuesta calcularla.

Un indicador no es una estrategia

Por eso esta ruta no usa strategy sino un IndicatorSelector. No hay ejecución, ni costes, ni contabilidad: signature.execution_contract es null.

Sustituye a las rutas del contrato anterior, v0 (POST /indicators, /indicators/rsi, /indicators/ema de los servicios :8001/:8002), y añade lo que aquellas no tenían: ventana, first_valid_index, mask_summary, proveedor declarado, stage_timings, periods_batch y sobre.

flowchart LR
    R[POST /v1/indicators] --> V{¿Indicador en el<br/>registro del proyecto?}
    V -- no --> PT[paso directo al motor<br/>ver sección 7]
    V -- sí --> S{¿source ∈ inputs?}
    S -- no --> E2[422 INDICATOR_PARAMS_INVALID<br/>field: indicator.source]
    S -- sí --> L[Cargar barras de la ventana]
    L --> Z{¿Hay barras?}
    Z -- no --> E3[409 INSUFFICIENT_HISTORY]
    Z -- sí --> C[Calcular por motor<br/>y por periodo de periods_batch]
    C --> OUT["200: results con provider,<br/>first_valid_index, mask, cola"]

2. Las 39 primitivas registradas

Solo los indicadores con contrato del proyecto se calculan en modo canónico, con fórmula y paridad (ver niveles de capacidad en el capítulo 2); el resto va por paso directo al motor (sección 7). Las 39 primitivas son estas; 33 son calculables por esta ruta y 6 (bandwidth, rolling_quantile, linreg_endpoint, sma_of, session_position, cross_sectional_rank) solo se usan dentro de estrategias. Aparecen con sus params de ejemplo tal y como los publica el catálogo aprobado (examples/api/16_indicators_catalog_approved.response.json):

Primitiva Params (ejemplo) Entradas Salidas first_valid_rule
sma period=10 close sma period_minus_1
ema period=10 close ema period_minus_1
rma period=10 close rma period_minus_1
wma period=20 close wma period_minus_1
hma period=32 close hma chained_sum
sma_of period=20 tr sma chained_sum
hl2 high, low hl2 none
hlc3 high, low, close hlc3 none
log_price close log_price none
linreg_endpoint period=20 val linreg_endpoint chained_sum
rolling_quantile period=20, q=0.1 bandwidth quantile chained_shifted_window
Primitiva Params (ejemplo) Entradas Salidas first_valid_rule
rsi period=14 close rsi period
macd fast=12, slow=26, signal=9 close macd, signal, hist chained_sum
stochrsi rsi_period=14, stoch_period=14, smooth_k=3, smooth_d=3 close k, d chained
dmi period=14 high, low, close plus_di, minus_di, adx chained
wavetrend channel=10, average=21 high, low, close wt1, wt2 chained
squeeze_momentum period=20, bb_k=2.0, kc_k=1.5 high, low, close squeeze, val, m chained_sum
log_return lag=1 close log_return period
momentum lookback=12, skip=1 close momentum period
Primitiva Params (ejemplo) Entradas Salidas first_valid_rule
stddev period=20 close stddev period_minus_1
bbands period=20, k=2.0 close middle, upper, lower period_minus_1
bandwidth period=20, k=2.0 middle, upper, lower bandwidth chained
tr high, low, close tr none
atr period=14 high, low, close atr period_minus_1
keltner_sma period=20, k=1.5 high, low, close middle, upper, lower chained
atr_trailing_stop period=10, key_value=3.0 high, low, close stop chained
chandelier period=22, k=3.0 high, low, close long_stop, short_stop, direction chained
rolling_vol window=21 close rolling_vol chained_sum
Primitiva Params (ejemplo) Entradas Salidas first_valid_rule
donchian period=20 high, low upper, lower period
supertrend period=10, multiplier=3.0 high, low, close upper_final, lower_final, direction chained
rolling_max period=20 high rolling_max period_minus_1
rolling_min period=20 low rolling_min period_minus_1
ichimoku tenkan=9, kijun=26, span_b=52, displacement=26 high, low tenkan, kijun, senkou_a, senkou_b chained
psar acceleration=0.02, maximum=0.2 high, low, close sar, direction, ep, af fixed_index
confirmed_pivots pivot_half_window=3 high, low pivot_high_level, pivot_low_level chained
session_position session_id, market_open_ns, market_close_ns bar_index_in_session, … session_local
opening_range range_minutes=30 high, low, market_open_ns, market_close_ns, session_id range_high, range_low, range_complete, in_range session_local
session_vwap high, low, close, volume, session_id vwap, var, cum_volume session_local
cross_sectional_rank descending=false, tiebreak=asset_id_asc score_panel, eligible_panel rank none

Qué significa cada first_valid_rule:

Regla Lectura
none Válido desde la primera barra (no hay ventana).
period_minus_1 El primer valor válido está en el índice period − 1 (la ventana se llena).
period Un índice más: necesita period diferencias (RSI, retornos).
chained, chained_sum, chained_shifted_window Encadenado: depende de otras features (p. ej. MACD = EMA de EMAs).
fixed_index Índice fijo propio del algoritmo (PSAR).
session_local Se reinicia en cada sesión de mercado.

Indicadores con entradas no-OHLC

Algunas primitivas consumen otras features (bandwidth consume middle/upper/lower; sma_of consume tr; cross_sectional_rank consume paneles). Existen sobre todo como piezas de estrategias; para explorarlas, empieza por su dossier GET /v1/help/{name}.


3. La petición

Petición real examples/api/05_indicators_rsi14_nvda.request.json (payload):

{
  "data": { "asset_id": "NVDA", "assets": null, "timeframe": "1min",
            "window": { "start": null, "end": null, "mode": "reset_flat", "checkpoint_id": null } },
  "indicator": { "indicator": "rsi", "params": { "period": 14 }, "source": "close", "periods_batch": null },
  "engine": "both",
  "cache": { "cache_mode": "WARM_DATA", "write_through": true, "checkpoint_out": null },
  "output": { "output_mode": "metrics", "include_timings": true, "include_parity": true, "tail_rows": 5 },
  "profile": { "profile": "one_core", "repetitions": null },
  "engine_options": {
    "vectorta": { "kernel": "scalar", "use_batch": false, "streaming_provider": "vectorta_stream" },
    "nautilus": { "adapter": "NT_ONLINE", "account_type": "MARGIN", "indicator_provider": "native",
                  "fill_model": null, "latency_model": null, "use_message_queue": false, "log_level": "ERROR" }
  }
}
Campo Significado
data Igual que en el backtest. Con start/end a null, todo el dataset.
indicator.indicator Nombre de la primitiva (una de las 39).
indicator.params Parámetros de la fórmula (period, k, …).
indicator.source Columna de entrada: open, high, low, close, hl2, hlc3. Debe estar entre las inputs de la primitiva.
indicator.periods_batch Rejilla de periodos en una sola petición; devuelve una entrada por periodo y motor, en el orden pedido.
engine vectorta, nautilus o both.
output.tail_rows Cuántas filas finales devuelve en values_tail.
BASE=http://localhost:8000
curl -sS -X POST "$BASE/v1/indicators" -H 'Content-Type: application/json' \
  -d @examples/api/05_indicators_rsi14_nvda.request.json \
  | jq '.payload.results[] | {engine, provider, n_bars_in, n_valid, first_valid_index, mask_summary}'
import requests
BASE = "http://localhost:8000"
body = {"schema_version": "v1", "payload": {
    "data": {"asset_id": "NVDA", "timeframe": "1min",
             "window": {"start": None, "end": None, "mode": "reset_flat"}},
    "indicator": {"indicator": "rsi", "params": {"period": 14}, "source": "close"},
    "engine": "both",
    "output": {"output_mode": "metrics", "tail_rows": 5},
    "engine_options": {"vectorta": {"kernel": "scalar"}},
}}
p = requests.post(f"{BASE}/v1/indicators", json=body, timeout=600).json()["payload"]
for r in p["results"]:
    print(r["engine"], r["provider"]["variant"], r["first_valid_index"],
          [round(v["value"], 2) for v in r["values_tail"]])

4. La respuesta (ejemplo real)

examples/api/05_indicators_rsi14_nvda.response.json, recortado:

{
  "status": "PASS",
  "warnings": [],
  "signature": { "data_hash": "e8b5fc36…95b0", "formula_version": "1", "params": { "period": 14 },
                 "execution_contract": null, "cache_mode": "WARM_DATA", "output_mode": "metrics", "…": "…" },
  "results": [
    {
      "engine": "vectorta", "adapter": "VTA_CPU_LEDGER",
      "provider": { "provider": "vectorta", "provider_version": "0.2.8", "primitive": "rsi",
                    "variant": "canonical", "kernel": "scalar", "first_valid_index": 14 },
      "n_bars_in": 631735, "n_valid": 631721, "first_valid_index": 14,
      "mask_summary": { "leading_nan": 14, "interior_nan": 0, "trailing_nan": 0 },
      "values_tail": [
        { "index": 631730, "datetime": "2026-09-14T19:56:00Z", "value": 29.542436906085864 },
        "…",
        { "index": 631734, "datetime": "2026-09-14T20:00:00Z", "value": 25.983092554457702 }
      ],
      "stage_timings": { "read_decode_ns": 345387690, "feature_build_ns": 223880436, "end_to_end_wall_ns": 572561393, "…": 0 },
      "memory": { "peak_rss_bytes": 631099392 }
    },
    {
      "engine": "nautilus", "adapter": "NT_FEATURES",
      "provider": { "provider": "nautilus", "provider_version": "1.231.0", "primitive": "rsi",
                    "variant": "nt_init_period_minus_1_scaled_0_1", "kernel": "cython", "first_valid_index": 13 },
      "n_bars_in": 631735, "n_valid": 631722, "first_valid_index": 13,
      "mask_summary": { "leading_nan": 13, "interior_nan": 0, "trailing_nan": 0 },
      "values_tail": [
        { "index": 631730, "datetime": "2026-09-14T19:56:00Z", "value": 26.49663618891308 },
        "…",
        { "index": 631734, "datetime": "2026-09-14T20:00:00Z", "value": 21.649139543227847 }
      ],
      "…": "…"
    }
  ],
  "parity": null,
  "cache": { "features_hits": [], "features_misses": ["rsi"], "result_hit": false, "…": "…" }
}

4.1 Campo a campo

Campo Qué te dice
provider.provider La librería que calculó (vectorta, nautilus, canonical), nunca una función.
provider.variant La forma exacta: canonical o una variante declarada (nt_init_period_minus_1_scaled_0_1).
provider.kernel El kernel realmente ejecutado (scalar, cython…), no el pedido.
n_bars_in Barras de entrada: 631 735 barras de 1 minuto de NVDA.
n_valid Barras con valor válido.
first_valid_index Primera barra fiable (con varias salidas, el máximo de todas).
mask_summary NaN al principio, en medio y al final. interior_nan: 0 significa que no hay huecos inesperados.
values_tail Últimas tail_rows filas con índice, fecha y valor.

Comprobación aritmética: n_bars_in − leading_nan = n_valid → 631 735 − 14 = 631 721 (VectorTA) y 631 735 − 13 = 631 722 (Nautilus).

4.2 14 frente a 13, y 25,98 frente a 21,65: semilla y fórmula

Por qué el RSI de Nautilus «empieza antes» y da otros valores

Hay dos diferencias distintas entre las dos curvas del ejemplo, y conviene no mezclarlas.

1. La semilla (explica 14 frente a 13). El RSI suaviza ganancias y pérdidas con una media exponencial que hay que sembrar:

  • La primitiva canónica (VectorTA) siembra con las primeras period diferencias ⇒ primer valor en el índice 14.
  • Nautilus marca initialized en period − 1 y trabaja internamente en 0..1 (el proveedor lo reescala a 0..100) ⇒ primer valor en el índice 13.

Una diferencia de semilla decae como (1−α)^k y, tras cientos de miles de barras, desaparece.

2. La fórmula de suavizado (explica 25,98 frente a 21,65). En la última barra del ejemplo (índice 631 734) VectorTA da 25,98 y Nautilus 21,65: más de 4 puntos tras 631 735 barras. Eso no es semilla. El RelativeStrengthIndex de nautilus_trader 1.231.0, con su media por defecto, suaviza con una EMA de α = 2/(n+1) (2/15 con n = 14), mientras que el RSI de Wilder que implementa la primitiva canónica usa α = 1/n (1/14). Medido en esta revisión sobre 20 000 puntos sintéticos: la curva de Nautilus coincide con un RSI de α = 2/15 hasta 1,4e-14 y difiere del de Wilder hasta 15,7 puntos en las últimas 1 000 barras. Las dos curvas no convergen nunca: es una diferencia de definición, como la BB de Nautilus sobre precio típico.

Lo firme: la ficha del RSI declara el carril de Nautilus como variant (nt_init_period_minus_1_scaled_0_1), no como native, y esta ruta no afirma paridad (parity: null). Una variante declarada no es un bug; una variante escondida sí lo sería.

Dos textos del repositorio dicen otra cosa

examples/api/README.md (paseo 5) afirma que en la cola la diferencia medida es de ~1e-13 sobre 632 095 barras, y el docstring de finazbench/features/provider_nautilus.py dice que el RSI de Nautilus converge «asintóticamente» a la canónica. El fichero regenerado (631 735 barras, 25,98 frente a 21,65) y la medida anterior los contradicen. Los backtests con NT_FEATURES sí pasan paridad porque ese adaptador comparte las features del lado canónico: la diferencia del RSI nativo de Nautilus solo aparece cuando Nautilus calcula el indicador por su cuenta.

Detalles de implementación que conviene conocer (compute_indicator en finazbench/runner/from_api.py)
  • stage_timings se mide una sola vez para toda la petición (lectura más el cálculo de los dos motores) y el mismo diccionario se copia en cada entrada de results: por eso las dos entradas del ejemplo tienen cifras idénticas (read_decode_ns ≈ 345 ms, feature_build_ns ≈ 224 ms, que suma los dos motores, total ≈ 573 ms). No leas esas cifras como el coste de cada motor.
  • El adapter de cada entrada no sale de la petición: el código escribe VTA_CPU_LEDGER para VectorTA y NT_FEATURES fijo para Nautilus. Por eso el ejemplo publica NT_FEATURES aunque pida NT_ONLINE; engine_options solo se valida. En esta ruta el indicador de Nautilus es siempre el nativo de la librería, con cualquier adaptador.
  • parity es siempre null en esta ruta, también con engine: "both" e include_parity: true: el código no compara las curvas. La comparación formal vive en POST /v1/parity (capítulo 4), no aquí.
  • status es "PASS" siempre que el cálculo termina: significa «calculado», no «los motores coinciden».
  • values_tail trae solo la primera salida de la primitiva (spec.outputs[0]); en indicadores con varias salidas, el resto no aparece en la cola.
  • La caché de features no está cableada (DEV-06b-01): features_misses: ["rsi"] siempre.

5. periods_batch: una rejilla en una llamada

Cuando quieres varias longitudes del mismo indicador (p. ej. EMAs de 5 a 200 para un barrido), periods_batch evita N peticiones:

"indicator": { "indicator": "ema", "params": { "period": 20 }, "source": "close",
               "periods_batch": [5, 10, 20, 40, 50, 100, 150, 200] }

La respuesta trae una entrada por periodo y por motor, en el orden pedido. Solo aplica a primitivas con parámetro period.

use_batch hoy no acelera

engine_options.vectorta.use_batch permite usar la forma <primitiva>_batch de VectorTA. El contrato documenta una medida en la que ema_batch(5..50) tardó 40 ms frente a 30 ms en bucle: con el wheel PyPI actual el batch no paraleliza. El resultado es idéntico en cualquier caso.


6. Copiar y pegar: example_curl

Cada ficha registrada (GET /v1/indicators/{name} o GET /v1/help/{name}) trae un example_curl que funciona tal cual. El real del RSI (09_indicator_detail.response.json):

curl -sS -X POST "$BASE/v1/indicators" -H 'Content-Type: application/json' -d '{"schema_version": "v1", "payload": {"data": {"asset_id": "NVDA", "timeframe": "1d", "window": {"start": null, "end": null, "mode": "reset_flat"}}, "indicator": {"indicator": "rsi", "params": {"period": 14}, "source": "close"}, "engine": "both", "engine_options": {"vectorta": {"kernel": "scalar", "use_batch": true}, "nautilus": {"adapter": "NT_FEATURES", "indicator_provider": "native"}}}}'

Un truco para cualquier primitiva:

NAME=bbands
curl -sS "$BASE/v1/help/$NAME" | jq -r '.payload.example_curl' > /tmp/req.sh
BASE=$BASE bash /tmp/req.sh | jq '.payload.results[] | {engine, first_valid_index, n_valid}'

7. Paso directo al motor

Las 39 primitivas registradas no son todo lo que la API sabe calcular. Para los 347 indicadores que solo existen en un motor (nivel discovered), POST /v1/indicators ofrece un segundo modo: el paso directo al motor (execution_mode: engine_passthrough). La API llama al motor que posee el indicador y devuelve exactamente lo que el motor calcula, sin reinterpretarlo.

Cifra (medida por tools/probe_indicators.py, runs/env/indicators_probe.json) Valor
Entradas del catálogo 386
Ejecutables por POST /v1/indicators 375
…en modo canónico (fórmula canónica y paridad verificada) 33
…por paso directo al motor (sin garantías canónicas) 342 (323 con VectorTA, 21 con Nautilus; 2 en ambos)
No ejecutables, con not_computable_reason 11
flowchart LR
    R["POST /v1/indicators"] --> K{"¿Primitiva<br/>registrada?"}
    K -- sí --> C["modo canonical<br/>fórmula + paridad"]
    K -- no --> M{"¿Algún motor<br/>lo posee?"}
    M -- no --> E0["422 INDICATOR_PARAMS_INVALID"]
    M -- sí --> P{"¿Params válidos<br/>para ese motor?"}
    P -- no --> E1["422 INDICATOR_PARAMS_INVALID<br/>details.accepted"]
    P -- sí --> A{"¿Alimentable con<br/>barras OHLCV?"}
    A -- no --> E2["501 LANE_UNSUPPORTED"]
    A -- sí --> PT["200 engine_passthrough<br/>nivel sigue discovered"]

7.1 Ejemplo real: alma con VectorTA

Petición (examples/api/17_indicators_alma_passthrough.request.json, recortada):

{
  "payload": {
    "data": {"asset_id": "NVDA", "timeframe": "1d",
             "window": {"start": null, "end": null, "mode": "reset_flat"}},
    "indicator": {"indicator": "alma", "params": {"period": 9}, "source": "close"},
    "engine": "vectorta",
    "engine_options": {"vectorta": {"kernel": "scalar", "use_batch": true}}
  }
}

Respuesta (17_indicators_alma_passthrough.response.json, recortada):

{
  "payload": {
    "status": "PASS",
    "warnings": ["ENGINE_PASSTHROUGH: valores del motor sin fórmula canónica ni paridad; semilla de calentamiento y escala propias de cada motor."],
    "execution_mode": "engine_passthrough",
    "capability": {"level": "discovered", "computable_via_api": true,
                   "computable_engines": ["vectorta"]},
    "guarantees": {"canonical_formula": false, "first_valid_rule": "observed",
                   "parity": "not_applicable"},
    "results": [{
      "engine": "vectorta", "adapter": "VTA_CPU_LEDGER",
      "native": {"module": "vector_ta", "name": "alma", "lane": "batch"},
      "n_bars_in": 1627, "n_valid": 1619, "first_valid_index": 8,
      "first_valid_rule": "observed",
      "outputs": [{"name": "value", "first_valid_index": 8, "n_valid": 1619}],
      "params_resolved": {
        "period": {"value": 9,    "source": "request"},
        "offset": {"value": 0.85, "source": "project_probe_default"},
        "sigma":  {"value": 6.0,  "source": "project_probe_default"}
      },
      "values_tail": [{"index": 1626, "datetime": "2026-09-14T20:00:00Z",
                       "value": 218.0196459373903}]
    }]
  }
}

Qué hay que leer:

  • status: PASS significa «el motor calculó», no «coincide con una fórmula del proyecto».
  • first_valid_index: 8 es observado (primer valor finito), no una regla declarada.
  • params_resolved dice de dónde sale cada parámetro: period lo pediste tú; offset y sigma los puso la sonda del proyecto.
  • capability.level sigue siendo discovered: el paso directo no promueve el nivel.

Hay un segundo ejemplo real con Nautilus (18_indicators_aroonosc_nautilus_passthrough.*): AroonOscillator con period=14, adaptador NT_ONLINE, first_valid_index 14 y tres salidas (value, aroon_up, aroon_down). Y uno de error (93_error_indicator_passthrough_params_invalid.response.json): pedir alma con periodo devuelve 422 INDICATOR_PARAMS_INVALID con details.accepted: ["offset", "period", "sigma"].

7.2 Campos nuevos

Campo Dónde Qué dice
execution_mode catálogo, ayuda y respuesta canonical o engine_passthrough
computable_via_api catálogo y ayuda si la API lo calcula de verdad (medido por la sonda)
computable_engines capability qué motores lo calculan por paso directo
not_computable_reason capability por qué no es ejecutable (solo en los 11 no ejecutables)
capability_counts.computable_passthrough catálogo entradas discovered calculables por paso directo (342); no es un nivel
mode filtro de GET /v1/indicators canonical | passthrough | all
params_resolved results[] valor y origen de cada parámetro: request, engine_default, engine_internal o project_probe_default
outputs results[] todas las salidas del motor con su first_valid_index observado
native results[] módulo, nombre y carril nativo que se ejecutó
guarantees respuesta y capability canonical_formula: false, first_valid_rule: observed, parity: not_applicable

7.3 Los 11 no ejecutables

Indicador Motivo Código
bandwidth, rolling_quantile, linreg_endpoint, sma_of, session_position, cross_sectional_rank primitivas registradas que leen series derivadas o paneles; solo se usan dentro de estrategias 422
rsmk, spearman_correlation necesitan una serie de comparación 501
decisionpoint_breadth_swenlin_trading_oscillator necesita datos de amplitud de mercado 501
SpreadAnalyzer se alimenta de cotizaciones bid/ask, no de barras 501
half_causal_estimator rechaza slots_per_day=1 en diario 422

Calcular no es certificar

Una curva de paso directo sirve para explorar. Para usarla en un resultado publicable con paridad, el indicador tiene que promoverse (ver Niveles de capacidad).


8. Errores típicos

Situación Código Pista en details
Nombre que no está en el catálogo de ningún motor 422 INDICATOR_PARAMS_INVALID field: "indicator.indicator", value, params
Paso directo con parámetros que el motor no admite (p. ej. periodo en alma) 422 INDICATOR_PARAMS_INVALID unknown, accepted
Paso directo de un indicador que no se alimenta con barras, o motor que no lo posee 501 LANE_UNSUPPORTED available
source que la primitiva no consume (p. ej. source: "high" en rsi) 422 INDICATOR_PARAMS_INVALID field: "indicator.source", accepted: ["close"]
Parámetros fuera de rango 422 INDICATOR_PARAMS_INVALID campo y regla
Ventana sin barras 409 INSUFFICIENT_HISTORY asset_id, timeframe
Activo o timeframe inexistente 404 ASSET_NOT_FOUND available
Un motor no ofrece la primitiva 501 STRATEGY_UNSUPPORTED_BY_ADAPTER indicator, engine
kernel: "avx2" con el wheel PyPI 422 ENGINE_OPTION_UNAVAILABLE accepted frente a available

Cambiar source cambia la fórmula

Pedir un RSI sobre high no es «otro RSI»: es otra fórmula. Por eso la API lo rechaza en lugar de calcularlo en silencio. Si necesitas un indicador sobre hl2, usa una primitiva que lo declare en sus inputs.


Resumen

  • POST /v1/indicators calcula una curva (no una estrategia) con first_valid_index, máscara, proveedor declarado y cola de valores.
  • 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 devuelve los valores del motor con first_valid observado y no promueve el nivel (discovered).
  • source debe pertenecer a las inputs de la primitiva; periods_batch devuelve una entrada por periodo y motor.
  • En el ejemplo real, RSI14 sobre 631 735 barras de NVDA 1min: first_valid_index 14 en VectorTA y 13 en Nautilus (semillas distintas) y valores de cola distintos (25,98 frente a 21,65) porque Nautilus suaviza con α = 2/(n+1) y no con el 1/n de Wilder.
  • En esta ruta el adapter de Nautilus es siempre NT_FEATURES, stage_timings es global a la petición y parity es siempre null.
  • Cada ficha trae un example_curl listo para ejecutar.

Para practicar

  1. Calcula bbands con period=20, k=2.0 sobre NVDA 1d. ¿Cuántas salidas trae values_tail por fila (pista: sección 4.2) y cuál es el first_valid_index?
  2. Pide rsi con source: "high". Lee details.accepted.
  3. Lanza ema con periods_batch: [10, 50, 200] y engine: "vectorta". Comprueba que first_valid_index sigue la regla period_minus_1.
  4. Calcula alma por paso directo. ¿Qué garantías declara la respuesta y qué dice su promotion_note en GET /v1/help/alma?
  5. Repite el ejemplo del RSI con engine: "nautilus" y tail_rows: 20. ¿Se acercan los valores de la cola a los de VectorTA? Explica por qué no, con la sección 4.2.