Инжиниринг признаков для машинного обучения (Часть 2): Реализация дробного дифференцирования с фиксированным окном в MQL5
Оглавление
- Введение
- Архитектурные ограничения при работе с потоком данных в реальном времени
- Класс CFFDEngine
- Вычисление весов в MQL5
- Вычисление для одного бара
- Пользовательский индикатор FFD
- Интеграция с экспертными советниками
- Проверка на соответствие Python-конвейеру обработки
- Вопросы производительности
- Заключение
- Список литературы
- Прикрепленные файлы
Введение
Часть 1 разобрала теорию и реализацию дробного дифференцирования на Python с использованием метода окна фиксированной ширины (FFD) из главы 5 AFML. Три свойства метода FFD делают его идеальным для работы с потоковыми данными в реальном времени: вектор весов предварительно вычисляется один раз, каждое наблюдение зависит от ограниченного окна истории, а вычисление сводится к одному скалярному произведению. В этой статье эти свойства переносятся в готовый к промышленному применению модуль на MQL5 для работы с потоком данных MetaTrader 5 в реальном времени.
Реализация состоит из двух компонентов: CFFDEngine (класс .mqh только в виде заголовочного файла для генерации весов и вычисления скалярного произведения) и FFD.mq5 (пользовательский индикатор, который оборачивает CFFDEngine и отображает дробно-дифференцированный ряд). Индикатор поддерживает оптимизацию MetaTrader prev_calculated и пересчитывает только то, что изменилось с момента предыдущего вызова.
Главная цель проектирования: нулевое выделение памяти при обработке тиков, O(width) на новый бар и амортизированная инициализация O(1) . На типичном инструменте при d = 0.4 и τ = 10−5, ширина окна, определяемая порогом, составляет 1457 баров. При τ = 10−4 она уменьшается до 281 бара. Скалярное произведение по такому окну выполняется на современном оборудовании за микросекунды.
Архитектурные ограничения при работе с потоком данных в реальном времени
MQL5 задает иную вычислительную модель, чем Python. Здесь нет NumPy, Numba и параллельных операций над массивами. Всё выполняется в одном потоке внутри процесса терминала. Эти ограничения определяют каждое проектное решение:
- Никакой динамической памяти при обработке тиков. В MQL5 функция ArrayResize относительно дорогая по сравнению с арифметикой. Массивы весов должны выделяться один раз в OnInit() и никогда не изменять размер при работе в реальном времени.
- Минимизировать CopyClose вызовы. Каждый CopyClose вызов требует IPC-обмена с сервером истории терминала. Мы запрашиваем ровно width + 1 баров на новый бар, не больше.
- Никаких избыточных вычислений логарифма. Для индикаторного варианта можно было бы заранее вычислить буфер логарифмов цен, но это добавило бы второй индикаторный буфер и усложнило реализацию. Поскольку MathLog обычно эффективно реализован на современном оборудовании, его вызов width + 1 раз на бар пренебрежимо дешев.
- prev_calculated оптимизация. MetaTrader вызывает OnCalculate на каждом тике, а не только на новых барах. Индикатор должен определять, изменилось ли число баров, и пропускать пересчет, если оно не изменилось.

