API para desarrollo de bots

Usa los indicadores Kaizen desde tus propias estrategias NinjaScript a través de una API pública: tu estrategia aloja el indicador y lee sus valores sin tocar la DLL.

Actualizado: 10 de septiembre de 2026

Los indicadores Kaizen exponen una API pública para usarlos desde tus propias estrategias NinjaScript. No necesitas abrir ni descompilar la DLL — tu estrategia aloja el indicador y lee sus datos directamente.

using NinjaTrader.NinjaScript.Indicators.Kaizen;

Si falta este using obtienes el error CS0246.

Seis reglas que deciden si funciona

Si incumples alguna, la API devuelve datos vacíos sin ningún mensaje de error. Ese silencio es lo que las hace caras: la estrategia corre, el log queda limpio y todos los valores son cero.

  1. Siempre el método helper, nunca new. new KaizenFootprint() no pasa por el cacheo ni por el ciclo de vida de NinjaTrader; el indicador se queda colgado en (Calculating…) para siempre y cada lectura devuelve 0 o null. Usa KaizenFootprint(...) con todos sus parámetros. No existen sobrecargas sin parámetros — el helper recibe todas las propiedades, desde 3 parámetros (SuperTrend) hasta 37 (Footprint).

  2. Créalo en State.DataLoaded, no en State.Configure. El AddChartIndicator(...), si lo usas, también va ahí.

  3. Llama a Update() en los indicadores sin plots. NinjaTrader mueve los indicadores alojados de forma perezosa. En uno con plots, basta con tocar Values[0][0] para disparar la cadena de actualización; en uno sin plots no hay nada que tocar, así que su OnBarUpdate solo corre si Tick Replay lo arrastra por casualidad. 11 de los 17 indicadores no tienen plots — mira la tabla más abajo. Sin Update() su engine no recibe ni una sola vela.

  4. Filtra contra el lookahead al leer listas de zonas o niveles. Estas listas contienen el estado actual de todas las zonas, incluidas las que aún no existían en la vela que estás evaluando, y IsActive refleja el hoy, no esa vela. En un backtest eso se convierte silenciosamente en lookahead. Filtra por la marca de tiempo o por el índice de vela de origen; los dos ejemplos de abajo muestran el patrón.

  5. Multi-timeframe: el host debe precargar la serie. Si pones useMTF: true, tu estrategia debe cargar esa misma serie en su State.Configure, por ejemplo AddDataSeries(BarsPeriodType.Minute, 30). Un NinjaScript alojado no puede cargar sus propios datos. Si te lo saltas, NinjaTrader desactiva la estrategia antes de la primera vela — obtienes 0 trades, archivos de salida vacíos, y el error aparece únicamente en el log de NinjaTrader, nunca en pantalla.

  6. Una licencia inválida devuelve datos vacíos, no un error. Si la validación de licencia falla, todas las API devuelven 0, null o una lista vacía. Comprueba primero que el indicador funciona en un gráfico.

Ejemplo: zonas de Supply/Demand combinadas con VWAP

Ambos ejemplos asumen el bloque using estándar que NinjaTrader inserta al crear una estrategia nueva (ya contiene System.Windows.Media para Brushes y NinjaTrader.Gui para DashStyleHelper) — más el namespace Kaizen mostrado arriba.

public class MiBotSupplyDemand : Strategy
{
    private KaizenSupplyDemand _sd;
    private KaizenVWAP         _vwap;

    protected override void OnStateChange()
    {
        if (State == State.SetDefaults)
        {
            Name                = "MiBotSupplyDemand";
            Calculate           = Calculate.OnBarClose;
            BarsRequiredToTrade = 25;
        }
        else if (State == State.DataLoaded)
        {
            _sd = KaizenSupplyDemand(
                false,                        // useMTF
                BarsPeriodType.Minute, 30,    // mtfBarType, mtfBarPeriod (ignorados si useMTF = false)
                Brushes.DodgerBlue,           // demandColor
                Brushes.OrangeRed,            // supplyColor
                0.40f, 0.15f, 0.10f, 0.05f,   // opacidades: linea/area activa, linea/area rota
                1,                            // zoneLineWidth
                false,                        // extendZones
                false, true,                  // hideActiveZones, hideBrokenZones
                200);                         // maxZoneCount

            _vwap = KaizenVWAP(
                KVWAPStyle.Directional,       // vWAPStyle
                1.28, 2.01, 2.51,             // multiplicadores banda 1 / 2 / 3
                5,                            // bandOpacity
                DashStyleHelper.Dash);        // bandLineStyle
        }
    }

