103 lines
3.1 KiB
Python
103 lines
3.1 KiB
Python
# Copyright 2023, MetaQuotes Ltd.
| |||
# https://www.mql5.com
| |||
| |||
from datetime import datetime
| |||
| |||
import tensorflow as tf
| |||
import MetaTrader5 as mt5
| |||
import numpy as np
| |||
import pandas as pd
| |||
import tf2onnx
| |||
from sklearn.model_selection import train_test_split
| |||
from tqdm import tqdm
| |||
from sys import argv
| |||
| |||
if not mt5.initialize():
| |||
print("initialize() failed, error code =",mt5.last_error())
| |||
quit()
| |||
| |||
#terminal_info=mt5.terminal_info()
| |||
#file_path=terminal_info.data_path+"\\MQL5\\Files\\"
| |||
| |||
# you code here
| |||
#
| |||
| |||
# we will save generated onnx-file near the our script
| |||
data_path=argv[0]
| |||
last_index=data_path.rfind("\\")+1
| |||
data_path=data_path[0:last_index]
| |||
print("data path to save onnx model",data_path)
| |||
| |||
start_date = datetime(2013, 1, 1, 0)
| |||
end_date = datetime(2024, 1, 1, 0)
| |||
| |||
eurusd_rates = mt5.copy_rates_range("GOLDmicro", mt5.TIMEFRAME_D1, start_date, end_date)
| |||
df = pd.DataFrame(eurusd_rates)
| |||
| |||
| |||
def collect_dataset(df: pd.DataFrame, history_size: int):
| |||
"""
| |||
Collect dataset for the following regression problem:
| |||
- input: history_size consecutive H1 bars;
| |||
- output: close price for the next bar.
| |||
| |||
:param df: H1 bars for a range of dates
| |||
:param history_size: how many bars should be considered for making a prediction
| |||
:return: features and labels
| |||
"""
| |||
n = len(df)
| |||
xs = []
| |||
ys = []
| |||
for i in tqdm(range(n - history_size)):
| |||
w = df.iloc[i: i + history_size + 1]
| |||
| |||
start_ts = w.iloc[-1].time
| |||
end_ts = w.iloc[0].time
| |||
hours_elapsed = (start_ts - end_ts) // 3600
| |||
if hours_elapsed > history_size:
| |||
# skip non-consecutive H1 bars
| |||
continue
| |||
| |||
x = w[['open', 'high', 'low', 'close']].iloc[:-1].values
| |||
y = w.iloc[-1]['close']
| |||
xs.append(x)
| |||
ys.append(y)
| |||
| |||
X = np.array(xs)
| |||
y = np.array(ys)
| |||
return X, y
| |||
| |||
| |||
X, y = collect_dataset(df, history_size=10)
| |||
m = X.mean(axis=1, keepdims=True)
| |||
s = X.std(axis=1, keepdims=True)
| |||
X_norm = (X - m) / s
| |||
y_norm = (y - m[:, 0, 3]) / s[:, 0, 3]
| |||
| |||
X_train, X_test, y_train, y_test = train_test_split(X_norm, y_norm, test_size=0.2, random_state=0)
| |||
| |||
model = tf.keras.Sequential([
| |||
tf.keras.layers.LSTM(64, input_shape=(10, 4)),
| |||
tf.keras.layers.BatchNormalization(),
| |||
tf.keras.layers.Dropout(0.1),
| |||
tf.keras.layers.Dense(32, activation='relu'),
| |||
tf.keras.layers.BatchNormalization(),
| |||
tf.keras.layers.Dropout(0.1),
| |||
tf.keras.layers.Dense(32, activation='relu'),
| |||
tf.keras.layers.Dense(1)
| |||
])
| |||
| |||
model.compile(optimizer='adam', loss='mse', metrics=['mae'])
| |||
| |||
lr_reduction = tf.keras.callbacks.ReduceLROnPlateau(monitor='val_loss', factor=0.1, patience=3, min_delta=1e-6)
| |||
history = model.fit(X_train, y_train, epochs=32, verbose=2, validation_split=0.15, callbacks=[lr_reduction])
| |||
| |||
test_loss, test_mae = model.evaluate(X_test, y_test)
| |||
print(f"test_loss={test_loss:.3f}")
| |||
print(f"test_mae={test_mae:.3f}")
| |||
| |||
output_path = data_path+"BESA_GOLD_D1_SIZE_10_model.onnx"
| |||
onnx_model = tf2onnx.convert.from_keras(model, output_path=output_path)
| |||
print(f"saved model to {output_path}")
| |||
| |||
mt5.shutdown()
|