This repository was archived by the owner on Sep 7, 2025. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 614
This repository was archived by the owner on Sep 7, 2025. It is now read-only.
ICC #513
Copy link
Copy link
Open
Description
#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
Labels
No labels