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.
-
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. UsaKaizenFootprint(...)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). -
Créalo en
State.DataLoaded, no enState.Configure. ElAddChartIndicator(...), si lo usas, también va ahí. -
Llama a
Update()en los indicadores sin plots. NinjaTrader mueve los indicadores alojados de forma perezosa. En uno con plots, basta con tocarValues[0][0]para disparar la cadena de actualización; en uno sin plots no hay nada que tocar, así que suOnBarUpdatesolo corre si Tick Replay lo arrastra por casualidad. 11 de los 17 indicadores no tienen plots — mira la tabla más abajo. SinUpdate()su engine no recibe ni una sola vela. -
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
IsActiverefleja 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. -
Multi-timeframe: el host debe precargar la serie. Si pones
useMTF: true, tu estrategia debe cargar esa misma serie en suState.Configure, por ejemploAddDataSeries(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. -
Una licencia inválida devuelve datos vacíos, no un error. Si la validación de licencia falla, todas las API devuelven 0,
nullo 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.
| Indicador | Plots | Tick Replay | Update() necesario | Parámetros del helper |
|---|---|---|---|---|
| KaizenBigTrades | – | sí | sí | 15 |
| KaizenCVD | 3 | sí | no | 9 |
| KaizenClusterSearch | – | sí | sí | 12 |
| KaizenDeltaCandle | 2 | sí | no | 8 |
| KaizenFootprint | – | sí | sí | 37 |
| KaizenNakedLevels | – | no | sí | 11 |
| KaizenOrderInfo | – | no | sí | 5 |
| KaizenReversalIndicator | – | sí | sí | 25 |
| KaizenSpeedOfTape | 2 | sí | no | 7 |
| KaizenStackedImbalance | – | sí | sí | 16 |
| KaizenStatGrid | 2 | sí | no | 25 |
| KaizenStatsProfile | – | no | sí | 18 |
| KaizenSuperTrend | 2 | no | no | 3 |
| KaizenSupplyDemand | – | no | sí | 14 |
| KaizenTPO | – | no | sí | 34 |
| KaizenVWAP | 8 | no | no | 6 |
| KaizenVolumeProfile | – | sí | sí | 23 |
KaizenAlertLine y KaizenDeltaProfile no tienen API para bots. Solo exponen propiedades de configuración.
API públicas disponibles
| Indicador | Miembro | Devuelve |
|---|---|---|
| KaizenSupplyDemand | .Zones | IReadOnlyList<KaizenSupplyDemandZoneInfo> — todas las zonas, activas y rotas |
.ActiveSupplyCount / .ActiveDemandCount | int | |
| KaizenVWAP | Values[0][0] … Values[7][0] | VWAP, 3 pares de bandas, VWAP de la sesión anterior — ver abajo |
.CurrentVWAP / .CurrentStdDev / .PriorSessionVWAP | double — solo vela actual | |
| KaizenStackedImbalance | .ActiveZones | IReadOnlyList<ImbalanceZone> — todas las zonas, activas y mitigadas |
.HistoricalZones | IReadOnlyList<ImbalanceZone> — solo zonas mitigadas | |
.RecentAbsorptions | IReadOnlyList<KaizenAbsorptionInfo> | |
.LastImbalanceBarIndex | int — -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 | |
.RecentAbsorptions | IReadOnlyList<KaizenAbsorptionInfo> | |
.UnfinishedLevels | IReadOnlyList<KaizenFootprintUnfinishedLevelInfo> | |
| KaizenVolumeProfile | .CurrentPOC / .CurrentVAH / .CurrentVAL | double |
.CurrentPriceVolume | SortedDictionary<double, long> — precio → volumen | |
.AllProfiles | IReadOnlyList<KaizenVolumeProfileInfo> | |
.NakedLevels | ReadOnlyCollection<KaizenVolumeProfileNakedLevelInfo> | |
| KaizenTPO | .SinglePrints | IReadOnlyList<KaizenSinglePrintInfo> |
.AllProfiles | IReadOnlyList<KaizenTpoProfileInfo> | |
.CurrentPOC / .CurrentVAH / .CurrentVAL | double | |
.CurrentIBHigh / .CurrentIBLow | double — initial balance | |
| KaizenCVD | .GetCvd(barsAgo) / .GetCvdAt(barIndex) | double — también variantes Open/High/Low |
.SessionCvd | double | |
.CvdCandleOpen / High / Low / Close | double | |
| KaizenDeltaCandle | .GetDeltaClose(barsAgo) / .GetDeltaCloseAt(barIndex) | double — también variantes Open/High/Low |
.CurrentBarDelta | long | |
.RecentAbsorptions | IReadOnlyList<KaizenAbsorptionInfo> | |
| KaizenBigTrades | .GetTradesForBar(barIndex) | IReadOnlyList<KaizenBigTradeInfo> |
| KaizenClusterSearch | .GetClustersForBar(barIndex) | IReadOnlyList<KaizenClusterHitInfo> |
| KaizenNakedLevels | .NakedLevels | IReadOnlyList<KaizenNakedLevelInfo> |
.CurrentSessionHigh / Low, .CurrentDailyHigh / Low, .CurrentWeeklyHigh / Low | double | |
| KaizenStatsProfile | .GetTodayTouchProbability(levelKey) | double? |
.GetTodaySampleSize(levelKey) / .GetLevelPrice(levelKey) | int? / double? | |
.IsLevelTouchedToday(levelKey) | bool | |
.TodayAmtClass / .StatsInstrument | string | |
.StatsLoaded / .StatsSessionCount | bool / int | |
| KaizenSuperTrend | .TrendDirection | int — +1 alcista, -1 bajista, 0 antes del primer cálculo |
.CurrentValue / .UpperBand / .LowerBand | double | |
.FlippedToUpThisBar / .FlippedToDownThisBar | bool | |
| KaizenReversalIndicator | .GetSignal(barIndex) | KaizenReversalSignal |
.IsBuySignal(barIndex) / .IsSellSignal(barIndex) | bool | |
.CurrentATR / .CurrentEffectiveFilter | double | |
| KaizenSpeedOfTape | .GetSpeed(barsAgo) / .GetSpeedAt(barIndex) | double |
.GetIsHighlight(barsAgo) / .GetIsHighlightAt(barIndex) | bool | |
.CurrentSpeedSum / .CurrentFilterValue / .CurrentIsBullish | double / 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íntoma | Causa |
|---|---|
| Una lista siempre vacía, ningún error por ninguna parte | Falta Update() (regla 3), o la licencia no cubre ese indicador |
| Indicador colgado en (Calculating…), todas las lecturas a 0 | new en lugar del método helper (regla 1) |
| La estrategia corre pero hace 0 trades, los archivos de salida solo tienen cabecera | El host no precargó la serie MTF (regla 5); el error está en el log de NinjaTrader |
| El backtest se ve excelente, en vivo no | Lookahead por listas de zonas sin filtrar (regla 4) |
| CS0246 sobre el nombre del indicador | Falta 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,CurrentBarPOCy los campos de resumen deGetFootprintForBar(...)se quedan en0dentro de una estrategia. El engine los calcula enOnRender, y un host de estrategia no disparaOnRenderde forma fiable. UsaRecentAbsorptionsen su lugar — ese se rellena enOnBarUpdate. 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.