English 中文 Español Deutsch 日本語 Português
preview
Пример сетевого анализа причинно-следственных связей (CNA) и модели векторной авторегресси для прогнозирования рыночных событий

Пример сетевого анализа причинно-следственных связей (CNA) и модели векторной авторегресси для прогнозирования рыночных событий

MetaTrader 5Торговые системы |
565 4
Javier Santiago Gaston De Iriarte Cabrera
Javier Santiago Gaston De Iriarte Cabrera

Введение

Сетевой анализ причинно-следственных связей (CNA) - это метод, используемый для понимания и моделирования сложных причинно-следственных связей между переменными в системе. Применительно к финансовым рынкам это может помочь определить, каким образом различные рыночные события и факторы влияют друг на друга, что потенциально может привести к более точным прогнозам.

Открытие причинно-следственной связи: Выявление причинно-следственных связей - это процесс установления причинно-следственных связей на основе данных наблюдений. В контексте финансовых рынков это означает определение того, какие переменные (например, экономические показатели, рыночные цены или внешние события) оказывают причинно-следственное воздействие на другие. Существует несколько алгоритмов для выявления причинно-следственных связей, но одним из самых популярных является алгоритм PC (Питера-Кларка (Peter-Clark)).

Этот торговый бот не реализует явным образом систему под названием «Сетевой анализ причинно-следственных связей для прогнозирования рыночных событий" в качестве именованного компонента. Однако, этот бот включает в себя элементы причинно-следственного анализа и сетевых подходов, которые концептуально схожи. Давайте разберем этот случай:

Анализ причинно-следственных связей: Бот использует алгоритм обнаружения причинно-следственных связей, в частности алгоритм быстрого причинно-следственного вывода (FCI) (вместо алгоритма PC). Это реализовано в функции FCIAlgorithm().

Сетевой анализ: Бот использует сетевую структуру для представления взаимосвязей между различными финансовыми инструментами или индикаторами. Это видно из структуры узла и функции SetupNetwork().

Прогноз события: Хотя бот явным образом не называется «прогнозом событий", он использует модель векторной авторегрессии (VAR) для составления прогнозов о будущих состояниях рынка. Это реализовано в таких функциях, как TrainVARModel() и PredictVARValue().


Сетевой анализ причинно-следственных связей: Новый рубеж в прогнозировании рыночных событий

В мире алгоритмического трейдинга новый подход набирает популярность как среди профессионалов, так и среди трейдеров: Сетевой анализ причинно-следственных связей для прогнозирования рыночных событий. Этот сложный метод сочетает в себе мощь причинно-следственных выводов, теории сетей и прогностической аналитики для прогнозирования значимых рыночных событий с беспрецедентной точностью.

Представьте себе финансовый рынок как обширную, взаимосвязанную сеть. Каждая цепочка представляет собой взаимосвязь между различными рыночными переменными - ценами на акции, экономическими показателями, геополитическими событиями и многим другим. Традиционный анализ часто фокусируется на корреляциях, но, как известно любому опытному трейдеру, корреляция не всегда подразумевает причинно-следственную связь.

Именно здесь вступает в действие сетевой анализ причинно-следственных связей. Он направлен на то, чтобы раскрыть истинные причинно-следственные внутри этой сложной сети. Таким образом, он обеспечивает трейдерам более глубокое понимание динамики рынка, позволяя им предвидеть события, которые могут быть незаметны для обычного анализа.

По своей сути, сетевой анализ причинно-следственных связей включает в себя три ключевых этапа:

1. Построение сети: Сначала мы создаем сеть, в которой каждый узел представляет собой рыночную переменную. Это может быть что угодно - от цен на активы и торговых объемов до экономических показателей и оценок настроений.

2. Открытие причинно-следственных связей: Далее используем передовые алгоритмы для определения причинно-следственных связей между этими узлами. Вот тут-то и происходит волшебство - мы не просто наблюдаем, какие переменные движутся вместе, но и какие из них на самом деле приводят к изменениям в других.

3. Прогнозирование событий: Наконец, мы используем эту причинно-следственную сеть для прогнозирования значимых событий на рынке. Понимая истинные движущие силы движения рынка, мы можем лучше предвидеть серьезные сдвиги, обвалы или подъемы.


Преимущества для трейдеров

Для трейдеров на MQL5 внедрение сетевого анализа причинно-следственных связей может кардинально изменить ситуацию. Объясняем, почему:

  • Усовершенствованное управление рисками: Выявив первопричины волатильности рынка, можно лучше подготовиться к потенциальным спадам.
  • Более точные прогнозы: Понимание причинно-следственных связей приводит к более надежным прогнозам, чем те, которые основаны на простых корреляциях.
  • Раскрытие скрытых возможностей: Причинно-следственная сеть может выявить связи, которые на первый взгляд неочевидны, что приводит к уникальным торговым возможностям.
  • Адаптивные стратегии: По мере развития рынка меняется и причинно-следственная сеть, что позволяет вашим стратегиям адаптироваться в режиме реального времени.


Реализация средствами MQL5

Хотя внедрение полноценной системы сетевого анализа причинно-следственных связей является сложной задачей. MQL5 предоставляет надежную платформу для такого рода расширенного анализа. Можно начать со следующего:

  1. Использование функции «iCustom()» для создания индикаторов для каждого узла в вашей сети.
  2. Реализация алгоритмов выявления причинно-следственных связей, таких как PC-алгоритм или Тесты Грейнджера на причинность (в данном случае мы использовали алгоритмы FCI).
  3. Использование возможности нейронной сети MQL5 для построения прогнозных моделей на основе вашей причинно-следственной сети.


Почему здесь нами использован FCI вместо PC?

