mql5/Experts/Advisors/ExpertPivotFractal.mq5

2190 lines
63 KiB
MQL5
Raw Permalink Normal View History

2026-04-11 00:22:38 +03:00
//+------------------------------------------------------------------+
2026-04-18 22:25:20 +03:00
//| Pivot + Fractals EA v2.0 (single TF engine) |
2026-04-11 00:22:38 +03:00
//+------------------------------------------------------------------+
#property strict
#include <Trade\Trade.mqh>
#include <Trade\OrderInfo.mqh>
#include <Trade\PositionInfo.mqh>
#include <Trade\HistoryOrderInfo.mqh>
#include <Arrays\ArrayLong.mqh>
#include <Arrays\ArrayDatetime.mqh>
2026-04-18 22:25:20 +03:00
CTrade trade;
CPositionInfo position;
COrderInfo order;
CArrayLong DistTickets;
CArrayDatetime DistStartTimes;
2026-04-11 00:22:38 +03:00
2026-04-18 22:25:20 +03:00
//--- inputs
2026-04-11 00:22:38 +03:00
input double Lots = 0.01;
input int Slippage = 20;
input int Magic = 260319;
input int BB_Period = 20;
input double BB_Deviation = 2.0;
2026-04-18 22:25:20 +03:00
input bool ShowVisual = true;
string VIS_PREFIX = "PFV_";
//--- индикаторы (единый движок)
int handleFractals_M = INVALID_HANDLE;
int handleFractals_H = INVALID_HANDLE;
int handleBB = INVALID_HANDLE;
int handleAO_M = INVALID_HANDLE; // быстрый AO (tfM)
int handleAO_H = INVALID_HANDLE; // HTF AO (tfH)
int handleATR_M = INVALID_HANDLE;
int handleATRSlow = INVALID_HANDLE;
int handleMA_H = INVALID_HANDLE;
int ATR_Period = 14;
int ATR_Slow_Period = 14;
//--- TF
int tfM = PERIOD_M5;
int tfH = PERIOD_H1;
int pending_tfM = PERIOD_M5;
int pending_tfH = PERIOD_H1;
//--- время баров
datetime lastBarTimeMinutes = 0;
datetime lastHTFBar = 0;
datetime lastExitTime = 0;
datetime aoFlipTime = 0;
datetime lastTFUpdate = 0;
2026-04-11 00:22:38 +03:00
2026-04-18 22:25:20 +03:00
//--- состояние сигнала
double lastEntry = 0.0;
double lastStop = 0.0;
double lastTp = 0.0;
int lastTrend = 0;
int trendAtPlacement = 0;
double lastPivot = 0.0;
double lastLot = 0.0;
2026-04-11 00:22:38 +03:00
2026-04-18 22:25:20 +03:00
//--- AO flip state
int aoFlipDirection = 0;
2026-04-11 00:22:38 +03:00
2026-04-18 22:25:20 +03:00
//--- AutoMinStopEngine
double extraEffMinStop = 0.0;
2026-04-11 00:22:38 +03:00
2026-04-18 22:25:20 +03:00
//--- флаги
bool canPlaceNewOrder = true;
2026-04-11 00:22:38 +03:00
2026-04-18 22:25:20 +03:00
//--- лог
int logHandle = INVALID_HANDLE;
string logFileName = "";
int logCurrentDate = 0;
2026-04-11 00:22:38 +03:00
2026-04-18 22:25:20 +03:00
bool htfReady = false;
ulong htfInitTime = 0;
bool mtfReady = false;
ulong mtfInitTime = 0;
2026-04-11 00:22:38 +03:00
2026-04-25 22:32:05 +03:00
//--- фрактальные буферы (глобальные)
double upHTF[]; // фракталы вверх
double dnHTF[]; // фракталы вниз
2026-04-11 00:22:38 +03:00
//+------------------------------------------------------------------+
2026-04-18 22:25:20 +03:00
//| Вспомогательные: TF → строка |
2026-04-11 00:22:38 +03:00
//+------------------------------------------------------------------+
string TimeframeToString(int tf)
2026-05-03 17:59:01 +03:00
{
2026-04-11 00:22:38 +03:00
switch(tf)
2026-05-03 17:59:01 +03:00
{
case PERIOD_M1:
return "M1";
case PERIOD_M5:
return "M5";
case PERIOD_M15:
return "M15";
case PERIOD_M30:
return "M30";
case PERIOD_H1:
return "H1";
case PERIOD_H4:
return "H4";
case PERIOD_D1:
return "D1";
case PERIOD_W1:
return "W1";
case PERIOD_MN1:
return "MN1";
}
2026-04-11 00:22:38 +03:00
return IntegerToString(tf);
2026-05-03 17:59:01 +03:00
}
2026-04-11 00:22:38 +03:00
//+------------------------------------------------------------------+
//| Логирование |
//+------------------------------------------------------------------+
void Log(string level, string msg)
2026-05-03 17:59:01 +03:00
{
2026-04-11 00:22:38 +03:00
if(logHandle == INVALID_HANDLE)
return;
string timeStr = TimeToString(TimeCurrent(), TIME_SECONDS);
string line = "[" + timeStr + "] [" + level + "] " + msg;
FileSeek(logHandle, 0, SEEK_END);
FileWrite(logHandle, line);
2026-05-03 17:59:01 +03:00
}
2026-04-11 00:22:38 +03:00
2026-05-03 17:59:01 +03:00
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
2026-04-11 00:22:38 +03:00
bool InitLog()
2026-05-03 17:59:01 +03:00
{
2026-04-11 00:22:38 +03:00
datetime now = TimeCurrent();
MqlDateTime dt;
TimeToStruct(now, dt);
int today = dt.year*10000 + dt.mon*100 + dt.day;
logCurrentDate = today;
string todayStr = IntegerToString(dt.year) + "-"
2026-05-03 17:59:01 +03:00
+ StringFormat("%02d", dt.mon) + "-"
+ StringFormat("%02d", dt.day);
2026-04-11 00:22:38 +03:00
logFileName = "EA_" + _Symbol + "_" + todayStr + ".log";
datetime y = now - 86400;
MqlDateTime dty;
TimeToStruct(y, dty);
string yStr = IntegerToString(dty.year) + "-"
2026-05-03 17:59:01 +03:00
+ StringFormat("%02d", dty.mon) + "-"
+ StringFormat("%02d", dty.day);
2026-04-11 00:22:38 +03:00
string yFile = "EA_" + _Symbol + "_" + yStr + ".log";
FileDelete(yFile);
logHandle = FileOpen(logFileName, FILE_WRITE|FILE_READ|FILE_TXT|FILE_COMMON);
if(logHandle == INVALID_HANDLE)
2026-05-03 17:59:01 +03:00
{
2026-04-11 00:22:38 +03:00
Print("❌ Cannot open log file: ", logFileName, " err=", GetLastError());
return(false);
2026-05-03 17:59:01 +03:00
}
2026-04-11 00:22:38 +03:00
FileSeek(logHandle, 0, SEEK_END);
Log("INFO", "Log started for symbol " + _Symbol);
return(true);
2026-05-03 17:59:01 +03:00
}
2026-04-11 00:22:38 +03:00
//+------------------------------------------------------------------+
2026-04-18 22:25:20 +03:00
//| Dynamic timeframe selection |
2026-04-11 00:22:38 +03:00
//+------------------------------------------------------------------+
int GetDynamicTF(double relSpread)
2026-05-03 17:59:01 +03:00
{
if(relSpread < 0.0001)
return PERIOD_M5;
if(relSpread < 0.0003)
return PERIOD_M15;
if(relSpread < 0.0008)
return PERIOD_M30;
if(relSpread < 0.0020)
return PERIOD_H1;
if(relSpread < 0.0050)
return PERIOD_H4;
if(relSpread < 0.0100)
return PERIOD_D1;
2026-04-11 00:22:38 +03:00
return PERIOD_W1;
2026-05-03 17:59:01 +03:00
}
2026-04-11 00:22:38 +03:00
2026-05-03 17:59:01 +03:00
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
2026-04-11 00:22:38 +03:00
int GetDynamicHTF(double relSpread)
2026-05-03 17:59:01 +03:00
{
if(relSpread < 0.0001)
return PERIOD_H1;
if(relSpread < 0.0003)
return PERIOD_H1;
if(relSpread < 0.0008)
return PERIOD_H4;
if(relSpread < 0.0020)
return PERIOD_D1;
if(relSpread < 0.0050)
return PERIOD_D1;
2026-04-11 00:22:38 +03:00
return PERIOD_W1;
2026-05-03 17:59:01 +03:00
}
2026-04-11 00:22:38 +03:00
//+------------------------------------------------------------------+
2026-04-18 22:25:20 +03:00
//| Единый TF/индикатор движок |
//+------------------------------------------------------------------+
void UpdateDynamicTF(double relSpread)
2026-05-03 17:59:01 +03:00
{
pending_tfM = GetDynamicTF(relSpread);
pending_tfH = GetDynamicHTF(relSpread);
}
2026-04-18 22:25:20 +03:00
//+------------------------------------------------------------------+
//| GetTrendAndPivot на HTF |
//+------------------------------------------------------------------+
int GetTrendAndPivot(double &pivot)
2026-05-03 17:59:01 +03:00
{
// Проверяем валидность handle
2026-04-18 22:25:20 +03:00
if(handleAO_H == INVALID_HANDLE || handleMA_H == INVALID_HANDLE)
2026-05-03 17:59:01 +03:00
{
2026-04-18 22:25:20 +03:00
Print("⛔ HTF handles invalid");
return 0;
2026-05-03 17:59:01 +03:00
}
2026-04-18 22:25:20 +03:00
2026-05-03 17:59:01 +03:00
// Проверяем готовность буферов
2026-04-18 22:25:20 +03:00
double aoTest[3];
double maTest[1];
int aoCount = CopyBuffer(handleAO_H, 0, 1, 3, aoTest);
int maCount = CopyBuffer(handleMA_H, 0, 2, 1, maTest);
if(aoCount < 3 || maCount < 1)
2026-05-03 17:59:01 +03:00
{
2026-04-18 22:25:20 +03:00
PrintFormat("⛔ HTF buffers not ready (ao=%d ma=%d)", aoCount, maCount);
return 0;
2026-05-03 17:59:01 +03:00
}
2026-04-18 22:25:20 +03:00
2026-05-03 17:59:01 +03:00
// Теперь индикаторы готовы — читаем данные
2026-04-18 22:25:20 +03:00
double h1 = iHigh(_Symbol, (ENUM_TIMEFRAMES)tfH, 2);
double h2 = iHigh(_Symbol, (ENUM_TIMEFRAMES)tfH, 3);
2026-05-03 17:59:01 +03:00
double l1 = iLow(_Symbol, (ENUM_TIMEFRAMES)tfH, 2);
double l2 = iLow(_Symbol, (ENUM_TIMEFRAMES)tfH, 3);
2026-04-18 22:25:20 +03:00
double ao0 = aoTest[0];
double ao1 = aoTest[1];
double ma = maTest[0];
double closePrev = iClose(_Symbol, (ENUM_TIMEFRAMES)tfH, 2);
bool upStruct = (h1 > h2 && l1 > l2);
bool downStruct = (h1 < h2 && l1 < l2);
bool aoUp = (ao0 > ao1);
bool aoDown = (ao0 < ao1);
bool maUp = (closePrev > ma);
bool maDown = (closePrev < ma);
pivot = (h1 + l1 + closePrev) / 3.0;
PrintFormat("TREND DEBUG: h1=%.5f h2=%.5f l1=%.5f l2=%.5f | upStruct=%d downStruct=%d | ao0=%.5f ao1=%.5f aoUp=%d aoDown=%d | ma=%.5f closePrev=%.5f maUp=%d maDown=%d | pivot=%.5f",
h1,h2,l1,l2, upStruct,downStruct, ao0,ao1, aoUp,aoDown, ma,closePrev, maUp,maDown, pivot);
2026-05-03 17:59:01 +03:00
// Строгий тренд-фильтр
if(upStruct && aoUp && maUp)
return +1;
if(downStruct && aoDown && maDown)
return -1;
2026-04-18 22:25:20 +03:00
int prevTrend = lastTrend;
int newTrend = 0;
2026-05-03 17:59:01 +03:00
if(upStruct && aoUp && maUp)
newTrend = +1;
else
if(downStruct && aoDown && maDown)
newTrend = -1;
else
newTrend = 0;
2026-04-18 22:25:20 +03:00
if(newTrend != prevTrend)
2026-05-03 17:59:01 +03:00
{
2026-04-18 22:25:20 +03:00
ExplainTrendChange(prevTrend, newTrend,
2026-05-03 17:59:01 +03:00
upStruct, downStruct,
ao0, ao1,
closePrev, ma);
}
2026-04-18 22:25:20 +03:00
return newTrend;
2026-05-03 17:59:01 +03:00
}
2026-04-18 22:25:20 +03:00
2026-05-03 17:59:01 +03:00
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
2026-04-18 22:25:20 +03:00
void ResetInternalState()
2026-05-03 17:59:01 +03:00
{
2026-04-18 22:25:20 +03:00
ClearVisualObjects();
lastEntry = 0;
lastStop = 0;
lastTp = 0;
canPlaceNewOrder = true;
2026-05-03 17:59:01 +03:00
}
2026-04-18 22:25:20 +03:00
2026-05-03 17:59:01 +03:00
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
2026-04-18 22:25:20 +03:00
void ResetSignalState()
2026-05-03 17:59:01 +03:00
{
// Закрываем позиции по символу
2026-04-18 22:25:20 +03:00
int total = (int)PositionsTotal();
for(int i = total - 1; i >= 0; i--)
2026-05-03 17:59:01 +03:00
{
2026-04-18 22:25:20 +03:00
if(position.SelectByIndex(i))
2026-05-03 17:59:01 +03:00
{
2026-04-18 22:25:20 +03:00
if(position.Symbol() == _Symbol)
2026-05-03 17:59:01 +03:00
{
2026-04-18 22:25:20 +03:00
ulong ticket = position.Ticket();
trade.PositionClose(ticket);
2026-05-03 17:59:01 +03:00
}
}
}
2026-04-18 22:25:20 +03:00
2026-05-03 17:59:01 +03:00
// Удаляем pending ордера по символу
2026-04-18 22:25:20 +03:00
int orders = (int)OrdersTotal();
for(int i = orders - 1; i >= 0; i--)
2026-05-03 17:59:01 +03:00
{
2026-04-18 22:25:20 +03:00
if(order.SelectByIndex(i))
2026-05-03 17:59:01 +03:00
{
2026-04-18 22:25:20 +03:00
if(order.Symbol() == _Symbol)
2026-05-03 17:59:01 +03:00
{
2026-04-18 22:25:20 +03:00
ulong ticket = order.Ticket();
trade.OrderDelete(ticket);
2026-05-03 17:59:01 +03:00
}
}
}
2026-04-18 22:25:20 +03:00
Print("🔁 Reset signal state (no position, no pending)");
2026-05-03 17:59:01 +03:00
}
2026-04-18 22:25:20 +03:00
//+------------------------------------------------------------------+
//| Visual helpers (pivot, levels, trail) |
//+------------------------------------------------------------------+
void ClearVisualObjects()
2026-05-03 17:59:01 +03:00
{
if(!ShowVisual)
return;
2026-04-18 22:25:20 +03:00
int total = (int)ObjectsTotal(0, 0, -1);
for(int i = total - 1; i >= 0; i--)
2026-05-03 17:59:01 +03:00
{
2026-04-18 22:25:20 +03:00
string name = ObjectName(0, i, 0, -1);
if(StringFind(name, VIS_PREFIX) == 0)
ObjectDelete(0, name);
2026-05-03 17:59:01 +03:00
}
}
2026-04-18 22:25:20 +03:00
2026-05-03 17:59:01 +03:00
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
2026-04-18 22:25:20 +03:00
void DrawTrailSL(ulong ticket, double oldSL, double newSL)
2026-05-03 17:59:01 +03:00
{
if(!ShowVisual)
return;
2026-04-18 22:25:20 +03:00
datetime t = TimeCurrent();
string base = IntegerToString((int)ticket) + "_" + IntegerToString((int)t);
string lineName = VIS_PREFIX + "TRAIL_LINE_" + base;
ObjectCreate(0, lineName, OBJ_TREND, 0, t, oldSL, t, newSL);
ObjectSetInteger(0, lineName, OBJPROP_COLOR, clrGold);
ObjectSetInteger(0, lineName, OBJPROP_STYLE, STYLE_DOT);
string txtName = VIS_PREFIX + "TRAIL_TEXT_" + base;
ObjectCreate(0, txtName, OBJ_TEXT, 0, t, newSL);
ObjectSetString(0, txtName, OBJPROP_TEXT, "TRAIL SL");
ObjectSetInteger(0, txtName, OBJPROP_COLOR, clrGold);
ObjectSetInteger(0, txtName, OBJPROP_FONTSIZE, 8);
2026-05-03 17:59:01 +03:00
}
2026-04-18 22:25:20 +03:00
//+------------------------------------------------------------------+
//| AutoMinStopEngine |
2026-04-11 00:22:38 +03:00
//+------------------------------------------------------------------+
double AMSE_BaseMinStop()
2026-05-03 17:59:01 +03:00
{
2026-04-11 00:22:38 +03:00
long stopLevelPoints = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL);
double baseMinStop = (stopLevelPoints + 3) * _Point;
return(baseMinStop);
2026-05-03 17:59:01 +03:00
}
2026-04-11 00:22:38 +03:00
2026-05-03 17:59:01 +03:00
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
2026-04-11 00:22:38 +03:00
double AMSE_ProfileMin()
2026-05-03 17:59:01 +03:00
{
2026-04-11 00:22:38 +03:00
double profileMin = 0.0;
2026-05-03 17:59:01 +03:00
if(StringFind(_Symbol, "EURUSD") >= 0)
profileMin = 10 * _Point;
if(StringFind(_Symbol, "EURUSD.r")>= 0)
profileMin = 10 * _Point;
if(StringFind(_Symbol, "GBPUSD") >= 0)
profileMin = 12 * _Point;
if(StringFind(_Symbol, "GBPUSD.r")>= 0)
profileMin = 12 * _Point;
if(StringFind(_Symbol, "USDJPY") >= 0)
profileMin = 0.01;
if(StringFind(_Symbol, "USDJPY.r")>= 0)
profileMin = 0.01;
if(StringFind(_Symbol, "USDCHF") >= 0)
profileMin = 10 * _Point;
if(StringFind(_Symbol, "USDCHF.r")>= 0)
profileMin = 10 * _Point;
if(StringFind(_Symbol, "USDCAD") >= 0)
profileMin = 12 * _Point;
if(StringFind(_Symbol, "USDCAD.r")>= 0)
profileMin = 12 * _Point;
if(StringFind(_Symbol, "AUDUSD") >= 0)
profileMin = 10 * _Point;
if(StringFind(_Symbol, "AUDUSD.r")>= 0)
profileMin = 10 * _Point;
if(StringFind(_Symbol, "NZDUSD") >= 0)
profileMin = 10 * _Point;
if(StringFind(_Symbol, "NZDUSD.r")>= 0)
profileMin = 10 * _Point;
if(StringFind(_Symbol, "EURGBP") >= 0)
profileMin = 10 * _Point;
if(StringFind(_Symbol, "EURGBP.r")>= 0)
profileMin = 10 * _Point;
if(StringFind(_Symbol, "EURJPY") >= 0)
profileMin = 0.01;
if(StringFind(_Symbol, "EURJPY.r")>= 0)
profileMin = 0.01;
if(StringFind(_Symbol, "GBPJPY") >= 0)
profileMin = 0.01;
if(StringFind(_Symbol, "GBPJPY.r")>= 0)
profileMin = 0.01;
if(StringFind(_Symbol, "AUDJPY") >= 0)
profileMin = 0.01;
if(StringFind(_Symbol, "AUDJPY.r")>= 0)
profileMin = 0.01;
if(StringFind(_Symbol, "CADJPY") >= 0)
profileMin = 0.01;
if(StringFind(_Symbol, "CADJPY.r")>= 0)
profileMin = 0.01;
if(StringFind(_Symbol, "CHFJPY") >= 0)
profileMin = 0.01;
if(StringFind(_Symbol, "CHFJPY.r")>= 0)
profileMin = 0.01;
if(StringFind(_Symbol, "EURAUD") >= 0)
profileMin = 15 * _Point;
if(StringFind(_Symbol, "EURAUD.r")>= 0)
profileMin = 15 * _Point;
if(StringFind(_Symbol, "EURNZD") >= 0)
profileMin = 15 * _Point;
if(StringFind(_Symbol, "EURNZD.r")>= 0)
profileMin = 15 * _Point;
if(StringFind(_Symbol, "GBPAUD") >= 0)
profileMin = 20 * _Point;
if(StringFind(_Symbol, "GBPAUD.r")>= 0)
profileMin = 20 * _Point;
if(StringFind(_Symbol, "GBPNZD") >= 0)
profileMin = 20 * _Point;
if(StringFind(_Symbol, "GBPNZD.r")>= 0)
profileMin = 20 * _Point;
if(StringFind(_Symbol, "BTCUSD") >= 0)
profileMin = 50 * _Point;
if(StringFind(_Symbol, "BTCUSD.r")>= 0)
profileMin = 50 * _Point;
if(StringFind(_Symbol, "ETHUSD") >= 0)
profileMin = 20 * _Point;
if(StringFind(_Symbol, "ETHUSD.r")>= 0)
profileMin = 20 * _Point;
2026-04-11 00:22:38 +03:00
2026-04-18 22:25:20 +03:00
return(profileMin);
2026-05-03 17:59:01 +03:00
}
2026-04-11 00:22:38 +03:00
2026-05-03 17:59:01 +03:00
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
2026-04-11 00:22:38 +03:00
double AMSE_VolatilityMin()
2026-05-03 17:59:01 +03:00
{
2026-04-18 22:25:20 +03:00
if(handleATR_M == INVALID_HANDLE)
2026-04-11 00:22:38 +03:00
return 0.0;
double atrBuffer[];
2026-04-18 22:25:20 +03:00
if(CopyBuffer(handleATR_M, 0, 0, 1, atrBuffer) <= 0)
2026-04-11 00:22:38 +03:00
return 0.0;
double atr = atrBuffer[0];
if(atr <= 0.0)
return 0.0;
return atr * 0.5;
2026-05-03 17:59:01 +03:00
}
2026-04-11 00:22:38 +03:00
2026-05-03 17:59:01 +03:00
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
2026-04-11 00:22:38 +03:00
double AMSE_GetEffMinStop()
2026-05-03 17:59:01 +03:00
{
2026-04-11 00:22:38 +03:00
double baseMinStop = AMSE_BaseMinStop();
double profileMin = AMSE_ProfileMin();
double volaMin = AMSE_VolatilityMin();
double effMinStop = baseMinStop;
effMinStop = MathMax(effMinStop, baseMinStop + extraEffMinStop);
if(profileMin > 0.0)
effMinStop = MathMax(effMinStop, profileMin);
if(volaMin > 0.0)
effMinStop = MathMax(effMinStop, volaMin);
double stopLevel = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL) * _Point;
effMinStop = MathMax(effMinStop, stopLevel);
effMinStop = MathMax(effMinStop, 10 * _Point);
return(effMinStop);
2026-05-03 17:59:01 +03:00
}
2026-04-11 00:22:38 +03:00
//+------------------------------------------------------------------+
2026-04-18 22:25:20 +03:00
//| AO / выход по AO |
2026-04-11 00:22:38 +03:00
//+------------------------------------------------------------------+
2026-04-18 22:25:20 +03:00
int GetAOColor()
2026-05-03 17:59:01 +03:00
{
2026-04-18 22:25:20 +03:00
double ao0 = iAO(_Symbol, PERIOD_CURRENT);
double ao1 = iAO(_Symbol, PERIOD_CURRENT);
2026-04-11 00:22:38 +03:00
2026-05-03 17:59:01 +03:00
if(ao0 > ao1)
return +1;
if(ao0 < ao1)
return -1;
2026-04-18 22:25:20 +03:00
return 0;
2026-05-03 17:59:01 +03:00
}
2026-04-11 00:22:38 +03:00
2026-05-03 17:59:01 +03:00
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
2026-04-18 22:25:20 +03:00
ulong GetMyPosition()
2026-05-03 17:59:01 +03:00
{
2026-04-18 22:25:20 +03:00
int total = PositionsTotal();
2026-04-11 00:22:38 +03:00
2026-04-18 22:25:20 +03:00
for(int i = total - 1; i >= 0; i--)
2026-05-03 17:59:01 +03:00
{
2026-04-18 22:25:20 +03:00
ulong ticket = PositionGetTicket(i);
if(ticket == 0)
continue;
2026-04-11 00:22:38 +03:00
2026-04-18 22:25:20 +03:00
if(!PositionSelectByTicket(ticket))
continue;
2026-04-11 00:22:38 +03:00
2026-04-18 22:25:20 +03:00
long magic = PositionGetInteger(POSITION_MAGIC);
string sym = PositionGetString(POSITION_SYMBOL);
if(magic == Magic && sym == _Symbol)
return ticket;
2026-05-03 17:59:01 +03:00
}
2026-04-18 22:25:20 +03:00
return 0;
2026-05-03 17:59:01 +03:00
}
2026-04-11 00:22:38 +03:00
2026-05-03 17:59:01 +03:00
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
2026-04-18 22:25:20 +03:00
void CloseMyPosition(ulong ticket)
2026-05-03 17:59:01 +03:00
{
2026-04-18 22:25:20 +03:00
if(!PositionSelectByTicket(ticket))
return;
2026-04-11 00:22:38 +03:00
2026-04-18 22:25:20 +03:00
MqlTradeRequest req;
MqlTradeResult res;
ZeroMemory(req);
ZeroMemory(res);
2026-04-11 00:22:38 +03:00
2026-04-18 22:25:20 +03:00
int type = (int)PositionGetInteger(POSITION_TYPE);
double vol = PositionGetDouble(POSITION_VOLUME);
2026-04-11 00:22:38 +03:00
2026-04-18 22:25:20 +03:00
req.action = TRADE_ACTION_DEAL;
req.symbol = _Symbol;
req.volume = vol;
req.magic = Magic;
2026-04-11 00:22:38 +03:00
2026-04-18 22:25:20 +03:00
if(type == POSITION_TYPE_BUY)
2026-05-03 17:59:01 +03:00
{
2026-04-18 22:25:20 +03:00
req.type = ORDER_TYPE_SELL;
req.price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
2026-05-03 17:59:01 +03:00
}
2026-04-18 22:25:20 +03:00
else
2026-05-03 17:59:01 +03:00
{
2026-04-18 22:25:20 +03:00
req.type = ORDER_TYPE_BUY;
req.price = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
2026-05-03 17:59:01 +03:00
}
2026-04-18 22:25:20 +03:00
if(OrderSend(req, res))
LogTF(StringFormat("✅ Position closed by AO reversal, ticket=", ticket));
else
LogTF(StringFormat("❌ Failed to close position by AO, retcode=", res.retcode));
2026-05-03 17:59:01 +03:00
}
2026-04-11 00:22:38 +03:00
2026-05-03 17:59:01 +03:00
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
2026-04-18 22:25:20 +03:00
void CheckAOExit()
2026-05-03 17:59:01 +03:00
{
2026-04-18 22:25:20 +03:00
ulong ticket = GetMyPosition();
if(ticket == 0)
return;
2026-04-11 00:22:38 +03:00
2026-04-18 22:25:20 +03:00
if(!PositionSelectByTicket(ticket))
return;
int type = (int)PositionGetInteger(POSITION_TYPE);
int aoColor = GetAOColor();
if(type == POSITION_TYPE_BUY)
2026-05-03 17:59:01 +03:00
{
2026-04-18 22:25:20 +03:00
if(aoColor == -1 && aoFlipDirection == 0)
2026-05-03 17:59:01 +03:00
{
2026-04-18 22:25:20 +03:00
aoFlipDirection = -1;
aoFlipTime = iTime(_Symbol, PERIOD_CURRENT, 0);
LogTF("AO flip detected: BUY → first RED bar");
return;
2026-05-03 17:59:01 +03:00
}
2026-04-11 00:22:38 +03:00
2026-04-18 22:25:20 +03:00
if(aoFlipDirection == -1)
2026-05-03 17:59:01 +03:00
{
2026-04-18 22:25:20 +03:00
datetime currentBarTime = iTime(_Symbol, PERIOD_CURRENT, 0);
2026-04-11 00:22:38 +03:00
2026-04-18 22:25:20 +03:00
if(currentBarTime != aoFlipTime)
2026-05-03 17:59:01 +03:00
{
2026-04-18 22:25:20 +03:00
if(aoColor == -1)
2026-05-03 17:59:01 +03:00
{
2026-04-18 22:25:20 +03:00
LogTF("⚠️ AO confirmed reversal: BUY → RED. Closing BUY.");
CloseMyPosition(ticket);
lastExitTime = TimeCurrent();
2026-05-03 17:59:01 +03:00
}
2026-04-18 22:25:20 +03:00
aoFlipDirection = 0;
2026-05-03 17:59:01 +03:00
}
}
}
2026-04-18 22:25:20 +03:00
if(type == POSITION_TYPE_SELL)
2026-05-03 17:59:01 +03:00
{
2026-04-18 22:25:20 +03:00
if(aoColor == +1 && aoFlipDirection == 0)
2026-05-03 17:59:01 +03:00
{
2026-04-18 22:25:20 +03:00
aoFlipDirection = +1;
aoFlipTime = iTime(_Symbol, PERIOD_CURRENT, 0);
LogTF("AO flip detected: SELL → first GREEN bar");
return;
2026-05-03 17:59:01 +03:00
}
2026-04-18 22:25:20 +03:00
if(aoFlipDirection == +1)
2026-05-03 17:59:01 +03:00
{
2026-04-18 22:25:20 +03:00
datetime currentBarTime = iTime(_Symbol, PERIOD_CURRENT, 0);
if(currentBarTime != aoFlipTime)
2026-05-03 17:59:01 +03:00
{
2026-04-18 22:25:20 +03:00
if(aoColor == +1)
2026-05-03 17:59:01 +03:00
{
2026-04-18 22:25:20 +03:00
LogTF("⚠️ AO confirmed reversal: SELL → GREEN. Closing SELL.");
CloseMyPosition(ticket);
lastExitTime = TimeCurrent();
2026-05-03 17:59:01 +03:00
}
2026-04-18 22:25:20 +03:00
aoFlipDirection = 0;
2026-05-03 17:59:01 +03:00
}
}
}
}
2026-04-11 00:22:38 +03:00
//+------------------------------------------------------------------+
2026-04-18 22:25:20 +03:00
//| Вспомогательные |
//+------------------------------------------------------------------+
bool IsValidPrice(double price)
2026-05-03 17:59:01 +03:00
{
if(price <= 0.0)
return(false);
if(price == DBL_MAX)
return(false);
if(price == EMPTY_VALUE)
return(false);
2026-04-18 22:25:20 +03:00
return(true);
2026-05-03 17:59:01 +03:00
}
2026-04-18 22:25:20 +03:00
2026-05-03 17:59:01 +03:00
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
2026-04-18 22:25:20 +03:00
bool HasPendingOrders()
2026-05-03 17:59:01 +03:00
{
int total = OrdersTotal();
for(int i = 0; i < total; i++)
{
ulong ticket = OrderGetTicket(i);
if(ticket == 0)
continue;
if(!OrderSelect(ticket))
continue;
string sym = OrderGetString(ORDER_SYMBOL);
long mg = OrderGetInteger(ORDER_MAGIC);
int type = (int)OrderGetInteger(ORDER_TYPE);
// фильтр по символу и magic
if(sym != _Symbol || mg != Magic)
continue;
// pending-типы
if(type == ORDER_TYPE_BUY_STOP ||
type == ORDER_TYPE_SELL_STOP ||
type == ORDER_TYPE_BUY_LIMIT ||
type == ORDER_TYPE_SELL_LIMIT)
2026-04-25 22:32:05 +03:00
{
2026-05-03 17:59:01 +03:00
return true;
2026-04-25 22:32:05 +03:00
}
2026-05-03 17:59:01 +03:00
}
2026-04-25 22:32:05 +03:00
2026-05-03 17:59:01 +03:00
return false;
}
2026-04-18 22:25:20 +03:00
2026-04-25 22:32:05 +03:00
2026-05-03 17:59:01 +03:00
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
2026-04-18 22:25:20 +03:00
bool HasOpenPosition()
2026-05-03 17:59:01 +03:00
{
2026-04-18 22:25:20 +03:00
for(int i = PositionsTotal() - 1; i >= 0; i--)
2026-05-03 17:59:01 +03:00
{
2026-04-18 22:25:20 +03:00
ulong ticket = PositionGetTicket(i);
2026-05-03 17:59:01 +03:00
if(ticket == 0)
continue;
2026-04-18 22:25:20 +03:00
2026-05-03 17:59:01 +03:00
if(!PositionSelectByTicket(ticket))
continue;
2026-04-18 22:25:20 +03:00
if(PositionGetString(POSITION_SYMBOL) == _Symbol &&
PositionGetInteger(POSITION_MAGIC) == Magic)
return true;
2026-05-03 17:59:01 +03:00
}
2026-04-18 22:25:20 +03:00
return false;
2026-05-03 17:59:01 +03:00
}
2026-04-18 22:25:20 +03:00
//+------------------------------------------------------------------+
//| CleanupPendingOrders (упрощённо) |
//+------------------------------------------------------------------+
bool CleanupPendingOrders()
2026-05-03 17:59:01 +03:00
{
bool removed = false;
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
ulong ticket = OrderGetTicket(i);
if(ticket == 0)
continue;
if(!OrderSelect(ticket))
continue;
ENUM_ORDER_TYPE type = (ENUM_ORDER_TYPE)OrderGetInteger(ORDER_TYPE);
if(type != ORDER_TYPE_BUY_STOP &&
type != ORDER_TYPE_SELL_STOP)
continue;
// --- Удаляем ВСЕ pending по символу и magic ---
if(OrderGetString(ORDER_SYMBOL) == _Symbol &&
OrderGetInteger(ORDER_MAGIC) == Magic)
2026-04-25 22:32:05 +03:00
{
2026-05-03 17:59:01 +03:00
MqlTradeRequest req;
MqlTradeResult res;
ZeroMemory(req);
ZeroMemory(res);
req.action = TRADE_ACTION_REMOVE;
req.order = ticket;
if(OrderSend(req, res))
{
Print("🗑 Pending removed: ", ticket);
removed = true;
}
2026-04-25 22:32:05 +03:00
}
2026-05-03 17:59:01 +03:00
}
2026-04-11 00:22:38 +03:00
2026-05-03 17:59:01 +03:00
return removed;
}
2026-04-11 00:22:38 +03:00
2026-04-25 22:32:05 +03:00
2026-04-11 00:22:38 +03:00
//+------------------------------------------------------------------+
2026-04-18 22:25:20 +03:00
//| FractalFilter / GetEntryAndSL / TrailByStructure / S/R |
2026-04-11 00:22:38 +03:00
//+------------------------------------------------------------------+
2026-04-18 22:25:20 +03:00
bool FractalFilter(int trend, double pivot, int tfM_in, int tfH_in)
2026-05-03 17:59:01 +03:00
{
2026-04-18 22:25:20 +03:00
double up[500], down[500];
datetime t[500];
2026-04-11 00:22:38 +03:00
2026-05-03 17:59:01 +03:00
if(CopyBuffer(handleFractals_M, 0, 0, 500, up) <= 0)
return true;
if(CopyBuffer(handleFractals_M, 1, 0, 500, down) <= 0)
return true;
if(CopyTime(_Symbol, (ENUM_TIMEFRAMES)tfM_in, 0, 500, t) <= 0)
return true;
2026-04-11 00:22:38 +03:00
2026-04-18 22:25:20 +03:00
datetime hStart = iTime(_Symbol, (ENUM_TIMEFRAMES)tfH_in, 1);
datetime hEnd = iTime(_Symbol, (ENUM_TIMEFRAMES)tfH_in, 0);
2026-04-11 00:22:38 +03:00
struct Fract { double price; datetime time; bool isUp; };
Fract frs[];
ArrayResize(frs, 0);
for(int i = 2; i < 500; i++)
2026-05-03 17:59:01 +03:00
{
if(t[i] < hStart || t[i] >= hEnd)
continue;
2026-04-11 00:22:38 +03:00
if(up[i] != 0.0 && up[i] != EMPTY_VALUE)
2026-05-03 17:59:01 +03:00
{
2026-04-11 00:22:38 +03:00
int n = ArraySize(frs);
ArrayResize(frs, n+1);
frs[n].price = up[i];
frs[n].time = t[i];
frs[n].isUp = true;
2026-05-03 17:59:01 +03:00
}
2026-04-11 00:22:38 +03:00
if(down[i] != 0.0 && down[i] != EMPTY_VALUE)
2026-05-03 17:59:01 +03:00
{
2026-04-11 00:22:38 +03:00
int n = ArraySize(frs);
ArrayResize(frs, n+1);
frs[n].price = down[i];
frs[n].time = t[i];
frs[n].isUp = false;
2026-05-03 17:59:01 +03:00
}
}
2026-04-11 00:22:38 +03:00
2026-05-03 17:59:01 +03:00
if(ArraySize(frs) < 3)
return true;
2026-04-11 00:22:38 +03:00
2026-05-03 17:59:01 +03:00
// сортировка по времени
2026-04-11 00:22:38 +03:00
for(int i = 0; i < ArraySize(frs)-1; i++)
for(int j = i+1; j < ArraySize(frs); j++)
if(frs[j].time < frs[i].time)
2026-05-03 17:59:01 +03:00
{
2026-04-11 00:22:38 +03:00
Fract tmp = frs[i];
frs[i] = frs[j];
frs[j] = tmp;
2026-05-03 17:59:01 +03:00
}
2026-04-11 00:22:38 +03:00
2026-04-18 22:25:20 +03:00
double lastUp1=0,lastUp2=0,lastDn1=0,lastDn2=0;
2026-04-11 00:22:38 +03:00
2026-05-03 17:59:01 +03:00
// последние два up
2026-04-11 00:22:38 +03:00
for(int i = ArraySize(frs)-1; i >= 0; i--)
2026-05-03 17:59:01 +03:00
{
2026-04-18 22:25:20 +03:00
if(frs[i].isUp)
2026-05-03 17:59:01 +03:00
{
if(lastUp1==0)
lastUp1=frs[i].price;
else
if(lastUp2==0)
{
lastUp2=frs[i].price;
break;
}
}
}
2026-04-11 00:22:38 +03:00
2026-05-03 17:59:01 +03:00
// последние два down
2026-04-11 00:22:38 +03:00
for(int i = ArraySize(frs)-1; i >= 0; i--)
2026-05-03 17:59:01 +03:00
{
2026-04-18 22:25:20 +03:00
if(!frs[i].isUp)
2026-05-03 17:59:01 +03:00
{
if(lastDn1==0)
lastDn1=frs[i].price;
else
if(lastDn2==0)
{
lastDn2=frs[i].price;
break;
}
}
}
2026-04-18 22:25:20 +03:00
if(trend == 1)
2026-05-03 17:59:01 +03:00
{
2026-04-18 22:25:20 +03:00
double priceNow = SymbolInfoDouble(_Symbol, SYMBOL_BID);
2026-04-11 00:22:38 +03:00
2026-04-18 22:25:20 +03:00
if(priceNow <= pivot)
2026-05-03 17:59:01 +03:00
{
2026-04-18 22:25:20 +03:00
LogTF(StringFormat("🛑 BUY blocked: price below pivot (%.5f)", pivot));
return false;
2026-05-03 17:59:01 +03:00
}
2026-04-11 00:22:38 +03:00
2026-04-18 22:25:20 +03:00
if(lastUp1==0 || lastUp2==0 || lastDn1==0 || lastDn2==0)
2026-05-03 17:59:01 +03:00
{
2026-04-18 22:25:20 +03:00
LogTF("🛑 BUY blocked: insufficient fractal data");
return false;
2026-05-03 17:59:01 +03:00
}
2026-04-12 21:21:00 +03:00
2026-04-18 22:25:20 +03:00
bool hh_ok = (lastUp1 > lastUp2);
bool hl_ok = (lastDn1 > lastDn2);
2026-04-11 00:22:38 +03:00
2026-04-18 22:25:20 +03:00
if(!hh_ok || !hl_ok)
2026-05-03 17:59:01 +03:00
{
2026-04-18 22:25:20 +03:00
LogTF(StringFormat("🛑 BUY blocked: structure not bullish (HH=%s HL=%s)",
2026-05-03 17:59:01 +03:00
hh_ok?"OK":"NO", hl_ok?"OK":"NO"));
2026-04-18 22:25:20 +03:00
return false;
2026-05-03 17:59:01 +03:00
}
2026-04-11 00:22:38 +03:00
2026-04-18 22:25:20 +03:00
if(lastDn1 >= lastUp1)
2026-05-03 17:59:01 +03:00
{
2026-04-18 22:25:20 +03:00
LogTF(StringFormat("🛑 BUY blocked: SL fractal (%.5f) not below entry fractal (%.5f)",
2026-05-03 17:59:01 +03:00
lastDn1, lastUp1));
2026-04-18 22:25:20 +03:00
return false;
2026-05-03 17:59:01 +03:00
}
2026-04-12 21:21:00 +03:00
2026-04-18 22:25:20 +03:00
return true;
2026-05-03 17:59:01 +03:00
}
2026-04-11 00:22:38 +03:00
if(trend == -1)
2026-05-03 17:59:01 +03:00
{
2026-04-12 21:21:00 +03:00
double priceNow = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
2026-04-18 22:25:20 +03:00
if(priceNow >= pivot)
2026-05-03 17:59:01 +03:00
{
2026-04-18 22:25:20 +03:00
LogTF(StringFormat("🛑 SELL blocked: price above pivot (%.5f)", pivot));
return false;
2026-05-03 17:59:01 +03:00
}
2026-04-11 00:22:38 +03:00
2026-04-18 22:25:20 +03:00
if(lastUp1==0 || lastUp2==0 || lastDn1==0 || lastDn2==0)
2026-05-03 17:59:01 +03:00
{
2026-04-18 22:25:20 +03:00
LogTF("🛑 SELL blocked: insufficient fractal data");
return false;
2026-05-03 17:59:01 +03:00
}
2026-04-11 00:22:38 +03:00
2026-04-18 22:25:20 +03:00
bool lh_ok = (lastUp1 < lastUp2);
bool ll_ok = (lastDn1 < lastDn2);
if(!lh_ok || !ll_ok)
2026-05-03 17:59:01 +03:00
{
2026-04-18 22:25:20 +03:00
LogTF(StringFormat("🛑 SELL blocked: structure not bearish (LH=%s LL=%s)",
2026-05-03 17:59:01 +03:00
lh_ok?"OK":"NO", ll_ok?"OK":"NO"));
2026-04-18 22:25:20 +03:00
return false;
2026-05-03 17:59:01 +03:00
}
2026-04-11 00:22:38 +03:00
2026-04-18 22:25:20 +03:00
if(lastUp1 <= lastDn1)
2026-05-03 17:59:01 +03:00
{
2026-04-18 22:25:20 +03:00
LogTF(StringFormat("🛑 SELL blocked: SL fractal (%.5f) not above entry fractal (%.5f)",
2026-05-03 17:59:01 +03:00
lastUp1, lastDn1));
2026-04-18 22:25:20 +03:00
return false;
2026-05-03 17:59:01 +03:00
}
2026-04-11 00:22:38 +03:00
2026-04-18 22:25:20 +03:00
return true;
2026-05-03 17:59:01 +03:00
}
2026-04-18 22:25:20 +03:00
return true;
2026-05-03 17:59:01 +03:00
}
2026-04-11 00:22:38 +03:00
2026-05-03 17:59:01 +03:00
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
2026-04-18 22:25:20 +03:00
bool GetEntryAndSL(int trend, double &entry, double &sl)
2026-05-03 17:59:01 +03:00
{
2026-04-18 22:25:20 +03:00
entry = 0.0;
sl = 0.0;
2026-05-03 17:59:01 +03:00
// --- Проверяем handle ---
2026-04-18 22:25:20 +03:00
if(handleFractals_H == INVALID_HANDLE)
2026-05-03 17:59:01 +03:00
{
2026-04-18 22:25:20 +03:00
Print("⛔ GetEntryAndSL: fractal handle invalid");
return false;
2026-05-03 17:59:01 +03:00
}
2026-04-11 00:22:38 +03:00
2026-05-03 17:59:01 +03:00
// --- Читаем буферы ---
2026-04-18 22:25:20 +03:00
double upBuf[10];
double dnBuf[10];
2026-04-11 00:22:38 +03:00
2026-04-18 22:25:20 +03:00
int upCount = CopyBuffer(handleFractals_H, 0, 0, 10, upBuf);
int dnCount = CopyBuffer(handleFractals_H, 1, 0, 10, dnBuf);
2026-04-11 00:22:38 +03:00
2026-04-18 22:25:20 +03:00
if(upCount <= 0 || dnCount <= 0)
2026-05-03 17:59:01 +03:00
{
2026-04-18 22:25:20 +03:00
LogTF(StringFormat("⛔ GetEntryAndSL: fractal buffers empty (up=%d dn=%d)", upCount, dnCount));
return false;
2026-05-03 17:59:01 +03:00
}
2026-04-12 21:21:00 +03:00
2026-05-03 17:59:01 +03:00
// --- Ищем последний фрактал ---
2026-04-18 22:25:20 +03:00
double lastUp = 0.0;
double lastDn = 0.0;
2026-04-11 00:22:38 +03:00
2026-04-18 22:25:20 +03:00
for(int i = 0; i < upCount; i++)
2026-05-03 17:59:01 +03:00
{
2026-04-18 22:25:20 +03:00
if(upBuf[i] > 0 && upBuf[i] < 1000000000)
2026-05-03 17:59:01 +03:00
{
2026-04-18 22:25:20 +03:00
lastUp = upBuf[i];
break;
2026-05-03 17:59:01 +03:00
}
}
2026-04-12 21:21:00 +03:00
2026-04-18 22:25:20 +03:00
for(int i = 0; i < dnCount; i++)
2026-05-03 17:59:01 +03:00
{
2026-04-18 22:25:20 +03:00
if(dnBuf[i] > 0 && dnBuf[i] < 1000000000)
2026-05-03 17:59:01 +03:00
{
2026-04-18 22:25:20 +03:00
lastDn = dnBuf[i];
break;
2026-05-03 17:59:01 +03:00
}
}
2026-04-12 21:21:00 +03:00
2026-05-03 17:59:01 +03:00
// --- BUY ---
2026-04-18 22:25:20 +03:00
if(trend == 1)
2026-05-03 17:59:01 +03:00
{
2026-04-18 22:25:20 +03:00
if(lastUp <= 0)
2026-05-03 17:59:01 +03:00
{
2026-04-18 22:25:20 +03:00
LogTF("⛔ GetEntryAndSL: BUY but no up fractal");
return false;
2026-05-03 17:59:01 +03:00
}
2026-04-12 21:21:00 +03:00
2026-04-18 22:25:20 +03:00
entry = lastUp;
2026-04-12 21:21:00 +03:00
2026-04-18 22:25:20 +03:00
if(lastDn > 0)
sl = lastDn;
else
sl = entry - 3 * _Point; // минимальный fallback
2026-04-11 00:22:38 +03:00
2026-04-18 22:25:20 +03:00
// --- Проверки ---
if(entry <= 0 || entry == DBL_MAX || entry > 1000000000)
2026-05-03 17:59:01 +03:00
{
2026-04-18 22:25:20 +03:00
LogTF(StringFormat("⛔ GetEntryAndSL: invalid BUY entry=%.5f", entry));
return false;
2026-05-03 17:59:01 +03:00
}
2026-04-12 21:21:00 +03:00
2026-04-18 22:25:20 +03:00
if(sl <= 0 || sl == DBL_MAX || sl >= entry)
2026-05-03 17:59:01 +03:00
{
2026-04-18 22:25:20 +03:00
LogTF(StringFormat("⛔ GetEntryAndSL: invalid BUY SL=%.5f (entry=%.5f)", sl, entry));
return false;
2026-05-03 17:59:01 +03:00
}
2026-04-12 21:21:00 +03:00
2026-04-18 22:25:20 +03:00
LogTF(StringFormat("✅ GetEntryAndSL BUY: entry=%.5f SL=%.5f", entry, sl));
return true;
2026-05-03 17:59:01 +03:00
}
2026-04-11 00:22:38 +03:00
2026-05-03 17:59:01 +03:00
// --- SELL ---
2026-04-18 22:25:20 +03:00
if(trend == -1)
2026-05-03 17:59:01 +03:00
{
2026-04-18 22:25:20 +03:00
if(lastDn <= 0)
2026-05-03 17:59:01 +03:00
{
2026-04-18 22:25:20 +03:00
LogTF("⛔ GetEntryAndSL: SELL but no down fractal");
return false;
2026-05-03 17:59:01 +03:00
}
2026-04-11 00:22:38 +03:00
2026-04-18 22:25:20 +03:00
entry = lastDn;
2026-04-11 00:22:38 +03:00
2026-04-18 22:25:20 +03:00
if(lastUp > 0)
sl = lastUp;
else
sl = entry + 3 * _Point;
2026-04-11 00:22:38 +03:00
2026-04-18 22:25:20 +03:00
if(entry <= 0 || entry == DBL_MAX || entry > 1000000000)
2026-05-03 17:59:01 +03:00
{
2026-04-18 22:25:20 +03:00
LogTF(StringFormat("⛔ GetEntryAndSL: invalid SELL entry=%.5f", entry));
return false;
2026-05-03 17:59:01 +03:00
}
2026-04-11 00:22:38 +03:00
2026-04-18 22:25:20 +03:00
if(sl <= entry || sl == DBL_MAX)
2026-05-03 17:59:01 +03:00
{
2026-04-18 22:25:20 +03:00
LogTF(StringFormat("⛔ GetEntryAndSL: invalid SELL SL=%.5f (entry=%.5f)", sl, entry));
return false;
2026-05-03 17:59:01 +03:00
}
2026-04-11 00:22:38 +03:00
2026-04-18 22:25:20 +03:00
LogTF(StringFormat("✅ GetEntryAndSL SELL: entry=%.5f SL=%.5f", entry, sl));
return true;
2026-05-03 17:59:01 +03:00
}
2026-04-11 00:22:38 +03:00
2026-04-18 22:25:20 +03:00
LogTF("⛔ GetEntryAndSL: trend=0");
return false;
2026-05-03 17:59:01 +03:00
}
2026-04-11 00:22:38 +03:00
2026-05-03 17:59:01 +03:00
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
2026-04-18 22:25:20 +03:00
void TrailByStructure(int tfM_in, int tfH_in, double pivot)
2026-05-03 17:59:01 +03:00
{
2026-04-18 22:25:20 +03:00
double up[200], down[200];
2026-04-11 00:22:38 +03:00
2026-05-03 17:59:01 +03:00
if(CopyBuffer(handleFractals_M, 0, 0, 200, up) <= 0)
return;
if(CopyBuffer(handleFractals_M, 1, 0, 200, down) <= 0)
return;
2026-04-11 00:22:38 +03:00
2026-04-18 22:25:20 +03:00
double atrCur = 0.0;
double atrBuf[1];
if(CopyBuffer(handleATR_M, 0, 0, 1, atrBuf) > 0)
atrCur = atrBuf[0];
2026-04-11 00:22:38 +03:00
if(atrCur <= 0.0)
return;
double buffer = atrCur * 0.3;
2026-04-18 22:25:20 +03:00
for(int i = PositionsTotal()-1; i >= 0; i--)
2026-05-03 17:59:01 +03:00
{
2026-04-11 00:22:38 +03:00
ulong ticket = PositionGetTicket(i);
if(!PositionSelectByTicket(ticket))
continue;
2026-04-18 22:25:20 +03:00
string sym = PositionGetString(POSITION_SYMBOL);
int mag = (int)PositionGetInteger(POSITION_MAGIC);
int type = (int)PositionGetInteger(POSITION_TYPE);
double sl = PositionGetDouble(POSITION_SL);
2026-04-11 00:22:38 +03:00
double priceOpen = PositionGetDouble(POSITION_PRICE_OPEN);
if(sym != _Symbol || mag != Magic)
continue;
if(type == POSITION_TYPE_BUY)
2026-05-03 17:59:01 +03:00
{
2026-04-11 00:22:38 +03:00
double lastDown = 0.0;
for(int j=2; j<100; j++)
2026-05-03 17:59:01 +03:00
{
2026-04-11 00:22:38 +03:00
if(down[j] > pivot && down[j] > sl)
2026-05-03 17:59:01 +03:00
{
2026-04-11 00:22:38 +03:00
if(lastDown == 0.0 || down[j] > lastDown)
lastDown = down[j];
2026-05-03 17:59:01 +03:00
}
}
2026-04-18 22:25:20 +03:00
2026-04-11 00:22:38 +03:00
if(lastDown > 0.0)
2026-05-03 17:59:01 +03:00
{
2026-04-11 00:22:38 +03:00
double newSL = lastDown - buffer;
if(newSL > sl && newSL < priceOpen)
2026-05-03 17:59:01 +03:00
{
2026-04-11 00:22:38 +03:00
MqlTradeRequest req;
MqlTradeResult res;
ZeroMemory(req);
ZeroMemory(res);
req.action = TRADE_ACTION_SLTP;
req.symbol = _Symbol;
req.position = ticket;
req.sl = newSL;
req.tp = PositionGetDouble(POSITION_TP);
if(OrderSend(req, res))
2026-05-03 17:59:01 +03:00
{
2026-04-18 22:25:20 +03:00
DrawTrailSL(ticket, sl, newSL);
PrintFormat("TRAIL BUY: oldSL=%.5f newSL=%.5f fractal=%.5f",
sl, newSL, lastDown);
2026-05-03 17:59:01 +03:00
}
}
}
}
else
if(type == POSITION_TYPE_SELL)
{
double lastUp = 0.0;
for(int j=2; j<100; j++)
{
if(up[j] < pivot && (sl == 0.0 || up[j] < sl))
{
if(lastUp == 0.0 || up[j] < lastUp)
lastUp = up[j];
}
}
if(lastUp > 0.0)
{
double newSL = lastUp + buffer;
if((sl == 0.0 || newSL < sl) && newSL > priceOpen)
{
MqlTradeRequest req;
MqlTradeResult res;
ZeroMemory(req);
ZeroMemory(res);
req.action = TRADE_ACTION_SLTP;
req.symbol = _Symbol;
req.position = ticket;
req.sl = newSL;
req.tp = PositionGetDouble(POSITION_TP);
if(OrderSend(req, res))
{
DrawTrailSL(ticket, sl, newSL);
PrintFormat("TRAIL SELL: oldSL=%.5f newSL=%.5f fractal=%.5f",
sl, newSL, lastUp);
}
}
}
}
}
}
2026-04-11 00:22:38 +03:00
2026-05-03 17:59:01 +03:00
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
2026-04-18 22:25:20 +03:00
double GetNearestSupport(double buffer, int tfM_in)
2026-05-03 17:59:01 +03:00
{
2026-04-18 22:25:20 +03:00
double down[200];
2026-04-11 00:22:38 +03:00
datetime t[200];
2026-05-03 17:59:01 +03:00
if(CopyBuffer(handleFractals_M, 1, 0, 200, down) <= 0)
return 0.0;
if(CopyTime(_Symbol, (ENUM_TIMEFRAMES)tfM_in, 0, 200, t) <= 0)
return 0.0;
2026-04-18 22:25:20 +03:00
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
2026-04-11 00:22:38 +03:00
double best = 0.0;
for(int i=2; i<200; i++)
2026-05-03 17:59:01 +03:00
{
2026-04-18 22:25:20 +03:00
if(down[i] != 0.0 && down[i] < bid - buffer)
2026-05-03 17:59:01 +03:00
{
2026-04-18 22:25:20 +03:00
if(best == 0.0 || down[i] > best)
best = down[i];
2026-05-03 17:59:01 +03:00
}
}
2026-04-18 22:25:20 +03:00
return best;
2026-05-03 17:59:01 +03:00
}
2026-04-11 00:22:38 +03:00
2026-05-03 17:59:01 +03:00
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
2026-04-18 22:25:20 +03:00
double GetNearestResistance(double buffer, int tfM_in)
2026-05-03 17:59:01 +03:00
{
2026-04-18 22:25:20 +03:00
double up[200];
2026-04-11 00:22:38 +03:00
datetime t[200];
2026-05-03 17:59:01 +03:00
if(CopyBuffer(handleFractals_M, 0, 0, 200, up) <= 0)
return 0.0;
if(CopyTime(_Symbol, (ENUM_TIMEFRAMES)tfM_in, 0, 200, t) <= 0)
return 0.0;
2026-04-18 22:25:20 +03:00
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
2026-04-11 00:22:38 +03:00
double best = 0.0;
for(int i=2; i<200; i++)
2026-05-03 17:59:01 +03:00
{
2026-04-18 22:25:20 +03:00
if(up[i] != 0.0 && up[i] > ask + buffer)
2026-05-03 17:59:01 +03:00
{
2026-04-18 22:25:20 +03:00
if(best == 0.0 || up[i] < best)
best = up[i];
2026-05-03 17:59:01 +03:00
}
}
2026-04-18 22:25:20 +03:00
return best;
2026-05-03 17:59:01 +03:00
}
2026-04-11 00:22:38 +03:00
//+------------------------------------------------------------------+
2026-04-18 22:25:20 +03:00
//| FindEntrySignal |
2026-04-11 00:22:38 +03:00
//+------------------------------------------------------------------+
bool FindEntrySignal()
2026-06-12 13:11:05 +03:00
{
// --- DEBUG вывод тренда и пивота ---
2026-04-18 22:25:20 +03:00
PrintFormat("DEBUG FindEntrySignal: trend=%d pivot=%.5f", lastTrend, lastPivot);
2026-04-11 00:22:38 +03:00
2026-04-18 22:25:20 +03:00
int trend = lastTrend;
double pivot = lastPivot;
2026-04-12 21:21:00 +03:00
2026-06-12 13:11:05 +03:00
// --- 1. Проверяем тренд ---
2026-04-18 22:25:20 +03:00
if(trend == 0)
2026-06-12 13:11:05 +03:00
{
2026-04-18 22:25:20 +03:00
Print("⛔ CANCEL: trend=0 (нет тренда)");
return false;
2026-06-12 13:11:05 +03:00
}
2026-04-12 21:21:00 +03:00
2026-06-12 13:11:05 +03:00
// --- 2. Проверяем готовность HTF индикаторов ---
2026-04-18 22:25:20 +03:00
if(!htfReady)
2026-06-12 13:11:05 +03:00
{
2026-04-18 22:25:20 +03:00
LogTF("⛔ CANCEL: HTF indicators not ready");
return false;
2026-06-12 13:11:05 +03:00
}
2026-04-12 21:21:00 +03:00
2026-06-12 13:11:05 +03:00
// --- 3. Получаем текущий тик ---
2026-04-18 22:25:20 +03:00
MqlTick tick;
if(!SymbolInfoTick(_Symbol, tick))
return false;
2026-04-12 21:21:00 +03:00
2026-04-18 22:25:20 +03:00
double bid = tick.bid;
double ask = tick.ask;
double mid = (bid + ask) * 0.5;
double spread = ask - bid;
double relSpread = (mid > 0 ? spread / mid : 0.0);
2026-04-12 21:21:00 +03:00
2026-06-12 13:11:05 +03:00
// --- 4. Определяем динамический TF ---
2026-04-18 22:25:20 +03:00
int tfM_local = GetDynamicTF(relSpread);
2026-04-11 00:22:38 +03:00
2026-06-12 13:11:05 +03:00
// --- 5. Фильтр структуры (фракталы) ---
2026-04-18 22:25:20 +03:00
if(!FractalFilter(trend, pivot, tfM_local, tfH))
2026-06-12 13:11:05 +03:00
{
2026-04-18 22:25:20 +03:00
LogTF(StringFormat("⛔ CANCEL: FractalFilter отклонил вход (trend=%d pivot=%.5f)", trend, pivot));
return false;
2026-06-12 13:11:05 +03:00
}
2026-04-11 00:22:38 +03:00
2026-06-12 13:11:05 +03:00
// --- 6. Получаем уровень входа и SL от фрактала ---
2026-04-18 22:25:20 +03:00
double entryPrice = 0.0;
double slFromFractal = 0.0;
if(!GetEntryAndSL(trend, entryPrice, slFromFractal))
2026-06-12 13:11:05 +03:00
{
2026-04-18 22:25:20 +03:00
LogTF(StringFormat("⛔ CANCEL: GetEntryAndSL не дал вход (trend=%d pivot=%.5f)", trend, pivot));
return false;
2026-06-12 13:11:05 +03:00
}
2026-04-18 22:25:20 +03:00
2026-06-12 13:11:05 +03:00
// --- 7. Проверка валидности entry ---
if(entryPrice <= 0 || entryPrice == DBL_MAX)
{
2026-04-18 22:25:20 +03:00
LogTF(StringFormat("⛔ CANCEL: invalid entryPrice=%.5f", entryPrice));
return false;
2026-06-12 13:11:05 +03:00
}
2026-04-11 00:22:38 +03:00
2026-06-12 13:11:05 +03:00
// --- 8. ATR фильтр (быстрый и сглаженный ATR) ---
2026-04-18 22:25:20 +03:00
int atrFast = iATR(_Symbol, (ENUM_TIMEFRAMES)tfM_local, 14);
int atrSlow = iATR(_Symbol, (ENUM_TIMEFRAMES)tfM_local, 50);
double bufFast[1], bufSlow[1];
if(CopyBuffer(atrFast, 0, 0, 1, bufFast) <= 0 ||
CopyBuffer(atrSlow, 0, 0, 1, bufSlow) <= 0)
2026-06-12 13:11:05 +03:00
{
2026-04-18 22:25:20 +03:00
Print("⛔ ATR not available");
return false;
2026-06-12 13:11:05 +03:00
}
2026-04-11 00:22:38 +03:00
2026-06-12 13:11:05 +03:00
double atr_now = bufFast[0]; // текущая волатильность
double atr_smooth = bufSlow[0]; // сглаженная волатильность
double volRatio = (atr_smooth > 0 ? atr_now / atr_smooth : 1.0);
2026-04-11 00:22:38 +03:00
2026-06-12 13:11:05 +03:00
// --- коэффициент силы рынка ---
2026-04-18 22:25:20 +03:00
double coef = 0.45;
2026-06-12 13:11:05 +03:00
if(volRatio < 0.8) coef = 0.30;
else if(volRatio < 1.2) coef = 0.45;
else if(volRatio < 1.5) coef = 0.60;
else coef = 0.75;
2026-04-11 00:22:38 +03:00
2026-06-12 13:11:05 +03:00
// --- SL = 2 ATR ---
2026-04-18 22:25:20 +03:00
double slDistance = atr_now * 2.0;
2026-04-11 00:22:38 +03:00
2026-06-12 13:11:05 +03:00
// --- 9. Risk-based lot ---
2026-04-18 22:25:20 +03:00
double balance = AccountInfoDouble(ACCOUNT_BALANCE);
2026-06-12 13:11:05 +03:00
double riskPercent = 0.0025; // 0.25%
2026-04-18 22:25:20 +03:00
double riskMoney = balance * riskPercent;
2026-04-11 00:22:38 +03:00
2026-04-18 22:25:20 +03:00
double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
double tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
double pointValue = tickValue / tickSize;
2026-04-11 00:22:38 +03:00
2026-06-12 13:11:05 +03:00
double lot = riskMoney / (slDistance / _Point * pointValue);
// --- округление лота ---
2026-04-20 21:30:01 +03:00
double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
2026-04-18 22:25:20 +03:00
double lotStep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
lot = MathFloor(lot / lotStep) * lotStep;
2026-05-03 17:59:01 +03:00
2026-04-20 21:30:01 +03:00
if(lot < minLot)
2026-06-12 13:11:05 +03:00
{
PrintFormat("⚠ lot=%.5f < minLot=%.5f -> setting lot to minLot", lot, minLot);
2026-04-20 21:30:01 +03:00
lot = minLot;
2026-06-12 13:11:05 +03:00
}
2026-05-03 17:59:01 +03:00
2026-04-20 21:30:01 +03:00
lastLot = lot;
2026-06-12 13:11:05 +03:00
// --- 10. SL и TP ---
double stopLoss, takeProfit;
2026-04-11 00:22:38 +03:00
2026-06-12 13:11:05 +03:00
if(trend == 1) // BUY
{
2026-04-18 22:25:20 +03:00
stopLoss = entryPrice - slDistance;
takeProfit = entryPrice + slDistance;
2026-06-12 13:11:05 +03:00
}
else // SELL
{
2026-04-18 22:25:20 +03:00
stopLoss = entryPrice + slDistance;
takeProfit = entryPrice - slDistance;
2026-06-12 13:11:05 +03:00
}
2026-04-11 00:22:38 +03:00
2026-06-12 13:11:05 +03:00
// --- 11. Проверка SL ---
2026-04-18 22:25:20 +03:00
if(stopLoss <= 0 || stopLoss == DBL_MAX)
2026-06-12 13:11:05 +03:00
{
2026-04-18 22:25:20 +03:00
LogTF(StringFormat("⛔ CANCEL: invalid stopLoss=%.5f", stopLoss));
return false;
2026-06-12 13:11:05 +03:00
}
2026-04-11 00:22:38 +03:00
2026-04-18 22:25:20 +03:00
if(trend == 1 && stopLoss >= entryPrice)
2026-06-12 13:11:05 +03:00
{
2026-04-18 22:25:20 +03:00
LogTF("⛔ CANCEL: BUY SL >= entry");
return false;
2026-06-12 13:11:05 +03:00
}
2026-04-11 00:22:38 +03:00
2026-04-18 22:25:20 +03:00
if(trend == -1 && stopLoss <= entryPrice)
2026-06-12 13:11:05 +03:00
{
2026-04-18 22:25:20 +03:00
LogTF("⛔ CANCEL: SELL SL <= entry");
return false;
2026-06-12 13:11:05 +03:00
}
2026-05-03 17:45:50 +03:00
2026-06-12 13:11:05 +03:00
// --- ВАЖНО ---
// STOP-логика поддержки/сопротивления удалена,
// потому что LIMIT-входы проверяются в OnTick().
2026-04-11 00:22:38 +03:00
2026-06-12 13:11:05 +03:00
// --- 12. Финальное сохранение ---
lastEntry = entryPrice; // уровень, НЕ цена входа
2026-04-18 22:25:20 +03:00
lastStop = stopLoss;
lastTp = takeProfit;
lastLot = lot;
2026-04-20 21:30:01 +03:00
LogTF(StringFormat("SIGNAL: trend=%d entry=%.5f SL=%.5f TP=%.5f lot=%.2f",
2026-05-03 17:59:01 +03:00
trend, entryPrice, stopLoss, takeProfit, lot));
2026-04-18 22:25:20 +03:00
return true;
2026-06-12 13:11:05 +03:00
}
double GetATR(int period = 14)
{
double atr[];
if(CopyBuffer(iATR(_Symbol, PERIOD_CURRENT, period), 0, 0, 1, atr) > 0)
return atr[0];
return 0;
}
void CancelOrder(ulong ticket)
{
MqlTradeRequest req;
MqlTradeResult res;
ZeroMemory(req);
ZeroMemory(res);
req.action = TRADE_ACTION_REMOVE;
req.order = ticket;
req.magic = Magic;
req.symbol = _Symbol;
if(!OrderSend(req, res))
PrintFormat("❌ Failed to remove pending order %I64d, retcode=%d", ticket, res.retcode);
else
PrintFormat("🧹 Pending order %I64d removed", ticket);
}
void CleanupLimitOrders()
{
double atr = GetATR(14);
if(atr <= 0)
return;
double K_cancel = 1.0; // коэффициент удаления
MqlTick tick;
if(!SymbolInfoTick(_Symbol, tick))
return;
double bid = tick.bid;
double ask = tick.ask;
// --- перебираем все pending ордера ---
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
ulong ticket = OrderGetTicket(i);
if(!OrderSelect(ticket))
continue;
// фильтруем по магику
if(OrderGetInteger(ORDER_MAGIC) != Magic)
continue;
int type = (int)OrderGetInteger(ORDER_TYPE);
// интересуют только LIMIT-ордера
if(type != ORDER_TYPE_BUY_LIMIT && type != ORDER_TYPE_SELL_LIMIT)
continue;
double entry = OrderGetDouble(ORDER_PRICE_OPEN);
// BUY LIMIT: рынок ушёл слишком далеко вниз
if(type == ORDER_TYPE_BUY_LIMIT)
{
if(ask < entry - atr * K_cancel)
{
PrintFormat("🧹 Removing BUY LIMIT: market too far (ask=%.5f entry=%.5f)",
ask, entry);
CancelOrder(ticket);
}
}
// SELL LIMIT: рынок ушёл слишком далеко вверх
if(type == ORDER_TYPE_SELL_LIMIT)
{
if(bid > entry + atr * K_cancel)
{
PrintFormat("🧹 Removing SELL LIMIT: market too far (bid=%.5f entry=%.5f)",
bid, entry);
CancelOrder(ticket);
}
}
}
}
2026-04-11 00:22:38 +03:00
//+------------------------------------------------------------------+
2026-04-18 22:25:20 +03:00
//| PlacePending |
2026-04-11 00:22:38 +03:00
//+------------------------------------------------------------------+
2026-04-18 22:25:20 +03:00
ENUM_ORDER_TYPE_FILLING GetFillingType()
2026-05-03 17:59:01 +03:00
{
2026-04-18 22:25:20 +03:00
long filling = SymbolInfoInteger(_Symbol, SYMBOL_FILLING_MODE);
2026-05-03 17:59:01 +03:00
if((filling & SYMBOL_FILLING_FOK) != 0)
return ORDER_FILLING_FOK;
if((filling & SYMBOL_FILLING_IOC) != 0)
return ORDER_FILLING_IOC;
2026-04-18 22:25:20 +03:00
return ORDER_FILLING_RETURN;
2026-05-03 17:59:01 +03:00
}
2026-04-11 00:22:38 +03:00
2026-05-03 17:59:01 +03:00
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
2026-06-12 13:11:05 +03:00
void PlacePending(int trend, double level, double sl, double tp)
{
double atr = GetATR(14);
if(atr <= 0)
return;
2026-05-03 17:59:01 +03:00
MqlTradeRequest req;
MqlTradeResult res;
ZeroMemory(req);
ZeroMemory(res);
2026-04-11 00:22:38 +03:00
2026-05-03 17:59:01 +03:00
req.action = TRADE_ACTION_PENDING;
req.symbol = _Symbol;
req.magic = Magic;
req.volume = lastLot;
req.sl = sl;
req.tp = tp;
2026-06-12 13:11:05 +03:00
req.type_filling = ORDER_FILLING_RETURN;
req.type_time = ORDER_TIME_GTC;
2026-05-03 17:59:01 +03:00
2026-06-12 13:11:05 +03:00
// --- BUY LIMIT (вход на откате вверх)
if(trend == 1)
{
req.type = ORDER_TYPE_BUY_LIMIT;
req.price = level - atr * 0.3; // ретест уровня
}
// --- SELL LIMIT (вход на откате вниз)
else
{
req.type = ORDER_TYPE_SELL_LIMIT;
req.price = level + atr * 0.3; // ретест уровня
}
2026-05-03 17:59:01 +03:00
if(!OrderSend(req, res))
2026-06-12 13:11:05 +03:00
{
PrintFormat("❌ OrderSend error: retcode=%d (%s) price=%.5f",
res.retcode, res.comment, req.price);
2026-05-03 17:59:01 +03:00
return;
2026-06-12 13:11:05 +03:00
}
2026-04-11 00:22:38 +03:00
2026-06-12 13:11:05 +03:00
PrintFormat("✅ Pending LIMIT placed: ticket=%I64d price=%.5f", res.order, req.price);
}
2026-04-11 00:22:38 +03:00
2026-05-03 17:59:01 +03:00
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void RecreateAllIndicators()
{
if(handleFractals_M != INVALID_HANDLE)
IndicatorRelease(handleFractals_M);
if(handleFractals_H != INVALID_HANDLE)
IndicatorRelease(handleFractals_H);
if(handleAO_M != INVALID_HANDLE)
IndicatorRelease(handleAO_M);
if(handleAO_H != INVALID_HANDLE)
IndicatorRelease(handleAO_H);
if(handleBB != INVALID_HANDLE)
IndicatorRelease(handleBB);
if(handleATR_M != INVALID_HANDLE)
IndicatorRelease(handleATR_M);
if(handleATRSlow != INVALID_HANDLE)
IndicatorRelease(handleATRSlow);
if(handleMA_H != INVALID_HANDLE)
IndicatorRelease(handleMA_H);
handleFractals_M = iFractals(_Symbol, (ENUM_TIMEFRAMES)tfM);
handleFractals_H = iFractals(_Symbol, (ENUM_TIMEFRAMES)tfH);
handleAO_M = iAO(_Symbol, (ENUM_TIMEFRAMES)tfM);
handleAO_H = iAO(_Symbol, (ENUM_TIMEFRAMES)tfH);
handleBB = iBands(_Symbol, (ENUM_TIMEFRAMES)tfH, BB_Period, 0, BB_Deviation, PRICE_CLOSE);
handleATR_M = iATR(_Symbol, (ENUM_TIMEFRAMES)tfM, ATR_Period);
handleATRSlow = iATR(_Symbol, PERIOD_D1, ATR_Slow_Period);
handleMA_H = iMA(_Symbol, (ENUM_TIMEFRAMES)tfH, 50, 0, MODE_EMA, PRICE_CLOSE);
htfReady = false;
htfInitTime = GetMicrosecondCount();
mtfReady = false;
mtfInitTime = GetMicrosecondCount();
}
2026-04-11 00:22:38 +03:00
2026-05-03 17:59:01 +03:00
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
2026-04-18 22:25:20 +03:00
void LogCurrentTF()
2026-05-03 17:59:01 +03:00
{
PrintFormat("📊 Current TF: M=%s H=%s",
TimeframeToString(tfM),
TimeframeToString(tfH));
}
2026-04-11 00:22:38 +03:00
2026-05-03 17:59:01 +03:00
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
2026-04-18 22:25:20 +03:00
void LogTF(string msg)
2026-05-03 17:59:01 +03:00
{
PrintFormat("%s | TF: M=%s H=%s",
msg,
TimeframeToString(tfM),
TimeframeToString(tfH));
}
2026-04-11 00:22:38 +03:00
2026-05-03 17:59:01 +03:00
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
2026-04-18 22:25:20 +03:00
string ExplainStructure(bool upStruct, bool downStruct)
2026-05-03 17:59:01 +03:00
{
if(upStruct)
return "structure=HH/HL (bullish)";
if(downStruct)
return "structure=LH/LL (bearish)";
return "structure=flat/undefined";
}
2026-04-11 00:22:38 +03:00
2026-05-03 17:59:01 +03:00
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
2026-04-18 22:25:20 +03:00
string ExplainAO(double ao0, double ao1)
2026-05-03 17:59:01 +03:00
{
if(ao0 > ao1 && ao0 > 0)
return "AO rising above zero (bullish)";
if(ao0 > ao1 && ao0 < 0)
return "AO rising but still below zero (weak bullish)";
if(ao0 < ao1 && ao0 < 0)
return "AO falling below zero (bearish)";
if(ao0 < ao1 && ao0 > 0)
return "AO falling but still above zero (weak bearish)";
return "AO flat";
}
2026-04-11 00:22:38 +03:00
2026-05-03 17:59:01 +03:00
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
2026-04-18 22:25:20 +03:00
string ExplainMA(double closePrev, double ma)
2026-05-03 17:59:01 +03:00
{
if(closePrev > ma)
return "price above MA50 (bullish)";
if(closePrev < ma)
return "price below MA50 (bearish)";
return "price near MA50 (neutral)";
}
2026-04-11 00:22:38 +03:00
2026-05-03 17:59:01 +03:00
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
2026-04-18 22:25:20 +03:00
void ExplainTrendChange(int prevTrend, int newTrend,
2026-05-03 17:59:01 +03:00
bool upStruct, bool downStruct,
double ao0, double ao1,
double closePrev, double ma)
{
PrintFormat("TREND CHANGE: %d → %d", prevTrend, newTrend);
Print("", ExplainStructure(upStruct, downStruct));
Print("", ExplainAO(ao0, ao1));
Print("", ExplainMA(closePrev, ma));
if(newTrend == 0)
Print("TREND CANCELLED: conditions no longer aligned");
else
Print("TREND CONFIRMED: all filters aligned");
}
2026-04-11 00:22:38 +03:00
2026-05-03 17:59:01 +03:00
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
2026-04-20 21:30:01 +03:00
bool IsPriceAligned(double price)
2026-05-03 17:59:01 +03:00
{
double tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
double normalized = MathRound(price / tickSize) * tickSize;
return MathAbs(price - normalized) < (tickSize / 2.0);
}
2026-04-25 22:32:05 +03:00
2026-05-03 17:59:01 +03:00
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
2026-04-25 22:32:05 +03:00
double GetLastFractalUp(const double &up[])
2026-05-03 17:59:01 +03:00
{
2026-04-25 22:32:05 +03:00
for(int i = 5; i < 300; i++) // начинаем с 5, чтобы фрактал был подтверждён
2026-05-03 17:59:01 +03:00
{
2026-04-25 22:32:05 +03:00
if(up[i] != 0.0 && up[i] != EMPTY_VALUE)
return up[i];
2026-05-03 17:59:01 +03:00
}
2026-04-25 22:32:05 +03:00
return EMPTY_VALUE;
2026-05-03 17:59:01 +03:00
}
2026-04-25 22:32:05 +03:00
2026-05-03 17:59:01 +03:00
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
2026-04-25 22:32:05 +03:00
double GetLastFractalDown(const double &dn[])
2026-05-03 17:59:01 +03:00
{
2026-04-25 22:32:05 +03:00
for(int i = 5; i < 300; i++)
2026-05-03 17:59:01 +03:00
{
2026-04-25 22:32:05 +03:00
if(dn[i] != 0.0 && dn[i] != EMPTY_VALUE)
return dn[i];
2026-05-03 17:59:01 +03:00
}
2026-04-25 22:32:05 +03:00
return EMPTY_VALUE;
2026-05-03 17:59:01 +03:00
}
2026-04-25 22:32:05 +03:00
2026-05-03 17:59:01 +03:00
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
2026-04-25 22:32:05 +03:00
void UpdateFractalsHTF()
2026-05-03 17:59:01 +03:00
{
if(handleFractals_H != INVALID_HANDLE)
{
ArrayResize(upHTF, 300);
ArrayResize(dnHTF, 300);
CopyBuffer(handleFractals_H, 0, 0, 300, upHTF);
CopyBuffer(handleFractals_H, 1, 0, 300, dnHTF);
}
}
2026-04-25 22:32:05 +03:00
2026-05-03 17:59:01 +03:00
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
2026-04-25 22:32:05 +03:00
bool TrendReversedStrongly(int trend)
2026-05-03 17:59:01 +03:00
{
2026-04-25 22:32:05 +03:00
bool structureBreak = false;
bool emaBreak = false;
bool aoFlip = false;
2026-05-03 17:59:01 +03:00
// --- 1. STRUCTURE BREAK ---
2026-04-25 22:32:05 +03:00
double lastUp = GetLastFractalUp(upHTF);
double lastDn = GetLastFractalDown(dnHTF);
if(trend == 1) // BUY-тренд
2026-05-03 17:59:01 +03:00
{
2026-04-25 22:32:05 +03:00
if(lastDn != EMPTY_VALUE && iLow(_Symbol, PERIOD_CURRENT, 1) < lastDn)
structureBreak = true;
2026-05-03 17:59:01 +03:00
}
else
if(trend == -1) // SELL-тренд
{
if(lastUp != EMPTY_VALUE && iHigh(_Symbol, PERIOD_CURRENT, 1) > lastUp)
structureBreak = true;
}
// --- 2. EMA BREAK ---
2026-04-25 22:32:05 +03:00
2026-05-03 17:59:01 +03:00
// --- READ EMA ---
2026-04-25 23:06:48 +03:00
double maBuf[1];
CopyBuffer(handleMA_H, 0, 2, 1, maBuf);
double ema = maBuf[0];
2026-04-25 22:32:05 +03:00
2026-05-03 17:59:01 +03:00
// --- EMA BREAK ---
2026-04-25 22:32:05 +03:00
if(trend == 1)
2026-05-03 17:59:01 +03:00
{
2026-04-25 23:06:48 +03:00
if(iClose(_Symbol, (ENUM_TIMEFRAMES)tfH, 1) < ema &&
2026-05-03 17:59:01 +03:00
iClose(_Symbol, (ENUM_TIMEFRAMES)tfH, 2) < ema)
2026-04-25 22:32:05 +03:00
emaBreak = true;
2026-05-03 17:59:01 +03:00
}
else
if(trend == -1)
{
if(iClose(_Symbol, (ENUM_TIMEFRAMES)tfH, 1) > ema &&
iClose(_Symbol, (ENUM_TIMEFRAMES)tfH, 2) > ema)
emaBreak = true;
}
2026-04-25 22:32:05 +03:00
2026-05-03 17:59:01 +03:00
// AO
2026-04-25 22:32:05 +03:00
static int handleAO = INVALID_HANDLE;
if(handleAO == INVALID_HANDLE)
2026-04-25 23:06:48 +03:00
handleAO = iAO(_Symbol, (ENUM_TIMEFRAMES)tfH);
2026-04-25 22:32:05 +03:00
2026-05-03 17:59:01 +03:00
// --- READ AO ---
2026-04-25 22:32:05 +03:00
double aoBuf[3];
2026-04-25 23:06:48 +03:00
CopyBuffer(handleAO_H, 0, 1, 3, aoBuf);
2026-04-25 22:32:05 +03:00
double ao0 = aoBuf[0];
double ao1 = aoBuf[1];
if(trend == 1)
2026-05-03 17:59:01 +03:00
{
2026-04-25 22:32:05 +03:00
if(ao0 < 0 && ao1 > 0)
aoFlip = true;
2026-05-03 17:59:01 +03:00
}
else
if(trend == -1)
{
if(ao0 > 0 && ao1 < 0)
aoFlip = true;
}
2026-04-25 22:32:05 +03:00
return (structureBreak && emaBreak && aoFlip);
2026-05-03 17:59:01 +03:00
}
2026-04-25 22:32:05 +03:00
2026-05-03 17:59:01 +03:00
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
2026-05-03 17:45:50 +03:00
bool FilterBars(int shift, ENUM_TIMEFRAMES tf)
2026-05-10 17:35:47 +03:00
{
// --- 0. Получаем текущие данные ---
double open = iOpen(_Symbol, tf, shift);
double close = iClose(_Symbol, tf, shift);
double high = iHigh(_Symbol, tf, shift);
double low = iLow(_Symbol, tf, shift);
double body = MathAbs(close - open);
double range = high - low;
// --- 1. ATR адаптивный ---
int atrHandle = iATR(_Symbol, tf, 14);
if(atrHandle == INVALID_HANDLE)
return true;
double atrBuf[1];
if(CopyBuffer(atrHandle, 0, shift, 1, atrBuf) <= 0)
return true;
double atr = atrBuf[0];
if(atr <= 0)
return true;
// Средний ATR инструмента (адаптация под инструмент)
int atrSlowHandle = iATR(_Symbol, tf, 50);
double atrSlowBuf[1];
if(CopyBuffer(atrSlowHandle, 0, shift, 1, atrSlowBuf) <= 0)
return true;
double atrAvg = atrSlowBuf[0];
// Коэффициент силы волатильности
double volaRatio = (atrAvg > 0 ? atr / atrAvg : 1.0);
// --- 2. Адаптивный порог ATR ---
double minAtr = atrAvg * 0.35; // вместо фиксированных 40 пунктов
if(atr < minAtr)
return false;
// --- 3. Размер тела свечи ---
double bodyRatio = (range > 0 ? body / range : 0);
// Если тренд сильный — допускаем 15%
// Если тренд слабый — требуем 25%
double minBodyRatio = (MathAbs(lastTrend) == 1 ? 0.15 : 0.25);
if(bodyRatio < minBodyRatio)
return false;
// --- 4. Диапазон последних N свечей ---
int N = 10;
int idxHigh = iHighest(_Symbol, tf, MODE_HIGH, N, shift);
int idxLow = iLowest(_Symbol, tf, MODE_LOW, N, shift);
double recentHigh = iHigh(_Symbol, tf, idxHigh);
double recentLow = iLow(_Symbol, tf, idxLow);
double recentRange = recentHigh - recentLow;
// Адаптивный порог
double minRecentRange = atrAvg * 0.7;
if(recentRange < minRecentRange)
return false;
// --- 5. AO + MACD (мягкий фильтр) ---
int aoHandle = iAO(_Symbol, tf);
double aoBuf2[2];
if(CopyBuffer(aoHandle, 0, shift, 2, aoBuf2) <= 0)
return true;
double ao0 = aoBuf2[0];
double ao1 = aoBuf2[1];
int macdHandle = iMACD(_Symbol, tf, 12, 26, 9, PRICE_CLOSE);
double macdHist[1];
if(CopyBuffer(macdHandle, 2, shift, 1, macdHist) <= 0)
return true;
double hist = macdHist[0];
// Если тренд сильный — допускаем несовпадение
if(MathAbs(lastTrend) == 1)
{
if((ao0 > 0 && hist < 0) && volaRatio < 0.8)
return false;
}
else
{
// Если тренд слабый — требуем совпадение
if((ao0 > 0 && hist < 0) || (ao0 < 0 && hist > 0))
return false;
}
return true;
}
2026-04-25 22:32:05 +03:00
2026-04-18 22:25:20 +03:00
//+------------------------------------------------------------------+
//| OnInit / OnDeinit / OnTick |
//+------------------------------------------------------------------+
int OnInit()
2026-05-03 17:59:01 +03:00
{
2026-04-18 22:25:20 +03:00
Print("🔄 Initializing EA for symbol ", _Symbol);
2026-04-11 00:22:38 +03:00
2026-04-18 22:25:20 +03:00
if(!InitLog())
Print("⚠️ Logging disabled (cannot init log)");
2026-04-11 00:22:38 +03:00
2026-05-03 17:59:01 +03:00
// первичная инициализация индикаторов
2026-04-18 22:25:20 +03:00
MqlTick tick;
if(SymbolInfoTick(_Symbol, tick))
2026-05-03 17:59:01 +03:00
{
2026-04-18 22:25:20 +03:00
double mid = (tick.bid + tick.ask) * 0.5;
double relSpread = (mid > 0 ? (tick.ask - tick.bid) / mid : 0.0);
UpdateDynamicTF(relSpread);
2026-05-03 17:59:01 +03:00
}
2026-04-11 00:22:38 +03:00
2026-05-03 17:59:01 +03:00
// мягкий сброс — не трогаем pending/позиции
2026-04-18 22:25:20 +03:00
RecreateAllIndicators();
LogCurrentTF();
ResetInternalState();
2026-04-11 00:22:38 +03:00
2026-04-18 22:25:20 +03:00
return(INIT_SUCCEEDED);
2026-05-03 17:59:01 +03:00
}
2026-04-11 00:22:38 +03:00
2026-05-03 17:59:01 +03:00
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
2026-04-18 22:25:20 +03:00
void OnDeinit(const int reason)
2026-05-03 17:59:01 +03:00
{
if(handleFractals_M != INVALID_HANDLE)
IndicatorRelease(handleFractals_M);
if(handleFractals_H != INVALID_HANDLE)
IndicatorRelease(handleFractals_H);
if(handleAO_M != INVALID_HANDLE)
IndicatorRelease(handleAO_M);
if(handleAO_H != INVALID_HANDLE)
IndicatorRelease(handleAO_H);
if(handleBB != INVALID_HANDLE)
IndicatorRelease(handleBB);
if(handleATR_M != INVALID_HANDLE)
IndicatorRelease(handleATR_M);
if(handleATRSlow != INVALID_HANDLE)
IndicatorRelease(handleATRSlow);
if(handleMA_H != INVALID_HANDLE)
IndicatorRelease(handleMA_H);
2026-04-11 00:22:38 +03:00
2026-04-18 22:25:20 +03:00
if(logHandle != INVALID_HANDLE)
FileClose(logHandle);
2026-05-03 17:59:01 +03:00
}
2026-04-11 00:22:38 +03:00
2026-05-03 17:59:01 +03:00
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
2026-04-18 22:25:20 +03:00
void OnTick()
2026-06-12 13:11:05 +03:00
{
2026-04-18 22:25:20 +03:00
MqlTick tick;
if(!SymbolInfoTick(_Symbol, tick))
2026-04-11 00:22:38 +03:00
return;
2026-04-18 22:25:20 +03:00
double bid = tick.bid;
double ask = tick.ask;
double mid = (bid + ask) * 0.5;
double spread = ask - bid;
double relSpread = (mid > 0 ? spread / mid : 0.0);
2026-04-11 00:22:38 +03:00
2026-06-12 13:11:05 +03:00
// 1. Обновляем TF/индикаторы
2026-04-18 22:25:20 +03:00
UpdateDynamicTF(relSpread);
2026-04-25 22:32:05 +03:00
UpdateFractalsHTF();
2026-04-11 00:22:38 +03:00
2026-06-12 13:11:05 +03:00
// 2. Ждём HTF
2026-04-18 22:25:20 +03:00
if(!htfReady)
2026-06-12 13:11:05 +03:00
{
2026-04-18 22:25:20 +03:00
if(GetMicrosecondCount() - htfInitTime < 300000)
return;
2026-06-12 13:11:05 +03:00
htfReady = true;
Print("✅ HTF indicators loaded");
}
2026-04-11 00:22:38 +03:00
2026-06-12 13:11:05 +03:00
// 3. Ждём MTF
2026-04-18 22:25:20 +03:00
if(!mtfReady)
2026-06-12 13:11:05 +03:00
{
2026-04-18 22:25:20 +03:00
if(GetMicrosecondCount() - mtfInitTime < 300000)
return;
2026-06-12 13:11:05 +03:00
if(handleFractals_M != INVALID_HANDLE &&
handleAO_M != INVALID_HANDLE &&
handleATR_M != INVALID_HANDLE)
{
mtfReady = true;
Print("✅ MTF indicators loaded");
}
2026-04-18 22:25:20 +03:00
else
2026-06-12 13:11:05 +03:00
{
Print("⛔ MTF indicators still invalid — waiting...");
mtfInitTime = GetMicrosecondCount();
return;
}
}
2026-04-11 00:22:38 +03:00
2026-06-12 13:11:05 +03:00
// 4. Новый бар tfM?
2026-04-18 22:25:20 +03:00
datetime curBarTime = iTime(_Symbol, (ENUM_TIMEFRAMES)tfM, 0);
bool newM = (curBarTime != lastBarTimeMinutes);
if(newM)
2026-06-12 13:11:05 +03:00
{
2026-04-18 22:25:20 +03:00
lastBarTimeMinutes = curBarTime;
2026-04-11 00:22:38 +03:00
2026-04-18 22:25:20 +03:00
double mid = (tick.bid + tick.ask) * 0.5;
double relSpread = (mid > 0 ? (tick.ask - tick.bid) / mid : 0.0);
UpdateDynamicTF(relSpread);
2026-04-11 00:22:38 +03:00
2026-04-18 22:25:20 +03:00
if(pending_tfM != tfM || pending_tfH != tfH)
2026-06-12 13:11:05 +03:00
{
2026-04-18 22:25:20 +03:00
PrintFormat("🔄 Reinitializing indicators: M=%s -> %s, H=%s -> %s",
2026-05-03 17:59:01 +03:00
TimeframeToString(tfM), TimeframeToString(pending_tfM),
TimeframeToString(tfH), TimeframeToString(pending_tfH));
2026-04-11 00:22:38 +03:00
2026-04-18 22:25:20 +03:00
tfM = pending_tfM;
tfH = pending_tfH;
2026-04-11 00:22:38 +03:00
2026-04-18 22:25:20 +03:00
RecreateAllIndicators();
LogCurrentTF();
return;
2026-06-12 13:11:05 +03:00
}
}
2026-04-11 00:22:38 +03:00
2026-06-12 13:11:05 +03:00
// 5. Новый бар HTF → тренд
2026-04-18 22:25:20 +03:00
datetime htfBar = iTime(_Symbol, (ENUM_TIMEFRAMES)tfH, 0);
if(htfBar != lastHTFBar)
2026-06-12 13:11:05 +03:00
{
2026-04-18 22:25:20 +03:00
lastHTFBar = htfBar;
lastTrend = GetTrendAndPivot(lastPivot);
PrintFormat("TREND UPDATE: trend=%d pivot=%.5f (tfH=%s)",
lastTrend, lastPivot, TimeframeToString(tfH));
2026-06-12 13:11:05 +03:00
}
2026-04-11 00:22:38 +03:00
2026-06-12 13:11:05 +03:00
// 6. Если нет нового бара — выходим
2026-04-18 22:25:20 +03:00
if(!newM)
2026-04-11 00:22:38 +03:00
return;
2026-06-12 13:11:05 +03:00
// 7. Проверяем M-индикаторы
2026-04-18 22:25:20 +03:00
if(handleFractals_M == INVALID_HANDLE ||
handleAO_M == INVALID_HANDLE ||
handleATR_M == INVALID_HANDLE)
2026-06-12 13:11:05 +03:00
{
2026-04-18 22:25:20 +03:00
Print("⏳ Waiting MTF indicators to load...");
2026-04-11 00:22:38 +03:00
return;
2026-06-12 13:11:05 +03:00
}
2026-04-11 00:22:38 +03:00
2026-06-12 13:11:05 +03:00
// 8. Проверяем позиции
2026-04-18 22:25:20 +03:00
bool hasPos = HasOpenPosition();
bool hasPend = HasPendingOrders();
2026-04-11 00:22:38 +03:00
2026-04-18 22:25:20 +03:00
if(!hasPos && !hasPend)
2026-06-12 13:11:05 +03:00
{
2026-04-18 22:25:20 +03:00
ResetInternalState();
2026-05-03 17:59:01 +03:00
2026-04-18 22:25:20 +03:00
if(lastTrend != 0 && FindEntrySignal())
2026-06-12 13:11:05 +03:00
{
Print("📌 Entry signal found — preparing LIMIT pending...");
// --- ATR для адаптивного входа ---
double atr = GetATR(14);
if(atr <= 0)
{
Print("⛔ ATR invalid");
return;
}
// --- объявляем и инициализируем переменные заранее ---
double entry = lastEntry;
double sl = lastStop;
double tp = lastTp;
// --- BUY LIMIT ---
if(lastTrend == 1)
{
entry = lastEntry - atr * 0.3;
if(entry >= ask)
{
PrintFormat("⛔ BUY LIMIT rejected: entry %.5f >= ask %.5f", entry, ask);
return;
}
double dist = ask - entry;
double stopsLevel = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL) * _Point;
if(dist < stopsLevel)
{
PrintFormat("⛔ BUY LIMIT too close: dist=%.5f stopsLevel=%.5f", dist, stopsLevel);
return;
}
}
2026-05-03 17:59:01 +03:00
2026-06-12 13:11:05 +03:00
// --- SELL LIMIT ---
if(lastTrend == -1)
{
entry = lastEntry + atr * 0.3;
2026-05-03 17:59:01 +03:00
2026-06-12 13:11:05 +03:00
if(entry <= bid)
{
PrintFormat("⛔ SELL LIMIT rejected: entry %.5f <= bid %.5f", entry, bid);
return;
}
2026-05-03 17:59:01 +03:00
2026-06-12 13:11:05 +03:00
double dist = entry - bid;
double stopsLevel = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL) * _Point;
2026-05-03 17:59:01 +03:00
2026-06-12 13:11:05 +03:00
if(dist < stopsLevel)
{
PrintFormat("⛔ SELL LIMIT too close: dist=%.5f stopsLevel=%.5f", dist, stopsLevel);
return;
}
}
2026-05-03 17:59:01 +03:00
2026-06-12 13:11:05 +03:00
// --- SL/TP проверки остаются прежними ---
double stopsLevel = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL) * _Point;
double mktPrice = (lastTrend == 1 ? ask : bid);
2026-04-20 21:30:01 +03:00
2026-06-12 13:11:05 +03:00
double distSL_mkt = MathAbs(sl - mktPrice);
double distTP_mkt = MathAbs(tp - mktPrice);
2026-04-20 21:30:01 +03:00
2026-06-12 13:11:05 +03:00
if(distSL_mkt < stopsLevel || distTP_mkt < stopsLevel)
{
PrintFormat("⛔ SL/TP too close: SLdist=%.5f TPdist=%.5f stopsLevel=%.5f",
distSL_mkt, distTP_mkt, stopsLevel);
return;
}
2026-05-03 17:59:01 +03:00
2026-06-12 13:11:05 +03:00
// --- ставим LIMIT pending ---
PlacePending(lastTrend, entry, sl, tp);
trendAtPlacement = lastTrend;
}
2026-04-11 00:22:38 +03:00
2026-04-18 22:25:20 +03:00
return;
2026-06-12 13:11:05 +03:00
}
2026-04-11 00:22:38 +03:00
2026-06-12 13:11:05 +03:00
// 9. Управление позицией
2026-04-18 22:25:20 +03:00
if(hasPos)
2026-06-12 13:11:05 +03:00
{
2026-04-18 22:25:20 +03:00
TrailByStructure(tfM, tfH, lastPivot);
CheckAOExit();
2026-06-12 13:11:05 +03:00
}
2026-04-18 22:25:20 +03:00
2026-06-12 13:11:05 +03:00
// 10. Если есть pending и тренд сменился — удаляем
2026-04-18 22:25:20 +03:00
if(hasPend)
2026-06-12 13:11:05 +03:00
{
2026-05-03 17:45:50 +03:00
if(!trendAtPlacement)
2026-06-12 13:11:05 +03:00
{
2026-05-03 17:45:50 +03:00
Print("🧹 Trend changed — deleting pending");
2026-04-18 22:25:20 +03:00
CleanupPendingOrders();
2026-06-12 13:11:05 +03:00
}
CleanupLimitOrders();
}
}