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,839 @@
|
|||||||
|
//+------------------------------------------------------------------+
|
||||||
|
//| CompleteStructure_MinorZones_EA.mq5 |
|
||||||
|
//| Major Zones (4H) + Minor Zones (15m) with Full Validation |
|
||||||
|
//+------------------------------------------------------------------+
|
||||||
|
#property copyright "Professional Trader & AI Assistant"
|
||||||
|
#property version "4.00"
|
||||||
|
#property strict
|
||||||
|
|
||||||
|
//--- Input Parameters - Major Structure (4H)
|
||||||
|
input int MajorPivotLookback = 3;
|
||||||
|
input int MaxBarsToScan = 500;
|
||||||
|
input color BullishZoneColor = clrLime;
|
||||||
|
input color BearishZoneColor = clrRed;
|
||||||
|
input color InvalidZoneColor = clrGray;
|
||||||
|
|
||||||
|
//--- Input Parameters - Minor Structure (15m)
|
||||||
|
input int MinorWickLookback = 2;
|
||||||
|
input double WickToBodyRatio = 2.0;
|
||||||
|
input color MinorHighZoneColor = clrOrange;
|
||||||
|
input color MinorLowZoneColor = clrDodgerBlue;
|
||||||
|
input color MinorInvalidColor = clrSilver;
|
||||||
|
input color EquilibriumColor = clrYellow;
|
||||||
|
|
||||||
|
//--- ساختار داده برای ماژور
|
||||||
|
struct MajorZone
|
||||||
|
{
|
||||||
|
double zoneHigh;
|
||||||
|
double zoneLow;
|
||||||
|
datetime zoneTime;
|
||||||
|
int barIndex;
|
||||||
|
bool isValid;
|
||||||
|
};
|
||||||
|
|
||||||
|
//--- ساختار داده برای مینور
|
||||||
|
struct MinorZone
|
||||||
|
{
|
||||||
|
double zoneHigh;
|
||||||
|
double zoneLow;
|
||||||
|
datetime zoneTime;
|
||||||
|
int barIndex;
|
||||||
|
bool isValid;
|
||||||
|
bool isLongSignal; // true = سیگنال لانگ (کف مینور), false = سیگنال شورت (سقف مینور)
|
||||||
|
};
|
||||||
|
|
||||||
|
//--- آرایههای ماژور
|
||||||
|
MajorZone majorLows[];
|
||||||
|
MajorZone majorHighs[];
|
||||||
|
int currentLowIndex = -1;
|
||||||
|
int currentHighIndex = -1;
|
||||||
|
|
||||||
|
//--- آرایههای مینور
|
||||||
|
MinorZone minorLows[];
|
||||||
|
MinorZone minorHighs[];
|
||||||
|
int currentMinorLowIndex = -1;
|
||||||
|
int currentMinorHighIndex = -1;
|
||||||
|
|
||||||
|
//--- متغیرهای بایاس و نواحی
|
||||||
|
string biasDirection = "NEUTRAL";
|
||||||
|
double equilibriumPrice = 0;
|
||||||
|
double premiumZone = 0;
|
||||||
|
double discountZone = 0;
|
||||||
|
bool structureValid = false;
|
||||||
|
|
||||||
|
//--- پیشوند آبجکتها
|
||||||
|
string mzPrefix = "MZ_";
|
||||||
|
string qzPrefix = "QZ_";
|
||||||
|
string mnPrefix = "MN_";
|
||||||
|
|
||||||
|
//+------------------------------------------------------------------+
|
||||||
|
int OnInit()
|
||||||
|
{
|
||||||
|
ArrayResize(majorLows, 0);
|
||||||
|
ArrayResize(majorHighs, 0);
|
||||||
|
ArrayResize(minorLows, 0);
|
||||||
|
ArrayResize(minorHighs, 0);
|
||||||
|
Print("EA v4.0 Initialized. Major + Minor Zones with Full Validation.");
|
||||||
|
return (INIT_SUCCEEDED);
|
||||||
|
}
|
||||||
|
|
||||||
|
//+------------------------------------------------------------------+
|
||||||
|
void OnDeinit(const int reason)
|
||||||
|
{
|
||||||
|
ObjectsDeleteAll(0, mzPrefix);
|
||||||
|
ObjectsDeleteAll(0, qzPrefix);
|
||||||
|
ObjectsDeleteAll(0, mnPrefix);
|
||||||
|
Comment("");
|
||||||
|
}
|
||||||
|
|
||||||
|
//+------------------------------------------------------------------+
|
||||||
|
void OnTick()
|
||||||
|
{
|
||||||
|
// 1. دریافت دادههای 4H
|
||||||
|
double high4H[], low4H[], open4H[], close4H[];
|
||||||
|
datetime time4H[];
|
||||||
|
|
||||||
|
ArraySetAsSeries(high4H, true);
|
||||||
|
ArraySetAsSeries(low4H, true);
|
||||||
|
ArraySetAsSeries(open4H, true);
|
||||||
|
ArraySetAsSeries(close4H, true);
|
||||||
|
ArraySetAsSeries(time4H, true);
|
||||||
|
|
||||||
|
int copied4H = CopyHigh(_Symbol, PERIOD_H4, 0, MaxBarsToScan, high4H);
|
||||||
|
CopyLow(_Symbol, PERIOD_H4, 0, MaxBarsToScan, low4H);
|
||||||
|
CopyOpen(_Symbol, PERIOD_H4, 0, MaxBarsToScan, open4H);
|
||||||
|
CopyClose(_Symbol, PERIOD_H4, 0, MaxBarsToScan, close4H);
|
||||||
|
CopyTime(_Symbol, PERIOD_H4, 0, MaxBarsToScan, time4H);
|
||||||
|
|
||||||
|
if (copied4H <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// 2. بررسی بیاعتباری ماژورهای جاری
|
||||||
|
CheckCurrentZonesInvalidation(close4H);
|
||||||
|
|
||||||
|
// 3. بهروزرسانی ماژورها (فقط در کندل جدید 4H)
|
||||||
|
static datetime lastBarTime4H = 0;
|
||||||
|
datetime currentBarTime4H = time4H[0];
|
||||||
|
|
||||||
|
if (lastBarTime4H != currentBarTime4H)
|
||||||
|
{
|
||||||
|
lastBarTime4H = currentBarTime4H;
|
||||||
|
UpdateCurrentZones(high4H, low4H, open4H, close4H, time4H, copied4H);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. اعتبارسنجی ساختاری و بایاس
|
||||||
|
ValidateStructure();
|
||||||
|
DetermineBias();
|
||||||
|
|
||||||
|
// 5. رسم ماژورها
|
||||||
|
DrawAllMajorZones();
|
||||||
|
|
||||||
|
// 6. اگر ساختار معتبر است، مینورها را پردازش کن
|
||||||
|
if (structureValid)
|
||||||
|
{
|
||||||
|
CalculateQuartileZones();
|
||||||
|
DrawQuartileZones();
|
||||||
|
ProcessMinorZones();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7. داشبورد
|
||||||
|
DisplayDashboard();
|
||||||
|
}
|
||||||
|
|
||||||
|
//+------------------------------------------------------------------+
|
||||||
|
//| بررسی بیاعتباری ماژورهای جاری |
|
||||||
|
//+------------------------------------------------------------------+
|
||||||
|
void CheckCurrentZonesInvalidation(double &close4H[])
|
||||||
|
{
|
||||||
|
if (currentLowIndex >= 0 && currentLowIndex < ArraySize(majorLows))
|
||||||
|
{
|
||||||
|
if (majorLows[currentLowIndex].isValid)
|
||||||
|
{
|
||||||
|
for (int i = 0; i <= majorLows[currentLowIndex].barIndex; i++)
|
||||||
|
{
|
||||||
|
if (close4H[i] < majorLows[currentLowIndex].zoneLow)
|
||||||
|
{
|
||||||
|
majorLows[currentLowIndex].isValid = false;
|
||||||
|
Print("Major Low INVALIDATED at index ", currentLowIndex);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentHighIndex >= 0 && currentHighIndex < ArraySize(majorHighs))
|
||||||
|
{
|
||||||
|
if (majorHighs[currentHighIndex].isValid)
|
||||||
|
{
|
||||||
|
for (int i = 0; i <= majorHighs[currentHighIndex].barIndex; i++)
|
||||||
|
{
|
||||||
|
if (close4H[i] > majorHighs[currentHighIndex].zoneHigh)
|
||||||
|
{
|
||||||
|
majorHighs[currentHighIndex].isValid = false;
|
||||||
|
Print("Major High INVALIDATED at index ", currentHighIndex);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//+------------------------------------------------------------------+
|
||||||
|
//| بهروزرسانی ماژورها |
|
||||||
|
//+------------------------------------------------------------------+
|
||||||
|
void UpdateCurrentZones(double &high4H[], double &low4H[], double &open4H[], double &close4H[], datetime &time4H[], int total)
|
||||||
|
{
|
||||||
|
// مدیریت کفها
|
||||||
|
bool needNewLow = (currentLowIndex == -1 || !majorLows[currentLowIndex].isValid);
|
||||||
|
if (needNewLow)
|
||||||
|
{
|
||||||
|
bool foundPrevious = false;
|
||||||
|
for (int i = ArraySize(majorLows) - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
if (majorLows[i].isValid)
|
||||||
|
{
|
||||||
|
currentLowIndex = i;
|
||||||
|
foundPrevious = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!foundPrevious)
|
||||||
|
{
|
||||||
|
MajorZone newLow;
|
||||||
|
if (FindNewMajorLow(high4H, low4H, open4H, close4H, time4H, total, newLow))
|
||||||
|
{
|
||||||
|
int newSize = ArraySize(majorLows);
|
||||||
|
ArrayResize(majorLows, newSize + 1);
|
||||||
|
majorLows[newSize] = newLow;
|
||||||
|
currentLowIndex = newSize;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
currentLowIndex = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// مدیریت سقفها
|
||||||
|
bool needNewHigh = (currentHighIndex == -1 || !majorHighs[currentHighIndex].isValid);
|
||||||
|
if (needNewHigh)
|
||||||
|
{
|
||||||
|
bool foundPrevious = false;
|
||||||
|
for (int i = ArraySize(majorHighs) - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
if (majorHighs[i].isValid)
|
||||||
|
{
|
||||||
|
currentHighIndex = i;
|
||||||
|
foundPrevious = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!foundPrevious)
|
||||||
|
{
|
||||||
|
MajorZone newHigh;
|
||||||
|
if (FindNewMajorHigh(high4H, low4H, open4H, close4H, time4H, total, newHigh))
|
||||||
|
{
|
||||||
|
int newSize = ArraySize(majorHighs);
|
||||||
|
ArrayResize(majorHighs, newSize + 1);
|
||||||
|
majorHighs[newSize] = newHigh;
|
||||||
|
currentHighIndex = newSize;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
currentHighIndex = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//+------------------------------------------------------------------+
|
||||||
|
bool FindNewMajorLow(double &high[], double &low[], double &open[], double &close[], datetime &time[], int total, MajorZone &outZone)
|
||||||
|
{
|
||||||
|
int lastMinorHighIdx = -1;
|
||||||
|
int lastMinorLowIdx = -1;
|
||||||
|
|
||||||
|
for (int i = total - MajorPivotLookback - 1; i >= MajorPivotLookback; i--)
|
||||||
|
{
|
||||||
|
if (IsPivot(high, i, true, total, MajorPivotLookback))
|
||||||
|
lastMinorHighIdx = i;
|
||||||
|
if (IsPivot(low, i, false, total, MajorPivotLookback))
|
||||||
|
lastMinorLowIdx = i;
|
||||||
|
|
||||||
|
if (lastMinorHighIdx != -1 && i < lastMinorHighIdx && close[i] > high[lastMinorHighIdx])
|
||||||
|
{
|
||||||
|
if (lastMinorLowIdx != -1 && lastMinorLowIdx > lastMinorHighIdx)
|
||||||
|
{
|
||||||
|
bool stillValid = true;
|
||||||
|
for (int j = 0; j <= i; j++)
|
||||||
|
{
|
||||||
|
if (close[j] < low[lastMinorLowIdx])
|
||||||
|
{
|
||||||
|
stillValid = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stillValid)
|
||||||
|
{
|
||||||
|
outZone.zoneLow = low[lastMinorLowIdx];
|
||||||
|
outZone.zoneHigh = open[lastMinorLowIdx];
|
||||||
|
outZone.zoneTime = time[lastMinorLowIdx];
|
||||||
|
outZone.barIndex = lastMinorLowIdx;
|
||||||
|
outZone.isValid = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//+------------------------------------------------------------------+
|
||||||
|
bool FindNewMajorHigh(double &high[], double &low[], double &open[], double &close[], datetime &time[], int total, MajorZone &outZone)
|
||||||
|
{
|
||||||
|
int lastMinorHighIdx = -1;
|
||||||
|
int lastMinorLowIdx = -1;
|
||||||
|
|
||||||
|
for (int i = total - MajorPivotLookback - 1; i >= MajorPivotLookback; i--)
|
||||||
|
{
|
||||||
|
if (IsPivot(high, i, true, total, MajorPivotLookback))
|
||||||
|
lastMinorHighIdx = i;
|
||||||
|
if (IsPivot(low, i, false, total, MajorPivotLookback))
|
||||||
|
lastMinorLowIdx = i;
|
||||||
|
|
||||||
|
if (lastMinorLowIdx != -1 && i < lastMinorLowIdx && close[i] < low[lastMinorLowIdx])
|
||||||
|
{
|
||||||
|
if (lastMinorHighIdx != -1 && lastMinorHighIdx > lastMinorLowIdx)
|
||||||
|
{
|
||||||
|
bool stillValid = true;
|
||||||
|
for (int j = 0; j <= i; j++)
|
||||||
|
{
|
||||||
|
if (close[j] > high[lastMinorHighIdx])
|
||||||
|
{
|
||||||
|
stillValid = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stillValid)
|
||||||
|
{
|
||||||
|
outZone.zoneHigh = high[lastMinorHighIdx];
|
||||||
|
outZone.zoneLow = open[lastMinorHighIdx];
|
||||||
|
outZone.zoneTime = time[lastMinorHighIdx];
|
||||||
|
outZone.barIndex = lastMinorHighIdx;
|
||||||
|
outZone.isValid = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//+------------------------------------------------------------------+
|
||||||
|
bool IsPivot(double &data[], int idx, bool isHigh, int total, int lookback)
|
||||||
|
{
|
||||||
|
for (int j = 1; j <= lookback; j++)
|
||||||
|
{
|
||||||
|
if (idx - j < 0 || idx + j >= total)
|
||||||
|
continue;
|
||||||
|
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 ValidateStructure()
|
||||||
|
{
|
||||||
|
structureValid = false;
|
||||||
|
|
||||||
|
if (currentLowIndex >= 0 && currentHighIndex >= 0)
|
||||||
|
{
|
||||||
|
if (majorLows[currentLowIndex].isValid && majorHighs[currentHighIndex].isValid)
|
||||||
|
{
|
||||||
|
if (majorHighs[currentHighIndex].zoneHigh > majorLows[currentLowIndex].zoneHigh)
|
||||||
|
{
|
||||||
|
structureValid = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
majorLows[currentLowIndex].isValid = false;
|
||||||
|
majorHighs[currentHighIndex].isValid = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//+------------------------------------------------------------------+
|
||||||
|
void DetermineBias()
|
||||||
|
{
|
||||||
|
if (!structureValid)
|
||||||
|
{
|
||||||
|
biasDirection = "NEUTRAL";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (majorHighs[currentHighIndex].barIndex < majorLows[currentLowIndex].barIndex)
|
||||||
|
{
|
||||||
|
biasDirection = "BEARISH";
|
||||||
|
}
|
||||||
|
else if (majorLows[currentLowIndex].barIndex < majorHighs[currentHighIndex].barIndex)
|
||||||
|
{
|
||||||
|
biasDirection = "BULLISH";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
biasDirection = "NEUTRAL";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//+------------------------------------------------------------------+
|
||||||
|
void DrawAllMajorZones()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < ArraySize(majorLows); i++)
|
||||||
|
{
|
||||||
|
string objName = mzPrefix + "Low_" + IntegerToString(i);
|
||||||
|
|
||||||
|
if (ObjectFind(0, objName) < 0)
|
||||||
|
{
|
||||||
|
ObjectCreate(0, objName, OBJ_RECTANGLE, 0, 0, 0, 0, 0);
|
||||||
|
ObjectSetInteger(0, objName, OBJPROP_FILL, true);
|
||||||
|
ObjectSetInteger(0, objName, OBJPROP_BACK, true);
|
||||||
|
ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
color zoneColor = majorLows[i].isValid ? BullishZoneColor : InvalidZoneColor;
|
||||||
|
ObjectSetInteger(0, objName, OBJPROP_COLOR, zoneColor);
|
||||||
|
ObjectSetDouble(0, objName, OBJPROP_PRICE, 0, majorLows[i].zoneHigh);
|
||||||
|
ObjectSetDouble(0, objName, OBJPROP_PRICE, 1, majorLows[i].zoneLow);
|
||||||
|
ObjectSetInteger(0, objName, OBJPROP_TIME, 0, majorLows[i].zoneTime);
|
||||||
|
ObjectSetInteger(0, objName, OBJPROP_TIME, 1, TimeCurrent());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < ArraySize(majorHighs); i++)
|
||||||
|
{
|
||||||
|
string objName = mzPrefix + "High_" + IntegerToString(i);
|
||||||
|
|
||||||
|
if (ObjectFind(0, objName) < 0)
|
||||||
|
{
|
||||||
|
ObjectCreate(0, objName, OBJ_RECTANGLE, 0, 0, 0, 0, 0);
|
||||||
|
ObjectSetInteger(0, objName, OBJPROP_FILL, true);
|
||||||
|
ObjectSetInteger(0, objName, OBJPROP_BACK, true);
|
||||||
|
ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
color zoneColor = majorHighs[i].isValid ? BearishZoneColor : InvalidZoneColor;
|
||||||
|
ObjectSetInteger(0, objName, OBJPROP_COLOR, zoneColor);
|
||||||
|
ObjectSetDouble(0, objName, OBJPROP_PRICE, 0, majorHighs[i].zoneHigh);
|
||||||
|
ObjectSetDouble(0, objName, OBJPROP_PRICE, 1, majorHighs[i].zoneLow);
|
||||||
|
ObjectSetInteger(0, objName, OBJPROP_TIME, 0, majorHighs[i].zoneTime);
|
||||||
|
ObjectSetInteger(0, objName, OBJPROP_TIME, 1, TimeCurrent());
|
||||||
|
}
|
||||||
|
|
||||||
|
ChartRedraw();
|
||||||
|
}
|
||||||
|
|
||||||
|
//+------------------------------------------------------------------+
|
||||||
|
void CalculateQuartileZones()
|
||||||
|
{
|
||||||
|
double majorHigh = majorHighs[currentHighIndex].zoneHigh;
|
||||||
|
double majorLow = majorLows[currentLowIndex].zoneLow;
|
||||||
|
double range = majorHigh - majorLow;
|
||||||
|
|
||||||
|
equilibriumPrice = majorLow + (range * 0.50);
|
||||||
|
premiumZone = majorLow + (range * 0.75);
|
||||||
|
discountZone = majorLow + (range * 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
|
//+------------------------------------------------------------------+
|
||||||
|
void DrawQuartileZones()
|
||||||
|
{
|
||||||
|
string eqName = qzPrefix + "Equilibrium";
|
||||||
|
if (ObjectFind(0, eqName) < 0)
|
||||||
|
{
|
||||||
|
ObjectCreate(0, eqName, OBJ_HLINE, 0, 0, equilibriumPrice);
|
||||||
|
ObjectSetInteger(0, eqName, OBJPROP_COLOR, EquilibriumColor);
|
||||||
|
ObjectSetInteger(0, eqName, OBJPROP_STYLE, STYLE_DASHDOT);
|
||||||
|
ObjectSetInteger(0, eqName, OBJPROP_WIDTH, 2);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ObjectSetDouble(0, eqName, OBJPROP_PRICE, 0, equilibriumPrice);
|
||||||
|
}
|
||||||
|
|
||||||
|
string topName = qzPrefix + "Top25";
|
||||||
|
if (ObjectFind(0, topName) < 0)
|
||||||
|
{
|
||||||
|
ObjectCreate(0, topName, OBJ_HLINE, 0, 0, premiumZone);
|
||||||
|
ObjectSetInteger(0, topName, OBJPROP_COLOR, clrDarkRed);
|
||||||
|
ObjectSetInteger(0, topName, OBJPROP_STYLE, STYLE_DOT);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ObjectSetDouble(0, topName, OBJPROP_PRICE, 0, premiumZone);
|
||||||
|
}
|
||||||
|
|
||||||
|
string botName = qzPrefix + "Bottom25";
|
||||||
|
if (ObjectFind(0, botName) < 0)
|
||||||
|
{
|
||||||
|
ObjectCreate(0, botName, OBJ_HLINE, 0, 0, discountZone);
|
||||||
|
ObjectSetInteger(0, botName, OBJPROP_COLOR, clrDarkGreen);
|
||||||
|
ObjectSetInteger(0, botName, OBJPROP_STYLE, STYLE_DOT);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ObjectSetDouble(0, botName, OBJPROP_PRICE, 0, discountZone);
|
||||||
|
}
|
||||||
|
|
||||||
|
ChartRedraw();
|
||||||
|
}
|
||||||
|
|
||||||
|
//+------------------------------------------------------------------+
|
||||||
|
//| پردازش مینورها: بررسی اعتبار + جستجوی جدید |
|
||||||
|
//+------------------------------------------------------------------+
|
||||||
|
void ProcessMinorZones()
|
||||||
|
{
|
||||||
|
// 1. دریافت دادههای 15 دقیقه
|
||||||
|
double high15m[], low15m[], open15m[], close15m[];
|
||||||
|
datetime time15m[];
|
||||||
|
|
||||||
|
ArraySetAsSeries(high15m, true);
|
||||||
|
ArraySetAsSeries(low15m, true);
|
||||||
|
ArraySetAsSeries(open15m, true);
|
||||||
|
ArraySetAsSeries(close15m, true);
|
||||||
|
ArraySetAsSeries(time15m, true);
|
||||||
|
|
||||||
|
int copied15m = CopyHigh(_Symbol, PERIOD_M15, 0, 500, high15m);
|
||||||
|
CopyLow(_Symbol, PERIOD_M15, 0, 500, low15m);
|
||||||
|
CopyOpen(_Symbol, PERIOD_M15, 0, 500, open15m);
|
||||||
|
CopyClose(_Symbol, PERIOD_M15, 0, 500, close15m);
|
||||||
|
CopyTime(_Symbol, PERIOD_M15, 0, 500, time15m);
|
||||||
|
|
||||||
|
if (copied15m <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// 2. بررسی بیاعتباری مینورهای جاری
|
||||||
|
CheckMinorZonesInvalidation(close15m);
|
||||||
|
|
||||||
|
// 3. بهروزرسانی مینورها (در هر تیک، چون 15 دقیقه سریعتر است)
|
||||||
|
UpdateCurrentMinorZones(high15m, low15m, open15m, close15m, time15m, copied15m);
|
||||||
|
|
||||||
|
// 4. رسم تمام مینورها
|
||||||
|
DrawAllMinorZones();
|
||||||
|
}
|
||||||
|
|
||||||
|
//+------------------------------------------------------------------+
|
||||||
|
//| بررسی بیاعتباری مینورهای جاری با Close کندل 15 دقیقه |
|
||||||
|
//+------------------------------------------------------------------+
|
||||||
|
void CheckMinorZonesInvalidation(double &close15m[])
|
||||||
|
{
|
||||||
|
// بررسی کف مینور جاری
|
||||||
|
if (currentMinorLowIndex >= 0 && currentMinorLowIndex < ArraySize(minorLows))
|
||||||
|
{
|
||||||
|
if (minorLows[currentMinorLowIndex].isValid)
|
||||||
|
{
|
||||||
|
for (int i = 0; i <= minorLows[currentMinorLowIndex].barIndex; i++)
|
||||||
|
{
|
||||||
|
if (close15m[i] < minorLows[currentMinorLowIndex].zoneLow)
|
||||||
|
{
|
||||||
|
minorLows[currentMinorLowIndex].isValid = false;
|
||||||
|
Print("Minor Low INVALIDATED at index ", currentMinorLowIndex);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// بررسی سقف مینور جاری
|
||||||
|
if (currentMinorHighIndex >= 0 && currentMinorHighIndex < ArraySize(minorHighs))
|
||||||
|
{
|
||||||
|
if (minorHighs[currentMinorHighIndex].isValid)
|
||||||
|
{
|
||||||
|
for (int i = 0; i <= minorHighs[currentMinorHighIndex].barIndex; i++)
|
||||||
|
{
|
||||||
|
if (close15m[i] > minorHighs[currentMinorHighIndex].zoneHigh)
|
||||||
|
{
|
||||||
|
minorHighs[currentMinorHighIndex].isValid = false;
|
||||||
|
Print("Minor High INVALIDATED at index ", currentMinorHighIndex);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//+------------------------------------------------------------------+
|
||||||
|
//| بهروزرسانی مینورها |
|
||||||
|
//+------------------------------------------------------------------+
|
||||||
|
void UpdateCurrentMinorZones(double &high15m[], double &low15m[], double &open15m[], double &close15m[], datetime &time15m[], int total)
|
||||||
|
{
|
||||||
|
// مدیریت کفهای مینور (فقط در بایاس صعودی)
|
||||||
|
if (biasDirection == "BULLISH")
|
||||||
|
{
|
||||||
|
bool needNewMinorLow = (currentMinorLowIndex == -1 || !minorLows[currentMinorLowIndex].isValid);
|
||||||
|
if (needNewMinorLow)
|
||||||
|
{
|
||||||
|
bool foundPrevious = false;
|
||||||
|
for (int i = ArraySize(minorLows) - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
if (minorLows[i].isValid)
|
||||||
|
{
|
||||||
|
currentMinorLowIndex = i;
|
||||||
|
foundPrevious = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!foundPrevious)
|
||||||
|
{
|
||||||
|
MinorZone newMinorLow;
|
||||||
|
if (FindNewMinorLow(high15m, low15m, open15m, close15m, time15m, total, newMinorLow))
|
||||||
|
{
|
||||||
|
int newSize = ArraySize(minorLows);
|
||||||
|
ArrayResize(minorLows, newSize + 1);
|
||||||
|
minorLows[newSize] = newMinorLow;
|
||||||
|
currentMinorLowIndex = newSize;
|
||||||
|
Print("New Minor Low Zone found at index: ", newSize);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
currentMinorLowIndex = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// مدیریت سقفهای مینور (فقط در بایاس نزولی)
|
||||||
|
if (biasDirection == "BEARISH")
|
||||||
|
{
|
||||||
|
bool needNewMinorHigh = (currentMinorHighIndex == -1 || !minorHighs[currentMinorHighIndex].isValid);
|
||||||
|
if (needNewMinorHigh)
|
||||||
|
{
|
||||||
|
bool foundPrevious = false;
|
||||||
|
for (int i = ArraySize(minorHighs) - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
if (minorHighs[i].isValid)
|
||||||
|
{
|
||||||
|
currentMinorHighIndex = i;
|
||||||
|
foundPrevious = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!foundPrevious)
|
||||||
|
{
|
||||||
|
MinorZone newMinorHigh;
|
||||||
|
if (FindNewMinorHigh(high15m, low15m, open15m, close15m, time15m, total, newMinorHigh))
|
||||||
|
{
|
||||||
|
int newSize = ArraySize(minorHighs);
|
||||||
|
ArrayResize(minorHighs, newSize + 1);
|
||||||
|
minorHighs[newSize] = newMinorHigh;
|
||||||
|
currentMinorHighIndex = newSize;
|
||||||
|
Print("New Minor High Zone found at index: ", newSize);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
currentMinorHighIndex = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//+------------------------------------------------------------------+
|
||||||
|
//| پیدا کردن کف مینور جدید (ناحیه Wick-to-Body در Discount) |
|
||||||
|
//+------------------------------------------------------------------+
|
||||||
|
bool FindNewMinorLow(double &high[], double &low[], double &open[], double &close[], datetime &time[], int total, MinorZone &outZone)
|
||||||
|
{
|
||||||
|
for (int i = 1; i < total - MinorWickLookback; i++)
|
||||||
|
{
|
||||||
|
double bodyTop = MathMax(open[i], close[i]);
|
||||||
|
double bodyBot = MathMin(open[i], close[i]);
|
||||||
|
double bodySize = bodyTop - bodyBot;
|
||||||
|
|
||||||
|
double lowerWick = bodyBot - low[i];
|
||||||
|
|
||||||
|
// شرط 1: شدوی پایینی بلند (حداقل 2 برابر بدنه)
|
||||||
|
// شرط 2: نوک شدو در ناحیه Discount (زیر 50%)
|
||||||
|
// شرط 3: بدنه بالای 50% بسته شده
|
||||||
|
if (lowerWick > 0 && bodySize > 0 && (lowerWick / bodySize) >= WickToBodyRatio)
|
||||||
|
{
|
||||||
|
if (low[i] < equilibriumPrice && bodyBot > equilibriumPrice)
|
||||||
|
{
|
||||||
|
// بررسی اعتبار: آیا از زمان تشکیل تا الان شکسته شده؟
|
||||||
|
bool stillValid = true;
|
||||||
|
for (int j = 0; j <= i; j++)
|
||||||
|
{
|
||||||
|
if (close[j] < low[i])
|
||||||
|
{
|
||||||
|
stillValid = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stillValid)
|
||||||
|
{
|
||||||
|
outZone.zoneLow = low[i]; // نوک شدو
|
||||||
|
outZone.zoneHigh = open[i]; // ابتدای بدنه (Wick-to-Body)
|
||||||
|
outZone.zoneTime = time[i];
|
||||||
|
outZone.barIndex = i;
|
||||||
|
outZone.isValid = true;
|
||||||
|
outZone.isLongSignal = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//+------------------------------------------------------------------+
|
||||||
|
//| پیدا کردن سقف مینور جدید (ناحیه Wick-to-Body در Premium) |
|
||||||
|
//+------------------------------------------------------------------+
|
||||||
|
bool FindNewMinorHigh(double &high[], double &low[], double &open[], double &close[], datetime &time[], int total, MinorZone &outZone)
|
||||||
|
{
|
||||||
|
for (int i = 1; i < total - MinorWickLookback; i++)
|
||||||
|
{
|
||||||
|
double bodyTop = MathMax(open[i], close[i]);
|
||||||
|
double bodyBot = MathMin(open[i], close[i]);
|
||||||
|
double bodySize = bodyTop - bodyBot;
|
||||||
|
|
||||||
|
double upperWick = high[i] - bodyTop;
|
||||||
|
|
||||||
|
// شرط 1: شدوی بالایی بلند
|
||||||
|
// شرط 2: نوک شدو در ناحیه Premium (بالای 50%)
|
||||||
|
// شرط 3: بدنه زیر 50% بسته شده
|
||||||
|
if (upperWick > 0 && bodySize > 0 && (upperWick / bodySize) >= WickToBodyRatio)
|
||||||
|
{
|
||||||
|
if (high[i] > equilibriumPrice && bodyTop < equilibriumPrice)
|
||||||
|
{
|
||||||
|
bool stillValid = true;
|
||||||
|
for (int j = 0; j <= i; j++)
|
||||||
|
{
|
||||||
|
if (close[j] > high[i])
|
||||||
|
{
|
||||||
|
stillValid = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stillValid)
|
||||||
|
{
|
||||||
|
outZone.zoneHigh = high[i]; // نوک شدو
|
||||||
|
outZone.zoneLow = open[i]; // ابتدای بدنه (Wick-to-Body)
|
||||||
|
outZone.zoneTime = time[i];
|
||||||
|
outZone.barIndex = i;
|
||||||
|
outZone.isValid = true;
|
||||||
|
outZone.isLongSignal = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//+------------------------------------------------------------------+
|
||||||
|
//| رسم تمام مینورها (معتبر و نامعتبر) |
|
||||||
|
//+------------------------------------------------------------------+
|
||||||
|
void DrawAllMinorZones()
|
||||||
|
{
|
||||||
|
// رسم کفهای مینور
|
||||||
|
for (int i = 0; i < ArraySize(minorLows); i++)
|
||||||
|
{
|
||||||
|
string objName = mnPrefix + "Low_" + IntegerToString(i);
|
||||||
|
|
||||||
|
if (ObjectFind(0, objName) < 0)
|
||||||
|
{
|
||||||
|
ObjectCreate(0, objName, OBJ_RECTANGLE, 0, 0, 0, 0, 0);
|
||||||
|
ObjectSetInteger(0, objName, OBJPROP_FILL, true);
|
||||||
|
ObjectSetInteger(0, objName, OBJPROP_BACK, true);
|
||||||
|
ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
color zoneColor = minorLows[i].isValid ? MinorLowZoneColor : MinorInvalidColor;
|
||||||
|
ObjectSetInteger(0, objName, OBJPROP_COLOR, zoneColor);
|
||||||
|
ObjectSetDouble(0, objName, OBJPROP_PRICE, 0, minorLows[i].zoneHigh);
|
||||||
|
ObjectSetDouble(0, objName, OBJPROP_PRICE, 1, minorLows[i].zoneLow);
|
||||||
|
ObjectSetInteger(0, objName, OBJPROP_TIME, 0, minorLows[i].zoneTime);
|
||||||
|
ObjectSetInteger(0, objName, OBJPROP_TIME, 1, TimeCurrent());
|
||||||
|
}
|
||||||
|
|
||||||
|
// رسم سقفهای مینور
|
||||||
|
for (int i = 0; i < ArraySize(minorHighs); i++)
|
||||||
|
{
|
||||||
|
string objName = mnPrefix + "High_" + IntegerToString(i);
|
||||||
|
|
||||||
|
if (ObjectFind(0, objName) < 0)
|
||||||
|
{
|
||||||
|
ObjectCreate(0, objName, OBJ_RECTANGLE, 0, 0, 0, 0, 0);
|
||||||
|
ObjectSetInteger(0, objName, OBJPROP_FILL, true);
|
||||||
|
ObjectSetInteger(0, objName, OBJPROP_BACK, true);
|
||||||
|
ObjectSetInteger(0, objName, OBJPROP_SELECTABLE, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
color zoneColor = minorHighs[i].isValid ? MinorHighZoneColor : MinorInvalidColor;
|
||||||
|
ObjectSetInteger(0, objName, OBJPROP_COLOR, zoneColor);
|
||||||
|
ObjectSetDouble(0, objName, OBJPROP_PRICE, 0, minorHighs[i].zoneHigh);
|
||||||
|
ObjectSetDouble(0, objName, OBJPROP_PRICE, 1, minorHighs[i].zoneLow);
|
||||||
|
ObjectSetInteger(0, objName, OBJPROP_TIME, 0, minorHighs[i].zoneTime);
|
||||||
|
ObjectSetInteger(0, objName, OBJPROP_TIME, 1, TimeCurrent());
|
||||||
|
}
|
||||||
|
|
||||||
|
ChartRedraw();
|
||||||
|
}
|
||||||
|
|
||||||
|
//+------------------------------------------------------------------+
|
||||||
|
void DisplayDashboard()
|
||||||
|
{
|
||||||
|
string dash = "\n========== COMPLETE STRUCTURE (v4.0) ==========\n";
|
||||||
|
dash += "Symbol: " + _Symbol + "\n";
|
||||||
|
dash += "------------------------------------------------------\n";
|
||||||
|
|
||||||
|
if (structureValid)
|
||||||
|
{
|
||||||
|
dash += "MAJOR HIGH: " + DoubleToString(majorHighs[currentHighIndex].zoneHigh, _Digits) + "\n";
|
||||||
|
dash += "MAJOR LOW: " + DoubleToString(majorLows[currentLowIndex].zoneLow, _Digits) + "\n";
|
||||||
|
dash += "------------------------------------------------------\n";
|
||||||
|
dash += "Equilibrium (50%): " + DoubleToString(equilibriumPrice, _Digits) + "\n";
|
||||||
|
dash += "Premium (75%): " + DoubleToString(premiumZone, _Digits) + "\n";
|
||||||
|
dash += "Discount (25%): " + DoubleToString(discountZone, _Digits) + "\n";
|
||||||
|
dash += "------------------------------------------------------\n";
|
||||||
|
dash += "BIAS: " + biasDirection + "\n";
|
||||||
|
dash += "------------------------------------------------------\n";
|
||||||
|
|
||||||
|
if (currentMinorLowIndex >= 0)
|
||||||
|
{
|
||||||
|
dash += "Current Minor Low: " + (minorLows[currentMinorLowIndex].isValid ? "✓ VALID" : " INVALID") + "\n";
|
||||||
|
dash += " Zone: " + DoubleToString(minorLows[currentMinorLowIndex].zoneLow, _Digits) +
|
||||||
|
" - " + DoubleToString(minorLows[currentMinorLowIndex].zoneHigh, _Digits) + "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentMinorHighIndex >= 0)
|
||||||
|
{
|
||||||
|
dash += "Current Minor High: " + (minorHighs[currentMinorHighIndex].isValid ? "✓ VALID" : "✗ INVALID") + "\n";
|
||||||
|
dash += " Zone: " + DoubleToString(minorHighs[currentMinorHighIndex].zoneLow, _Digits) +
|
||||||
|
" - " + DoubleToString(minorHighs[currentMinorHighIndex].zoneHigh, _Digits) + "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
dash += "Structure: INVALID - Waiting for valid Major Zones\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
dash += "======================================================\n";
|
||||||
|
|
||||||
|
Comment(dash);
|
||||||
|
}
|
||||||
|
//+------------------------------------------------------------------+
|
||||||
Reference in New Issue
Block a user