FCI (быстрый причинно-следственный вывод) и PC (Питер-Кларк) - это алгоритмы обнаружения причинно-следственных связей, используемые в области причинно-следственного вывода. Они предназначены для установления причинно-следственных связей на основе данных наблюдений. Приводим причины, почему можно предпочесть FCI вместо PC:

  1. Скрытые помехи: Основным преимуществом FCI перед PC является его способность справляться со скрытыми помехами. FCI может сделать вывод о наличии скрытых общих причин, в то время как PC предполагает достаточность причинно-следственной связи (отсутствие скрытых помех).
  2. Погрешность выбора: FCI также может учитывать погрешность отбора в данных, чего не может сделать PC.
  3. Более общая модель: FCI создает более общую графическую модель, называемую частичным унаследованным графом (Partial Ancestral Graph, PAG), которая может представлять более широкий класс причинно-следственных структур, чем направленные ациклические графы (Directed Acyclic Graph, DAG), создаваемые PC.
  4. Надежность и полнота: FCI является надежным и полным алгоритмом для класса систем с причинно-следственной недостаточностью, что означает, что он может корректно идентифицировать все возможные причинно-следственные связи при неограниченном объеме выборки.
  5. Устойчивость: Благодаря своей способности справляться со скрытыми помехами и необъективностью при отборе, FCI вообще более надежен при работе с реальными данными, где скрытые переменные встречаются часто.

Однако стоит отметить, что FCI имеет некоторые недостатки по сравнению с PC:

  1. Вычислительная сложность: FCI, как правило, требует больших вычислительных затрат, чем PC, особенно при работе с большими наборами данных.
  2. Менее определенная выводимая информация: PAG, создаваемые FCI, часто содержат больше неопределенных направлений рёбер, чем DAG от PC, что отражает дополнительную неопределенность, связанную с потенциальными скрытыми помехами.
  3. Трактовка значений: Для неспециалистов трактовка PAG может оказаться более сложной, чем DAG.

На практике выбор между FCI и PC часто зависит от конкретных требований, предъявляемых к вашей задаче причинно-следственного вывода, характера ваших данных и ваших предположений о причинно-следственной системе. Если вы уверены в отсутствии скрытых помех и необъективности при отборе, PC может оказаться достаточным и более эффективным. Если вы подозреваете наличие скрытых переменных или необъективность при отборе, более подходящим выбором будет FCI.


Модель векторной авторегрессии (VAR)

VAR - это алгоритм многомерного прогнозирования, используемый, когда два или более временных ряда влияют друг на друга. В этой торговой системе он, скорее всего, используется для моделирования взаимосвязей между различными финансовыми инструментами или экономическими показателями.

Основные особенности VAR:

  1. Он фиксирует линейные взаимозависимости между несколькими временными рядами.
  2. Каждая переменная является линейной функцией прошлых лагов себя и прошлых лагов других переменных.
  3. Это позволяет получить богатую динамику в системе с несколькими временными рядами.

В контексте данной торговой системы:

  • Модель VAR обучается с использованием функции `TrainVARModel`.
  • В функции `PredictVARValue` используется обученная модель для составления прогнозов.
  • Система оптимизирует VAR-модель, выбирая оптимальный лаг и значимые переменные с помощью функции `OptimizeVARModel`.


Математическое представление:

Для модели VAR с двумя переменными и лагом 1:

y1,t = c1 + φ11y1,t-1 + φ12y2,t-1 + ε1,t

y2,t = c2 + φ21y1,t-1 + φ22y2,t-1 + ε2,t

Где:

  • y1,t и y2,t являются значениями двух переменных за время t
  • c1 и c2 являются постоянными
  • φij являются коэффициентами
  • ε1,t и ε2,t являются параметрами ошибки

Система оценивает эти коэффициенты для составления прогнозов.

Представьте, что вы не просто предсказываете будущую цену одного актива, но одновременно прогнозируете множество взаимосвязанных финансовых переменных. Именно здесь блещет VAR. Это все равно, что иметь хрустальный шар, который показывает вам не одно будущее, а сразу несколько взаимосвязанных вариантов будущего.

По сути, VAR - это алгоритм многомерного прогнозирования, используемый, когда два или более временных ряда влияют друг на друга. Проще говоря, это способ зафиксировать линейные зависимости между несколькими временными рядами.


Каким образом VAR творит волшебство?

Давайте разберем его:

  1. Сбор данных: Вы начинаете со сбора исторических данных по всем переменным, которые надо включить в свою модель. Это могут быть данные о ценах на несколько валютных пар, сырьевые товары или даже экономические показатели.
  2. Спецификация модели: Вы сами решаете, сколько лагов (прошлых периодов времени) включить. Вот где пригодится ваш опыт торговли!
  3. Оценка: Модель оценивает, как на каждую переменную влияют ее собственные прошлые значения, а также прошлые значения других переменных.
  4. Прогнозирование: После оценки модель VAR может генерировать прогнозы для всех включенных переменных одновременно.


Реализация VAR в MQL5

Поскольку в MQL5 нет встроенной функции VAR, вы можете реализовать ее самостоятельно. Вот упрощенный пример того, как можно структурировать свой код:

// Define your VAR model
struct VARModel {
    int lag;
    int variables;
    double[][] coefficients;
};

// Estimate VAR model
VARModel EstimateVAR(double[][] data, int lag) {
    // Implement estimation logic here
    // You might use matrix operations for efficiency
}

// Make predictions
double[] Forecast(VARModel model, double[][] history) {
    // Implement forecasting logic here
}

// In your EA or indicator
void OnTick() {
    // Collect your multivariate data
    double[][] data = CollectData();
    
    // Estimate model
    VARModel model = EstimateVAR(data, 5); // Using 5 lags
    
    // Make forecast
    double[] forecast = Forecast(model, data);
    
    // Use forecast in your trading logic
    // ...
}


