Skip to content
This repository was archived by the owner on Sep 7, 2025. It is now read-only.
This repository was archived by the owner on Sep 7, 2025. It is now read-only.

ICC #513

@rjspratt2

Description

@rjspratt2

#region Using declarations
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Windows.Media;
using System.Xml.Serialization;
using NinjaTrader.Cbi;
using NinjaTrader.Core.FloatingPoint;
using NinjaTrader.Data;
using NinjaTrader.Gui;
using NinjaTrader.Gui.Chart;
using NinjaTrader.Gui.NinjaScript;
using NinjaTrader.Gui.Tools;
using NinjaTrader.NinjaScript.DrawingTools;
using NinjaTrader.NinjaScript.Indicators;
#endregion

namespace NinjaTrader.NinjaScript.Strategies
{
[DisplayName("ICCStrategy")]
public class ICCStrategy : Strategy
{
public enum PerformanceMode { HighDetail_OnPriceChange, Standard_OnBarClose }

    #region Private State Variables
    private IndicatorState currentState = IndicatorState.Searching;
    private MarketRegime   marketRegime = MarketRegime.Neutral;
    private TradePhase     phase        = TradePhase.Idle;
    private enum IndicatorState { Searching, AwaitingReversal, AwaitingContinuation }
    private enum MarketRegime   { Bullish, Bearish, Neutral }
    private enum TradePhase     { Idle, CorrArmed, CorrFired, Pause, ContArmed, ContFired, Cooldown }
    private bool hasFiredThisBar = false;
    private int  lastSignalBar   = -1;
    private int  barsSinceSignal = 999;
    private int  lastIndicationDirection = 0;
    private double orderBlockHigh, orderBlockLow;
    private bool   use50PctOrderBlock = false;
    private int    dynamicSwingStrength = 10;
    private double dynamicContinuationMinScore = 10;
    private double dynamicReversalMinScore     = 10;
    private int phaseStartBar = 0;
    private int pauseBars = 3;
    private int coolDownBars = 10;
    private const int    WeightVWAP  = 3;
    private const int    WeightHVN   = 2;
    private const int    WeightDelta = 2;
    private const int    WeightSweep = 4;
    private const int    HVNLookback         = 120;
    private const int    HVNProximityTicks = 4;
    private const int    DeltaLookback       = 30;
    private const double DeltaStrengthMult = 1.2;
    private const int    SweepLookback       = 12;
    private double vwapCumPV = 0.0;
    private double vwapCumV  = 0.0;
    private double highestSinceEntry = double.MinValue;
    private double lowestSinceEntry  = double.MaxValue;
	private Order stopLossOrder = null;
	private bool isStopManagedManually = false;
	private bool isBreakevenStopSet = false;
    #endregion

    #region Private Indicator Instances
    private Swing swing240m, swing60m, swingPrimary;
    private ATR   atrPrimary, atr60;
    private SMA   atrSma;
    private RSI   rsiPrimary;
    private EMA   emaDaily;
    private Series<double> dailyBias, macroTrend, primaryTrend;
    private Series<double> sessionVwap;
    private Series<double> hvnPrice;
    private Series<double> barDelta;
    #endregion

