Нейросети в трейдинге: Сеточная аппроксимация событийного потока как инструмент анализа ценовых паттернов (CDC-модуль)
Введение
Мы продолжаем последовательную и, что важно, осмысленную работу по переносу идей фреймворка EEMFlow в практическую плоскость алгоритмической торговли. Предыдущие статьи заложили прочный фундамент: от постановки самой проблемы и мотивации перехода к событийной модели рынка, до реализации ключевых адаптивных механизмов, отвечающих за стабилизацию и структурирование анализируемого потока данных. И теперь настал логичный момент сделать шаг дальше. Но прежде чем углубляться в очередной уровень реализации, спокойно и связно напомним, зачем весь этот путь вообще был начат. В чём заключается принципиальная сила фреймворка EEMFlow, и какие архитектурные идеи лежат в его основе.
Современный финансовый рынок по своей природе давно перестал быть гладким временным рядом. Он дискретен, неравномерен и асинхронен. Периоды затишья внезапно сменяются всплесками активности, а классические модели, привыкшие к равномерной сетке времени, либо теряют чувствительность, либо начинают видеть шум там, где формируется сигнал. Именно эта фундаментальная проблема — разрыв между реальной динамикой рынка и способом её формального описания — стала отправной точкой для данной работы. Мы сознательно отказались от иллюзии равного времени и сделали ставку на событийное представление, где рынок описывается не количеством секунд, а фактом изменения состояния.
Такой подход сразу обнажает новую сложность. Событийный поток живёт по своим законам, его плотность меняется на порядки, распределение нестационарно, а локальные всплески могут полностью доминировать над глобальной структурой. Без адаптации такой поток либо перегружает модель, либо, напротив, разрежается до потери информативности. Именно здесь проявляется ключевая философия EEMFlow: прежде чем что‑то оценивать, нужно привести данные в форму, с которой вообще можно работать. Не сглаживать рынок грубо и не нормализовать его по средним, а научиться динамически подстраиваться под его текущую плотность и ритм.
В предыдущих статьях мы подробно разобрали Adaptive Density Module. Он решает задачу, которую в классических торговых системах обычно пытаются закрыть костылями: фильтрами волатильности, эвристическими ограничителями. Здесь же плотность событий становится управляемой величиной. Модель сама выбирает, сколько информации ей нужно в текущий момент, и в каком масштабе эту информацию следует воспринимать. Для рынка это означает устойчивость, а для алгоритма — предсказуемость поведения.
Важно подчеркнуть, что EEMFlow не стремится угадать рынок. Он стремится корректно его измерить. Это принципиально иной взгляд, близкий к классической инженерной школе. Сначала авторы фреймворка выстраивают надёжный измерительный прибор, и только затем модель начинает делать выводы. Именно поэтому архитектура фреймворка выглядит на первый взгляд сдержанно и даже консервативно. Здесь нет избыточной глубины ради глубины, нет декоративных слоёв, каждый блок выполняет строго определённую функцию и логически вытекает из предыдущего.
Базовая архитектура EEMFlow строится вокруг последовательного преобразования событийного потока в структурированное пространственно‑временное представление. События агрегируются, выравниваются по плотности, проходят многоуровневое кодирование и постепенно превращаются в поток движения. При этом принципиально важно, что обработка ведётся не в абсолютных координатах времени, а в относительных изменениях. Это делает архитектуру устойчивой к смене режимов рынка и естественным образом масштабируемой.

