Bot development API
Use the Kaizen indicators from your own NinjaScript strategies through a public API. Your strategy hosts the indicator and reads its values directly, no DLL access needed.
Updated: September 10, 2026
The Kaizen indicators expose a public API for use from your own NinjaScript strategies. You do not need to open or decompile the DLL — your strategy hosts the indicator and reads its data directly.
using NinjaTrader.NinjaScript.Indicators.Kaizen;
Omitting this using produces error CS0246.
Six rules that decide whether it works
Break any of these and the API returns empty data without an error message. That silence is what makes them expensive — the strategy runs, the log stays clean, and every value is zero.
-
Always use the helper method, never
new.new KaizenFootprint()does not pass through NinjaTrader’s caching and lifecycle wiring; the indicator hangs in (Calculating…) forever and every read returns 0 or null. UseKaizenFootprint(...)with all its parameters. There are no parameterless overloads — the helper takes every property, from 3 parameters (SuperTrend) to 37 (Footprint). -
Create it in
State.DataLoaded, notState.Configure.AddChartIndicator(...), if you use it, belongs there too. -
Call
Update()on indicators that have no plots. NinjaTrader drives hosted indicators lazily. For an indicator with plots, touchingValues[0][0]triggers the update chain; an indicator without plots has nothing to touch, so itsOnBarUpdateonly runs when Tick Replay happens to drive it. 11 of the 17 indicators have no plots — see the table below. WithoutUpdate()their engines never receive a single bar. -
Filter for look-ahead when reading zone or level lists. These lists carry the current state of every zone, including ones that did not exist yet at the bar you are evaluating, and
IsActivereflects today rather than that bar. In a backtest this silently becomes look-ahead. Filter on the timestamp or the origin bar index — both examples below show the pattern. -
Multi-timeframe: the host must preload the series. If you set
useMTF: true, your strategy must load that same series in itsState.Configure, e.g.AddDataSeries(BarsPeriodType.Minute, 30). A hosted NinjaScript may not load its own data. Skip this and NinjaTrader disables the strategy before the first bar — you get 0 trades, empty output files, and the error appears only in the NinjaTrader log, never on screen. -
An invalid license returns empty data, not an error. Every API returns 0,
null, or an empty list if the licence check fails. Confirm the indicator works on a chart first.
Example: Supply/Demand zones combined with VWAP
Both examples below assume the standard using block that NinjaTrader inserts when you create a new strategy (it already contains System.Windows.Media for Brushes and NinjaTrader.Gui for DashStyleHelper) — plus the Kaizen namespace shown above.
public class MySupplyDemandBot : Strategy
{
private KaizenSupplyDemand _sd;
private KaizenVWAP _vwap;
protected override void OnStateChange()
{
if (State == State.SetDefaults)
{
Name = "MySupplyDemandBot";
Calculate = Calculate.OnBarClose;
BarsRequiredToTrade = 25;
}
else if (State == State.DataLoaded)
{
_sd = KaizenSupplyDemand(
false, // useMTF
BarsPeriodType.Minute, 30, // mtfBarType, mtfBarPeriod (ignored when useMTF = false)
Brushes.DodgerBlue, // demandColor
Brushes.OrangeRed, // supplyColor
0.40f, 0.15f, 0.10f, 0.05f, // opacities: active line/area, broken line/area
1, // zoneLineWidth
false, // extendZones
false, true, // hideActiveZones, hideBrokenZones
200); // maxZoneCount
_vwap = KaizenVWAP(
KVWAPStyle.Directional, // vWAPStyle
1.28, 2.01, 2.51, // band 1 / 2 / 3 multipliers
5, // bandOpacity
DashStyleHelper.Dash); // bandLineStyle
}
}
protected override void OnBarUpdate()
{
if (CurrentBar < 25) return;
_sd.Update(); // required: Supply/Demand has no plots
double vwap = _vwap.Values[0][0];
double lower1 = _vwap.Values[2][0]; // lower band at 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 look-ahead: the zone must already exist, and must not have
// been broken, at the bar being evaluated
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, "LongDemandBelowVWAP");
}
}
}
}
Example: Stacked Imbalance zones
Note the parameter list — the helper has no short form. The values below are the indicator’s own defaults.
public class MyStackedImbalanceBot : Strategy
{
private KaizenStackedImbalance _si;
protected override void OnStateChange()
{
if (State == State.SetDefaults)
{
Name = "MyStackedImbalanceBot";
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(); // required: no 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 look-ahead
if (z.OriginBarIndex > CurrentBar) continue;
if (z.MitigationBarIndex >= 0 && z.MitigationBarIndex <= CurrentBar) continue;
if (Close[0] >= z.PriceLow && Close[0] <= z.PriceHigh)
{
Print("Price inside " + (z.IsBullish ? "bullish" : "bearish") + " zone "
+ z.PriceLow + " - " + z.PriceHigh);
}
}
}
}
Which indicator needs what
Update() is required whenever the indicator has no plots. Tick Replay must be enabled on the chart for the order-flow indicators.
| Indicator | Plots | Tick Replay | Update() required | Helper parameters |
|---|---|---|---|---|
| KaizenBigTrades | – | yes | yes | 15 |
| KaizenCVD | 3 | yes | no | 9 |
| KaizenClusterSearch | – | yes | yes | 12 |
| KaizenDeltaCandle | 2 | yes | no | 8 |
| KaizenFootprint | – | yes | yes | 37 |
| KaizenNakedLevels | – | no | yes | 11 |
| KaizenOrderInfo | – | no | yes | 5 |
| KaizenReversalIndicator | – | yes | yes | 25 |
| KaizenSpeedOfTape | 2 | yes | no | 7 |
| KaizenStackedImbalance | – | yes | yes | 16 |
| KaizenStatGrid | 2 | yes | no | 25 |
| KaizenStatsProfile | – | no | yes | 18 |
| KaizenSuperTrend | 2 | no | no | 3 |
| KaizenSupplyDemand | – | no | yes | 14 |
| KaizenTPO | – | no | yes | 34 |
| KaizenVWAP | 8 | no | no | 6 |
| KaizenVolumeProfile | – | yes | yes | 23 |
KaizenAlertLine and KaizenDeltaProfile have no bot API. They expose configuration properties only.
Available public APIs
| Indicator | Member | Returns |
|---|---|---|
| KaizenSupplyDemand | .Zones | IReadOnlyList<KaizenSupplyDemandZoneInfo> — all zones, active and broken |
.ActiveSupplyCount / .ActiveDemandCount | int | |
| KaizenVWAP | Values[0][0] … Values[7][0] | VWAP, 3 band pairs, prior-session VWAP — see below |
.CurrentVWAP / .CurrentStdDev / .PriorSessionVWAP | double — current bar only | |
| KaizenStackedImbalance | .ActiveZones | IReadOnlyList<ImbalanceZone> — all zones, active and mitigated |
.HistoricalZones | IReadOnlyList<ImbalanceZone> — mitigated zones only | |
.RecentAbsorptions | IReadOnlyList<KaizenAbsorptionInfo> | |
.LastImbalanceBarIndex | int — -1 if none yet | |
| KaizenFootprint | .GetBarDelta(barIndex) | long — signed delta for the bar |
.GetBarPOCPrice(barIndex) | double | |
.GetBullishImbalancePrices(barIndex) / .GetBearishImbalancePrices(barIndex) | List<double> | |
.GetBullishAbsorptionPrices(barIndex) / .GetBearishAbsorptionPrices(barIndex) | List<double> | |
.GetFootprintForBar(barIndex) | KaizenFootprintBarInfo — see limitation below | |
.RecentAbsorptions | IReadOnlyList<KaizenAbsorptionInfo> | |
.UnfinishedLevels | IReadOnlyList<KaizenFootprintUnfinishedLevelInfo> | |
| KaizenVolumeProfile | .CurrentPOC / .CurrentVAH / .CurrentVAL | double |
.CurrentPriceVolume | SortedDictionary<double, long> — price → volume | |
.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 — also Open/High/Low variants |
.SessionCvd | double | |
.CvdCandleOpen / High / Low / Close | double | |
| KaizenDeltaCandle | .GetDeltaClose(barsAgo) / .GetDeltaCloseAt(barIndex) | double — also Open/High/Low variants |
.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 up, -1 down, 0 before the first calculation |
.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, volume, buy/sell split, duration |
| KaizenOrderInfo | .GetRenderedOrders() | IReadOnlyDictionary<double, KaizenOrderLevelInfo> |
KaizenVWAP plot indices: 0 VWAP · 1/2 band 1 upper/lower · 3/4 band 2 · 5/6 band 3 · 7 prior-session VWAP.
Data structures
// 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; // when the zone formed
public DateTime? DateInactive; // when it broke; null if still active
}
// KaizenStackedImbalance.ActiveZones / .HistoricalZones
public sealed class ImbalanceZone
{
public int OriginBarIndex;
public int MitigationBarIndex; // -1 while still active
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 buy aggressor, -1 sell aggressor
public bool IsBuyAggressor, IsSellAggressor;
}
// KaizenTPO.SinglePrints
public sealed class KaizenSinglePrintInfo
{
public double Price;
public int OriginBarIndex, MitigationBarIndex, OwnerProfileStartBar;
public bool IsActive;
}
When it silently returns nothing
| Symptom | Cause |
|---|---|
| A list is always empty, no error anywhere | Update() missing (rule 3), or the licence is not valid for that indicator |
| Indicator stuck in (Calculating…), all reads 0 | new instead of the helper method (rule 1) |
| Strategy runs but makes 0 trades, output files contain headers only | MTF series not preloaded by the host (rule 5); the error is in the NinjaTrader log |
| Backtest looks excellent, live does not | Look-ahead through unfiltered zone lists (rule 4) |
| CS0246 on the indicator name | using NinjaTrader.NinjaScript.Indicators.Kaizen; missing |
Important notes
- Tick Replay must be enabled on the chart for the order-flow indicators (see the table above).
- Namespace: always
NinjaTrader.NinjaScript.Indicators.Kaizen, never.Custom. - Compiling: NinjaScript Editor → New → Strategy → paste your code → F5.
- Licence: your key must cover every Kaizen indicator you reference.
- Bar index under
OnBarClosehosts (fixed in v2.3): when a Kaizen indicator was hosted inside a strategy runningCalculate = OnBarClose, the bot APIs returned bar data off by one index — “this bar” gave the next bar’s values. If you built rules on those values and backtested before v2.3, review the results: the offset acted like look-ahead. - Known limitation — Footprint summary values:
CurrentBarDelta,CurrentBarTotalVolume,CurrentBarPOCand the summary fields ofGetFootprintForBar(...)stay0in a strategy. The engine computes them inOnRender, which a strategy host does not reliably trigger. UseRecentAbsorptionsinstead — it is populated inOnBarUpdate. The per-level methods (GetBarDelta,GetBarPOCPrice, the imbalance and absorption price lists) are unaffected. - Minimum version: v2.3. Earlier releases had different helper signatures and the bar-index offset described above.