Разработка инструментария для анализа Price Action (Часть 30): Советник CCI Zero Line
Введение
В этой статье мы создаем инструмент на MQL5, который упрощает торговлю по Price Action. Система автоматизирует стратегию, основанную на четырех элементах: двойном CCI (Commodity Channel Index), экспоненциальной скользящей средней (EMA) с периодом 34, Average True Range (ATR) и чистом анализе Price Action. После обзора каждого индикатора и описания полной торговой стратегии мы шаг за шагом разберем разработку советника, протестируем его, проанализируем результаты и сделаем выводы. Сводка других разработанных нами инструментов приведена в таблице на последней странице статьи.
Ознакомьтесь с оглавлением ниже.
Обзор индикатора
Индекс товарного канала (CCI) – это осциллятор моментума, который сравнивает текущую цену с ее недавним средним значением, чтобы выявлять потенциальные состояния перекупленности, перепроданности или разворота тренда. Формула Commodity Channel Index (CCI) выглядит так:
CI = (Типичная цена - SMA) / (0,015 × Среднее отклонение)
Типичная цена (TP) вычисляется как сумма максимума, минимума и цены закрытия за период, деленная на три:
h + l + c
TP = ---------
3
SMA – простая скользящая средняя
Простая скользящая средняя (SMA) берет типичные цены за заданный период, например за последние 20 сессий, суммирует их и делит на число периодов. Этот процесс сглаживает ежедневные колебания и делает основной тренд более заметным.
Среднее отклонение
Это среднее значение абсолютных отклонений типичных цен от их простой скользящей средней.
Эта статистика показывает, насколько в среднем каждая типичная цена отклоняется от SMA, и дает практичную оценку изменчивости цены, то есть волатильности, за выбранный период.
Постоянный множитель 0,015Этот фиксированный коэффициент масштабирует значения CCI так, что подавляющее большинство из них оказывается примерно в диапазоне от -100 до +100. Это делает индикатор более наглядным и позволяет резким отклонениям, указывающим на выраженную перекупленность или перепроданность, лучше выделяться.
Разработанный Дональдом Ламбертом и впервые опубликованный в 1980 году для анализа циклических движений на товарных рынках, этот индикатор теперь широко применяется к акциям, валютам и другим классам активов. В этой стратегии мы одновременно используем два CCI: быстрый CCI (25) для оценки краткосрочного моментума и медленный CCI (50) для оценки общей силы рынка. Большинство значений CCI находится в диапазоне от -100 до +100, а выход за его пределы часто указывает на необычно сильное или слабое движение. Хотя эти крайние уровни могут служить фильтром волатильности, основным триггером в нашей стратегии остается именно нулевая линия. Переход из отрицательной области в положительную сигнализирует о возможном переходе к бычьему моментуму; движение в противоположном направлении указывает на нарастающее давление продавцов.
Поскольку пересечения нулевой линии возникают раньше, чем пробои уровней ±100, они дают советнику более ранние входы, хотя иногда это приводит к ложным сигналам. Чтобы ослабить этот шум, советник при необходимости ищет дополнительное подтверждение: устойчивое движение выше +100 усиливает сигналы на покупку, а падение ниже -100 усиливает сигналы на продажу. Иными словами, пересечения нулевой линии формируют основные сигналы, а уровни ±100 выступают вторичным фильтром, а не их заменой.

Рис. 2. Индикатор CCI
Ниже показано, как создать в MQL5 два хэндла CCI, корректно ими управлять и получать их значения.
- Создание хэндлов CCI в OnInit() с помощью:
handleCCI_Long = iCCI(_Symbol, _Period, CCI_LongPeriod, PRICE_TYPICAL); handleCCI_Short = iCCI(_Symbol, _Period, CCI_ShortPeriod, PRICE_TYPICAL);
- Копирование значений буфера с помощью CopyBuffer() в OnTick():
CopyBuffer(handleCCI_Long, 0, 0, 2, cciL); CopyBuffer(handleCCI_Short, 0, 0, 1, cciS);
Экспоненциальная скользящая средняя (EMA) с периодом 34
Экспоненциальная скользящая средняя (EMA) – это трендовый индикатор, который быстро реагирует на новую ценовую информацию, придавая больший вес последним свечам. Такое экспоненциальное взвешивание отфильтровывает краткосрочный шум и делает преобладающий тренд более заметным. На часовом графике часто используется EMA с периодом 34, поскольку она охватывает примерно одну торговую неделю. Торговать следует только по направлению EMA 34: открывать длинные позиции, когда цена выше EMA, а короткие – когда цена ниже нее. Это правило служит непрерывным фильтром тренда и не позволяет стратегии входить в сделки против тренда.