Отдельного внимания заслуживает идея многоуровневой обработки плотности. В EEMFlow она реализована как система взаимодействующих механизмов, которые работают и на глобальном, и на локальном уровне. И здесь фреймворк показывает свою практичность: адаптация плотности позволяет перераспределять вычислительные ресурсы туда, где рынок действительно активен, не расходуя их на пустые участки.
Важное преимущество EEMFlow, которое постепенно раскрывается по мере реализации, — его модульность. Архитектура допускает поэтапное развитие без необходимости переписывать уже работающие части. Мы начали с событийного представления и реализовали ADM. Каждый новый компонент встраивается в систему органично, усиливая её, а не ломая баланс.
Не менее важен и вопрос интерпретируемости. Несмотря на внешнюю сложность, EEMFlow остаётся прозрачным с инженерной точки зрения. Мы всегда можем ответить на вопрос, почему модель в данный момент реагирует именно так. Плотность событий, выбранный масштаб, активные признаки — всё это наблюдаемо и поддаётся анализу. Для прикладных торговых систем это критично. Рынок не прощает слепых решений, даже если они красиво выглядят в тестере.
Логическим продолжением нашей работы становится вопрос детализации. После того как событийный поток стабилизирован по плотности, выровнен по признакам и приведён к согласованному пространственно‑временному представлению, возникает следующая, уже более тонкая задача — восстановление локальной структуры движения там, где данные неполны, разрежены или противоречивы. Рынок, как и любое событийное пространство, не даёт информации равномерно. Он оставляет пробелы, маскирует истинную динамику и часто прячет важные детали именно в тех зонах, где сигнал выглядит слабым.
В архитектуре фреймворка EEMFlow эту проблему решает Confidence‑induced Detail Completion Module (CDC). Его роль нельзя свести к косметическому улучшению результата. Речь идёт о принципиальном этапе, где модель начинает осознанно различать надёжные участки движения и области неопределённости, а затем аккуратно, без агрессивных предположений, дополнять недостающую структуру. Это возвращает системе целостность и позволяет сохранить непрерывность динамической картины без искажения базового сигнала.
Именно к построению и адаптации этого модуля средствами MQL5 мы сегодня займемся.
Архитектура CDC-модуля
В архитектуре EEMFlowConfidence-induced Detail Completion модуль появляется не случайно и не для красоты. Это ответ на вполне прикладную проблему, которая неизбежно возникает при многоуровневой обработке движения. На этапе апсемплинга разные движения начинают смешиваться, границы размываются, а локальная структура сигнала теряет чёткость. Для торговой интерпретации это особенно критично, поскольку именно в зонах перехода, ускорения и смены режима рынок чаще всего формирует значимые решения.
CDC работает как аккуратный механизм доводки уже полученного движения. На входе он получает поток меньшего масштаба и сначала переносит его на следующий уровень простой билинейной интерполяцией. Это лишь черновик, первичное приближение, которое сознательно не претендует на точность. Дальше в работу вступают два взаимодополняющих контура — самокоррекции и самокорреляции. Каждый из них решает свою часть задачи.
Ветвь самокоррекции действует локально и предельно прагматично. Она анализирует согласованные признаки соседних событий и, опираясь на плотную сверточную структуру, восстанавливает корректное движение вблизи границ и зон разрыва. Одновременно с этим формируется карта доверия — практическая оценка того, где результат наиболее уязвим к ошибкам. По сути, система сама отмечает участки, где не уверена в исходной интерполяции.
Вторая ветвь — самокорреляционная — смотрит шире. Используя механизм Self-Attention и большое рецептивное поле, она ищет в пространстве признаков области с похожей динамикой и переносит эту информацию на проблемные зоны. Это уже не локальный ремонт, а глобальная сверка. Eсли где-то рынок ведёт себя аналогично, эта структура может быть использована для уточнения текущего движения.
Финальный результат формируется взвешенным объединением этих двух представлений. Исправленное движение, полученное через локальную коррекцию, и уточнённый поток, усиленный Self-Attention, аккуратно смешиваются. Причём решающую роль здесь играет карта доверия. Там, где исходная оценка надёжна, система сохраняет её. Там, где вероятность ошибки высока, приоритет отдаётся уточнённому варианту. В итоге движение не перерисовывается, а деликатно дополняется именно там, где это действительно необходимо.