    protected override void OnBarUpdate()
    {
        if (CurrentBar < 25) return;

        _sd.Update();                         // obligatorio: Supply/Demand no tiene plots

        double vwap   = _vwap.Values[0][0];
        double lower1 = _vwap.Values[2][0];   // banda inferior a 1,28 SD
        if (vwap <= 0) return;

        IReadOnlyList<KaizenSupplyDemandZoneInfo> zones = _sd.Zones;
        if (zones == null) return;

        for (int i = 0; i < zones.Count; i++)
        {
            KaizenSupplyDemandZoneInfo z = zones[i];

            // anti-lookahead: la zona debe existir ya en la vela evaluada
            // y no debe haber sido rota todavia en ese momento
            if (z.Date > Time[0]) continue;
            if (!z.IsActive && z.DateInactive.HasValue && z.DateInactive.Value <= Time[0])
                continue;

            if (z.Type == KaizenSupplyDemandZoneType.Demand
                && Close[0] <= z.High && Close[0] >= z.Low
                && Close[0] < lower1)
            {
                EnterLong(1, "LargoDemandaBajoVWAP");
            }
        }
    }
}

Ejemplo: zonas de Stacked Imbalance

Fíjate en la lista de parámetros — el helper no tiene forma corta. Los valores de abajo son los propios valores por defecto del indicador.

public class MiBotStackedImbalance : Strategy
{
    private KaizenStackedImbalance _si;

    protected override void OnStateChange()
    {
        if (State == State.SetDefaults)
        {
            Name                = "MiBotStackedImbalance";
            Calculate           = Calculate.OnBarClose;
            BarsRequiredToTrade = 20;
        }
        else if (State == State.DataLoaded)
        {
            _si = KaizenStackedImbalance(
                300,                          // imbalanceRatio (%)
                3,                            // minStackSize
                10,                           // volumeFilter
                false,                        // ignoreZeroValues
                true, 6, 2,                   // showTickMarks, tickMarkWidthPx, tickMarkThickness
                true, 20,                     // showZones, zoneOpacity
                KSIZoneEndCondition.Touch,    // zoneEndCondition
                true, 2, 10,                  // showAbsorption, absorptionDepth, absorptionMinVolume
                KBTMarkerShape.Diamond, 10,   // absorptionMarkerShape, absorptionMarkerSize
                500);                         // maxStoredBars
        }
    }

    protected override void OnBarUpdate()
    {
        if (_si == null || CurrentBar < 20) return;

        _si.Update();                         // obligatorio: sin plots

        IReadOnlyList<KaizenStackedImbalance.ImbalanceZone> zones = _si.ActiveZones;
        if (zones == null) return;

        for (int i = 0; i < zones.Count; i++)
        {
            KaizenStackedImbalance.ImbalanceZone z = zones[i];

            // anti-lookahead
            if (z.OriginBarIndex > CurrentBar) continue;
            if (z.MitigationBarIndex >= 0 && z.MitigationBarIndex <= CurrentBar) continue;

            if (Close[0] >= z.PriceLow && Close[0] <= z.PriceHigh)
            {
                Print("Precio dentro de zona " + (z.IsBullish ? "alcista" : "bajista") + " "
                      + z.PriceLow + " - " + z.PriceHigh);
            }
        }
    }
}

Qué necesita cada indicador

Update() es obligatorio siempre que el indicador no tenga plots. Tick Replay debe estar activado en el gráfico para los indicadores de order flow.

IndicadorPlotsTick ReplayUpdate() necesarioParámetros del helper
KaizenBigTrades15
KaizenCVD3no9
KaizenClusterSearch12
KaizenDeltaCandle2no8
KaizenFootprint37
KaizenNakedLevelsno11
KaizenOrderInfono5
KaizenReversalIndicator25
KaizenSpeedOfTape2no7
KaizenStackedImbalance16
KaizenStatGrid2no25
KaizenStatsProfileno18
KaizenSuperTrend2nono3
KaizenSupplyDemandno14
KaizenTPOno34
KaizenVWAP8nono6
KaizenVolumeProfile23

