Нейросети в трейдинге: Асинхронная обработка событий в потоковых моделях (Окончание)
Введение
Финансовый рынок остаётся одной из немногих динамических систем, которые по-прежнему пытаются описывать средствами, удобными человеку, но несоответствующими реальности. Мы привыкли мыслить барами, свечами, таймфреймами и окнами, выстраивая графики так, словно время на рынке течёт равномерно. Но рынок живёт иначе — он не знает минут и часов, он знает события. Иногда их много, иногда почти нет. Иногда рынок сжимается, словно пружина, а иногда разряжается за доли секунды. И каждый раз, когда мы насильно укладываем эту динамику в равные временные ячейки, мы теряем часть информации, даже не замечая этого.
Проблема заключается в самой точке отсчёта. Свеча — это компромисс, а не истина, бар — это агрегат, а не событие. Любая временная агрегация неизбежно сглаживает, задерживает и искажает структуру потока. Именно поэтому современные модели вынуждены усложняться: добавляются фильтры, сглаживания, каскады, ансамбли. Всё это попытки компенсировать потерю информации, которая произошла в самом начале. Авторы фреймворка EVA-Flow предлагают отказаться от этого компромисса и вернуться к естественной форме данных — потоку событий.
Ключевая идея EVA-Flow проста и потому особенно ценна. Модель не должна ждать, пока накопится очередной кусок истории, она должна реагировать сразу, как только произошло событие. Любое изменение цены, объёма или ликвидности — это сигнал к обновлению внутреннего состояния. Такой подход в корне меняет логику анализа: модель перестаёт работать рывками, она начинает работать непрерывно. Именно поэтому архитектура EVA-Flow идеально ложится на финансовый рынок, хотя изначально была разработана для компьютерного зрения. В обоих случаях мы имеем дело с асинхронным, неравномерным и потенциально бесконечным потоком данных, где задержка зачастую важнее абсолютной точности.
В основе фреймворка лежит представление данных в виде событийного пространства. Каждое событие имеет своё место и вес, а не просто попадает в очередную временную корзину. Это принципиально иной способ хранения и обработки информации. В таком пространстве рынок перестаёт быть набором свечей и превращается в живую ткань движения, где важны плотность, направление и скорость потока. Именно это позволяет модели одинаково устойчиво работать как в спокойные периоды, так и в моменты резких импульсов, когда классические методы либо запаздывают, либо захлёбываются шумом.
Архитектура EVA-Flow построена как многоуровневая система последовательного уточнения. Модель сначала формирует грубую, быструю оценку текущего состояния рынка. Эта оценка может быть неточной, но она появляется мгновенно. Затем в работу вступают последующие уровни, которые уточняют, стабилизируют и согласуют эту оценку с более широким контекстом. В оригинальной работе этот механизм реализован через каскад модулей пространственно-временного уточнения движения, и именно этот принцип мы переносим в торговую среду. В результате модель одновременно видит рынок в текущем моменте и сохраняет целостное понимание происходящего на более длинных интервалах.
Здесь важно подчеркнуть один момент. EVA-Flow не пытается прогнозировать рынок в привычном смысле. Он формирует внутреннее состояние, которое постоянно адаптируется к потоку событий. Это состояние — динамическое поле, которое живёт вместе с рынком. Такой подход оказывается намного устойчивее в условиях смены режимов, чем любые модели, привязанные к фиксированным окнам данных. Рынок ускоряется — модель ускоряется вместе с ним. Рынок замедляется — модель естественным образом переходит в режим ожидания, не создавая ложных сигналов.
С вычислительной точки зрения EVA-Flow тоже выглядит неожиданно практичным. Архитектура не требует постоянного перерасчёта всей истории, не зависит от размера окна и не нуждается в жёсткой синхронизации по времени. Это делает её особенно привлекательной для реализации средствами MQL5, где важно сочетание скорости, контроля над ресурсами и стабильной работы в реальном времени. Мы не подгоняем рынок под терминал, мы подгоняем архитектуру под саму природу рынка и аккуратно вписываем её в существующую инфраструктуру.

