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