Исходный код
Представленный ниже код является экспертным советником (Expert Advisor) для MQL4, который реализует логику частичного закрытия сетки. Он предназначен для работы в связке с любой сеточной стратегией, использующей уникальный MagicNumber. Советник сканирует открытые ордера, определяет общую просадку по сетке (отдельно для покупок и продаж), и если она превышает заданный порог, приступает к частичному закрытию убыточных ордеров. Вы можете настроить, закрывать ли самые старые или самые убыточные ордера, а также процент закрываемого объема. Этот подход помогает активно управлять риском и снижать потенциальную просадку, не закрывая всю сетку целиком.
#property copyright "FinFluct"
#property link "https://finfluct.com"
#property version "1.00"
#property strict
//--- Input parameters
extern int MagicNumber = 12345; // Magic number for EA's orders
extern double MinLossToClose = 100.0; // Minimum total loss (in account currency) for a grid to trigger partial close
extern double ClosePercentage = 0.25; // Percentage of volume to close from each selected order (e.g., 0.25 for 25%)
extern int MaxOrdersToClose = 3; // Maximum number of orders to partially close per processing cycle
extern bool CloseOnlyOldest = true; // If true, closes oldest losing orders first; otherwise, closes most losing orders first
extern int Slippage = 3; // Maximum allowed slippage in points
extern string CommentPrefix = "PartialClose"; // Prefix for order comments
//--- Structure to hold order information for sorting
struct OrderInfo {
int ticket;
int type;
double volume;
double profit;
datetime openTime;
};
//--- Global variable to prevent multiple executions on the same tick
datetime lastTickTime = 0;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit() {
Print("Partial Grid Close EA initialized. Magic: ", MagicNumber);
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason) {
Print("Partial Grid Close EA deinitialized.");
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick() {
// Prevent multiple executions on the same tick
if (lastTickTime == TimeCurrent()) return;
lastTickTime = TimeCurrent();
//--- Collect all relevant orders
OrderInfo buyOrders[];
OrderInfo sellOrders[];
int buyCount = 0;
int sellCount = 0;
for (int i = OrdersTotal() - 1; i >= 0; i--) {
if (!OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) continue;
// Filter by current symbol and magic number
if (OrderSymbol() != Symbol() || OrderMagicNumber() != MagicNumber) continue;
// Only consider BUY and SELL orders
if (OrderType() != OP_BUY && OrderType() != OP_SELL) continue;
OrderInfo currentOrder;
currentOrder.ticket = OrderTicket();
currentOrder.type = OrderType();
currentOrder.volume = OrderLots();
currentOrder.profit = OrderProfit();
currentOrder.openTime = OrderOpenTime();
if (currentOrder.type == OP_BUY) {
ArrayResize(buyOrders, buyCount + 1);
buyOrders[buyCount] = currentOrder;
buyCount++;
} else if (currentOrder.type == OP_SELL) {
ArrayResize(sellOrders, sellCount + 1);
sellOrders[sellCount] = currentOrder;
sellCount++;
}
}
//--- Process BUY orders grid
ProcessGrid(buyOrders, buyCount, OP_BUY);
//--- Process SELL orders grid
ProcessGrid(sellOrders, sellCount, OP_SELL);
}
//+------------------------------------------------------------------+
//| Helper function to process a grid of orders (either BUY or SELL) |
//+------------------------------------------------------------------+
void ProcessGrid(OrderInfo& orders[], int count, int orderType) {
if (count == 0) return;
//--- Calculate total profit/loss for this grid
double totalGridProfit = 0;
for (int i = 0; i < count; i++) {
totalGridProfit += orders[i].profit;
}
//--- Check if the grid is in significant loss
if (totalGridProfit < -MinLossToClose) {
Print("Grid (", (orderType == OP_BUY ? "BUY" : "SELL"), ") total loss: ", DoubleToString(totalGridProfit, 2), " exceeds threshold: ", DoubleToString(MinLossToClose, 2));
//--- Sort orders based on configuration
if (CloseOnlyOldest) {
// Sort by open time ascending (oldest first) using bubble sort
for (int i = 0; i < count - 1; i++) {
for (int j = i + 1; j < count; j++) {
if (orders[i].openTime > orders[j].openTime) {
OrderInfo temp = orders[i];
orders[i] = orders[j];
orders[j] = temp;
}
}
}
} else {
// Sort by profit ascending (most losing first) using bubble sort
for (int i = 0; i < count - 1; i++) {
for (int j = i + 1; j < count; j++) {
if (orders[i].profit > orders[j].profit) { // Swap if i is less losing than j
OrderInfo temp = orders[i];
orders[i] = orders[j];
orders[j] = temp;
}
}
}
}
int closedCount = 0;
for (int i = 0; i < count && closedCount < MaxOrdersToClose; i++) {
// Only consider closing losing orders
if (orders[i].profit < 0) {
double volumeToClose = orders[i].volume * ClosePercentage;
// Ensure volume is within allowed limits
double minLot = MarketInfo(Symbol(), MODE_MINLOT);
double lotStep = MarketInfo(Symbol(), MODE_LOTSTEP);
// Round volume to lot step
volumeToClose = NormalizeDouble(volumeToClose / lotStep, 0) * lotStep;
if (volumeToClose < minLot) {
Print("Calculated volume ", DoubleToString(volumeToClose, 2), " for order ", orders[i].ticket, " is less than min lot (", DoubleToString(minLot, 2), "). Skipping partial close.");
continue;
}
if (volumeToClose > orders[i].volume) { // Don't close more than available
volumeToClose = orders[i].volume;
}
// Select the order again to ensure it's still open and get fresh data
if (OrderSelect(orders[i].ticket, SELECT_BY_TICKET)) {
double currentPrice;
if (orderType == OP_BUY) currentPrice = MarketInfo(Symbol(), MODE_BID);
else currentPrice = MarketInfo(Symbol(), MODE_ASK);
if (OrderClose(orders[i].ticket, volumeToClose, currentPrice, Slippage, CommentPrefix)) {
Print("Partially closed order #", orders[i].ticket, " (", (orderType == OP_BUY ? "BUY" : "SELL"), ") volume: ", DoubleToString(volumeToClose, 2), " at price: ", DoubleToString(currentPrice, Digits), ". Remaining volume: ", DoubleToString(OrderLots() - volumeToClose, 2));
closedCount++;
Sleep(100); // Small delay to avoid flooding the server
} else {
Print("Failed to partially close order #", orders[i].ticket, ". Error: ", GetLastError(), " (", ErrorDescription(GetLastError()), ")");
}
} else {
Print("Failed to select order #", orders[i].ticket, " for partial close. It might have been closed already or ticket is invalid. Error: ", GetLastError(), " (", ErrorDescription(GetLastError()), ")");
}
}
}
}
}
Разбор параметров
MagicNumber: Уникальный магический номер, используемый экспертным советником для идентификации своих ордеров. Убедитесь, что он совпадает сMagicNumberвашей основной сеточной стратегии.MinLossToClose: Минимальный общий убыток (в валюте счета) для группы ордеров (сетки) одного типа (покупки или продажи), при достижении которого активируется алгоритм частичного закрытия.ClosePercentage: Процент от текущего объема каждого выбранного ордера, который будет закрыт. Например,0.25означает закрытие 25% объема.MaxOrdersToClose: Максимальное количество ордеров, которые будут частично закрыты за один цикл обработки. Это позволяет контролировать скорость и объем закрытий.CloseOnlyOldest: Логический параметр. Еслиtrue, советник будет сначала закрывать самые старые убыточные ордера. Еслиfalse, будут закрываться ордера с наибольшим текущим убытком.Slippage: Максимально допустимое проскальзывание в пунктах при исполнении ордера на частичное закрытие.CommentPrefix: Префикс, добавляемый к комментарию ордера при его частичном закрытии. Помогает отслеживать действия советника в истории счета.
Как запустить
Для использования алгоритма частичного закрытия сетки выполните следующие шаги:
1. Скопируйте предоставленный MQL4 код в MetaEditor (обычно находится в File -> Open Data Folder -> MQL4 -> Experts).
2. Сохраните файл с расширением .mq4 (например, PartialGridClose.mq4).
3. Скомпилируйте код, нажав кнопку ‘Compile’ в MetaEditor. Убедитесь, что нет ошибок.
4. Откройте торговый терминал MetaTrader 4.
5. Перетащите скомпилированный экспертный советник PartialGridClose из окна ‘Навигатор’ на график валютной пары, на которой работает ваша основная сеточная стратегия. Убедитесь, что MagicNumber этого советника совпадает с MagicNumber вашей основной стратегии.
6. В окне настроек советника перейдите на вкладку ‘Входные параметры’ и настройте значения MinLossToClose, ClosePercentage, MaxOrdersToClose и CloseOnlyOldest в соответствии с вашей стратегией управления рисками.
7. Убедитесь, что на вкладке ‘Общие’ установлен флажок ‘Разрешить автоматическую торговлю’ и кнопка ‘Автоторговля’ в терминале активна (зеленая).
8. Настоятельно рекомендуется сначала протестировать советник на демо-счете или в тестере стратегий. Хотя этот алгоритм предназначен для MQL4, принципы тестирования и оптимизации универсальны. Для более глубокого понимания тестирования сложных систем, особенно мультивалютных, вы можете ознакомиться с нашей статьей Мультивалютное тестирование в MT5: обход ограничений тестера, которая хоть и ориентирована на MT5, но подчеркивает важность тщательной проверки.
9. Советник будет автоматически отслеживать ваши ордера и выполнять частичное закрытие при достижении заданных условий, помогая снизить просадку.




