API für Bot-Entwicklung
Nutze die Kaizen-Indikatoren aus deinen NinjaScript-Strategien über eine öffentliche API: Deine Strategie hostet den Indikator und liest seine Werte, ohne die DLL.
Aktualisiert: 10. September 2026
Die Kaizen-Indikatoren stellen eine öffentliche API für die Nutzung aus deinen eigenen NinjaScript-Strategien bereit. Du musst die DLL weder öffnen noch dekompilieren — deine Strategie hostet den Indikator und liest seine Daten direkt.
using NinjaTrader.NinjaScript.Indicators.Kaizen;
Fehlt dieses using, bekommst du den Fehler CS0246.
Sechs Regeln, die darüber entscheiden, ob es funktioniert
Verletzt du eine davon, liefert die API leere Daten — ohne Fehlermeldung. Genau diese Stille macht sie teuer: Die Strategie läuft, das Log bleibt sauber, und jeder Wert ist null.
-
Immer die Helper-Methode, nie
new.new KaizenFootprint()durchläuft NinjaTraders Cache- und Lifecycle-Verdrahtung nicht; der Indikator hängt dauerhaft in (Calculating…), und jeder Lesezugriff liefert 0 oder null. NutzeKaizenFootprint(...)mit allen Parametern. Es gibt keine parameterlosen Überladungen — die Helper-Methode nimmt jede Property entgegen, von 3 Parametern (SuperTrend) bis 37 (Footprint). -
Erzeuge ihn in
State.DataLoaded, nicht inState.Configure. EinAddChartIndicator(...)gehört, falls du es nutzt, ebenfalls dorthin. -
Rufe
Update()bei Indikatoren ohne Plots auf. NinjaTrader treibt gehostete Indikatoren nur bei Bedarf an. Bei einem Indikator mit Plots löst schon der Zugriff aufValues[0][0]die Update-Kette aus; bei einem Indikator ohne Plots gibt es nichts abzugreifen, seinOnBarUpdateläuft dann nur, wenn Tick Replay ihn zufällig mit antreibt. 11 der 17 Indikatoren haben keine Plots — siehe Tabelle unten. OhneUpdate()bekommt ihre Engine keine einzige Bar. -
Filtere gegen Lookahead, wenn du Zonen- oder Level-Listen liest. Diese Listen enthalten den aktuellen Zustand aller Zonen — auch solcher, die es an der ausgewerteten Bar noch gar nicht gab; und
IsActivebeschreibt heute, nicht jene Bar. Im Backtest wird daraus still ein Lookahead. Filtere über den Zeitstempel oder den Ursprungs-Bar-Index; beide Beispiele unten zeigen das Muster. -
Multi-Timeframe: Der Host muss die Serie vorladen. Setzt du
useMTF: true, muss deine Strategie dieselbe Serie in ihremState.Configureladen, z. B.AddDataSeries(BarsPeriodType.Minute, 30). Ein gehostetes NinjaScript darf keine eigenen Daten laden. Lässt du das weg, deaktiviert NinjaTrader die Strategie vor der ersten Bar — du bekommst 0 Trades, leere Ausgabedateien, und der Fehler steht ausschliesslich im NinjaTrader-Log, nie auf dem Bildschirm. -
Eine ungültige Lizenz liefert leere Daten, keinen Fehler. Schlägt die Lizenzprüfung fehl, geben alle APIs 0,
nulloder eine leere Liste zurück. Prüfe zuerst, ob der Indikator auf einem Chart funktioniert.
Beispiel: Supply-/Demand-Zonen kombiniert mit VWAP
Beide Beispiele setzen den Standard-using-Block voraus, den NinjaTrader beim Anlegen einer neuen Strategie einfügt (er enthält bereits System.Windows.Media für Brushes und NinjaTrader.Gui für DashStyleHelper) — dazu den oben gezeigten Kaizen-Namespace.
public class MeinSupplyDemandBot : Strategy
{
private KaizenSupplyDemand _sd;
private KaizenVWAP _vwap;
protected override void OnStateChange()
{
if (State == State.SetDefaults)
{
Name = "MeinSupplyDemandBot";
Calculate = Calculate.OnBarClose;
BarsRequiredToTrade = 25;
}
else if (State == State.DataLoaded)
{
_sd = KaizenSupplyDemand(
false, // useMTF
BarsPeriodType.Minute, 30, // mtfBarType, mtfBarPeriod (ohne Wirkung bei useMTF = false)
Brushes.DodgerBlue, // demandColor
Brushes.OrangeRed, // supplyColor
0.40f, 0.15f, 0.10f, 0.05f, // Deckkraft: Linie/Flaeche aktiv, Linie/Flaeche gebrochen
1, // zoneLineWidth
false, // extendZones
false, true, // hideActiveZones, hideBrokenZones
200); // maxZoneCount
_vwap = KaizenVWAP(
KVWAPStyle.Directional, // vWAPStyle
1.28, 2.01, 2.51, // Multiplikatoren Band 1 / 2 / 3
5, // bandOpacity
DashStyleHelper.Dash); // bandLineStyle
}
}
protected override void OnBarUpdate()
{
if (CurrentBar < 25) return;
_sd.Update(); // Pflicht: Supply/Demand hat keine Plots
double vwap = _vwap.Values[0][0];
double lower1 = _vwap.Values[2][0]; // unteres Band bei 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: Die Zone muss an der ausgewerteten Bar bereits
// existiert haben und darf dort noch nicht gebrochen gewesen sein
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, "LongNachfrageUnterVWAP");
}
}
}
}
Beispiel: Stacked-Imbalance-Zonen
Beachte die Parameterliste — die Helper-Methode hat keine Kurzform. Die Werte unten sind die Standardwerte des Indikators.
public class MeinStackedImbalanceBot : Strategy
{
private KaizenStackedImbalance _si;
protected override void OnStateChange()
{
if (State == State.SetDefaults)
{
Name = "MeinStackedImbalanceBot";
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(); // Pflicht: keine 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("Preis in " + (z.IsBullish ? "bullischer" : "baerischer") + " Zone "
+ z.PriceLow + " - " + z.PriceHigh);
}
}
}
}
Welcher Indikator braucht was
Update() ist immer dann Pflicht, wenn der Indikator keine Plots hat. Tick Replay muss für die Orderflow-Indikatoren auf dem Chart aktiviert sein.
| Indikator | Plots | Tick Replay | Update() nötig | Helper-Parameter |
|---|---|---|---|---|
| KaizenBigTrades | – | ja | ja | 15 |
| KaizenCVD | 3 | ja | nein | 9 |
| KaizenClusterSearch | – | ja | ja | 12 |
| KaizenDeltaCandle | 2 | ja | nein | 8 |
| KaizenFootprint | – | ja | ja | 37 |
| KaizenNakedLevels | – | nein | ja | 11 |
| KaizenOrderInfo | – | nein | ja | 5 |
| KaizenReversalIndicator | – | ja | ja | 25 |
| KaizenSpeedOfTape | 2 | ja | nein | 7 |
| KaizenStackedImbalance | – | ja | ja | 16 |
| KaizenStatGrid | 2 | ja | nein | 25 |
| KaizenStatsProfile | – | nein | ja | 18 |
| KaizenSuperTrend | 2 | nein | nein | 3 |
| KaizenSupplyDemand | – | nein | ja | 14 |
| KaizenTPO | – | nein | ja | 34 |
| KaizenVWAP | 8 | nein | nein | 6 |
| KaizenVolumeProfile | – | ja | ja | 23 |
KaizenAlertLine und KaizenDeltaProfile haben keine Bot-API. Sie stellen ausschliesslich Konfigurations-Properties bereit.
Verfügbare öffentliche APIs
| Indikator | Member | Liefert |
|---|---|---|
| KaizenSupplyDemand | .Zones | IReadOnlyList<KaizenSupplyDemandZoneInfo> — alle Zonen, aktive und gebrochene |
.ActiveSupplyCount / .ActiveDemandCount | int | |
| KaizenVWAP | Values[0][0] … Values[7][0] | VWAP, 3 Bandpaare, VWAP der Vorsession — siehe unten |
.CurrentVWAP / .CurrentStdDev / .PriorSessionVWAP | double — nur aktuelle Bar | |
| KaizenStackedImbalance | .ActiveZones | IReadOnlyList<ImbalanceZone> — alle Zonen, aktive und mitigierte |
.HistoricalZones | IReadOnlyList<ImbalanceZone> — nur mitigierte Zonen | |
.RecentAbsorptions | IReadOnlyList<KaizenAbsorptionInfo> | |
.LastImbalanceBarIndex | int — -1, wenn noch keine | |
| KaizenFootprint | .GetBarDelta(barIndex) | long — vorzeichenbehaftetes Delta der Bar |
.GetBarPOCPrice(barIndex) | double | |
.GetBullishImbalancePrices(barIndex) / .GetBearishImbalancePrices(barIndex) | List<double> | |
.GetBullishAbsorptionPrices(barIndex) / .GetBearishAbsorptionPrices(barIndex) | List<double> | |
.GetFootprintForBar(barIndex) | KaizenFootprintBarInfo — siehe Einschränkung unten | |
.RecentAbsorptions | IReadOnlyList<KaizenAbsorptionInfo> | |
.UnfinishedLevels | IReadOnlyList<KaizenFootprintUnfinishedLevelInfo> | |
| KaizenVolumeProfile | .CurrentPOC / .CurrentVAH / .CurrentVAL | double |
.CurrentPriceVolume | SortedDictionary<double, long> — Preis → 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 — ebenso Open-/High-/Low-Varianten |
.SessionCvd | double | |
.CvdCandleOpen / High / Low / Close | double | |
| KaizenDeltaCandle | .GetDeltaClose(barsAgo) / .GetDeltaCloseAt(barIndex) | double — ebenso Open-/High-/Low-Varianten |
.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 aufwärts, -1 abwärts, 0 vor der ersten Berechnung |
.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, Buy-/Sell-Aufteilung, Dauer |
| KaizenOrderInfo | .GetRenderedOrders() | IReadOnlyDictionary<double, KaizenOrderLevelInfo> |
KaizenVWAP — Plot-Indizes: 0 VWAP · 1/2 Band 1 oben/unten · 3/4 Band 2 · 5/6 Band 3 · 7 VWAP der Vorsession.
Datenstrukturen
// 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; // wann die Zone entstand
public DateTime? DateInactive; // wann sie brach; null, wenn noch aktiv
}
// KaizenStackedImbalance.ActiveZones / .HistoricalZones
public sealed class ImbalanceZone
{
public int OriginBarIndex;
public int MitigationBarIndex; // -1, solange noch aktiv
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 Käufer-Aggressor, -1 Verkäufer-Aggressor
public bool IsBuyAggressor, IsSellAggressor;
}
// KaizenTPO.SinglePrints
public sealed class KaizenSinglePrintInfo
{
public double Price;
public int OriginBarIndex, MitigationBarIndex, OwnerProfileStartBar;
public bool IsActive;
}
Wenn still nichts zurückkommt
| Symptom | Ursache |
|---|---|
| Eine Liste ist immer leer, nirgends ein Fehler | Update() fehlt (Regel 3), oder die Lizenz deckt diesen Indikator nicht ab |
| Indikator hängt in (Calculating…), alle Werte 0 | new statt der Helper-Methode (Regel 1) |
| Strategie läuft, macht aber 0 Trades, Ausgabedateien nur mit Kopfzeile | MTF-Serie vom Host nicht vorgeladen (Regel 5); der Fehler steht im NinjaTrader-Log |
| Backtest sieht hervorragend aus, live nicht | Lookahead durch ungefilterte Zonenlisten (Regel 4) |
| CS0246 auf den Indikatornamen | using NinjaTrader.NinjaScript.Indicators.Kaizen; fehlt |
Wichtige Hinweise
- Tick Replay muss auf dem Chart für die Orderflow-Indikatoren aktiviert sein (siehe Tabelle oben).
- Namespace: immer
NinjaTrader.NinjaScript.Indicators.Kaizen, nie.Custom. - Kompilieren: NinjaScript Editor → New → Strategy → Code einfügen → F5.
- Lizenz: Dein Schlüssel muss jeden Kaizen-Indikator abdecken, den du referenzierst.
- Bar-Index unter OnBarClose-Hosts (in v2.3 korrigiert): Wird ein Kaizen-Indikator in einer Strategie mit
Calculate = OnBarClosegehostet, lieferten die Bot-APIs die Balkendaten um einen Index versetzt — „dieser Balken” gab die Werte des nächsten zurück. Wenn du Regeln auf diesen Werten gebaut und vor v2.3 gebacktestet hast, prüfe die Ergebnisse: Der Versatz wirkte wie ein Lookahead. - Bekannte Einschränkung — Footprint-Summenwerte:
CurrentBarDelta,CurrentBarTotalVolume,CurrentBarPOCund die Summenfelder ausGetFootprintForBar(...)bleiben in einer Strategie0. Die Engine berechnet sie inOnRender, und ein Strategie-Host löstOnRendernicht zuverlässig aus. Nutze stattdessenRecentAbsorptions— das wird inOnBarUpdategefüllt. Die Methoden auf Ebene einzelner Preislevel (GetBarDelta,GetBarPOCPrice, die Imbalance- und Absorptionslisten) sind davon nicht betroffen. - Mindestversion: v2.3. Frühere Versionen hatten abweichende Helper-Signaturen und den oben beschriebenen Bar-Index-Versatz.