    #region OnStateChange
    protected override void OnStateChange()
    {
        if (State == State.SetDefaults)
        {
            Name = "ICCStrategy";
            Description = "A self-contained strategy for the ICC logic with advanced trade management.";
            Calculate = Calculate.OnBarClose;
            EntriesPerDirection = 1;
            EntryHandling = EntryHandling.AllEntries;
            IsExitOnSessionCloseStrategy = true;
            ExitOnSessionCloseSeconds = 30;
            IsFillLimitOnTouch = false;
            MaximumBarsLookBack = MaximumBarsLookBack.TwoHundredFiftySix;
            OrderFillResolution = OrderFillResolution.Standard;
            Slippage = 0;
            StartBehavior = StartBehavior.WaitUntilFlat;
            TimeInForce = TimeInForce.Gtc;
            TraceOrders = true; 
            RealtimeErrorHandling = RealtimeErrorHandling.StopCancelClose;
            StopTargetHandling = StopTargetHandling.PerEntryExecution;
            BarsRequiredToTrade = 200;
            IsUnmanaged = false;

            RiskAmountInDollars = 100;
            UseTrailingStopOnly = true;
            TrailStopAtrBuffer = 1.0;
			AllowManualStopManagement = true;
            MinScore_WithVWAP = 7;
            MinScore_AgainstVWAP = 10;
			BreakevenTriggerTicks = 15;
			BreakevenPlusTicks = 2;
            EnableNewsFilter = true;
            News1_StartTime = 073000;
            News1_EndTime   = 074500;
            News2_StartTime = 130000;
            News2_EndTime   = 131500;
            IndicatorPerformanceMode = PerformanceMode.HighDetail_OnPriceChange;
            ManageDataSeries = true;
            EnableSessionFilter = true;
            NY_StartTime = 083000; NY_EndTime  = 150000;
            LON_StartTime = 020000; LON_EndTime = 110000;
            Asia_StartTime = 180000; Asia_EndTime = 030000;
            ShowDashboard = true;
            DashboardPosition = TextPosition.TopRight;
            DashboardColor = Brushes.Gold;
            PrintHeartbeat = true;
        }
        else if (State == State.Configure)
        {
            Calculate = (IndicatorPerformanceMode == PerformanceMode.HighDetail_OnPriceChange) ? Calculate.OnEachTick : Calculate.OnBarClose;
            if (ManageDataSeries) { AddDataSeries(BarsPeriodType.Day, 1); AddDataSeries(BarsPeriodType.Minute, 240); AddDataSeries(BarsPeriodType.Minute, 60); }
        }
        else if (State == State.DataLoaded)
        {
            dailyBias  = new Series<double>(this);
            macroTrend = new Series<double>(this);
            primaryTrend = new Series<double>(this);
            atrPrimary = ATR(14);
            atrSma     = SMA(ATR(14), 50);
            rsiPrimary = RSI(14, 3);
            if (BarsArray.Length >= 4)
            {
                emaDaily  = EMA(BarsArray[1], 200);
                swing240m = Swing(BarsArray[2], 10);
                swing60m  = Swing(BarsArray[3], 10);
                atr60     = ATR(BarsArray[3], 14);
            }
            swingPrimary = Swing(Input, 5);
            sessionVwap = new Series<double>(this);
            hvnPrice    = new Series<double>(this);
            barDelta    = new Series<double>(this);
        }
    }
    #endregion

    #region Event Handlers
    protected override void OnBarUpdate()
    {
        if (CurrentBar < BarsRequiredToTrade || (ManageDataSeries && BarsArray.Length < 4)) return;
        if (Bars.IsFirstBarOfSession) { vwapCumPV = 0; vwapCumV  = 0; }
        hasFiredThisBar = (Calculate == Calculate.OnEachTick) ? (IsFirstTickOfBar ? false : hasFiredThisBar) : false;
        double typical = (High[0] + Low[0] + Close[0]) / 3.0;
        double vol = Volume[0] == 0 ? 1 : Volume[0];
        vwapCumPV += typical * vol; vwapCumV  += vol;
        sessionVwap[0] = vwapCumV > 0 ? (vwapCumPV / vwapCumV) : (CurrentBar > 0 ? sessionVwap[1] : Close[0]);
        barDelta[0] = Close[0] > Open[0] ? Volume[0] : (Close[0] < Open[0] ? -Volume[0] : 0);
        if (IsFirstTickOfBar) { hvnPrice[0] = ComputeHVNPrice(Math.Min(HVNLookback, CurrentBar + 1)); }
        UpdateAdaptiveParameters(); UpdateContext();
        
        DrawDashboardOnce(); DrawZones();

        if (Position.MarketPosition != MarketPosition.Flat)
        {
            MaintainTrailing();
            return;
        }
        
        if (Position.MarketPosition == MarketPosition.Flat && IsOkToTrade() && IsInTradingSession())
        {
            barsSinceSignal++;
            switch (currentState)
            {
                case IndicatorState.Searching:            ManageState_Searching();            break;
                case IndicatorState.AwaitingReversal:     ManageState_AwaitingReversal();     break;
                case IndicatorState.AwaitingContinuation: ManageState_AwaitingContinuation(); break;
            }
            ManagePhaseTransitions();
        }
        else if(currentState != IndicatorState.Searching) { ResetState(); }

        if (PrintHeartbeat && (IsFirstTickOfBar || Calculate == Calculate.OnBarClose))
            Print($"{Instrument} | {Time[0]} | State:{currentState} Phase:{phase} Regime:{marketRegime} Daily:{(dailyBias.Count > 0 ? dailyBias[0]:0)} Macro:{(macroTrend.Count > 0 ? macroTrend[0]:0)} Primary:{(primaryTrend.Count > 0 ? primaryTrend[0]:0)}");
    }

    protected override void OnExecutionUpdate(Cbi.Execution execution, string executionId, double price, int quantity, Cbi.MarketPosition marketPosition, string orderId, DateTime time)
    {
        if (execution.IsEntry)
        {
            highestSinceEntry = execution.Price;
            lowestSinceEntry = execution.Price;
			isStopManagedManually = false;
			isBreakevenStopSet = false;
			stopLossOrder = null;
        }
    }
	
	protected override void OnOrderUpdate(Order order)
	{
		if (order.Name == "Stop loss")
		{
			if (order.OrderState == OrderState.Working)
				stopLossOrder = order;
			else if (order.OrderState == OrderState.Cancelled || order.OrderState == OrderState.Filled)
				stopLossOrder = null;
			
			if(AllowManualStopManagement && order.IsChange)
			{
				isStopManagedManually = true;
				if(PrintHeartbeat) Print($"Manual Stop Detected for {order.Name}. Strategy will disengage automated trailing.");
			}
		}
	}
    #endregion
    
    #region Logic, State, and Trade Execution
    private void ManageState_Searching() { if (HasBars(3, 2) && swing60m != null && swing60m.Count > 1 && barsSinceSignal > 12) { bool bullBreak = Closes[3][0] > swing60m.SwingHigh[1]; bool bearBreak = Closes[3][0] < swing60m.SwingLow[1]; if (bullBreak && marketRegime == MarketRegime.Bullish) { ProcessIndication(true);  phase = TradePhase.CorrArmed; phaseStartBar = CurrentBar; } else if (bearBreak && marketRegime == MarketRegime.Bearish) { ProcessIndication(false); phase = TradePhase.CorrArmed; phaseStartBar = CurrentBar; } } }
    private void ManageState_AwaitingReversal() { int score = CalculateReversalScore(); if (score >= dynamicReversalMinScore) { FireTrade(lastIndicationDirection == -1, true, "ICC-CORR"); } if (IsPullbackToContinuationZone()) currentState = IndicatorState.AwaitingContinuation; }
    private void ManageState_AwaitingContinuation() { if (IsPullbackToContinuationZone()) { int contScore = CalculateContinuationScore(); if (contScore >= dynamicContinuationMinScore) { FireTrade(lastIndicationDirection == 1, false, "ICC-CONT"); } } }
    
    private void FireTrade(bool isLong, bool isReversal, string signalType)
    {
        if (hasFiredThisBar || lastSignalBar == CurrentBar) return;
        bool isWithVWAP = true; if (sessionVwap != null && sessionVwap.Count > 0) { isWithVWAP = isLong ? (Close[0] >= sessionVwap[0]) : (Close[0] <= sessionVwap[0]); }
        int requiredScore = isWithVWAP ? MinScore_WithVWAP : MinScore_AgainstVWAP;
        string reasons; int confidenceScore = ComputeConfidenceScore(isLong, out reasons);
        if (confidenceScore < requiredScore) { Print($"FILTERED [{signalType}] score={confidenceScore}/{requiredScore} (VWAP aligned: {isWithVWAP}) | {reasons}"); return; }
        Print($"CONFIRM [{signalType}] score={confidenceScore}/{requiredScore} (VWAP aligned: {isWithVWAP}) | {reasons}");
        
        double sl = isLong ? (Low[0] - (atrPrimary[0] * TrailStopAtrBuffer)) : (High[0] + (atrPrimary[0] * TrailStopAtrBuffer));
        double entryPrice = isLong ? GetCurrentAsk() : GetCurrentBid();
		double riskPerContract = Math.Abs(entryPrice - sl) * Instrument.MasterInstrument.PointValue;
		if(riskPerContract <= 0) return;
		int quantity = (int)Math.Max(1, Math.Floor(RiskAmountInDollars / riskPerContract));
		
        double riskInDollars = riskPerContract * quantity;
        Print($"TRADE EXECUTION: {(isLong ? "Long" : "Short")} on {Instrument.MasterInstrument.Name}. {quantity} contract(s). Initial risk: {riskInDollars:C}");
        
        if (isLong) { SetStopLoss(CalculationMode.Price, sl); if (!UseTrailingStopOnly) { int barsAgo = (swing60m != null) ? swing60m.SwingHighBar(0, 1, CurrentBars[3]) : -1; double tp = (barsAgo > 0 && barsAgo < BarsArray[3].Count) ? Highs[3][barsAgo] : entryPrice + ((entryPrice - sl) * 2.0); SetProfitTarget(CalculationMode.Price, tp); } EnterLong(quantity); }
        else { SetStopLoss(CalculationMode.Price, sl); if (!UseTrailingStopOnly) { int barsAgo = (swing60m != null) ? swing60m.SwingLowBar(0, 1, CurrentBars[3]) : -1; double tp = (barsAgo > 0 && barsAgo < BarsArray[3].Count) ? Lows[3][barsAgo] : entryPrice - ((sl - entryPrice) * 2.0); SetProfitTarget(CalculationMode.Price, tp); } EnterShort(quantity); }
        lastSignalBar = CurrentBar; hasFiredThisBar = true;
        if (isReversal) { phase = TradePhase.CorrFired; phaseStartBar = CurrentBar; } else { phase = TradePhase.ContFired; phaseStartBar = CurrentBar; }
    }

    private void MaintainTrailing()
    {
		if (isStopManagedManually || stopLossOrder == null || stopLossOrder.OrderState != OrderState.Working) return;

		if (!isBreakevenStopSet)
		{
			double breakevenPrice = 0;
			bool triggerMet = false;
			if (Position.MarketPosition == MarketPosition.Long)
			{
				if ((High[0] - Position.AveragePrice) >= (BreakevenTriggerTicks * TickSize))
				{
					breakevenPrice = Position.AveragePrice + (BreakevenPlusTicks * TickSize);
					if (breakevenPrice > stopLossOrder.StopPrice)
						triggerMet = true;
				}
			}
			else if (Position.MarketPosition == MarketPosition.Short)
			{
				if ((Position.AveragePrice - Low[0]) >= (BreakevenTriggerTicks * TickSize))
				{
					breakevenPrice = Position.AveragePrice - (BreakevenPlusTicks * TickSize);
					if (breakevenPrice < stopLossOrder.StopPrice)
						triggerMet = true;
				}
			}

			if (triggerMet)
			{
				ChangeOrder(stopLossOrder, stopLossOrder.Quantity, 0, breakevenPrice);
				isBreakevenStopSet = true;
				return;
			}
		}

		if (isBreakevenStopSet)
		{
			double newTrailPrice = 0;
			if (Position.MarketPosition == MarketPosition.Long)
			{
				highestSinceEntry = Math.Max(highestSinceEntry, High[0]);
				newTrailPrice = highestSinceEntry - (atrPrimary[0] * TrailStopAtrBuffer);
				if (newTrailPrice > stopLossOrder.StopPrice)
				{
					ChangeOrder(stopLossOrder, stopLossOrder.Quantity, 0, newTrailPrice);
				}
			}
			else if (Position.MarketPosition == MarketPosition.Short)
			{
				lowestSinceEntry = Math.Min(lowestSinceEntry, Low[0]);
				newTrailPrice = lowestSinceEntry + (atrPrimary[0] * TrailStopAtrBuffer);
				if (newTrailPrice < stopLossOrder.StopPrice)
				{
					ChangeOrder(stopLossOrder, stopLossOrder.Quantity, 0, newTrailPrice);
				}
			}
		}
    }
    #endregion

    #region Properties (User Inputs)
	[NinjaScriptProperty, Range(1, 10000), Display(Name="Risk Amount ($)", GroupName="1. Trade Management", Order=0)] public double RiskAmountInDollars { get; set; }
    [NinjaScriptProperty, Display(Name="Use Trailing Stop Only (No TP)", GroupName="1. Trade Management", Order=1)] public bool UseTrailingStopOnly { get; set; }
    [NinjaScriptProperty, Range(0.1, 10), Display(Name="Trail Stop Buffer (x ATR)", GroupName="1. Trade Management", Order=2)] public double TrailStopAtrBuffer { get; set; }
	[NinjaScriptProperty, Display(Name="Allow Manual Stop Management", GroupName="1. Trade Management", Order=3)] public bool AllowManualStopManagement { get; set; }
    [NinjaScriptProperty, Range(1, 20), Display(Name="Min Score with VWAP", GroupName="1. Trade Management", Order=4)] public int MinScore_WithVWAP { get; set; }
    [NinjaScriptProperty, Range(1, 20), Display(Name="Min Score against VWAP", GroupName="1. Trade Management", Order=5)] public int MinScore_AgainstVWAP { get; set; }
	[NinjaScriptProperty, Range(0, 100), Display(Name="Breakeven Trigger (Ticks)", GroupName="1. Trade Management", Order=6)] public int BreakevenTriggerTicks { get; set; }
	[NinjaScriptProperty, Range(0, 100), Display(Name="Breakeven Plus (Ticks)", GroupName="1. Trade Management", Order=7)] public int BreakevenPlusTicks { get; set; }
	[NinjaScriptProperty, Display(Name="Enable News Filter", GroupName="2. News Filter", Order=0)] public bool EnableNewsFilter { get; set; }
    [NinjaScriptProperty, Display(Name="News 1 - Start Time (HHMMSS)", GroupName="2. News Filter", Order=1)] public int News1_StartTime { get; set; }
    [NinjaScriptProperty, Display(Name="News 1 - End Time (HHMMSS)", GroupName="2. News Filter", Order=2)] public int News1_EndTime { get; set; }
    [NinjaScriptProperty, Display(Name="News 2 - Start Time (HHMMSS)", GroupName="2. News Filter", Order=3)] public int News2_StartTime { get; set; }
    [NinjaScriptProperty, Display(Name="News 2 - End Time (HHMMSS)", GroupName="2. News Filter", Order=4)] public int News2_EndTime { get; set; }
    [NinjaScriptProperty, Display(Name="Indicator Performance Mode", GroupName="3. Indicator Settings", Order=0)] public PerformanceMode IndicatorPerformanceMode { get; set; }
    [NinjaScriptProperty, Display(Name="Auto-add higher TF series", GroupName="3. Indicator Settings", Order=1)] public bool ManageDataSeries { get; set; }
    [NinjaScriptProperty, Display(Name="Enable Session Filter", GroupName="4. Sessions", Order=10)] public bool EnableSessionFilter { get; set; }
    [NinjaScriptProperty, Display(Name="NY Start (HHMMSS)", GroupName="4. Sessions", Order=11)] public int NY_StartTime { get; set; }
    [NinjaScriptProperty, Display(Name="NY End (HHMMSS)", GroupName="4. Sessions", Order=12)] public int NY_EndTime { get; set; }
    [NinjaScriptProperty, Display(Name="London Start (HHMMSS)", GroupName="4. Sessions", Order=13)] public int LON_StartTime { get; set; }
    [NinjaScriptProperty, Display(Name="London End (HHMMSS)", GroupName="4. Sessions", Order=14)] public int LON_EndTime { get; set; }
    [NinjaScriptProperty, Display(Name="Asia Start (HHMMSS)", GroupName="4. Sessions", Order=15)] public int Asia_StartTime { get; set; }
    [NinjaScriptProperty, Display(Name="Asia End (HHMMSS)", GroupName="4. Sessions", Order=16)] public int Asia_EndTime { get; set; }
    [NinjaScriptProperty, Display(Name="Show Dashboard", GroupName="5. UI", Order=30)] public bool ShowDashboard { get; set; }
    [NinjaScriptProperty, Display(Name="Dashboard Position", GroupName="5. UI", Order=31)] public TextPosition DashboardPosition { get; set; }
    private Brush _dashboardColor = Brushes.Gold;
    [NinjaScriptProperty, XmlIgnore, Display(Name="Dashboard Color", GroupName="5. UI", Order=32)] public Brush DashboardColor { get => _dashboardColor; set { _dashboardColor = value ?? Brushes.Gold; if (_dashboardColor.CanFreeze && !_dashboardColor.IsFrozen) { try { _dashboardColor.Freeze(); } catch { } } } }
    [Browsable(false)] public string DashboardColorSerializable { get { return Serialize.BrushToString(DashboardColor); } set { DashboardColor = Serialize.StringToBrush(value); } }
    [NinjaScriptProperty, Display(Name="Heartbeat to Output", GroupName="6. Debug", Order=40)] public bool PrintHeartbeat { get; set; }
    #endregion