Преимущество VAR в действии

Представьте, что торгуете в валютной паре EUR/USD. При традиционном подходе можно было бы просто посмотреть на прошлые цены EUR/USD. Но с помощью VAR можно включить:

  • цены валютной пары EUR/USD
  • Цены на USD/JPY (чтобы отразить общую силу доллара США)
  • Цены на нефть (важный фактор для многих валют)
  • Индекс S&P 500 (для оценки общего настроения рынка)
  • Разница в процентных ставках между США и ЕС

Теперь ваша модель отражает гораздо более полную картину рынка Форекс, что потенциально может привести к принятию более обоснованных торговых решений.


Проблемы и особенности

Как и у любого мощного инструмента, у VAR есть свои проблемы:

  1. Требования к данным: Модели VAR могут быть требовательны к данным. Убедитесь, что у вас достаточно исторических данных для достоверных оценок.
  2. Вычислительная интенсивность: По мере увеличения переменных и лагов, растут требования к вычислениям. Оптимизируйте код для повышения эффективности.
  3. Стационарность: VAR предполагает стационарный временной ряд. Возможно, вам потребуется предварительная обработка ваших данных (например, определение различий), чтобы соответствовать этому предположению.
  4. Трактовка значений: При наличии множества переменных и лагов, трактовка результатов VAR может быть сложной. Не забывайте сочетать статистическую информацию со своими знаниями в области трейдинга.


Сетевой анализ в торговой системе CNA

Сетевой анализ, реализованный в этом советнике (EA), является фундаментальным компонентом его анализа рынка и стратегии прогнозирования. Он предназначен для представления и анализа сложных взаимосвязей между различными финансовыми инструментами или рыночными переменными.

Подведем итог ключевым моментам сетевого анализа, используемого в этом советнике:

  1. Структура: Сеть состоит из узлов, каждый из которых представляет собой финансовый инструмент (обычно валютную пару).
  2. Цель: Он предназначен для моделирования взаимосвязей и взаимозависимостей между различными финансовыми инструментами на рынке.
  3. Открытие причинно-следственной связи: Советник использует алгоритм быстрого причинно-следственного вывода (FCI) для выявления потенциальных причинно-следственных связей между этими инструментами.
  4. Представление: Эти взаимосвязи представлены в виде матрицы смежности, показывающей, какие инструменты непосредственно связаны с точки зрения причинно-следственного влияния.
  5. Анализ: Советник выполняет различные виды анализов в этой сети, включая выявление v-образных структур (особый паттерн в причинно-следственных графах) и применение правил ориентации для дальнейшего улучшения понимания причинно-следственных связей.
  6. Интеграция с прогнозом: Структура сети и взаимосвязи, обнаруженные в ходе этого анализа, служат исходными данными для модели векторной авторегрессии (VAR), которая используется для составления прогнозов.
  7. Адаптивный характер: Сетевой анализ не является статическим. Со временем он может обновляться, позволяя советнику адаптироваться к изменяющимся рыночным условиям и взаимосвязям между инструментами.

Ключевая идея, лежащая в основе этого подхода, заключается в том, что финансовые инструменты не движутся изолированно. Моделируя и анализируя сеть взаимосвязей между различными инструментами, советник стремится получить более полное представление о динамике рынка. Теоретически это должно привести к более точным прогнозам и более обоснованным торговым решениям.

Однако важно отметить, что, несмотря на сложность данного подхода, финансовые рынки чрезвычайно сложны и подвержены влиянию многих факторов. Эффективность этого сетевого анализа будет зависеть от того, насколько хорошо он отражает реальную динамику рынка и как он интегрирован с другими компонентами торговой стратегии.


Структура сети

Сеть представлена следующими структурами:

struct Node
{
    string name;
    double value;
};

Node g_network[];
int g_node_count = 0;

Каждый узел в сети представляет собой определенный финансовый инструмент или рыночную переменную. Поле "название" идентифицирует инструмент (например, "EURUSD", "GBPUSD"), а в поле "значение" могут храниться соответствующие данные для этого узла.


Создание сети

Инициализация сети осуществляется в функции SetupNetwork():

void SetupNetwork()
{
    string symbols[] = {"EURUSD", "GBPUSD", "USDCAD", "USDCHF", "USDJPY", "AUDUSD"};
    for(int i = 0; i < ArraySize(symbols); i++)
    {
        AddNode(symbols[i]);
    }
}

void AddNode(string name)
{
    ArrayResize(g_network, g_node_count + 1);
    g_network[g_node_count].name = name;
    g_network[g_node_count].value = 0; // Initialize with default value
    g_node_count++;
}

Данная настройка создает сеть, в которой каждый узел представляет отдельную валютную пару.


Назначение сети

В этом советнике сеть служит нескольким ключевым целям:

  1. Представление структуры рынка: Моделирует взаимосвязи между различными валютными парами, позволяя советнику учитывать, каким образом изменения в одной паре могут повлиять на другие.
  2. Основа для анализа причинно-следственных связей: Структура сети используется в качестве основы для алгоритма быстрого причинно-следственного вывода (FCI), который пытается обнаружить причинно-следственные связи между узлами.
  3. Исходные данные для прогнозирующего моделирования: Структура сети и взаимосвязи, обнаруженные в ходе анализа причинно-следственных связей, служат исходными данными для модели векторной авторегрессии (VAR), используемой для прогнозов.


Сетевой анализ в действии

Советник выполняет несколько видов анализа в этой сети:

  1. Открытие причинно-следственной связи: Функция FCIAlgorithm() применяет алгоритм быстрого причинно-следственного вывода для выявления потенциальных причинно-следственных связей между узлами.
  2. Матрица смежности: Причинно-следственные связи представлены в матрице смежности, где каждая запись указывает, существует ли прямая причинно-следственная связь между двумя узлами.
  3. Ориентация V-образных конструкций: Функция OrientVStructures() идентифицирует и ориентирует v-образные структуры в сети, которые являются важными паттернами в причинно-следственных графах.
  4. Анализ графиков: Окончательная структура графика анализируется для обоснования торговых решений с предположением, что инструменты с причинно-следственными связями могут предоставлять прогнозирующую информацию друг другу.


Последствия для торговли

Такой сетевой подход позволяет советнику:

  1. Рассматривать сложную динамику рынка, которая может быть неочевидна при изолированном рассмотрении инструментов.
  2. Потенциально выявлять опережающие индикаторы среди анализируемых инструментов.
  3. Делать более обоснованные прогнозы, принимая во внимание более широкий рыночный контекст.
  4. Адаптироваться к изменяющимся рыночным условиям, поскольку причинно-следственная структура периодически пересматривается.

Используя этот сетевой анализ, советник стремится получить более глубокое представление о динамике рынка, что потенциально может привести к более точным прогнозам и более обоснованным торговым решениям.


Пример кода

Данный код следует за этими двумя блок-схемами:

Flux diagram

Function flow diagram



Подробное объяснение ключевых функций торговой программы на MQL5

Данный торговый советник довольно сложен и сочетает в себе несколько передовых подходов:

  1. Для прогнозов в нем используется модель векторной авторегрессии (VAR).
  2. Для понимания взаимосвязей между различными рыночными переменными в нем реализован алгоритм обнаружения причинно-следственных связей (FCI - Быстрый причинно-следственный вывод).
  3. В нем используется сетевая структура для одновременного представления и анализа множественных финансовых инструментов.
  4. Он включает в себя различные технические фильтры, такие как волатильность, RSI и сила тренда.
  5. В нем используется адаптивное управление рисками и определение размера позиции.
  6. В нем реализуется стратегия скользящего окна для постоянного обновления и переобучения модели.
  7. Он включает в себя методы перекрестной проверки и оптимизации модели.

Структура советника позволяет проводить комплексный анализ и принимать решения, сохраняя при этом гибкость для будущих улучшений или адаптации к различным рыночным условиям.


Подробное объяснение ключевых функций в торговом советнике

1. OnInit()

int OnInit()
  {
// Step 1: Set up your network structure
   SetupNetwork();

// Step 2: Run the causal discovery algorithm (e.g., PC or FCI)
   FCIAlgorithm();

// Step 3: Train the optimized causal model
   TrainOptimizedVARModel();

   ArrayResize(g_previous_predictions, g_node_count);
   ArrayInitialize(g_previous_predictions, 0);  // Initialize with zeros

   return(INIT_SUCCEEDED);
  }

Это функция инициализации, которая запускается при первой загрузке или перезагрузке советника на графике.

Основные задачи:

  • Настраивает структуру сети, вызывая функцию SetupNetwork()
  • Запускает алгоритм открытия причинно-следственных связей (FCI) с помощью функции FCIAlgorithm()
  • Обучает оптимизированную причинно-следственную модель с помощью TrainOptimizedVARModel()
  • Инициализирует массив предыдущих прогнозов

Настоящая функция закладывает основу для всего анализа и прогнозирования, которые будет выполнять советник.

2. OnTick()

void OnTick()
  {
   ENUM_TIMEFRAMES tf = ConvertTimeframe(InputTimeframe);

   static datetime lastBarTime = 0;
   datetime currentBarTime = iTime(Symbol(), tf, 0);

   if(currentBarTime == lastBarTime)
      return;

   lastBarTime = currentBarTime;

   Print("--- New bar on timeframe ", EnumToString(tf), " ---");

   UpdateModelSlidingWindow();

   for(int i = 0; i < g_node_count; i++)
     {
      string symbol = g_network[i].name;
      Print("Processing symbol: ", symbol);

      double prediction = PredictVARValue(i);
      Print("Prediction for ", symbol, ": ", prediction);

      int signal = GenerateSignal(symbol, prediction);
      Print("Signal for ", symbol, ": ", signal);

      // Imprimir más detalles sobre las condiciones
      Print("RSI: ", CustomRSI(symbol, ConvertTimeframe(InputTimeframe), 14, PRICE_CLOSE, 0));
      Print("Trend: ", DetermineTrend(symbol, ConvertTimeframe(InputTimeframe)));
      Print("Trend Strong: ", IsTrendStrong(symbol, ConvertTimeframe(InputTimeframe)));
      Print("Volatility OK: ", VolatilityFilter(symbol, ConvertTimeframe(InputTimeframe)));
      Print("Fast MA > Slow MA: ", (iMA(symbol, PERIOD_CURRENT, 14, 0, MODE_EMA, PRICE_CLOSE) > iMA(symbol, PERIOD_CURRENT, 24, 0, MODE_EMA, PRICE_CLOSE)));

      if(signal != 0)
        {
         Print("Attempting to execute trade for ", symbol);
         ExecuteTrade(symbol, signal);
        }
      else
        {
         g_debug_no_signal_count++;
         Print("No trade signal generated for ", symbol, ". Total no-signal count: ", g_debug_no_signal_count);
        }
      ManageOpenPositions();
      ManageExistingOrders(symbol);


      Print("Current open positions for ", symbol, ": ", PositionsTotal());
      Print("Current pending orders for ", symbol, ": ", OrdersTotal());
     }

   Print("--- Bar processing complete ---");
   Print("Total no-signal count: ", g_debug_no_signal_count);
   Print("Total failed trade count: ", g_debug_failed_trade_count);
  }

Эта функция вызывается на каждом тике.

Основные задачи:

  • Конвертирует вводный таймфрейм
  • Обновляет модель с помощью скользящего окна используя функцию UpdateModelSlidingWindow()
  • Для каждого символа в сети:
    • Прогнозирует значения с помощью функции PredictVARValue()
    • Генерирует торговые сигналы с помощью GenerateSignal()
    • Совершает сделки при появлении сигналов
    • Управляет открытыми позициями
    • Управляет существующими ордерами

Это операционное ядро советника, где принимаются торговые решения.

3. OnCalculate()

Хотя эта функция явно не указана в предоставленном коде, она обычно используется для вычисления пользовательских значений индикатора. В данном советнике часть этой функциональности, по-видимому, интегрирована в OnTick().

4. CheckMarketConditions()

int GenerateSignal(string symbol, double prediction)
  {
   int node_index = FindNodeIndex(symbol);
   if(node_index == -1)
      return 0;

   static bool first_prediction = true;
   if(first_prediction)
     {
      first_prediction = false;
      return 0; // No generar señal en la primera predicción
     }

   double current_price = SymbolInfoDouble(symbol, SYMBOL_BID);

// Calculate predicted change as a percentage
   double predicted_change = (prediction - current_price) / current_price * 100;

   bool volatility_ok = VolatilityFilter(symbol, ConvertTimeframe(InputTimeframe));
   double rsi = CustomRSI(symbol, ConvertTimeframe(InputTimeframe), 14, PRICE_CLOSE, 0);
   bool trend_strong = IsTrendStrong(symbol, ConvertTimeframe(InputTimeframe));
   int trend = DetermineTrend(symbol, ConvertTimeframe(InputTimeframe));

   double fastMA = iMA(symbol, PERIOD_CURRENT, 8, 0, MODE_EMA, PRICE_CLOSE);
   double slowMA = iMA(symbol, PERIOD_CURRENT, 24, 0, MODE_EMA, PRICE_CLOSE);

   Print("Debugging GenerateSignal for ", symbol);
   Print("Current price: ", current_price);
   Print("Prediction diff: ", prediction);
   Print("predicted_change: ", predicted_change);
   Print("RSI: ", rsi);
   Print("Trend: ", trend);
   Print("Trend Strong: ", trend_strong);
   Print("Volatility OK: ", volatility_ok);
   Print("Fast MA: ", fastMA, ", Slow MA: ", slowMA);

   bool buy_condition =  prediction >  0.00001 && rsi < 30 && trend_strong  && volatility_ok && fastMA > slowMA;
   bool sell_condition = prediction < -0.00001 && rsi > 70 && trend_strong && volatility_ok && fastMA < slowMA;

   Print("Buy condition met: ", buy_condition);
   Print("Sell condition met: ", sell_condition);

// Buy conditions
   if(buy_condition)
     {
      Print("Buy signal generated for ", symbol);
      int signal = 1;
      return signal;  // Buy signal
     }
// Sell conditions
   else
      if(sell_condition)
        {
         Print("Sell signal generated for ", symbol);
         int signal = -1;
         return signal; // Sell signal
        }
      else
        {
         Print("No signal generated for ", symbol);
         int signal = 0;
         return signal; // No signal
        }


  }

Данная функция явным образом не определена в коде, но ее функциональность распределена по нескольким частям, в основном, внутри GenerateSignal():

  • Проверяет волатильность с помощью VolatilityFilter()
  • Проверяет RSI
  • Проверяет силу тренда с помощью функции IsTrendStrong()
  • Определяет направление тренда с помощью функции DetermineTrend()
  • Сравнивает быстрые и медленные скользящие средние

Эти проверки определяют, являются ли рыночные условия благоприятными для торговли.

5. ExecuteTrade()

void ExecuteTrade(string symbol, int signal)
  {
   if(!IsMarketOpen(symbol) || !IsTradingAllowed())
     {
      Print("Market is closed or trading is not allowed for ", symbol);
      return;
     }

   double price = (signal == 1) ? SymbolInfoDouble(symbol, SYMBOL_ASK) : SymbolInfoDouble(symbol, SYMBOL_BID);

   double stopLoss, takeProfit;
   CalculateAdaptiveLevels(symbol, signal, stopLoss, takeProfit);

   double lotSize = CalculateDynamicLotSize(symbol, stopLoss);

   if(lotSize <= 0)
     {
      Print("Invalid lot size for symbol: ", symbol);
      return;
     }

   trade.SetExpertMagicNumber(123456);
   trade.SetTypeFilling(ORDER_FILLING_FOK);

   bool result = false;
   int attempts = 3;

   for(int i = 0; i < attempts; i++)
     {
      if(signal == 1)
        {
         result = trade.Buy(lotSize, symbol, price, stopLoss, takeProfit, "CNA Buy");
         Print("Attempting Buy order: Symbol=", symbol, ", Lot Size=", lotSize, ", Price=", price, ", SL=", stopLoss, ", TP=", takeProfit);
        }
      else
         if(signal == -1)
           {
            result = trade.Sell(lotSize, symbol, price, stopLoss, takeProfit, "CNA Sell");
            Print("Attempting Sell order: Symbol=", symbol, ", Lot Size=", lotSize, ", Price=", price, ", SL=", stopLoss, ", TP=", takeProfit);
           }

      if(result)
        {
         Print("Order executed successfully for ", symbol, ", Type: ", (signal == 1 ? "Buy" : "Sell"), ", Lot size: ", lotSize);
         break;
        }
      else
        {
         int lastError = GetLastError();
         Print("Attempt ", i+1, " failed for ", symbol, ". Error: ", lastError, " - ", GetErrorDescription(lastError));

         if(lastError == TRADE_RETCODE_REQUOTE || lastError == TRADE_RETCODE_PRICE_CHANGED || lastError == TRADE_RETCODE_INVALID_PRICE)
           {
            price = (signal == 1) ? SymbolInfoDouble(symbol, SYMBOL_ASK) : SymbolInfoDouble(symbol, SYMBOL_BID);
            CalculateAdaptiveLevels(symbol, signal, stopLoss, takeProfit);
           }
         else
            break;
        }
     }

   if(!result)
     {
      Print("All attempts failed for ", symbol, ". Last error: ", GetLastError(), " - ", GetErrorDescription(GetLastError()));
     }
  }

Данная функция управляет фактическим исполнением сделок.

Ключевые аспекты:

  • Проверяет, открыт ли рынок и разрешена ли торговля
  • Рассчитывает адаптивные уровни стоп-лосса и тейк-профита
  • Определяет размер лота на основе управления рисками
  • Пытается выполнить ордер, с повторными попытками в случае неудачи
  • Устраняет ошибки и предоставляет подробную обратную связь

6. OnDeinit()

void OnDeinit(const int reason)
  {
   if(atr_handle != INVALID_HANDLE)
      IndicatorRelease(atr_handle);
   if(rsi_handle != INVALID_HANDLE)
      IndicatorRelease(rsi_handle);
   if(momentum_handle != INVALID_HANDLE)
      IndicatorRelease(momentum_handle);
   GenerateFinalSummary();
  }

Данная функция вызывается, когда советник удаляется с графика или когда терминал закрывается.

Основные задачи:

  • Освобождает хэндлы индикатора
  • Генерирует окончательную сводку о работе советника, вызывая функцию GenerateFinalSummary()


Дополнительные важные функции

UpdateModelSlidingWindow()

void UpdateModelSlidingWindow()
  {
   static int bars_since_update = 0;
   ENUM_TIMEFRAMES tf = ConvertTimeframe(InputTimeframe);

// Check if it's time to update
   if(bars_since_update >= g_update_frequency)
     {
      // Collect new data
      double training_data[];
      CollectTrainingData(training_data, g_window_size, tf);

      // Retrain model
      TrainModel(training_data);

      // Validate model
      double validation_score = ValidateModel();
      Print("Model updated. Validation score: ", validation_score);

      bars_since_update = 0;
     }
   else
     {
      bars_since_update++;
     }
  }

Обновляет модель VAR, используя скользящее окно данных, позволяя модели адаптироваться к изменяющимся рыночным условиям.


TrainOptimizedVARModel()

void TrainOptimizedVARModel()
  {
   OptimizationResult opt_result = OptimizeVARModel();

   Print("Lag óptimo encontrado: ", opt_result.optimal_lag);
   Print("AIC: ", opt_result.aic);
   Print("Variables seleccionadas: ", ArraySize(opt_result.selected_variables));

// Usar opt_result.optimal_lag y opt_result.selected_variables para entrenar el modelo final
   TrainVARModel(opt_result.optimal_lag, opt_result.selected_variables);
  }
Оптимизирует и обучает VAR-модель, выбирая оптимальное количество лагов и значимых переменных.


PredictVARValue()

double PredictVARValue(int node_index)
{
   if(node_index < 0 || node_index >= g_node_count)
   {
      Print("Error: Invalid node index: ", node_index);
      return 0;
   }

   double prediction = 0;
   int lag = g_var_params[node_index].lag;

   Print("Predicting for node: ", g_network[node_index].name, ", Lag: ", lag);

   // Retrieve the previous prediction
   double previous_prediction = g_previous_predictions[node_index];

   // Verify if there are enough coefficients
   int expected_coefficients = g_node_count * lag + 1; // +1 for the intercept
   if(ArraySize(g_var_params[node_index].coefficients) < expected_coefficients)
   {
      Print("Error: Not enough coefficients for node ", node_index, ". Expected: ", expected_coefficients, ", Actual: ", ArraySize(g_var_params[node_index].coefficients));
      return 0;
   }

   prediction = g_var_params[node_index].coefficients[0]; // Intercept
   Print("Intercept: ", prediction);

   double sum_predictions = 0;
   double sum_weights = 0;
   double current_price = iClose(g_network[node_index].name, PERIOD_CURRENT, 0);

   for(int l = 1; l <= lag; l++)
   {
      double time_weight = 1.0 - (double)(l-1) / lag; // Time-based weighting
      sum_weights += time_weight;

      double lag_prediction = 0;
      for(int j = 0; j < g_node_count; j++)
      {
         int coeff_index = (l - 1) * g_node_count + j + 1;
         if(coeff_index >= ArraySize(g_var_params[node_index].coefficients))
         {
            Print("Warning: Coefficient index out of range. Skipping. Index: ", coeff_index, ", Array size: ", ArraySize(g_var_params[node_index].coefficients));
            continue;
         }

         double coeff = g_var_params[node_index].coefficients[coeff_index];
         double raw_value = iClose(g_network[j].name, PERIOD_CURRENT, l);

         if(raw_value == 0 || !MathIsValidNumber(raw_value))
         {
            Print("Warning: Invalid raw value for ", g_network[j].name, " at lag ", l);
            continue;
         }

         // Normalize the value as a percentage change
         double normalized_value = (raw_value - current_price) / current_price;

         double partial_prediction = coeff * normalized_value;
         lag_prediction += partial_prediction;

         Print("Lag ", l, ", Node ", j, ": Coeff = ", coeff, ", Raw Value = ", raw_value,
               ", Normalized Value = ", normalized_value, ", Partial prediction: ", partial_prediction);
      }

      sum_predictions += lag_prediction * time_weight;
      Print("Lag ", l, " prediction: ", lag_prediction, ", Weighted: ", lag_prediction * time_weight);
   }

   Print("Sum of weights: ", sum_weights);

   // Calculate the final prediction
   if(sum_weights > 1e-10)
   {
      prediction = sum_predictions / sum_weights;
   }
   else
   {
      Print("Warning: sum_weights is too small (", sum_weights, "). Using raw sum of predictions.");
      prediction = sum_predictions;
   }

   // Calculate the difference between the current prediction and the previous prediction
   double prediction_change = prediction - previous_prediction;

   Print("Previous prediction: ", previous_prediction);
   Print("Current prediction: ", prediction);
   Print("Prediction change: ", prediction_change);

   // Convert the prediction to a percentage change
   double predicted_change_percent = (prediction - current_price) / current_price * 100;

   Print("Final prediction (as percentage change): ", predicted_change_percent, "%");

   // Update the current prediction for the next iteration
   g_previous_predictions[node_index] = prediction;

   // Return the difference in basis points
   return prediction_change * 10000; // Multiply by 10000 to convert to basis points
}

Делает прогнозы, используя обученную модель VAR, с учетом исторических значений и коэффициентов модели.


ManageOpenPositions() and ManageExistingOrders()

void ManageOpenPositions()
  {
   for(int i = PositionsTotal() - 1; i >= 0; i--)
     {
      ulong ticket = PositionGetTicket(i);
      if(PositionSelectByTicket(ticket))
        {
         string symbol = PositionGetString(POSITION_SYMBOL);
         double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
         double currentPrice = PositionGetDouble(POSITION_PRICE_CURRENT);
         double stopLoss = PositionGetDouble(POSITION_SL);
         ENUM_POSITION_TYPE positionType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);

         // Implement trailing stop
         if(positionType == POSITION_TYPE_BUY && currentPrice - openPrice > 2 * CustomATR(symbol, PERIOD_CURRENT, 14, 0))
           {
            double newStopLoss = NormalizeDouble(currentPrice - CustomATR(symbol, PERIOD_CURRENT, 14, 0), SymbolInfoInteger(symbol, SYMBOL_DIGITS));
            if(newStopLoss > stopLoss)
              {
               trade.PositionModify(ticket, newStopLoss, 0);
              }
           }
         else
            if(positionType == POSITION_TYPE_SELL && openPrice - currentPrice > 2 * CustomATR(symbol, PERIOD_CURRENT, 14, 0))
              {
               double newStopLoss = NormalizeDouble(currentPrice + CustomATR(symbol, PERIOD_CURRENT, 14, 0), SymbolInfoInteger(symbol, SYMBOL_DIGITS));
               if(newStopLoss < stopLoss || stopLoss == 0)
                 {
                  trade.PositionModify(ticket, newStopLoss, 0);
                 }
              }
        }
     }
  }


void ManageExistingOrders(string symbol)
  {
   for(int i = PositionsTotal() - 1; i >= 0; i--)
     {
      ulong ticket = PositionGetTicket(i);
      if(PositionSelectByTicket(ticket) && PositionGetString(POSITION_SYMBOL) == symbol)
        {
         double stopLoss = PositionGetDouble(POSITION_SL);
         double takeProfit = PositionGetDouble(POSITION_TP);
         double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
         double currentPrice = PositionGetDouble(POSITION_PRICE_CURRENT);
         ENUM_POSITION_TYPE positionType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);

         double atr = CustomATR(symbol, PERIOD_CURRENT, 14, 0);
         double newStopLoss, newTakeProfit;

         if(positionType == POSITION_TYPE_BUY)
           {
            newStopLoss = NormalizeDouble(currentPrice - 2 * atr, SymbolInfoInteger(symbol, SYMBOL_DIGITS));
            newTakeProfit = NormalizeDouble(currentPrice + 3 * atr, SymbolInfoInteger(symbol, SYMBOL_DIGITS));
            if(newStopLoss > stopLoss && currentPrice - openPrice > atr)
              {
               trade.PositionModify(ticket, newStopLoss, newTakeProfit);
              }
           }
         else
            if(positionType == POSITION_TYPE_SELL)
              {
               newStopLoss = NormalizeDouble(currentPrice + 2 * atr, SymbolInfoInteger(symbol, SYMBOL_DIGITS));
               newTakeProfit = NormalizeDouble(currentPrice - 3 * atr, SymbolInfoInteger(symbol, SYMBOL_DIGITS));
               if(newStopLoss < stopLoss && openPrice - currentPrice > atr)
                 {
                  trade.PositionModify(ticket, newStopLoss, newTakeProfit);
                 }
              }

         // Implementar cierre parcial
         double profit = PositionGetDouble(POSITION_PROFIT);
         double volume = PositionGetDouble(POSITION_VOLUME);
         if(profit > 0 && MathAbs(currentPrice - openPrice) > 2 * atr)
           {
            double closeVolume = volume * 0.5;
            if(IsValidVolume(symbol, closeVolume))
              {
               ClosePosition(ticket, closeVolume);
              }
           }
        }
     }
  }

Управляйте открытыми позициями и существующими ордерами, реализуя такие стратегии, как трейлинг-стопы и частичное закрытие.

Данный советник сочетает в себе традиционный технический анализ с передовым статистическим моделированием (VAR) и методами машинного обучения (открытие причинно-следственных связей) для принятия торговых решений. Модульная структура позволяет легко модифицировать и совершенствовать отдельные компоненты.


Результаты

Это результаты для некоторых символов, настройки и входные данные останутся прежними для всех изучаемых символов:

Настройки

Inputs


EURUSD

EURUSD


GBPUSD

GBPUSD


AUDUSD

AUDUSD

USDJPY

USDJPY


USDCAD

USDCAD


USDCHF

USDCHF


Как получить лучшие результаты?