В предыдущих работах мы последовательно и довольно скрупулёзно разбирали отдельные элементы EVA-Flow, словно собирали сложный механизм по деталям, проверяя каждую шестерню на плавность хода и точность посадки. Мы исследовали, как формируется событийный поток, как накапливается и нормализуется информация, как работает многоуровневое уточнение состояния, и каким образом архитектура остаётся устойчивой в условиях шума и резких изменений динамики. Каждый модуль рассматривался отдельно, в изоляции, чтобы понять его поведение, ограничения и реальную ценность для торговой системы. Этот этап был необходим, без него любая попытка собрать целостную модель превратилась бы в угадывание, а не в инженерную работу.
Теперь мы подходим к моменту, который всегда оказывается самым сложным и одновременно самым интересным. Перед нами стоит задача объединить все эти части в единую, живую систему.
Объект верхнего уровня
Фреймворк EVA-Flow изначально проектировался как непрерывная система, живущая внутри потока событий. Поэтому процесс объединения модулей — это не механическое соединение входов и выходов, а выстраивание единой траектории движения данных. Событие должно пройти через модель без остановок, без лишних буферов, без искусственных барьеров, которые возвращали бы нас к старой логике временных окон. Каждый модуль обязан не просто обработать данные, но и аккуратно встроить результат в текущее состояние системы, не разрушая его и не создавая внутренних противоречий.
Именно здесь в полной мере раскрывается архитектурная философия EVA-Flow. Первичные модули работают быстро и грубо, фиксируя сам факт изменения и его направление. Следующие уровни уточняют, сглаживают, стабилизируют и согласуют эти изменения с более длинным контекстом. При этом модель не пересчитывает историю и не пересобирает себя заново при каждом событии. Она обновляет состояние ровно там, где это необходимо, и ровно настолько, насколько требует ситуация. В результате поток остаётся непрерывным, а система — устойчивой даже в моменты резких рыночных всплесков.
Объединяя модули, мы фактически формируем внутреннюю механику модели — её ритм, её инерцию, её способность реагировать и ждать. Здесь важно всё: порядок вызовов, передача промежуточных состояний, распределение ответственности между уровнями и даже то, где именно принимаются решения. Небольшая ошибка на этом этапе способна превратить изящную архитектуру в шумный, дергающийся механизм. Но аккуратная интеграция, напротив, создаёт ощущение цельности, когда модель начинает вести себя как единое целое, а не как набор независимых функций.
В контексте торговых систем этот момент особенно критичен. Рынок не прощает задержек, рассинхронизаций и внутренних конфликтов в логике обработки данных. Поэтому задача объединения модулей — это не просто следующий шаг разработки, а точка, где теория окончательно встречается с практикой. Именно здесь становится понятно, способен ли EVA-Flow работать в реальном времени, выдерживать поток событий, сохранять устойчивость и при этом оставаться достаточно гибким, чтобы адаптироваться к смене режима рынка.
Мы делаем шаг, который логически завершает предыдущие этапы разработки. Все модули, которые до этого рассматривались по отдельности, теперь собираются в единый объект верхнего уровня. Именно здесь EVA-Flow перестаёт быть набором архитектурных идей и превращается в цельную, управляемую систему, способную жить внутри реального потока событий. С инженерной точки зрения это момент истины. Если архитектура действительно устойчива, она выдержит интеграцию, если нет — ошибки проявятся мгновенно.
Центральным элементом становится класс верхнего уровня CNeuronEVAFlow, который наследует базовую логику пространственно-временного уточнения от CNeuronSpikeSMR. Это принципиальный момент — мы не строим новый объект поверх старых модулей, а встраиваем EVA-Flow в уже сформированную и проверенную иерархию потоковых нейронов. Таким образом сохраняется единая логика распространения данных, градиентов и состояний, что критически важно для устойчивой работы модели в режиме реального времени.
class CNeuronEVAFlow : public CNeuronSpikeSMR { protected: CLayer cPrepare; CLayer cEncoder; CNeuronInitialFlow cInitFlow; CLayer cDecoder; //--- virtual bool feedForward(CNeuronBaseOCL *NeuronOCL) override; virtual bool feedForward(CNeuronBaseOCL *NeuronOCL, CBufferFloat *SecondInput) override { return feedForward(NeuronOCL); } virtual bool updateInputWeights(CNeuronBaseOCL *NeuronOCL) override; virtual bool updateInputWeights(CNeuronBaseOCL *NeuronOCL, CBufferFloat *second) override { return updateInputWeights(NeuronOCL); } virtual bool calcInputGradients(CNeuronBaseOCL *NeuronOCL) override; virtual bool calcInputGradients(CNeuronBaseOCL *NeuronOCL, CBufferFloat *SecondInput, CBufferFloat *SecondGradient, ENUM_ACTIVATION SecondActivation = None) override { return calcInputGradients(NeuronOCL); } public: CNeuronEVAFlow(void) {}; ~CNeuronEVAFlow(void) {}; //--- virtual bool Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl, uint forecast, uint dimension, uint hidden_dimension, uint layers, uint candidates, ENUM_OPTIMIZATION optimization_type, uint batch); //--- virtual int Type(void) override const { return defNeuronEVAFlow; } //--- methods for working with files virtual bool Save(int const file_handle) override; virtual bool Load(int const file_handle) override; //--- virtual void SetOpenCL(COpenCLMy *obj) override; //--- virtual bool WeightsUpdate(CNeuronBaseOCL *source, float tau) override; virtual void TrainMode(bool flag) override; virtual bool Clear(void) override; };
Структура класса отражает внутреннюю логику EVA-Flow почти буквально. Блок cPrepare отвечает за первичную подготовку событийного потока, приводя анализируемые данные к форме, удобной для дальнейшей обработки. Здесь решаются вопросы нормализации, выравнивания масштабов и устранения технического шума. Это своеобразный входной фильтр, который защищает архитектуру от хаотичных выбросов и резких скачков, неизбежных в реальном рынке.
Далее поток попадает в cEncoder — модуль, который формирует компактное и информативное представление текущего состояния. Именно здесь события превращаются в структурированное пространство признаков, пригодное для работы каскадных механизмов уточнения. В терминах архитектуры EVA-Flow это переход от сырого потока к внутреннему языку модели.
Объект CNeuronInitialFlow выполняет особую роль. Он формирует начальное приближение движения, то самое быстрое и грубое состояние, которое появляется мгновенно после события. Это фундаментальный элемент принципа anytime: модель обязана иметь рабочее состояние сразу, не дожидаясь накопления информации. В торговых системах это означает, что алгоритм не слепнет в начале движения и не запаздывает в критические моменты.
Завершает цепочку cDecoder, который переводит внутреннее состояние модели в выходное представление, пригодное для использования в стратегии. Здесь происходит согласование уровней, финальное сглаживание и формирование сигнала, который уже может быть интерпретирован как прогноз, оценка направления или мера уверенности. Важно, что декодер не ломает поток, а аккуратно завершает его, сохраняя непрерывность всей системы.
Внутренняя архитектура нашего объекта верхнего уровня формируется в момент инициализации, и именно здесь становится видна настоящая логика EVA-Flow как цельной системы, а не набора отдельных модулей. Метод инициализации выполняет роль архитектора. Он выстраивает поток обработки данных так, чтобы события могли проходить через модель непрерывно, без разрывов, возвратов и лишней синхронизации. Это та точка, где концепция поточного мышления окончательно превращается в инженерную реальность.
bool CNeuronEVAFlow::Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl, uint forecast, uint dimension, uint hidden_dimension, uint layers, uint candidates, ENUM_OPTIMIZATION optimization_type, uint batch) { if(!CNeuronSpikeSMR::Init(numOutputs, myIndex, open_cl, (forecast + 1) / 2, hidden_dimension, 2 * hidden_dimension, 2 * dimension, optimization_type, batch)) ReturnFalse;
Первым шагом инициализации управление передается родительскому классу. Это важный архитектурный жест. EVA-Flow сразу встраивается в уже существующую систему пространственно-временного уточнения, а не создаётся как отдельная, замкнутая модель. Таким образом мы гарантируем, что вся логика распространения состояний, градиентов и временных зависимостей будет подчиняться единым правилам. С этого момента EVA-Flow живёт в том же ритме, что и остальные потоковые нейроны модели, и не нарушает целостность общей архитектуры.
Далее начинается формирование входного тракта. Блок cPrepare собирается первым, и это не случайно. Он служит фильтром и адаптером между сырым событийным потоком и внутренним пространством модели. Здесь мы намеренно используем связку BatchNorm и SpikeUSR. Нормализация стабилизирует масштаб входных данных, устраняя резкие перепады и перекосы распределений.
//--- Prepare cPrepare.Clear(); cPrepare.SetOpenCL(OpenCL); CNeuronBatchNormOCL* norm = NULL; CNeuronSpikeUSR* usr = NULL; uint index = 0; norm = new CNeuronBatchNormOCL(); if(!norm || !norm.Init(0, index, OpenCL, dimension, iBatch, optimization) || !cPrepare.Add(norm)) DeleteObjAndFalse(norm); norm.SetActivationFunction(None);
А CNeuronSpikeUSR формирует поток и переводит его в форму, удобную для дальнейшей каскадной обработки. В результате модель не захлёбывается на входе и не тратит вычислительные ресурсы на борьбу с хаосом рынка.
index++; usr = new CNeuronSpikeUSR(); if(!usr || !usr.Init(0, index, OpenCL, forecast, dimension, hidden_dimension, 1, optimization, iBatch) || !cPrepare.Add(usr)) DeleteObjAndFalse(usr);
Следом формируются энкодер и декодер — два симметричных тракта, между которыми разворачивается основная работа архитектуры. Энкодер постепенно сжимает поток, увеличивая плотность представления и извлекая устойчивые признаки движения. Для этого используются блоки CNeuronSpikeSuperKernelBlock, которые работают с несколькими временными окнами и шагами одновременно. Такой подход позволяет модели видеть рынок сразу в нескольких масштабах, не теряя при этом непрерывности потока. Это особенно важно в трейдинге, где импульсы разных порядков могут накладываться друг на друга, создавая иллюзию шума там, где на самом деле есть структура.
//--- Encoder / Decoder cEncoder.Clear(); cDecoder.Clear(); cEncoder.SetOpenCL(OpenCL); cDecoder.SetOpenCL(OpenCL); CNeuronBaseOCL* neuron = NULL; CNeuronSpikeSuperKernelBlock* block = NULL; CNeuronSpikeSMR* smr = NULL; uint windows[] = { 3, 5}; uint steps_enc[] = {1, 2}; uint steps_dec[] = {1, 1}; uint units_in = forecast; uint units_out = MathMax((units_in + 1) / 2, 1); for(uint i = 0; i < layers; i++) { index++; block = new CNeuronSpikeSuperKernelBlock(); if(!block || !block.Init(0, index, OpenCL, hidden_dimension, hidden_dimension, windows, steps_enc, units_out, 1, optimization, iBatch) || !cEncoder.Add(block)) DeleteObjAndFalse(block);
По мере продвижения по слоям энкодера размер временного представления уменьшается, а информационная плотность растёт. Это классический приём, но в поточной архитектуре он приобретает новый смысл. Мы не просто уменьшаем размерность, мы постепенно переводим поток из событийной формы в форму устойчивого состояния. Каждое последующее сжатие делает модель менее чувствительной к случайным всплескам и более чувствительной к направлению движения.
На стороне декодера разворачивается обратный процесс. Здесь в работу вступают модули CNeuronSpikeSMR, которые выполняют пространственно-временное уточнение уже сформированного состояния. Если энкодер отвечает за понимание, то декодер — за согласование и стабилизацию.
if(i > 0) { index++; smr = new CNeuronSpikeSMR(); if(!smr || !smr.Init(0, index, OpenCL, units_out, hidden_dimension, (1 + int(i != (layers - 1))) * hidden_dimension, 2 * hidden_dimension, optimization, iBatch) || !cDecoder.Add(smr)) DeleteObjAndFalse(smr); }
В промежуточных слоях используются базовые нейроны без активации, что позволяет сохранить линейную передачу состояния там, где нелинейность только навредила бы, создавая лишние искажения. Это тонкий, но принципиальный момент — EVA-Flow не стремится быть максимально нелинейным, он стремится быть устойчивым.
if(i < (layers - 1)) { index++; neuron = new CNeuronBaseOCL(); if(!neuron || !neuron.Init(0, index, OpenCL, units_out * hidden_dimension * 2, optimization, iBatch) || !cDecoder.Add(neuron)) DeleteObjAndFalse(neuron); neuron.SetActivationFunction(None); } units_in = units_out; units_out = MathMax((units_in + 1) / 2, 1); }
Отдельного внимания заслуживает инициализация модуля cInitFlow, которая происходит в самом конце. Это сознательный выбор. Начальный поток формируется на основе уже построенной архитектуры, а не наоборот. Модуль получает контекст, размеры и структуру системы, в которую он встроен, и только после этого начинает работать.
index++; if(!cInitFlow.Init(0, index, OpenCL, hidden_dimension, units_in, hidden_dimension, candidates, 2 * forecast, optimization, iBatch)) ReturnFalse; //--- return true; }
Именно здесь реализуется принцип anytime в чистом виде. Модель получает возможность сформировать первичную оценку состояния мгновенно, сразу после первого события, не дожидаясь накопления истории. Для торговых алгоритмов это критично — система не простаивает, не разгоняется, а начинает работать сразу.
Если посмотреть на метод инициализации целиком, становится ясно, что он не просто создаёт набор объектов. Он задаёт траекторию движения данных, формирует ритм модели и определяет её инерцию. Каждый слой появляется в строго определённый момент и в строго определённом месте. В результате EVA-Flow начинает напоминать динамический механизм, где поток событий проходит через последовательность преобразований, постепенно превращаясь в устойчивое внутреннее состояние, пригодное для принятия решений.
Алгоритм прямого прохода — это сердце всей архитектуры EVA-Flow. Именно здесь абстрактная схема, собранная в методе инициализации, начинает работать как единый потоковый механизм. Если инициализация задаёт форму системы, то прямой проход задаёт её движение. Каждое событие, попадая на вход, проходит строго определённый путь, не перескакивая между слоями и не теряя контекст. В этом коде особенно хорошо видно, что EVA-Flow сопровождает поток, шаг за шагом уточняя внутреннее состояние модели.
bool CNeuronEVAFlow::feedForward(CNeuronBaseOCL *NeuronOCL) { CNeuronBaseOCL* prev = NeuronOCL; CNeuronBaseOCL* curr = NULL; //--- for(int i = 0; i < cPrepare.Total(); i++) { curr = cPrepare[i]; if(!curr || !curr.FeedForward(prev)) ReturnFalse; prev = curr; }
Первые строки задают направление движения потока. Переменная prev всегда указывает на анализируемое состояние, которое передаётся от модуля к модулю. Это простой приём, но именно он обеспечивает непрерывность. Поток не копируется, не пересобирается и не возвращается назад, он движется вперёд, меняя форму, но не теряя своей сути.
Сначала поток проходит через блок подготовки данных cPrepare. Здесь каждое событие стабилизируется, нормализуется и переводится в форму, пригодную для дальнейшей работы. Важно, что мы не делаем никаких разветвлений. Слой работает как фильтр, а не как отдельная логическая ветка. После него поток событий уже чистый, согласованный и готов к анализу. Это своего рода входной шлюз, который защищает внутреннюю архитектуру от хаотичных рыночных выбросов.
Затем поток попадает в энкодер. Здесь происходит постепенное сжатие и уплотнение информации.
for(int i = 0; i < cEncoder.Total(); i++) { curr = cEncoder[i]; if(!curr || !curr.FeedForward(prev)) ReturnFalse; prev = curr; }
Каждый элемент cEncoder принимает анализируемое состояние и формирует более устойчивое представление движения. Модель перестаёт реагировать на отдельные тики и начинает видеть структуру. Именно на этом этапе рыночный шум теряет свою власть, а направление и инерция движения начинают проявляться отчётливо.
Принципиально, что этот процесс идёт последовательно, без параллельных веток. Поток сохраняет линейность, а значит — предсказуемость и устойчивость.
После энкодера управление передаётся модулю инициализации потока cInitFlow. Здесь модель формирует первичную оценку движения — быструю, грубую, но мгновенную.
if(!cInitFlow.FeedForward(prev))
ReturnFalse;
Это центральная реализация принципа anytime. Система не ждёт завершения всех вычислений. Она уже знает, куда смотрит рынок, и может начать действовать. Для алгоритмического трейдинга это критический момент. Решение появляется вовремя.
Далее начинается обратное движение через декодер. И здесь архитектура раскрывается особенно красиво. Мы идём от последнего слоя к первому, шаг за шагом уточняя состояние.
CNeuronBaseOCL* flow = cInitFlow.AsObject(); for(int i = cDecoder.Total() - 1; i >= 0; i -= 2) { curr = cDecoder[i]; if(!curr || !curr.FeedForward(flow, prev.getOutput())) ReturnFalse; flow = curr; if(i <= 0) continue; prev = cDecoder[i - 1]; if(!prev) ReturnFalse; CNeuronSpikeSMR* smr = flow; uint dimension = smr.GetWindow() / 2; uint units = prev.Neurons() / (2 * dimension); if(!cEncoder[i / 2] || !Concat(smr.GetHidden(), cEncoder[i / 2].getOutput(), prev.getOutput(), dimension, dimension, units)) ReturnFalse; }
Каждый модуль декодера получает текущее состояние потока flow и контекст из энкодера. Именно здесь происходит слияние быстрой оценки и устойчивого представления. Функция Concat аккуратно объединяет скрытые состояния модуля оценки потока более грубого уровня, сохраняя согласованность размерностей и смыслов. Это не просто техническая операция, а ключевой момент, где разные временные масштабы снова собираются в единое поле.
Особенность цикла в том, что декодер работает в связке с энкодером. Мы не теряем информацию, сжатую на этапе кодирования, а возвращаем её в уточнённом виде. Таким образом, модель не забывает, откуда пришло движение, и не реагирует на случайные флуктуации, не имеющие продолжения. Это и есть та самая устойчивость, которой так не хватает классическим моделям на основе окон.
Финальным шагом становится вызов одноименного метода родительского класса.
if(!CNeuronSpikeSMR::feedForward(flow, prev.getOutput())) ReturnFalse; //--- return true; }
Здесь поток окончательно согласуется и приводится к форме, пригодной для использования в стратегии. Важно, что этот вызов завершает процесс, а не запускает новый. Модель не пересчитывает себя заново, она просто доводит текущее состояние до логического завершения.
В итоге прямой проход EVA-Flow — это непрерывный маршрут события через архитектуру. Каждое событие меняет состояние модели, а модель, в свою очередь, аккуратно и без суеты меняет своё представление о рынке. В этом и заключается ключевое отличие EVA-Flow от классических моделей: здесь нет кадров, нет окон, нет пауз — есть только поток и система, которая умеет жить внутри него.
Однако прежде чем модель сможет дать осмысленный и устойчивый результат, её необходимо обучить. И здесь EVA-Flow снова ведёт себя как потоковая система, где ошибка прогноза — это сигнал, который должен быть аккуратно распределён по всей траектории движения события. Именно организация этого процесса определяет, будет ли модель учиться понимать рынок или просто подстраиваться под шум. В потоковых архитектурах ошибка распространяется вдоль пути, которым шёл поток, восстанавливая причинно-следственные связи между событием и результатом.
Алгоритм распределения градиента ошибки реализован в методе calcInputGradients, и по своей логике он зеркален прямому проходу. Однако не лишен важных архитектурных нюансов. Если прямой проход собирает состояние, то обратный — аккуратно его разбирает, возвращая информацию об ошибке каждому участнику процесса именно в том виде, в котором он может её использовать.
bool CNeuronEVAFlow::calcInputGradients(CNeuronBaseOCL *NeuronOCL) { if(!NeuronOCL) ReturnFalse; //--- CNeuronBaseOCL* prev = (cDecoder.Total() >= 1 ? cDecoder[0] : cEncoder[-1]); CNeuronBaseOCL* flow = (cDecoder.Total() >= 2 ? cDecoder[1] : cInitFlow.AsObject()); CNeuronBaseOCL* curr = NULL; //--- if(!CNeuronSpikeSMR::calcInputGradients(flow, prev.getOutput(), prev.getGradient(), (ENUM_ACTIVATION) prev.Activation())) ReturnFalse;
Первым шагом обратного прохода становится распространение градиента через завершающий модуль пространственно-временного уточнения. Это принципиально важно: мы не начинаем с энкодера, не прыгаем сразу к входу, а строго следуем тому же маршруту, по которому шёл поток данных. Модель буквально идёт обратно по своим следам, восстанавливая влияние каждого преобразования на итоговый результат. Такой подход позволяет избежать перекосов и локальных искажений, которые часто возникают в глубоких архитектурах.
Далее градиенты проходят через декодер. Здесь используется тот же шаговый ритм, что и в прямом проходе. Каждый второй слой, каждая пара модулей обрабатывается согласованно.
for(int i = 1; i < cDecoder.Total(); i += 2) { curr = cDecoder[i]; prev = ((i + 1) < cDecoder.Total() ? cDecoder[i + 1] : cEncoder[-1]); flow = ((i + 2) < cDecoder.Total() ? cDecoder[i + 2] : cInitFlow.AsObject()); if(!curr || !prev || !curr.CalcHiddenGradients(flow, prev.getOutput(), prev.getGradient(), (ENUM_ACTIVATION) prev.Activation())) ReturnFalse; }
Метод CalcHiddenGradients возвращает ошибку в скрытые состояния, а контекст из энкодера обеспечивает правильную ориентацию градиента во временном и пространственном смысле. Это ключевой момент — ошибка не просто уменьшается, она уточняется, так же как и само состояние на прямом проходе. Архитектура остаётся симметричной, а обучение — устойчивым.
После прохождения декодера происходит один из самых тонких этапов — согласование градиента между инициализирующим потоком и энкодером.
CBufferFloat* temp = prev.getGradient(); if(!prev.SetGradient(prev.getPrevOutput(), false) || !prev.CalcHiddenGradients(cInitFlow.AsObject()) || !SumAndNormilize(temp, prev.getGradient(), temp, 1, false, 0, 0, 0, 1) || !prev.SetGradient(temp, false)) ReturnFalse; curr = prev;
Здесь мы учитываем получение градиента ошибки на каждый слой энкодера как по магистрали потока, так и по магистрали контекста, передаваемого в декодер. Поэтому используется суммирование данных от двух информационных потоков. Это не даёт ошибке перетянуть модель в сторону случайных импульсов и сохраняет баланс между быстрыми и устойчивыми компонентами. В терминах трейдинга это означает, что модель не переобучается на единичные всплески и не игнорирует длительные движения.
Затем начинается обратное движение через энкодер.
for(int i = cEncoder.Total() - 2; i >= 0; i--) { prev = cEncoder[i]; if(!prev || !prev.CalcHiddenGradients(curr)) ReturnFalse; curr = cDecoder[i * 2]; uint dimension = ((CNeuronSpikeSuperKernelBlock*)prev).GetWindow(); uint units = ((CNeuronSpikeSuperKernelBlock*)prev).GetUnits(); if(!curr || !DeConcat(curr.getPrevOutput(), prev.getPrevOutput(), curr.getGradient(), dimension, dimension, units)) ReturnFalse; if(prev.Activation() != None) if(!DeActivation(prev.getOutput(), prev.getPrevOutput(), prev.getPrevOutput(), prev.Activation())) ReturnFalse; if(!SumAndNormilize(prev.getGradient(), prev.getPrevOutput(), prev.getGradient(), 1, false, 0, 0, 0, 1)) ReturnFalse; curr = prev; }
Это зеркальный процесс по отношению к прямому проходу. Каждое скрытое состояние получает ровно ту часть ошибки, за которую оно отвечало при формировании признаков. Если слой использовал нелинейную активацию, её производная аккуратно учитывается, чтобы градиент сохранял физический смысл и не искажался.
После энкодера ошибка возвращается в блок подготовки данных. Здесь она уже сильно сглажена, очищена от локального шума и приведена к форме, пригодной для корректировки входных весов.
for(int i = cPrepare.Total() - 1; i >= 0; i--) { prev = cPrepare[i]; if(!prev || !prev.CalcHiddenGradients(curr)) ReturnFalse; curr = prev; }
Это важный момент. Входные слои обучаются на согласованной, стабилизированной информации о том, где модель действительно ошиблась. В результате обучение становится спокойным, предсказуемым и устойчивым даже при резких изменениях рыночного режима.
Финальный шаг — передача градиента на уровень объекта исходных данных, с которого всё началось.
if(!NeuronOCL.CalcHiddenGradients(curr)) ReturnFalse; //--- return true; }
На этом цикл замыкается. Событие прошло через модель вперёд, сформировало состояние, породило ошибку и вернулось обратно, оставив след в каждом модуле, который принимал участие в обработке. Именно такой замкнутый цикл делает EVA-Flow обучаемым в реальном времени, без необходимости пересчитывать историю или останавливать поток.
Таким образом, объект CNeuronEVAFlow становится полноценной точкой сборки всей архитектуры. Он задаёт ритм работы системы, определяет порядок движения данных и обеспечивает целостность состояния. Именно на этом уровне EVA-Flow окончательно превращается в инженерно-завершённый фреймворк, способный работать в реальном времени, адаптироваться к потоку событий и сохранять устойчивость.
Тестирование
Наш проект подошел к следующему этапу, на котором инженерная работа перестаёт быть абстрактным конструированием и впервые сталкивается с реальностью рынка. Теперь можно с уверенностью сказать: формирование собственного взгляда на идеи, заложенные авторами EVA-Flow, завершено. Архитектура больше не существует в виде схем и фрагментов кода, она обрела форму целостного механизма, реализованного средствами MQL5, связанного внутренней логикой и готового к практическому испытанию. Теперь перед нами стоит задача, которая всегда расставляет всё по местам: проверить, как этот механизм ведёт себя в условиях отсутствия учебных примеров, идеальных условий и права на ошибку.
Рынок — среда жёсткая, он не прощает неопределённости, не объясняет свои намерения и не делает скидок на красоту архитектурных решений. Именно поэтому следующий шаг неизбежен — выход из лаборатории в поле. И здесь особенно важно, что EVA-Flow органично встраивается в уже существующую систему. Мы не перестраиваем модель целиком, не ломаем отлаженные компоненты и не усложняем структуру ради усложнения. Новый объект верхнего уровня CNeuronEVAFlow заменяет лишь финальный слой энкодера анализа окружающей среды, аккуратно встраиваясь в цепочку обработки. Такой подход позволяет сохранить устойчивость всей системы и одновременно добавить ей принципиально новые возможности в работе с динамикой событий.
Как и в предыдущих проектах, мы остаёмся верны основной идее. Торговая модель должна уметь анализировать рынок самостоятельно, без жёстко зашитых правил и эвристик. Нас интересует не отдельный индикатор и не набор условий входа, а поведение системы в целом. Именно поэтому EVA-Flow здесь выступает как средство организации восприятия рыночной реальности во времени. Он связывает события, выстраивает причинно-следственные цепочки и формирует представление о том, как рынок переходит из одного состояния в другое.
Экспериментальная работа началась с офлайн-обучения на исторических данных EURUSD таймфрейма H1 за период с Января 2024 по Июнь 2025 года. Этот этап можно сравнить с обучением пилота в симуляторе. Условия стабильны, всё под контролем, ошибки не приводят к катастрофе. Но именно здесь закладываются базовые навыки. Модель шаг за шагом осваивала рынок, словно исследователь, движущийся по незнакомой местности. Она училась замечать повторяющиеся структуры, каждое событие становится локальным наблюдением, а их последовательность постепенно складывалась в связное представление о динамике движения цены.
Важно подчеркнуть. Модель училась выделять устойчивые закономерности и различать шум и сигнал. Где зарождается движение? Где оно теряет импульс? Какие сценарии повторяются и усиливаются, а какие исчезают, не оставляя следа? Эти вопросы решались через внутреннюю организацию потока состояний, где EVA-Flow выступал связующим элементом между прошлым, настоящим и будущим.
После этого последовал этап тонкой онлайн-настройки в тестере стратегий MetaTrader 5. Здесь условия изменились радикально. Данные перестали быть стерильными. Рынок стал шумным, капризным и противоречивым. Менялись режимы поведения, появлялись резкие скачки после новостей, возникали провалы ликвидности, и модель уже не могла полагаться на спокойную статистику. Она была вынуждена адаптироваться, корректировать локальные оценки, но при этом сохранять накопленный опыт. Именно на этом этапе проявляется сила архитектуры EVA-Flow. Система не теряет целостности, а плавно перестраивает внутренние представления, оставаясь устойчивой даже при резких изменениях внешней среды.
Финальная проверка проводилась на полностью новых данных за период с Июля по Декабрь 2025 года. Здесь мы сознательно отказались от любой дополнительной подстройки параметров. Модель работала в том виде, в каком вышла из предыдущих этапов обучения. Это принципиальный момент. В этих условиях проверяется не способность подгонять модель под рынок, а её умение обобщать опыт, переносить знания и сохранять устойчивость вне обучающей выборки. Перед ней возникали незнакомые сценарии, новые комбинации событий, непривычные фазы рынка. И именно здесь становится видно, является ли архитектура по-настоящему рабочей, или она лишь хорошо приспособилась к прошлому.


Согласно результатам тестирования за полгода модель заработала более 44% чистой прибыли, совершив всего 22 сделки. Система работает редко, но осмысленно. Каждый вход — результат накопленного контекста, а не реакция на случайное движение.
Кривая капитала в целом имеет выраженный восходящий характер, но развивается ступенчато. Долгие периоды ожидания сменяются короткими фазами активной работы, после которых баланс фиксирует новый уровень. Такая форма характерна для систем, ориентированных на события и импульсы, а не на постоянное присутствие в рынке. Линия Equity при этом заметно колеблется вокруг баланса, что говорит о наличии плавающих просадок. Однако эти колебания остаются контролируемыми и не переходят в разрушительные провалы. Важный момент — после резких подъёмов практически всегда следует откат и фиксация прибыли. Это признак рабочей логики выхода, а не агрессивного удержания позиций в надежде на продолжение движения.
Просадки заслуживают отдельного внимания. Максимальная относительная просадка составила около 19% от депозита, что для начального капитала в 100.0USD выглядит ощутимо, но не критично. Модель способна восстанавливаться после неблагоприятных серий, хотя делает это не мгновенно. Recovery Factor находится на уровне около 1.3 — это говорит о том, что система в целом компенсирует потери, но запас устойчивости пока не избыточен. Иными словами, перед нами рабочий механизм, но для уверенного промышленного применения ему потребуется дополнительная закалка.
Если посмотреть на распределение сделок, становится видно, что система выигрывает чаще, чем проигрывает. Около 64% сделок завершились прибылью, что создаёт комфортный психологический профиль. При этом средний убыток превышает среднюю прибыль — классическая ситуация для стратегий, которые работают по импульсу и выходят из рынка с задержкой. Компенсация достигается не размером выигрыша, а частотой правильных решений. Это вполне осознанный компромисс, заложенный в архитектуру.
И всё же стоит быть честными — выборка пока невелика. Двадцать две сделки — это лишь первый штрих, а не окончательный портрет. Доверительные интервалы метрик широки, а некоторые показатели могут быть переоценены. Поэтому текущий результат следует рассматривать как подтверждение жизнеспособности идеи. Архитектура EVA-Flow ведёт себя именно так, как от неё ожидалось. Она аккумулирует опыт, избегает хаотичной торговли, устойчиво переживает неблагоприятные фазы и сохраняет положительное математическое ожидание вне обучающей выборки.
Заключение
Подводя итоги, можно констатировать, что проведённая работа по интеграции и практической реализации фреймворка EVA-Flow средствами MQL5 позволила превратить архитектурные идеи в полноценный инструмент для анализа и прогнозирования рыночной динамики. Новый объект CNeuronEVAFlow показал, что интеграция инновационных подходов возможна без разрушения проверенной структуры модели. Стабильность и логика работы системы сохранены, а возможности адаптации и обучения существенно расширены.
Тестирование модели на исторических и независимых данных продемонстрировало её способность аккумулировать рыночный опыт, выявлять повторяющиеся паттерны и строить устойчивое представление о динамике цен. Система проявила контролируемую просадку, положительное математическое ожидание, адекватное распределение выигрышей и проигрышей. Эти результаты подтверждают, что EVA-Flow функционирует как самостоятельный аналитический механизм, способный корректно интерпретировать изменения рыночного режима и принимать решения без жёстко заданных эвристик.
Вместе с тем, тест выявил ограничения текущей реализации: малое число сделок и умеренный Recovery Factor требуют расширенного тестирования и оптимизации параметров выхода.
Ссылки
Программы, используемые в статье
| # | Имя | Тип | Описание |
|---|---|---|---|
| 1 | Study.mq5 | Советник | Советник офлайн обучения моделей |
| 2 | StudyOnline.mq5 | Советник | Советник онлайн обучения моделей |
| 3 | Test.mq5 | Советник | Советник для тестирования модели |
| 4 | Trajectory.mqh | Библиотека класса | Структура описания состояния системы и архитектуры моделей |
| 5 | NeuroNet.mqh | Библиотека класса | Библиотека классов для создания нейронной сети |
| 6 | NeuroNet.cl | Библиотека | Библиотека кода OpenCL-программы |
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.
Особенности написания Пользовательских Индикаторов
Нейросети в трейдинге: Асинхронная обработка событий в потоковых моделях (Основные компоненты)
Алгоритм сверчков — Cricket Algorithm (CA)
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования