English 中文 Español Deutsch 日本語 Português
preview
Машинное обучение и Data Science (Часть 28): Прогнозирование множества будущих значений для EURUSD

Машинное обучение и Data Science (Часть 28): Прогнозирование множества будущих значений для EURUSD

MetaTrader 5Трейдинг | 12 февраля 2025, 15:28
607 0
Omega J Msigwa
Omega J Msigwa

Содержание


Введение

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

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



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


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

  • Прямое многошаговое прогнозирование
  • Рекурсивное многошаговое прогнозирование
  • Модели с множеством выходов
  • Векторная авторегрессия (VAR) (о ней будем говорить в следующей статье(ях))

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

# Create target variables for multiple future steps

def create_target(df, future_steps=10):
    target = pd.concat([df['Close'].shift(-i) for i in range(1, future_steps + 1)], axis=1) # using close prices for the next i bar
    target.columns = [f'target_close_{i}' for i in range(1, future_steps + 1)] # naming the columns
    return target

# Combine features and targets

new_df = pd.DataFrame({
    'Open': df['Open'],
    'High': df['High'],
    'Low': df['Low'],
    'Close': df['Close']
})


future_steps = 5

target_columns = create_target(new_df, future_steps).dropna()
combined_df = pd.concat([new_df, target_columns], axis=1) #concatenating the new pandas dataframe with the target columns

combined_df = combined_df.dropna() #droping rows with NaN values caused by shifting values

target_cols_names = [f'target_close_{i}' for i in range(1, future_steps + 1)]

X = combined_df.drop(columns=target_cols_names).values #dropping all target columns from the x array
y = combined_df[target_cols_names].values # creating the target variables

print(f"x={X.shape} y={y.shape}")
combined_df.head(10)

Прямое многошаговое прогнозирование

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

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

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

Сначала создадим функцию для обработки данных из множества шагов.


Подготовка данных

Код Python

def multi_steps_data_process(data, step, train_size=0.7, random_state=42):

    # Since we are using the OHLC values only
    
    data["next signal"] = data["Signal"].shift(-step) # The target variable from next n future values
    data = data.dropna()
        
    y = data["next signal"]
    X = data.drop(columns=["Signal", "next signal"])
    
    return train_test_split(X, y, train_size=train_size, random_state=random_state) 

Эта функция создает новую целевую переменную, используя столбец «Сигнал» из набора данных. Целевая переменная берется из значения индекса step+1 в столбце сигнала. 

Предположим, что у нас есть такие данные:

Сигналы
1
2
3
4

На шаге 1 следующим сигналом будет 2, на шаге 2 следующим сигналом будет 3 и т. д.

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

В этой статье мы будем использовать данные с часового таймфрейма EURUSD на 1000 баров.

Код Python

df = pd.read_csv("/kaggle/input/eurusd-period-h1/EURUSD.PERIOD_H1.csv")

print(df.shape)
df.head(10)

Результаты

Для простоты я создал мини-набор данных только для пяти переменных.

Мини-выборка по EURUSD-H1

Столбец «Сигнал» отображает сигналы бычьей или медвежьей свечи. Он был создан по такой логике: всякий раз, когда цена закрытия была больше цены открытия, сигналу присваивалось значение 1, а в противоположном случае — значение 0.

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


Обучение нескольких моделей для прогнозирования

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

Код Python

for pred_step in range(1, 6): # We want to 5 future values

    lgbm_model = lgbm.LGBMClassifier(**params)
    
    
    X_train, X_test, y_train, y_test = multi_steps_data_process(new_df, pred_step) # preparing data for the current step
    
    
    lgbm_model.fit(X_train, y_train) # training the model for this step
    
    # Testing the trained mdoel 
    
    test_pred = lgbm_model.predict(X_test) # Changes from bst to pipe
 
        
    # Ensuring the lengths are consistent

    if len(y_test) != len(test_pred):
        test_pred = test_pred[:len(y_test)]
        
    
    print(f"model for next_signal[{pred_step} accuracy={accuracy_score(y_test, test_pred)}")
    
    #  Saving the model in ONNX format, Registering ONNX converter

    update_registered_converter(
        lgbm.LGBMClassifier,
        "GBMClassifier",
        calculate_linear_classifier_output_shapes,
        convert_lightgbm,
        options={"nocl": [False], "zipmap": [True, False, "columns"]},
    )
    # Final LightGBM conversion to ONNX

    model_onnx = convert_sklearn(
        lgbm_model,
        "lightgbm_model",
        [("input", FloatTensorType([None, X_train.shape[1]]))],
        target_opset={"": 12, "ai.onnx.ml": 2},
    )

    # And save.
    with open(f"lightgbm.EURUSD.h1.pred_close.step.{pred_step}.onnx", "wb") as f:
        f.write(model_onnx.SerializeToString())

