#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 LuxAlgoMarketStructureFractal : Indicator { // v1 = initial version (4-JUN-2025) #region Variables private string codeUpdated = "Last updated: 4-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: LuxAlgoMarketStructureFractal"; // Fractal structure private class Fractal { public double Value { get; set; } public int Location { get; set; } public bool IsCrossed { get; set; } } // Variables for fractal tracking private Fractal upper; private Fractal lower; private int p; private int bullMsCount = 0; private int bearMsCount = 0; private int bullfCount = 0; private int bearfCount = 0; private bool brokenSup = false; private bool brokenRes = false; private int os = 0; // Order state: 1 = bullish, -1 = bearish // Support/Resistance levels private double supportLevel = double.NaN; private double resistanceLevel = double.NaN; private bool supportActive = false; private bool resistanceActive = false; #endregion #region Config public override string DisplayName { get { return Name; } } #endregion protected override void OnStateChange() { if (State == State.SetDefaults) { Name = "Market Structure CHoCH/BOS (Fractal) [LuxAlgo]"; Calculate = Calculate.OnBarClose; IsOverlay = true; versionInfo = codeVers + " " + codeUpdated; ShowTransparentPlotsInDataBox = true; // Initialize variables upper = new Fractal(); lower = new Fractal(); // Add plots for breakouts AddPlot(Brushes.Green, "Resistance Breakout"); AddPlot(Brushes.Red, "Support Breakout"); AddPlot(Brushes.Transparent, "Direction"); // Set default values Length = 5; ShowBull = true; BullColor = Brushes.Green; ShowBear = true; BearColor = Brushes.Red; ShowSupport = false; SupportColor = Brushes.Green; ShowResistance = false; ResistanceColor = Brushes.Red; ShowDash = false; DashLoc = "Top Right"; TextSize = "Small"; } else if (State == State.DataLoaded) { PrintSettings(); p = Length / 2; } else if (State == State.Configure) { } } protected override void OnBarUpdate() { if (CurrentBar < Math.Max(Length, BarsRequiredToPlot)) return; Direction[0] = Direction[1]; // repeat last Direction int n = CurrentBar; // Calculate fractal detection sums double dh = 0; double dl = 0; for (int i = 0; i < p; i++) { dh += Math.Sign(High[i] - High[i + 1]); dl += Math.Sign(Low[i] - Low[i + 1]); } double dhPrevious = 0; double dlPrevious = 0; if (CurrentBar >= p) { for (int i = p; i < 2 * p; i++) { dhPrevious += Math.Sign(High[i] - High[i + 1]); dlPrevious += Math.Sign(Low[i] - Low[i + 1]); } } // Check for bullish fractal bool bullf = false; if (CurrentBar >= Length - 1) { double highest = High[p]; for (int i = 0; i < Length; i++) { if (High[i] > highest) { highest = High[i]; break; } } bullf = (dh == -p && dhPrevious == p && High[p] == highest); } // Check for bearish fractal bool bearf = false; if (CurrentBar >= Length - 1) { double lowest = Low[p]; for (int i = 0; i < Length; i++) { if (Low[i] < lowest) { lowest = Low[i]; break; } } bearf = (dl == p && dlPrevious == -p && Low[p] == lowest); } if (bullf) { bullfCount++; upper.Value = High[p]; upper.Location = n - p; upper.IsCrossed = false; } if (bearf) { bearfCount++; lower.Value = Low[p]; lower.Location = n - p; lower.IsCrossed = false; } // Bullish market structure if (Close[0] > upper.Value && !upper.IsCrossed && upper.Value > 0) { if (ShowBull) { Draw.Line(this, "BullLine" + CurrentBar, CurrentBar - upper.Location, upper.Value, 0, upper.Value, BullColor); string labelText = (os == -1) ? "ChoCH" : "BOS"; Draw.Text(this, "BullLabel" + CurrentBar, labelText, 0, upper.Value + TickSize * 5, BullColor); } // Set support level if (ShowSupport) { int k = 1; double min = Low[1]; for (int i = 2; i < (n - upper.Location); i++) { if (Low[i] < min) { min = Low[i]; k = i; } } supportLevel = min; supportActive = true; brokenSup = false; Draw.Line(this, "SupportLine" + CurrentBar, IsAutoScale, k, min, 0, min, SupportColor, DashStyleHelper.Dash, 1); } upper.IsCrossed = true; bullMsCount++; os = 1; Direction[0] = 1; // Set last direction } // Check support break if (supportActive && !brokenSup && Close[0] < supportLevel) { brokenSup = true; Values[1][0] = High[0]; // Support breakout plot } else { Values[1][0] = double.NaN; } // Bearish market structure if (Close[0] < lower.Value && !lower.IsCrossed && lower.Value > 0) { if (ShowBear) { Draw.Line(this, "BearLine" + CurrentBar, CurrentBar - lower.Location, lower.Value, 0, lower.Value, BearColor); string labelText = (os == 1) ? "ChoCH" : "BOS"; Draw.Text(this, "BearLabel" + CurrentBar, labelText, 0, lower.Value - TickSize * 5, BearColor); } // Set resistance level if (ShowResistance) { int k = 1; double max = High[1]; for (int i = 2; i < (n - lower.Location); i++) { if (High[i] > max) { max = High[i]; k = i; } } resistanceLevel = max; resistanceActive = true; brokenRes = false; Draw.Line(this, "ResistanceLine" + CurrentBar, IsAutoScale, k, max, 0, max, ResistanceColor, DashStyleHelper.Dash, 1); } lower.IsCrossed = true; bearMsCount++; os = -1; Direction[0] = -1; } // Check resistance break if (resistanceActive && !brokenRes && Close[0] > resistanceLevel) { brokenRes = true; Values[0][0] = Low[0]; // Resistance breakout plot } else { Values[0][0] = double.NaN; } } #region Properties [Browsable(false)] [XmlIgnore] public Series Direction { get { return Values[2]; } } [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] [Range(3, int.MaxValue)] [Display(Name = "Length", Description = "Fractal detection length", Order = 1, GroupName = "Parameters")] public int Length { get; set; } [NinjaScriptProperty] [Display(Name = "Show Bullish Structures", Description = "", Order = 2, GroupName = "Style")] public bool ShowBull { get; set; } [NinjaScriptProperty] [XmlIgnore] [Display(Name = "Bullish Color", Description = "", Order = 3, GroupName = "Style")] public Brush BullColor { get; set; } [Browsable(false)] public string BullColorSerializable { get { return Serialize.BrushToString(BullColor); } set { BullColor = Serialize.StringToBrush(value); } } [NinjaScriptProperty] [Display(Name = "Show Bearish Structures", Description = "", Order = 4, GroupName = "Style")] public bool ShowBear { get; set; } [NinjaScriptProperty] [XmlIgnore] [Display(Name = "Bearish Color", Description = "", Order = 5, GroupName = "Style")] public Brush BearColor { get; set; } [Browsable(false)] public string BearColorSerializable { get { return Serialize.BrushToString(BearColor); } set { BearColor = Serialize.StringToBrush(value); } } [NinjaScriptProperty] [Display(Name = "Show Support", Description = "", Order = 6, GroupName = "Style")] public bool ShowSupport { get; set; } [NinjaScriptProperty] [XmlIgnore] [Display(Name = "Support Color", Description = "", Order = 7, GroupName = "Style")] public Brush SupportColor { get; set; } [Browsable(false)] public string SupportColorSerializable { get { return Serialize.BrushToString(SupportColor); } set { SupportColor = Serialize.StringToBrush(value); } } [NinjaScriptProperty] [Display(Name = "Show Resistance", Description = "", Order = 8, GroupName = "Style")] public bool ShowResistance { get; set; } [NinjaScriptProperty] [XmlIgnore] [Display(Name = "Resistance Color", Description = "", Order = 9, GroupName = "Style")] public Brush ResistanceColor { get; set; } [Browsable(false)] public string ResistanceColorSerializable { get { return Serialize.BrushToString(ResistanceColor); } set { ResistanceColor = Serialize.StringToBrush(value); } } [NinjaScriptProperty] [Display(Name = "Show Dashboard", Description = "", Order = 10, GroupName = "Dashboard")] public bool ShowDash { get; set; } [NinjaScriptProperty] [Display(Name = "Dashboard Location", Description = "", Order = 11, GroupName = "Dashboard")] public string DashLoc { get; set; } [NinjaScriptProperty] [Display(Name = "Text Size", Description = "", Order = 12, GroupName = "Dashboard")] public string TextSize { get; set; } #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("Parameters - Length: " + Length); PM("Style - Show Bullish Structures: " + ShowBull); PM("Style - Bullish Color: " + GetBrushName(BullColor)); PM("Style - Show Bearish Structures: " + ShowBear); PM("Style - Bearish Color: " + GetBrushName(BearColor)); PM("Style - Show Support: " + ShowSupport); PM("Style - Support Color: " + GetBrushName(SupportColor)); PM("Style - Show Resistance: " + ShowResistance); PM("Style - Resistance Color: " + GetBrushName(ResistanceColor)); PM("Dashboard - Show Dashboard: " + ShowDash); PM("Dashboard - Dashboard Location: " + DashLoc); PM("Dashboard - Text Size: " + TextSize); 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 LuxAlgoMarketStructureFractal[] cacheLuxAlgoMarketStructureFractal; public LuxAlgoMarketStructureFractal LuxAlgoMarketStructureFractal(bool showOutput, string versionInfo, int length, bool showBull, Brush bullColor, bool showBear, Brush bearColor, bool showSupport, Brush supportColor, bool showResistance, Brush resistanceColor, bool showDash, string dashLoc, string textSize) { return LuxAlgoMarketStructureFractal(Input, showOutput, versionInfo, length, showBull, bullColor, showBear, bearColor, showSupport, supportColor, showResistance, resistanceColor, showDash, dashLoc, textSize); } public LuxAlgoMarketStructureFractal LuxAlgoMarketStructureFractal(ISeries input, bool showOutput, string versionInfo, int length, bool showBull, Brush bullColor, bool showBear, Brush bearColor, bool showSupport, Brush supportColor, bool showResistance, Brush resistanceColor, bool showDash, string dashLoc, string textSize) { if (cacheLuxAlgoMarketStructureFractal != null) for (int idx = 0; idx < cacheLuxAlgoMarketStructureFractal.Length; idx++) if (cacheLuxAlgoMarketStructureFractal[idx] != null && cacheLuxAlgoMarketStructureFractal[idx].showOutput == showOutput && cacheLuxAlgoMarketStructureFractal[idx].versionInfo == versionInfo && cacheLuxAlgoMarketStructureFractal[idx].Length == length && cacheLuxAlgoMarketStructureFractal[idx].ShowBull == showBull && cacheLuxAlgoMarketStructureFractal[idx].BullColor == bullColor && cacheLuxAlgoMarketStructureFractal[idx].ShowBear == showBear && cacheLuxAlgoMarketStructureFractal[idx].BearColor == bearColor && cacheLuxAlgoMarketStructureFractal[idx].ShowSupport == showSupport && cacheLuxAlgoMarketStructureFractal[idx].SupportColor == supportColor && cacheLuxAlgoMarketStructureFractal[idx].ShowResistance == showResistance && cacheLuxAlgoMarketStructureFractal[idx].ResistanceColor == resistanceColor && cacheLuxAlgoMarketStructureFractal[idx].ShowDash == showDash && cacheLuxAlgoMarketStructureFractal[idx].DashLoc == dashLoc && cacheLuxAlgoMarketStructureFractal[idx].TextSize == textSize && cacheLuxAlgoMarketStructureFractal[idx].EqualsInput(input)) return cacheLuxAlgoMarketStructureFractal[idx]; return CacheIndicator(new LuxAlgoMarketStructureFractal(){ showOutput = showOutput, versionInfo = versionInfo, Length = length, ShowBull = showBull, BullColor = bullColor, ShowBear = showBear, BearColor = bearColor, ShowSupport = showSupport, SupportColor = supportColor, ShowResistance = showResistance, ResistanceColor = resistanceColor, ShowDash = showDash, DashLoc = dashLoc, TextSize = textSize }, input, ref cacheLuxAlgoMarketStructureFractal); } } } namespace NinjaTrader.NinjaScript.MarketAnalyzerColumns { public partial class MarketAnalyzerColumn : MarketAnalyzerColumnBase { public Indicators.LuxAlgoMarketStructureFractal LuxAlgoMarketStructureFractal(bool showOutput, string versionInfo, int length, bool showBull, Brush bullColor, bool showBear, Brush bearColor, bool showSupport, Brush supportColor, bool showResistance, Brush resistanceColor, bool showDash, string dashLoc, string textSize) { return indicator.LuxAlgoMarketStructureFractal(Input, showOutput, versionInfo, length, showBull, bullColor, showBear, bearColor, showSupport, supportColor, showResistance, resistanceColor, showDash, dashLoc, textSize); } public Indicators.LuxAlgoMarketStructureFractal LuxAlgoMarketStructureFractal(ISeries input , bool showOutput, string versionInfo, int length, bool showBull, Brush bullColor, bool showBear, Brush bearColor, bool showSupport, Brush supportColor, bool showResistance, Brush resistanceColor, bool showDash, string dashLoc, string textSize) { return indicator.LuxAlgoMarketStructureFractal(input, showOutput, versionInfo, length, showBull, bullColor, showBear, bearColor, showSupport, supportColor, showResistance, resistanceColor, showDash, dashLoc, textSize); } } } namespace NinjaTrader.NinjaScript.Strategies { public partial class Strategy : NinjaTrader.Gui.NinjaScript.StrategyRenderBase { public Indicators.LuxAlgoMarketStructureFractal LuxAlgoMarketStructureFractal(bool showOutput, string versionInfo, int length, bool showBull, Brush bullColor, bool showBear, Brush bearColor, bool showSupport, Brush supportColor, bool showResistance, Brush resistanceColor, bool showDash, string dashLoc, string textSize) { return indicator.LuxAlgoMarketStructureFractal(Input, showOutput, versionInfo, length, showBull, bullColor, showBear, bearColor, showSupport, supportColor, showResistance, resistanceColor, showDash, dashLoc, textSize); } public Indicators.LuxAlgoMarketStructureFractal LuxAlgoMarketStructureFractal(ISeries input , bool showOutput, string versionInfo, int length, bool showBull, Brush bullColor, bool showBear, Brush bearColor, bool showSupport, Brush supportColor, bool showResistance, Brush resistanceColor, bool showDash, string dashLoc, string textSize) { return indicator.LuxAlgoMarketStructureFractal(input, showOutput, versionInfo, length, showBull, bullColor, showBear, bearColor, showSupport, supportColor, showResistance, resistanceColor, showDash, dashLoc, textSize); } } } #endregion