От новичка до эксперта: Раскрываем скрытые уровни коррекции Фибоначчи
Содержание:
Введение
Уровни коррекции Фибоначчи широко используются, но иногда цена реагирует на промежуточные или повторяющиеся нестандартные соотношения. Наш вопрос заключается в том, можем ли мы использовать основанные на данных систематические методы для выявления таких уровней, проверки того, встречаются ли они чаще одного раза, и, если они надежны, добавить их в качестве первоклассных уровней в наши торговые инструменты и стратегии?
Почему традиционные уровни Фибоначчи могут быть неполными
Классические соотношения Фибоначчи, такие как 23.6%, 38.2%, 50%, 61.8%, и 78,6%, получены из последовательности Фибоначчи и золотого сечения. Несмотря на широкое признание, трейдеры часто замечают, что рынки иногда учитывают промежуточные или альтернативные уровни коррекции, не включенные в этот традиционный набор. Это говорит о том, что стандартная структура может не в полной мере отражать поведение рынка.
Наблюдения за скрытой реакцией рынка
Как ни странно, цена часто останавливается, разворачивается или ускоряется вблизи уровней от 50% до 61,8% или около других нестандартных точек. Давайте рассмотрим, как назвать эти “скрытые” уровни Фибоначчи. Трудность заключается в том, что такие наблюдения являются субъективными, основанными на визуальном изучении графиков, и могут не совпадать по инструментам или таймфреймам.
Противоречие между неподтвержденными данными и статистическими доказательствами
Визуальное распознавание паттернов склонно к предвзятости подтверждения: мы помним случаи, когда цена реагировала на предполагаемом уровне, но забываем о промахах. Без систематического тестирования эти скрытые уровни остаются спекулятивными. Тем не менее, неофициальные данные дают нам отправную точку для структурированной проверки этих идей и придания большей точности теории, на которую опираются трейдеры. Задача состоит в том, чтобы отличить подлинные структурные тенденции от произвольности и шума.

Рисунок 1. Демонстрация нестандартных уровней коррекции Фибоначчи
На рисунке 1 (выше) показан скриншот из MetaTrader 5, показывающий EURUSD на таймфрейме M15. Колебание A–B измерено с помощью инструмента Фибоначчи; стандартные уровни коррекции выделены синим цветом и помечены метками. Цена не остановилась именно на этих классических соотношениях — вместо этого вы можете наблюдать отчетливую ценовую активность в промежуточных точках между отмеченными уровнями (38,2 и 50). Я выделил эти промежуточные реакции красными пунктирными линиями и пометил их знаком “?”, чтобы указать, что это неизвестные, потенциально значимые уровни.
Эти промежуточные реакции - именно то, на что направлено наше исследование. Хотя мы могли бы вычислить и нарисовать точные значения коррекции программно, ручная проверка ненадежна, поскольку встроенный инструмент Фибоначчи отображает только стандартные соотношения. Для этого требуется двухэтапный процесс: во-первых, собрать и статистически отфильтровать большой набор нормализованных наблюдений за коррекцией, чтобы определить, какие промежуточные диапазоны неоднократно учитываются; во-вторых, реализовать алгоритм, который откалибрует и отобразит эти подтвержденные уровни с помощью инструмента Фибоначчи на MetaTrader 5 (с указанием степени достоверности).
Стратегия реализации
Методы определения диапазона баров и колебаний
В этом проекте максимумы и минимумы каждого бара рассматриваются как простой “колебательный” диапазон. Применяем фильтр минимального диапазона (например, множитель ATR), чтобы подавить шум и сосредоточиться на значимых движениях. Этот прокси-сервер с диапазоном баров намеренно облегчен: он прост в реализации, быстро обрабатывает большие истории и предоставляет детерминированный набор данных по одному наблюдению на закрытый бар, который идеально подходит для статистического анализа.
На будущих этапах мы внедрим более совершенный многоступенчатый детектор колебаний, позволяющий фиксировать более длительные или конструктивно значимые колебания. Нынешний выбор — начать с диапазонов баров — сделан осознанно: это сводит к минимуму затраты на разработку, позволяя нам быстро собирать большие выборки, проверять наши статистические методы и затем повторять их. Целесообразность такого подхода основывается на практическом наблюдении за рынком: японские свечи часто повторяют часть предыдущего движения, создавая измеримые пики в нормализованном распределении отката. Смотрите рисунок 2, на котором представлено поведения коррекции внутри бара, которое служит мотивацией для этого метода.