Результаты

model for next_signal[1 accuracy=0.5033333333333333
model for next_signal[2 accuracy=0.5566666666666666
model for next_signal[3 accuracy=0.4866666666666667
model for next_signal[4 accuracy=0.4816053511705686
model for next_signal[5 accuracy=0.5317725752508361

Удивительно, но модель для прогнозирования следующего второго бара оказалась самой точной, ее точность 55%, за ней следует модель для прогнозирования следующего пятого бара, которая показала точность 53%.


Загрузка моделей для прогнозирования в MetaTrader 5

Начнем с интеграции всех моделей LightGBM, сохраненных в формате ONNX, в наш советник в виде файлов ресурсов.

Код MQL5

#resource "\\Files\\lightgbm.EURUSD.h1.pred_close.step.1.onnx" as uchar model_step_1[]
#resource "\\Files\\lightgbm.EURUSD.h1.pred_close.step.2.onnx" as uchar model_step_2[]
#resource "\\Files\\lightgbm.EURUSD.h1.pred_close.step.3.onnx" as uchar model_step_3[]
#resource "\\Files\\lightgbm.EURUSD.h1.pred_close.step.4.onnx" as uchar model_step_4[]
#resource "\\Files\\lightgbm.EURUSD.h1.pred_close.step.5.onnx" as uchar model_step_5[]

#include <MALE5\Gradient Boosted Decision Trees(GBDTs)\LightGBM\LightGBM.mqh>

CLightGBM *light_gbm[5]; //for storing 5 different models
MqlRates rates[];

Затем инициализируем наши 5 различных моделей.

Код MQL5

int OnInit()
  {
//---

   for (int i=0; i<5; i++)
     light_gbm[i] = new CLightGBM(); //Creating LightGBM objects

//---
   
   if (!light_gbm[0].Init(model_step_1))
     {
       Print("Failed to initialize model for step=1 predictions");
       return INIT_FAILED;
     }
   
   if (!light_gbm[1].Init(model_step_2))
     {
       Print("Failed to initialize model for step=2 predictions");
       return INIT_FAILED;
     }
   
   if (!light_gbm[2].Init(model_step_3))
     {
       Print("Failed to initialize model for step=3 predictions");
       return INIT_FAILED;
     }
   
   if (!light_gbm[3].Init(model_step_4))
     {
       Print("Failed to initialize model for step=4 predictions");
       return INIT_FAILED;
     }
   
   if (!light_gbm[4].Init(model_step_5))
     {
       Print("Failed to initialize model for step=5 predictions");
       return INIT_FAILED;
     }
      
   
   return(INIT_SUCCEEDED);
  }

Наконец, мы можем собрать значения цен открытия, максимума, минимума и закрытия (Open, High, Low и Close) предыдущего бара и использовать их для получения прогнозов по всем 5 различным моделям.

Код MQL5

void OnTick()
  {
//---
   
   CopyRates(Symbol(), PERIOD_H1, 1, 1, rates);
   vector input_x = {rates[0].open, rates[0].high, rates[0].low, rates[0].close};
   
   string comment_string = "";
   int signal = -1;
   
   for (int i=0; i<5; i++)
    {
      signal = (int)light_gbm[i].predict_bin(input_x);
      comment_string += StringFormat("\n Next[%d] bar predicted signal=%s",i+1, signal==1?"Buy":"Sell");
    }
    
   Comment(comment_string);
  }

Результат

Многошаговые предсказанные сигналы


Преимущества прямого многошагового прогнозирования

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


Недостатки прямого многошагового прогнозирования

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


Рекурсивное многошаговое прогнозирование

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

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

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

Код Python

new_df = pd.DataFrame({
    'Close': df['Close'],
    'target close': df['Close'].shift(-1) # next bar closing price
})

Then.

new_df = new_df.dropna() # after shifting we want to drop all NaN values

X = new_df[["Close"]].values # Assigning close values into a 2D x array
y = new_df["target close"].values

print(new_df.shape)
new_df.head(10)

Результаты

Обучение и тестирование модели линейной регрессии

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

model = Pipeline([
    ("scaler", StandardScaler()),
    ("linear_regression", LinearRegression())
])


# Split the data into training and test sets

train_size = int(len(new_df) * 0.7)
X_train, X_test = X[:train_size], X[train_size:]
y_train, y_test = y[:train_size], y[train_size:]

# Train the model
model.fit(X_train, y_train)

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

# Testing the Model

test_pred = model.predict(X_test) # Make predictions on the test set

# Plot the actual vs predicted values
plt.figure(figsize=(7.5, 5))
plt.plot(y_test, label='Actual Values')
plt.plot(test_pred, label='Predicted Values')
plt.xlabel('Samples')
plt.ylabel('Close Prices')
plt.title('Actual vs Predicted Values')
plt.legend()
plt.show()

Результат

График результатов рекурсивной модели

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

Код Python

# Function for recursive forecasting
def recursive_forecast(model, initial_value, steps):
    predictions = []
    current_input = np.array([[initial_value]])
    
    for _ in range(steps):
        prediction = model.predict(current_input)[0]
        predictions.append(prediction)
        
        # Update the input for the next prediction
        current_input = np.array([[prediction]])
    
    return predictions

Далее мы можем получить будущие прогнозы для 10 баров.

current_close = X[-1][0]  # Use the last value in the array

# Number of future steps to forecast
steps = 10

# Forecast future values
forecasted_values = recursive_forecast(model, current_close, steps)

print("Forecasted Values:")
print(forecasted_values)

Результаты

Forecasted Values:
[1.0854623040804965, 1.0853751608200348, 1.0852885667357617, 1.0852025183667728, 1.0851170122739744, 1.085032045039946, 1.0849476132688034, 1.0848637135860637, 1.0847803426385094, 1.0846974970940555]

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

predicted = []

for i in range(0, X_test.shape[0], steps):
    
    current_close = X_test[i][0]  # Use the last value in the test array
    
    forecasted_values = recursive_forecast(model, current_close, steps)
    predicted.extend(forecasted_values)
    
print(len(predicted))

Результаты


Точность рекурсивной модели составила 91%.

Наконец, можем сохранить модель линейной регрессии в формате ONNX, совместимом с MQL5.

# Convert the trained pipeline to ONNX
initial_type = [('float_input', FloatTensorType([None, 1]))]
onnx_model = convert_sklearn(model, initial_types=initial_type)

# Save the ONNX model to a file
with open("Lr.EURUSD.h1.pred_close.onnx", "wb") as f:
    f.write(onnx_model.SerializeToString())

print("Model saved to Lr.EURUSD.h1.pred_close.onnx")


Рекурсивные прогнозы в MQL5.

Начнем с добавления модели линейной регрессии ONNX в наш советник.

#resource "\\Files\\Lr.EURUSD.h1.pred_close.onnx" as uchar lr_model[]

Затем импортируем класс обработчика модели линейной регрессии.

#include <MALE5\Linear Models\Linear Regression.mqh>
CLinearRegression lr;

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

int OnInit()
  {
//---
   
   if (!lr.Init(lr_model))
     return INIT_FAILED;
   
//---
   
   ArraySetAsSeries(rates, true);

   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   
   CopyRates(Symbol(), PERIOD_H1, 1, 1, rates);
   vector input_x = {rates[0].close}; //get the previous closed bar close price
   
   vector predicted_close(10); //predicted values for the next 10 timestepps
   
   for (int i=0; i<10; i++)
    {
      predicted_close[i] = lr.predict(input_x);
      input_x[0] = predicted_close[i]; //The current predicted value is the next input
    }
   
   Print(predicted_close);
  }

Результаты

OR      0       16:39:37.018    Recursive-Multi step forecasting (EURUSD,H4)    [1.084011435508728,1.083933353424072,1.083855748176575,1.083778619766235,1.083701968193054,1.083625793457031,1.083550095558167,1.08347487449646,1.083400130271912,1.083325862884521]

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

   if (NewBar())
    {
      for (int i=0; i<10; i++)
       {
         predicted_close[i] = lr.predict(input_x);
         input_x[0] = predicted_close[i]; //The current predicted value is the next input
        
        //---
        
            ObjectDelete(0, "step"+string(i+1)+"-prediction"); //delete an object if it exists
            TrendCreate("step"+string(i+1)+"-prediction",rates[0].time, predicted_close[i], rates[0].time+(10*60*60), predicted_close[i], clrBlack); //draw a line starting from the previous candle to 10 hours forward
       }
    }

Функция TrendCreate создает короткую горизонтальную линию тренда, начиная с предыдущего закрытого бара на 10 баров вперед.

Результат


Преимущества рекурсивного многошагового прогнозирования

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


Недостатки рекурсивного многошагового прогнозирования

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


Многошаговое прогнозирование с использованием моделей с множественными выходами

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

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


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

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

Код Python

# Create target variables for multiple future steps

def create_target(df, future_steps=10):
    target = pd.concat([df['Close'].shift(-i) for i in range(1, future_steps + 1)], axis=1) # using close prices for the next i bar
    target.columns = [f'target_close_{i}' for i in range(1, future_steps + 1)] # naming the columns
    return target

# Combine features and targets

new_df = pd.DataFrame({
    'Open': df['Open'],
    'High': df['High'],
    'Low': df['Low'],
    'Close': df['Close']
})


future_steps = 5

target_columns = create_target(new_df, future_steps).dropna()
combined_df = pd.concat([new_df, target_columns], axis=1) #concatenating the new pandas dataframe with the target columns

combined_df = combined_df.dropna() #droping rows with NaN values caused by shifting values

target_cols_names = [f'target_close_{i}' for i in range(1, future_steps + 1)]

X = combined_df.drop(columns=target_cols_names).values #dropping all target columns from the x array
y = combined_df[target_cols_names].values # creating the target variables

print(f"x={X.shape} y={y.shape}")
combined_df.head(10)

Результаты

x=(995, 4) y=(995, 5)


Обучение и тестирование нейронной сети с множественными выходами

Начнем с определения последовательной модели нейронной сети.

Код Python

# Defining the neural network model
model = Sequential([
    Input(shape=(X.shape[1],)),
    Dense(units = 256, activation='relu'),
    Dense(units = 128, activation='relu'),
    Dense(units = future_steps)
])

# Compiling the model
adam = Adam(learning_rate=0.01)
model.compile(optimizer=adam, loss='mse')

# Mmodel summary
model.summary()

Результаты

┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
┃ Layer (type)                    ┃ Output Shape           ┃       Param # ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│ dense (Dense)                   │ (None, 256)            │         1,280 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense_1 (Dense)                 │ (None, 128)            │        32,896 │
├─────────────────────────────────┼────────────────────────┼───────────────┤
│ dense_2 (Dense)                 │ (None, 5)              │           645 │
└─────────────────────────────────┴────────────────────────┴───────────────┘
 Total params: 34,821 (136.02 KB)
 Trainable params: 34,821 (136.02 KB)
 Non-trainable params: 0 (0.00 B)

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

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

# Split the data into training and test sets
X_train, X_test, y_train, y_test = train_test_split(X, y, train_size=0.7, random_state=42)

scaler = MinMaxScaler()

X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)

# Training the model

early_stopping = EarlyStopping(monitor='val_loss', patience=5, restore_best_weights=True) # stop training when 5 epochs doesn't improve

history = model.fit(X_train, y_train, epochs=20, validation_split=0.2, batch_size=32, callbacks=[early_stopping])

Затем тестируем модель на тестовом наборе данных.

# Testing the Model

test_pred = model.predict(X_test) # Make predictions on the test set

# Plotting the actual vs predicted values for each future step
plt.figure(figsize=(7.5, 10))
for i in range(future_steps):
    plt.subplot((future_steps + 1) // 2, 2, i + 1)  # subplots grid 
    plt.plot(y_test[:, i], label='Actual Values')
    plt.plot(test_pred[:, i], label='Predicted Values')
    plt.xlabel('Samples')
    plt.ylabel(f'Close Price +{i+1}')
    plt.title(f'Actual vs Predicted Values (Step {i+1})')
    plt.legend()

plt.tight_layout()
plt.show()

# Evaluating the model for each future step
for i in range(future_steps):
    accuracy = r2_score(y_test[:, i], test_pred[:, i])
    print(f"Step {i+1} - R^2 Score: {accuracy}")

Ниже представлен результат.

Нейронная сеть с несколькими выходами

Step 1 - R^2 Score: 0.8664635514027637
Step 2 - R^2 Score: 0.9375671150885528
Step 3 - R^2 Score: 0.9040736780305894
Step 4 - R^2 Score: 0.8491904738263638
Step 5 - R^2 Score: 0.8458062142647863

Нейронная сеть показала впечатляющие результаты при решении этой задачи регрессии. Код ниже позволяет получить прогнозы в Python.

# Predicting multiple future values

current_input = X_test[0].reshape(1, -1) # use the first row of the test set, reshape the data also
predicted_values = model.predict(current_input)[0] # adding[0] ensures we get a 1D array instead of 2D

print("Predicted Future Values:")
print(predicted_values)

Результаты

1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 21ms/step
Predicted Future Values:
[1.0892788 1.0895394 1.0892794 1.0883198 1.0884078]

Затем можем сохранить эту модель в формате ONNX, а файлы масштабирования — в бинарных файлах.

import tf2onnx

# Convert the Keras model to ONNX
spec = (tf.TensorSpec((None, X_train.shape[1]), tf.float16, name="input"),)
model.output_names=['output']

onnx_model, _ = tf2onnx.convert.from_keras(model, input_signature=spec, opset=13)

# Save the ONNX model to a file
with open("NN.EURUSD.h1.onnx", "wb") as f:
    f.write(onnx_model.SerializeToString())
    
# Save the used scaler parameters to binary files

scaler.data_min_.tofile("NN.EURUSD.h1.min_max.min.bin")
scaler.data_max_.tofile("NN.EURUSD.h1.min_max.max.bin")

Наконец, можем использовать сохраненную модель и ее параметры масштабирования данных в MQL5.


Получение многошаговых предсказаний нейронной сети в MQL5

Начнем с добавления модели и параметров масштабирования Min-Max в советник.

#resource "\\Files\\NN.EURUSD.h1.onnx" as uchar onnx_model[]; //rnn model in onnx format
#resource "\\Files\\NN.EURUSD.h1.min_max.max.bin" as double min_max_max[];
#resource "\\Files\\NN.EURUSD.h1.min_max.min.bin" as double min_max_min[];

Затем импортируем класс регрессионной нейронной сети ONNX и обработчик библиотеки масштабирования MinMax.

#include <MALE5\Neural Networks\Regressor Neural Nets.mqh>
#include <MALE5\preprocessing.mqh>

CNeuralNets nn;
MinMaxScaler *scaler;

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

MqlRates rates[];
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---

   if (!nn.Init(onnx_model))
     return INIT_FAILED;
   
   scaler = new MinMaxScaler(min_max_min, min_max_max); //Initializing the scaler, populating it with trained values
   
//---
   
   ArraySetAsSeries(rates, true);
   
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
     if (CheckPointer(scaler)!=POINTER_INVALID)
       delete (scaler);
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---

   CopyRates(Symbol(), PERIOD_H1, 1, 1, rates);
   vector input_x = {rates[0].open, rates[0].high, rates[0].low, rates[0].close};
   input_x = scaler.transform(input_x); // We normalize the input data
   
   vector preds = nn.predict(input_x);
   
   Print("predictions = ",preds);
  }

Результаты

2024.07.31 19:13:20.785 Multi-step forecasting using Multi-outputs model (EURUSD,H4)    predictions = [1.080284595489502,1.082370758056641,1.083482265472412,1.081504583358765,1.079929828643799]

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

void OnTick()
  {
//---

   CopyRates(Symbol(), PERIOD_H1, 1, 1, rates);
   vector input_x = {rates[0].open, rates[0].high, rates[0].low, rates[0].close};
   
   if (NewBar())
    {
      input_x = scaler.transform(input_x); // We normalize the input data 
      vector preds = nn.predict(input_x);
      
      for (int i=0; i<(int)preds.Size(); i++)
       {  
        //---
        
            ObjectDelete(0, "step"+string(i+1)+"-prediction"); //delete an object if it exists
            TrendCreate("step"+string(i+1)+"-prediction",rates[0].time, preds[i], rates[0].time+(5*60*60), preds[i], clrBlack); //draw a line starting from the previous candle to 5 hours forward
       }
    }
  }

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

Предсказания с множеством выходов


Обзор многошагового прогнозирования с использованием моделей с множественными выходами

Преимущества

  • Прогнозируя несколько шагов одновременно, модель может улавливать взаимосвязи и зависимости между будущими временными шагами.
  • Нужна только одна модель, что упрощает внедрение и обслуживание.
  • Модель учится делать последовательные прогнозы на всем горизонте прогнозирования.

Недостатки

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



Использование многошагового прогнозирования в торговых стратегиях

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

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

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


Заключение

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

Всем добра.



За развитием этой модели машинного обучения и многого другого из этой серии статей можно следить в моем репозитории на GitHub.


Таблица вложений

Наименование файла Тип файла Описание и использование

Direct Muilti step Forecasting.mq5
Multi-step forecasting using multi-outputs model.mq5
Recursive-Multi step forecasting.mq5

 Советники  Советник использует несколько моделей LightGBM для многошагового прогнозирования.
Советник на основе модели нейронной сети, прогнозирующей несколько шагов с использованием структуры с несколькими выходами.
В этом советнике линейная регрессия итеративно предсказывает будущие временные шаги.
LightGBM.mqh  Файл библиотеки MQL5  Содержит код для загрузки модели LightGBM в формате ONNX и использования ее для прогнозирования.
Linear Regression.mqh  Файл библиотеки MQL5  Содержит код для загрузки модели линейной регрессии в формате ONNX и использования ее для прогнозирования.
preprocessing.mqh  Файл библиотеки MQL5   Этот файл содержит масштабатор MInMax, используемый для нормализации входных данных.
Regressor Neural Nets.mqh  Файл библиотеки MQL5   Содержит код для загрузки и развертывания модели нейронной сети из формата ONNX в MQL5.

lightgbm.EURUSD.h1.pred_close.step.1.onnx
lightgbm.EURUSD.h1.pred_close.step.2.onnx
lightgbm.EURUSD.h1.pred_close.step.3.onnx
lightgbm.EURUSD.h1.pred_close.step.4.onnx
lightgbm.EURUSD.h1.pred_close.step.5.onnx


Lr.EURUSD.h1.pred_close.onnx


NN.EURUSD.h1.onnx

 Модели ИИ в формате ONNX


 Модели LightGBM для прогнозирования значений следующего будущего шага




Простая модель линейной регрессии в формате ONNX


Нейронная сеть с прямой связью в формате ONNX

NN.EURUSD.h1.min_max.max.bin
NN.EURUSD.h1.min_max.min.bin
 Двоичные файлы  Содержит максимальные и минимальные значения соответственно для масштабатора MinMax
 predicting-multiple-future-tutorials.ipynb  Jupyter Notebook   Весь код Python, показанный в этой статье, можно найти в этом файле.



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

Прикрепленные файлы |
Attachments.zip (519.77 KB)
Автооптимизация тейк-профитов и параметров индикатора с помощью SMA и EMA Автооптимизация тейк-профитов и параметров индикатора с помощью SMA и EMA
В статье представлен продвинутый советник для торговли на рынке Форекс, сочетающий машинное обучение с техническим анализом. Он предназначен для торговли акциями Apple с использованием адаптивной оптимизации, управления рисками и множества стратегий. Тестирование на исторических данных показывает многообещающие результаты, но также и значительные просадки, что указывает на потенциал для дальнейшего совершенствования.
От начального до среднего уровня: Приоритеты операторов От начального до среднего уровня: Приоритеты операторов
Это, несомненно, самый сложный вопрос, который можно объяснить исключительно теоретически. Поэтому я советую вам попрактиковаться с материалами, которые будут показаны здесь. Хотя на первый взгляд всё может показаться простым, данный вопрос с операторами можно понять только на практике в сочетании с постоянным изучением.
Анализируем двоичный код цен на бирже (Часть II): Преобразуем в BIP39 и пишем GPT модель Анализируем двоичный код цен на бирже (Часть II): Преобразуем в BIP39 и пишем GPT модель
Продолжаем попытки дешифровать движения цен... Как насчет лингвистического анализа "словаря рынка", который мы получим, преобразовав бинарный код цены в BIP39? В этой статье мы углубимся в инновационный подход к анализу биржевых данных и рассмотрим, как современные методы обработки естественного языка могут быть применены к языку рынка.
Возможности Мастера MQL5, которые вам нужно знать (Часть 31): Выбор функции потерь Возможности Мастера MQL5, которые вам нужно знать (Часть 31): Выбор функции потерь
Функция потерь (Loss Function) — это ключевая метрика алгоритмов машинного обучения, которая обеспечивает обратную связь для процесса обучения, количественно определяя, насколько хорошо данный набор параметров работает по сравнению с предполагаемым целевым значением. Мы рассмотрим различные форматы этой функции в пользовательском классе Мастера MQL5.