The strategy trades NQ futures using breakout and reversal patterns with SMA-based sentiment analysis (Strong Bullish, Bullish, Bearish, and Strong Bearish). Breakouts trigger when the price exceeds the high/low of the last 5 bars, and sentiment aligns. Stop-loss and take-profit use a risk-reward ratio and ATR-based thresholds. Reversals activate on significant trend changes when ATR exceeds a threshold, and the Fast SMA crosses the Slow SMA.
I’ve disabled the time of day and day of week filters for debugging. I’m backtesting NQ12-24 (Sep 12–Dec 13). Any ideas on what I might be missing? Cheers!
#region Using declarations
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using System.Xml.Serialization;
using NinjaTrader.Cbi;
using NinjaTrader.Gui;
using NinjaTrader.Gui.Chart;
using NinjaTrader.Gui.SuperDom;
using NinjaTrader.Data;
using NinjaTrader.NinjaScript;
using NinjaTrader.Core.FloatingPoint;
using NinjaTrader.NinjaScript.Indicators;
using NinjaTrader.NinjaScript.DrawingTools;
#endregion
//This namespace holds strategies in this folder and is required. Do not change it.
namespace NinjaTrader.NinjaScript.Strategies
{
public class NQIntraDay : Strategy
{
#region Properties
[NinjaScriptProperty]
[Display(Name = "Trading Start Time", Order = 1, GroupName = "Trading Schedule")]
public TimeSpan StartTime { get; set; }
[NinjaScriptProperty]
[Display(Name = "Trading End Time", Order = 2, GroupName = "Trading Schedule")]
public TimeSpan EndTime { get; set; }
[NinjaScriptProperty]
[Display(Name = "Allowed Days of Week", Order = 3, GroupName = "Trading Schedule")]
public DayOfWeek[] AllowedDays { get; set; }
[NinjaScriptProperty]
[Display(Name = "Cooldown Period (minutes)", Order = 4, GroupName = "Trade Settings")]
public int CooldownMinutes { get; set; }
[NinjaScriptProperty]
[Display(Name = "Limit Order Bar Duration", Order = 5, GroupName = "Trade Settings")]
public int LimitOrderBarDuration { get; set; }
[NinjaScriptProperty]
[Display(Name = "Stop-Loss Period", Order = 6, GroupName = "Trade Settings")]
public int StopLossPeriod { get; set; }
[NinjaScriptProperty]
[Display(Name = "Risk-Reward Ratio", Order = 7, GroupName = "Trade Settings")]
public double RiskRewardRatio { get; set; }
[NinjaScriptProperty]
[Display(Name = "ATR Threshold", Order = 8, GroupName = "Volatility Settings")]
public double ATRThreshold { get; set; }
[NinjaScriptProperty]
[Display(Name = "Fast Trend-Reversal SMA Period", Order = 9, GroupName = "Trend Reversal Settings")]
public int FastTrendReversalSMAPeriod { get; set; }
[NinjaScriptProperty]
[Display(Name = "Slow Trend-Reversal SMA Period", Order = 10, GroupName = "Trend Reversal Settings")]
public int SlowTrendReversalSMAPeriod { get; set; }
[NinjaScriptProperty]
[Display(Name = "Reversal Duration (Bars)", Order = 11, GroupName = "Trend Reversal Settings")]
public int ReversalDurationBars { get; set; }
[NinjaScriptProperty]
[Display(Name = "Longer-Term SMA Period", Order = 12, GroupName = "Market Sentiment Settings")]
public int LongerTermSMAPeriod { get; set; }
#endregion
#region Fields
private SMA fastSMA;
private SMA slowSMA;
private SMA longerTermSMA;
private ATR atr;
private bool isReversalActive;
private int reversalBarsCounter;
private DateTime lastTradeExitTime;
private int activeOrderBars;
#endregion
#region State Management
protected override void OnStateChange()
{
if (State == State.SetDefaults)
{
Name = "NQIntraDay";
StartTime = new TimeSpan(9, 30, 0);
EndTime = new TimeSpan(16, 0, 0);
AllowedDays = new DayOfWeek[] { DayOfWeek.Sunday, DayOfWeek.Monday, DayOfWeek.Tuesday, DayOfWeek.Wednesday, DayOfWeek.Thursday, DayOfWeek.Friday, DayOfWeek.Saturday };
CooldownMinutes = 10;
LimitOrderBarDuration = 5;
StopLossPeriod = 14;
RiskRewardRatio = 2.0;
ATRThreshold = 2.0;
FastTrendReversalSMAPeriod = 5;
SlowTrendReversalSMAPeriod = 50;
ReversalDurationBars = 10;
LongerTermSMAPeriod = 200;
Calculate = Calculate.OnEachTick; // Change to operate on a per-tick basis
}
else if (State == State.Configure)
{
fastSMA = SMA(Close, FastTrendReversalSMAPeriod);
slowSMA = SMA(Close, SlowTrendReversalSMAPeriod);
longerTermSMA = SMA(Close, LongerTermSMAPeriod);
atr = ATR(14);
AddChartIndicator(fastSMA);
AddChartIndicator(slowSMA);
AddChartIndicator(longerTermSMA);
AddChartIndicator(atr);
lastTradeExitTime = DateTime.MinValue;
isReversalActive = false;
reversalBarsCounter = 0;
}
}
#endregion
#region OnBarUpdate
protected override void OnBarUpdate()
{
// Ensure enough bars are loaded for indicators
if (CurrentBars[0] < BarsRequiredToTrade
|| CurrentBars[0] < Math.Max(StopLossPeriod, Math.Max(FastTrendReversalSMAPeriod, SlowTrendReversalSMAPeriod)))
return;
if (State != State.Realtime)
return;
if ((DateTime.Now - lastTradeExitTime).TotalMinutes < CooldownMinutes)
return;
CheckForTrendReversal();
if (isReversalActive)
{
ExecuteReversalLogic();
reversalBarsCounter++;
if (reversalBarsCounter >= ReversalDurationBars || fastSMA[0] > slowSMA[0])
{
isReversalActive = false;
reversalBarsCounter = 0;
}
}
else
{
ExecuteBreakoutStrategy();
}
}
#endregion
#region Helper Methods
private bool IsInTradingWindow()
{
return true; // Temporarily disable time of day check for debugging
}
private bool IsAllowedDay()
{
return true; // Temporarily allow all days for debugging
}
public void RecordTradeExit()
{
lastTradeExitTime = DateTime.Now;
}
private string AssessMarketSentiment()
{
double currentPrice = Close[0];
double fastSMAValue = fastSMA[1];
double slowSMAValue = slowSMA[1];
double longerTermSMAValue = longerTermSMA[1];
string sentiment;
if (fastSMAValue > slowSMAValue && slowSMAValue > longerTermSMAValue)
sentiment = "Strong Bullish"; // All SMAs aligned upwards
else if (fastSMAValue < slowSMAValue && slowSMAValue < longerTermSMAValue)
sentiment = "Strong Bearish"; // All SMAs aligned downwards
else if (currentPrice > longerTermSMAValue)
sentiment = "Bullish"; // Price above longer-term trend
else if (currentPrice < longerTermSMAValue)
sentiment = "Bearish"; // Price below longer-term trend
else
sentiment = "Neutral"; // No strong trend alignment
// Debug print for market sentiment
Print($"AssessMarketSentiment -> Sentiment: {sentiment}, FastSMA: {fastSMAValue}, SlowSMA: {slowSMAValue}, LongerSMA: {longerTermSMAValue}, Close[0]: {currentPrice}");
return sentiment;
}
private void CheckForTrendReversal()
{
if (atr[1] > ATRThreshold && fastSMA[0] <= slowSMA[0] && fastSMA[1] >= slowSMA[1])
{
isReversalActive = true;
reversalBarsCounter = 0;
}
}
private void ExecuteReversalLogic()
{
string sentiment = AssessMarketSentiment();
// Exit existing positions before reversal logic
if (Position.MarketPosition == MarketPosition.Long)
{
ExitLong("ExitLongReversal", "ReversalLong");
}
else if (Position.MarketPosition == MarketPosition.Short)
{
ExitShort("ExitShortReversal", "ReversalShort");
}
// Enter new positions
if (sentiment == "Bullish" && fastSMA[0] > slowSMA[0] && Position.MarketPosition != MarketPosition.Long)
{
EnterLong("ReversalLong");
}
else if (sentiment == "Bearish" && fastSMA[0] < slowSMA[0] && Position.MarketPosition != MarketPosition.Short)
{
EnterShort("ReversalShort");
}
}
private void ExecuteBreakoutStrategy()
{
string sentiment = AssessMarketSentiment();
Print($"Market Sentiment: {AssessMarketSentiment()}, FastSMA: {fastSMA[0]}, SlowSMA: {slowSMA[0]}, LongerSMA: {longerTermSMA[0]}");
if (sentiment == "Neutral") sentiment = "Bullish"; // Temporary override for debugging
// Long Breakout
if ((sentiment == "Bullish" || sentiment == "Strong Bullish") && Close[0] > MAX(High, 5)[1] && Position.MarketPosition != MarketPosition.Long)
{
Print($"Entering Long: Sentiment = {sentiment}, Close[0] = {Close[0]}, MAX(High, 5)[1] = {MAX(High, 5)[1]}");
Draw.ArrowUp(this, "LongSignal" + CurrentBar, true, 0, Low[0] - TickSize, Brushes.Green); // Debugging arrow
EnterLongLimit(Close[0], "BreakoutLong");
SetStopLossForLong();
SetTakeProfitForLong();
}
// Short Breakout
if ((sentiment == "Bearish" || sentiment == "Strong Bearish") && Close[0] < MIN(Low, 5)[1] && Position.MarketPosition != MarketPosition.Short)
{
Print($"Entering Short: Sentiment = {sentiment}, Close[0] = {Close[0]}, MIN(Low, 5)[1] = {MIN(Low, 5)[1]}");
Draw.ArrowDown(this, "ShortSignal" + CurrentBar, true, 0, High[0] + TickSize, Brushes.Red); // Debugging arrow
double limitPrice = Close[0]; // Use current Close price as the limit price
EnterShortLimit(limitPrice, "BreakoutShort");
SetStopLossForShort();
SetTakeProfitForShort();
}
}
private void SetStopLossForLong()
{
double lowestLow = MIN(Low, StopLossPeriod)[0];
SetStopLoss(CalculationMode.Price, lowestLow - 2 * TickSize);
}
private void SetStopLossForShort()
{
double highestHigh = MAX(High, StopLossPeriod)[0];
SetStopLoss(CalculationMode.Price, highestHigh + 2 * TickSize);
}
private void SetTakeProfitForLong()
{
double entryPrice = Close[0];
double stopLoss = MIN(Low, StopLossPeriod)[0] - 2 * TickSize;
SetProfitTarget(CalculationMode.Price, entryPrice + (entryPrice - stopLoss) * RiskRewardRatio);
}
private void SetTakeProfitForShort()
{
double entryPrice = Close[0];
double stopLoss = MAX(High, StopLossPeriod)[0] + 2 * TickSize;
SetProfitTarget(CalculationMode.Price, entryPrice - (stopLoss - entryPrice) * RiskRewardRatio);
}
#endregion
}
}

Comment