    #region Helper Methods
    private bool IsOkToTrade() { if (!EnableNewsFilter) return true; int now = ToTime(Time[0]); if (now >= News1_StartTime && now <= News1_EndTime) return false; if (now >= News2_StartTime && now <= News2_EndTime) return false; return true; }
    private bool HasBars(int seriesIndex, int barsRequired = 1) => BarsArray != null && BarsArray.Length > seriesIndex && BarsArray[seriesIndex].Count > barsRequired;
    private void ManagePhaseTransitions() { if (phase == TradePhase.CorrFired && CurrentBar >= phaseStartBar + pauseBars) phase = TradePhase.ContArmed; if (phase == TradePhase.ContFired && CurrentBar >= phaseStartBar + coolDownBars) { ResetState(); } }
    private int CalculateContinuationScore() { int score = 0; if (dailyBias.Count > 0 && dailyBias[0] == lastIndicationDirection) score += 4; if (macroTrend.Count > 0 && macroTrend[0] == lastIndicationDirection) score += 4; if (primaryTrend.Count > 0 && primaryTrend[0] == lastIndicationDirection) score += 3; if (IsPinBar(0, lastIndicationDirection == 1) || IsEngulfing(0, lastIndicationDirection == 1)) { score += 5; } return score; }
    private int CalculateReversalScore() { int score = 0; bool bull = (lastIndicationDirection == 1); if (swingPrimary == null || swingPrimary.Count < 2 || rsiPrimary == null || rsiPrimary.Count < 2) return 0; if (bull) { int sh = swingPrimary.SwingHighBar(0, 1, Bars.Count); if (sh > 0 && sh < Bars.Count && High[0] > swingPrimary.SwingHigh[0] && rsiPrimary[0] < rsiPrimary[sh]) score += 6; if (Low[0] < swingPrimary.SwingLow[0]) score += 5; if (High[0] < swingPrimary.SwingHigh[0]) score += 5; if (IsEngulfing(0, false) || IsPinBar(0, false)) score += 4; } else { int sl = swingPrimary.SwingLowBar(0, 1, Bars.Count); if (sl > 0 && sl < Bars.Count && Low[0] < swingPrimary.SwingLow[0] && rsiPrimary[0] > rsiPrimary[sl]) score += 6; if (High[0] > swingPrimary.SwingHigh[0]) score += 5; if (Low[0] > swingPrimary.SwingLow[0]) score += 5; if (IsEngulfing(0, true) || IsPinBar(0, true)) score += 4; } return score; }
    private void UpdateAdaptiveParameters() { if (atrPrimary == null || atrSma == null) return; double cur = atrPrimary[0], avg = atrSma[0]; if (avg > 0) { if (cur > avg * 1.5) { dynamicSwingStrength = 15; dynamicContinuationMinScore = 12; } else if (cur < avg * 0.75) { dynamicSwingStrength = 7; dynamicContinuationMinScore = 8; } else { dynamicSwingStrength = 10; dynamicContinuationMinScore = 10; } } if (swing60m != null)  swing60m.Strength  = dynamicSwingStrength; if (swing240m != null) swing240m.Strength = dynamicSwingStrength; if (swingPrimary != null) swingPrimary.Strength = Math.Max(3, (int)(dynamicSwingStrength / 2.0)); if (HasBars(3, 2) && atr60 != null) { double range = Math.Abs(Highs[3][1] - Lows[3][1]); use50PctOrderBlock = (atr60.Count > 1 && atr60[1] > 0 && range > atr60[1] * 2.0); } }
    private void UpdateContext() { if (emaDaily != null && emaDaily.Count > 0) { if (Close[0] > emaDaily[0]) marketRegime = MarketRegime.Bullish; else if (Close[0] < emaDaily[0]) marketRegime = MarketRegime.Bearish; else marketRegime = MarketRegime.Neutral; } if (HasBars(1, 1)) dailyBias[0] = Close[0] > Opens[1][0] ? 1 : (Close[0] < Opens[1][0] ? -1 : 0); if (swing240m != null && swing240m.Count > 1) { if (swing240m.SwingHigh[0] > swing240m.SwingHigh[1] && swing240m.SwingLow[0] > swing240m.SwingLow[1]) macroTrend[0] = 1; else if (swing240m.SwingHigh[0] < swing240m.SwingHigh[1] && swing240m.SwingLow[0] < swing240m.SwingLow[1]) macroTrend[0] = -1; else macroTrend[0] = macroTrend.Count > 0 ? macroTrend[1] : 0; } if (swing60m != null && swing60m.Count > 1) { if (swing60m.SwingLow[0] > swing60m.SwingLow[1]) primaryTrend[0] = 1; else if (swing60m.SwingHigh[0] < swing60m.SwingHigh[1]) primaryTrend[0] = -1; else primaryTrend[0] = primaryTrend.Count > 0 ? primaryTrend[1] : 0; } }
    private bool IsPullbackToContinuationZone() => (orderBlockHigh > 0 && Low[0] <= orderBlockHigh && High[0] >= orderBlockLow);
    private bool IsInTradingSession() { if (!EnableSessionFilter) return true; int now = ToTime(Time[0]); if (now >= NY_StartTime  && now <= NY_EndTime)  return true; if (now >= LON_StartTime && now <= LON_EndTime) return true; if (Asia_StartTime > Asia_EndTime && (now >= Asia_StartTime || now <= Asia_EndTime)) return true; if (Asia_StartTime < Asia_EndTime && (now >= Asia_StartTime && now <= Asia_EndTime)) return true; return false; }
    private bool IsEngulfing(int b, bool isLong) { if (b + 1 >= Bars.Count) return false; return isLong ? (Close[b] > Open[b] && Close[b+1] < Open[b+1] && Close[b] > Open[b+1] && Open[b] < Close[b+1]) : (Close[b] < Open[b] && Close[b+1] > Open[b+1] && Close[b] < Open[b+1] && Open[b] > Close[b+1]); }
    private bool IsPinBar(int b, bool isLong) { double range = High[b] - Low[b]; if (range == 0) return false; double body  = Math.Abs(Open[b] - Close[b]); double lw = Math.Min(Open[b], Close[b]) - Low[b]; double uw = High[b] - Math.Max(Open[b], Close[b]); return isLong ? (lw > range * 0.6 && body < range * 0.3) : (uw > range * 0.6 && body < range * 0.3); }
    private void ProcessIndication(bool isBullish) { if (!HasBars(3, 2) || swing60m == null || swing60m.Count < 2) return; int barsAgo = isBullish ? swing60m.SwingLowBar(1, 1, CurrentBars[3]) : swing60m.SwingHighBar(1, 1, CurrentBars[3]); if (barsAgo > 0 && barsAgo < BarsArray[3].Count) { lastIndicationDirection = isBullish ? 1 : -1; if (use50PctOrderBlock) { double mid = Lows[3][barsAgo] + ((Highs[3][barsAgo] - Lows[3][barsAgo]) / 2.0); double pad = 2 * TickSize; orderBlockHigh = mid + pad; orderBlockLow  = mid - pad; } else { orderBlockHigh = Highs[3][barsAgo]; orderBlockLow  = Lows[3][barsAgo]; } currentState = IndicatorState.AwaitingReversal; } }
    private void ResetState() { barsSinceSignal = 0; lastIndicationDirection = 0; currentState = IndicatorState.Searching; phase = TradePhase.Idle; orderBlockHigh = 0; orderBlockLow = 0;}
    private void DrawDashboardOnce() { if (!ShowDashboard) return; string status = currentState switch { IndicatorState.AwaitingReversal => $"ICC: Await CORR @ zone [{orderBlockLow:F2}..{orderBlockHigh:F2}]", IndicatorState.AwaitingContinuation  => $"ICC: Await CONT @ zone [{orderBlockLow:F2}..{orderBlockHigh:F2}]", _ => "SEARCHING" }; string bias = dailyBias.Count > 0 ? (dailyBias[0] == 1 ? "Bullish" : (dailyBias[0] == -1 ? "Bearish" : "Neutral")) : "Neutral"; string macro = macroTrend.Count > 0 ? (macroTrend[0] == 1 ? "Uptrend" : (macroTrend[0] == -1 ? "Downtrend" : "Neutral")) : "Neutral"; string primary = primaryTrend.Count > 0 ? (primaryTrend[0] == 1 ? "Uptrend" : (primaryTrend[0] == -1 ? "Downtrend" : "Neutral")) : "Neutral"; string txt = $"Status: {status}\nPhase: {phase}\nRegime: {marketRegime}\nDaily Bias: {bias}\nMacro (4H): {macro}\nPrimary (1H): {primary}"; Draw.TextFixed(this, "ICCStrategy_Dashboard", txt, DashboardPosition, DashboardColor, new SimpleFont("Arial", 12), Brushes.Transparent, Brushes.Transparent, 0); }
    private void DrawZones() { if (currentState != IndicatorState.Searching) { Brush zoneBrush = lastIndicationDirection == 1 ? Brushes.DarkGreen : Brushes.DarkRed; Draw.Rectangle(this, "ICCStrategy_OB_ZONE", true, 0, orderBlockHigh, -50, orderBlockLow, Brushes.Transparent, zoneBrush, 10); } else { if(DrawObjects["ICCStrategy_OB_ZONE"] != null) RemoveDrawObject("ICCStrategy_OB_ZONE"); } }
    private int ComputeConfidenceScore(bool isLong, out string reasons) { int score = 0; var sb = new System.Text.StringBuilder(); if (sessionVwap != null && sessionVwap.Count > 0) { bool ok = isLong ? (Close[0] >= sessionVwap[0]) : (Close[0] <= sessionVwap[0]); if (ok) { score += WeightVWAP; sb.Append($"VWAP(+{WeightVWAP}) "); } else sb.Append("VWAP(0) "); } if (hvnPrice.Count > 0 && hvnPrice[0] > 0) { double proxTicks = Math.Abs(Close[0] - hvnPrice[0]) / TickSize; if (proxTicks <= HVNProximityTicks) { score += WeightHVN; sb.Append($"HVN(+{WeightHVN}) "); } else sb.Append("HVN(0) "); } double rolling = RollingSum(barDelta, DeltaLookback); double avgAbs = RollingMeanAbs(barDelta, DeltaLookback); bool strongUp = rolling > avgAbs * DeltaStrengthMult; bool strongDn = rolling < -avgAbs * DeltaStrengthMult; bool deltaOK = isLong ? strongUp : strongDn; if (deltaOK) { score += WeightDelta; sb.Append($"Delta(+{WeightDelta}) "); } else sb.Append("Delta(0) "); bool swept = isLong ? DidSweepLow(SweepLookback) : DidSweepHigh(SweepLookback); if (swept) { score += WeightSweep; sb.Append($"Sweep(+{WeightSweep}) "); } else sb.Append("Sweep(0) "); reasons = sb.ToString().Trim(); return score; }
    private double RollingSum(ISeries<double> s, int n) { double sum = 0; int max = Math.Min(n, CurrentBar + 1); for (int i = 0; i < max; i++) sum += s[i]; return sum; }
    private double RollingMeanAbs(ISeries<double> s, int n) { double sum = 0; int max = Math.Min(n, CurrentBar + 1); for (int i = 0; i < max; i++) sum += Math.Abs(s[i]); return max > 0 ? sum / max : 0.0; }
    private bool DidSweepHigh(int lookback) { if (CurrentBar < lookback + 1) return false; double recentHigh = MAX(High, lookback)[1]; bool tookHigh = High[0] > recentHigh; bool rejected = Close[0] < recentHigh; return tookHigh && rejected; }
    private bool DidSweepLow(int lookback) { if (CurrentBar < lookback + 1) return false; double recentLow = MIN(Low, lookback)[1]; bool tookLow = Low[0] < recentLow; bool rejected = Close[0] > recentLow; return tookLow && rejected; }
    private double ComputeHVNPrice(int lookback) { var map = new Dictionary<long, double>(); double ts = TickSize; int max = Math.Min(lookback, CurrentBar + 1); for (int i = 0; i < max; i++) { long key = (long)Math.Round(Close[i] / ts); double vol = Volume[i] == 0 ? 1 : Volume[i]; if (map.TryGetValue(key, out double v)) map[key] = v + vol; else map[key] = vol; } if (map.Count == 0) return 0; long bestKey = 0; double bestVol = double.MinValue; foreach (var kv in map) if (kv.Value > bestVol) { bestKey = kv.Key; bestVol = kv.Value; } return bestKey * ts; }
    #endregion
}

}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions