Сеточный советник на клеточном автомате с онлайн-обучением в MQL5 (Часть III): Живой граф признаков
Обучаемый граф признаков: когда связи между рыночными факторами становятся живыми
В предыдущей части этой серии мы перевели архитектуру Cellular10K из классической сетки в графовую форму. Признаки перестали быть просто массивом feat[50], а клетки клеточного автомата получили не только геометрические соседства, но и логические связи по признакам, стратегиям и дальним small-world-хордам. Это был важный шаг: модель начала мыслить не плоской решёткой, а сетью взаимодействующих агентов.
Но у фиксированного графа остаётся фундаментальное ограничение. Если мы заранее прописали, что momentum связан с trend strength, RSI — с Bollinger, а volatility — с ATR, то мы снова внесли в систему жёсткую гипотезу. Да, она умнее обычной сетки, но всё ещё статична. Рынок же меняется: сегодня объём подтверждает пробой, завтра тот же объём оказывается ловушкой; сегодня RSI работает как разворотный индикатор, завтра он просто показывает силу тренда.
По этой причине третий шаг архитектуры — сделать граф обучаемым, а не фиксированным. Теперь связи между признаками усиливаются, если совместно приводят к прибыльному прогнозу, и ослабляются, если их совместное влияние ведёт к ошибке. В результате система получает не только 10 000 адаптивных клеток, но и живую структуру признакового пространства.
Главная идея: не веса нейросети, а связи между причинами
Классическая нейросеть обучает веса между нейронами. В Cellular10K третьей версии обучаются не только клетки, но и связи между рыночными признаками. Это принципиально другой уровень адаптации.
Признак сам по себе редко имеет устойчивый смысл. Momentum без волатильности может быть шумом. RSI без положения цены в канале может быть ложным сигналом. Объём без контекста тренда может означать как накопление, так и распределение. Поэтому в реальной торговле важны не отдельные признаки, а их рабочие комбинации.
Обучаемый граф признаков отвечает на вопрос: "Какие признаки в текущем рыночном режиме действительно усиливают друг друга, а какие только добавляют шум?"
Для этого вводится матрица связей:
double m_fgraph[CNN_FEATURES * CNN_FEATURES]; // 50 × 50 обучаемых связей feature → feature
Если m_fgraph[0][5] растёт, значит краткосрочный momentum и краткосрочная сила тренда часто совместно давали правильный прогноз. Если m_fgraph[16][22] падает, значит связка RSI14 + Bollinger20 в текущем режиме перестала работать как надёжный разворотный паттерн.
Инициализация графа: мягкая структура вместо случайного хаоса
Обучаемый граф не должен стартовать полностью случайным. Если все связи равны нулю, модель потратит слишком много времени на первичную самоорганизацию. Если связи слишком сильные — она станет догматичной. Поэтому используется мягкая инициализация: локальные связи признаков получают небольшой положительный вес, дальние small-world-связи — ещё меньший.
//+------------------------------------------------------------------+ //| Инициализация обучаемого графа признаков | //+------------------------------------------------------------------+ void InitLearnableFeatureGraph() { for(int i = 0; i < CNN_FEATURES; i++) { for(int j = 0; j < CNN_FEATURES; j++) { double w = 0.0; //--- локальная близость признаков: momentum рядом с momentum, RSI рядом с RSI if(MathAbs(i - j) == 1) w = 0.08; //--- дальняя small-world-хорда: признак получает слабую связь с удалённой группой int chord = (i * 17 + 11) % CNN_FEATURES; if(j == chord) w = 0.04; //--- самосвязь запрещена, чтобы признак не усиливал сам себя бесконечно if(i == j) w = 0.0; m_fgraph[i * CNN_FEATURES + j] = w; } } }
Это напоминает биологическую нервную систему: при рождении связи уже есть, но их сила не окончательна. Дальше рынок сам показывает, какие связи полезны, а какие должны атрофироваться.
Графовый слой признаков
После вычисления базовых 50 признаков применяется графовый слой. Он не заменяет признаки, а пропускает через них информацию соседних признаков с учётом текущей силы связей.
//+------------------------------------------------------------------+ //| Графовый слой признаков (один шаг message passing) | //+------------------------------------------------------------------+ void ApplyLearnableFeatureGraph(double &feat[]) { double src[CNN_FEATURES]; double dst[CNN_FEATURES]; for(int i = 0; i < CNN_FEATURES; i++) { src[i] = feat[i]; dst[i] = feat[i] * 0.72; // собственный голос признака остаётся главным } for(int i = 0; i < CNN_FEATURES; i++) { double acc = 0.0; double norm = 0.0; for(int j = 0; j < CNN_FEATURES; j++) { if(i == j) continue; double w = m_fgraph[i * CNN_FEATURES + j]; if(MathAbs(w) < 0.0001) continue; acc += src[j] * w; norm += MathAbs(w); } if(norm > 0.0001) dst[i] += 0.28 * acc / norm; } for(int i = 0; i < CNN_FEATURES; i++) feat[i] = MathTanh(dst[i]); }
Формально это один шаг message passing по графу признаков. Каждый признак получает сообщение от других признаков, но сила сообщения определяется не вручную, а обучаемой матрицей m_fgraph.
| Элемент | Роль | Зачем нужен |
|---|---|---|
| src[i] | Исходное значение признака | Сохраняет чистый рыночный сигнал до графовой обработки |
| m_fgraph[i][j] | Сила связи признаков | Показывает, насколько признак j влияет на признак i |
| acc / norm | Нормированное сообщение соседей | Защищает от взрыва масштаба при росте числа связей |
| MathTanh() | Ограничение диапазона | Возвращает значения в диапазон [-1,+1] |
Как граф обучается после прогноза
Главное правило простое: если два признака были активны в одном направлении и итоговый прогноз оказался правильным — связь между ними усиливается. Если они совместно поддерживали ошибочный прогноз — связь ослабляется.
Для этого перед прогнозом сохраняется снимок признаков:
double m_last_feat[CNN_FEATURES]; double m_last_vote; bool m_has_last_graph_sample;
После появления фактического движения цены вызывается обучение графа:
//+------------------------------------------------------------------+ //| Обучение графа признаков по факту движения цены | //+------------------------------------------------------------------+ void TrainFeatureGraph(double actual_move) { if(!m_has_last_graph_sample) return; double pred_sign = (m_last_vote > 0.0) ? 1.0 : -1.0; double real_sign = (actual_move > 0.0) ? 1.0 : -1.0; double reward = pred_sign * real_sign; // +1 правильно, -1 ошибка double lr = 0.006; // скорость обучения графа for(int i = 0; i < CNN_FEATURES; i++) { for(int j = 0; j < CNN_FEATURES; j++) { if(i == j) continue; double coact = m_last_feat[i] * m_last_feat[j]; double delta = lr * reward * coact; m_fgraph[i * CNN_FEATURES + j] += delta; //--- ограничение силы связи if(m_fgraph[i * CNN_FEATURES + j] > 1.0) m_fgraph[i * CNN_FEATURES + j] = 1.0; if(m_fgraph[i * CNN_FEATURES + j] < -1.0) m_fgraph[i * CNN_FEATURES + j] = -1.0; } } }
Это не градиентный спуск в классическом смысле. Здесь нет обратного распространения ошибки (backpropagation), матриц из внешних библиотек и оптимизатора Adam. Это локальное правило усиления/ослабления связей, похожее на рыночную версию правила Хебба (Hebbian learning):
Признаки, которые вместе правильно предсказывают рынок, связываются сильнее. Признаки, которые вместе ошибаются, разъединяются.
Почему связь может стать отрицательной
Важно, что граф не только усиливает положительные связи. Он может выучить отрицательную связь. Это означает, что когда один признак активен, второй должен интерпретироваться наоборот.
Например, система может обнаружить:
- RSI в зоне перекупленности обычно разворотный, но при сильном trend strength он становится признаком продолжения;
- высокий объём при низкой волатильности может быть накоплением, а при высокой волатильности — паникой;
- momentum без подтверждения диапазоном цены часто даёт ложный пробой.
Отрицательная связь — это не ошибка. Это способ сказать: этот признак в присутствии другого признака нужно читать противоположно.
Защита от переобучения графа
Если граф лишь усиливать после каждой удачи, он быстро станет агрессивным и начнёт переобучаться на последние 20–30 баров. Поэтому в систему добавлены три стабилизатора.
| Стабилизатор | Реализация | Эффект |
|---|---|---|
| Ограничение веса | w ∈ [-1,+1] | Связь не может стать бесконечно сильной |
| Медленное обучение | lr = 0.006 | Граф меняется плавно, а не прыгает от бара к бару |
| Нормировка сообщений | acc / norm | Сильносвязанные узлы не взрывают масштаб признаков |
Дополнительно можно вводить забывание старых связей:
//+------------------------------------------------------------------+ //| Забывание старых связей графа признаков | //+------------------------------------------------------------------+ void DecayFeatureGraph() { for(int i = 0; i < CNN_FEATURES * CNN_FEATURES; i++) m_fgraph[i] *= 0.9995; }
Такой decay полезен на инструментах, где рыночные режимы часто меняются. Старые связи не удаляются мгновенно, но постепенно теряют влияние, если рынок больше их не подтверждает.
Интеграция с клеточными автоматами
Обучаемый граф признаков не заменяет клеточный автомат. Он стоит перед ним и подаёт в CA уже контекстно-обогащённые признаки.
//+------------------------------------------------------------------+ //| Прогноз следующего движения через граф и клетки CA | //+------------------------------------------------------------------+ double PredictNextMove(string sym) { double feat[CNN_FEATURES]; if(!ExtractFeatures(sym, feat)) return 0.0; //--- новый слой: признаки проходят через обучаемый граф ApplyLearnableFeatureGraph(feat); //--- сохраняем снимок для будущего обучения графа for(int i = 0; i < CNN_FEATURES; i++) m_last_feat[i] = feat[i]; //--- дальше работает обычная эволюция 10 000 клеток for(int t = 0; t < CNN_CA_TICKS; t++) StepCA(feat); double vote = WeightedVote(); m_last_vote = vote; m_has_last_graph_sample = true; return vote; }
Теперь каждая клетка получает не исходные признаки, а признаки после графового согласования. Например, клетка, работающая с feat[0], фактически получает краткосрочный momentum уже с учётом того, как он сейчас связан с трендом, волатильностью, объёмом, диапазоном и временем суток.
Обучаемый граф внутри BPC
Бинарный предиктор тоже получает обучаемый граф. Это важно, потому что основной CA и BPC смотрят на рынок с разных горизонтов. CA оценивает текущий импульс, BPC проверяет направление через 10 баров. Поэтому их графы не обязаны совпадать.
| Модуль | Горизонт | Что обучает граф |
|---|---|---|
| CCellularNeuralNet10K | Текущий бар / ближайшее движение | Связи признаков, полезные для немедленного направления |
| CBinaryPredictor10K | 10 баров | Связи признаков, полезные для среднесрочного направления |
Это даёт интересный эффект: один и тот же признак может быть полезен в CA и вреден в BPC. Например, резкий импульс может быть хорошим сигналом на ближайший бар, но плохим сигналом на 10 баров, если после импульса часто происходит откат. Раздельные графы позволяют системе не смешивать эти режимы.
Диагностика: как понять, что граф действительно учится
Для практической торговли важно не только обучать граф, но и видеть его состояние. Минимальный набор диагностик:
| Метрика | Норма | Опасный сигнал | Интерпретация |
|---|---|---|---|
| Graph Density | 15–45% | >70% | Слишком много сильных связей — граф переобучается |
| Average |W| | 0.05–0.35 | >0.6 | Связи стали слишком жёсткими |
| Positive / Negative edges | 40/60–60/40 | 90/10 | Граф потерял контраст и начал всё усиливать |
| Top edges stability | Смена лидеров при смене режима | Один и тот же топ месяцами | Граф перестал адаптироваться |
Пример функции вывода сильнейших связей:
//+------------------------------------------------------------------+ //| Диагностика: вывод сильнейших связей графа признаков | //+------------------------------------------------------------------+ void PrintTopFeatureEdges(int topN = 10) { for(int k = 0; k < topN; k++) { double best = 0.0; int bi = -1; int bj = -1; for(int i = 0; i < CNN_FEATURES; i++) { for(int j = 0; j < CNN_FEATURES; j++) { if(i == j) continue; double w = MathAbs(m_fgraph[i * CNN_FEATURES + j]); if(w > best) { best = w; bi = i; bj = j; } } } if(bi >= 0) Print("GraphEdge #", k + 1, " F", bi, " -> F", bj, " W=", DoubleToString(m_fgraph[bi * CNN_FEATURES + bj], 4)); } }
На трендовом рынке в топе обычно появляются связи между momentum, trend strength, volatility и price level. На возвратном рынке усиливаются RSI, Bollinger, mean reversion и Hurst. Это можно использовать как дополнительную диагностику текущего режима.
Пример интерпретации обученного графа
Допустим, после 500 прогнозов система выводит такие сильные связи:
| Связь | Вес | Интерпретация |
|---|---|---|
| feat[0] → feat[5] | +0.72 | Краткосрочный momentum подтверждается краткосрочной силой тренда |
| feat[16] → feat[22] | −0.48 | RSI14 и Bollinger20 в этом режиме конфликтуют; разворотный сигнал ненадёжен |
| feat[24] → feat[2] | +0.51 | Объём подтверждает среднесрочный импульс |
| feat[36] → feat[7] | +0.63 | Hurst подтверждает трендовый режим |
Это уже не просто прогноз. Это объяснимая карта того, какие рыночные факторы сейчас действительно работают вместе.
Почему это сильнее обычной feature engineering
В классическом подходе разработчик вручную создаёт кросс-признаки:
feat[45] = momentum * volume; feat[46] = trend / volatility; feat[47] = rsi * bollinger;
Проблема в том, что такие признаки навсегда фиксируют предположение автора. Обучаемый граф делает то же самое, но динамически и в масштабе всех 50 признаков. Вместо трёх ручных кросс-признаков система получает до 2450 направленных связей.
| Подход | Количество связей | Адаптация | Недостаток |
|---|---|---|---|
| Ручные кросс-признаки | 3–10 | Нет | Зависит от гипотез разработчика |
| Фиксированный граф | 50–150 | Нет | Топология не меняется |
| Обучаемый граф | До 2450 | Да, онлайн | Нужен контроль переобучения |
Именно поэтому обучаемый граф — это не косметическое улучшение. Это переход от ручной инженерии признаков к самоорганизующейся рыночной топологии.
Практические пороги для запуска
После внедрения обучаемого графа первые 200–300 прогнозов следует рассматривать как фазу самоорганизации. В этот период не стоит делать выводы по каждой отдельной сделке. Важнее смотреть на динамику метрик.
- 0–100 прогнозов: граф только формирует первичные связи; возможна высокая нестабильность.
- 100–300 прогнозов: появляются устойчивые топ-связи; Network Health должен начать стабилизироваться.
- 300+ прогнозов: можно оценивать Hit Rate, BPC Precision и плотность графа.
Практическая рекомендация: если после 300 прогнозов Graph Density превышает 70% и Average |W| выше 0.6, а Hit Rate не растёт, граф переобучается. Нужно уменьшить learning rate или усилить decay.
//--- более консервативный режим обучения графа GRAPH_LR = 0.003; GRAPH_DECAY = 0.9990;
Если граф слишком слабый и почти не меняется, можно поднять скорость обучения:
//--- более агрессивный режим для быстрой адаптации GRAPH_LR = 0.009; GRAPH_DECAY = 0.9997;
Что получилось в архитектуре третьей версии
Теперь система состоит из трёх адаптивных уровней:
| Уровень | Что адаптируется | Как обновляется |
|---|---|---|
| Обучаемый граф признаков | Связи feature → feature | Усиливаются после правильных прогнозов, ослабляются после ошибок |
| Граф клеточных автоматов | Состояния 10 000 клеток и их стратегии | R1–R12, PnL, соседский консенсус, смена стратегии |
| Геномы советника | Риск, шаг сетки, множитель лота, уровни | По результатам торговых циклов |
Это уже не нейросеть в привычном смысле. Это живая вычислительная среда: признаки образуют обучаемый граф, клетки образуют адаптивную популяцию агентов, а торговый модуль мутирует параметры под текущий рынок.
Скелет полной логики
//+------------------------------------------------------------------+ //| Обработка нового бара: проверка, обучение, торговля | //+------------------------------------------------------------------+ void HandleNewBar() { //--- 1. Проверяем прошлый прогноз и получаем фактическое движение double actual_move = VerifyPrediction(); //--- 2. Обучаем клетки по факту движения g_ca.UpdateStats(TimeHour(TimeCurrent()), actual_move); //--- 3. Обучаем граф признаков g_ca.TrainFeatureGraph(actual_move); //--- 4. BPC проверяет прогноз 10 баров назад и обучает свой граф g_bpc.OnNewBar(_Symbol); //--- 5. Извлекаем новые признаки и пропускаем их через обучаемый граф double vote = g_ca.PredictNextMove(_Symbol); //--- 6. Комбинируем CA + BPC double effConf = CombineSignals(vote, g_bpc); //--- 7. Торговое решение if(effConf >= g_cur.min_conf && g_direction == 0) { int dir = (vote > 0) ? 1 : -1; OpenPosition(dir, 0); } }
Рассмотрим бэктест советника с проверкой на новый бар на ценах открытия, за период 2026.05.01 - 2026.06.01, на EURUSD H1 :

