BAcktesting Multi Instrument Strategy

Hello NinjaTrader Support,

I am developing a multi-instrument strategy using AddDataSeries() (multiple instruments running in parallel inside one strategy).

I currently have no way to test it:

  • I have not traded live yet
  • I have not used Market Replay yet (replay data must be downloaded day by day)
  • Therefore I tried the Strategy Analyzer

However, nothing happens in the Analyzer


Code (simplified)

protected override void OnStateChange()
{
    if (State == State.Configure)
    {
        AddDataSeries("NQ MAR25",  BarsPeriodType.Minute, 5);
        AddDataSeries("MNQ MAR25", BarsPeriodType.Minute, 5);
        AddDataSeries("6E MAR25",  BarsPeriodType.Minute, 15);
        AddDataSeries("M6E MAR25", BarsPeriodType.Minute, 15);
    }
}

protected override void OnBarUpdate()
{
    if (BarsInProgress == 3)
        EnterLong(3, 1, "RSI_L");

    if (BarsInProgress == 1)
        EnterLong(1, 1, "SHA_L");
}

Problem / Question

  • The Strategy Analyzer shows no trades
  • OnExecutionUpdate() is never called
  • CSV logging stays empty

How is a multi-instrument strategy supposed to be tested historically?
Is Market Replay the only supported way, or is there a recommended Analyzer-compatible approach? and if repla is the only way do i have to download ever day at a time?

Thank you.

Hi Arthur, The structure of your code snippet looks fine. You can absolutely test multi-instrument and multi-timeframe strategies in the analyzer. If you’re not getting any output, there is likely something else going wrong here. After an analyzer run, if you switch to the chart do you see anything? Do you have the data for the contracts you are interested in? If you’re looking for the current contracts, it should be 26 not 25 (Happy New Year!)

Also, just a heads up but there’s no official NT support here. If you need help from them you have to email support.

Thank you for the nice reply,
first of all happy new year to you too.
I don’t get any charttrades nor do i get any other trades. I suspect its the connection between the indicators and the strategy. I cant post the whole code because it would be rather long, however this is how i define a signal from the indicator in
OnBarUpdate:


so the signal resets every bar.

Signal:

if (isBullishCond && touchedEmaCond && closeAboveEmaCond && emaBelowShaHighCond && priceAbovePocCond)
        {
            double stop = (haHigh + haLow) / 2.0;
            double slDist = close - stop;

            if (slDist >= MinStopSize && slDist <= MaxStopSize)
            {
                int qty = CalcQty(slDist);
                if (qty > 0)
                {
                    EntryPrice  = close;
                    StopPrice   = stop;
                    TargetPrice = close + (slDist * RR);
                    Quantity    = qty;
                    HasEntry    = true; 
                    Print(Time[0] + " | !!! SIGNAL GEFUNDEN IM INDIKATOR !!!");
                }
                else Print(Time[0] + " | Qty ist 0");
            }
            else Print(Time[0] + " | SL Distanz ungĂĽltig: " + slDist);
        }
    }
}

and this is how i read the indicator for testing:

namespace NinjaTrader.NinjaScript.Strategies
{
public class SHA_Signal_Tester : Strategy
{
private SHAEMA_Indicator_FULLSYNC shaInd;

protected override void OnStateChange()
{
    if (State == State.SetDefaults)
    {
        Name = "SHA_Signal_Tester";
        Calculate = Calculate.OnBarClose;
    }
    else if (State == State.Configure)
    {
        // BIP 1: Der NQ 5-Minuten Chart
        AddDataSeries("NQ 03-26", BarsPeriodType.Minute, 5); 
			Print("data loaded TEST");
    }
    else if (State == State.DataLoaded)
    {
        // HIER liegt der Fehlerteufel: 
        // Wir mĂĽssen dem Indikator explizit sagen: Rechne auf BarsArray[1]!
        shaInd = SHAEMA_Indicator_FULLSYNC(BarsArray[1], 28, 28, 20, 800, 5, 5, 147, 0.5, 100, true);
		Print("Indikator bestĂĽckt");
    }
}

protected override void OnBarUpdate()
{
    // Wir reagieren NUR, wenn der Bar auf BIP 1 (NQ) schlieĂźt
    if (BarsInProgress == 1)
	{	
		Print("bars in Progress 1");
	
        if (shaInd.HasEntry) 
        	{
            Print(Time[0] + " | STRATEGIE HAT SIGNAL ERHALTEN!");
        	}
	}
	}
}

}

now the output:
data loaded TEST
Indikator bestĂĽckt
bars in Progress 1
.
.
.
!!Signal Gefunden im Indikator!! (telling me that the Indikator was set on HasEntry true)

but there is no print from the strategy finding the change, and i don’t know anymore where to find the solution..
Thanks allot for your help! Really means alot to a newcoming “programmer”!

I’m happy to help as best I can. Working with multiple timeframes and instruments can be a little tricky to get the timing right. I highly recommend reading this documentation page: Multi-Time Frame & Instruments to get more familiar with when/how the OnBarUpdate() calls will happen. It’s hard to say if this is the source of your problem without knowing the primary data series to which your strategy is attached. Is it also a 5 minute series? I don’t see anything in your code that’s glaringly wrong, but I do have some ideas that may help.

Personally, I’ve never used indicator properties to report state directly the way you are with HasEntry, HasExit, etc. I would be concerned that the reset would be happening out of sync. Typically, I use the Values array to communicate with a consumer. So for example, to report when the indicator has an entry I would do something like this:

if(signalFound)
    Values[0][0] = 1;
else
    Values[0][0] = 0;

Then in the strategy you can check the indicators state just like any other value. It’s also nice because you can now plot the value of the signal and see how it aligns with the data. You can also make it more convenient to the consumer by defining a property in your indicator. For example:

public Series<double> HasEntry { get { return Values[0];} }

Then the consumer can simply do a HasEntry[0] to get the signal state of the indicator’s “current” bar. I put current in quotes because again, the timing can be a little tricky when there’s more than one series involved.

If you want to output lots of things from your indicator all you have to do is AddPlot() to add more series to the Values array (similar to how AddDataSeries adds to the BarsArray).

In scenarios like this, I’ve also had problems with indicators not being kept up to date (requiring a call to Update() to force the indicator to calculate). However, if the debug output you showed is coming from a strategy run, then it appears your indicator is getting data just fine. I would be suspicious of a timing issue.

One thing you could do is add another print statement when you reset HasEntry in your indicator

if(HasEntry) Print(Time[0] + " | Resetting HasEntry");
HasEntry=false;

Then if your debug output shows the signal found and reset before the next strategy “bars in progress 1”, you’ll know it’s a timing issue.

While you can do a lot with just print() debugging, another great option is to use visual studio to debug the code as it is running. However, if you’re new that may be too advanced.

Hope some of that helps! And feel free to post as much code as you’re comfortable with. There’s quite a few really smart coders who post here and may be able to offer advice.

1 Like

Thanks very much!
gonna try your tips asap. I really appreciate the time you take explaining those things to me.
Hope to be able to give an update soon.

I just wanted to thank you again. Your tipps took care of the problem.
Have a nice day!!

1 Like

That’s great news. I’m glad you fixed your issues. Good luck in your future NT adventures!