last ...
This commit is contained in:
@@ -0,0 +1,312 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| MajorStructure_Zones_EA.mq5 |
|
||||
//| Step 1.5: BOS Confirmation + Zone Drawing + Bias |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Professional Trader & AI Assistant"
|
||||
#property version "1.50"
|
||||
#property strict
|
||||
|
||||
//--- Input Parameters
|
||||
input int MinorPivotLookback = 3; // تعداد کندل چپ/راست برای شناسایی مینور (در 15m = 3 یعنی 45 دقیقه)
|
||||
input int MaxBarsToScan = 500; // حداکثر کندلهای 4H برای جستجو
|
||||
input color BullishZoneColor = clrLime; // رنگ ناحیه کف ماژور (حمایت)
|
||||
input color BearishZoneColor = clrRed; // رنگ ناحیه سقف ماژور (مقاومت)
|
||||
input int ZoneTransparency = 80; // شفافیت ناحیه (0-255)
|
||||
|
||||
//--- Global Variables
|
||||
// نواحی ماژور (High و Low کندل مربوطه)
|
||||
double majorLowZoneHigh = 0, majorLowZoneLow = 0;
|
||||
datetime majorLowZoneTime = 0;
|
||||
int majorLowBarIndex = -1;
|
||||
|
||||
double majorHighZoneHigh = 0, majorHighZoneLow = 0;
|
||||
datetime majorHighZoneTime = 0;
|
||||
int majorHighBarIndex = -1;
|
||||
|
||||
string biasDirection = "NEUTRAL";
|
||||
|
||||
// نام آبجکتهای گرافیکی
|
||||
string lowZoneName = "MajorLowZone";
|
||||
string highZoneName = "MajorHighZone";
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert initialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
int OnInit()
|
||||
{
|
||||
Print("EA v1.5 Initialized. Scanning for BOS-confirmed Major Zones...");
|
||||
return (INIT_SUCCEEDED);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert deinitialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnDeinit(const int reason)
|
||||
{
|
||||
// پاک کردن تمام آبجکتهای رسم شده
|
||||
ObjectDelete(0, lowZoneName);
|
||||
ObjectDelete(0, highZoneName);
|
||||
Comment("");
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert tick function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnTick()
|
||||
{
|
||||
// فقط یک بار در هر کندل جدید 4 ساعته محاسبات را انجام میدهیم
|
||||
static datetime lastBarTime = 0;
|
||||
datetime currentBarTime = iTime(_Symbol, PERIOD_H4, 0);
|
||||
|
||||
if (lastBarTime == currentBarTime)
|
||||
{
|
||||
// در تیکهای میانی، فقط ناحیه جاری را تا زمان حال گسترش میدهیم
|
||||
ExtendZonesToCurrentTime();
|
||||
return;
|
||||
}
|
||||
lastBarTime = currentBarTime;
|
||||
|
||||
// 1. دریافت دادههای تایم فریم 4 ساعته
|
||||
double high[], low[], close[];
|
||||
datetime time[];
|
||||
|
||||
ArraySetAsSeries(high, true);
|
||||
ArraySetAsSeries(low, true);
|
||||
ArraySetAsSeries(close, true);
|
||||
ArraySetAsSeries(time, true);
|
||||
|
||||
int copied = CopyHigh(_Symbol, PERIOD_H4, 0, MaxBarsToScan, high);
|
||||
CopyLow(_Symbol, PERIOD_H4, 0, MaxBarsToScan, low);
|
||||
CopyClose(_Symbol, PERIOD_H4, 0, MaxBarsToScan, close);
|
||||
CopyTime(_Symbol, PERIOD_H4, 0, MaxBarsToScan, time);
|
||||
|
||||
if (copied <= 0)
|
||||
{
|
||||
Print("Error copying 4H data.");
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. اسکن تاریخی برای شناسایی BOS و نواحی ماژور
|
||||
ScanForMajorZones(high, low, close, time, copied);
|
||||
|
||||
// 3. تعیین بایاس
|
||||
DetermineBias();
|
||||
|
||||
// 4. رسم نواحی روی چارت
|
||||
DrawMajorZones();
|
||||
|
||||
// 5. نمایش داشبورد
|
||||
DisplayDashboard();
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| اسکن تاریخی برای شناسایی نواحی ماژور با تاییدیه BOS |
|
||||
//+------------------------------------------------------------------+
|
||||
void ScanForMajorZones(double &high[], double &low[], double &close[], datetime &time[], int total)
|
||||
{
|
||||
// ریست کردن متغیرها
|
||||
majorLowBarIndex = -1;
|
||||
majorHighBarIndex = -1;
|
||||
|
||||
int lastMinorHighIdx = -1;
|
||||
int lastMinorLowIdx = -1;
|
||||
|
||||
// حلقه از گذشته به حال (اندیس بزرگ به کوچک)
|
||||
for (int i = total - MinorPivotLookback - 1; i >= MinorPivotLookback; i--)
|
||||
{
|
||||
// بررسی آیا کندل i یک سقف مینور است
|
||||
if (IsMinorPivot(high, i, true))
|
||||
{
|
||||
lastMinorHighIdx = i;
|
||||
}
|
||||
|
||||
// بررسی آیا کندل i یک کف مینور است
|
||||
if (IsMinorPivot(low, i, false))
|
||||
{
|
||||
lastMinorLowIdx = i;
|
||||
}
|
||||
|
||||
// بررسی BOS صعودی: آیا Close کندل i بالاتر از آخرین سقف مینور است؟
|
||||
if (lastMinorHighIdx != -1 && i < lastMinorHighIdx && close[i] > high[lastMinorHighIdx])
|
||||
{
|
||||
// BOS صعودی رخ داد -> کف مینور قبلی حالا کف ماژور تایید شده است
|
||||
if (lastMinorLowIdx != -1 && lastMinorLowIdx > lastMinorHighIdx)
|
||||
{
|
||||
majorLowBarIndex = lastMinorLowIdx;
|
||||
majorLowZoneHigh = high[lastMinorLowIdx];
|
||||
majorLowZoneLow = low[lastMinorLowIdx];
|
||||
majorLowZoneTime = time[lastMinorLowIdx];
|
||||
}
|
||||
}
|
||||
|
||||
// بررسی BOS نزولی: آیا Close کندل i پایینتر از آخرین کف مینور است؟
|
||||
if (lastMinorLowIdx != -1 && i < lastMinorLowIdx && close[i] < low[lastMinorLowIdx])
|
||||
{
|
||||
// BOS نزولی رخ داد -> سقف مینور قبلی حالا سقف ماژور تایید شده است
|
||||
if (lastMinorHighIdx != -1 && lastMinorHighIdx > lastMinorLowIdx)
|
||||
{
|
||||
majorHighBarIndex = lastMinorHighIdx;
|
||||
majorHighZoneHigh = high[lastMinorHighIdx];
|
||||
majorHighZoneLow = low[lastMinorHighIdx];
|
||||
majorHighZoneTime = time[lastMinorHighIdx];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| بررسی آیا کندل idx یک پراوت مینور است |
|
||||
//+------------------------------------------------------------------+
|
||||
bool IsMinorPivot(double &data[], int idx, bool isHigh)
|
||||
{
|
||||
for (int j = 1; j <= MinorPivotLookback; j++)
|
||||
{
|
||||
if (isHigh)
|
||||
{
|
||||
if (data[idx] <= data[idx - j] || data[idx] <= data[idx + j])
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (data[idx] >= data[idx - j] || data[idx] >= data[idx + j])
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| تعیین بایاس بر اساس قانون تازگی |
|
||||
//+------------------------------------------------------------------+
|
||||
void DetermineBias()
|
||||
{
|
||||
if (majorHighBarIndex == -1 && majorLowBarIndex == -1)
|
||||
{
|
||||
biasDirection = "NEUTRAL (No Major Structure)";
|
||||
return;
|
||||
}
|
||||
|
||||
if (majorHighBarIndex == -1)
|
||||
{
|
||||
biasDirection = "BULLISH (Only Major Low Found)";
|
||||
return;
|
||||
}
|
||||
|
||||
if (majorLowBarIndex == -1)
|
||||
{
|
||||
biasDirection = "BEARISH (Only Major High Found)";
|
||||
return;
|
||||
}
|
||||
|
||||
// در آرایه سری، اندیس کوچکتر = زمان جدیدتر
|
||||
if (majorHighBarIndex < majorLowBarIndex)
|
||||
{
|
||||
biasDirection = "BEARISH (Short Only)";
|
||||
}
|
||||
else if (majorLowBarIndex < majorHighBarIndex)
|
||||
{
|
||||
biasDirection = "BULLISH (Long Only)";
|
||||
}
|
||||
else
|
||||
{
|
||||
biasDirection = "NEUTRAL (Equal Time)";
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| رسم نواحی ماژور روی چارت |
|
||||
//+------------------------------------------------------------------+
|
||||
void DrawMajorZones()
|
||||
{
|
||||
// رسم ناحیه کف ماژور (حمایت)
|
||||
if (majorLowBarIndex != -1)
|
||||
{
|
||||
if (ObjectFind(0, lowZoneName) < 0)
|
||||
{
|
||||
ObjectCreate(0, lowZoneName, OBJ_RECTANGLE, 0, 0, 0, 0, 0);
|
||||
ObjectSetInteger(0, lowZoneName, OBJPROP_COLOR, BullishZoneColor);
|
||||
ObjectSetInteger(0, lowZoneName, OBJPROP_FILL, true);
|
||||
ObjectSetInteger(0, lowZoneName, OBJPROP_BACK, true);
|
||||
}
|
||||
ObjectSetDouble(0, lowZoneName, OBJPROP_PRICE, 0, majorLowZoneHigh);
|
||||
ObjectSetDouble(0, lowZoneName, OBJPROP_PRICE, 1, majorLowZoneLow);
|
||||
ObjectSetInteger(0, lowZoneName, OBJPROP_TIME, 0, majorLowZoneTime);
|
||||
ObjectSetInteger(0, lowZoneName, OBJPROP_TIME, 1, TimeCurrent());
|
||||
}
|
||||
|
||||
// رسم ناحیه سقف ماژور (مقاومت)
|
||||
if (majorHighBarIndex != -1)
|
||||
{
|
||||
if (ObjectFind(0, highZoneName) < 0)
|
||||
{
|
||||
ObjectCreate(0, highZoneName, OBJ_RECTANGLE, 0, 0, 0, 0, 0);
|
||||
ObjectSetInteger(0, highZoneName, OBJPROP_COLOR, BearishZoneColor);
|
||||
ObjectSetInteger(0, highZoneName, OBJPROP_FILL, true);
|
||||
ObjectSetInteger(0, highZoneName, OBJPROP_BACK, true);
|
||||
}
|
||||
ObjectSetDouble(0, highZoneName, OBJPROP_PRICE, 0, majorHighZoneHigh);
|
||||
ObjectSetDouble(0, highZoneName, OBJPROP_PRICE, 1, majorHighZoneLow);
|
||||
ObjectSetInteger(0, highZoneName, OBJPROP_TIME, 0, majorHighZoneTime);
|
||||
ObjectSetInteger(0, highZoneName, OBJPROP_TIME, 1, TimeCurrent());
|
||||
}
|
||||
|
||||
ChartRedraw();
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| گسترش نواحی تا زمان حال (در هر تیک) |
|
||||
//+------------------------------------------------------------------+
|
||||
void ExtendZonesToCurrentTime()
|
||||
{
|
||||
if (majorLowBarIndex != -1)
|
||||
{
|
||||
ObjectSetInteger(0, lowZoneName, OBJPROP_TIME, 1, TimeCurrent());
|
||||
}
|
||||
if (majorHighBarIndex != -1)
|
||||
{
|
||||
ObjectSetInteger(0, highZoneName, OBJPROP_TIME, 1, TimeCurrent());
|
||||
}
|
||||
ChartRedraw();
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| نمایش داشبورد روی چارت |
|
||||
//+------------------------------------------------------------------+
|
||||
void DisplayDashboard()
|
||||
{
|
||||
string dash = "\n========== 4H MAJOR STRUCTURE (BOS) ==========\n";
|
||||
dash += "Symbol: " + _Symbol + " | Bias: " + biasDirection + "\n";
|
||||
dash += "--------------------------------------------------\n";
|
||||
|
||||
if (majorLowBarIndex != -1)
|
||||
{
|
||||
dash += "MAJOR LOW ZONE (Support):\n";
|
||||
dash += " High: " + DoubleToString(majorLowZoneHigh, _Digits) + "\n";
|
||||
dash += " Low: " + DoubleToString(majorLowZoneLow, _Digits) + "\n";
|
||||
dash += " Time: " + TimeToString(majorLowZoneTime, TIME_DATE | TIME_MINUTES) + "\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
dash += "MAJOR LOW ZONE: Not Found\n";
|
||||
}
|
||||
|
||||
dash += "--------------------------------------------------\n";
|
||||
|
||||
if (majorHighBarIndex != -1)
|
||||
{
|
||||
dash += "MAJOR HIGH ZONE (Resistance):\n";
|
||||
dash += " High: " + DoubleToString(majorHighZoneHigh, _Digits) + "\n";
|
||||
dash += " Low: " + DoubleToString(majorHighZoneLow, _Digits) + "\n";
|
||||
dash += " Time: " + TimeToString(majorHighZoneTime, TIME_DATE | TIME_MINUTES) + "\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
dash += "MAJOR HIGH ZONE: Not Found\n";
|
||||
}
|
||||
|
||||
dash += "==================================================\n";
|
||||
|
||||
Comment(dash);
|
||||
Print("Bias: ", biasDirection);
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
@@ -0,0 +1,226 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| MajorStructure_Bias_EA.mq5 |
|
||||
//| Step 1: 4H Major Structure & Bias Logic |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Professional Trader & AI Assistant"
|
||||
#property version "1.00"
|
||||
#property strict
|
||||
|
||||
//--- Input Parameters
|
||||
input int MajorSwingLookback = 5; // تعداد کندلهای چپ و راست برای تایید سوئینگ ماژور (در 4H = 5 یعنی 40 ساعت)
|
||||
input int MaxBarsToScan = 200; // حداکثر تعداد کندلهای 4H برای جستجو (برای بهینهسازی سرعت)
|
||||
|
||||
//--- Global Variables
|
||||
int lastMajorHighIndex = -1;
|
||||
int lastMajorLowIndex = -1;
|
||||
double lastMajorHighPrice = 0;
|
||||
double lastMajorLowPrice = 0;
|
||||
datetime lastMajorHighTime = 0;
|
||||
datetime lastMajorLowTime = 0;
|
||||
|
||||
string biasDirection = "NEUTRAL";
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert initialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
int OnInit()
|
||||
{
|
||||
Print("EA Initialized. Scanning 4H Chart for Major Structure...");
|
||||
return (INIT_SUCCEEDED);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert deinitialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnDeinit(const int reason)
|
||||
{
|
||||
Comment(""); // پاک کردن داشبورد هنگام حذف EA
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert tick function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnTick()
|
||||
{
|
||||
// فقط یک بار در هر کندل جدید 4 ساعته محاسبات را انجام میدهیم تا CPU درگیر نشود
|
||||
static datetime lastBarTime = 0;
|
||||
datetime currentBarTime = iTime(_Symbol, PERIOD_H4, 0);
|
||||
|
||||
if (lastBarTime == currentBarTime)
|
||||
return;
|
||||
lastBarTime = currentBarTime;
|
||||
|
||||
// 1. دریافت دادههای تایم فریم 4 ساعته
|
||||
double high[], low[];
|
||||
datetime time[];
|
||||
|
||||
ArraySetAsSeries(high, true);
|
||||
ArraySetAsSeries(low, true);
|
||||
ArraySetAsSeries(time, true);
|
||||
|
||||
int copiedH = CopyHigh(_Symbol, PERIOD_H4, 0, MaxBarsToScan, high);
|
||||
int copiedL = CopyLow(_Symbol, PERIOD_H4, 0, MaxBarsToScan, low);
|
||||
int copiedT = CopyTime(_Symbol, PERIOD_H4, 0, MaxBarsToScan, time);
|
||||
|
||||
if (copiedH <= 0 || copiedL <= 0 || copiedT <= 0)
|
||||
{
|
||||
Print("Error copying 4H data.");
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. پیدا کردن آخرین سقف ماژور
|
||||
lastMajorHighIndex = FindMajorSwing(high, true);
|
||||
if (lastMajorHighIndex != -1)
|
||||
{
|
||||
lastMajorHighPrice = high[lastMajorHighIndex];
|
||||
lastMajorHighTime = time[lastMajorHighIndex];
|
||||
}
|
||||
|
||||
// 3. پیدا کردن آخرین کف ماژور
|
||||
lastMajorLowIndex = FindMajorSwing(low, false);
|
||||
if (lastMajorLowIndex != -1)
|
||||
{
|
||||
lastMajorLowPrice = low[lastMajorLowIndex];
|
||||
lastMajorLowTime = time[lastMajorLowIndex];
|
||||
}
|
||||
|
||||
// 4. تعیین بایاس (جهت بازار) بر اساس قانون تازگی
|
||||
DetermineBias();
|
||||
|
||||
// 5. نمایش خروجی روی چارت (داشبورد)
|
||||
DisplayDashboard();
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| تابع پیدا کردن سوئینگ ماژور (سقف یا کف) |
|
||||
//+------------------------------------------------------------------+
|
||||
int FindMajorSwing(double &data[], bool isHigh)
|
||||
{
|
||||
// حلقه از کندلهای قدیمیتر به جدیدتر (اندیس بزرگتر به کوچکتر)
|
||||
// ما از MajorSwingLookback شروع میکنیم تا کندلهای کافی برای مقایسه داشته باشیم
|
||||
for (int i = MaxBarsToScan - MajorSwingLookback - 1; i >= MajorSwingLookback; i--)
|
||||
{
|
||||
bool isMajor = true;
|
||||
|
||||
// بررسی کندلهای سمت چپ (جدیدتر - اندیس کوچکتر)
|
||||
for (int j = 1; j <= MajorSwingLookback; j++)
|
||||
{
|
||||
if (isHigh)
|
||||
{
|
||||
if (data[i] <= data[i - j])
|
||||
{
|
||||
isMajor = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (data[i] >= data[i - j])
|
||||
{
|
||||
isMajor = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!isMajor)
|
||||
continue;
|
||||
|
||||
// بررسی کندلهای سمت راست (قدیمیتر - اندیس بزرگتر)
|
||||
for (int j = 1; j <= MajorSwingLookback; j++)
|
||||
{
|
||||
if (isHigh)
|
||||
{
|
||||
if (data[i] <= data[i + j])
|
||||
{
|
||||
isMajor = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (data[i] >= data[i + j])
|
||||
{
|
||||
isMajor = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// اگر هر دو شرط برقرار بود، این یک سوئینگ ماژور است
|
||||
if (isMajor)
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1; // پیدا نشد
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| تابع تعیین جهت بازار (Bias) |
|
||||
//+------------------------------------------------------------------+
|
||||
void DetermineBias()
|
||||
{
|
||||
if (lastMajorHighIndex == -1 || lastMajorLowIndex == -1)
|
||||
{
|
||||
biasDirection = "NEUTRAL (Data Not Found)";
|
||||
return;
|
||||
}
|
||||
|
||||
// در آرایههای سری، اندیس کوچکتر = زمان جدیدتر
|
||||
if (lastMajorHighIndex < lastMajorLowIndex)
|
||||
{
|
||||
// سقف ماژور جدیدتر از کف ماژور است -> بازار نزولی است
|
||||
biasDirection = "BEARISH (Short Only)";
|
||||
}
|
||||
else if (lastMajorLowIndex < lastMajorHighIndex)
|
||||
{
|
||||
// کف ماژور جدیدتر از سقف ماژور است -> بازار صعودی است
|
||||
biasDirection = "BULLISH (Long Only)";
|
||||
}
|
||||
else
|
||||
{
|
||||
biasDirection = "NEUTRAL (Equal Time)";
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| تابع نمایش داشبورد روی چارت |
|
||||
//+------------------------------------------------------------------+
|
||||
void DisplayDashboard()
|
||||
{
|
||||
string dash = "\n========== 4H MAJOR STRUCTURE ==========\n";
|
||||
dash += "Symbol: " + _Symbol + "\n";
|
||||
dash += "----------------------------------------\n";
|
||||
|
||||
if (lastMajorHighIndex != -1)
|
||||
{
|
||||
dash += "Last MAJOR HIGH: " + DoubleToString(lastMajorHighPrice, _Digits) + "\n";
|
||||
dash += "High Time: " + TimeToString(lastMajorHighTime, TIME_DATE | TIME_MINUTES) + "\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
dash += "Last MAJOR HIGH: Not Found\n";
|
||||
}
|
||||
|
||||
dash += "----------------------------------------\n";
|
||||
|
||||
if (lastMajorLowIndex != -1)
|
||||
{
|
||||
dash += "Last MAJOR LOW: " + DoubleToString(lastMajorLowPrice, _Digits) + "\n";
|
||||
dash += "Low Time: " + TimeToString(lastMajorLowTime, TIME_DATE | TIME_MINUTES) + "\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
dash += "Last MAJOR LOW: Not Found\n";
|
||||
}
|
||||
|
||||
dash += "========================================\n";
|
||||
dash += "CURRENT BIAS (Direction): " + biasDirection + "\n";
|
||||
dash += "========================================\n";
|
||||
|
||||
Comment(dash);
|
||||
|
||||
// چاپ در تب Experts برای لاگگیری
|
||||
Print("Bias Updated: ", biasDirection, " | High Index: ", lastMajorHighIndex, " | Low Index: ", lastMajorLowIndex);
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
@@ -0,0 +1,360 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| SmartShadowScanner.mq5 |
|
||||
//| Advanced Shadow Scanner with 3-Step Checklist Filter |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Professional Trader Setup 2026"
|
||||
#property version "3.00"
|
||||
#property description "Scans for large rejection shadows filtered by Sweep, POI, and Session"
|
||||
|
||||
//--- User Inputs
|
||||
input group "=== Base Shadow Settings ==="
|
||||
input double ShadowMultiplier = 2.0; // Shadow to Body Multiplier (e.g., 2.0 means shadow is 2x body)
|
||||
input double MinRangeATR_Multi = 0.5; // Min Candle Range as ATR Multiplier (0.5 = 50% of ATR)
|
||||
input int MinCandleSizePoints = 30; // Min Absolute Candle Size in Points (Filters micro-candles)
|
||||
input ENUM_TIMEFRAMES Timeframe = PERIOD_H4; // Timeframe to Analyze
|
||||
|
||||
input group "=== Filter 1: Liquidity Sweep ==="
|
||||
input bool EnableSweepFilter = true; // Enable Liquidity Sweep Filter
|
||||
input int SwingLookback = 10; // Swing Lookback Period (Left & Right candles)
|
||||
|
||||
input group "=== Filter 2: Point of Interest (POI) ==="
|
||||
input bool EnablePOIFilter = true; // Enable POI Filter
|
||||
input int POI_ProximityPoints = 50; // Allowed Distance to POI in Points
|
||||
input bool CheckPDH_PDL = true; // Check Previous Day High/Low
|
||||
input bool CheckOrderBlock = true; // Check Recent Order Blocks
|
||||
|
||||
input group "=== Filter 3: Trading Session ==="
|
||||
input bool EnableSessionFilter = true; // Enable Session Time Filter
|
||||
input int LondonStartHour = 8; // London Start Hour (Server Time)
|
||||
input int LondonEndHour = 12; // London End Hour (Server Time)
|
||||
input int NYStartHour = 13; // New York Start Hour (Server Time)
|
||||
input int NYEndHour = 18; // New York End Hour (Server Time)
|
||||
|
||||
input group "=== Display & Alert Settings ==="
|
||||
input bool DrawArrows = true; // Draw Arrows on Chart
|
||||
input bool ShowAlerts = true; // Show Popup Alerts
|
||||
input bool SendPush = false; // Send Push Notifications
|
||||
input color BullishArrowColor = clrLime; // Bullish Arrow Color (Lower Shadow)
|
||||
input color BearishArrowColor = clrRed; // Bearish Arrow Color (Upper Shadow)
|
||||
input int ArrowDistancePoints = 10; // Arrow Distance from Shadow Tip (Points)
|
||||
|
||||
//--- Global Variables
|
||||
datetime lastBarTime = 0;
|
||||
int atrHandle = INVALID_HANDLE;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert initialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
int OnInit()
|
||||
{
|
||||
// Initialize ATR indicator handle for volatility filtering
|
||||
atrHandle = iATR(_Symbol, Timeframe, 14);
|
||||
if(atrHandle == INVALID_HANDLE)
|
||||
{
|
||||
Print("Error creating ATR indicator handle!");
|
||||
return(INIT_FAILED);
|
||||
}
|
||||
|
||||
Print("=== Smart Shadow Scanner v3.0 Initialized ===");
|
||||
Print("Sweep Filter: ", EnableSweepFilter ? "Enabled" : "Disabled");
|
||||
Print("POI Filter: ", EnablePOIFilter ? "Enabled" : "Disabled");
|
||||
Print("Session Filter: ", EnableSessionFilter ? "Enabled" : "Disabled");
|
||||
return(INIT_SUCCEEDED);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert deinitialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnDeinit(const int reason)
|
||||
{
|
||||
// Clean up chart objects and release indicator memory
|
||||
ObjectsDeleteAll(0, "ShadowArrow_");
|
||||
IndicatorRelease(atrHandle);
|
||||
ChartRedraw();
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert tick function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnTick()
|
||||
{
|
||||
// Process only on new bar close to avoid redundant calculations
|
||||
datetime currentBarTime = iTime(_Symbol, Timeframe, 0);
|
||||
if(currentBarTime == lastBarTime) return;
|
||||
lastBarTime = currentBarTime;
|
||||
|
||||
// Copy required bars (Need enough history for Swing Lookback)
|
||||
int barsNeeded = SwingLookback * 2 + 10;
|
||||
MqlRates rates[];
|
||||
ArraySetAsSeries(rates, true);
|
||||
if(CopyRates(_Symbol, Timeframe, 0, barsNeeded, rates) < barsNeeded) return;
|
||||
|
||||
// Copy ATR value for the closed candle (index 1)
|
||||
double atrBuffer[];
|
||||
ArraySetAsSeries(atrBuffer, true);
|
||||
if(CopyBuffer(atrHandle, 0, 1, 1, atrBuffer) < 1) return;
|
||||
double currentATR = atrBuffer[0];
|
||||
|
||||
// Extract OHLC data for the target candle (index 1)
|
||||
double openPrice = rates[1].open;
|
||||
double highPrice = rates[1].high;
|
||||
double lowPrice = rates[1].low;
|
||||
double closePrice = rates[1].close;
|
||||
datetime barTime = rates[1].time;
|
||||
|
||||
// Calculate candle metrics
|
||||
double body = MathAbs(openPrice - closePrice);
|
||||
double upperShadow = highPrice - MathMax(openPrice, closePrice);
|
||||
double lowerShadow = MathMin(openPrice, closePrice) - lowPrice;
|
||||
double totalRange = highPrice - lowPrice;
|
||||
|
||||
// ==========================================
|
||||
// Base Filters (Volatility & Size)
|
||||
// ==========================================
|
||||
// Filter 1: Absolute minimum size (ignores dead-market micro candles)
|
||||
if(totalRange < MinCandleSizePoints * _Point) return;
|
||||
|
||||
// Filter 2: Dynamic ATR filter (candle must be at least X% of average range)
|
||||
if(totalRange < (currentATR * MinRangeATR_Multi)) return;
|
||||
|
||||
// Identify shadow type
|
||||
bool isBullishShadow = (lowerShadow >= (ShadowMultiplier * body) && lowerShadow > upperShadow);
|
||||
bool isBearishShadow = (upperShadow >= (ShadowMultiplier * body) && upperShadow > lowerShadow);
|
||||
|
||||
if(!isBullishShadow && !isBearishShadow) return;
|
||||
|
||||
// ==========================================
|
||||
// Advanced 3-Step Checklist
|
||||
// ==========================================
|
||||
bool passedSweep = !EnableSweepFilter || CheckLiquiditySweep(rates, isBullishShadow);
|
||||
bool passedPOI = !EnablePOIFilter || CheckNearPOI(rates, isBullishShadow, barTime);
|
||||
bool passedSession = !EnableSessionFilter || CheckSession(barTime);
|
||||
|
||||
// Trigger final signal only if ALL enabled filters are passed
|
||||
if(passedSweep && passedPOI && passedSession)
|
||||
{
|
||||
ProcessSignal(isBullishShadow, barTime, isBullishShadow ? lowPrice : highPrice);
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Filter 1: Check for Liquidity Sweep |
|
||||
//+------------------------------------------------------------------+
|
||||
bool CheckLiquiditySweep(const MqlRates &rates[], bool isBullish)
|
||||
{
|
||||
int targetIndex = 1; // Target candle is at index 1
|
||||
|
||||
if(isBullish) // For lower shadow, it must have swept a previous Swing Low
|
||||
{
|
||||
for(int i = targetIndex + 2; i < ArraySize(rates) - SwingLookback; i++)
|
||||
{
|
||||
if(IsSwingLow(rates, i, SwingLookback))
|
||||
{
|
||||
double swingLow = rates[i].low;
|
||||
// Sweep condition: Wick pierces the swing low, but body closes above it
|
||||
if(rates[targetIndex].low < swingLow && rates[targetIndex].close > swingLow)
|
||||
{
|
||||
return true; // Valid liquidity sweep detected
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else // For upper shadow, it must have swept a previous Swing High
|
||||
{
|
||||
for(int i = targetIndex + 2; i < ArraySize(rates) - SwingLookback; i++)
|
||||
{
|
||||
if(IsSwingHigh(rates, i, SwingLookback))
|
||||
{
|
||||
double swingHigh = rates[i].high;
|
||||
// Sweep condition: Wick pierces the swing high, but body closes below it
|
||||
if(rates[targetIndex].high > swingHigh && rates[targetIndex].close < swingHigh)
|
||||
{
|
||||
return true; // Valid liquidity sweep detected
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false; // No valid sweep found
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Identify Swing High |
|
||||
//+------------------------------------------------------------------+
|
||||
bool IsSwingHigh(const MqlRates &rates[], int index, int lookback)
|
||||
{
|
||||
if(index - lookback < 0 || index + lookback >= ArraySize(rates)) return false;
|
||||
|
||||
double high = rates[index].high;
|
||||
for(int i = 1; i <= lookback; i++)
|
||||
{
|
||||
if(rates[index - i].high >= high) return false;
|
||||
if(rates[index + i].high >= high) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Identify Swing Low |
|
||||
//+------------------------------------------------------------------+
|
||||
bool IsSwingLow(const MqlRates &rates[], int index, int lookback)
|
||||
{
|
||||
if(index - lookback < 0 || index + lookback >= ArraySize(rates)) return false;
|
||||
|
||||
double low = rates[index].low;
|
||||
for(int i = 1; i <= lookback; i++)
|
||||
{
|
||||
if(rates[index - i].low <= low) return false;
|
||||
if(rates[index + i].low <= low) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Filter 2: Check Proximity to Point of Interest (POI) |
|
||||
//+------------------------------------------------------------------+
|
||||
bool CheckNearPOI(const MqlRates &rates[], bool isBullish, datetime barTime)
|
||||
{
|
||||
double currentPrice = isBullish ? rates[1].low : rates[1].high;
|
||||
double proximity = POI_ProximityPoints * _Point;
|
||||
|
||||
// POI Type 1: Previous Day High / Previous Day Low (PDH/PDL)
|
||||
if(CheckPDH_PDL)
|
||||
{
|
||||
MqlRates dayRates[];
|
||||
ArraySetAsSeries(dayRates, true);
|
||||
|
||||
// Fetch previous daily candle data
|
||||
if(CopyRates(_Symbol, PERIOD_D1, 1, 1, dayRates) >= 1)
|
||||
{
|
||||
double pdh = dayRates[0].high;
|
||||
double pdl = dayRates[0].low;
|
||||
|
||||
if(isBullish && MathAbs(currentPrice - pdl) <= proximity) return true; // Near PDL
|
||||
if(!isBullish && MathAbs(currentPrice - pdh) <= proximity) return true; // Near PDH
|
||||
}
|
||||
}
|
||||
|
||||
// POI Type 2: Recent Order Blocks (OB)
|
||||
if(CheckOrderBlock)
|
||||
{
|
||||
// Scan the last 20 candles for Order Blocks
|
||||
for(int i = 2; i < MathMin(20, ArraySize(rates) - 1); i++)
|
||||
{
|
||||
// Bullish OB: Bearish candle followed by a strong bullish displacement
|
||||
if(isBullish && IsBullishOrderBlock(rates, i))
|
||||
{
|
||||
double obLow = rates[i].low;
|
||||
double obHigh = rates[i].high;
|
||||
if(currentPrice >= (obLow - proximity) && currentPrice <= (obHigh + proximity))
|
||||
return true;
|
||||
}
|
||||
|
||||
// Bearish OB: Bullish candle followed by a strong bearish displacement
|
||||
if(!isBullish && IsBearishOrderBlock(rates, i))
|
||||
{
|
||||
double obLow = rates[i].low;
|
||||
double obHigh = rates[i].high;
|
||||
if(currentPrice >= (obLow - proximity) && currentPrice <= (obHigh + proximity))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false; // Not near any valid POI
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Identify Bullish Order Block |
|
||||
//+------------------------------------------------------------------+
|
||||
bool IsBullishOrderBlock(const MqlRates &rates[], int index)
|
||||
{
|
||||
// Current candle must be bearish
|
||||
if(rates[index].close >= rates[index].open) return false;
|
||||
|
||||
if(index - 1 < 0) return false;
|
||||
|
||||
// Next candle (index-1) must be a strong bullish displacement
|
||||
double nextBody = MathAbs(rates[index-1].close - rates[index-1].open);
|
||||
double nextRange = rates[index-1].high - rates[index-1].low;
|
||||
|
||||
if(rates[index-1].close <= rates[index-1].open) return false; // Must be bullish
|
||||
if(nextBody < (nextRange * 0.6)) return false; // Body must be at least 60% of total range
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Identify Bearish Order Block |
|
||||
//+------------------------------------------------------------------+
|
||||
bool IsBearishOrderBlock(const MqlRates &rates[], int index)
|
||||
{
|
||||
// Current candle must be bullish
|
||||
if(rates[index].close <= rates[index].open) return false;
|
||||
|
||||
if(index - 1 < 0) return false;
|
||||
|
||||
// Next candle (index-1) must be a strong bearish displacement
|
||||
double nextBody = MathAbs(rates[index-1].close - rates[index-1].open);
|
||||
double nextRange = rates[index-1].high - rates[index-1].low;
|
||||
|
||||
if(rates[index-1].close >= rates[index-1].open) return false; // Must be bearish
|
||||
if(nextBody < (nextRange * 0.6)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Filter 3: Check Trading Session Time |
|
||||
//+------------------------------------------------------------------+
|
||||
bool CheckSession(datetime barTime)
|
||||
{
|
||||
MqlDateTime dt;
|
||||
TimeToStruct(barTime, dt);
|
||||
int hour = dt.hour;
|
||||
|
||||
// London Session
|
||||
if(hour >= LondonStartHour && hour < LondonEndHour) return true;
|
||||
|
||||
// New York Session
|
||||
if(hour >= NYStartHour && hour < NYEndHour) return true;
|
||||
|
||||
return false; // Outside active sessions (e.g., Asian session)
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Process Final Signal (Draw, Alert, Push) |
|
||||
//+------------------------------------------------------------------+
|
||||
void ProcessSignal(bool isBullish, datetime barTime, double extremePrice)
|
||||
{
|
||||
string direction = isBullish ? "BULLISH" : "BEARISH";
|
||||
string message = StringFormat("✅ Golden %s Signal | %s | Price: %s",
|
||||
direction,
|
||||
TimeToString(barTime, TIME_DATE|TIME_MINUTES),
|
||||
DoubleToString(extremePrice, _Digits));
|
||||
|
||||
// 1. Draw Arrow on Chart
|
||||
if(DrawArrows)
|
||||
{
|
||||
string objName = "ShadowArrow_" + IntegerToString(barTime);
|
||||
int arrowCode = isBullish ? 233 : 234; // 233: Up arrow, 234: Down arrow
|
||||
color arrowColor = isBullish ? BullishArrowColor : BearishArrowColor;
|
||||
double arrowPrice = isBullish ?
|
||||
(extremePrice - ArrowDistancePoints * _Point) :
|
||||
(extremePrice + ArrowDistancePoints * _Point);
|
||||
|
||||
ObjectCreate(0, objName, OBJ_ARROW, 0, barTime, arrowPrice);
|
||||
ObjectSetInteger(0, objName, OBJPROP_ARROWCODE, arrowCode);
|
||||
ObjectSetInteger(0, objName, OBJPROP_COLOR, arrowColor);
|
||||
ObjectSetInteger(0, objName, OBJPROP_WIDTH, 3);
|
||||
ObjectSetInteger(0, objName, OBJPROP_ANCHOR, isBullish ? ANCHOR_TOP : ANCHOR_BOTTOM);
|
||||
}
|
||||
|
||||
// 2. Trigger Alerts
|
||||
if(ShowAlerts) Alert(message);
|
||||
if(SendPush) SendNotification(message);
|
||||
|
||||
// 3. Print to Experts Log
|
||||
Print(message);
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
Reference in New Issue
Block a user