KaizenAlertLine y KaizenDeltaProfile no tienen API para bots. Solo exponen propiedades de configuración.

API públicas disponibles

IndicadorMiembroDevuelve
KaizenSupplyDemand.ZonesIReadOnlyList<KaizenSupplyDemandZoneInfo> — todas las zonas, activas y rotas
.ActiveSupplyCount / .ActiveDemandCountint
KaizenVWAPValues[0][0]Values[7][0]VWAP, 3 pares de bandas, VWAP de la sesión anterior — ver abajo
.CurrentVWAP / .CurrentStdDev / .PriorSessionVWAPdouble — solo vela actual
KaizenStackedImbalance.ActiveZonesIReadOnlyList<ImbalanceZone>todas las zonas, activas y mitigadas
.HistoricalZonesIReadOnlyList<ImbalanceZone> — solo zonas mitigadas
.RecentAbsorptionsIReadOnlyList<KaizenAbsorptionInfo>
.LastImbalanceBarIndexint-1 si aún no hay ninguna
KaizenFootprint.GetBarDelta(barIndex)long — delta con signo de la vela
.GetBarPOCPrice(barIndex)double
.GetBullishImbalancePrices(barIndex) / .GetBearishImbalancePrices(barIndex)List<double>
.GetBullishAbsorptionPrices(barIndex) / .GetBearishAbsorptionPrices(barIndex)List<double>
.GetFootprintForBar(barIndex)KaizenFootprintBarInfo — ver limitación abajo
.RecentAbsorptionsIReadOnlyList<KaizenAbsorptionInfo>
.UnfinishedLevelsIReadOnlyList<KaizenFootprintUnfinishedLevelInfo>
KaizenVolumeProfile.CurrentPOC / .CurrentVAH / .CurrentVALdouble
.CurrentPriceVolumeSortedDictionary<double, long> — precio → volumen
.AllProfilesIReadOnlyList<KaizenVolumeProfileInfo>
.NakedLevelsReadOnlyCollection<KaizenVolumeProfileNakedLevelInfo>
KaizenTPO.SinglePrintsIReadOnlyList<KaizenSinglePrintInfo>
.AllProfilesIReadOnlyList<KaizenTpoProfileInfo>
.CurrentPOC / .CurrentVAH / .CurrentVALdouble
.CurrentIBHigh / .CurrentIBLowdouble — initial balance
KaizenCVD.GetCvd(barsAgo) / .GetCvdAt(barIndex)double — también variantes Open/High/Low
.SessionCvddouble
.CvdCandleOpen / High / Low / Closedouble
KaizenDeltaCandle.GetDeltaClose(barsAgo) / .GetDeltaCloseAt(barIndex)double — también variantes Open/High/Low
.CurrentBarDeltalong
.RecentAbsorptionsIReadOnlyList<KaizenAbsorptionInfo>
KaizenBigTrades.GetTradesForBar(barIndex)IReadOnlyList<KaizenBigTradeInfo>
KaizenClusterSearch.GetClustersForBar(barIndex)IReadOnlyList<KaizenClusterHitInfo>
KaizenNakedLevels.NakedLevelsIReadOnlyList<KaizenNakedLevelInfo>
.CurrentSessionHigh / Low, .CurrentDailyHigh / Low, .CurrentWeeklyHigh / Lowdouble
KaizenStatsProfile.GetTodayTouchProbability(levelKey)double?
.GetTodaySampleSize(levelKey) / .GetLevelPrice(levelKey)int? / double?
.IsLevelTouchedToday(levelKey)bool
.TodayAmtClass / .StatsInstrumentstring
.StatsLoaded / .StatsSessionCountbool / int
KaizenSuperTrend.TrendDirectionint+1 alcista, -1 bajista, 0 antes del primer cálculo
.CurrentValue / .UpperBand / .LowerBanddouble
.FlippedToUpThisBar / .FlippedToDownThisBarbool
KaizenReversalIndicator.GetSignal(barIndex)KaizenReversalSignal
.IsBuySignal(barIndex) / .IsSellSignal(barIndex)bool
.CurrentATR / .CurrentEffectiveFilterdouble
KaizenSpeedOfTape.GetSpeed(barsAgo) / .GetSpeedAt(barIndex)double
.GetIsHighlight(barsAgo) / .GetIsHighlightAt(barIndex)bool
.CurrentSpeedSum / .CurrentFilterValue / .CurrentIsBullishdouble / double / bool
KaizenStatGrid.GetStatsForBar(barIndex)KaizenStatGridBarInfo — delta, volumen, reparto compra/venta, duración
KaizenOrderInfo.GetRenderedOrders()IReadOnlyDictionary<double, KaizenOrderLevelInfo>

KaizenVWAP — índices de plot: 0 VWAP · 1/2 banda 1 superior/inferior · 3/4 banda 2 · 5/6 banda 3 · 7 VWAP de la sesión anterior.

Estructuras de datos

// KaizenSupplyDemand.Zones
public sealed class KaizenSupplyDemandZoneInfo
{
    public double    High, Low;
    public int       BarIndex, EndBarIndex;
    public KaizenSupplyDemandZoneType   Type;      // Supply, Demand
    public KaizenSupplyDemandZoneOrigin Origin;    // Regular, Continuation
    public bool      IsActive;
    public DateTime  Date;                         // cuando se formo la zona
    public DateTime? DateInactive;                 // cuando se rompio; null si sigue activa
}

// KaizenStackedImbalance.ActiveZones / .HistoricalZones
public sealed class ImbalanceZone
{
    public int    OriginBarIndex;
    public int    MitigationBarIndex;   // -1 mientras sigue activa
    public double PriceHigh, PriceLow;
    public bool   IsBullish;
    public bool   IsActive;
}

// KaizenBigTrades.GetTradesForBar(...)
public sealed class KaizenBigTradeInfo
{
    public int    BarIndex;
    public double Price;
    public long   Volume, TickPrice, Timestamp;
    public int    Side;                 // +1 agresor comprador, -1 agresor vendedor
    public bool   IsBuyAggressor, IsSellAggressor;
}

// KaizenTPO.SinglePrints
public sealed class KaizenSinglePrintInfo
{
    public double Price;
    public int    OriginBarIndex, MitigationBarIndex, OwnerProfileStartBar;
    public bool   IsActive;
}

Cuando no devuelve nada y no dice por qué

SíntomaCausa
Una lista siempre vacía, ningún error por ninguna parteFalta Update() (regla 3), o la licencia no cubre ese indicador
Indicador colgado en (Calculating…), todas las lecturas a 0new en lugar del método helper (regla 1)
La estrategia corre pero hace 0 trades, los archivos de salida solo tienen cabeceraEl host no precargó la serie MTF (regla 5); el error está en el log de NinjaTrader
El backtest se ve excelente, en vivo noLookahead por listas de zonas sin filtrar (regla 4)
CS0246 sobre el nombre del indicadorFalta using NinjaTrader.NinjaScript.Indicators.Kaizen;

Notas importantes

  • Tick Replay debe estar activado en el gráfico para los indicadores de order flow (ver la tabla de arriba).
  • Namespace: siempre NinjaTrader.NinjaScript.Indicators.Kaizen, nunca .Custom.
  • Compilar: NinjaScript Editor → New → Strategy → pega tu código → F5.
  • Licencia: tu clave debe cubrir cada indicador Kaizen que referencies.
  • Índice de vela bajo hosts OnBarClose (corregido en v2.3): cuando un indicador Kaizen se aloja dentro de una estrategia con Calculate = OnBarClose, las API para bots devolvían los datos de vela con un índice de desfase — «esta vela» daba los valores de la siguiente. Si construiste reglas sobre esos valores y las backtesteaste antes de la v2.3, revisa los resultados: el desfase actuaba como un lookahead.
  • Limitación conocida — valores de resumen del Footprint: CurrentBarDelta, CurrentBarTotalVolume, CurrentBarPOC y los campos de resumen de GetFootprintForBar(...) se quedan en 0 dentro de una estrategia. El engine los calcula en OnRender, y un host de estrategia no dispara OnRender de forma fiable. Usa RecentAbsorptions en su lugar — ese se rellena en OnBarUpdate. Los métodos por nivel de precio (GetBarDelta, GetBarPOCPrice, las listas de imbalance y absorción) no están afectados.
  • Versión mínima: v2.3. Las versiones anteriores tenían firmas de helper distintas y el desfase de índice descrito arriba.