Рисунок 1. Архитектура вычисления FFD в MQL5
- OnInit(): один раз предварительно вычисляет вектор весов FFD и сохраняет его в статическом массиве. После этого шага дополнительных выделений памяти не происходит.
- OnTick() / OnCalculate(): определяет формирование нового бара и получает минимальное окно истории через CopyClose().
- Compute(): применяет логарифмическое преобразование (опционально) и вычисляет скалярное произведение вектора весов и ценового окна.
- Кольцевой буфер: хранит последние width + 1 логарифмов цен для обновления за O(1) на бар при использовании схемы прямого встраивания в EA.
- На выходе получается значение FFD, которое либо сохраняется в индикаторном буфере для отображения на графике, либо используется напрямую в торговой логике EA.
Класс CFFDEngine
Класс CFFDEngine предоставляет три метода: Init() (вызывается один раз), Compute() (вызывается на каждом баре в режиме EA) и ComputeBuffer() (вызывается на каждом тике в режиме индикатора). Всё внутреннее состояние (массив весов, ширина окна и параметры конфигурации) задается во время инициализации и затем не изменяется.
//+------------------------------------------------------------------+ //| FFDEngine.mqh — Fixed-Width Fractional Differentiation Engine | //| Copyright 2025, Patrick M. Njoroge | //+------------------------------------------------------------------+ #ifndef FFDENGINE_MQH #define FFDENGINE_MQH class CFFDEngine { private: double m_weights[]; // reversed weight vector (oldest lag first) int m_width; // number of weights minus 1 double m_d; // differentiation order double m_threshold; // weight cutoff τ bool m_use_log; // apply log transform bool m_initialized; // initialization flag void BuildWeights(void); //--- ln(max(price, 1e-8)): matches Python's clip(lower=1e-8) before np.log double SafeLog(double price) const { if(price<1e-8) price=1e-8; return(MathLog(price)); } public: CFFDEngine(void) : m_d(0),m_threshold(1e-5), m_use_log(true),m_width(0), m_initialized(false) {} bool Init(double d,double threshold=1e-5, bool use_log=true); int GetWidth(void) const { return(m_width); } int GetMinBars(void) const { return(m_width+1); } double GetD(void) const { return(m_d); } bool IsReady(void) const { return(m_initialized); } //--- Single-bar: prices[0]=oldest, prices[count-1]=newest double Compute(const double &prices[],int count); //--- Заполнение индикаторного буфера с оптимизацией prev_calculated int ComputeBuffer(const double &prices[], double &buffer[], int total,int prev_calculated); }; #endif // FFDENGINE_MQH
Класс CFFDEngine полностью реализован в заголовочном файле. Отдельная .mq5 единица компиляции не требуется, поэтому любой EA или индикатор, который подключает FFDEngine.mqh получает полную реализацию, скомпилированную inline. Это устраняет проблемы версионирования библиотек и сводит установку к одному файлу. Используйте #include "FFDEngine.mqh" в файлах-потребителях. Затем компилятор ищет в MQL5\Include, поэтому заголовочный файл должен находиться там.
Вычисление весов в MQL5
Генерация весов в точности повторяет реализацию на Python: итеративное рекуррентное соотношение ωk = −ωk−1 · (d − k + 1) / k, завершается, когда |ωk| становится меньше порога. Результат разворачивается так, что m_weights[0] соответствует самому старому наблюдению, а m_weights[m_width] соответствует самому свежему наблюдению в окне.
void CFFDEngine::BuildWeights(void) { //--- doubling-growth strategy: start at capacity 512, double when full. //--- this avoids an ArrayResize call on every iteration. int capacity=512; double temp[]; ArrayResize(temp,capacity); temp[0]=1.0; int n=1; for(int k=1; ; k++) { double w_next=-temp[n-1]*(m_d-(double)k+1.0)/(double)k; if(MathAbs(w_next)<m_threshold) break; // mirrors Python: "if abs(weights_) < thres: break" if(n>=capacity) { capacity*=2; ArrayResize(temp,capacity); } temp[n]=w_next; n++; } //--- trim to exact size, then reverse into m_weights. //--- m_weights[0] = smallest |w|, multiplies oldest price. //--- m_weights[n-1] = 1.0, multiplies newest price. ArrayResize(temp,n); m_width=n-1; ArrayResize(m_weights,n); for(int i=0; i<n; i++) m_weights[i]=temp[n-1-i]; }
Фиксированного потолка для числа итераций нет. Цикл завершается, когда |ωk| падает ниже порога, ровно как в Python-функции get_weights_ffd(). Фиксированное ограничение опасно: оно может незаметно обрезать вектор весов и привести к значениям FFD, которые больше не совпадают с Python-конвейером обработки. Ограничение в 1000 элементов обрежет веса и исказит результат при d < 0.5 и τ = 10−5 , без ошибки или предупреждения. Буфер с удвоением емкости требует O(log N) вызовов выделения памяти вместо O(N) и перед разворотом обрезается до точного размера.
Метод Init() проверяет параметры, вызывает BuildWeights() и выводит конфигурацию в журнал:
bool CFFDEngine::Init(double d,double threshold=1e-5, bool use_log=true) { if(d<0.0 || d>2.0) { PrintFormat("CFFDEngine::Init — d must be in [0, 2], got %.4f",d); return(false); } if(threshold<=0.0) { Print("CFFDEngine::Init — threshold must be positive"); return(false); } m_d=d; m_threshold=threshold; m_use_log=use_log; BuildWeights(); m_initialized=(m_width>0); if(m_initialized) PrintFormat("CFFDEngine: d=%.4f threshold=%.2e width=%d min_bars=%d use_log=%s", m_d,m_threshold,m_width,GetMinBars(), m_use_log ? "true" : "false"); else Print("CFFDEngine::Init — no weights generated (d too small?)"); return(m_initialized); }
Вычисление для одного бара
Метод Compute() — сердце движка. Он принимает массив цен и возвращает значение FFD для самого последнего бара. Реализация — прямое скалярное произведение: без ветвлений, без выделения памяти и без вызовов функций, кроме SafeLog:
double CFFDEngine::Compute(const double &prices[],int count) { if(!m_initialized || count<m_width+1) return(EMPTY_VALUE); //--- use the last (m_width + 1) prices. int start=count-m_width-1; double result=0.0; for(int i=0; i<=m_width; i++) { double val=prices[start+i]; if(m_use_log) val=SafeLog(val); // ln(max(price, 1e-8)) — matches Python clip result+=m_weights[i]*val; } return(result); }
Цикл выполняется ровно m_width + 1 раз. Для d = 0.4 при τ = 10−5 ширина окна равна 1457, поэтому для каждого нового бара требуется 1458 операций умножения-сложения, что значительно меньше одной миллисекунды. SafeLog применяет нижнюю границу 10−8 перед вызовом MathLog, что точно соответствует Python-варианту clip(lower=1e-8). Для реальных ценовых данных нижняя граница никогда не достигается; она нужна только для обеспечения численно идентичных результатов перекрестной проверки относительно Python-конвейера обработки.
Буферный вариант для индикаторов следует той же логике, но добавляет оптимизацию prev_calculated:
int CFFDEngine::ComputeBuffer(const double &prices[], double &buffer[], int total,int prev_calculated) { if(!m_initialized) return(0); //--- determine starting bar int start; if(prev_calculated>m_width) start=prev_calculated-1; // recompute only current bar else { //--- full computation: mark lookback bars as empty for(int i=0; i<m_width && i<total; i++) buffer[i]=EMPTY_VALUE; start=m_width; } //--- main computation loop for(int i=start; i<total; i++) { double result=0.0; for(int k=0; k<=m_width; k++) { double val=prices[i-m_width+k]; if(m_use_log) val=SafeLog(val); // ln(max(price, 1e-8)) result+=m_weights[k]*val; } buffer[i]=result; } return(total); }
Оптимизация на основе prev_calculated — ключевой механизм эффективности. При первом вызове (prev_calculated == 0) функция вычисляет значения FFD для каждого бара, начиная с m_width — это единоразовая стоимость для всей истории. При последующих вызовах prev_calculated отражает число обработанных баров. На практике цикл обрабатывает не более двух баров: последний закрытый бар и вновь сформированный бар. Это означает, что индикатор обрабатывает каждый входящий тик за O(width) времени, а не за O(total × width).
Пользовательский индикатор FFD
Индикатор оборачивает CFFDEngine и отображает ряд FFD в отдельном подокне. Он использует вторую форму OnCalculate, которая принимает один массив price[], выбранный входным параметром ENUM_APPLIED_PRICE. Это позволяет индикатору обрабатывать любую применяемую цену (close, open, high, low, median, typical или weighted) без изменений кода.
//+------------------------------------------------------------------+ //| FFD.mq5 — Fixed-Width Fractional Differentiation Indicator | //| Copyright 2025, Patrick M. Njoroge | //+------------------------------------------------------------------+ #property indicator_separate_window #property indicator_buffers 1 #property indicator_plots 1 #property indicator_label1 "FFD" #property indicator_type1 DRAW_LINE #property indicator_color1 clrDodgerBlue #property indicator_style1 STYLE_SOLID #property indicator_width1 2 //--- inputs input double InpD = 0.4; // Differentiation order d input double InpThreshold = 1e-5; // Weight cutoff τ input bool InpUseLog = true; // Log-transform prices input ENUM_APPLIED_PRICE InpPrice = PRICE_CLOSE; // Applied price #include "FFDEngine.mqh" double FFDBuffer[]; CFFDEngine g_engine; //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit(void) { SetIndexBuffer(0,FFDBuffer,INDICATOR_DATA); PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,EMPTY_VALUE); if(!g_engine.Init(InpD,InpThreshold,InpUseLog)) { Print("FFD indicator: engine initialization failed"); return(INIT_FAILED); } //--- hide bars before the lookback window is filled PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,g_engine.GetWidth()); //--- display name in chart window string short_name=StringFormat("FFD(%.2f, τ=%.0e)", InpD,InpThreshold); IndicatorSetString(INDICATOR_SHORTNAME,short_name); IndicatorSetInteger(INDICATOR_DIGITS,6); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) { return(g_engine.ComputeBuffer(price,FFDBuffer, rates_total,prev_calculated)); }
Вся логика индикатора — это одна строка в OnCalculate, а всё остальное — конфигурация. Это результат инкапсуляции вычисления в CFFDEngine: индикатор представляет собой тонкий слой визуализации, а тот же модуль можно повторно использовать в EA, скриптах и многоиндикаторных фреймворках без повторения кода.
Использование индикатора из EA
EA, которому нужно значение FFD для конкретного символа и таймфрейма, может вызвать индикатор через iCustom:
int ffd_handle; int OnInit(void) { ffd_handle=iCustom(_Symbol,_Period,"FFD", 0.4, // InpD 1e-5, // InpThreshold true, // InpUseLog PRICE_CLOSE); // InpPrice if(ffd_handle==INVALID_HANDLE) { Print("Failed to create FFD indicator"); return(INIT_FAILED); } return(INIT_SUCCEEDED); } void OnTick(void) { double ffd_val[]; if(CopyBuffer(ffd_handle,0,0,1,ffd_val)<1) return; //--- ffd_val[0] is the FFD value of the current (forming) bar if(ffd_val[0]==EMPTY_VALUE) return; //--- use ffd_val[0] in trading logic... }
Этот подход делегирует все вычисления индикаторному процессу, который выполняется только один раз независимо от того, сколько EA запрашивают тот же буфер. Если несколько EA торгуют один и тот же символ на одном таймфрейме с одинаковым d, они совместно используют один экземпляр индикатора: MetaTrader автоматически переиспользует экземпляр индикатора.
Интеграция с экспертными советниками
Для EA, которым нужен прямой контроль или которые работают с символами и таймфреймами, не совпадающими с графиком, модуль можно встроить напрямую. EA вызывает CopyClose на каждом новом баре и передает результат в Compute():
#include "FFDEngine.mqh" CFFDEngine g_ffd; datetime g_last_bar=0; input double InpD = 0.4; input double InpThreshold = 1e-5; int OnInit(void) { if(!g_ffd.Init(InpD,InpThreshold,true)) return(INIT_FAILED); return(INIT_SUCCEEDED); } void OnTick(void) { //--- new bar detection datetime current_bar=iTime(_Symbol,_Period,0); if(current_bar==g_last_bar) return; g_last_bar=current_bar; //--- retrieve prices int bars_needed=g_ffd.GetMinBars(); double prices[]; if(CopyClose(_Symbol,_Period,1,bars_needed,prices)<bars_needed) return; //--- compute FFD value for the last closed bar double ffd_value=g_ffd.Compute(prices,bars_needed); if(ffd_value==EMPTY_VALUE) return; //--- trading logic uses ffd_value as a feature ProcessSignal(ffd_value); }
На две детали реализации стоит обратить внимание. Во-первых, CopyClose(_Symbol, _Period, 1, bars_needed, prices) начинает копирование с индекса бара 1 (самого последнего закрытого бара), а не с индекса бара 0 (текущего формирующегося бара). Это исключает вычисление FFD на незавершенном баре с изменяющейся ценой закрытия. Иначе может возникнуть смещение из-за заглядывания в будущее (look-ahead bias) при принятии решений на закрытии бара.
Во-вторых, определение нового бара использует iTime для сравнения временных меток открытия баров. Это надежнее, чем подсчет тиков, поскольку MetaTrader может доставить несколько тиков в пределах одной миллисекунды, а счетчик тиков может сброситься при переподключении.
FFD для нескольких признаков
В полноценном ML-конвейере EA может потребоваться дробно дифференцировать несколько признаков (цену close, VWAP, volume-weighted close и т. д.), каждый из которых потенциально имеет собственное оптимальное d*. Движок поддерживает это за счет создания нескольких экземпляров CFFDEngine объектов:
CFFDEngine g_ffd_close; // d* = 0.40 for close price CFFDEngine g_ffd_volume; // d* = 0.25 for cumulative volume int OnInit(void) { if(!g_ffd_close.Init(0.40,1e-5,true)) return(INIT_FAILED); if(!g_ffd_volume.Init(0.25,1e-5,false)) return(INIT_FAILED); return(INIT_SUCCEEDED); }
Каждый экземпляр модуля хранит собственный вектор весов. Совокупный объем памяти составляет всего несколько килобайт. Стоимость на бар растет линейно с числом признаков, но каждое скалярное произведение независимо, поэтому суммарное время остается значительно меньше одной миллисекунды даже для пяти-шести одновременных признаков.
Проверка на соответствие Python-конвейеру обработки
Перед использованием модуль на MQL5 должен давать численно идентичные результаты с Python-конвейером обработки на одних и тех же входных данных. Процедура валидации такова:
- Экспортировать ценовой ряд из MetaTrader в CSV с помощью скрипта.
- Запустить Python-функцию frac_diff_ffd на ценах из CSV с теми же настройками d, τ и use_log.
- Запустить MQL5-класс CFFDEngine на тех же ценах (либо через скрипт, записывающий результаты в CSV, либо сравнив значения индикаторного буфера с выводом Python).
- Вычислить максимальное абсолютное расхождение по всем барам. Максимальное абсолютное расхождение должно быть ниже 10−12.
Следующий MQL5-скрипт автоматизирует экспорт и вычисление для валидации:
//+------------------------------------------------------------------+ //| FFDValidation.mq5 — Export FFD values for cross-validation | //+------------------------------------------------------------------+ #include "FFDEngine.mqh" input double InpD = 0.4; input double InpThreshold = 1e-5; input int InpBars = 5000; //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart(void) { CFFDEngine engine; if(!engine.Init(InpD,InpThreshold,true)) return; double close[]; int copied=CopyClose(_Symbol,_Period,0,InpBars,close); if(copied<engine.GetMinBars()) { PrintFormat("Not enough bars: got %d, need at least %d", copied,engine.GetMinBars()); return; } //--- ensure chronological order (oldest first) ArraySetAsSeries(close,false); //--- compute FFD for all bars double ffd_buffer[]; ArrayResize(ffd_buffer,copied); engine.ComputeBuffer(close,ffd_buffer,copied,0); //--- write to CSV int file=FileOpen("ffd_validation.csv",FILE_WRITE|FILE_CSV,","); if(file==INVALID_HANDLE) { Print("Cannot open file"); return; } FileWrite(file,"bar_index","close","ffd"); for(int i=0; i<copied; i++) { if(ffd_buffer[i]!=EMPTY_VALUE) FileWrite(file,i,DoubleToString(close[i],8), DoubleToString(ffd_buffer[i],12)); } FileClose(file); PrintFormat("Validation file written: %d bars",copied); }
Соответствующая проверка на Python читает оба CSV-файла и сообщает расхождение:
import pandas as pd import numpy as np from afml.features.fracdiff import frac_diff_ffd # Load MQL5 output mql5 = pd.read_csv("ffd_validation.csv") # Reconstruct the same computation in Python prices = pd.Series(mql5["close"].values, name="close") py_ffd = frac_diff_ffd(prices, d=0.4, thres=1e-5, use_log=True) # Align indices and compare mql5_ffd = mql5["ffd"].values py_ffd_vals = py_ffd.values max_diff = np.max(np.abs(mql5_ffd[:len(py_ffd_vals)] - py_ffd_vals)) print(f"Max absolute difference: {max_diff:.2e}") assert max_diff < 1e-12, "VALIDATION FAILED" print("VALIDATION PASSED")
Если максимальное расхождение превышает 10−12, две самые частые причины — обрезанный вектор весов и ошибка порядка массива. CopyClose записывает данные в соответствии с флагом ArraySetAsSeries целевого массива. Если другая часть EA изменит этот флаг, порядок массива может незаметно развернуться. Всегда вызывайте ArraySetAsSeries(close, false) в качестве меры предосторожности перед передачей массива в ComputeBuffer.
Вопросы производительности
Вычисление FFD легковесно, но несколько проектных решений влияют на реальную производительность в работающем EA.
Когда вычислять
Самая важная оптимизация не вычислительная, а временная: вычислять FFD только при формировании нового бара, а не на каждом тике. График M1 на активной валютной паре получает 5–50 тиков в секунду. Вычисление FFD на каждом тике расходует CPU и дает нестабильные значения (close текущего бара меняется с каждым тиком). Показанная выше схема определения нового бара полностью устраняет эту проблему.
Для индикатора MetaTrader обрабатывает это автоматически через prev_calculated. На последующих тиках внутри того же бара prev_calculated равен rates_total, а тело цикла ComputeBuffer выполняется ровно один раз (чтобы обновить значение формирующегося бара). Это приемлемо для визуального отображения, где пользователь ожидает видеть обновление индикатора в реальном времени.
Размещение в памяти
Массивы MQL5 непрерывны в памяти. Внутренний цикл скалярного произведения обращается к prices[] и m_weights[] последовательно, что оптимально для кэш-линий CPU. Особые соображения по размещению не нужны: естественный паттерн доступа уже дружественен к кэшу.
Ветвление в горячем цикле
Метод if(m_use_log) внутри цикла скалярного произведения добавляет ветвление на каждой итерации. Для максимальной пропускной способности можно разделить Compute() на версии с логарифмированием и без него и выбрать функцию в Init(). На практике предсказатель ветвлений быстро сойдется, потому что условие неизменно внутри вызова, поэтому накладные расходы фактически равны нулю. Ясность кода одной функции перевешивает теоретическое наносекундное улучшение.
Численная точность
MQL5 использует арифметику двойной точности IEEE 754 (64 бита), такую же, как Python float64. Скалярное произведение накапливает width + 1 слагаемых. Для ширины от сотен до нескольких тысяч накопленная ошибка с плавающей точкой имеет порядок 10−13 до 10−14, что намного ниже любой практической значимости. Компенсированное суммирование (Кэхэна) не требуется.
| Параметр | Типичное значение | Влияние |
|---|---|---|
| d | 0.3 – 0.5 | Управляет компромиссом между стационарностью и памятью |
| τ (порог) | 10−5 – 10−4 | Основной фактор ширины окна; уменьшение τ вдвое примерно удваивает ширину |
| Ширина (бары) | 200 – 4000 | Задается d и τ. При τ = 10−5: от 926 (d = 0.5) до 4075 (d = 0.1). При τ = 10−4: от 199 до 650. |
| Время вычисления на бар | < 1 ms | Масштабируется линейно с шириной. При width = 1457 скалярное произведение выполняется значительно меньше чем за миллисекунду, что пренебрежимо мало относительно задержки CopyClose. |
| Память на один экземпляр модуля | < 40 KB | Массив весов (8 байт × width) + переменные состояния. При width = 4075: ~33 КБ. |
Таблица 1. Характеристики производительности CFFDEngine для типичных значений параметров
Заключение
Реализация на MQL5 сохраняет математические свойства, установленные в сопутствующей статье, и при этом адаптируется к ограничениям торговой среды реального времени. Вектор весов вычисляется один раз в цикле, управляемом порогом, без фиксированного ограничения; вычисление на бар — это ограниченное скалярное произведение; индикатор интегрируется с оптимизацией prev_calculated в MetaTrader.
Поддерживаются три схемы использования. Пользовательский индикатор (FFD.mq5) обеспечивает визуальную обратную связь и служит общим источником данных для нескольких EA через дескрипторы iCustom. Шаблон прямого встраивания использует CFFDEngine внутри EA для полного контроля над временем вычислений и выбором цены. Шаблон нескольких признаков создает отдельные движки для каждого признака, которому требуется дробное дифференцирование, при этом каждый экземпляр модуля хранит собственный d* и вектор весов.
Скрипт валидации подтверждает численную эквивалентность Python-конвейеру обработки. После проверки значения d*, определенные с помощью fracdiff_optimal в Python, можно жестко задать как входные параметры EA, замыкая цикл между офлайн-исследованием и работой с потоковыми данными в реальном времени. По мере развития конвейера обработки будущие статьи интегрируют модуль FFD в полную цепочку построения признаков и вывода модели внутри фреймворка Expert Advisor.
Список литературы
- Лопес де Прадо, М. (2018). Advances in Financial Machine Learning. Wiley. Глава 5: Дробно дифференцированные признаки.
- Хоскинг, J. R. M. (1981). «Fractional differencing». Biometrika, 68(1), 165–176.
- Справочник MQL5: CopyBuffer, CopyClose, OnCalculate.
Прикрепленные файлы
| Файл | Поместить в | Описание |
|---|---|---|
| FFDEngine.mqh | MQL5\Include | Класс CFFDEngine: генерация весов, вычисление для одного бара, заполнение индикаторного буфера. |
| FFD.mq5 | MQL5\Indicators | Пользовательский индикатор, отображающий ряд FFD в отдельном окне |
| FFDValidation.mq5 | MQL5\Scripts | Скрипт валидации, экспортирующий значения FFD для сверки с Python |
| ffd_cross_validate.py | Каталог проекта | Читает CSV, экспортированный FFDValidation.mq5, повторно вычисляет значения FFD в Python через frac_diff_ffd с теми же параметрами d, threshold и log, а также сообщает максимальное абсолютное расхождение по барам. Требуются NumPy и Pandas. Определяет d из имени файла. |
Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/22015
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.
Нейросети в трейдинге: от рыночного шума к устойчивому торговому плану (MomAD)
Моделирование рынка: Первые шаги на SQL в MQL5 (V)
Торговые инструменты MQL5 (Часть 24): Улучшение восприятия глубины с помощью 3D-кривых, режима панорамирования и навигации через виджет ViewCube
Торговые инструменты MQL5 (Часть 23): Трёхмерные графики с управляемой камерой и поддержкой DirectX для анализа распределений
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования