Code: Select all
using System;
using System.Collections.Generic;
using System.Linq;
using cAlgo.API;
using cAlgo.API.Internals;
namespace cAlgo
{
[Indicator(IsOverlay = true, TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
public class AsianMicrostructureELITE : Indicator
{
// =========================================================================
// INPUTS & CONFIGURATION
// =========================================================================
[Parameter("Session Start (HH:mm)", DefaultValue = "00:00", Group = "Session Parameters")]
public string SessionStart { get; set; }
[Parameter("Session End (HH:mm)", DefaultValue = "08:00", Group = "Session Parameters")]
public string SessionEnd { get; set; }
[Parameter("London Projection Length (Hours)", DefaultValue = 6, Group = "Session Parameters")]
public int ExtendHours { get; set; }
[Parameter("Average Lookback (Days)", DefaultValue = 15, Group = "Expansion Logic")]
public int Lookback { get; set; }
[Parameter("Expansion Multiplier", DefaultValue = 1.5, Group = "Expansion Logic")]
public double Multiplier { get; set; }
[Parameter("Normal Color", DefaultValue = "#2962FF", Group = "Aesthetics")]
public string NormalColorHex { get; set; }
[Parameter("Expansion Color", DefaultValue = "#FF5252", Group = "Aesthetics")]
public string ExpandColorHex { get; set; }
[Parameter("Premium Color", DefaultValue = "#FF5252", Group = "Aesthetics")]
public string PremiumColorHex { get; set; }
[Parameter("Discount Color", DefaultValue = "#2962FF", Group = "Aesthetics")]
public string DiscountColorHex { get; set; }
[Parameter("Line Color", DefaultValue = "#787B86", Group = "Aesthetics")]
public string LineColorHex { get; set; }
[Parameter("Show Data Dashboard", DefaultValue = true, Group = "Aesthetics")]
public bool ShowHUD { get; set; }
// =========================================================================
// STATE VARIABLES
// =========================================================================
private TimeSpan _startTime;
private TimeSpan _endTime;
private List<double> _pastRanges;
private bool _inSession;
private bool _alertTriggered;
private bool _bslSwept;
private bool _sslSwept;
private double _sessionHigh;
private double _sessionLow;
private DateTime _sessionStartTime;
private DateTime _sessionEndTime;
private Color _normalColor;
private Color _expandColor;
private Color _premColor;
private Color _discColor;
private Color _lineColor;
// HUD UI Elements
private Border _hudContainer;
private TextBlock _txtCurrentRange;
private TextBlock _txtAvgRange;
private TextBlock _txtStatus;
private TextBlock _txtLiquidity;
protected override void Initialize()
{
TimeSpan.TryParse(SessionStart, out _startTime);
TimeSpan.TryParse(SessionEnd, out _endTime);
_pastRanges = new List<double>();
_normalColor = Color.FromHex(NormalColorHex);
_expandColor = Color.FromHex(ExpandColorHex);
_premColor = Color.FromArgb(20, Color.FromHex(PremiumColorHex)); // 20 Alpha for shading
_discColor = Color.FromArgb(20, Color.FromHex(DiscountColorHex)); // 20 Alpha for shading
_lineColor = Color.FromHex(LineColorHex);
if (ShowHUD)
{
InitializeHUD();
}
}
public override void Calculate(int index)
{
var barTime = Bars.OpenTimes[index];
var timeOfDay = barTime.TimeOfDay;
bool isInside = IsTimeInSession(timeOfDay);
// ---------------------------------------------------------
// 1. SESSION TRANSITIONS & DATA LOGGING
// ---------------------------------------------------------
if (isInside && !_inSession)
{
_inSession = true;
_alertTriggered = false;
_bslSwept = false;
_sslSwept = false;
_sessionStartTime = barTime;
_sessionHigh = Bars.HighPrices[index];
_sessionLow = Bars.LowPrices[index];
}
else if (isInside && _inSession)
{
_sessionHigh = Math.Max(_sessionHigh, Bars.HighPrices[index]);
_sessionLow = Math.Min(_sessionLow, Bars.LowPrices[index]);
}
else if (!isInside && _inSession)
{
_inSession = false;
_sessionEndTime = barTime;
double finalRange = _sessionHigh - _sessionLow;
double sessionEq = _sessionLow + (finalRange / 2);
// Store range for AAR math
_pastRanges.Add(finalRange);
if (_pastRanges.Count > Lookback)
{
_pastRanges.RemoveAt(0);
}
// Project Forward Matrices into London (Time-based rendering)
DateTime projectionEnd = _sessionEndTime.AddHours(ExtendHours);
string ticks = _sessionStartTime.Ticks.ToString();
var premBox = Chart.DrawRectangle("PremBox_" + ticks, _sessionEndTime, _sessionHigh, projectionEnd, sessionEq, _premColor);
premBox.IsFilled = true;
premBox.Color = Color.Transparent; // Hide border
var discBox = Chart.DrawRectangle("DiscBox_" + ticks, _sessionEndTime, sessionEq, projectionEnd, _sessionLow, _discColor);
discBox.IsFilled = true;
discBox.Color = Color.Transparent; // Hide border
var extH = Chart.DrawTrendLine("ExtH_" + ticks, _sessionEndTime, _sessionHigh, projectionEnd, _sessionHigh, Color.FromHex(PremiumColorHex));
extH.LineStyle = LineStyle.Solid;
var extL = Chart.DrawTrendLine("ExtL_" + ticks, _sessionEndTime, _sessionLow, projectionEnd, _sessionLow, Color.FromHex(DiscountColorHex));
extL.LineStyle = LineStyle.Solid;
}
// ---------------------------------------------------------
// 2. ACTIVE SESSION DRAWING (BOX & QUARTILES)
// ---------------------------------------------------------
if (_inSession)
{
double currentRange = _sessionHigh - _sessionLow;
double avgRange = _pastRanges.Count > 0 ? _pastRanges.Average() : 0;
double sessionEq = _sessionLow + (currentRange / 2);
double session75 = _sessionLow + (currentRange * 0.75);
double session25 = _sessionLow + (currentRange * 0.25);
bool isExpansion = (avgRange > 0 && currentRange > (avgRange * Multiplier));
Color activeColor = isExpansion ? _expandColor : _normalColor;
string ticks = _sessionStartTime.Ticks.ToString();
// Draw Main Box
var box = Chart.DrawRectangle("AsiaBox_" + ticks, _sessionStartTime, _sessionHigh, barTime, _sessionLow, activeColor);
box.IsFilled = true;
box.Color = Color.FromArgb(40, activeColor);
// Draw Internal Quartiles
Chart.DrawTrendLine("AsiaEq_" + ticks, _sessionStartTime, sessionEq, barTime, sessionEq, _lineColor).LineStyle = LineStyle.Lines;
Chart.DrawTrendLine("Asia75_" + ticks, _sessionStartTime, session75, barTime, session75, _lineColor).LineStyle = LineStyle.Dots;
Chart.DrawTrendLine("Asia25_" + ticks, _sessionStartTime, session25, barTime, session25, _lineColor).LineStyle = LineStyle.Dots;
// Real-time Push Alert
if (IsLastBar && isExpansion && !_alertTriggered)
{
Print($"[{Symbol.Name}] Tokyo Expansion Triggered. London fades invalidated.");
Chart.DrawStaticText("AlertTxt", "⚠️ EXPANSION TRIGGERED (NO FADE)", VerticalAlignment.Top, HorizontalAlignment.Center, _expandColor);
_alertTriggered = true;
}
if (IsLastBar && ShowHUD) UpdateHUD(currentRange, avgRange, isExpansion);
}
else if (IsLastBar)
{
Chart.RemoveObject("AlertTxt"); // Clean up static alert text after session closes
}
// ---------------------------------------------------------
// 3. LONDON LIQUIDITY PURGE DETECTION
// ---------------------------------------------------------
bool inLondon = (!isInside && _sessionEndTime != default && barTime <= _sessionEndTime.AddHours(ExtendHours));
if (inLondon)
{
// Buy-Side Liquidity Purge
if (Bars.HighPrices[index] > _sessionHigh && !_bslSwept)
{
_bslSwept = true;
var txt = Chart.DrawText("BSL_" + index, "BSL", index, Bars.HighPrices[index], Color.FromHex(PremiumColorHex));
txt.VerticalAlignment = VerticalAlignment.Bottom;
if (IsLastBar) Print($"[{Symbol.Name}] BSL Purged in London Session.");
}
// Sell-Side Liquidity Purge
if (Bars.LowPrices[index] < _sessionLow && !_sslSwept)
{
_sslSwept = true;
var txt = Chart.DrawText("SSL_" + index, "SSL", index, Bars.LowPrices[index], Color.FromHex(DiscountColorHex));
txt.VerticalAlignment = VerticalAlignment.Top;
if (IsLastBar) Print($"[{Symbol.Name}] SSL Purged in London Session.");
}
// Update HUD regarding sweep statuses while in London
if (IsLastBar && ShowHUD)
{
double currentRange = _sessionHigh - _sessionLow;
double avgRange = _pastRanges.Count > 0 ? _pastRanges.Average() : 0;
bool isExpansion = (avgRange > 0 && currentRange > (avgRange * Multiplier));
UpdateHUD(currentRange, avgRange, isExpansion);
}
}
}
private bool IsTimeInSession(TimeSpan time)
{
if (_startTime < _endTime)
return time >= _startTime && time < _endTime;
return time >= _startTime || time < _endTime;
}
// =========================================================================
// NATIVE UI DASHBOARD (HUD)
// =========================================================================
private void InitializeHUD()
{
var panel = new StackPanel { Orientation = Orientation.Vertical, Margin = new Thickness(10) };
panel.AddChild(new TextBlock { Text = "MICROSTRUCTURE [ELITE]", ForegroundColor = Color.White, FontWeight = FontWeight.ExtraBold, Margin = new Thickness(0, 0, 0, 10) });
_txtCurrentRange = new TextBlock { ForegroundColor = Color.LightGray, Margin = new Thickness(0, 0, 0, 5) };
_txtAvgRange = new TextBlock { ForegroundColor = Color.LightGray, Margin = new Thickness(0, 0, 0, 10) };
_txtStatus = new TextBlock { FontWeight = FontWeight.Bold, Margin = new Thickness(0, 0, 0, 5) };
_txtLiquidity = new TextBlock { FontWeight = FontWeight.Bold };
panel.AddChild(_txtCurrentRange);
panel.AddChild(_txtAvgRange);
panel.AddChild(_txtStatus);
panel.AddChild(_txtLiquidity);
_hudContainer = new Border
{
BackgroundColor = Color.FromArgb(220, 19, 23, 34),
BorderColor = Color.FromArgb(255, 54, 58, 69),
BorderThickness = new Thickness(1),
CornerRadius = 3,
Margin = new Thickness(20),
HorizontalAlignment = HorizontalAlignment.Right,
VerticalAlignment = VerticalAlignment.Bottom,
Child = panel
};
Chart.AddControl(_hudContainer);
}
private void UpdateHUD(double currentRange, double avgRange, bool isExpansion)
{
double pipsCurrent = Math.Round(currentRange / Symbol.PipSize, 1);
double pipsAvg = Math.Round(avgRange / Symbol.PipSize, 1);
double pctOfAvg = avgRange > 0 ? (currentRange / avgRange) * 100 : 0;
_txtCurrentRange.Text = $"Current Range: {pipsCurrent} pips";
_txtAvgRange.Text = $"15D Average: {pipsAvg} pips ({Math.Round(pctOfAvg)}%)";
// Validity Status
if (isExpansion)
{
_txtStatus.Text = "Setup Validity: EXPANSION (NO FADE)";
_txtStatus.ForegroundColor = _expandColor;
}
else
{
_txtStatus.Text = "Setup Validity: NORMAL / ACCUMULATION";
_txtStatus.ForegroundColor = _normalColor;
}
// Liquidity Sweep Status
if (_bslSwept && _sslSwept)
{
_txtLiquidity.Text = "London Liquidity: Both Sides Purged";
_txtLiquidity.ForegroundColor = Color.MediumPurple;
}
else if (_bslSwept)
{
_txtLiquidity.Text = "London Liquidity: BSL Purged (High Taken)";
_txtLiquidity.ForegroundColor = Color.FromHex(PremiumColorHex);
}
else if (_sslSwept)
{
_txtLiquidity.Text = "London Liquidity: SSL Purged (Low Taken)";
_txtLiquidity.ForegroundColor = Color.FromHex(DiscountColorHex);
}
else
{
_txtLiquidity.Text = "London Liquidity: Intact";
_txtLiquidity.ForegroundColor = Color.Gray;
}
}
}
}