Просьба написать советник для МТ4
1. Советник открывает по одному ордеру каждый день в заданное время
2. Направление ордера зависит от выбора одного из двух вариантов в настройках (1,2)
Если 1, ордер открывается в направлении последней закрывшейся свечи (бычья — покупка, медвежья — продажа)
Если 2, ордер открывается в противоположном направлении (бычья — продажа, медвежья — покупка)
Из этого следует, что возможные ТФ для работы от М1 до Н4
3. В конце дня (00:00) ордер закрывается советником, если ранее не успел закрыться сам по ТП или СЛ
4. В код желательно добавить функцию для автоматического повышения/понижения объёма следующего ордера при изменении баланса
Пример: Начальный лот задан 0.1, баланс 10К. Если после закрытия очередного ордера по ТП баланс стал 11К или более,
следующий открывается 0.11. При 12К — 0.12. И т.д. При уменьшении баланса лот меняется по такому же принципу в сторону уменьшения
Настройки:
Время
Направление (1,2)
Начальный лот
ТП
СЛ
-
0
- Просмотров: 1135
- 23 октября 2025, 19:19
- Kashtan
Понравилcя материал? Не забудьте поставить плюс и поделиться в социальной сети!
Комментарии (20)
35 AM2 Сообщений: 16665 - Андрей
24 igrun Сообщений: 1814 - igrun
24 igrun Сообщений: 1814 - igrun
35 AM2 Сообщений: 16665 - Андрей
19 Kashtan Автор Сообщений: 747 - Игорь
24 igrun Сообщений: 1814 - igrun
24 igrun Сообщений: 1814 - igrun
19 Kashtan Автор Сообщений: 747 - Игорь
24 igrun Сообщений: 1814 - igrun
24 igrun Сообщений: 1814 - igrun
24 igrun Сообщений: 1814 - igrun
19 Kashtan Автор Сообщений: 747 - Игорь
24 igrun Сообщений: 1814 - igrun
19 Kashtan Автор Сообщений: 747 - Игорь
24 igrun Сообщений: 1814 - igrun
24 igrun Сообщений: 1814 - igrun
19 Kashtan Автор Сообщений: 747 - Игорь
//| DailyTrader.mq4 |
//| Copyright 2023, MetaQuotes Software Corp. |
//| www.mql5.com |
//+------------------------------------------------------------------+
#property copyright «Copyright 2023, MetaQuotes Software Corp.»
#property link «www.mql5.com»
#property version «1.00»
#property strict
//--- Входные параметры
input string TradeTime = «10:00»; // Время открытия ордера (формат «ЧЧ: ММ»)
input int DirectionMode = 1; // Режим направления (1-по тренду, 2-против тренда)
input double InitialLot = 0.1; // Начальный лот
input double TakeProfit = 50.0; // Тейк-профит в пунктах
input double StopLoss = 30.0; // Стоп-лосс в пунктах
input int MagicNumber = 12345; // Магический номер ордеров
//--- Глобальные переменные
datetime lastTradeDate = 0;
datetime lastCloseTime = 0;
bool closeInProgress = false;
double currentLot = InitialLot;
double balanceReference = 0;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
balanceReference = AccountBalance();
Print(«Советник инициализирован. Начальный баланс: », balanceReference);
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
Print(«Советник деинициализирован»);
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
datetime currentTime = TimeCurrent();
// В первую очередь — проверяем необходимость закрытия ордеров в конце дня
if(IsEndOfDayTime() && !closeInProgress)
{
if(lastCloseTime != TimeCurrentDate())
{
Print("=== КОНЕЦ ДНЯ: Начинаем закрытие ордеров ===");
closeInProgress = true;
if(CloseAllOrders())
{
lastCloseTime = TimeCurrentDate();
lastTradeDate = 0; // Сбрасываем дату торговли
Print("=== Все ордера закрыты ===");
}
closeInProgress = false;
}
return;
}
// Сбрасываем флаг закрытия после 01:00
if(TimeHour(currentTime) == 1 && TimeMinute(currentTime) == 0 && lastCloseTime == TimeCurrentDate() — 86400)
{
lastCloseTime = 0;
Print(«Сброс флагов для нового торгового дня»);
}
// Если идет процесс закрытия — не открываем новые ордера
if(closeInProgress) return;
// Проверяем, есть ли открытые ордера — если есть, не открываем новые
if(CountOpenOrders() > 0)
{
// Если сегодня уже должна была быть торговля, но ордера остались — закрываем их
if(lastTradeDate == TimeCurrentDate())
{
Print(«Обнаружены открытые ордера в торговое время. Закрываем...»);
CloseAllOrders();
lastTradeDate = 0; // Сбрасываем дату торговли
}
return;
}
// Проверяем, было ли уже открытие ордера сегодня
if(lastTradeDate == TimeCurrentDate()) return;
// Проверяем время открытия ордера
if(IsTradeTime())
{
Print("=== Время открытия ордера ===");
OpenTrade();
lastTradeDate = TimeCurrentDate();
}
}
//+------------------------------------------------------------------+
//| Проверка времени закрытия (конец дня) |
//+------------------------------------------------------------------+
bool IsEndOfDayTime()
{
datetime currentTime = TimeCurrent();
MqlDateTime timeStruct;
TimeToStruct(currentTime, timeStruct);
// Закрываем с 23:55 до 00:05 (окно 10 минут для надежности)
if((timeStruct.hour == 23 && timeStruct.min >= 55) ||
(timeStruct.hour == 0 && timeStruct.min <= 5))
{
return true;
}
return false;
}
24 igrun Сообщений: 1814 - igrun
//| Проверка времени для открытия ордера |
//+------------------------------------------------------------------+
bool IsTradeTime()
{
datetime currentTime = TimeCurrent();
MqlDateTime timeStruct;
TimeToStruct(currentTime, timeStruct);
string tradeTimeStr = TradeTime;
int tradeHour = (int)StringToInteger(StringSubstr(tradeTimeStr, 0, 2));
int tradeMinute = (int)StringToInteger(StringSubstr(tradeTimeStr, 3, 2));
return (timeStruct.hour == tradeHour && timeStruct.min == tradeMinute && timeStruct.sec >= 0 && timeStruct.sec <= 59);
}
//+------------------------------------------------------------------+
//| Функция открытия торговли |
//+------------------------------------------------------------------+
void OpenTrade()
{
// Перед открытием финальная проверка — нет ли открытых ордеров
if(CountOpenOrders() > 0)
{
Print(«ОШИБКА: Обнаружены открытые ордера перед открытием нового!»);
return;
}
// Обновляем объем лота на основе изменения баланса
UpdateLotSize();
int orderType = GetOrderDirection();
double openPrice = 0;
double sl = 0, tp = 0;
if(orderType == OP_BUY)
{
openPrice = Ask;
sl = CalculateSL(openPrice, OP_BUY);
tp = CalculateTP(openPrice, OP_BUY);
}
else if(orderType == OP_SELL)
{
openPrice = Bid;
sl = CalculateSL(openPrice, OP_SELL);
tp = CalculateTP(openPrice, OP_SELL);
}
else
{
Print(«Ошибка: Не удалось определить направление ордера»);
return;
}
// Проверяем корректность стоп-уровней
if(!ValidateStopLevels(openPrice, sl, tp, orderType))
{
Print(«Ошибка: Некорректные стоп-уровни. Ордер не открыт.»);
return;
}
int ticket = OrderSend(Symbol(), orderType, currentLot, openPrice, 3, sl, tp,
«DailyTrade», MagicNumber, 0, clrBlue);
if(ticket > 0)
{
Print(«УСПЕХ: Ордер открыт: », OrderTypeToString(orderType),
", Лот: ", currentLot,
", Цена: ", openPrice,
", SL: ", sl,
", TP: ", tp);
}
else
{
int error = GetLastError();
Print(«ОШИБКА открытия ордера: », error, " — ", GetErrorDescription(error));
// Дополнительная диагностика для ошибки 130
if(error == 130)
{
Print(«ДИАГНОСТИКА 130:»);
Print(" — Ask: ", Ask, ", Bid: ", Bid);
Print(" — SL: ", sl, ", TP: ", tp);
Print(" — Stop Level: ", MarketInfo(Symbol(), MODE_STOPLEVEL));
Print(" — Spread: ", MarketInfo(Symbol(), MODE_SPREAD));
}
}
}
//+------------------------------------------------------------------+
//| Проверка корректности стоп-уровней |
//+------------------------------------------------------------------+
bool ValidateStopLevels(double price, double &sl, double &tp, int orderType)
{
if(sl == 0 && tp == 0) return true;
double point = MarketInfo(Symbol(), MODE_POINT);
double spread = MarketInfo(Symbol(), MODE_SPREAD) * point;
double stopLevel = MarketInfo(Symbol(), MODE_STOPLEVEL) * point;
double minDistance = stopLevel + spread;
// Добавляем запас 10% для надежности
minDistance *= 1.1;
if(orderType == OP_BUY)
{
if(sl > 0 && (price — sl) < minDistance)
{
double newSL = NormalizeDouble(price — minDistance, Digits);
Print(«Корректируем SL для BUY. Было: », sl, " Стало: ", newSL);
sl = newSL;
}
if(tp > 0 && (tp — price) < minDistance)
{
double newTP = NormalizeDouble(price + minDistance, Digits);
Print(«Корректируем TP для BUY. Было: », tp, " Стало: ", newTP);
tp = newTP;
}
}
else if(orderType == OP_SELL)
{
if(sl > 0 && (sl — price) < minDistance)
{
double newSL = NormalizeDouble(price + minDistance, Digits);
Print(«Корректируем SL для SELL. Было: », sl, " Стало: ", newSL);
sl = newSL;
}
if(tp > 0 && (price — tp) < minDistance)
{
double newTP = NormalizeDouble(price — minDistance, Digits);
Print(«Корректируем TP для SELL. Было: », tp, " Стало: ", newTP);
tp = newTP;
}
}
return true;
}
//+------------------------------------------------------------------+
//| Определение направления ордера |
//+------------------------------------------------------------------+
int GetOrderDirection()
{
// Получаем информацию о последней закрытой свече
double open = iOpen(Symbol(), PERIOD_CURRENT, 1);
double close = iClose(Symbol(), PERIOD_CURRENT, 1);
bool isBullish = (close > open);
if(DirectionMode == 1)
{
return isBullish? OP_BUY: OP_SELL;
}
else if(DirectionMode == 2)
{
return isBullish? OP_SELL: OP_BUY;
}
return -1;
}
24 igrun Сообщений: 1814 - igrun
//| Обновление размера лота на основе изменения баланса |
//+------------------------------------------------------------------+
void UpdateLotSize()
{
double currentBalance = AccountBalance();
double balanceChange = currentBalance / balanceReference;
currentLot = NormalizeDouble(InitialLot * balanceChange, 2);
// Проверяем минимальный и максимальный лот
double minLot = MarketInfo(Symbol(), MODE_MINLOT);
double maxLot = MarketInfo(Symbol(), MODE_MAXLOT);
double lotStep = MarketInfo(Symbol(), MODE_LOTSTEP);
if(currentLot < minLot) currentLot = minLot;
if(currentLot > maxLot) currentLot = maxLot;
currentLot = MathRound(currentLot / lotStep) * lotStep;
currentLot = NormalizeDouble(currentLot, 2);
Print(«Обновление лота: Баланс=», currentBalance, ", Референс=", balanceReference, ", Лот=", currentLot);
}
//+------------------------------------------------------------------+
//| Расчет стоп-лосса |
//+------------------------------------------------------------------+
double CalculateSL(double price, int orderType)
{
if(StopLoss == 0) return 0;
double point = MarketInfo(Symbol(), MODE_POINT);
double slPoints = StopLoss * point;
if(orderType == OP_BUY)
return NormalizeDouble(price — slPoints, Digits);
else if(orderType == OP_SELL)
return NormalizeDouble(price + slPoints, Digits);
return 0;
}
//+------------------------------------------------------------------+
//| Расчет тейк-профита |
//+------------------------------------------------------------------+
double CalculateTP(double price, int orderType)
{
if(TakeProfit == 0) return 0;
double point = MarketInfo(Symbol(), MODE_POINT);
double tpPoints = TakeProfit * point;
if(orderType == OP_BUY)
return NormalizeDouble(price + tpPoints, Digits);
else if(orderType == OP_SELL)
return NormalizeDouble(price — tpPoints, Digits);
return 0;
}
//+------------------------------------------------------------------+
//| Закрытие всех ордеров |
//+------------------------------------------------------------------+
bool CloseAllOrders()
{
int totalOrders = OrdersTotal();
if(totalOrders == 0)
{
Print(«Нет ордеров для закрытия»);
return true;
}
int closedCount = 0;
int errorCount = 0;
for(int i = totalOrders — 1; i >= 0; i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
{
double closePrice = 0;
int orderType = OrderType();
if(orderType == OP_BUY)
closePrice = Bid;
else if(orderType == OP_SELL)
closePrice = Ask;
else
continue; // Пропускаем отложенные ордера
bool result = OrderClose(OrderTicket(), OrderLots(), closePrice, 3, clrRed);
if(result)
{
closedCount++;
Print(«Ордер закрыт: », OrderTypeToString(orderType),
", Прибыль: ", OrderProfit());
}
else
{
errorCount++;
int error = GetLastError();
Print(«Ошибка закрытия ордера », OrderTypeToString(orderType),
": ", error, " — ", GetErrorDescription(error));
}
// Небольшая пауза между закрытиями
Sleep(100);
}
}
}
// Обновляем баланс после закрытия ордеров
balanceReference = AccountBalance();
Print(«Итог закрытия: успешно=», closedCount, ", ошибки=", errorCount, ", новый баланс=", balanceReference);
return (errorCount == 0);
}
//+------------------------------------------------------------------+
//| Подсчет открытых ордеров |
//+------------------------------------------------------------------+
int CountOpenOrders()
{
int count = 0;
for(int i = 0; i < OrdersTotal(); i++)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber &&
(OrderType() == OP_BUY || OrderType() == OP_SELL))
{
count++;
}
}
}
return count;
}
//+------------------------------------------------------------------+
//| Получение даты без времени |
//+------------------------------------------------------------------+
datetime TimeCurrentDate()
{
MqlDateTime timeStruct;
TimeToStruct(TimeCurrent(), timeStruct);
timeStruct.hour = 0;
timeStruct.min = 0;
timeStruct.sec = 0;
return StructToTime(timeStruct);
}
//+------------------------------------------------------------------+
//| Конвертация типа ордера в строку |
//+------------------------------------------------------------------+
string OrderTypeToString(int orderType)
{
if(orderType == OP_BUY) return «BUY»;
if(orderType == OP_SELL) return «SELL»;
return «UNKNOWN»;
}
//+------------------------------------------------------------------+
//| Получение описания ошибки |
//+------------------------------------------------------------------+
string GetErrorDescription(int errorCode)
{
switch(errorCode)
{
case 130: return «Неправильные стопы — проверьте минимальные расстояния»;
case 131: return «Неправильный объем»;
case 132: return «Неправильная цена»;
case 133: return «Торговля запрещена»;
case 134: return «Недостаточно денег»;
case 135: return «Цена изменилась»;
case 136: return «Нет цен»;
case 137: return «Брокер занят»;
case 138: return «Требуется перезагрузка цен»;
case 139: return «Ордер заблокирован»;
case 140: return «Разрешение на торговлю запрещено»;
case 146: return «Торговая подсистема занята»;
case 4108: return «Ордер не найден»;
default: return «Неизвестная ошибка: » + IntegerToString(errorCode);
}
}
24 igrun Сообщений: 1814 - igrun
Зарегистрируйтесь или авторизуйтесь, чтобы оставить комментарий