Лучших результатов можно добиться, если в узле "Символы" добавить больше символов, а также использовать совместно интегрированные и коррелированные символы. Чтобы узнать, что это за символы, можете воспользоваться моим скриптом на python из этой статьи:  Применение теории игр Нэша с фильтрацией НММ в трейдинге - статьи MQL5.

Кроме того, лучших результатов можно добиться, если добавить к стратегии глубокое обучение. Существует несколько примеров вышеуказанного в таких статьях, как эта:  Анализ сентимента (рыночных настроений) и глубокое обучение для торговли советником и тестирование на истории с помощью Python - статьи MQL5 

Также, не была проведена оптимизация фильтров, а вы также можете попробовать добавить дополнительные фильтры.

Пример того, как меняется график при добавлении дополнительных символов в сеть

void SetupNetwork()
  {
   string symbols[] = {"EURUSD", "GBPUSD", "USDCAD", "USDCHF", "USDJPY", "AUDUSD", "XAUUSD", "SP500m", "ND100m"};
   for(int i = 0; i < ArraySize(symbols); i++)
     {
      AddNode(symbols[i]);
     }
  }


EURUSD modified

EURUSD modified

Просто добавив больше символов в сеть, нам удалось улучшить следующее:

  1. Торговая активность (больше общего количества сделок)
  2. Коэффициент выигрыша в короткой сделке
  3. Процент прибыльных сделок
  4. Размер самой крупной прибыльной сделки
  5. Средняя прибыль на прибыльную сделку
  6. Более низкий процент убыточных сделок
  7. Меньший средний убыток на убыточную сделку

Эти улучшения предполагают улучшение общего управления торговлей и контроля рисков, несмотря на снижение некоторых ключевых показателей эффективности (таких как коэффициент прибыли и коэффициент восстановления).


Заключение

В настоящей статье рассматривается продвинутая торговая система, которая объединяет сетевой анализ причинно-следственных связей (CNA) и векторную авторегрессию (VAR) для прогнозирования рыночных событий и принятия торговых решений. В системе используются сложные статистические методы и методы машинного обучения для моделирования взаимосвязей между финансовыми инструментами. Несмотря на многообещающий характер, подчеркивается важность сочетания такого подхода с надежным управлением рисками и глубоким пониманием рынков.

Для трейдеров, стремящихся к лучшим результатам, в статье предлагается расширить сеть за счет большего количества коррелирующих символов, внедрить глубокое обучение и оптимизировать методы фильтрации. Непрерывное обучение и адаптация имеют решающее значение для долгосрочного успеха в трейдинге.

Удачной торговли и пусть ваши алгоритмы всегда будут на шаг впереди рынка!


Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/15665

Прикрепленные файлы |
CNA_Final_v4.mq5 (179.13 KB)
Последние комментарии | Перейти к обсуждению на форуме трейдеров (4)
Javier Santiago Gaston De Iriarte Cabrera
Javier Santiago Gaston De Iriarte Cabrera | 7 сент. 2024 в 16:17

Если с этим советником у вас возникнут проблемы, вы можете попробовать с этим (это более старая версия, и графики не совпадают, но я просто попробовал его и он работает).

Пожалуйста, сообщайте мне, когда советник не работает должным образом, потому что время от времени я форматирую компьютер и теряю все остальные версии.

Javier Santiago Gaston De Iriarte Cabrera
Javier Santiago Gaston De Iriarte Cabrera | 7 сент. 2024 в 16:23

Этот тоже работает

Javier Santiago Gaston De Iriarte Cabrera
Javier Santiago Gaston De Iriarte Cabrera | 7 сент. 2024 в 16:30

Извините, это должно работать

Javier Santiago Gaston De Iriarte Cabrera
Javier Santiago Gaston De Iriarte Cabrera | 7 сент. 2024 в 16:40
Этот не выдает ошибок, и именно его я буду использовать для начала работы.
MQL5-советник, интегрированный в Telegram (Часть 4): Модуляризация функций кода для улучшенного повторного использования MQL5-советник, интегрированный в Telegram (Часть 4): Модуляризация функций кода для улучшенного повторного использования
В этой статье мы реорганизуем существующий код отправки сообщений и скриншотов из MQL5 в Telegram, преобразовав его в многоразовые модульные функции. Это оптимизирует процесс, обеспечивая более эффективное выполнение и более простое управление кодом в нескольких экземплярах.
От начального до среднего уровня: Массив (I) От начального до среднего уровня: Массив (I)
Данная статья является переходом между тем, что рассматривалось до этого, и новым этапом исследований. Чтобы понять эту статью, необходимо прочитать предыдущие. Представленные здесь материалы предназначены только для обучения. Ни в коем случае нельзя рассматривать это приложение как окончательное, цели которого будут иные, кроме изучения представленных концепций.
Нейросети в трейдинге: Адаптивное обнаружение рыночных аномалий (DADA) Нейросети в трейдинге: Адаптивное обнаружение рыночных аномалий (DADA)
Предлагаем познакомиться с фреймворком DADA — инновационным методом выявления аномалий во временных рядах. Он помогает отличить случайные колебания от подозрительных отклонений. В отличие от традиционных методов, DADA гибко подстраивается под разные данные. Вместо фиксированного уровня сжатия он использует несколько вариантов и выбирает наиболее подходящий для каждого случая.
Переосмысливаем классические стратегии (Часть VII): Анализ валютных рынков и суверенного долга на USDJPY Переосмысливаем классические стратегии (Часть VII): Анализ валютных рынков и суверенного долга на USDJPY
Мы проанализируем взаимосвязь между валютными курсами и государственными облигациями. Облигации являются одной из самых популярных форм ценных бумаг с фиксированным доходом. Посмотрим, можно ли улучшить классическую стратегию с помощью ИИ.