Рис. 3. EMA с периодом 34
Ниже показано, как создать в MQL5 хэндл для EMA 34.
- Объявление:
input int EMAPeriod = 34;
- Инициализация:
handleEMA = iMA(_Symbol, _Period, EMAPeriod, 0, MODE_EMA, PRICE_CLOSE);
- Копирование буфера (та же логика):
double ema34[1]; if(CopyBuffer(handleEMA, 0, 0, 1, ema34) < 1) return; double ema_now = ema34[0];
Средний истинный диапазон (ATR)
Средний истинный диапазон (ATR) – это индикатор волатильности, разработанный Дж. Уэллсом Уайлдером-младшим и описанный в его книге 1978 года "New Concepts in Technical Trading Systems". Он усредняет True Range (истинный диапазон) за выбранный период (по умолчанию 14), поэтому отражает естественные ценовые колебания рынка.
В этой статье мы используем средний истинный диапазон (ATR), чтобы вычислить и установить уровни стоп-лосса и тейк-профита. Ниже показано, как реализовать это в MQL5:
- Объявление
int handleATR = INVALID_HANDLE; input int ATR_Period = 14;
- Инициализация
handleATR = iATR(_Symbol, _Period, ATR_Period); if(handleATR == INVALID_HANDLE) return INIT_FAILED;
- Копирование буфера
double atrBuf[1]; if(CopyBuffer(handleATR, 0, 0, 1, atrBuf) > 0) { double atrValue = atrBuf[0]; // use atrValue here… }
- Очистка
if(handleATR != INVALID_HANDLE) IndicatorRelease(handleATR);
Логика стратегии
В этом разделе мы объясним, как наш советник CCI Zero Line генерирует сигналы на вход с помощью двух индикаторов индекса товарного канала (CCI) и экспоненциальной скользящей средней (EMA). Когда значение CCI пересекает нулевую линию снизу вверх, это указывает на усиление восходящего моментума и на возможный бычий сигнал либо усиление давления покупателей. И наоборот, когда значение CCI пересекает нулевую линию сверху вниз, это сигнализирует о развороте рыночного моментума вниз и указывает на возможный медвежий тренд или усиление давления продавцов.
Когда цена поднимается выше EMA 34, это обычно указывает на усиление восходящего моментума и на возможный бычий тренд или возможность покупки. С другой стороны, если цена опустится ниже EMA 34, это указывает на снижение моментума и потенциальное начало медвежьего тренда, что может быть сигналом к продаже.
Используемые индикаторы
- CCI 25 – быстрый индекс товарного канала
- CCI 50 – медленный индекс товарного канала
- EMA 34 – экспоненциальная скользящая средняя с периодом 34 по ценам закрытия
Критерии сигнала на покупку
- CCI 25 выше нулевой линии: CCI 25 должен находиться выше нулевой линии, указывая на краткосрочный бычий моментум.
- CCI 50 пересекает нулевую линию: CCI 50 должен пересечь нулевую линию снизу вверх, подтверждая смену более долгосрочного тренда.
- Подтверждение ценой: текущая свеча должна закрыться выше EMA 34, подтверждая бычий сценарий.

Рис. 4. Условия сигнала на покупку
Когда все три условия выполняются на новом баре, советник фиксирует сигнал на покупку.
Критерии сигнала на продажу
- CCI 25 ниже нулевой линии: CCI 25 должен находиться ниже нулевой линии, указывая на краткосрочный медвежий моментум.
- CCI 50 пересекает нулевую линию: CCI 50 должен пересечь нулевую линию сверху вниз, подтверждая смену более долгосрочного тренда на медвежий.
- Подтверждение ценой: текущая свеча должна закрыться ниже EMA 34, подтверждая медвежий сценарий.

Рис. 5. Условия сигнала на продажу
Когда все три условия выполняются на новом баре, советник фиксирует сигнал на продажу.
Разработка советника
При запуске советник сначала создает необходимые хэндлы индикаторов, которые позволяют ему получать с графика данные в реальном времени. Это происходит в функции OnInit(). Хэндлы создаются для двух CCI, один с большим периодом (50) для определения более масштабного тренда и второй с меньшим периодом (25) в качестве подтверждающего индикатора, а также для EMA с периодом 34, которая выступает фильтром тренда. В частности, код создает эти хэндлы с помощью таких функций, как iCCI() и iMA(), которые запрашивают индикаторы у внутреннего движка MetaTrader.
Корректное создание этих хэндлов критически важно: если какой-либо из них не будет создан и функция вернет INVALID_HANDLE, советник остановится, предотвращая ложные сигналы и ошибки во время работы. Кроме того, предусмотрен необязательный хэндл ATR, который создается только в том случае, если включено управление стоп-лоссом и тейк-профитом на основе ATR (параметр UseATR).
handleCCI_Long = iCCI(_Symbol, _Period, CCI_LongPeriod, PRICE_TYPICAL); handleCCI_Short = iCCI(_Symbol, _Period, CCI_ShortPeriod, PRICE_TYPICAL); handleEMA = iMA(_Symbol, _Period, EMAPeriod, 0, MODE_EMA, PRICE_CLOSE); if(UseATR) handleATR = iATR(_Symbol, _Period, ATR_Period);
- Этот подход гарантирует, что у советника будут все необходимые источники данных для принятия обоснованных торговых решений на каждом тике.
В советнике предусмотрена возможность ограничивать торговую активность определенными часами, например токийской сессией для USDJPY, что помогает избегать торговли в периоды низкого объема, когда движение цены может быть непредсказуемым. Это реализуется проверкой текущего серверного времени внутри функции OnTick(). Если фильтр сессии включен, код преобразует текущее время в структуру MqlDateTime и сравнивает значение часа с заданными пользователем начальным и конечным часами. Если текущий час выходит за пределы этого окна, советник просто завершает обработку, пропуская дальнейший анализ и торговлю.
if(UseTokyoSessionFilter && _Symbol == "USDJPY") { datetime now = TimeTradeServer(); MqlDateTime tm; TimeToStruct(now, tm); int hr = tm.hour; if(hr < TokyoStartHour || hr >= TokyoEndHour) return; }
- Торговля только в заданные часы помогает снизить риск проскальзывания и ложных сигналов, которые часто возникают на тонком рынке вне пиковых периодов.
Чтобы не выполнять проверку несколько раз в пределах одной свечи, советник использует простую, но эффективную логику: он запоминает временную метку последнего обработанного бара и продолжает работу только тогда, когда сформировался новый бар. Это достигается с помощью статической переменной lastTime, которая сохраняет свое значение между вызовами функции. Когда поступает новый тик, код сравнивает текущее время бара из iTime() с этим сохраненным значением. Если они совпадают, функция завершается досрочно, и все расчеты и решения выполняются только один раз на бар.
static datetime lastTime=0; datetime t = iTime(_Symbol, _Period, 0); if(t == lastTime) return; // same bar, skip lastTime = t;
- Оптимизируя работу, предотвращая дублирование сигналов и согласуя выполнение с привычным для трейдера анализом по таймфреймам, этот подход повышает общую эффективность и точность торговой стратегии.
На каждом новом баре советник вызывает CopyBuffer(), чтобы получить последние значения CCI 25, CCI 50 и EMA 34, включая предыдущий бар для обнаружения пересечений. Медленный CCI 50 определяет более долгосрочный тренд, а быстрый CCI 25 подтверждает моментум для входа. EMA фильтрует шум и помогает убедиться, что цена движется по тренду. Если включены стопы на основе ATR, советник также получает текущее значение ATR 14.
double cciL[2], cciS[1], emaVal[1]; if(CopyBuffer(handleCCI_Long, 0, 0, 2, cciL) < 2) return; if(CopyBuffer(handleCCI_Short, 0, 0, 1, cciS) < 1) return; if(CopyBuffer(handleEMA, 0, 0, 1, emaVal) < 1) return; double atrValue = 0.0; if(UseATR) { double atrBuf[1]; if(CopyBuffer(handleATR, 0, 0, 1, atrBuf) < 1) return; atrValue = atrBuf[0]; }
- В совокупности эти значения определяют все торговые решения: CCI выявляют развороты и продолжения, EMA подтверждает соответствие направлению тренда, а ATR позволяет динамически рассчитывать риск.
Суть торговой логики заключается в определении момента, когда долгосрочный CCI пересекает свою нулевую линию, сигнализируя о возможном изменении тренда. Пересечение вверх, то есть переход из отрицательной области в положительную, указывает на бычье движение, тогда как пересечение вниз говорит о медвежьем моментуме. Код фиксирует эти моменты, сравнивая предыдущие и текущие значения буфера медленного CCI. Как только пересечение обнаружено, советник применяет дополнительные фильтры: короткий CCI должен подтверждать направление сигнала, то есть быть положительным для бычьего сценария и отрицательным для медвежьего, а текущая цена должна находиться соответственно выше или ниже EMA.
double price = SymbolInfoDouble(_Symbol, SYMBOL_BID); if(crossUp && cciS[0] > 0 && price > emaVal[0]) RegisterSignal(true, price, atrValue); if(crossDown && cciS[0] < 0 && price < emaVal[0]) RegisterSignal(false, price, atrValue);
- Такое многоуровневое подтверждение помогает отфильтровывать ложные сигналы и обеспечивает вход в сделки только тогда, когда несколько индикаторов указывают в одну сторону, а фильтр тренда поддерживает движение.
Как только найден корректный сигнал, советник рассчитывает соответствующие уровни стоп-лосса и тейк-профита. Если включено управление на основе ATR, эти уровни динамически рассчитываются умножением текущего значения ATR на заданный пользователем множитель, например 1,5. Уровень стоп-лосса устанавливается на расстоянии от цены входа: ниже нее для длинных сделок и выше – для коротких. Уровень тейк-профита устанавливается пропорционально дальше в зависимости от соотношения риск/прибыль, обычно на расстоянии, в 1,5 раза превышающем расстояние до стоп-лосса.
double dist = UseATR ? atrValue * ATR_Multiplier : SLBufferPoints * _Point; double slPrice = isBuy ? price - dist : price + dist; double tpPrice = isBuy ? price + dist * RiskRewardRatio : price - dist * RiskRewardRatio;
- Затем советник создает на графике визуальные маркеры: стрелки рядом с текущей свечой для обозначения точек входа и горизонтальные линии, показывающие уровни стоп-лосса и тейк-профита.
ObjectCreate(0, name, OBJ_ARROW, 0, barTime, arrowPrice); ObjectSetInteger(0, name, OBJPROP_ARROWCODE, arrowCode); ObjectSetInteger(0, name, OBJPROP_COLOR, isBuy ? BuyArrowColor : SellArrowColor); ObjectCreate(0, "SL_Line", OBJ_HLINE, 0, 0, slPrice); ObjectCreate(0, "TP_Line", OBJ_HLINE, 0, 0, tpPrice);
- Эти визуальные подсказки позволяют наглядно проверять сигналы и убеждаться, что логика советника соответствует реальным рыночным условиям.
После построения визуальных маркеров советник выдает оповещение с подробной информацией о сделке: ценой входа, уровнем стоп-лосса и уровнем тейк-профита.
Alert("CCI ZeroLine EMA + R:R 1:1.5 " + (isBuy ? "BUY" : "SELL") + StringFormat(" @%.5f | SL: %.5f | TP: %.5f", price, slPrice, tpPrice));
Он также обновляет внутренние массивы отслеживания, в которых хранятся уровни стоп-лосса и тейк-профита для каждой сделки, а также логический флаг, указывающий, была ли уже зафиксирована ее отработка. Эти массивы необходимы для отслеживания текущих сделок, особенно при тестировании на исторических данных или в реальной торговле, поскольку позволяют советнику одновременно управлять несколькими открытыми позициями и оценивать их эффективность.
signalSL[totalSignals] = slPrice;
signalTP[totalSignals] = tpPrice;
resolved[totalSignals] = false; Функция ResolveSignals() отвечает за отслеживание активных сделок. Она проходит по всем открытым сигналам, проверяя цены максимума и минимума последних баров, чтобы определить, были ли достигнуты уровни стоп-лосса или тейк-профита. Если максимум превышает тейк-профит, сделка помечается как прибыльная; если минимум пробивает стоп-лосс, она помечается как убыточная. Этот процесс обновляет счетчики общего числа сигналов и прибыльных сделок, предоставляя статистическую обратную связь об эффективности стратегии. Такой мониторинг особенно важен при тестировании на исторических данных, где точная фиксация результатов влияет на общие метрики эффективности, такие как процент выигрышных сделок, коэффициент прибыльности и просадка.
double high = iHigh(_Symbol, _Period, idx); double low = iLow(_Symbol, _Period, idx); if(high >= signalTP[i]) { winSignals++; resolved[i]=true; } if(low <= signalSL[i]) { resolved[i]=true; }
Когда советник удаляется или график закрывается, функция OnDeinit() гарантирует освобождение всех хэндлов индикаторов с помощью IndicatorRelease(). Она также удаляет все графические объекты, созданные в процессе работы, такие как стрелки и горизонтальные линии. Корректное управление ресурсами предотвращает утечки памяти и не захламляет график, благодаря чему последующие запуски этого или других советников проходят без остаточных артефактов.
IndicatorRelease(handleCCI_Long); IndicatorRelease(handleCCI_Short); ObjectDelete(0, "SL_Line"); ObjectDelete(0, "TP_Line");
Полный код
//+------------------------------------------------------------------+ //| CCI Zero-Line EA| //| Copyright 2025, MetaQuotes Ltd.| //| https://www.mql5.com/ru/users/lynnchris| //+------------------------------------------------------------------+ #property copyright "Copyright 2025, MetaQuotes Ltd." #property link "https://www.mql5.com/ru/users/lynnchris" #property version "1.0" #property strict #include <Trade\Trade.mqh> #include <Tools\Datetime.mqh> CTrade trade; //--- Session filter for USDJPY (Tokyo session) input bool UseTokyoSessionFilter = false; // enable Tokyo session filter input int TokyoStartHour = 0; // session start hour (server time) input int TokyoEndHour = 9; // session end hour (server time, exclusive) //--- SL/TP settings input bool UseATR = true; // true = ATR-based SL/TP, false = fixed points input int ATR_Period = 14; // ATR look-back input double ATR_Multiplier = 1.5; // SL/TP distance = ATR × this input double SLBufferPoints = 10.0; // fallback SL offset in pips if UseATR=false input double RiskRewardRatio = 1.5; // TP = SL × 1.5 (1:1.5 RR) //--- Indicator periods input int CCI_LongPeriod = 50; // CCI long period (zero-line cross) input int CCI_ShortPeriod = 25; // CCI short period (confirmation) input int EMAPeriod = 34; // EMA period for trend filter //--- Arrow colors input color BuyArrowColor = clrLime; input color SellArrowColor = clrRed; //--- Indicator handles int handleCCI_Long = INVALID_HANDLE; int handleCCI_Short= INVALID_HANDLE; int handleEMA = INVALID_HANDLE; int handleATR = INVALID_HANDLE; //--- Signal-tracking arrays int totalSignals = 0; int winSignals = 0; int signalBar[]; double signalSL[]; double signalTP[]; bool resolved[]; //+------------------------------------------------------------------+ //| Expert initialization | //+------------------------------------------------------------------+ int OnInit() { handleCCI_Long = iCCI(_Symbol, _Period, CCI_LongPeriod, PRICE_TYPICAL); handleCCI_Short = iCCI(_Symbol, _Period, CCI_ShortPeriod, PRICE_TYPICAL); handleEMA = iMA(_Symbol, _Period, EMAPeriod, 0, MODE_EMA, PRICE_CLOSE); if(handleCCI_Long == INVALID_HANDLE || handleCCI_Short== INVALID_HANDLE || handleEMA == INVALID_HANDLE) return(INIT_FAILED); if(UseATR) { handleATR = iATR(_Symbol, _Period, ATR_Period); if(handleATR == INVALID_HANDLE) return(INIT_FAILED); } if(RiskRewardRatio <= 0 || ATR_Multiplier <= 0 || (SLBufferPoints <= 0 && !UseATR)) return(INIT_FAILED); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { if(handleCCI_Long != INVALID_HANDLE) IndicatorRelease(handleCCI_Long); if(handleCCI_Short != INVALID_HANDLE) IndicatorRelease(handleCCI_Short); if(handleEMA != INVALID_HANDLE) IndicatorRelease(handleEMA); if(UseATR && handleATR != INVALID_HANDLE) IndicatorRelease(handleATR); ObjectDelete(0, "SL_Line"); ObjectDelete(0, "TP_Line"); } //+------------------------------------------------------------------+ //| Strategy Tester summary | //+------------------------------------------------------------------+ double OnTester() { double winRate = totalSignals > 0 ? 100.0 * winSignals / totalSignals : 0.0; PrintFormat("=== Backtest Win-Rate ===\nSignals: %d Wins: %d Win-Rate: %.2f%%", totalSignals, winSignals, winRate); return(winRate); } //+------------------------------------------------------------------+ //| Tick handler | //+------------------------------------------------------------------+ void OnTick() { // Tokyo session filter for USDJPY if(UseTokyoSessionFilter && _Symbol == "USDJPY") { datetime now = TimeTradeServer(); MqlDateTime tm; TimeToStruct(now, tm); int hr = tm.hour; if(hr < TokyoStartHour || hr >= TokyoEndHour) return; } // Only act on new bar static datetime lastTime = 0; datetime t = iTime(_Symbol, _Period, 0); if(t == lastTime) return; lastTime = t; // Copy indicator buffers double cciL[2], cciS[1], emaVal[1]; if(CopyBuffer(handleCCI_Long, 0, 0, 2, cciL) < 2) return; if(CopyBuffer(handleCCI_Short, 0, 0, 1, cciS) < 1) return; if(CopyBuffer(handleEMA, 0, 0, 1, emaVal) < 1) return; // ATR if needed double atrValue = 0.0; if(UseATR) { double atrBuf[1]; if(CopyBuffer(handleATR, 0, 0, 1, atrBuf) < 1) return; atrValue = atrBuf[0]; } double price = SymbolInfoDouble(_Symbol, SYMBOL_BID); // Detect zero-line cross bool crossUp = (cciL[1] < 0 && cciL[0] > 0); bool crossDown = (cciL[1] > 0 && cciL[0] < 0); // Confirm with short CCI & EMA trend filter if(crossUp && cciS[0] > 0 && price > emaVal[0]) RegisterSignal(true, price, atrValue); if(crossDown && cciS[0] < 0 && price < emaVal[0]) RegisterSignal(false, price, atrValue); // Resolve pending signals for SL/TP hits ResolveSignals(); } //+------------------------------------------------------------------+ //| Register new buy/sell signal | //+------------------------------------------------------------------+ void RegisterSignal(bool isBuy, double price, double atrVal) { double dist = UseATR ? atrVal * ATR_Multiplier : SLBufferPoints * _Point; double slPrice = isBuy ? price - dist : price + dist; double tpPrice = isBuy ? price + dist * RiskRewardRatio : price - dist * RiskRewardRatio; // Arrow placement just outside the candle datetime barTime = iTime(_Symbol, _Period, 0); double barHigh = iHigh(_Symbol, _Period, 0); double barLow = iLow(_Symbol, _Period, 0); double offset = 5 * _Point; // e.g. 5-pip offset double arrowPrice = isBuy ? barLow - offset : barHigh + offset; // Use numeric arrow codes directly int arrowCode = isBuy ? 233 : 234; string name = (isBuy ? "BUY_" : "SELL_") + TimeToString(TimeTradeServer(), TIME_SECONDS); ObjectCreate(0, name, OBJ_ARROW, 0, barTime, arrowPrice); ObjectSetInteger(0, name, OBJPROP_ARROWCODE, arrowCode); ObjectSetInteger(0, name, OBJPROP_COLOR, isBuy ? BuyArrowColor : SellArrowColor); ObjectSetInteger(0, name, OBJPROP_WIDTH, 2); // Draw SL/TP lines DrawLine("SL_Line", slPrice, clrRed); DrawLine("TP_Line", tpPrice, clrLime); // Alert Alert("CCI ZeroLine EMA + R:R 1:1.5 " + (isBuy ? "BUY" : "SELL") + StringFormat(" @%.5f | SL: %.5f | TP: %.5f", price, slPrice, tpPrice)); // Track for backtest metrics totalSignals++; ArrayResize(signalBar, totalSignals); ArrayResize(signalSL, totalSignals); ArrayResize(signalTP, totalSignals); ArrayResize(resolved, totalSignals); signalBar[totalSignals-1] = 0; signalSL[totalSignals-1] = slPrice; signalTP[totalSignals-1] = tpPrice; resolved[totalSignals-1] = false; } //+------------------------------------------------------------------+ //| Resolve pending signals (SL/TP checks) | //+------------------------------------------------------------------+ void ResolveSignals() { int bars = Bars(_Symbol, _Period); for(int i = 0; i < totalSignals; i++) { if(resolved[i]) continue; signalBar[i]++; int idx = signalBar[i]; if(idx >= bars) continue; double high = iHigh(_Symbol, _Period, idx); double low = iLow(_Symbol, _Period, idx); if(high >= signalTP[i]) { winSignals++; resolved[i] = true; } else if(low <= signalSL[i]) { resolved[i] = true; } } } //+------------------------------------------------------------------+ //| Draw a horizontal SL/TP line | //+------------------------------------------------------------------+ void DrawLine(string name, double price, color clr) { if(ObjectFind(0, name) >= 0) ObjectDelete(0, name); ObjectCreate(0, name, OBJ_HLINE, 0, 0, price); ObjectSetInteger(0, name, OBJPROP_COLOR, clr); ObjectSetInteger(0, name, OBJPROP_WIDTH, 2); ObjectSetString(0, name, OBJPROP_TEXT, name); } //+------------------------------------------------------------------+
Тестирование
Ниже приведены результаты тестирования нашего советника на исторических данных.
Я протестировал советник на USDJPY, и ниже приведены записи из журнала.
- Входные параметры
2025.06.30 09:34:54.140 UseTokyoSessionFilter=false 2025.06.30 09:34:54.140 TokyoStartHour=0 2025.06.30 09:34:54.140 TokyoEndHour=9 2025.06.30 09:34:54.140 UseATR=true 2025.06.30 09:34:54.140 ATR_Period=14 2025.06.30 09:34:54.140 ATR_Multiplier=1.5 2025.06.30 09:34:54.140 SLBufferPoints=10.0 2025.06.30 09:34:54.140 RiskRewardRatio=1.0 2025.06.30 09:34:54.140 CCI_LongPeriod=50 2025.06.30 09:34:54.140 CCI_ShortPeriod=25 2025.06.30 09:34:54.140 EMAPeriod=34 2025.06.30 09:34:54.140 BuyArrowColor=65280 2025.06.30 09:34:54.140 SellArrowColor=255
Конфигурация показывает, что фильтр токийской сессии отключен, поэтому сделки могут совершаться вне заданных для Токио часов; однако при его включении торговля будет ограничена интервалом с 00:00 до 09:00 по токийскому времени. Советник использует индикатор ATR с периодом 14 и множителем 1,5 для корректировки уровней стоп-лосса и тейк-профита с учетом текущей рыночной волатильности. К стоп-лоссу добавляется буфер в 10 пунктов, чтобы снизить риск преждевременного выхода, а соотношение риск/прибыль установлено на уровне 1:1, что уравновешивает потенциальную прибыль и убытки.
В стратегии используются два CCI: с периодом 50 как более медленный индикатор и с периодом 25 как более быстрый, а также EMA 34. Визуальные сигналы отображаются стрелками: зеленые соответствуют сигналам на покупку, а синие – сигналам на продажу.
Входные параметры можно настроить по своему усмотрению. Приведенные настройки — это те, которые я использовал во время тестирования и которые, на мой взгляд, дали хорошие результаты.
- Сигналы
2025.06.30 09:34:54.447 2025.01.02 15:00:00 Alert: CCI ZeroLine EMA + R:R 1:1.5 SELL @157.01800 | SL: 157.45075 | TP: 156.58525 2025.06.30 09:34:56.655 2025.01.09 06:00:00 Alert: CCI ZeroLine EMA + R:R 1:1.5 SELL @158.04800 | SL: 158.30343 | TP: 157.79257 2025.06.30 09:40:35.572 2025.01.29 07:00:00 Alert: CCI ZeroLine EMA + R:R 1:1.5 SELL @155.21100 | SL: 155.47704 | TP: 154.94496 2025.06.30 09:40:36.759 2025.01.31 07:00:00 Alert: CCI ZeroLine EMA + R:R 1:1.5 BUY @154.68200 | SL: 154.25439 | TP: 155.10961 2025.06.30 09:40:45.870 2025.02.26 06:00:00 Alert: CCI ZeroLine EMA + R:R 1:1.5 BUY @149.42600 | SL: 149.05400 | TP: 149.79800 2025.06.30 09:40:46.737 2025.02.28 03:00:00 Alert: CCI ZeroLine EMA + R:R 1:1.5 SELL @149.51900 | SL: 150.03650 | TP: 149.00150 2025.06.30 09:40:49.726 2025.03.06 01:00:00 Alert: CCI ZeroLine EMA + R:R 1:1.5 SELL @149.14200 | SL: 149.68200 | TP: 148.60200 2025.06.30 09:40:58.522 2025.03.21 16:00:00 Alert: CCI ZeroLine EMA + R:R 1:1.5 SELL @148.97200 | SL: 149.35418 | TP: 148.58982 2025.06.30 09:41:01.363 2025.04.01 00:00:00 Alert: CCI ZeroLine EMA + R:R 1:1.5 BUY @149.93500 | SL: 149.57425 | TP: 150.29575 2025.06.30 09:41:16.557 2025.04.28 13:00:00 Alert: CCI ZeroLine EMA + R:R 1:1.5 SELL @143.33500 | SL: 143.69232 | TP: 142.97768 2025.06.30 09:41:36.021 2025.06.18 14:00:00 Alert: CCI ZeroLine EMA + R:R 1:1.5 SELL @144.85500 | SL: 145.08911 | TP: 144.62089 2025.06.30 09:41:37.454 2025.06.23 19:00:00 Alert: CCI ZeroLine EMA + R:R 1:1.5 SELL @146.15000 | SL: 146.65711 | TP: 145.64289 2025.06.30 09:41:38.360 2025.06.25 23:00:00 Alert: CCI ZeroLine EMA + R:R 1:1.5 SELL @145.21700 | SL: 145.47286 | TP: 144.96114
- Процент выигрышных сделок
2025.06.30 09:41:39.206 2025.06.29 23:59:59 Signals: 13 Wins: 10 Win-Rate: 76.92%
Ниже приведен GIF, визуализирующий результаты тестирования: красные стрелки вниз обозначают сигналы на продажу, зеленые стрелки вверх – сигналы на покупку, красные линии показывают уровни стоп-лосса, а зеленые – уровни тейк-профита.

Заключение
Подводя итог, отметим, что советник CCI Zero Line выводит на график все, что нужно для четких входов по заданным правилам:
- Мы используем CCI 50, чтобы определить общий тренд, и CCI 25, чтобы подтвердить готовность моментума, поэтому входим в рынок только тогда, когда оба сигнала совпадают.
- EMA с периодом 34 помогает не гнаться за случайными всплесками. Если цена закрывается по нужную сторону этой линии, это подтверждает, что тренд не случаен.
- При использовании стоп-лосса на основе ATR расстояние до него меняется вместе с волатильностью: на спокойном рынке оно меньше, а при усилении движения – больше. Уровень тейк-профита остается пропорциональным, поддерживая стабильное соотношение прибыли к риску.
Это означает, что стрелки появляются только тогда, когда цена, моментум и волатильность указывают в одну сторону. Это означает меньше ложных сигналов, более гибкое управление рисками и полную прозрачность в том, почему был выбран каждый уровень входа и выхода. Вы сможете тонко настраивать периоды CCI, длину EMA и множитель ATR под любой символ и таймфрейм и торговать увереннее, используя инструмент, основанный на надежном многоуровневом анализе. Данный советник предназначен только для образовательных целей. Не используйте его на реальном рынке без тщательного тестирования на исторических данных или проверки на демо-счете. Прежде чем переходить к реальной торговле, убедитесь, что вас устраивают полученные результаты.
Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/18551
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.
Торговые инструменты MQL5 (Часть 21): Добавление темы в стиле киберпанк в графики регрессии
Создание самооптимизирующихся советников на MQL5 (Часть 14): Преобразования данных как параметры настройки регулятора с обратной связью
Статистический арбитраж с использованием коинтегрированных акций (Часть 4): Обновление параметров модели в реальном времени
Разработка инструментария для анализа Price Action (Часть 29): Советник "Boom and Crash Interceptor"
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования