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