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

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions