Page 2 of 3

Re: Tokyo morning expansion days: when I refuse the London fade

Posted: Tue Sep 22, 2026 9:24 am
by PTScalper
Here are the Professional versions for both MetaTrader 4 and MetaTrader 5.

These versions introduce the Equilibrium Midline (50%), Liquidity Projection Lines that extend into the London session, a Heads-Up Display (HUD) anchored to the chart corner, and Real-time Push Alerts that trigger the moment the expansion threshold is crossed.

Re: Tokyo morning expansion days: when I refuse the London fade

Posted: Tue Sep 22, 2026 9:25 am
by PTScalper
MetaTrader 4 (MQL4) [PRO]

Save as AsianMicrostructure_PRO_MT4.mq4 in your MQL4/Indicators folder.

Code: Select all

//+------------------------------------------------------------------+
//|                                AsianMicrostructure_PRO_MT4.mq4   |
//+------------------------------------------------------------------+
#property copyright "Indicator Port"
#property version   "2.00"
#property strict
#property indicator_chart_window

// --- INPUTS ---
input string   InpSessionStart = "00:00"; // Asian Session Start (HH:MM)
input string   InpSessionEnd   = "08:00"; // Asian Session End (HH:MM)
input int      InpExtendHours  = 6;       // London Projection Length (Hours)
input int      InpLookback     = 15;      // Average Range Lookback (Days)
input double   InpMultiplier   = 1.5;     // Expansion Multiplier

input color    InpColorNormal  = clrCornflowerBlue; // Normal Range Color
input color    InpColorExpand  = clrCrimson;        // Expansion Range Color
input color    InpColorLine    = clrGray;           // Liquidity & Eq Lines
input bool     InpShowHUD      = true;              // Show Data Dashboard

// --- GLOBALS ---
int startHour, startMin, endHour, endMin;
double pastRanges[];
int rangeCount = 0;

bool inSession = false;
bool alertTriggered = false;
double sessionHigh = 0;
double sessionLow = 0;
datetime sessionStartTime = 0;

int OnInit()
{
    startHour = (int)StringToInteger(StringSubstr(InpSessionStart, 0, 2));
    startMin  = (int)StringToInteger(StringSubstr(InpSessionStart, 3, 2));
    endHour   = (int)StringToInteger(StringSubstr(InpSessionEnd, 0, 2));
    endMin    = (int)StringToInteger(StringSubstr(InpSessionEnd, 3, 2));
    
    ArrayResize(pastRanges, InpLookback);
    ArrayInitialize(pastRanges, 0.0);
    return(INIT_SUCCEEDED);
}

void OnDeinit(const int reason)
{
    ObjectsDeleteAll(0, "AsiaPro_");
    ObjectsDeleteAll(0, "AsiaHUD_");
}

bool IsTimeInSession(datetime t) 
{
    MqlDateTime dt;
    TimeToStruct(t, dt);
    int currentMins = dt.hour * 60 + dt.min;
    int startMins = startHour * 60 + startMin;
    int endMins = endHour * 60 + endMin;
    
    if (startMins < endMins) return (currentMins >= startMins && currentMins < endMins);
    return (currentMins >= startMins || currentMins < endMins);
}

void AddRange(double range) 
{
    for(int i = InpLookback - 1; i > 0; i--) pastRanges[i] = pastRanges[i-1];
    pastRanges[0] = range;
    if(rangeCount < InpLookback) rangeCount++;
}

double GetAvgRange() 
{
    if (rangeCount == 0) return 0;
    double sum = 0;
    for(int i = 0; i < rangeCount; i++) sum += pastRanges[i];
    return sum / (double)rangeCount;
}

void CreateLabel(string name, int x, int y, string text, color clr, int size = 8, bool bold = false)
{
    if(ObjectFind(0, name) < 0) {
        ObjectCreate(0, name, OBJ_LABEL, 0, 0, 0);
        ObjectSetInteger(0, name, OBJPROP_CORNER, CORNER_RIGHT_LOWER);
        ObjectSetInteger(0, name, OBJPROP_ANCHOR, ANCHOR_RIGHT_LOWER);
    }
    ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x);
    ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y);
    ObjectSetString(0, name, OBJPROP_TEXT, text);
    ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
    ObjectSetString(0, name, OBJPROP_FONT, bold ? "Arial Bold" : "Arial");
    ObjectSetInteger(0, name, OBJPROP_FONTSIZE, size);
}

int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
{
    if(rates_total < 2) return(0);
    
    int limit = rates_total - prev_calculated;
    if(prev_calculated == 0) {
        limit = rates_total - 1;
        ArrayInitialize(pastRanges, 0.0);
        rangeCount = 0;
        inSession = false;
        ObjectsDeleteAll(0, "AsiaPro_");
    }

    for(int i = limit; i >= 0; i--) {
        datetime t = time[i];
        bool isInside = IsTimeInSession(t);
        
        if(isInside && !inSession) {
            inSession = true;
            alertTriggered = false;
            sessionStartTime = t;
            sessionHigh = high[i];
            sessionLow = low[i];
        } 
        else if (isInside && inSession) {
            if(high[i] > sessionHigh) sessionHigh = high[i];
            if(low[i] < sessionLow) sessionLow = low[i];
        }
        else if (!isInside && inSession) {
            inSession = false;
            double currentRange = sessionHigh - sessionLow;
            AddRange(currentRange);
            
            // Draw Liquidity Extensions upon session close
            string prefixExt = "AsiaPro_Ext_" + IntegerToString((long)sessionStartTime);
            datetime extensionEnd = t + (InpExtendHours * 3600);
            
            ObjectCreate(0, prefixExt + "_H", OBJ_TREND, 0, t, sessionHigh, extensionEnd, sessionHigh);
            ObjectSetInteger(0, prefixExt + "_H", OBJPROP_COLOR, InpColorLine);
            ObjectSetInteger(0, prefixExt + "_H", OBJPROP_STYLE, STYLE_DOT);
            ObjectSetInteger(0, prefixExt + "_H", OBJPROP_RAY_RIGHT, false);
            
            ObjectCreate(0, prefixExt + "_L", OBJ_TREND, 0, t, sessionLow, extensionEnd, sessionLow);
            ObjectSetInteger(0, prefixExt + "_L", OBJPROP_COLOR, InpColorLine);
            ObjectSetInteger(0, prefixExt + "_L", OBJPROP_STYLE, STYLE_DOT);
            ObjectSetInteger(0, prefixExt + "_L", OBJPROP_RAY_RIGHT, false);
        }
        
        if(inSession) {
            double avgRange = GetAvgRange();
            double currentRange = sessionHigh - sessionLow;
            double sessionEq = sessionLow + (currentRange / 2.0);
            bool isExpand = (avgRange > 0 && currentRange > (avgRange * InpMultiplier));
            color boxColor = isExpand ? InpColorExpand : InpColorNormal;
            
            string objName = "AsiaPro_Box_" + IntegerToString((long)sessionStartTime);
            string eqName = "AsiaPro_Eq_" + IntegerToString((long)sessionStartTime);
            
            // Box
            if(ObjectFind(0, objName) < 0) {
                ObjectCreate(0, objName, OBJ_RECTANGLE, 0, sessionStartTime, sessionHigh, t, sessionLow);
                ObjectSetInteger(0, objName, OBJPROP_BACK, true);
            }
            ObjectSetDouble(0, objName, OBJPROP_PRICE1, sessionHigh);
            ObjectSetDouble(0, objName, OBJPROP_PRICE2, sessionLow);
            ObjectSetInteger(0, objName, OBJPROP_TIME2, t);
            ObjectSetInteger(0, objName, OBJPROP_COLOR, boxColor);
            
            // Equilibrium Line
            if(ObjectFind(0, eqName) < 0) {
                ObjectCreate(0, eqName, OBJ_TREND, 0, sessionStartTime, sessionEq, t, sessionEq);
                ObjectSetInteger(0, eqName, OBJPROP_STYLE, STYLE_DASH);
                ObjectSetInteger(0, eqName, OBJPROP_RAY_RIGHT, false);
            }
            ObjectSetDouble(0, eqName, OBJPROP_PRICE1, sessionEq);
            ObjectSetDouble(0, eqName, OBJPROP_PRICE2, sessionEq);
            ObjectSetInteger(0, eqName, OBJPROP_TIME2, t);
            ObjectSetInteger(0, eqName, OBJPROP_COLOR, InpColorLine);

            // Active Alert
            if (isExpand && !alertTriggered && i == 0) {
                Alert("Tokyo Expansion Triggered on ", Symbol(), ". London fades invalidated.");
                alertTriggered = true;
            }
        }
        
        // HUD Updates (Live Candle Only)
        if(i == 0 && InpShowHUD) {
            double avgRange = GetAvgRange();
            double curRange = sessionHigh - sessionLow;
            double pctOfAvg = avgRange > 0 ? (curRange / avgRange) * 100 : 0;
            bool isExpand = (avgRange > 0 && curRange > (avgRange * InpMultiplier));
            
            int curPts = (int)MathRound(curRange / Point);
            int avgPts = (int)MathRound(avgRange / Point);
            
            color statClr = isExpand ? InpColorExpand : InpColorNormal;
            string statTxt = isExpand ? "EXPANSION (NO FADE)" : "NORMAL";
            
            CreateLabel("AsiaHUD_1", 20, 80, "TOKYO MICROSTRUCTURE", clrWhite, 9, true);
            CreateLabel("AsiaHUD_2", 20, 60, "Current Range: " + IntegerToString(curPts) + " points", clrLightGray);
            CreateLabel("AsiaHUD_3", 20, 45, "15D Average: " + IntegerToString(avgPts) + " points (" + DoubleToString(pctOfAvg, 0) + "%)", clrLightGray);
            CreateLabel("AsiaHUD_4", 20, 25, "London Setup: " + statTxt, statClr, 8, true);
        }
    }
    return(rates_total);
}

Re: Tokyo morning expansion days: when I refuse the London fade

Posted: Tue Sep 22, 2026 9:25 am
by PTScalper
MetaTrader 5 (MQL5) [PRO]

Save as AsianMicrostructure_PRO_MT5.mq5 in your MQL5/Indicators folder. This version uses ArraySetAsSeries and solid object fills.

Code: Select all

//+------------------------------------------------------------------+
//|                                AsianMicrostructure_PRO_MT5.mq5   |
//+------------------------------------------------------------------+
#property copyright "Indicator Port"
#property version   "2.00"
#property indicator_chart_window
#property indicator_buffers 0
#property indicator_plots   0

// --- INPUTS ---
input string   InpSessionStart = "00:00"; // Asian Session Start (HH:MM)
input string   InpSessionEnd   = "08:00"; // Asian Session End (HH:MM)
input int      InpExtendHours  = 6;       // London Projection Length (Hours)
input int      InpLookback     = 15;      // Average Range Lookback (Days)
input double   InpMultiplier   = 1.5;     // Expansion Multiplier

input color    InpColorNormal  = clrCornflowerBlue; // Normal Range Color
input color    InpColorExpand  = clrCrimson;        // Expansion Range Color
input color    InpColorLine    = clrGray;           // Liquidity & Eq Lines
input bool     InpShowHUD      = true;              // Show Data Dashboard

// --- GLOBALS ---
int startHour, startMin, endHour, endMin;
double pastRanges[];
int rangeCount = 0;

bool inSession = false;
bool alertTriggered = false;
double sessionHigh = 0;
double sessionLow = 0;
datetime sessionStartTime = 0;

int OnInit()
{
    startHour = (int)StringToInteger(StringSubstr(InpSessionStart, 0, 2));
    startMin  = (int)StringToInteger(StringSubstr(InpSessionStart, 3, 2));
    endHour   = (int)StringToInteger(StringSubstr(InpSessionEnd, 0, 2));
    endMin    = (int)StringToInteger(StringSubstr(InpSessionEnd, 3, 2));
    
    ArrayResize(pastRanges, InpLookback);
    ArrayInitialize(pastRanges, 0.0);
    return(INIT_SUCCEEDED);
}

void OnDeinit(const int reason)
{
    ObjectsDeleteAll(0, "AsiaPro_");
    ObjectsDeleteAll(0, "AsiaHUD_");
}

bool IsTimeInSession(datetime t) 
{
    MqlDateTime dt;
    TimeToStruct(t, dt);
    int currentMins = dt.hour * 60 + dt.min;
    int startMins = startHour * 60 + startMin;
    int endMins = endHour * 60 + endMin;
    
    if (startMins < endMins) return (currentMins >= startMins && currentMins < endMins);
    return (currentMins >= startMins || currentMins < endMins);
}

void AddRange(double range) 
{
    for(int i = InpLookback - 1; i > 0; i--) pastRanges[i] = pastRanges[i-1];
    pastRanges[0] = range;
    if(rangeCount < InpLookback) rangeCount++;
}

double GetAvgRange() 
{
    if (rangeCount == 0) return 0;
    double sum = 0;
    for(int i = 0; i < rangeCount; i++) sum += pastRanges[i];
    return sum / (double)rangeCount;
}

void CreateLabel(string name, int x, int y, string text, color clr, int size = 8, bool bold = false)
{
    if(ObjectFind(0, name) < 0) {
        ObjectCreate(0, name, OBJ_LABEL, 0, 0, 0);
        ObjectSetInteger(0, name, OBJPROP_CORNER, CORNER_RIGHT_LOWER);
        ObjectSetInteger(0, name, OBJPROP_ANCHOR, ANCHOR_RIGHT_LOWER);
    }
    ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x);
    ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y);
    ObjectSetString(0, name, OBJPROP_TEXT, text);
    ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
    ObjectSetString(0, name, OBJPROP_FONT, bold ? "Arial Bold" : "Arial");
    ObjectSetInteger(0, name, OBJPROP_FONTSIZE, size);
}

int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
{
    if(rates_total < 2) return(0);
    
    ArraySetAsSeries(time, true);
    ArraySetAsSeries(high, true);
    ArraySetAsSeries(low, true);
    
    int limit = rates_total - prev_calculated;
    if(prev_calculated == 0) {
        limit = rates_total - 1;
        ArrayInitialize(pastRanges, 0.0);
        rangeCount = 0;
        inSession = false;
        ObjectsDeleteAll(0, "AsiaPro_");
    }

    for(int i = limit; i >= 0; i--) {
        datetime t = time[i];
        bool isInside = IsTimeInSession(t);
        
        if(isInside && !inSession) {
            inSession = true;
            alertTriggered = false;
            sessionStartTime = t;
            sessionHigh = high[i];
            sessionLow = low[i];
        } 
        else if (isInside && inSession) {
            if(high[i] > sessionHigh) sessionHigh = high[i];
            if(low[i] < sessionLow) sessionLow = low[i];
        }
        else if (!isInside && inSession) {
            inSession = false;
            double currentRange = sessionHigh - sessionLow;
            AddRange(currentRange);
            
            // Draw Liquidity Extensions
            string prefixExt = "AsiaPro_Ext_" + IntegerToString((long)sessionStartTime);
            datetime extensionEnd = t + (InpExtendHours * 3600);
            
            ObjectCreate(0, prefixExt + "_H", OBJ_TREND, 0, t, sessionHigh, extensionEnd, sessionHigh);
            ObjectSetInteger(0, prefixExt + "_H", OBJPROP_COLOR, InpColorLine);
            ObjectSetInteger(0, prefixExt + "_H", OBJPROP_STYLE, STYLE_DOT);
            ObjectSetInteger(0, prefixExt + "_H", OBJPROP_RAY_RIGHT, false);
            
            ObjectCreate(0, prefixExt + "_L", OBJ_TREND, 0, t, sessionLow, extensionEnd, sessionLow);
            ObjectSetInteger(0, prefixExt + "_L", OBJPROP_COLOR, InpColorLine);
            ObjectSetInteger(0, prefixExt + "_L", OBJPROP_STYLE, STYLE_DOT);
            ObjectSetInteger(0, prefixExt + "_L", OBJPROP_RAY_RIGHT, false);
        }
        
        if(inSession) {
            double avgRange = GetAvgRange();
            double currentRange = sessionHigh - sessionLow;
            double sessionEq = sessionLow + (currentRange / 2.0);
            bool isExpand = (avgRange > 0 && currentRange > (avgRange * InpMultiplier));
            color boxColor = isExpand ? InpColorExpand : InpColorNormal;
            
            string objName = "AsiaPro_Box_" + IntegerToString((long)sessionStartTime);
            string eqName = "AsiaPro_Eq_" + IntegerToString((long)sessionStartTime);
            
            // Box
            if(ObjectFind(0, objName) < 0) {
                ObjectCreate(0, objName, OBJ_RECTANGLE, 0, sessionStartTime, sessionHigh, t, sessionLow);
                ObjectSetInteger(0, objName, OBJPROP_FILL, true); // MT5 solid fill
                ObjectSetInteger(0, objName, OBJPROP_BACK, true);
            }
            ObjectSetDouble(0, objName, OBJPROP_PRICE1, sessionHigh);
            ObjectSetDouble(0, objName, OBJPROP_PRICE2, sessionLow);
            ObjectSetInteger(0, objName, OBJPROP_TIME2, t);
            ObjectSetInteger(0, objName, OBJPROP_COLOR, boxColor);
            
            // Equilibrium Line
            if(ObjectFind(0, eqName) < 0) {
                ObjectCreate(0, eqName, OBJ_TREND, 0, sessionStartTime, sessionEq, t, sessionEq);
                ObjectSetInteger(0, eqName, OBJPROP_STYLE, STYLE_DASH);
                ObjectSetInteger(0, eqName, OBJPROP_RAY_RIGHT, false);
            }
            ObjectSetDouble(0, eqName, OBJPROP_PRICE1, sessionEq);
            ObjectSetDouble(0, eqName, OBJPROP_PRICE2, sessionEq);
            ObjectSetInteger(0, eqName, OBJPROP_TIME2, t);
            ObjectSetInteger(0, eqName, OBJPROP_COLOR, InpColorLine);

            // Active Alert
            if (isExpand && !alertTriggered && i == 0) {
                Alert("Tokyo Expansion Triggered on ", Symbol(), ". London fades invalidated.");
                alertTriggered = true;
            }
        }
        
        // HUD Updates
        if(i == 0 && InpShowHUD) {
            double avgRange = GetAvgRange();
            double curRange = sessionHigh - sessionLow;
            double pctOfAvg = avgRange > 0 ? (curRange / avgRange) * 100 : 0;
            bool isExpand = (avgRange > 0 && curRange > (avgRange * InpMultiplier));
            
            int curPts = (int)MathRound(curRange / Point());
            int avgPts = (int)MathRound(avgRange / Point());
            
            color statClr = isExpand ? InpColorExpand : InpColorNormal;
            string statTxt = isExpand ? "EXPANSION (NO FADE)" : "NORMAL";
            
            CreateLabel("AsiaHUD_1", 20, 80, "TOKYO MICROSTRUCTURE", clrWhite, 9, true);
            CreateLabel("AsiaHUD_2", 20, 60, "Current Range: " + IntegerToString(curPts) + " points", clrLightGray);
            CreateLabel("AsiaHUD_3", 20, 45, "15D Average: " + IntegerToString(avgPts) + " points (" + DoubleToString(pctOfAvg, 0) + "%)", clrLightGray);
            CreateLabel("AsiaHUD_4", 20, 25, "London Setup: " + statTxt, statClr, 8, true);
        }
    }
    return(rates_total);
}

Re: Tokyo morning expansion days: when I refuse the London fade

Posted: Tue Sep 22, 2026 9:26 am
by PTScalper
Save this as AsianMicrostructure_PRO.cs in your cTrader Automate indicators folder.

This Pro version leverages cTrader's native UI capabilities to create a sleek, modern HUD dashboard that anchors to the chart window and updates without lagging. It uses native DateTime charting methods to effortlessly project liquidity lines into the future London session, bypassing the bar-index limitations found in other platforms.

Ctrader Pro version:

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 AsianMicrostructurePRO : 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 & HUD")]
        public string NormalColorHex { get; set; }

        [Parameter("Expansion Color", DefaultValue = "#FF5252", Group = "Aesthetics & HUD")]
        public string ExpandColorHex { get; set; }

        [Parameter("Line Color", DefaultValue = "#787B86", Group = "Aesthetics & HUD")]
        public string LineColorHex { get; set; }

        [Parameter("Show Data Dashboard", DefaultValue = true, Group = "Aesthetics & HUD")]
        public bool ShowHUD { get; set; }

        // =========================================================================
        // STATE VARIABLES
        // =========================================================================
        private TimeSpan _startTime;
        private TimeSpan _endTime;
        private List<double> _pastRanges;
        
        private bool _inSession;
        private bool _alertTriggered;
        private double _sessionHigh;
        private double _sessionLow;
        private DateTime _sessionStartTime;
        
        private Color _normalColor;
        private Color _expandColor;
        private Color _lineColor;

        // HUD UI Elements
        private Border _hudContainer;
        private TextBlock _txtCurrentRange;
        private TextBlock _txtAvgRange;
        private TextBlock _txtStatus;

        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);
            _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);
            
            // Session Transitions
            if (isInside && !_inSession)
            {
                _inSession = true;
                _alertTriggered = 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;
                double finalRange = _sessionHigh - _sessionLow;
                
                // Store range for AAR math
                _pastRanges.Add(finalRange);
                if (_pastRanges.Count > Lookback)
                {
                    _pastRanges.RemoveAt(0);
                }

                // Draw Liquidity Projections (Extends past the current bar)
                DateTime projectionEnd = barTime.AddHours(ExtendHours);
                string projHighName = "AsiaProjH_" + _sessionStartTime.Ticks;
                string projLowName  = "AsiaProjL_" + _sessionStartTime.Ticks;

                var extH = Chart.DrawTrendLine(projHighName, barTime, _sessionHigh, projectionEnd, _sessionHigh, _lineColor);
                extH.LineStyle = LineStyle.Dots;
                
                var extL = Chart.DrawTrendLine(projLowName, barTime, _sessionLow, projectionEnd, _sessionLow, _lineColor);
                extL.LineStyle = LineStyle.Dots;
            }
            
            // Active Session Visuals & Logic
            if (_inSession)
            {
                double currentRange = _sessionHigh - _sessionLow;
                double avgRange = _pastRanges.Count > 0 ? _pastRanges.Average() : 0;
                double sessionEq = _sessionLow + (currentRange / 2);
                
                bool isExpansion = (avgRange > 0 && currentRange > (avgRange * Multiplier));
                Color activeColor = isExpansion ? _expandColor : _normalColor;
                Color boxFill = Color.FromArgb(40, activeColor); // 40 = opacity
                
                // Draw Box
                string boxName = "AsiaBox_" + _sessionStartTime.Ticks;
                var box = Chart.DrawRectangle(boxName, _sessionStartTime, _sessionHigh, barTime, _sessionLow, activeColor);
                box.IsFilled = true;
                box.Color = boxFill;
                
                // Draw Equilibrium Midline
                string eqName = "AsiaEq_" + _sessionStartTime.Ticks;
                var eqLine = Chart.DrawTrendLine(eqName, _sessionStartTime, sessionEq, barTime, sessionEq, _lineColor);
                eqLine.LineStyle = LineStyle.Lines;

                // Alerts & Notifications
                if (IsLastBar && isExpansion && !_alertTriggered)
                {
                    Print($"[{Symbol.Name}] Tokyo Expansion Triggered. London fades invalidated.");
                    Chart.DrawStaticText("AlertTxt", "⚠️ EXPANSION TRIGGERED", VerticalAlignment.Top, HorizontalAlignment.Center, _expandColor);
                    _alertTriggered = true;
                }

                // Update HUD metrics live
                if (IsLastBar && ShowHUD)
                {
                    UpdateHUD(currentRange, avgRange, isExpansion);
                }
            }
            else if (IsLastBar && ShowHUD)
            {
                // Clear active text if session is closed and we're looking at current bar
                Chart.RemoveObject("AlertTxt");
            }
        }
        
        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) 
            };

            var title = new TextBlock 
            { 
                Text = "TOKYO MICROSTRUCTURE", 
                ForegroundColor = Color.White, 
                FontWeight = FontWeight.ExtraBold, 
                Margin = new Thickness(0, 0, 0, 10) 
            };
            panel.AddChild(title);

            _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 };

            panel.AddChild(_txtCurrentRange);
            panel.AddChild(_txtAvgRange);
            panel.AddChild(_txtStatus);

            _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)}%)";
            
            if (isExpansion)
            {
                _txtStatus.Text = "London Setup: EXPANSION (NO FADE)";
                _txtStatus.ForegroundColor = _expandColor;
            }
            else
            {
                _txtStatus.Text = "London Setup: NORMAL";
                _txtStatus.ForegroundColor = _normalColor;
            }
        }
    }
}

Re: Tokyo morning expansion days: when I refuse the London fade

Posted: Tue Sep 22, 2026 9:28 am
by PTScalper
To make this the ultimate, elite-tier microstructure tool for your M15 charts, we need to move beyond just measuring the range and start automatically mapping how London interacts with that range.

Since your strategy revolves around liquidity and mean-reversion versus expansion, this [ELITE] upgrade introduces three advanced smart-money concepts:

Automated Liquidity Purge Detection (BSL/SSL): It actively monitors the London projection window. If price sweeps the Asian High (Buy-Side Liquidity) or Asian Low (Sell-Side Liquidity), it automatically tags the chart with a "BSL/SSL Purge" marker and fires an alert.

Deep Premium & Discount Quartiles (75% & 25%): Equilibrium (50%) is great, but the highest probability mean-reversion entries occur in the outer quartiles. The script now draws the 75% (Premium) and 25% (Discount) internal thresholds.

Forward-Projected PD Arrays: Instead of just drawing dotted lines, it projects a lightly shaded Premium Zone (Red) and Discount Zone (Blue) into the London session, giving you a crystal-clear visual of where you are in the intraday matrix.

Re: Tokyo morning expansion days: when I refuse the London fade

Posted: Tue Sep 22, 2026 9:28 am
by PTScalper
Pine Script v5: Asian Microstructure [ELITE]

Code: Select all

//@version=5
indicator("Asian Range Microstructure [ELITE]", overlay=true, max_boxes_count=100, max_lines_count=200, max_labels_count=100)

// =========================================================================
// INPUTS & CONFIGURATION
// =========================================================================
var string G_TIME = "Session Parameters"
sessionTime = input.session("0000-0800", title="Asian Session (Exchange TZ)", group=G_TIME)
extendHours = input.int(6, title="London Projection Length (Hours)", minval=1, maxval=12, group=G_TIME)

var string G_CALC = "Expansion Logic"
lookback    = input.int(15, title="Average Range Lookback (Days)", minval=5, maxval=50, group=G_CALC)
multiplier  = input.float(1.5, title="Expansion Multiplier", step=0.1, group=G_CALC)

var string G_STYLE = "Aesthetics & HUD"
colorNormal = input.color(color.new(#2962FF, 85), title="Normal Range", group=G_STYLE)
colorExpand = input.color(color.new(#FF5252, 85), title="Expansion Range", group=G_STYLE)
colorPrem   = input.color(color.new(#FF5252, 92), title="Premium Projection (Top 50%)", group=G_STYLE)
colorDisc   = input.color(color.new(#2962FF, 92), title="Discount Projection (Bot 50%)", group=G_STYLE)
colorLine   = input.color(color.new(#787B86, 30), title="Internal Structure Lines", group=G_STYLE)
showHUD     = input.bool(true, title="Show Data Dashboard", group=G_STYLE)

// =========================================================================
// STATE VARIABLES
// =========================================================================
inSession  = not na(time(timeframe.period, sessionTime))
newSession = inSession and not inSession[1]
endSession = not inSession and inSession[1]

var float sessionHigh = na
var float sessionLow  = na
var int   sessionStartBar = na

if newSession
    sessionHigh := high
    sessionLow  := low
    sessionStartBar := bar_index
else if inSession
    sessionHigh := math.max(sessionHigh, high)
    sessionLow  := math.min(sessionLow, low)

currentRange = sessionHigh - sessionLow
sessionEq    = sessionLow + (currentRange / 2)
session75    = sessionLow + (currentRange * 0.75) // Deep Premium
session25    = sessionLow + (currentRange * 0.25) // Deep Discount

// =========================================================================
// HISTORICAL AVERAGE & STDEV CALCULATION
// =========================================================================
var float[] pastRanges = array.new_float(0)

if endSession
    array.unshift(pastRanges, currentRange)
    if array.size(pastRanges) > lookback
        array.pop(pastRanges)

avgRange   = array.size(pastRanges) > 0 ? array.avg(pastRanges) : na
stdevRange = array.size(pastRanges) > 0 ? array.stdev(pastRanges) : na
isExpansion = inSession and not na(avgRange) and (currentRange > (avgRange * multiplier))

// =========================================================================
// LIQUIDITY PURGE DETECTION (LONDON SESSION)
// =========================================================================
barsToExtend = math.round((60 / timeframe.multiplier) * extendHours)
inLondon = not inSession and ta.barssince(endSession) <= barsToExtend

var bool bslSwept = false
var bool sslSwept = false

if newSession
    bslSwept := false
    sslSwept := false

// Trigger a purge only if it's the first time London crosses the Asia High/Low
bslTrigger = inLondon and high >= sessionHigh and not bslSwept
sslTrigger = inLondon and low <= sessionLow and not sslSwept

if bslTrigger
    bslSwept := true
    label.new(bar_index, high, "BSL", style=label.style_label_down, color=color.new(#FF5252, 20), textcolor=color.white, size=size.tiny)
    alert("BSL Purged on " + syminfo.ticker + " in London Session.", alert.freq_once_per_bar)

if sslTrigger
    sslSwept := true
    label.new(bar_index, low, "SSL", style=label.style_label_up, color=color.new(#2962FF, 20), textcolor=color.white, size=size.tiny)
    alert("SSL Purged on " + syminfo.ticker + " in London Session.", alert.freq_once_per_bar)

// =========================================================================
// DRAWING CORE VISUALS
// =========================================================================
var box sessionBox = na
var line eqLine = na
var line q75Line = na
var line q25Line = na

if newSession
    sessionBox := box.new(left=bar_index, top=sessionHigh, right=bar_index, bottom=sessionLow, border_color=color.new(colorNormal, 30), bgcolor=colorNormal)
    eqLine  := line.new(x1=bar_index, y1=sessionEq, x2=bar_index, y2=sessionEq, color=colorLine, style=line.style_dashed)
    q75Line := line.new(x1=bar_index, y1=session75, x2=bar_index, y2=session75, color=colorLine, style=line.style_dotted)
    q25Line := line.new(x1=bar_index, y1=session25, x2=bar_index, y2=session25, color=colorLine, style=line.style_dotted)

else if inSession
    currentColor = isExpansion ? colorExpand : colorNormal
    
    // Update Master Box
    box.set_top(sessionBox, sessionHigh)
    box.set_bottom(sessionBox, sessionLow)
    box.set_right(sessionBox, bar_index)
    box.set_bgcolor(sessionBox, currentColor)
    box.set_border_color(sessionBox, color.new(currentColor, 30))
    
    // Update Internal Structure Lines
    line.set_y1(eqLine, sessionEq), line.set_y2(eqLine, sessionEq), line.set_x2(eqLine, bar_index)
    line.set_y1(q75Line, session75), line.set_y2(q75Line, session75), line.set_x2(q75Line, bar_index)
    line.set_y1(q25Line, session25), line.set_y2(q25Line, session25), line.set_x2(q25Line, bar_index)

if endSession
    endBar = bar_index + barsToExtend
    
    // Project Premium and Discount Zones into London
    box.new(left=bar_index, top=sessionHigh, right=endBar, bottom=sessionEq, border_color=na, bgcolor=colorPrem)
    box.new(left=bar_index, top=sessionEq, right=endBar, bottom=sessionLow, border_color=na, bgcolor=colorDisc)
    
    // Solid boundary lines for London
    line.new(x1=bar_index, y1=sessionHigh, x2=endBar, y2=sessionHigh, color=color.new(#FF5252, 20), style=line.style_solid)
    line.new(x1=bar_index, y1=sessionLow, x2=endBar, y2=sessionLow, color=color.new(#2962FF, 20), style=line.style_solid)

// Alert for active expansion during Asia
expansionTrigger = isExpansion and not isExpansion[1]
if expansionTrigger
    alert("Asian Expansion Triggered on " + syminfo.ticker + ". London fades invalidated.", alert.freq_once_per_bar)

// =========================================================================
// HUD (HEADS UP DISPLAY)
// =========================================================================
var table hud = table.new(position.bottom_right, columns=2, rows=5, bgcolor=color.new(#131722, 10), border_width=1, border_color=color.new(#363A45, 50))

if showHUD and barstate.islast
    rangeTicks = currentRange / syminfo.mintick
    avgTicks   = na(avgRange) ? 0 : (avgRange / syminfo.mintick)
    pctOfAvg   = na(avgRange) ? 0 : (currentRange / avgRange) * 100
    
    statusColor = isExpansion ? color.red : color.blue
    statusText  = isExpansion ? "EXPANSION (NO FADE)" : "NORMAL / ACCUMULATION"
    
    // Build Sweep Status Text
    sweepStatus = "Intact"
    sweepColor  = color.gray
    if bslSwept and sslSwept
        sweepStatus := "Both Sides Purged"
        sweepColor := color.purple
    else if bslSwept
        sweepStatus := "BSL Purged (High Taken)"
        sweepColor := color.red
    else if sslSwept
        sweepStatus := "SSL Purged (Low Taken)"
        sweepColor := color.blue
    
    table.cell(hud, 0, 0, "MICROSTRUCTURE [ELITE]", text_color=color.white, text_size=size.small, bgcolor=color.new(#2A2E39, 0), span=2)
    
    table.cell(hud, 0, 1, "Current Range:", text_color=color.gray, text_size=size.small, text_halign=text.align_left)
    table.cell(hud, 1, 1, str.tostring(rangeTicks, "#.0") + " ticks", text_color=color.white, text_size=size.small, text_halign=text.align_right)
    
    table.cell(hud, 0, 2, "15D Average (AAR):", text_color=color.gray, text_size=size.small, text_halign=text.align_left)
    table.cell(hud, 1, 2, str.tostring(avgTicks, "#.0") + " ticks (" + str.tostring(pctOfAvg, "#") + "%)", text_color=color.white, text_size=size.small, text_halign=text.align_right)
    
    table.cell(hud, 0, 3, "Setup Validity:", text_color=color.gray, text_size=size.small, text_halign=text.align_left)
    table.cell(hud, 1, 3, statusText, text_color=statusColor, text_size=size.small, text_halign=text.align_right)

    table.cell(hud, 0, 4, "London Liquidity:", text_color=color.gray, text_size=size.small, text_halign=text.align_left)
    table.cell(hud, 1, 4, sweepStatus, text_color=sweepColor, text_size=size.small, text_halign=text.align_right)

Re: Tokyo morning expansion days: when I refuse the London fade

Posted: Tue Sep 22, 2026 9:28 am
by PTScalper
How this alters your London Playbook:

The Expansion Override: If the box turns RED during Tokyo, the rest of the indicator is purely informational. The mean-reversion playbook is closed.

The High-Probability Fade: If the box stays BLUE (Normal), and London pushes up into the Red Shaded Premium Zone without immediately breaking the High, you look for your M1/M5 short setups at the 75% Quartile.

The Judas Swing / Turtle Soup: If the box is BLUE, and London aggressively spikes through the solid Red boundary line (Asian High) and immediately rejects back inside the zone, the script instantly prints a BSL tag. That is your mechanical confirmation that liquidity was purged, not expanded upon. You enter the fade targeting the Equilibrium (50%) or the 25% Quartile.

Re: Tokyo morning expansion days: when I refuse the London fade

Posted: Tue Sep 22, 2026 9:29 am
by PTScalper
Here are the Elite-tier versions for MetaTrader 4 and MetaTrader 5.

These scripts upgrade the previous versions with full Smart Money Concepts (SMC) integration: they project Premium/Discount arrays into the London session, track the 25%/75% internal quartiles, mechanically detect BSL (Buy-Side) and SSL (Sell-Side) liquidity purges, and dynamically update the HUD.

Re: Tokyo morning expansion days: when I refuse the London fade

Posted: Tue Sep 22, 2026 9:29 am
by PTScalper
MetaTrader 4 (MQL4) [ELITE]

Save as AsianMicrostructure_ELITE_MT4.mq4 in your MQL4/Indicators folder.

Code: Select all

//+------------------------------------------------------------------+
//|                               AsianMicrostructure_ELITE_MT4.mq4  |
//+------------------------------------------------------------------+
#property copyright "Indicator Port"
#property version   "3.00"
#property strict
#property indicator_chart_window

// --- INPUTS ---
input string   InpSessionStart = "00:00"; // Asian Session Start (HH:MM)
input string   InpSessionEnd   = "08:00"; // Asian Session End (HH:MM)
input int      InpExtendHours  = 6;       // London Projection Length (Hours)
input int      InpLookback     = 15;      // Average Range Lookback (Days)
input double   InpMultiplier   = 1.5;     // Expansion Multiplier

input color    InpColorNormal  = clrCornflowerBlue; // Normal Range Box
input color    InpColorExpand  = clrCrimson;        // Expansion Range Box
input color    InpColorPrem    = clrMistyRose;      // Premium Projection (Top 50%)
input color    InpColorDisc    = clrLightCyan;      // Discount Projection (Bot 50%)
input color    InpColorLine    = clrGray;           // Internal Structure Lines
input bool     InpShowHUD      = true;              // Show Data Dashboard

// --- GLOBALS ---
int startHour, startMin, endHour, endMin;
double pastRanges[];
int rangeCount = 0;

bool inSession = false;
bool alertTriggered = false;
bool bslSwept = false;
bool sslSwept = false;

double sessionHigh = 0;
double sessionLow = 0;
datetime sessionStartTime = 0;
datetime sessionEndTime = 0;

int OnInit()
{
    startHour = (int)StringToInteger(StringSubstr(InpSessionStart, 0, 2));
    startMin  = (int)StringToInteger(StringSubstr(InpSessionStart, 3, 2));
    endHour   = (int)StringToInteger(StringSubstr(InpSessionEnd, 0, 2));
    endMin    = (int)StringToInteger(StringSubstr(InpSessionEnd, 3, 2));
    
    ArrayResize(pastRanges, InpLookback);
    ArrayInitialize(pastRanges, 0.0);
    return(INIT_SUCCEEDED);
}

void OnDeinit(const int reason)
{
    ObjectsDeleteAll(0, "AsiaElite_");
    ObjectsDeleteAll(0, "AsiaHUD_");
}

bool IsTimeInSession(datetime t) 
{
    MqlDateTime dt;
    TimeToStruct(t, dt);
    int currentMins = dt.hour * 60 + dt.min;
    int startMins = startHour * 60 + startMin;
    int endMins = endHour * 60 + endMin;
    
    if (startMins < endMins) return (currentMins >= startMins && currentMins < endMins);
    return (currentMins >= startMins || currentMins < endMins);
}

void AddRange(double range) 
{
    for(int i = InpLookback - 1; i > 0; i--) pastRanges[i] = pastRanges[i-1];
    pastRanges[0] = range;
    if(rangeCount < InpLookback) rangeCount++;
}

double GetAvgRange() 
{
    if (rangeCount == 0) return 0;
    double sum = 0;
    for(int i = 0; i < rangeCount; i++) sum += pastRanges[i];
    return sum / (double)rangeCount;
}

void CreateLabel(string name, int x, int y, string text, color clr, int size = 8, bool bold = false)
{
    if(ObjectFind(0, name) < 0) {
        ObjectCreate(0, name, OBJ_LABEL, 0, 0, 0);
        ObjectSetInteger(0, name, OBJPROP_CORNER, CORNER_RIGHT_LOWER);
        ObjectSetInteger(0, name, OBJPROP_ANCHOR, ANCHOR_RIGHT_LOWER);
    }
    ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x);
    ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y);
    ObjectSetString(0, name, OBJPROP_TEXT, text);
    ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
    ObjectSetString(0, name, OBJPROP_FONT, bold ? "Arial Bold" : "Arial");
    ObjectSetInteger(0, name, OBJPROP_FONTSIZE, size);
}

int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
{
    if(rates_total < 2) return(0);
    
    int limit = rates_total - prev_calculated;
    if(prev_calculated == 0) {
        limit = rates_total - 1;
        ArrayInitialize(pastRanges, 0.0);
        rangeCount = 0;
        inSession = false;
        ObjectsDeleteAll(0, "AsiaElite_");
    }

    for(int i = limit; i >= 0; i--) {
        datetime t = time[i];
        bool isInside = IsTimeInSession(t);
        
        // Session Start
        if(isInside && !inSession) {
            inSession = true;
            alertTriggered = false;
            bslSwept = false;
            sslSwept = false;
            sessionStartTime = t;
            sessionHigh = high[i];
            sessionLow = low[i];
        } 
        // Inside Session Updates
        else if (isInside && inSession) {
            if(high[i] > sessionHigh) sessionHigh = high[i];
            if(low[i] < sessionLow) sessionLow = low[i];
        }
        // Session End
        else if (!isInside && inSession) {
            inSession = false;
            sessionEndTime = t;
            double currentRange = sessionHigh - sessionLow;
            AddRange(currentRange);
            
            // Draw London Projections
            string prefix = "AsiaElite_";
            string tStr = IntegerToString((long)sessionStartTime);
            datetime extensionEnd = sessionEndTime + (InpExtendHours * 3600);
            double sessionEq = sessionLow + (currentRange / 2.0);
            
            // Premium Box
            ObjectCreate(0, prefix + "Prem_" + tStr, OBJ_RECTANGLE, 0, sessionEndTime, sessionHigh, extensionEnd, sessionEq);
            ObjectSetInteger(0, prefix + "Prem_" + tStr, OBJPROP_COLOR, InpColorPrem);
            ObjectSetInteger(0, prefix + "Prem_" + tStr, OBJPROP_BACK, true);
            
            // Discount Box
            ObjectCreate(0, prefix + "Disc_" + tStr, OBJ_RECTANGLE, 0, sessionEndTime, sessionEq, extensionEnd, sessionLow);
            ObjectSetInteger(0, prefix + "Disc_" + tStr, OBJPROP_COLOR, InpColorDisc);
            ObjectSetInteger(0, prefix + "Disc_" + tStr, OBJPROP_BACK, true);

            // Boundary Lines
            ObjectCreate(0, prefix + "ExtH_" + tStr, OBJ_TREND, 0, sessionEndTime, sessionHigh, extensionEnd, sessionHigh);
            ObjectSetInteger(0, prefix + "ExtH_" + tStr, OBJPROP_COLOR, clrCrimson);
            ObjectSetInteger(0, prefix + "ExtH_" + tStr, OBJPROP_RAY_RIGHT, false);
            
            ObjectCreate(0, prefix + "ExtL_" + tStr, OBJ_TREND, 0, sessionEndTime, sessionLow, extensionEnd, sessionLow);
            ObjectSetInteger(0, prefix + "ExtL_" + tStr, OBJPROP_COLOR, clrRoyalBlue);
            ObjectSetInteger(0, prefix + "ExtL_" + tStr, OBJPROP_RAY_RIGHT, false);
        }
        
        // Active Session Drawing (Boxes & Quartiles)
        if(inSession) {
            double avgRange = GetAvgRange();
            double currentRange = sessionHigh - sessionLow;
            double sessionEq = sessionLow + (currentRange / 2.0);
            double session75 = sessionLow + (currentRange * 0.75);
            double session25 = sessionLow + (currentRange * 0.25);
            
            bool isExpand = (avgRange > 0 && currentRange > (avgRange * InpMultiplier));
            color boxColor = isExpand ? InpColorExpand : InpColorNormal;
            
            string prefix = "AsiaElite_";
            string tStr = IntegerToString((long)sessionStartTime);
            
            // Main Box
            if(ObjectFind(0, prefix + "Box_" + tStr) < 0) {
                ObjectCreate(0, prefix + "Box_" + tStr, OBJ_RECTANGLE, 0, sessionStartTime, sessionHigh, t, sessionLow);
                ObjectSetInteger(0, prefix + "Box_" + tStr, OBJPROP_BACK, true);
            }
            ObjectSetDouble(0, prefix + "Box_" + tStr, OBJPROP_PRICE1, sessionHigh);
            ObjectSetDouble(0, prefix + "Box_" + tStr, OBJPROP_PRICE2, sessionLow);
            ObjectSetInteger(0, prefix + "Box_" + tStr, OBJPROP_TIME2, t);
            ObjectSetInteger(0, prefix + "Box_" + tStr, OBJPROP_COLOR, boxColor);
            
            // Eq, 75%, 25% Lines
            string[] lines = {"Eq_", "Q75_", "Q25_"};
            double[] prices = {sessionEq, session75, session25};
            int[] styles = {STYLE_DASH, STYLE_DOT, STYLE_DOT};
            
            for(int j=0; j<3; j++) {
                string lineName = prefix + lines[j] + tStr;
                if(ObjectFind(0, lineName) < 0) {
                    ObjectCreate(0, lineName, OBJ_TREND, 0, sessionStartTime, prices[j], t, prices[j]);
                    ObjectSetInteger(0, lineName, OBJPROP_STYLE, styles[j]);
                    ObjectSetInteger(0, lineName, OBJPROP_RAY_RIGHT, false);
                    ObjectSetInteger(0, lineName, OBJPROP_COLOR, InpColorLine);
                }
                ObjectSetDouble(0, lineName, OBJPROP_PRICE1, prices[j]);
                ObjectSetDouble(0, lineName, OBJPROP_PRICE2, prices[j]);
                ObjectSetInteger(0, lineName, OBJPROP_TIME2, t);
            }

            // Expansion Alert
            if (isExpand && !alertTriggered && i == 0) {
                Alert("Tokyo Expansion on ", Symbol(), " (No Fade Zone)");
                alertTriggered = true;
            }
        }
        
        // Liquidity Purge Detection (Post-Session)
        bool inLondon = (!isInside && sessionEndTime > 0 && t <= sessionEndTime + (InpExtendHours * 3600));
        if (inLondon) {
            string prefix = "AsiaElite_";
            string tStr = IntegerToString((long)sessionStartTime);
            
            // BSL Sweep
            if (high[i] > sessionHigh && !bslSwept) {
                bslSwept = true;
                string bslName = prefix + "BSL_" + IntegerToString((long)t);
                ObjectCreate(0, bslName, OBJ_TEXT, 0, t, high[i]);
                ObjectSetString(0, bslName, OBJPROP_TEXT, "BSL");
                ObjectSetInteger(0, bslName, OBJPROP_COLOR, clrCrimson);
                if (i == 0) Alert("BSL Purged on ", Symbol(), " in London Session.");
            }
            
            // SSL Sweep
            if (low[i] < sessionLow && !sslSwept) {
                sslSwept = true;
                string sslName = prefix + "SSL_" + IntegerToString((long)t);
                ObjectCreate(0, sslName, OBJ_TEXT, 0, t, low[i]);
                ObjectSetString(0, sslName, OBJPROP_TEXT, "SSL");
                ObjectSetInteger(0, sslName, OBJPROP_COLOR, clrRoyalBlue);
                if (i == 0) Alert("SSL Purged on ", Symbol(), " in London Session.");
            }
        }
        
        // Live HUD Updates
        if(i == 0 && InpShowHUD) {
            double avgRange = GetAvgRange();
            double curRange = sessionHigh - sessionLow;
            double pctOfAvg = avgRange > 0 ? (curRange / avgRange) * 100 : 0;
            bool isExpand = (avgRange > 0 && curRange > (avgRange * InpMultiplier));
            
            int curPts = (int)MathRound(curRange / Point);
            int avgPts = (int)MathRound(avgRange / Point);
            
            color statClr = isExpand ? InpColorExpand : InpColorNormal;
            string statTxt = isExpand ? "EXPANSION (NO FADE)" : "NORMAL / ACCUMULATION";
            
            string sweepTxt = "Intact";
            color sweepClr = clrGray;
            if (bslSwept && sslSwept) { sweepTxt = "Both Sides Purged"; sweepClr = clrMediumPurple; }
            else if (bslSwept) { sweepTxt = "BSL Purged (High Taken)"; sweepClr = clrCrimson; }
            else if (sslSwept) { sweepTxt = "SSL Purged (Low Taken)"; sweepClr = clrRoyalBlue; }
            
            CreateLabel("AsiaHUD_1", 20, 100, "MICROSTRUCTURE [ELITE]", clrWhite, 9, true);
            CreateLabel("AsiaHUD_2", 20, 80,  "Current Range: " + IntegerToString(curPts) + " points", clrLightGray);
            CreateLabel("AsiaHUD_3", 20, 65,  "15D Average: " + IntegerToString(avgPts) + " points (" + DoubleToString(pctOfAvg, 0) + "%)", clrLightGray);
            CreateLabel("AsiaHUD_4", 20, 45,  "Setup Validity: " + statTxt, statClr, 8, true);
            CreateLabel("AsiaHUD_5", 20, 25,  "London Liquidity: " + sweepTxt, sweepClr, 8, true);
        }
    }
    return(rates_total);
}

Re: Tokyo morning expansion days: when I refuse the London fade

Posted: Tue Sep 22, 2026 9:30 am
by PTScalper
MetaTrader 5 (MQL5) [ELITE]

Save as AsianMicrostructure_ELITE_MT5.mq5 in your MQL5/Indicators folder. This version uses OBJPROP_FILL for perfect opacity on the Premium and Discount zones.

Code: Select all

//+------------------------------------------------------------------+
//|                               AsianMicrostructure_ELITE_MT5.mq5  |
//+------------------------------------------------------------------+
#property copyright "Indicator Port"
#property version   "3.00"
#property indicator_chart_window
#property indicator_buffers 0
#property indicator_plots   0

// --- INPUTS ---
input string   InpSessionStart = "00:00"; // Asian Session Start (HH:MM)
input string   InpSessionEnd   = "08:00"; // Asian Session End (HH:MM)
input int      InpExtendHours  = 6;       // London Projection Length (Hours)
input int      InpLookback     = 15;      // Average Range Lookback (Days)
input double   InpMultiplier   = 1.5;     // Expansion Multiplier

input color    InpColorNormal  = clrCornflowerBlue; // Normal Range Box
input color    InpColorExpand  = clrCrimson;        // Expansion Range Box
input color    InpColorPrem    = clrMistyRose;      // Premium Projection (Top 50%)
input color    InpColorDisc    = clrLightCyan;      // Discount Projection (Bot 50%)
input color    InpColorLine    = clrGray;           // Internal Structure Lines
input bool     InpShowHUD      = true;              // Show Data Dashboard

// --- GLOBALS ---
int startHour, startMin, endHour, endMin;
double pastRanges[];
int rangeCount = 0;

bool inSession = false;
bool alertTriggered = false;
bool bslSwept = false;
bool sslSwept = false;

double sessionHigh = 0;
double sessionLow = 0;
datetime sessionStartTime = 0;
datetime sessionEndTime = 0;

int OnInit()
{
    startHour = (int)StringToInteger(StringSubstr(InpSessionStart, 0, 2));
    startMin  = (int)StringToInteger(StringSubstr(InpSessionStart, 3, 2));
    endHour   = (int)StringToInteger(StringSubstr(InpSessionEnd, 0, 2));
    endMin    = (int)StringToInteger(StringSubstr(InpSessionEnd, 3, 2));
    
    ArrayResize(pastRanges, InpLookback);
    ArrayInitialize(pastRanges, 0.0);
    return(INIT_SUCCEEDED);
}

void OnDeinit(const int reason)
{
    ObjectsDeleteAll(0, "AsiaElite_");
    ObjectsDeleteAll(0, "AsiaHUD_");
}

bool IsTimeInSession(datetime t) 
{
    MqlDateTime dt;
    TimeToStruct(t, dt);
    int currentMins = dt.hour * 60 + dt.min;
    int startMins = startHour * 60 + startMin;
    int endMins = endHour * 60 + endMin;
    
    if (startMins < endMins) return (currentMins >= startMins && currentMins < endMins);
    return (currentMins >= startMins || currentMins < endMins);
}

void AddRange(double range) 
{
    for(int i = InpLookback - 1; i > 0; i--) pastRanges[i] = pastRanges[i-1];
    pastRanges[0] = range;
    if(rangeCount < InpLookback) rangeCount++;
}

double GetAvgRange() 
{
    if (rangeCount == 0) return 0;
    double sum = 0;
    for(int i = 0; i < rangeCount; i++) sum += pastRanges[i];
    return sum / (double)rangeCount;
}

void CreateLabel(string name, int x, int y, string text, color clr, int size = 8, bool bold = false)
{
    if(ObjectFind(0, name) < 0) {
        ObjectCreate(0, name, OBJ_LABEL, 0, 0, 0);
        ObjectSetInteger(0, name, OBJPROP_CORNER, CORNER_RIGHT_LOWER);
        ObjectSetInteger(0, name, OBJPROP_ANCHOR, ANCHOR_RIGHT_LOWER);
    }
    ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x);
    ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y);
    ObjectSetString(0, name, OBJPROP_TEXT, text);
    ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
    ObjectSetString(0, name, OBJPROP_FONT, bold ? "Arial Bold" : "Arial");
    ObjectSetInteger(0, name, OBJPROP_FONTSIZE, size);
}

int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
{
    if(rates_total < 2) return(0);
    
    ArraySetAsSeries(time, true);
    ArraySetAsSeries(high, true);
    ArraySetAsSeries(low, true);
    
    int limit = rates_total - prev_calculated;
    if(prev_calculated == 0) {
        limit = rates_total - 1;
        ArrayInitialize(pastRanges, 0.0);
        rangeCount = 0;
        inSession = false;
        ObjectsDeleteAll(0, "AsiaElite_");
    }

    for(int i = limit; i >= 0; i--) {
        datetime t = time[i];
        bool isInside = IsTimeInSession(t);
        
        // Session Start
        if(isInside && !inSession) {
            inSession = true;
            alertTriggered = false;
            bslSwept = false;
            sslSwept = false;
            sessionStartTime = t;
            sessionHigh = high[i];
            sessionLow = low[i];
        } 
        // Inside Session Updates
        else if (isInside && inSession) {
            if(high[i] > sessionHigh) sessionHigh = high[i];
            if(low[i] < sessionLow) sessionLow = low[i];
        }
        // Session End
        else if (!isInside && inSession) {
            inSession = false;
            sessionEndTime = t;
            double currentRange = sessionHigh - sessionLow;
            AddRange(currentRange);
            
            // Draw London Projections
            string prefix = "AsiaElite_";
            string tStr = IntegerToString((long)sessionStartTime);
            datetime extensionEnd = sessionEndTime + (InpExtendHours * 3600);
            double sessionEq = sessionLow + (currentRange / 2.0);
            
            // Premium Box
            ObjectCreate(0, prefix + "Prem_" + tStr, OBJ_RECTANGLE, 0, sessionEndTime, sessionHigh, extensionEnd, sessionEq);
            ObjectSetInteger(0, prefix + "Prem_" + tStr, OBJPROP_COLOR, InpColorPrem);
            ObjectSetInteger(0, prefix + "Prem_" + tStr, OBJPROP_FILL, true);
            ObjectSetInteger(0, prefix + "Prem_" + tStr, OBJPROP_BACK, true);
            
            // Discount Box
            ObjectCreate(0, prefix + "Disc_" + tStr, OBJ_RECTANGLE, 0, sessionEndTime, sessionEq, extensionEnd, sessionLow);
            ObjectSetInteger(0, prefix + "Disc_" + tStr, OBJPROP_COLOR, InpColorDisc);
            ObjectSetInteger(0, prefix + "Disc_" + tStr, OBJPROP_FILL, true);
            ObjectSetInteger(0, prefix + "Disc_" + tStr, OBJPROP_BACK, true);

            // Boundary Lines
            ObjectCreate(0, prefix + "ExtH_" + tStr, OBJ_TREND, 0, sessionEndTime, sessionHigh, extensionEnd, sessionHigh);
            ObjectSetInteger(0, prefix + "ExtH_" + tStr, OBJPROP_COLOR, clrCrimson);
            ObjectSetInteger(0, prefix + "ExtH_" + tStr, OBJPROP_RAY_RIGHT, false);
            
            ObjectCreate(0, prefix + "ExtL_" + tStr, OBJ_TREND, 0, sessionEndTime, sessionLow, extensionEnd, sessionLow);
            ObjectSetInteger(0, prefix + "ExtL_" + tStr, OBJPROP_COLOR, clrRoyalBlue);
            ObjectSetInteger(0, prefix + "ExtL_" + tStr, OBJPROP_RAY_RIGHT, false);
        }
        
        // Active Session Drawing
        if(inSession) {
            double avgRange = GetAvgRange();
            double currentRange = sessionHigh - sessionLow;
            double sessionEq = sessionLow + (currentRange / 2.0);
            double session75 = sessionLow + (currentRange * 0.75);
            double session25 = sessionLow + (currentRange * 0.25);
            
            bool isExpand = (avgRange > 0 && currentRange > (avgRange * InpMultiplier));
            color boxColor = isExpand ? InpColorExpand : InpColorNormal;
            
            string prefix = "AsiaElite_";
            string tStr = IntegerToString((long)sessionStartTime);
            
            // Main Box
            if(ObjectFind(0, prefix + "Box_" + tStr) < 0) {
                ObjectCreate(0, prefix + "Box_" + tStr, OBJ_RECTANGLE, 0, sessionStartTime, sessionHigh, t, sessionLow);
                ObjectSetInteger(0, prefix + "Box_" + tStr, OBJPROP_FILL, true);
                ObjectSetInteger(0, prefix + "Box_" + tStr, OBJPROP_BACK, true);
            }
            ObjectSetDouble(0, prefix + "Box_" + tStr, OBJPROP_PRICE1, sessionHigh);
            ObjectSetDouble(0, prefix + "Box_" + tStr, OBJPROP_PRICE2, sessionLow);
            ObjectSetInteger(0, prefix + "Box_" + tStr, OBJPROP_TIME2, t);
            ObjectSetInteger(0, prefix + "Box_" + tStr, OBJPROP_COLOR, boxColor);
            
            // Eq, 75%, 25% Lines
            string[] lines = {"Eq_", "Q75_", "Q25_"};
            double[] prices = {sessionEq, session75, session25};
            int[] styles = {STYLE_DASH, STYLE_DOT, STYLE_DOT};
            
            for(int j=0; j<3; j++) {
                string lineName = prefix + lines[j] + tStr;
                if(ObjectFind(0, lineName) < 0) {
                    ObjectCreate(0, lineName, OBJ_TREND, 0, sessionStartTime, prices[j], t, prices[j]);
                    ObjectSetInteger(0, lineName, OBJPROP_STYLE, styles[j]);
                    ObjectSetInteger(0, lineName, OBJPROP_RAY_RIGHT, false);
                    ObjectSetInteger(0, lineName, OBJPROP_COLOR, InpColorLine);
                }
                ObjectSetDouble(0, lineName, OBJPROP_PRICE1, prices[j]);
                ObjectSetDouble(0, lineName, OBJPROP_PRICE2, prices[j]);
                ObjectSetInteger(0, lineName, OBJPROP_TIME2, t);
            }

            // Expansion Alert
            if (isExpand && !alertTriggered && i == 0) {
                Alert("Tokyo Expansion on ", Symbol(), " (No Fade Zone)");
                alertTriggered = true;
            }
        }
        
        // Liquidity Purge Detection (Post-Session)
        bool inLondon = (!isInside && sessionEndTime > 0 && t <= sessionEndTime + (InpExtendHours * 3600));
        if (inLondon) {
            string prefix = "AsiaElite_";
            
            // BSL Sweep
            if (high[i] > sessionHigh && !bslSwept) {
                bslSwept = true;
                string bslName = prefix + "BSL_" + IntegerToString((long)t);
                ObjectCreate(0, bslName, OBJ_TEXT, 0, t, high[i]);
                ObjectSetString(0, bslName, OBJPROP_TEXT, "BSL");
                ObjectSetInteger(0, bslName, OBJPROP_COLOR, clrCrimson);
                if (i == 0) Alert("BSL Purged on ", Symbol(), " in London Session.");
            }
            
            // SSL Sweep
            if (low[i] < sessionLow && !sslSwept) {
                sslSwept = true;
                string sslName = prefix + "SSL_" + IntegerToString((long)t);
                ObjectCreate(0, sslName, OBJ_TEXT, 0, t, low[i]);
                ObjectSetString(0, sslName, OBJPROP_TEXT, "SSL");
                ObjectSetInteger(0, sslName, OBJPROP_COLOR, clrRoyalBlue);
                if (i == 0) Alert("SSL Purged on ", Symbol(), " in London Session.");
            }
        }
        
        // Live HUD Updates
        if(i == 0 && InpShowHUD) {
            double avgRange = GetAvgRange();
            double curRange = sessionHigh - sessionLow;
            double pctOfAvg = avgRange > 0 ? (curRange / avgRange) * 100 : 0;
            bool isExpand = (avgRange > 0 && curRange > (avgRange * InpMultiplier));
            
            int curPts = (int)MathRound(curRange / Point());
            int avgPts = (int)MathRound(avgRange / Point());
            
            color statClr = isExpand ? InpColorExpand : InpColorNormal;
            string statTxt = isExpand ? "EXPANSION (NO FADE)" : "NORMAL / ACCUMULATION";
            
            string sweepTxt = "Intact";
            color sweepClr = clrGray;
            if (bslSwept && sslSwept) { sweepTxt = "Both Sides Purged"; sweepClr = clrMediumPurple; }
            else if (bslSwept) { sweepTxt = "BSL Purged (High Taken)"; sweepClr = clrCrimson; }
            else if (sslSwept) { sweepTxt = "SSL Purged (Low Taken)"; sweepClr = clrRoyalBlue; }
            
            CreateLabel("AsiaHUD_1", 20, 100, "MICROSTRUCTURE [ELITE]", clrWhite, 9, true);
            CreateLabel("AsiaHUD_2", 20, 80,  "Current Range: " + IntegerToString(curPts) + " points", clrLightGray);
            CreateLabel("AsiaHUD_3", 20, 65,  "15D Average: " + IntegerToString(avgPts) + " points (" + DoubleToString(pctOfAvg, 0) + "%)", clrLightGray);
            CreateLabel("AsiaHUD_4", 20, 45,  "Setup Validity: " + statTxt, statClr, 8, true);
            CreateLabel("AsiaHUD_5", 20, 25,  "London Liquidity: " + sweepTxt, sweepClr, 8, true);
        }
    }
    return(rates_total);
}