Code: Select all
using System;
using cAlgo.API;
using cAlgo.API.Internals;
using cAlgo.API.Indicators;
namespace cAlgo
{
[Indicator(IsOverlay = true, TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
public class LiquiditySweep : Indicator
{
[Parameter("Pivot Left Bars", Group = "Structure", DefaultValue = 5, MinValue = 1)]
public int LeftBars { get; set; }
[Parameter("Pivot Right Bars", Group = "Structure", DefaultValue = 5, MinValue = 1)]
public int RightBars { get; set; }
// State variables to track the most recent structural liquidity pools
private double _lastHigh = double.NaN;
private double _lastLow = double.NaN;
private int _lastHighIndex = -1;
private int _lastLowIndex = -1;
protected override void Initialize()
{
// Initialization logic (chart is clean by default)
}
public override void Calculate(int index)
{
// Ensure enough data is present to calculate lookbacks
if (index < LeftBars + RightBars)
return;
// 1. Identify structural pivots historically at the (index - RightBars) shift
int shiftIndex = index - RightBars;
EvaluatePivot(shiftIndex);
// 2. Sweep Logic evaluating on the current forming candle
double currentHigh = Bars.High[index];
double currentLow = Bars.Low[index];
double currentClose = Bars.Close[index];
// Calculate a dynamic gap to keep arrows from overlapping the candlestick wick
double atrOffset = (currentHigh - currentLow) * 0.2;
// Bearish Sweep: Grab liquidity above the structural high, close below
string bearSweepName = "BearSweep_" + index;
if (!double.IsNaN(_lastHigh) && currentHigh > _lastHigh && currentClose < _lastHigh)
{
Chart.DrawIcon(bearSweepName, ChartIconType.DownArrow, index, currentHigh + atrOffset, Color.Red);
}
else
{
// If conditions invalidate mid-tick (e.g., candle closes above the level), erase the arrow
Chart.RemoveObject(bearSweepName);
}
// Bullish Sweep: Grab liquidity below the structural low, close above
string bullSweepName = "BullSweep_" + index;
if (!double.IsNaN(_lastLow) && currentLow < _lastLow && currentClose > _lastLow)
{
Chart.DrawIcon(bullSweepName, ChartIconType.UpArrow, index, currentLow - atrOffset, Color.LimeGreen);
}
else
{
// Erase mid-tick if invalidated
Chart.RemoveObject(bullSweepName);
}
// 3. Extend projection dashed lines dynamically to the current bar
if (!double.IsNaN(_lastHigh))
{
Chart.DrawTrendLine("LiqHighLine", _lastHighIndex, _lastHigh, index, _lastHigh, Color.Red, 1, LineStyle.Lines);
}
if (!double.IsNaN(_lastLow))
{
Chart.DrawTrendLine("LiqLowLine", _lastLowIndex, _lastLow, index, _lastLow, Color.LimeGreen, 1, LineStyle.Lines);
}
}
private void EvaluatePivot(int shiftIndex)
{
bool isHigh = true;
bool isLow = true;
double shiftHigh = Bars.High[shiftIndex];
double shiftLow = Bars.Low[shiftIndex];
// Check Left Side
for (int i = 1; i <= LeftBars; i++)
{
if (Bars.High[shiftIndex - i] > shiftHigh) isHigh = false;
if (Bars.Low[shiftIndex - i] < shiftLow) isLow = false;
}
// Check Right Side
for (int i = 1; i <= RightBars; i++)
{
if (Bars.High[shiftIndex + i] >= shiftHigh) isHigh = false;
if (Bars.Low[shiftIndex + i] <= shiftLow) isLow = false;
}
// Register Swing High
if (isHigh)
{
_lastHigh = shiftHigh;
_lastHighIndex = shiftIndex;
Chart.DrawIcon("PivotHigh_" + shiftIndex, ChartIconType.DownTriangle, shiftIndex, shiftHigh, Color.Gray);
}
// Register Swing Low
if (isLow)
{
_lastLow = shiftLow;
_lastLowIndex = shiftIndex;
Chart.DrawIcon("PivotLow_" + shiftIndex, ChartIconType.UpTriangle, shiftIndex, shiftLow, Color.Gray);
}
}
}
}