//+------------------------------------------------------------------+
//| Обновление размера лота на основе изменения баланса |
//+------------------------------------------------------------------+
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);
}
//+------------------------------------------------------------------+
//| Расчет стоп-лосса |
//+------------------------------------------------------------------+
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;
}
//+------------------------------------------------------------------+
//| Закрытие всех ордеров |
//+------------------------------------------------------------------+
void CloseAllOrders()
{
int totalOrders = OrdersTotal();
if(totalOrders == 0) return;
int closedCount = 0;
for(int i = totalOrders - 1; i >= 0; i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == 0)
{
double closePrice = 0;
int orderType = OrderType();
if(orderType == OP_BUY)
closePrice = Bid;
else if(orderType == OP_SELL)
closePrice = Ask;
else
continue; // Пропускаем отложенные ордера
if(OrderClose(OrderTicket(), OrderLots(), closePrice, 3, clrRed))
{
closedCount++;
Print("Ордер закрыт в конце дня: ", OrderTypeToString(orderType),
", Прибыль: ", OrderProfit(), ", Баланс: ", AccountBalance());
}
else
{
Print("Ошибка закрытия ордера ", OrderTypeToString(orderType),
": ", GetLastError(), " - ", GetErrorDescription(GetLastError()));
}
}
}
}
if(closedCount > 0)
{
balanceReference = AccountBalance();
Print("Итог: закрыто ", closedCount, " ордеров. Новый баланс: ", balanceReference);
}
}
//+------------------------------------------------------------------+
//| Подсчет открытых ордеров |
//+------------------------------------------------------------------+
int CountOpenOrders()
{
int count = 0;
for(int i = 0; i < OrdersTotal(); i++)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == 0 &&
(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);
}
}
//+------------------------------------------------------------------+
//| DailyTrader.mq4 |
//| Copyright 2023, MetaQuotes Software Corp. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Software Corp."
#property link "https://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; // Стоп-лосс в пунктах
//--- Глобальные переменные
datetime lastTradeDate = 0;
datetime lastCloseTime = 0;
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();
// Проверяем закрытие ордеров в конце дня (23:59:30 - 00:00:30)
if(IsEndOfDayTime())
{
if(lastCloseTime != TimeCurrentDate())
{
Print("Время закрытия ордеров в конце дня: ", TimeToString(currentTime));
CloseAllOrders();
lastCloseTime = TimeCurrentDate();
}
return;
}
// Сбрасываем флаг закрытия после 00:30
if(TimeHour(currentTime) == 0 && TimeMinute(currentTime) >= 30 && lastCloseTime == TimeCurrentDate() - 86400)
{
lastCloseTime = 0;
lastTradeDate = 0;
Print("Сброс флагов для нового торгового дня: ", TimeToString(currentTime));
}
// Проверяем, было ли уже открытие ордера сегодня
if(lastTradeDate == TimeCurrentDate())
return;
// Проверяем время открытия ордера
if(IsTradeTime())
{
// Перед открытием нового ордера проверяем, что нет открытых ордеров
if(CountOpenOrders() == 0)
{
OpenTrade();
lastTradeDate = TimeCurrentDate();
}
else
{
Print("ВНИМАНИЕ: Обнаружены открытые ордера перед открытием нового. Закрываем их.");
CloseAllOrders();
// Не открываем новый ордер сегодня, ждем следующего дня
lastTradeDate = TimeCurrentDate();
}
}
}
//+------------------------------------------------------------------+
//| Проверка времени закрытия (конец дня) |
//+------------------------------------------------------------------+
bool IsEndOfDayTime()
{
datetime currentTime = TimeCurrent();
MqlDateTime timeStruct;
TimeToStruct(currentTime, timeStruct);
// Закрываем с 23:59:30 до 00:00:30 (окно 1 минута)
if((timeStruct.hour == 23 && timeStruct.min == 59 && timeStruct.sec >= 30) ||
(timeStruct.hour == 0 && timeStruct.min == 0 && timeStruct.sec <= 30))
{
return true;
}
return false;
}
//+------------------------------------------------------------------+
//| Проверка времени для открытия ордера |
//+------------------------------------------------------------------+
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);
}
//+------------------------------------------------------------------+
//| Функция открытия торговли |
//+------------------------------------------------------------------+
void OpenTrade()
{
// Обновляем объем лота на основе изменения баланса
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
return;
// Проверяем корректность стоп-уровней
if(!ValidateStopLevels(openPrice, sl, tp, orderType))
{
Print("Ошибка: Некорректные стоп-уровни. Ордер не открыт.");
return;
}
int ticket = OrderSend(Symbol(), orderType, currentLot, openPrice, 3, sl, tp,
"DailyTrade", 0, 0, clrBlue);
if(ticket > 0)
{
Print("Ордер открыт: ", OrderTypeToString(orderType),
", Лот: ", currentLot,
", Время: ", TimeToString(TimeCurrent()),
", Цена: ", openPrice,
", SL: ", sl,
", TP: ", tp);
}
else
{
Print("Ошибка открытия ордера: ", GetLastError(), " - ", GetErrorDescription(GetLastError()));
}
}
//+------------------------------------------------------------------+
//| Проверка корректности стоп-уровней |
//+------------------------------------------------------------------+
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 = MathMax(stopLevel, spread * 2);
if(orderType == OP_BUY)
{
if(sl > 0 && (price - sl) < minDistance)
{
double newSL = price - minDistance;
Print("Корректируем SL для BUY. Было: ", sl, " Стало: ", newSL);
sl = newSL;
}
if(tp > 0 && (tp - price) < minDistance)
{
double newTP = price + minDistance;
Print("Корректируем TP для BUY. Было: ", tp, " Стало: ", newTP);
tp = newTP;
}
}
else if(orderType == OP_SELL)
{
if(sl > 0 && (sl - price) < minDistance)
{
double newSL = price + minDistance;
Print("Корректируем SL для SELL. Было: ", sl, " Стало: ", newSL);
sl = newSL;
}
if(tp > 0 && (price - tp) < minDistance)
{
double newTP = price - minDistance;
Print("Корректируем TP для SELL. Было: ", tp, " Стало: ", newTP);
tp = newTP;
}
}
// Нормализуем цены
sl = NormalizeDouble(sl, Digits);
tp = NormalizeDouble(tp, Digits);
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;
}
igrun