I have a multi-series NinjaTrader strategy.
The strategy only trades on BarsInProgress == 1 and uses its own internally created indicator with Closes[1] and Closes[2].
However, simply changing the host chart (BIP0) to another instrument or timeframe changes the backtest results (mainly exit timing), even though BIP0 is never used in logic.
Is there a way to make such a strategy truly host-chart independent or is this a NinjaTrader engine limitation?
Thank you all in advance!
#region Using declarations
using System;
using NinjaTrader.Cbi;
using NinjaTrader.Data;
using NinjaTrader.NinjaScript;
using NinjaTrader.NinjaScript.Strategies;
#endregion
namespace NinjaTrader.NinjaScript.Strategies
{
public class MultiBipTestStrategy : Strategy
{
private DummySignalEngine sig;
protected override void OnStateChange()
{
if (State == State.SetDefaults)
{
Name = "MultiBipTestStrategy";
Calculate = Calculate.OnBarClose;
EntriesPerDirection = 1;
EntryHandling = EntryHandling.AllEntries;
IsExitOnSessionCloseStrategy = false;
}
else if (State == State.Configure)
{
// BIP0 = Host Chart (ignored)
// BIP1 = Trading timeframe
// BIP2 = Higher timeframe
AddDataSeries("6E MAR26", BarsPeriodType.Minute, 15); // BIP1
AddDataSeries("6E MAR26", BarsPeriodType.Minute, 120); // BIP2
}
else if (State == State.DataLoaded)
{
// Create signal engine on BIP1
sig = DummySignalEngine(Closes[1]);
sig.SetHtfSeries(Closes[2]);
// IMPORTANT: NOT adding to chart
// AddChartIndicator(sig); // intentionally NOT used
}
}
protected override void OnBarUpdate()
{
// Only process trading series
if (BarsInProgress != 1)
return;
if (sig == null)
return;
if (CurrentBars[1] < 50 || CurrentBars[2] < 50)
return;
int d = (int)Math.Round(sig[0]);
if (d == 0)
return;
// Exit
if (d < 0)
ExitLong("ExitL", "Long");
if (d > 0)
ExitShort("ExitS", "Short");
// Entry
if (d > 0)
EnterLong(1, "Long");
if (d < 0)
EnterShort(1, "Short");
}
}
}
#region Using declarations
using System;
using NinjaTrader.NinjaScript;
using NinjaTrader.NinjaScript.Indicators;
using System.Windows.Media;
#endregion
namespace NinjaTrader.NinjaScript.Indicators
{
public class DummySignalEngine : Indicator
{
private ISeries htfClose;
private int internalState = 0;
public void SetHtfSeries(ISeries<double> htfSeries)
{
htfClose = htfSeries;
}
protected override void OnStateChange()
{
if (State == State.SetDefaults)
{
Name = "DummySignalEngine";
Calculate = Calculate.OnBarClose;
IsOverlay = false;
AddPlot(Brushes.Transparent, "Delta");
}
}
protected override void OnBarUpdate()
{
// Only calculate on primary input (which is Closes[1] from strategy)
if (BarsInProgress != 0)
return;
Values[0][0] = 0;
if (htfClose == null)
return;
if (CurrentBar < 50 || htfClose.Count < 50)
return;
// =========================
// DUMMY LOGIC (NON-TRADING)
// =========================
// Just some deterministic but meaningless state machine
double a = Input[0];
double b = htfClose[0];
int oldState = internalState;
int newState = internalState;
if (a > b)
newState = 1;
else if (a < b)
newState = -1;
int delta = newState - oldState;
internalState = newState;
Values[0][0] = delta;
}
}
}