Итоговая статистика:

В первой части была решена проблема фиксированных весов: 10 000 клеток стали живыми агентами, которые обновляются на каждом баре. Во второй части была решена проблема плоской топологии: признаки и клетки были переведены в графовую структуру. В третьей части решается ещё более глубокая проблема — фиксированная структура связей.
Теперь граф признаков обучается сам. Он усиливает комбинации, которые дают прибыльный прогноз, и ослабляет комбинации, которые ведут к ошибкам. Это превращает систему из набора индикаторов и правил в самоорганизующуюся карту рыночных причинно-следственных связей.
Практический смысл прост: трейдер больше не обязан заранее знать, какая комбинация признаков будет работать завтра. Система сама проверяет эти комбинации на живом потоке данных и перестраивает внутренний граф без остановки торговли.
В результате Cellular10K становится трёхуровневой адаптивной архитектурой:
- обучаемый граф признаков понимает, какие рыночные факторы сейчас связаны;
- граф клеточных автоматов превращает эти связи в коллективное решение 10 000 агентов;
- торговый геном переводит решение в параметры сетки, риска и управления позицией.
Это уже не просто советник с нейросетью. Это рыночная экосистема, где признаки, агенты и торговые параметры обучаются совместно.
| Название файла | Описание файла |
|---|---|
| CellularNeuralNet10K.mqh | Основной модуль CA 10K с обучаемым графом признаков и графом клеточных автоматов |
| CellularBinaryPredictor10K.mqh | Бинарный предиктор с отдельным обучаемым графом признаков на горизонте 10 баров |
| CA_Grid_10K.mq5 | Пример советника, объединяющего CA, BPC и адаптивные торговые геномы |
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.
Особенности написания Пользовательских Индикаторов
Рыночные секреты Ларри Уильямса (Часть 11): Индикатор для обнаружения разворотов Smash Day
Моделирование рынка: Position View (V)
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования