#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.Gui.Tools; using NinjaTrader.Data; using NinjaTrader.NinjaScript; using NinjaTrader.Core.FloatingPoint; using NinjaTrader.NinjaScript.DrawingTools; #endregion //This namespace holds Indicators in this folder and is required. Do not change it. namespace NinjaTrader.NinjaScript.Indicators { public class OrderBlockIndicator : Indicator { // v1 = initial version (8-JUN-2025) #region Version Info private string codeUpdated = "Last updated: 8-JUN-2025"; private string codeVers = "v1"; private string codeAuthor = "Author: XX"; private string codeFor = "For: Reece"; private string codeOrder = "Order Ref: # 10278"; private string codeProductName = "Product Name: OrderBlockIndicator"; public override string DisplayName { get { return Name; } } #endregion private List bullishOrderBlocks; private List bearishOrderBlocks; // Input properties private bool plotOB = true; private Brush obBullColor = Brushes.Green; private Brush obBearColor = Brushes.Red; private int obMaxBoxSet = 10; private bool plotLabelOB = true; protected override void OnStateChange() { if (State == State.SetDefaults) { Description = @"Order Block Indicator - Body-based Order Blocks"; Name = "Order Block Indicator"; versionInfo = codeVers + " " + codeUpdated; Calculate = Calculate.OnBarClose; IsOverlay = true; DisplayInDataBox = true; DrawOnPricePanel = true; DrawHorizontalGridLines = true; DrawVerticalGridLines = true; PaintPriceMarkers = true; ScaleJustification = NinjaTrader.Gui.Chart.ScaleJustification.Right; IsSuspendedWhileInactive = true; // Initialize input properties PlotOB = true; ObBullColor = Brushes.Green; ObBearColor = Brushes.Red; ObMaxBoxSet = 10; PlotLabelOB = true; AddPlot(new Stroke(Brushes.Transparent), PlotStyle.Dot, "BullishObTop"); AddPlot(new Stroke(Brushes.Transparent), PlotStyle.Dot, "BullishObBottom"); AddPlot(new Stroke(Brushes.Transparent), PlotStyle.Dot, "BearishObTop"); AddPlot(new Stroke(Brushes.Transparent), PlotStyle.Dot, "BearishObBottom"); ShowTransparentPlotsInDataBox = true; } else if (State == State.Configure) { // Initialize collections bullishOrderBlocks = new List(); bearishOrderBlocks = new List(); } else if (State == State.DataLoaded) { PrintSettings(); } } protected override void OnBarUpdate() { // Initialize plots to 0, indicating no new FVG on this bar BullishObTop[0] = 0; BullishObBottom[0] = 0; BearishObTop[0] = 0; BearishObBottom[0] = 0; // Need at least 3 bars for order block detection if (CurrentBar < 2) return; // Check for bullish order block formation if (IsObUp(1) && plotOB) { // Calculate body high for bullish OB (use body high, not wick) double bodyHigh2 = Math.Max(Open[2], Close[2]); double bodyLow1 = Math.Min(Open[1], Close[1]); double bodyLow2 = Math.Min(Open[2], Close[2]); double obBottom = Math.Min(bodyLow2, bodyLow1); BullishObTop[0] = bodyHigh2; BullishObBottom[0] = obBottom; // Create bullish order block rectangle DrawingTools.Rectangle bullRect = Draw.Rectangle(this, "BullOB_" + CurrentBar.ToString(), false, 2, bodyHigh2, 0, obBottom, obBullColor, obBullColor, 50); // Add to collection and manage max count bullishOrderBlocks.Add(bullRect); if (bullishOrderBlocks.Count > obMaxBoxSet) { if (ChartControl != null) RemoveDrawObject(bullishOrderBlocks[0].Tag); bullishOrderBlocks.RemoveAt(0); } // Add label if enabled if (plotLabelOB) { Draw.Text(this, "BullOBLabel_" + CurrentBar.ToString(), false, "OB+", 0, bodyHigh2, 0, Brushes.Gray, new SimpleFont("Arial", 8), TextAlignment.Right, Brushes.Transparent, Brushes.Transparent, 0); } } // Check for bearish order block formation if (IsObDown(1) && plotOB) { // Calculate body low for bearish OB (use body low, not wick) double bodyLow2 = Math.Min(Open[2], Close[2]); double bodyHigh1 = Math.Max(Open[1], Close[1]); double bodyHigh2 = Math.Max(Open[2], Close[2]); double obTop = Math.Max(bodyHigh2, bodyHigh1); BearishObTop[0] = obTop; BearishObBottom[0] = bodyLow2; // Create bearish order block rectangle DrawingTools.Rectangle bearRect = Draw.Rectangle(this, "BearOB_" + CurrentBar.ToString(), false, 2, obTop, 0, bodyLow2, obBearColor, obBearColor, 50); // Add to collection and manage max count bearishOrderBlocks.Add(bearRect); if (bearishOrderBlocks.Count > obMaxBoxSet) { if (ChartControl != null) RemoveDrawObject(bearishOrderBlocks[0].Tag); bearishOrderBlocks.RemoveAt(0); } // Add label if enabled if (plotLabelOB) { Draw.Text(this, "BearOBLabel_" + CurrentBar.ToString(), false, "OB-", 0, bodyLow2, 0, Brushes.Gray, new SimpleFont("Arial", 8), TextAlignment.Right, Brushes.Transparent, Brushes.Transparent, 0); } } } // Helper method to check if candle is up private bool IsUp(int index) { return Close[index] > Open[index]; } // Helper method to check if candle is down private bool IsDown(int index) { return Close[index] < Open[index]; } // Helper method to detect bullish order block formation (body-based) private bool IsObUp(int index) { // Previous candle is down, current candle is up, and current close is above previous body high double prevBodyHigh = Math.Max(Open[index + 1], Close[index + 1]); return IsDown(index + 1) && IsUp(index) && Close[index] > prevBodyHigh; } // Helper method to detect bearish order block formation (body-based) private bool IsObDown(int index) { // Previous candle is up, current candle is down, and current close is below previous body low double prevBodyLow = Math.Min(Open[index + 1], Close[index + 1]); return IsUp(index + 1) && IsDown(index) && Close[index] < prevBodyLow; } #region Properties [Browsable(false)] [XmlIgnore] public Series BullishObTop { get { return Values[0]; } } [Browsable(false)] [XmlIgnore] public Series BullishObBottom { get { return Values[1]; } } [Browsable(false)] [XmlIgnore] public Series BearishObTop { get { return Values[2]; } } [Browsable(false)] [XmlIgnore] public Series BearishObBottom { get { return Values[3]; } } [NinjaScriptProperty] [Display(Name = "NinjaScript Output", Description = "", Order = 10, GroupName = "Settings")] public bool showOutput { get; set; } [NinjaScriptProperty] [ReadOnly(true)] [Display(Name = "Version:", Description = "", Order = 30, GroupName = "Settings")] public string versionInfo { get; set; } [NinjaScriptProperty] [Display(Name = "Plot Order Blocks", Description = "Enable/disable order block plotting", Order = 1, GroupName = "Order Blocks")] public bool PlotOB { get { return plotOB; } set { plotOB = value; } } [NinjaScriptProperty] [XmlIgnore] [Display(Name = "Bullish OB Color", Description = "Color for bullish order blocks", Order = 2, GroupName = "Order Blocks")] public Brush ObBullColor { get { return obBullColor; } set { obBullColor = value; } } [Browsable(false)] public string ObBullColorSerializable { get { return Serialize.BrushToString(obBullColor); } set { obBullColor = Serialize.StringToBrush(value); } } [NinjaScriptProperty] [XmlIgnore] [Display(Name = "Bearish OB Color", Description = "Color for bearish order blocks", Order = 3, GroupName = "Order Blocks")] public Brush ObBearColor { get { return obBearColor; } set { obBearColor = value; } } [Browsable(false)] public string ObBearColorSerializable { get { return Serialize.BrushToString(obBearColor); } set { obBearColor = Serialize.StringToBrush(value); } } [NinjaScriptProperty] [Range(1, 100)] [Display(Name = "Maximum OB Boxes", Description = "Maximum number of order block boxes to display", Order = 4, GroupName = "Order Blocks")] public int ObMaxBoxSet { get { return obMaxBoxSet; } set { obMaxBoxSet = Math.Max(1, value); } } [NinjaScriptProperty] [Display(Name = "Plot OB Labels", Description = "Enable/disable order block labels", Order = 5, GroupName = "Order Blocks")] public bool PlotLabelOB { get { return plotLabelOB; } set { plotLabelOB = value; } } #endregion #region Helpers private void PrintSettings() { PM("#########################################################"); PM(codeAuthor); PM(codeFor); PM(codeOrder); PM(codeProductName); PM(codeUpdated); PM(codeVers); PM("#########################################################"); PM("Indicator enabled: " + DateTime.Now); PM("Indicator name: " + this.Name); PM("Indicator Settings..."); PM("Chart: " + this.ChartBars); PM("Chart: " + Bars.ToChartString()); PM("Instrument: " + Instrument.FullName); PM("Default Trading Hours: " + Instrument.MasterInstrument.TradingHours); PM("Chart Trading Hours: " + TradingHours.Name); PM("Timeframe: " + this.BarsPeriod); PM("Bars Period Type Name: " + BarsPeriod.BarsPeriodTypeName + " Type #" + BarsPeriod.BarsPeriodTypeSerialize + " Value: " + BarsPeriod.Value + " Value 2: " + BarsPeriod.Value2); PM("Tick Replay: " + IsTickReplays[0]); PM("Bars on chart: " + Count); PM("Bars - Start Date: " + Bars.FromDate.ToString("yyyy-MM-dd")); PM("Bars - End Date: " + Bars.ToDate.ToString("yyyy-MM-dd")); PM("Timezone: " + Core.Globals.GeneralOptions.TimeZoneInfo); PM("Install folder: " + NinjaTrader.Core.Globals.InstallDir); PM("User directory: " + NinjaTrader.Core.Globals.UserDataDir); PM("Machine ID: " + Cbi.License.MachineId); PM("---------------------------------------------------------"); PM("Setup..."); PM("Calculate: " + Calculate); PM("Label: " + this.DisplayName); PM("Maximum bars look back: " + MaximumBarsLookBack); PM("BarsRequiredToPlot: " + BarsRequiredToPlot); PM("---------------------------------------------------------"); PM("Settings - NinjaScript Output: " + showOutput); PM("Settings - Version: " + versionInfo); PM("Order Blocks - Plot Order Blocks: " + PlotOB); PM("Order Blocks - Bullish OB Color: " + GetBrushName(ObBullColor)); PM("Order Blocks - Bearish OB Color: " + GetBrushName(ObBearColor)); PM("Order Blocks - Maximum OB Boxes: " + ObMaxBoxSet); PM("Order Blocks - Plot OB Labels: " + PlotLabelOB); PM("---------------------------------------------------------"); } private void PM(string msg, [System.Runtime.CompilerServices.CallerMemberName] string sourceMethod = "") { if (!showOutput) return; DateTime timestamp = DateTime.Now; try { if (Bars != null && CurrentBar >= 0) { timestamp = Time[0]; } } catch { /* Fallback to DateTime.Now */ } Print(string.Format("[{0} - {1} {2}] {3:yyyy-MM-dd HH:mm:ss} - {4}", Name, Bars != null ? Bars.ToChartString() : "No Bars", sourceMethod, timestamp, msg)); } public string GetBrushName(Brush brush) { SolidColorBrush solidColorBrush = brush as SolidColorBrush; if (solidColorBrush == null) return "Not a SolidColorBrush"; string colorString = solidColorBrush.Color.ToString(); foreach (var colorProperty in typeof(Colors).GetProperties()) { if (colorProperty.PropertyType == typeof(Color)) { Color color = (Color)colorProperty.GetValue(null); if (color.ToString() == colorString) { return colorProperty.Name; } } } // No matching named color found, return the original color string return colorString; } #endregion } } #region NinjaScript generated code. Neither change nor remove. namespace NinjaTrader.NinjaScript.Indicators { public partial class Indicator : NinjaTrader.Gui.NinjaScript.IndicatorRenderBase { private OrderBlockIndicator[] cacheOrderBlockIndicator; public OrderBlockIndicator OrderBlockIndicator(bool showOutput, string versionInfo, bool plotOB, Brush obBullColor, Brush obBearColor, int obMaxBoxSet, bool plotLabelOB) { return OrderBlockIndicator(Input, showOutput, versionInfo, plotOB, obBullColor, obBearColor, obMaxBoxSet, plotLabelOB); } public OrderBlockIndicator OrderBlockIndicator(ISeries input, bool showOutput, string versionInfo, bool plotOB, Brush obBullColor, Brush obBearColor, int obMaxBoxSet, bool plotLabelOB) { if (cacheOrderBlockIndicator != null) for (int idx = 0; idx < cacheOrderBlockIndicator.Length; idx++) if (cacheOrderBlockIndicator[idx] != null && cacheOrderBlockIndicator[idx].showOutput == showOutput && cacheOrderBlockIndicator[idx].versionInfo == versionInfo && cacheOrderBlockIndicator[idx].PlotOB == plotOB && cacheOrderBlockIndicator[idx].ObBullColor == obBullColor && cacheOrderBlockIndicator[idx].ObBearColor == obBearColor && cacheOrderBlockIndicator[idx].ObMaxBoxSet == obMaxBoxSet && cacheOrderBlockIndicator[idx].PlotLabelOB == plotLabelOB && cacheOrderBlockIndicator[idx].EqualsInput(input)) return cacheOrderBlockIndicator[idx]; return CacheIndicator(new OrderBlockIndicator(){ showOutput = showOutput, versionInfo = versionInfo, PlotOB = plotOB, ObBullColor = obBullColor, ObBearColor = obBearColor, ObMaxBoxSet = obMaxBoxSet, PlotLabelOB = plotLabelOB }, input, ref cacheOrderBlockIndicator); } } } namespace NinjaTrader.NinjaScript.MarketAnalyzerColumns { public partial class MarketAnalyzerColumn : MarketAnalyzerColumnBase { public Indicators.OrderBlockIndicator OrderBlockIndicator(bool showOutput, string versionInfo, bool plotOB, Brush obBullColor, Brush obBearColor, int obMaxBoxSet, bool plotLabelOB) { return indicator.OrderBlockIndicator(Input, showOutput, versionInfo, plotOB, obBullColor, obBearColor, obMaxBoxSet, plotLabelOB); } public Indicators.OrderBlockIndicator OrderBlockIndicator(ISeries input , bool showOutput, string versionInfo, bool plotOB, Brush obBullColor, Brush obBearColor, int obMaxBoxSet, bool plotLabelOB) { return indicator.OrderBlockIndicator(input, showOutput, versionInfo, plotOB, obBullColor, obBearColor, obMaxBoxSet, plotLabelOB); } } } namespace NinjaTrader.NinjaScript.Strategies { public partial class Strategy : NinjaTrader.Gui.NinjaScript.StrategyRenderBase { public Indicators.OrderBlockIndicator OrderBlockIndicator(bool showOutput, string versionInfo, bool plotOB, Brush obBullColor, Brush obBearColor, int obMaxBoxSet, bool plotLabelOB) { return indicator.OrderBlockIndicator(Input, showOutput, versionInfo, plotOB, obBullColor, obBearColor, obMaxBoxSet, plotLabelOB); } public Indicators.OrderBlockIndicator OrderBlockIndicator(ISeries input , bool showOutput, string versionInfo, bool plotOB, Brush obBullColor, Brush obBearColor, int obMaxBoxSet, bool plotLabelOB) { return indicator.OrderBlockIndicator(input, showOutput, versionInfo, plotOB, obBullColor, obBearColor, obMaxBoxSet, plotLabelOB); } } } #endregion