В контексте событийного рынка CDC выглядит особенно уместно. Мы не навязываем рынку лишнюю интерпретацию и не сглаживаем сигнал механически. Мы позволяем системе самой определить зоны неопределённости и восполнить их, опираясь на уже наблюдаемую структуру данных.
Блок коррекции потока
Практическую работу мы начнём с блока самокоррекции потока. Это осознанный и консервативно-инженерный выбор. Именно здесь формируется та самая локальная чувствительность системы, от которой впоследствии зависит качество восстановления движения в зонах неопределённости. Прежде чем подключать механизмы внимания и глобального сопоставления, необходимо научить модель аккуратно читать ближайшее окружение события, видеть границы, разрывы и тонкие переходы между различными режимами динамики.
С практической точки зрения такой блок идеально ложится на возможности MQL5. Он компактен, предсказуем по вычислительной нагрузке и хорошо масштабируется. Мы начинаем с плотного, последовательного анализа локальных признаков, закладывая основу для дальнейшей коррекции движения и формирования карты доверия.
Весь процесс самокоррекции мы сознательно организуем в рамках отдельного объекта. Это не просто удобство реализации, а прямое следствие архитектурной логики CDC. Ветвь Self-Corrector должна быть автономной, замкнутой и предсказуемой в поведении, поскольку именно она отвечает за локальную корректировку движения и формирование карты доверия. Поэтому вынос её в самостоятельный блок выглядит естественно.
Класс CNeuronCDCSelfCorrector органично встраивается в уже знакомую иерархию, наследуясь от CNeuronBatchNormOCL. Это сразу задаёт нужный контекст работы. Без лишнего героизма и ручного управления буферами — всё строго в рамках уже отлаженного вычислительного конвейера.
class CNeuronCDCSelfCorrector : public CNeuronBatchNormOCL { protected: uint iUnits; uint iVariables; CLayer cEstimator; //--- virtual bool feedForward(CNeuronBaseOCL *NeuronOCL) override; virtual bool updateInputWeights(CNeuronBaseOCL *NeuronOCL) override; virtual bool calcInputGradients(CNeuronBaseOCL *NeuronOCL) override; public: CNeuronCDCSelfCorrector(void) {}; ~CNeuronCDCSelfCorrector(void) {}; //--- virtual bool Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl, uint window, uint &inside_dimensions[], uint window_out, uint units_count, uint variables, ENUM_OPTIMIZATION optimization_type, uint batch); //--- virtual int Type(void) override const { return defNeuronCDCSelfCorrector; } //--- methods for working with files virtual bool Save(int const file_handle) override; virtual bool Load(int const file_handle) override; //--- virtual bool WeightsUpdate(CNeuronBaseOCL *source, float tau) override; virtual bool Clear(void) override; //--- virtual void SetActivationFunction(ENUM_ACTIVATION value) override { }; virtual void SetOpenCL(COpenCLMy *obj) override; virtual void TrainMode(bool flag) override; };
Внутренняя структура класса отражает функциональную роль модуля. Параметры iUnits и iVariables задают размерность и степень выразительности локального анализа. Здесь нет случайных величин. Размер последовательности и количество каналов напрямую определяют, насколько тонко блок сможет различать пограничные состояния движения. Объект cEstimator выступает в роли плотного оценщика — того самого сверточного ядра, которое аккуратно извлекает локальные закономерности из согласованных признаков соседних событий.
Ключевые вычислительные этапы инкапсулированы в стандартных для нейронной архитектуры методах.
Метод инициализации стоит разобрать отдельно, потому что именно в нём наиболее явно проявляется логика архитектуры Self-Corrector и её соответствие исходной идее CDC. Этот метод не просто создаёт набор внутренних слоёв. Он шаг за шагом собирает плотный корректирующий анализатор, рассчитанный на работу с локальной структурой движения и постепенное расширение контекста без потери разрешения.
bool CNeuronCDCSelfCorrector::Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl, uint window, uint &inside_dimensions[], uint window_out, uint units_count, uint variables, ENUM_OPTIMIZATION optimization_type, uint batch) { uint layers = inside_dimensions.Size(); if(layers <= 0) return false; if(!CNeuronBatchNormOCL::Init(numOutputs, myIndex, open_cl, window_out * units_count * variables, batch, optimization_type)) return false; activation = None;
Инициализация начинается с проверки внутренней конфигурации. Массив inside_dimensions задаёт глубину и форму анализатора, и если он пуст, модуль просто не имеет смысла. Это подчёркивает важный момент: Self-Corrector не является фиксированным блоком. Его выразительная способность определяется заранее выбранной архитектурой, а не жёстко зашитым числом слоёв.
Далее вызывается базовая инициализация родительского класса. Это ключевой момент. Мы сразу приводим весь блок к согласованному формату данных, совместимому с остальной частью CDC и всей событийной архитектурой. Функция активации намеренно отключается. Self-Corrector не должен искажать динамику нелинейностями на уровне контейнера — все нелинейные преобразования сосредоточены внутри оценщика.
Основная логика сосредоточена в построении последовательности cEstimator. Здесь мы видим плотную, почти классическую схему: свёртка, нормализация, конкатенация, и так по слоям. Каждый цикл увеличивает входное окно за счёт добавления новых фильтров, что постепенно расширяет локальный контекст. Это важная деталь. Вместо резкого роста рецептивного поля мы получаем аккуратное наращивание информации, что идеально подходит для работы с границами движений и локальными разрывами.
iUnits = units_count; iVariables = variables; //--- CNeuronMultiWindowsConvWPadOCL* conv = NULL; CNeuronBatchNormOCL* norm = NULL; CNeuronBaseOCL* concat = NULL; uint index = 0; uint inp_window = window; uint windows[1] = { 3 * inp_window }; for(uint i = 0; i < layers; i++) { conv = new CNeuronMultiWindowsConvWPadOCL(); if(!conv || !conv.Init(0, index, OpenCL, windows, inp_window, inside_dimensions[i], iUnits, iVariables, optimization, iBatch) || !cEstimator.Add(conv)) { DeleteObj(conv) return false; } conv.SetActivationFunction(SoftPlus); index++; inp_window += conv.GetFilters(); windows[0] = 3 * inp_window; norm = new CNeuronBatchNormOCL(); if(!norm || !norm.Init(0, index, OpenCL, conv.Neurons(), iBatch, optimization) || !cEstimator.Add(norm)) { DeleteObj(norm) return false; } norm.SetActivationFunction(None); index++; concat = new CNeuronBaseOCL(); if(!concat || !concat.Init(0, index, OpenCL, inp_window * iUnits * iVariables, optimization, iBatch) || !cEstimator.Add(concat)) { DeleteObj(concat) return false; } index++; concat.SetActivationFunction(None); }
Свёрточные слои создаются с поддержкой паддинга. Это позволяет анализировать движение не разрушая пространственную согласованность. Активационная функция SoftPlus выбрана здесь не случайно. Она сохраняет плавность отклика и хорошо ведёт себя вблизи нуля, что критично при анализе слабых и пограничных движений. Это не агрессивная нелинейность, а корректный инженерный компромисс между чувствительностью и устойчивостью.
После каждой свёртки следует нормализация. Она стабилизирует распределение признаков и делает обучение предсказуемым, особенно в условиях неравномерной плотности событий. Затем идёт слой конкатенации, который собирает текущие и ранее накопленные признаки в единое представление. Именно здесь Self-Corrector приобретает плотный характер. Информация не теряется по пути, а накапливается и переиспользуется на каждом уровне.
Финальный свёрточный слой завершает формирование корректирующего представления и приводит его к заданному размеру. Он аккуратно замыкает архитектуру, не добавляя лишних операций и не усложняя вычислительный граф.
conv = new CNeuronMultiWindowsConvWPadOCL(); if(!conv || !conv.Init(0, index, OpenCL, windows, inp_window, window_out, iUnits, iVariables, optimization, iBatch) || !cEstimator.Add(conv)) { DeleteObj(conv) return false; } //--- return true; }
В результате мы получаем компактный, но выразительный анализатор, способный извлекать локальные закономерности движения и подготавливать данные для формирования корректирующего потока и карты доверия.
В целом метод инициализации демонстрирует тот самый подход, который проходит красной нитью через всю архитектуру фреймворка EEMFlow. Никакой магии, никакой избыточной сложности. Только последовательное, логически выверенное построение блока, который делает ровно то, что от него требуется. И делает это устойчиво. Именно с таких деталей и складывается рабочая архитектура CDC в контексте реального торгового применения.
Алгоритм прямого прохода реализован предельно аккуратно и, что важно, полностью отражает архитектурную идею плотной локальной коррекции. Здесь нет скрытых переходов и неявных преобразований. Каждый шаг логически следует из предыдущего и работает на одну цель — сформировать устойчивое, насыщенное признаками представление локального движения.
bool CNeuronCDCSelfCorrector::feedForward(CNeuronBaseOCL *NeuronOCL) { CNeuronBaseOCL* residual = NeuronOCL; CNeuronBaseOCL* prev = residual; CNeuronBaseOCL* curr = NULL; if(!residual) return false; //--- uint count = iUnits * iVariables; uint res_window = residual.Neurons() / count; int total = cEstimator.Total();
Прямой проход начинается с получения указателя на объект исходных данных, который в контексте CDC играет роль остаточного представления. Это не просто входной тензор, а базовый поток, относительно которого далее наращивается уточняющая информация.
Мы сразу фиксируем размерность окна, исходя из числа каналов и длины последовательности, чтобы вся последующая обработка велась в согласованном формате. Такой расчёт на старте избавляет от лишних проверок и пересчётов внутри цикла.
Далее управление передаётся последовательности cEstimator, которая, напомню, содержит всю плотную структуру Self-Corrector. Итерация по её внутренним слоям реализует ключевой принцип плотного соединения.
for(int i = 0; i < total; i++) { curr = cEstimator[i]; if(!curr) return false; if(curr.Type() == defNeuronBaseOCL) { if(!Concat(residual.getOutput(), prev.getOutput(), curr.getOutput(), res_window, prev.Neurons() / count, count)) return false; residual = curr; res_window = curr.Neurons() / count; } else if(!curr.FeedForward(prev)) return false; prev = curr; }
Проверка типа компонентов последовательности позволяет нам выделить объекты конкатенации. Текущее остаточное представление объединяется с предыдущим результатом. Это критический момент. Вместо того чтобы затирать информацию, мы последовательно расширяем контекст, позволяя каждому следующему слою работать уже с обогащённым набором признаков.
Во всех остальных случаях выполняется стандартный прямой проход слоя относительно предыдущего результата. Такой разделённый сценарий делает код прозрачным и избавляет от неявных зависимостей. Мы всегда точно знаем, где происходит свёртка, где нормализация, а где — структурное объединение признаков.
По мере продвижения по слоям обновляется и текущее остаточное представление. Его окно постепенно растёт, отражая расширение локального контекста. Это особенно важно для корректировки границ движений. Система не делает резких скачков в масштабе, а аккуратно подсматривает всё дальше, сохраняя связь с исходным сигналом.
Финальный этап прямого прохода — вызов одноименного метода родительского класса. Здесь выход плотного оценщика приводится к нормализованному виду, совместимому с остальной частью CDC.
if(!CNeuronBatchNormOCL::feedForward(prev)) return false; //--- return true; }
Это завершает формирование корректирующего потока и карты доверия, не нарушая общей согласованности вычислительного графа.
В результате метод feedForward реализует именно тот сценарий, который и ожидается от Self-Corrector. Локальный анализ, плотное накопление признаков, аккуратная нормализация и отсутствие агрессивных преобразований. Всё работает спокойно, предсказуемо и строго по архитектуре. Такой код не пытается быть эффектным. Он просто делает свою работу — и делает её правильно.
Обучение модели — это не формальная обратная операция, а аккуратный разбор допущенных неточностей с сохранением архитектурной логики плотного анализатора. Метод calcInputGradients как раз и реализует эту работу над ошибками, последовательно проводя сигнал градиента обратно через всю структуру Self-Corrector, не разрушая накопленные связи и не теряя локальный контекст.
bool CNeuronCDCSelfCorrector::calcInputGradients(CNeuronBaseOCL *NeuronOCL) { if(!NeuronOCL) return false; //--- if(!CNeuronBatchNormOCL::calcInputGradients(cEstimator[-1])) return false;
Процедура начинается с верхнего уровня. Сначала вычисляются входные градиенты для последнего элемента cEstimator через механизм пакетной нормализации. Это важный шаг. Именно здесь ошибка приводится к согласованному масштабу, чтобы дальнейшее распространение не усиливало случайные всплески и не заглушало значимые отклонения. По сути, мы возвращаемся из пространства выходных оценок обратно в плотный корректирующий анализатор уже в очищенном виде.
Далее начинается обратный проход по слоям cEstimator в строгом обратном порядке. Каждый слой анализирует вклад своих выходов в итоговую ошибку, используя стандартный механизм вычисления скрытых градиентов.
CNeuronBaseOCL* next = cEstimator[-1]; CNeuronBaseOCL* curr = NULL; CNeuronBaseOCL* residual = NULL; uint count = iUnits * iVariables; //--- for(int i = cEstimator.Total() - 2; i >= 0; i--) { curr = cEstimator[i]; if(!curr || !curr.CalcHiddenGradients(next)) return false;
Однако ключевая особенность проявляется в моменте обработки слоёв конкатенации. Здесь обратное распространение не может быть тривиальным, поскольку выход формировался путём объединения нескольких потоков признаков.
Когда встречается слой, отвечающий за конкатенацию, градиенты аккуратно перераспределяются. Часть ошибки относится к вновь сформированным признакам, часть — к ранее накопленному остаточному представлению.
if(curr.Type() == defNeuronBaseOCL && i > 0) { int concat_win = curr.Neurons() / (int)count; if(!!residual) if(!SumAndNormilize(curr.getGradient(), residual.getPrevOutput(), curr.getGradient(), concat_win, false, 0, 0, 0, 1)) return false; next = cEstimator[i - 1]; if(!next) return false; int prev_win = next.Neurons() / (int)count; if(!DeConcat(curr.getPrevOutput(), next.getGradient(), curr.getGradient(), concat_win - prev_win, prev_win, count)) return false; residual = curr; Deactivation(next); i--; } else next = curr; }
Для этого сначала выполняется суммирование градиентов с учётом остаточного потока, а затем — деконкатенация, которая возвращает соответствующие компоненты ошибки на предыдущий уровень. Это критически важный момент: именно здесь сохраняется плотный характер архитектуры и обеспечивается корректное обучение всех уровней сразу, а не только последних слоёв.
Дополнительно, при необходимости, выполняется деактивация предыдущего слоя. Это подчёркивает, что Self-Corrector не является линейной цепочкой, он содержит вложенные нелинейные преобразования, и каждое из них должно быть корректно откручено при обратном проходе. Код делает это явно, без скрытых допущений, что особенно важно в условиях сложной событийной динамики.
После завершения обратного прохода по cEstimator градиенты передаются на объект исходных данных, с которого началась коррекция. Здесь снова учитывается возможное наличие функции активации и остаточного соединения.
if(!NeuronOCL.CalcHiddenGradients(next)) return false; if(!!residual) { if(NeuronOCL.Activation() != None) if(!DeActivation(NeuronOCL.getOutput(), residual.getPrevOutput(), residual.getPrevOutput(), NeuronOCL.Activation())) return false; if(!SumAndNormilize(NeuronOCL.getGradient(), residual.getPrevOutput(), NeuronOCL.getGradient(), NeuronOCL.Neurons() / count, false, 0, 0, 0, 1)) return false; } //--- return true; }
Если остаточный поток участвовал в формировании результата, его вклад добавляется обратно через суммирование. Тем самым мы замыкаем цикл обучения, возвращая информацию об ошибке к исходному представлению потока.
В результате calcInputGradients реализует не просто стандартный обратный проход, а аккуратный, архитектурно согласованный механизм обучения плотного корректирующего модуля. Ошибка не проваливается назад механически, а осмысленно распределяется между всеми уровнями локального анализа. Именно это позволяет Self-Corrector постепенно учиться различать истинные границы движения и случайные артефакты, не теряя устойчивости и не переобучаясь на шум.
Модуль доверительной детализации
Следующим шагом мы переходим от локального анализа к целостной, уже спайковой реализации CDC. Именно здесь разрозненные компоненты — самокоррекция, корреляционный поиск и маскирование доверия — собираются в единый вычислительный узел. Класс CNeuronSpikeCDC играет роль такого узла и, по сути, является прикладным воплощением всей идеи Confidence-induced Detail Completion в событийной форме.
class CNeuronSpikeCDC : public CNeuronSpikeConvBlock { protected: CNeuronCDCSelfCorrector cEsimator; CNeuronBaseOCL cFlowCorrector; CNeuronRelativeSelfAttention cFlowCorrelation; CNeuronBaseOCL cFlowMask; CNeuronBaseOCL cFlow; //--- virtual bool feedForward(CNeuronBaseOCL *NeuronOCL) override; virtual bool updateInputWeights(CNeuronBaseOCL *NeuronOCL) override; virtual bool calcInputGradients(CNeuronBaseOCL *NeuronOCL) override; public: CNeuronSpikeCDC(void) {}; ~CNeuronSpikeCDC(void) {}; //--- virtual bool Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl, uint window, uint &inside_dimensions[], uint window_out, uint units_count, uint variables, ENUM_OPTIMIZATION optimization_type, uint batch); //--- virtual int Type(void) override const { return defNeuronSpikeCDC; } //--- methods for working with files virtual bool Save(int const file_handle) override; virtual bool Load(int const file_handle) override; //--- virtual bool WeightsUpdate(CNeuronBaseOCL *source, float tau) override; virtual bool Clear(void) override; //--- virtual void SetActivationFunction(ENUM_ACTIVATION value) override { }; virtual void SetOpenCL(COpenCLMy *obj) override; virtual void TrainMode(bool flag) override; };
Наследование от CNeuronSpikeConvBlock сразу задаёт правильный масштаб. Мы больше не работаем с отдельным корректирующим фильтром, а встраиваем CDC в общий спайковый конвейер, где каждый блок обязан быть совместимым с событийной динамикой и работать в условиях разреженного, неравномерного потока. Это не косметическая адаптация, а архитектурный переход к полноценной событийной модели.
Внутренняя структура класса отражает логику исходного фреймворка практически напрямую, но в форме, удобной для практической реализации. Объект cEstimator инкапсулирует самокорректирующую ветвь, с которой мы только что подробно разобрались. Это локальный аналитик, отвечающий за уточнение движения и формирование карты доверия. Он работает на уровне непосредственного соседства событий и задаёт основу для всех последующих решений.
Объект cFlowCorrector служит связующим звеном между локальной коррекцией и итоговым потоком. Именно через него проходят скорректированные смещения, которые затем сопоставляются с глобальной структурой движения. Здесь нет самостоятельной логики — это аккуратный контейнер, обеспечивающий согласованность данных и правильное включение корректирующего сигнала в общий поток.
Ветвь cFlowCorrelation реализует самокорреляционный механизм. Это Self-Attention, ориентированный не на агрессивное усиление признаков, а на поиск согласованных динамических структур в расширенном контексте. Он позволяет модели подсмотреть похожие движения и использовать их для уточнения проблемных зон, не разрушая локальную геометрию потока.
Инициализация объекта выстроена так, чтобы все внутренние компоненты сразу заняли свои строго определённые места в общем вычислительном потоке. Здесь нет импровизации или динамической сборки на лету. Архитектура CDC формируется целиком на этапе инициализации, что делает поведение модуля предсказуемым и удобным для анализа.
bool CNeuronSpikeCDC::Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl, uint window, uint &inside_dimensions[], uint window_out, uint units_count, uint variables, ENUM_OPTIMIZATION optimization_type, uint batch) { if(!CNeuronSpikeConvBlock::Init(numOutputs, myIndex, open_cl, window, window, window_out, units_count, variables, optimization_type, batch)) return false;
Процесс начинается с базовой инициализации родительского класса. Этим шагом мы сразу вписываем CDC в общий спайковый конвейер, задавая единый формат окон, размерность каналов и параметры исполнения.
Далее последовательно инициализируются внутренние компоненты. Первым создаётся cEstimator, то есть самокорректирующая ветвь.
uint index = 0; if(!cEsimator.Init(0, index, OpenCL, window, inside_dimensions, 2 * window, units_count, variables, optimization, iBatch)) return false;
Ей передаётся исходное окно, внутренняя конфигурация слоёв и расширенное выходное окно. Таким образом мы заранее готовим этот блок к работе с обогащённым локальным контекстом. Он становится источником двух ключевых сущностей — скорректированного движения и карты доверия, пусть пока ещё в неразделённом виде.
Следующий элемент — cFlowCorrector. Он принимает половину выходной размерности Self-Corrector, что отражает разделение информации на потоковые компоненты. Здесь принципиально отключается функция активации. Этот блок не должен интерпретировать данные, его задача — передать скорректированное движение дальше без искажений.
index++; if(!cFlowCorrector.Init(0, index, OpenCL, cEsimator.Neurons() / 2, optimization, iBatch)) return false; cFlowCorrector.SetActivationFunction(None);
После этого инициализируется cFlowCorrelation. Это реализация механизма Self-Attention.
index++; if(!cFlowCorrelation.Init(0, index, OpenCL, window, (window + 3) / 4, units_count * variables, 4, optimization, iBatch)) return false;
В архитектурном смысле это тот самый широкий взгляд, который дополняет локальную коррекцию глобальной сверкой движения.
Затем создаётся cFlowMask. Этот блок формирует маску доверия, и именно поэтому для него выбрана сигмоидальная активация.
index++; if(!cFlowMask.Init(0, index, OpenCL, cFlowCorrector.Neurons(), optimization, iBatch)) return false; cFlowMask.SetActivationFunction(SIGMOID);
Маска должна лежать в ограниченном диапазоне и интерпретироваться как вес, а не как жёсткое решение. Это ключевой элемент, который позволяет CDC работать не бинарно, а мягко, с учётом неопределённости.
Финальным этапом инициализации становится cFlow. Он принимает ту же размерность, что и корректирующий поток, и формирует скорректированный поток. Функция активации снова отключена, чтобы сохранить линейность смешивания и не вносить лишних нелинейностей на завершающем этапе.
index++; if(!cFlow.Init(0, index, OpenCL, cFlowCorrector.Neurons(), optimization, iBatch)) return false; cFlow.SetActivationFunction(None); //--- return true; }
В итоге метод инициализации собирает CDC как целостный, сбалансированный модуль. Каждый компонент получает чёткую роль, согласованную размерность и заранее заданное место в цепочке.
Алгоритм прямого прохода выстроен строго последовательно и, что важно, логически прозрачно. Здесь нет скрытых обходных манёвров — каждый шаг отражает исходную идею Confidence-induced Detail Completion и аккуратно переносит её в спайковую среду.
bool CNeuronSpikeCDC::feedForward(CNeuronBaseOCL *NeuronOCL) { if(!cEsimator.FeedForward(NeuronOCL)) return false;
Работа начинается с вызова прямого прохода для ветви коррекции потока. Это отправная точка всего модуля. На этом этапе формируется первичное представление движения, уже обогащённое внутренней оценкой надёжности. Фактически, мы получаем плотный, но ещё не разделённый сигнал, в котором информация о самом потоке и уверенности в нём сосуществуют в одном пространстве.
Следующий шаг — явное разделение этих сущностей. Через DeConcat выход оценщика раскладывается на два потока: корректируемый cFlowCorrector и маску доверия cFlowMask. Размерности вычисляются не абстрактно, а строго из параметров сверточного блока: длина анализируемой последовательности, число переменных и размер окна. Это подчёркивает архитектурную дисциплину — CDC не живёт отдельно от конвейера, он встроен в него органично.
uint count = cConv.GetUnits() * cConv.GetVariables(); uint window = cConv.GetWindow(); if(!DeConcat(cFlowCorrector.getOutput(), cFlowMask.getOutput(), cEsimator.getOutput(), window, window, count)) return false; dActivation(cFlowCorrector) dActivation(cFlowMask)
После деконкатенации каждый из потоков проходит свою активационную стадию. Для корректируемого движения это, по сути, формальность, поскольку активация там отключена. А вот для маски доверия сигмоидальная функция принципиальна: она переводит данные в интерпретируемое пространство весов, где значение — это мера уверенности. Здесь CDC начинает вести себя как зрелый вероятностный механизм, а не бинарный фильтр.
Далее мы выполняем аккуратное суммирование значений коррекции потока с исходными анализируемыми признаками, формируя альтернативный скорректированный поток. Этот этап критически важен: он позволяет объединить локальную корректировку Self-Corrector с исходной информацией, не отбрасывая важные детали и при этом плавно подавляя шум.
if(!SumAndNormilize(cFlowCorrector.getOutput(), NeuronOCL.getOutput(), cFlowCorrector.getOutput(), window, false, 0, 0, 0, 1)) return false;
По сути, мы создаём промежуточный поток, который уже учитывает внесённые коррекции и сохраняет структурную целостность исходного сигнала.
После этого скорректированный поток подаётся в cFlowCorrelation. Именно здесь включается механизм Self-Attention. Он анализирует пространственно-временные связи внутри окна и выявляет согласованные структуры движения. В контексте событийного трейдинга это особенно ценно: рынок редко движется изолированно, и корреляционные паттерны часто важнее локальных всплесков.
if(!cFlowCorrelation.FeedForward(cFlowCorrector.AsObject())) return false;
Следует подчеркнуть принципиальное отличие нашей реализации от авторской версии фреймворка. В оригинале Self-Correlation основана на механизме Self-Attention, который ищет в пространстве признаков области, идентичные движению в проблемной зоне исходного потока. Другими словами, внимание применяется к сырому потоку до корректировки, и модель пытается восстановить детали на основе исходных, ещё не уточнённых данных.
В нашей реализации мы переносим механизм Self-Attention на скорректированный поток. То есть внимание теперь действует уже на локально уточнённые значения, где часть шумов и артефактов устранена Self-Corrector. Это даёт сразу два преимущества. Во-первых, поиск совпадений становится более точным, поскольку коррекция уже выровняла ключевые признаки движения. Во-вторых, итоговая карта внимания отражает уже доверенную информацию, что повышает устойчивость последующего комбинирования.
Полученные корреляционные признаки затем аккуратно интегрируются с анализируемым потоком посредством взвешенного объединения, где маска доверия выступает в роли весов. Это не просто перемножение тензоров: каждый элемент скорректированного потока масштабируется в соответствии с уровнем доверия, присвоенным Self-Corrector.
if(!GateElementMult(NeuronOCL.getOutput(), cFlowCorrelation.getOutput(), cFlowMask.getOutput(), cFlow.getOutput())) return false;
Таким образом, детали дополняются не везде, а только там, где система действительно верит в их корректность. Это и есть практическая реализация Confidence-induced подхода — строго, экономно и без лишнего шума.
Финальным шагом становится передача управления родительскому классу. На этом этапе CDC полностью растворяется в общем спайковом конвейере, передавая дальше уже уточнённое, взвешенное и архитектурно согласованное представление потока.
if(!CNeuronSpikeConvBlock::feedForward(cFlow.AsObject())) return false; //--- return true; }
В результате прямой проход выглядит не как набор разрозненных операций, а как цельная логическая цепочка. Каждый блок делает ровно то, что от него ожидается. Ни больше, ни меньше. Именно такая сдержанная инженерная строгость и делает CDC пригодным для реальных торговых систем. Где ошибка — это не абстракция, а вполне конкретные деньги.
Переход к CNeuronSpikeCDC знаменует собой качественно новый этап. Мы выходим за рамки отдельных корректирующих приёмов и собираем зрелый событийный модуль, способный работать с неопределённостью, восстанавливать структуру движения и делать это в форме, пригодной для реального торгового применения.
Сегодня мы проделали серьёзную работу и настало время дать мыслям улежаться. В следующей статье мы доведём реализацию подходов фреймворка EEMFlow до логического завершения, полностью воплотив ключевые компоненты и оценив их практическую эффективность на исторических рыночных данных.
Заключение
Проведённая работа позволила глубоко погрузиться в архитектуру фреймворка EEMFlow и успешно реализовать его ключевые компоненты средствами MQL5. На текущем этапе мы успешно реализовали CDC-модуль, включающий Self-Corrector, интеграцию механизма Self-Attention для скорректированного потока и взвешенное объединение сигналов через маску доверия. Этот модуль обеспечивает согласованное формирование итогового скорректированного потока, осуществляя обработку локальных и глобальных признаков движения. Реализация подтверждает логическую целостность архитектуры и инженерную зрелость подхода.
Несмотря на то, что окончательная оценка эффективности фреймворка возможна только после завершения всех этапов тестирования на исторических данных, уже сейчас можно отметить его потенциальную ценность. Реализация средствами MQL5 демонстрирует предсказуемость работы компонентов, а модульная архитектура открывает широкие возможности для дальнейшей оптимизации, масштабирования и применения в реальных рыночных сценариях.
Ссылки
Программы, используемые в статье
| # | Имя | Тип | Описание |
|---|---|---|---|
| 1 | Study.mq5 | Советник | Советник офлайн обучения моделей |
| 2 | StudyOnline.mq5 | Советник | Советник онлайн обучения моделей |
| 3 | Test.mq5 | Советник | Советник для тестирования модели |
| 4 | Trajectory.mqh | Библиотека класса | Структура описания состояния системы и архитектуры моделей |
| 5 | NeuroNet.mqh | Библиотека класса | Библиотека классов для создания нейронной сети |
| 6 | NeuroNet.cl | Библиотека | Библиотека кода OpenCL-программы |
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.
Знакомство с языком MQL5 (Часть 21): Автоматическое обнаружение паттернов Гартли
Таблицы в парадигме MVC на MQL5: Таблица корреляции символов
Оптимизатор на основе экологического цикла — Ecological Cycle Optimizer (ECO)
Нейросети в трейдинге: Сеточная аппроксимация событийного потока как инструмент анализа ценовых паттернов (ADM-модуль)
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования