Files
MQL5Data/Experts/MajorStructure_Bias_EA.mq5
2026-07-12 21:26:53 +03:30

839 lines
29 KiB
Plaintext

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