Рисунок 2. Откат по диапазону и барам японских свечей
Сбор и подготовка данных
Первым шагом является сбор исторических данных OHLCV по нескольким инструментам (например, EURUSD, GBPUSD, S&P500, XAUUSD) и таймфреймам (M15, H1, H4, D1). Достаточно обширная выборка — в идеале данные за три—пять лет - позволяет получить представление о различных рыночных режимах, от этапов формирования тенденций до консолидации. Перед использованием данные также необходимо очистить, отфильтровав пропущенные бары, аномальные всплески и пробелы, которые могут повлиять на результаты анализа. Эта основа гарантирует, что набор данных об откате отражает реальную структуру рынка, а не случайные аномалии.
Основываясь на этой чистой базе, каждый закрытый бар рассматривается как автономный диапазон колебаний. Максимум и минимум определяют контрольные границы, и уже следующий бар проверяется на глубину отката. Для подавления шума исключаются диапазоны, меньшие порогового значения, установленного на основе ATR. Затем процент коррекции нормализуется по шкале от 0 до 100 и регистрируется вместе с такими метаданными, как символ, таймфрейм, направление, временная метка и объем. Важно отметить, что не все последовательности допустимы: сборщик отклоняет недопустимые случаи, когда тестовый бар открывается за пределы заданного диапазона или закрывается против направления контрольного бара. Логика развернутого окна сворачивает последовательные внутренние бары или флаги, охватывая паттерны и разрывы, гарантируя, что они не будут ошибочно приняты за обычные коррекции. Применяя эти проверки и собирая метаданные, результирующий набор данных становится чистым и воспроизводимым — готовым также для статистического исследования классических соотношений Фибоначчи.
Разработка скрипта сбора данных
На следующих этапах мы подготовим наш скрипт сбора данных на MQL5 и будем использовать его для генерации CSV-файла данных с восстановлением. Затем этот файл будет проанализирован в Jupyter Notebook для изучения паттернов и извлечения информации.
Инициализация — устанавливаем элементы управления и помощники
Вверху мы объявляем все параметры, которые управляют поведением: количество проверяемых баров, настройки ATR, ограничения в направлении вперёд и выходные флаги. При запуске скрипт считывает эти входные данные и подготавливает два небольших помощника (форматтер и средство преобразования таймфреймов в строки), чтобы остальной код оставался аккуратным. Затем скрипт создает имя выходного файла, которое включает символ и таймфрейм, и открывает CSV-файл для записи. Если файл не удается открыть, он останавливается и сообщает об ошибке — так что мы всегда знаем, начался ли запуск фактически.
//--- input parameters input int BarsToProcess = 20000; // how many candidate reference bars to process input int StartShift = 1; // skip most recent N bars input int ATR_Period = 14; // ATR period input double ATR_Multiplier = 0.3; // min ATR filter input int MaxLookahead = 3; // extended-window lookahead input bool UsePerfectSetup = true; // require perfect setups input bool OutputOnlySameDir = false; // require same-dir support input bool IncludeInvalidRows= false; // output invalids input string OutFilePrefix = "CandleRangeData"; // file prefix //--- output file string OutFileName = StringFormat("%s_%s_%s.csv", OutFilePrefix, _Symbol, PeriodToString(_Period)); int fh = FileOpen(OutFileName, FILE_WRITE|FILE_CSV|FILE_ANSI); if(fh == INVALID_HANDLE) { PrintFormat("Error opening file %s", OutFileName); return; }
Базовый уровень волатильности — создаем хэндл ATR, чтобы скрипт мог фильтровать шум.
Перед сканированием баров создаем хэндл индикатора ATR. Для каждого потенциального эталонного бара скрипт будет считывать значение ATR на этом баре; значение ATR служит критерием волатильности. Если диапазон бара меньше, чем ATR * ATR_Multiplier, скрипт рассматривает этот бар как шум и пропускает его. Удаление небольших диапазонов предотвращает появление ложных входов при откатах на небольших случайных барах. Это повышает качество сигнала.
//--- prepare ATR handle int atr_handle = iATR(_Symbol, _Period, ATR_Period); if(atr_handle == INVALID_HANDLE) { Print("ATR handle invalid"); FileClose(fh); return; }
Основной цикл сканирования — скрипт просматривает историю
Скрипт выполняет перебор закрытых баров в обратном направлении от StartShift до доступной истории или до тех пор, пока не запишет ряды BarsToProcess. Для каждой итерации (для каждого потенциального контрольного бара) скрипт считывает значения максимума, минимума, открытия и закрытия контрольного бара, вычисляет диапазон и немедленно применяет параметр ATR. Если столбец проходит, скрипт переходит к анализу тестовых баров, которые следуют за ссылкой. Этот цикл является механизмом, который превращает необработанную историю в возможные события коррекции. Таким образом, мы сокращаем количество неудачных случаев на ранних стадиях, чтобы улучшить последующую статистику.
int bars = iBars(_Symbol,_Period); double atr_buf[]; for(int r = StartShift; r <= bars - 1; r++) { double RefTop = iHigh(_Symbol,_Period,r); double RefBot = iLow(_Symbol,_Period,r); double RefOpen = iOpen(_Symbol,_Period,r); double RefClose = iClose(_Symbol,_Period,r); double Range = RefTop - RefBot; // ATR filter if(CopyBuffer(atr_handle,0,r,1,atr_buf) <= 0) continue; if(Range < atr_buf[0] * ATR_Multiplier) continue; // process this reference... }
Определяем исходное направление — обозначаем колебание как бычье/медвежье/нейтральное
Для контрольного бара проверяем, закрылся ли он выше, чем открылся (бычий), ниже, чем открылся (медвежий), или равным (нейтральный). Это направление определяет, какой экстремум на тестовом баре представляет собой откат (бычий ориентир ищет минимумы, медвежий - максимумы). Нормализация процента возврата зависит от того, была ли ссылка увеличена или уменьшена.
string RefDir; if(RefClose > RefOpen) RefDir = "Bull"; else if(RefClose < RefOpen) RefDir = "Bear"; else RefDir = "Neutral";
Первоначальная проверка тестового бара и идеальной настройки - сначала проверяем простые и понятные случаи
Скрипт считывает непосредственно следующий бар (тестовый бар) после эталонного; именно на этом баре обычно происходит откат. Если мы включили фильтр “perfect setup” (“идеальная настройка”), скрипт проверяет два условия в стиле трейдера: тестовый бар должен открываться внутри контрольного диапазона, а его закрытие не должно быть против контрольного направления (например, для бычьего ориентира тестовый бар не должен закрываться медвежьим). Если тестовый бар не работает и нам не нужны диагностические ряды, скрипт пропускает запись чего-либо для этой ссылки.
int testIndex = r - 1; double testOpen = iOpen(_Symbol,_Period,testIndex); double testClose= iClose(_Symbol,_Period,testIndex); bool ValidSetup = true; if(UsePerfectSetup) { if(testOpen < RefBot || testOpen > RefTop) ValidSetup = false; if(RefDir=="Bull" && testClose < testOpen) ValidSetup = false; if(RefDir=="Bear" && testClose > testOpen) ValidSetup = false; } if(!ValidSetup && !IncludeInvalidRows) continue;
Обработка развернутых окон - мы позволяем скрипту фиксировать реалистичные откаты на несколько баров
Когда эта функция включена, скрипт просматривает данные еще дальше назад (на настраиваемое количество баров), чтобы свернуть короткие последовательности, которые в совокупности создают истинный экстремум коррекции. При сканировании баров, находящихся впереди, он выполняет три действия:
- Обнаруживает пробелы — если бар открывается за пределами контрольного диапазона, скрипт помечает пробел и записывает его размер.
- Сворачивает внутри баров — если несколько небольших последовательных баров полностью находятся внутри контрольного диапазона, скрипт обновляет экстремум (Ext) до наихудшего минимума (для бычьего рынка) или наихудшего максимума (для медвежьего рынка) на этих барах и увеличивает InsideCount.
- Обнаруживает поглощающие бары — если более поздний бар полностью поглощает ссылку, скрипт классифицирует ее как Engulf и устанавливает флаг HighMomentum.
Это сворачивание гарантирует, что наблюдение представляет собой завершенный эпизод восстановления, а не преждевременное частичное касание.
double Ext = (RefDir=="Bull") ? testLow : testHigh; string SeqType = "Single"; bool HighMomentum = false; int InsideCount = 0; for(int k=1; k<=MaxLookahead; k++) { int idx = r - k; if(idx < 0) break; double kOpen = iOpen(_Symbol,_Period,idx); double kHigh = iHigh(_Symbol,_Period,idx); double kLow = iLow(_Symbol,_Period,idx); if(kOpen > RefTop || kOpen < RefBot) { SeqType="Gap"; break; } if(kHigh <= RefTop && kLow >= RefBot) { if(RefDir=="Bull") Ext = MathMin(Ext,kLow); if(RefDir=="Bear") Ext = MathMax(Ext,kHigh); InsideCount++; continue; } if(kHigh >= RefTop && kLow <= RefBot) { SeqType="Engulf"; HighMomentum=true; break; } // if retrace detected, stop break; }
Вычисление процента отката — скрипт нормализует результат для анализа
Используя последний зарегистрированный экстремум (Ext), скрипт вычисляет RetracePct по шкале от 0 до 100:
- Для бычьей отметки: RetracePct = (RefTop - Ext) / диапазон * 100
- Для медвежьей отметки: RetracePct = (Ext - RefBot) / диапазон * 100
Затем отмечает событие:
- NoRetrace при отрицательном значении (цена отклонилась в сторону),
- Коррекция при значении от 0 до 100,
- Разворачивание, если цена превысит 100 (цена вышла за пределы контрольной отметки).
double Rpct = EMPTY_VALUE; string Type = "Undefined"; if(RefDir=="Bull") Rpct = (RefTop - Ext) / Range * 100.0; if(RefDir=="Bear") Rpct = (Ext - RefBot) / Range * 100.0; if(Rpct < 0) Type="NoRetrace"; else if(Rpct<=100) Type="Retracement"; else Type="Extension";
Практическая диагностика — вычисление доступного для торговли расстояния и близости к классическим уровням Фибоначчи
Мы рассчитываем RetracePips, абсолютное количество пипсов, которое представляет собой откат, чтобы можно было отбросить тривиальные, неторгуемые касания (например, меньшие, чем спред). Мы также вычисляем, какой классический уровень Фибоначчи находится ближе всего и на каком расстоянии (NearestFibPct, NearestFibDistPct). Наконец, устанавливаем SameDirSupport, проверяя, закрылся ли представительный бар (последний бар в свернутой последовательности) в том же направлении, что и контрольная отметка.
double RetracePips = (RefDir=="Bull") ? (RefTop-Ext)/_Point : (Ext-RefBot)/_Point; // Same-dir support bool SameDirSupport = (RefDir=="Bull") ? (testClose >= testOpen) : (testClose <= testOpen); // Nearest Fibonacci comparison double fibLevels[] = {0,23.6,38.2,50.0,61.8,78.6,100.0}; double nearest = fibLevels[0]; double minDist = fabs(Rpct - fibLevels[0]); for(int i=1;i<ArraySize(fibLevels);i++) { double d = fabs(Rpct - fibLevels[i]); if(d < minDist) { minDist = d; nearest = fibLevels[i]; } }
Правила вывода
Основываясь на флагах (OutputOnlySameDir, IncludeInvalidRows), скрипт либо пропускает, либо записывает ряд. Если записывает, ряд содержит все метаданные (время, символ, RefTop/RefBot, Ext, RetracePips, RetracePct, SeqType, SameDirSupport, ближайшее совпадение по fib, объем и спред). Файл является детерминированным: один и тот же символ, таймфрейм и параметры всегда создают один и тот же CSV-файл.
if(OutputOnlySameDir && !SameDirSupport) continue; FileWrite(fh, _Symbol, PeriodToString(_Period), TimeToString(iTime(_Symbol,_Period,r),TIME_DATE|TIME_SECONDS), RefTop, RefBot, Range, RefDir, Ext, RetracePips, Rpct, Type, SeqType, HighMomentum, InsideCount, SameDirSupport, nearest, minDist );
Очистка и отчетность — мы завершаем запуск и сообщаем команде, что произошло
После завершения цикла скрипт освобождает хэндл ATR, закрывает файл и выводит краткую сводку с указанием того, сколько рядов было записано и сколько недопустимых кандидатов было пропущено. Эта мгновенная обратная связь определяет наши дальнейшие действия (например, увеличение количества баров, ослабление фильтров или изменение множителя ATR).
IndicatorRelease(atr_handle); FileClose(fh); PrintFormat("CandleRangeData_v2: finished. Wrote %d rows to %s", written, OutFileName);
Статистический анализ и визуализация данных коррекции Фибоначчи с помощью Python в Jupyter Notebook
Чтобы продвинуть наши исследования, мы будем использовать Python для уточнения данных и создания статистических отчетов. Для этой цели я выбрал Jupyter Notebook, поскольку он обеспечивает улучшенный рабочий процесс для Python и поддерживает тот тип результатов, к которому мы стремимся.
Jupyter Notebook это интерактивная веб-среда, предназначенная для научных вычислений, анализа данных и визуализации. Он позволяет коду, визуальным выводам и документации сосуществовать в одном рабочем пространстве, что делает его особенно эффективным для решения задач, связанных с исследованиями. В нашем случае эта среда обеспечивает гибкость для экспериментов с данными о восстановлении, тестирования статистических методов и мгновенной визуализации результатов, таких как гистограммы и графики плотности. В отличие от статического скрипта, который должен выполняться от начала до конца, Jupyter позволяет нам выполнять небольшие ячейки независимо — ценное преимущество при корректировке вычислений или повторном выполнении определенных шагов без перезапуска всего процесса. Этот интерактивный рабочий процесс хорошо вписывается в итеративный и исследовательский характер интеллектуального анализа финансовых данных.
В следующем разделе описываются шаги по настройке Jupyter Notebook в Windows.
Чтобы запустить приведенные ниже ячейки, используйте Jupyter (Notebook или Lab) в Windows. Этими шагами являются:
1. Установить Python 3.10+ с ресурса python.org (выбрать “Add Python to PATH”).2. Открыть командную строку (PowerShell или CMD) и создать виртуальную среду (необязательно, но рекомендуется):
python -m venv venv venv\Scripts\activate3. Установить Jupyter и необходимые библиотеки:
pip install jupyter pandas numpy matplotlib scipy scikit-learn
4. Запустить Jupyter:
Если вы хотите работать в определенной папке, используйте команду cd, чтобы изменить каталог. Например, чтобы перейти к папке, в которую экспортируется файл CSV, как правило, C:\Users\YourComputerName\MQL5\Files, необходимо ввести:
cd C:\Users\YourComputerName\TerminalDataFolder\MQL5\Files После этого вы можете запустить его с помощью
jupyter notebook
Библиотеки и их назначение
- pandas — загружает таблицу CSV (ряды/столбцы) и управляет ею.
- numpy —числовые массивы и вспомогательная математика.
- matplotlib — построение гистограмм и KDE.
- scipy—статистические функции и KDE.
- scikit-learn (sklearn)—Модель гауссовских смесей для 1-мерной кластеризации.
Ячейка 1—Настройка среды Python
В начале работы над нашим блокнотом подготавливаем среду, импортируя библиотеки Python, которые будут использоваться для нашего анализа. Каждая библиотека играет специализированную роль в обработке данных, экспортируемых из скрипта MQL5.
- Pandas (pd) используется для обработки структурированных данных. Она позволяет загружать CSV-файл, содержащий записи о восстановлении, манипулировать рядами и столбцами и легко вычислять статистику.
- Numpy (np) предоставляет математические инструменты и численные операции, такие как массивы и линейная алгебра, которые лежат в основе многих наших статистических вычислений.
- Matplotlib.pyplot (plt) - это основа для построения графиков, таких как гистограммы и графики плотности, которые помогут нам визуализировать поведение отката.
- Seaborn (sns) использует matplotlib для создания более элегантных и простых в оформлении графиков, что делает статистические паттерны более понятными.
- Scipy.stats (статистика) использует передовые статистические методы, такие как ядерная оценка плотности (KDE), проверка гипотез и распределение вероятностей.
- Sklearn.mixture.GaussianMixture взят из библиотеки машинного обучения scikit-learn и позволяет использовать модели гауссовских смесей. Это полезно при кластеризации уровней коррекции, чтобы определить, где могут быть сосредоточены скрытые уровни, подобные уровням Фибоначчи.
Запуская эту ячейку, мы эффективно загружаем все инструменты, необходимые для нашей работы.
import pandas as pd import numpy as np import matplotlib.pyplot as plt import seaborn as sns from scipy import stats from sklearn.mixture import GaussianMixture
Ячейка 2 —Загружает CSV и создаёт логический столбец ValidSetup_bool
На этом этапе в записной книжке скрипт начинает с загрузки набора данных о восстановлении, экспортированного из MQL5. CSV-файл считывается в DataFrame pandas, и программа немедленно выводит количество загруженных рядов и доступных столбцов. Это важная контрольная точка, поскольку она подтверждает корректность пути к файлу и наличие ожидаемых полей, таких как RetracementPct, Type или ValidSetup. Чтобы помочь нам визуализировать структуру, скрипт также отображает первые несколько рядов, чтобы можно было быстро проверить, соответствуют ли данные тому, что мы ожидаем от нашего экспорта в MetaTrader.
Как только набор данных окажется в памяти, следующая задача - решить, какие ряды являются допустимыми для дальнейшего анализа. Проблема в том, что не всем расчетам коррекции с помощью скрипта MQL5 следует доверять: некоторые из них могут быть неполными или помечены как недопустимые настройки. Поскольку в наборах данных могут использоваться несколько иные имена столбцов для проверки достоверности (например, ValidSetup, Valid или validsetup), скрипт выполняет поиск по нескольким распространенным вариантам. Если таковой найден, он стандартизирует результаты в новый логический столбец с именем ValidSetup_bool. Такие значения, как “True”, “1” или “Yes”, интерпретируются как допустимые. Если столбец validity вообще не найден, скрипт по умолчанию обрабатывает все ряды как допустимые, гарантируя, что у нас все еще есть данные для работы. Наконец, создается отфильтрованный набор данных с именем df_valid, содержащий только допустимые ряды, и выводится краткая статистика, показывающая, сколько всего рядов было загружено в сравнении с тем, сколько из них прошло фильтр допустимости.
# Robust Cell 2: load CSV and create a boolean ValidSetup_bool column import pandas as pd from IPython.display import display csv_file = "CandleRangeData_NZDUSD_H4.csv" # <- set your filename here df = pd.read_csv(csv_file, sep=None, engine="python") print("Loaded rows:", len(df)) print("Columns found:", list(df.columns)) # Show first 5 rows display(df.head()) # Try to detect a 'valid setup' column (several common name variants) candidates = ["ValidSetup", "validsetup", "Valid_Setup", "Valid", "valid", "ValidSetup_bool", "Valid_Setup_bool"] found = None for c in candidates: if c in df.columns: found = c break # Case-insensitive detection if exact not found if found is None: lower_map = {col.lower(): col for col in df.columns} for c in candidates: if c.lower() in lower_map: found = lower_map[c.lower()] break # Create boolean column 'ValidSetup_bool' using detected column or fallback if found is not None: print("Using column for validity:", found) series = df[found].astype(str).str.strip().str.lower() df["ValidSetup_bool"] = series.isin(["true", "1", "yes", "y", "t"]) else: print("No ValidSetup-like column found. Creating ValidSetup_bool=True for all rows (no filtering).") df["ValidSetup_bool"] = True # Quick stats total = len(df) valid_count = df["ValidSetup_bool"].sum() print(f"Total rows = {total}, ValidSetup_bool True = {valid_count}") # Create df_valid for downstream cells (the rest of notebook expects df_valid) df_valid = df[df["ValidSetup_bool"] == True].copy() print("df_valid rows:", len(df_valid)) # Display preview display(df_valid.head())
Ячейка 3 — Вычисление значений отката на основе схемы сборщика
Теперь, когда в ячейке 2 подготовлен достоверный набор данных, блокнот фокусируется на обеспечении постоянного доступа к значениям коррекции для анализа. В экспортированном наборе данных MetaTrader некоторые из этих полей, возможно, уже вычислены, в то время как другие необходимо вывести из исходных уровней цен. Этот шаг гармонизирует процесс расчета коррекции.
Первое действие - убедиться, что столбец Range (диапазон) является числовым. Этот столбец, экспортированный из сборщика MQL5, представляет собой общий размер контрольного бара (разницу между его верхом и низом). Преобразование его в значение с плавающей точкой гарантирует, что последующие математические операции будут выполняться так, как ожидалось.
Далее скрипт проверяет, содержит ли экспорт MetaTrader столбец RetracementPct. Если это значение присутствует, оно интерпретируется как процентное значение коррекции (например, 50 означает 50%) и преобразуется в нормализованную дробь от 0 до 1 путем деления на 100. Такой подход обеспечивает согласованность вычислений и повторное использование предварительно вычисленных значений, когда они доступны. Если такой столбец не найден, скрипт возвращается к вычислению коррекции вручную по формуле:

Здесь Ext представляет собой экстремальный уровень коррекции, достигнутый перед закрытием бара, а RefBot - нижнюю границу контрольного бара. Деление расстояния между Ext и RefBot на общее значение Range дает пропорциональную коррекцию.
Поскольку уровни коррекции всегда должны находиться в диапазоне от 0% до 100%, скрипт применяет операцию clip, чтобы поместить все значения в интервал [0,1]. Это защищает от аномалий, вызванных неточностями в данных или сбоями в вычислениях.
Наконец, в качестве шага проверки блокнот выводит первые несколько строк ключевых столбцов — RefTop, RefBot, Ext, Range и Retracement — чтобы подтвердить, что вычисленные или импортированные значения коррекции выглядят разумно. Этот предварительный просмотр убеждает нас в том, что конвейер производит последовательные нормализованные показатели коррекции, готовые для последующего статистического анализа.
# Cell 3: Compute retracement values based on the collector's schema # In our MT5 output: # - RefTop = top of the reference bar # - RefBot = bottom of the reference bar # - Ext = the extreme retracement reached before close # - RetracementPct = retracement % already computed in MT5 # 1. Use the collector's "Range" directly df_valid["Range"] = df_valid["Range"].astype(float) # 2. Use the already provided retracement percentage (if available) if "RetracementPct" in df_valid.columns: df_valid["Retracement"] = df_valid["RetracementPct"].astype(float) / 100.0 print("Using MT5-calculated RetracementPct column.") else: # fallback: compute from RefTop/RefBot and Ext df_valid["Retracement"] = (df_valid["Ext"].astype(float) - df_valid["RefBot"].astype(float)) / df_valid["Range"] print("No RetracementPct column found — computed from RefTop/RefBot/Ext.") # 3. Clip between 0 and 1 (0%–100%) df_valid["Retracement"] = df_valid["Retracement"].clip(0, 1) # Quick check print("Preview of retracement values:") display(df_valid[["RefTop","RefBot","Ext","Range","Retracement"]].head())

Рисунок 3. Таблица рассчитываемых значений
Ячейка 4 — Визуальное распределение значений коррекции
Теперь, когда значения коррекции были проверены и стандартизированы, эта ячейка берет на себя задачу визуализации их распределения. Но перед построением графика скрипт добавляет дополнительную защиту: он гарантирует наличие столбца коррекции Retracement, который можно использовать, независимо от того, какие столбцы были включены в экспорт MetaTrader.
Логика начинается с проверки того, присутствует ли уже Retracement. Если нет, то она пытается её сконструировать. Сначала ищет RetracementPct, основанный на процентах показатель коррекции, экспортируемый MetaTrader 5, и преобразует его в нормализованную шкалу от 0 до 1. Если этот столбец недоступен, скрипт возвращается к пользовательскому вычислению с использованием RefTop, RefBot, Ext и RefDir. Включение RefDir (направление контрольного бара) делает этот расчет надежным, поскольку коррекции должны интерпретироваться по-разному для бычьих и медвежьих контрльных баров:
- Для бычьего бара откат измеряется от RefTop вниз до крайнего значения.
- Для медвежьего бара он измеряется от RefBot вверх до крайнего значения.
Если ни один из требуемых столбцов недоступен, код выдает четкое сообщение об ошибке, описывающее, какие поля отсутствуют.
Как только значения коррекции собраны, они обрезаются, чтобы оставаться в пределах допустимого интервала [0,1], а записи NaN удаляются. Если после очистки не остается допустимых значений, скрипт останавливается с ошибкой, чтобы избежать построения вводящих в заблуждение графиков. В противном случае для прозрачности выводится число полезных наблюдений.
Если доступна библиотека Seaborn, скрипт использует sns.histplot для создания гистограммы, на которую для наглядности накладывается плавная кривая KDE. Если он не установлен, запускается резервный вариант с использованием чистого Matplotlib: гистограмма плюс вычисленная вручную ядерная оценка плотности (через scipy.stats.gaussian_kde). Это гарантирует, что график будет выглядеть безупречно даже в минимальных средах.
Итоговая диаграмма четко отмечена названиями осей и ограничена диапазоном 0-1 по оси X. Она показывает общее распределение коэффициентов отката по всем допустимым настройкам, давая непосредственное представление о том, как часто происходят мелкие, средние или глубокие откаты. При желании рисунок можно сохранить в формате PNG конкретно для использования в документации или презентации.
# Plotting cell 4 (robust): ensures 'Retracement' exists and draws a 750px-wide plot import numpy as np import matplotlib.pyplot as plt # --- Build 'Retracement' column if needed --- if "Retracement" not in df_valid.columns: if "RetracementPct" in df_valid.columns: # MT5 already computed it as percent (0-100) df_valid["Retracement"] = pd.to_numeric(df_valid["RetracementPct"], errors="coerce") / 100.0 print("Using existing RetracementPct -> created Retracement (0-1).") else: # try to compute from RefTop/RefBot/Ext with direction awareness required = {"RefTop","RefBot","Ext","RefDir"} if required.issubset(set(df_valid.columns)): def compute_r(row): try: rng = float(row["RefTop"]) - float(row["RefBot"]) if rng == 0: return np.nan if str(row["RefDir"]).strip().lower().startswith("b") : # Bull return (float(row["RefTop"]) - float(row["Ext"])) / rng elif str(row["RefDir"]).strip().lower().startswith("be"): # Bear return (float(row["Ext"]) - float(row["RefBot"])) / rng else: return np.nan except Exception: return np.nan df_valid["Retracement"] = df_valid.apply(compute_r, axis=1).astype(float) print("Computed Retracement from RefTop/RefBot/Ext/RefDir.") else: raise KeyError("No 'Retracement' or 'RetracementPct' column, and required columns for computation are missing. " "Found columns: " + ", ".join(df_valid.columns)) # Clip to 0..1 df_valid["Retracement"] = df_valid["Retracement"].clip(lower=0.0, upper=1.0) # Drop NaNs vals = df_valid["Retracement"].dropna().values if len(vals) == 0: raise ValueError("No valid retracement values to plot after preprocessing.") print(f"Plotting {len(vals)} retracement observations (0..1 scale).") # --- Plot size: target ~750 px width --- # Use figsize such that width_inches * dpi = 750. We'll choose dpi=100, width=7.5in. fig_w, fig_h = 7.5, 4.0 fig, ax = plt.subplots(figsize=(fig_w, fig_h), dpi=100) # Prefer seaborn if available for nice KDE overlay, otherwise fallback try: import seaborn as sns sns.histplot(vals, bins=50, stat="density", kde=True, ax=ax) except Exception: # fallback to matplotlib ax.hist(vals, bins=50, density=True, alpha=0.6) # manual KDE overlay from scipy.stats import gaussian_kde kde = gaussian_kde(vals) xgrid = np.linspace(0,1,500) ax.plot(xgrid, kde(xgrid), linewidth=2) ax.set_title("Retracement Ratio Distribution") ax.set_xlabel("Retracement (0 = 0%, 1 = 100%)") ax.set_ylabel("Density") ax.set_xlim(0,1) plt.tight_layout() # Optionally save a sized PNG (uncomment to save) # plt.savefig("retracement_distribution_750px.png", dpi=100) plt.show()

Рисунок 4. Распределение коэффициента коррекции
Ячейка 5—Ядерная оценка плотности (KDE)
На этом этапе мы выходим за рамки простой визуализации и проводим более продвинутый статистический анализ: Ядерная оценка плотности (KDE) в сочетании с определением пиков. Этот подход помогает выявить общие уровни коррекции — “скрытые” зоны, где цена часто имеет тенденцию к остановке или развороту — путем анализа формы распределения, а не просто необработанных показателей на гистограмме.
Сценарий начинается с проверки того, что столбец коррекции Retracement доступен в нормализованном виде (0-1). Если он отсутствует, скрипт восстанавливает его либо из поля RetracementPct, либо, при необходимости, из RefTop, RefBot, Ext и RefDir, используя ту же бычью/ медвежью логику, что и ранее. После приведения значений к допустимому диапазону [0,1] и удаления значений NAN он проверяет наличие как минимум 10 допустимых точек отката. Эта мера предосторожности предотвращает зашумленные или бессмысленные оценки KDE, когда набор данных слишком мал.
Затем KDE вычисляется по тонкой сетке из 1001 точки, охватывающей от 0 до 1. Это высокое разрешение позволяет кривой плотности отображать тонкую структуру данных, такую как множественные локальные максимумы. Чтобы определить эти максимумы, скрипт нормализует кривую плотности и применяет scipy.signal.find_peaks, настроенные таким образом, чтобы игнорировать незначительные колебания, требуя минимального выступа и расстояния между ними. Результирующие пиковые индексы соответствуют уровням коррекции, где функция плотности локально наиболее сильна — фактически это “предпочтительные” уровни коррекции, скрытые в данных.
Для наглядности кривая KDE нанесена на график с затенением под ней, а каждый обнаруженный пик выделен красной точкой и помечен процентом коррекции (например, 38,20%). В отличие от предыдущей ячейки для построения графика, в этой ячейке нет фиксированной ширины в пикселях, поэтому размер рисунка гибко адаптируется к различным условиям. Метки, диапазоны осей и сетка включены для того, чтобы сделать график понятным и интерпретируемым.
Наконец, скрипт выводит список обнаруженных уровней коррекции в процентах вместе с их относительными значениями заметности, предоставляя как визуальную, так и числовую сводку о том, где могут находиться самые сильные скрытые уровни коррекции. Такое сочетание KDE и обнаружения пиков преобразует необработанные наблюдения за коррекцией в полезную статистическую информацию.
# Cell 5: KDE + peak detection (robust, flexible sizing) import numpy as np import matplotlib.pyplot as plt from scipy import stats from scipy.signal import find_peaks # --- ensure Retracement column exists (0..1 scale) --- if "Retracement" not in df_valid.columns: if "RetracementPct" in df_valid.columns: df_valid["Retracement"] = pd.to_numeric(df_valid["RetracementPct"], errors="coerce") / 100.0 else: required = {"RefTop","RefBot","Ext","RefDir"} if required.issubset(set(df_valid.columns)): def compute_r(row): try: rng = float(row["RefTop"]) - float(row["RefBot"]) if rng == 0: return np.nan rd = str(row["RefDir"]).strip().lower() if rd.startswith("b"): # Bull return (float(row["RefTop"]) - float(row["Ext"])) / rng elif rd.startswith("be") or rd.startswith("bear"): # Bear return (float(row["Ext"]) - float(row["RefBot"])) / rng else: return np.nan except Exception: return np.nan df_valid["Retracement"] = df_valid.apply(compute_r, axis=1).astype(float) else: raise KeyError("Cannot build 'Retracement' — missing required columns. Found: " + ", ".join(df_valid.columns)) # Clip and drop NaNs df_valid["Retracement"] = df_valid["Retracement"].clip(0,1) vals = df_valid["Retracement"].dropna().values n = len(vals) if n < 10: raise ValueError(f"Too few retracement observations to compute KDE/peaks reliably (n={n}).") # --- KDE on a fine grid --- grid = np.linspace(0, 1, 1001) # 0.001 (0.1%) resolution kde = stats.gaussian_kde(vals) dens = kde(grid) # --- peak detection on normalized density --- dens_norm = dens / dens.max() peaks_idx, props = find_peaks(dens_norm, prominence=0.02, distance=8) # tweak params as needed peak_levels = grid[peaks_idx] peak_heights = dens[peaks_idx] # --- Plot (flexible sizing, no fixed pixel restriction) --- fig, ax = plt.subplots(figsize=(8, 4)) # default flexible size ax.plot(grid, dens, label="KDE", linewidth=2) ax.fill_between(grid, dens, alpha=0.2) # annotate peaks for lvl, h in zip(peak_levels, peak_heights): ax.plot(lvl, h, "o", color="red") ax.text(lvl, h, f" {lvl*100:.2f}%", va="bottom", ha="left", fontsize=9) ax.set_title("Kernel Density of Retracement Ratios") ax.set_xlabel("Retracement (0 = 0%, 1 = 100%)") ax.set_ylabel("Density") ax.set_xlim(0,1) ax.grid(True, linestyle="--", alpha=0.5) plt.tight_layout() plt.show() # --- print candidate levels --- print("Candidate Hidden Retracement Levels (%):") print(np.round(peak_levels*100, 2)) print("Peak prominences (relative):", np.round(props["prominences"], 4) if "prominences" in props else "n/a")

Рисунок5. Коэффициент плотности ядра коррекции
Тестирование и результаты
Все ячейки в Jupyter notebook давали визуальные результаты, которые можно было интерпретировать статистически. Под каждой ячейкой мы представили как код, так и соответствующие ему выходные данные, которые иллюстрируют одно из ключевых преимуществ работы в Jupyter — возможность легко сочетать вычисления и визуализацию.
После выполнения заключительной выходной ячейки, предназначенной для сохранения найденных скрытых значений, мы обнаружили, что для таймфрейма H4 на паре NZDUSD обнаружены только два значимых уровня. Хотя это дало полезную информацию о структуре данных, проверка гипотезы не оправдала наших ожиданий.
Detected peaks at (pct): [29.7 58.3] Bootstrap 200/1000... Bootstrap 400/1000... Bootstrap 600/1000... Bootstrap 800/1000... Bootstrap 1000/1000... Bootstrap done in 4.5s Peak testing results (window ±0.40%): Level 29.700% mass=9.909890e-03 p=0.4960 significant=False Level 58.300% mass=9.729858e-03 p=0.4960 significant=False Accepted (FDR<0.05) candidate levels (pct): []
Алгоритм обнаружил два потенциальных пика в распределении данных, расположенных на уровнях 29,7% и 58,3%. Эти пики представляют собой точки, в которых алгоритм изначально наблюдал локальную концентрацию данных, что указывает на потенциальную скрытую структуру или повторяющиеся паттерны.
Чтобы оценить, были ли эти пики статистически значимыми или просто случайными колебаниями, модель провела бутстрэп-тест с 1000 повторными выборками. Процесс начальной загрузки бутстрэп оценивал, как часто подобные пики будут появляться в случайно перемешанных версиях данных, что давало оценку статистической значимости.
Для обоих обнаруженных уровней:
- Уровень 29,7% → масса = 0,0099, p = 0,4960 → Незначимо
- Уровень 58,3% → масса = 0,0097, p = 0,4960 → Незначимо
P-значения (≈0,50) указывают на то, что эти пики наблюдались в рандомизированных выборках бутстрэп так же часто, как и в реальных данных, что означает, что они статистически неотличимы от шума. Поскольку ни один пик не преодолел порог коэффициента ложных открытий (FDR), равный 0,05, алгоритм пришел к выводу, что в наборе данных для выбранного таймфрейма (H4 по NZDUSD) не было существенных скрытых уровней.
Стратегии тестирования на истории с обнаруженными уровнями
В MetaTrader 5 я поэкспериментировал, добавив два пользовательских уровня коррекции к стандартному набору Фибоначчи: 29,7% и 58,3%. Согласно нашему анализу в Jupyter, оба уровня дали результаты массы = 0,0099, p = 0,4960 и массы = 0,0097, p = 0,4960 соответственно, которые не были статистически значимыми. Однако, судя по графикам, один из этих уровней, по-видимому, в прошлом соблюдался ценовым действием. Это говорит о том, что, хотя статистический тест не подтвердил значимость, все же, возможно, существует практическая значимость, заслуживающая изучения.
В настоящее время эти уровни были добавлены вручную, но в будущем может потребоваться программная интеграция таких значений в MetaTrader 5 для автоматического тестирования на нескольких парах и таймфреймах. Смотрите рисунок 6 с иллюстрацией.

Рисунок 6. Добавлены новые уровни
Заключение
Этот проект был продиктован амбициями и любопытством — от основанных на графиках наблюдений за движением цены в рамках коррекции Фибоначчи до выявления нарушений в том, как цена взаимодействует с известными уровнями, и даже вопроса о том, могут ли существовать скрытые уровни между традиционными точками коррекции. Чтобы изучить эти идеи, мы объединили MQL5 для автоматизированного сбора данных с Python в интерактивной веб-среде Jupyter, которая обеспечила мощные инструменты для анализа данных, визуализации и многоязычной интеграции.
Несмотря на то, что мы успешно добились предварительных результатов, возникли некоторые проблемы. Наш набор данных был ограничен, и ручное применение рассчитанных значений коррекции к графикам было многообещающим, но не соответствовало нашим первоначальным гипотезам. Это говорит о том, что как наш процесс сбора данных, так и методы обнаружения колебаний/отката могут нуждаться в доработке. Расширение анализа с целью охвата нескольких валютных пар и таймфреймов, а также перестройка алгоритмов обнаружения, вероятно, повысили бы точность и надежность.
Несмотря на эти минусы заложенный нами фундамент очень ценен. Он демонстрирует, как MQL5 и Python могут быть объединены для количественных исследований в области торговли, и служит практической начальной точкой для начинающих, которые хотят объединить автоматизацию торговой платформы с наукой о данных. Хотя первоначальные результаты не полностью оправдали наши ожидания, графики по-прежнему открывают интересные возможности, заслуживающие дальнейшего изучения. Благодаря более надежному тестированию и усовершенствованным методам это направление исследований все еще может дать представление о скрытой динамике коррекции.
В отличие от того, чтобы полагаться на догадки при добавлении пользовательских уровней коррекции, этот подход использует науку о данных вместе с доступными инструментами на MQL5 для повышения эффективности и структурированности процесса. Ниже приведена таблица с приложенными ресурсами. Вы можете ознакомиться с источниками и поэкспериментировать, чтобы поделиться своими мыслями для дальнейшего обсуждения. До следующей публикации следите за обновлениями.
Вложения
| Название файла | Версия | Описание |
|---|---|---|
| CandleRangeData.mq5 | 1.0 | Скрипт на MQL5, который собирает данные о диапазоне свеч и коррекции с графиков MetaTrader 5 и экспортирует их в формат CSV для анализа. |
| HiddenFiboLevels.ipynb | отсутствует | Jupyter Notebook содержит код на Python для загрузки экспортированного CSV-файла, очистки данных, тестирования потенциальных скрытых уровней коррекции Фибоначчи и визуализации результатов. |
| CandleRangeData_NZDUSD_H4.csv | отсутствует | Набор данных образцов, сгенерированный скриптом MQL5 для валютной пары NZDUSD на таймфрейме H4, используется в качестве входных данных для анализа на Python. |
Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/19780
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.
Нейросети в трейдинге: Агрегация движения по времени (Основные компоненты)
Выборочные методы MCMC: Алгоритм выборки по уровням (Slice sampling)
Команда ИИ-агентов с ротацией по прибыли: Эволюция живой торговой системы в MQL5
Нейросети в трейдинге: Агрегация движения по времени (TMA)
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования