English Deutsch
preview
PSAR、平均足、ディープラーニングを組み合わせて取引に活用する

PSAR、平均足、ディープラーニングを組み合わせて取引に活用する

MetaTrader 5 | 15 11月 2024, 10:39
37 0
Javier Santiago Gaston De Iriarte Cabrera
Javier Santiago Gaston De Iriarte Cabrera

はじめに

優位性を見つけることは、成功と失敗を分ける重要な要素です。利用可能なデータ量が膨大であるため、トレーダーは競争優位を得るために、ディープラーニングのような高度なテクノロジーをますます活用しています。この過程では、従来のテクニカル分析ツールと最新の人工知能(AI)モデルを組み合わせ、市場状況を迅速に評価し、適応できるようにしています。この可能性を探るために、ディープラーニングの予測能力と実績のある取引指標を融合させたPythonスクリプトを開発しました。このスクリプトは、本格的な取引システムを目指すものではなく、取引戦略が収益性を生む可能性があるかを迅速にテストするための実験的なツールとして設計されています。並行して、MetaTrader 5のスクリプトは、同じ原則を使用して、このアプローチをライブ取引環境に持ち込み、理論的なバックテストと実際の取引アプリケーションのギャップを埋めています。


ディープラーニングONNXモデルの作成

変化の速い外国為替取引の世界では、常に先手を打つことが重要です。そのため、ディープラーニングの力を活用して通貨ペアの動きを予測するPythonスクリプトを開発しました。このエキサイティングなコードを見て回り、魔法のような仕組みを見てみましょう。

私たちのスクリプトは、必要なツールをすべてインポートすることから始まります。複雑なレシピの材料を集めるようなものだと考えてください。私たちは、多くの最新のAIアプリケーションを支える原動力であるTensorFlowやKerasなどのライブラリを使用しています。また、実際の外国為替データを取得するためにMetaTrader 5も使用しています。

import MetaTrader5 as mt5
import tensorflow as tf
import numpy as np
import pandas as pd
import tf2onnx
import keras

次に、重要なパラメータをいくつか設定します。EURUSDペアの120日間の履歴データを調べます。なぜ120日間なのかといえば、これは、モデルに負担をかけずにパターンを見つけるのに十分な履歴が得られる最適な期間だからです。

次に、MetaTrader 5を使用してこのデータを取得します。これは、タイムトラベラーを過去120日間に送り込んで価格情報を収集するようなものです。このデータを取得したら、AIが理解できる形式に整理します。

inp_history_size = 120

sample_size = inp_history_size*3*20
symbol = "EURUSD"
optional = "D1_2024"
inp_model_name = str(symbol)+"_"+str(optional)+".onnx" 

if not mt5.initialize():
    print("initialize() failed, error code =",mt5.last_error())
    quit()

# we will save generated onnx-file near the our script to use as resource
from sys import argv
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)

# and save to MQL5\Files folder to use as file
terminal_info=mt5.terminal_info()
file_path=terminal_info.data_path+"\\MQL5\\Files\\"
print("file path to save onnx model",file_path)

# set start and end dates for history data
from datetime import timedelta, datetime
#end_date = datetime.now()
end_date = datetime(2024, 1, 1, 0)
start_date = end_date - timedelta(days=inp_history_size*20*3)

# print start and end dates
print("data start date =",start_date)
print("data end date =",end_date)

# get rates
eurusd_rates = mt5.copy_rates_from(symbol, mt5.TIMEFRAME_D1, end_date, sample_size)

生の金融データは乱雑になる可能性があるため、クリーンアップします。終値、つまり各取引日の最終価格に焦点を当てます。また、このデータを0から1の範囲にスケーリングします。このステップは、すべてを共通言語に翻訳するのと同じように、AIモデルがより効果的に学習するのに役立つため、非常に重要です。

from sklearn.preprocessing import MinMaxScaler
scaler=MinMaxScaler(feature_range=(0,1))
scaled_data = scaler.fit_transform(data)

モデルにデータを記憶させたくないので、データを訓練データ(80%)とテストデータ(20%)の2つに分割します。これは、AIの学生に学習用の教科書(訓練データ)と、その知識をテストするための別のクイズ(テストデータ)を与えるようなものです。

# scale data
from sklearn.preprocessing import MinMaxScaler
scaler=MinMaxScaler(feature_range=(0,1))
scaled_data = scaler.fit_transform(data)

# training size is 80% of the data
training_size = int(len(scaled_data)*0.80) 
print("Training_size:",training_size)
train_data_initial = scaled_data[0:training_size,:]
test_data_initial = scaled_data[training_size:,:1]

# split a univariate sequence into samples
def split_sequence(sequence, n_steps):
    X, y = list(), list()
    for i in range(len(sequence)):
       # find the end of this pattern
       end_ix = i + n_steps
       # check if we are beyond the sequence
       if end_ix > len(sequence)-1:
          break
       # gather input and output parts of the pattern
       seq_x, seq_y = sequence[i:end_ix], sequence[end_ix]
       X.append(seq_x)
       y.append(seq_y)
    return np.array(X), np.array(y)

# split into samples
time_step = inp_history_size
x_train, y_train = split_sequence(train_data_initial, time_step)
x_test, y_test = split_sequence(test_data_initial, time_step)

# reshape input to be [samples, time steps, features] which is required for LSTM
x_train =x_train.reshape(x_train.shape[0],x_train.shape[1],1)
x_test = x_test.reshape(x_test.shape[0],x_test.shape[1],1)

次は、AIモデルを構築するというエキサイティングな部分です。さまざまなAI技術を組み合わせて使用​​します。

  1.  畳み込みニューラルネットワーク(CNN):人間の目が画像内のパターンを見つけるのと同じように、シーケンス内のパターンを見つけるのに優れています。
  2.  長期短期記憶(LSTM)ネットワーク:データの長期的な依存関係を理解するのに優れており、時間の経過に伴う傾向を把握するのに最適です。
  3.  密結合層:モデルが最終的な予測をおこなうのに役立ちます。

また、モデルが自信過剰になり、データに「過剰適合」するのを防ぐために、ドロップアウト層も追加します。

# define model
from keras.models import Sequential
from keras.layers import Dense, Activation, Conv1D, MaxPooling1D, Dropout, Flatten, LSTM
from keras.metrics import RootMeanSquaredError as rmse
from tensorflow.keras import callbacks
model = Sequential()
model.add(Conv1D(filters=256, kernel_size=2, strides=1, padding='same', activation='relu', input_shape=(inp_history_size,1)))
model.add(MaxPooling1D(pool_size=2))
model.add(LSTM(100, return_sequences = True))
model.add(Dropout(0.3))
model.add(LSTM(100, return_sequences = False))
model.add(Dropout(0.3))
model.add(Dense(units=1, activation = 'sigmoid'))
model.compile(optimizer='adam', loss= 'mse' , metrics = [rmse()])

# Set up early stopping
early_stopping = callbacks.EarlyStopping(
    monitor='val_loss',
    patience=20,
    restore_best_weights=True,
)

# model training for 300 epochs
history = model.fit(x_train, y_train, epochs = 300 , validation_data = (x_test,y_test), batch_size=32, callbacks=[early_stopping], verbose=2)

モデルを訓練するのは、AIを学校に送るようなものです。訓練データを何度も(最大300エポック)表示し、AIは徐々に過去のパターンに基づいて将来の価格を予測することを学習します。AIが長時間学習して学習ではなく記憶を始めないようにするために、「早期停止」と呼ばれるものを使用します。

訓練後、モデルのパフォーマンスをテストします。RMSE(二乗平均平方根誤差)などのメトリクスを使用して、予測が実際の価格にどれだけ近いかを確認します。これは、AIのテスト結果を採点するようなものです。

# evaluate training data
train_loss, train_rmse = model.evaluate(x_train,y_train, batch_size = 32)
print(f"train_loss={train_loss:.3f}")
print(f"train_rmse={train_rmse:.3f}")

# evaluate testing data
test_loss, test_rmse = model.evaluate(x_test,y_test, batch_size = 32)
print(f"test_loss={test_loss:.3f}")
print(f"test_rmse={test_rmse:.3f}")

最後のステップは、訓練済みのモデルをONNX形式に変換することです。ONNXはAIモデルの汎用言語のようなもので、MetaTraderスクリプトを含むさまざまな環境でモデルを使用できるようになります。

# Define a function that represents your model
@tf.function(input_signature=[tf.TensorSpec([None, inp_history_size, 1], tf.float32)])
def model_function(x):
    return model(x)

output_path = data_path+inp_model_name
# Convert the model to ONNX
onnx_model, _ = tf2onnx.convert.from_function(
    model_function, 
    input_signature=[tf.TensorSpec([None, inp_history_size, 1], tf.float32)],
    opset=13,
    output_path=output_path
)

print(f"Saved ONNX model to {output_path}")


# save model to ONNX
output_path = data_path+inp_model_name
onnx_model = tf2onnx.convert.from_keras(model, output_path=output_path)
print(f"saved model to {output_path}")

output_path = file_path+inp_model_name
onnx_model = tf2onnx.convert.from_keras(model, output_path=output_path)
print(f"saved model to {output_path}")

単なる数値の提示にとどまらず、モデルのパフォーマンスを視覚的に示すため、いくつかのグラフも作成します。これらのグラフでは、モデルの学習進捗や、予測と実際の価格の比較などを確認できます。

このスクリプトの最後には、外国為替取引戦略に活用できる高性能なONNXモデルが完成します。このモデルは過去のEUR/USDデータに基づいて訓練されており、将来の価格変動を予測する手助けをします。

このモデルは高精度ですが、万能の予測ツールではない点も忘れないでください。外国為替市場には多くの複雑な要因が影響します。AIの予測は、取引ツールキットのひとつとして活用し、常にリスク管理の重要性を念頭に置いてください。


戦略をテストするPythonスクリプト

このPythonスクリプトは、取引戦略が利益を生む可能性をテストする簡便な手段です。あくまで迅速な実験用ツールであり、本格的な取引システムではありません。ディープラーニングモデルとテクニカル分析を組み合わせることで、複雑な最適化に陥らず、市場での優位性があるかどうかを素早く確認できるように設計されています。

スクリプトはまずMetaTrader 5に接続し、EUR/USDの過去価格データを取得します。1時間ごとのデータを用いて短期的な動きを捉え、平均足ローソク足を適用して市場のノイズを平滑化します。これにより、トレンドがより明確に把握でき、戦略を迅速にテストする際に重要な役割を果たします。さらに、PSAR、SMA、RSI、ATRなどの代表的なテクニカル指標を複数重ねて用います。これらの指標により、市場の状況を迅速に把握し、売買判断に必要なコンテキストをモデルに提供します。

import MetaTrader5 as mt5
import pandas as pd
import numpy as np
import onnxruntime as ort
from sklearn.preprocessing import MinMaxScaler
from ta.trend import PSARIndicator, SMAIndicator
from ta.momentum import RSIIndicator
from ta.volatility import AverageTrueRange
import matplotlib.pyplot as plt

# Inicializar conexión con MetaTrader5
if not mt5.initialize():
    print("Inicialización fallida")
    mt5.shutdown()

def get_historical_data(symbol, timeframe, start_date, end_date):
    rates = mt5.copy_rates_range(symbol, timeframe, start_date, end_date)
    df = pd.DataFrame(rates)
    df['time'] = pd.to_datetime(df['time'], unit='s')
    df.set_index('time', inplace=True)
    return df

def calculate_heikin_ashi(df):
    ha_close = (df['open'] + df['high'] + df['low'] + df['close']) / 4
    ha_open = (df['open'].shift(1) + df['close'].shift(1)) / 2
    ha_high = df[['high', 'open', 'close']].max(axis=1)
    ha_low = df[['low', 'open', 'close']].min(axis=1)
    
    df['ha_close'] = ha_close
    df['ha_open'] = ha_open
    df['ha_high'] = ha_high
    df['ha_low'] = ha_low
    return df

def add_indicators(df):
    # Calcular Heikin Ashi
    df = calculate_heikin_ashi(df)
    
    # PSAR con parámetros ajustados
    psar = PSARIndicator(df['high'], df['low'], df['close'], step=0.02, max_step=0.2)
    df['psar'] = psar.psar()
    
    # Añadir SMA
    sma = SMAIndicator(df['close'], window=50)
    df['sma'] = sma.sma_indicator()
    
    # Añadir RSI
    rsi = RSIIndicator(df['close'], window=14)
    df['rsi'] = rsi.rsi()
    
    # Añadir ATR para medir volatilidad
    atr = AverageTrueRange(df['high'], df['low'], df['close'], window=14)
    df['atr'] = atr.average_true_range()
    
    # Añadir filtro de tendencia simple
    df['trend'] = np.where(df['close'] > df['sma'], 1, -1)
    
    return df

このスクリプトの要となるのは、事前訓練済みのONNXディープラーニングモデルの活用です。データをスケーリングしてモデルに入力することで、市場が次にどの方向に動くかの予測を生成します。この予測とテクニカル指標を組み合わせ、基本的な取引ルールのセットを構築します。たとえば、価格が移動平均を上回り、RSIが買われ過ぎでないことを示している場合、ロングポジションのエントリーチャンスを探します。逆に、条件が反対になればショートポジションのシグナルが出ます。スクリプトにはリスク管理のための適応型ストップロスとテイクプロフィット機能も追加していますが、簡便なテストに重きを置くため、これらの機能は比較的シンプルにとどめています。

def prepare_data(df, window_size=120):
    scaler = MinMaxScaler(feature_range=(0, 1))
    scaled_data = scaler.fit_transform(df[['close']])
    
    X = []
    for i in range(window_size, len(scaled_data)):
        X.append(scaled_data[i-window_size:i])
    
    return np.array(X), scaler

def load_onnx_model(model_path):
    return ort.InferenceSession(model_path)

def predict_with_onnx(model, input_data):
    input_name = model.get_inputs()[0].name
    output_name = model.get_outputs()[0].name
    return model.run([output_name], {input_name: input_data})[0]

def backtest(df, model, scaler, window_size=120, initial_balance=10000):
    scaled_data = scaler.transform(df[['close']])
    
    predictions = []
    for i in range(window_size, len(scaled_data)):
        X = scaled_data[i-window_size:i].reshape(1, window_size, 1)
        pred = predict_with_onnx(model, X.astype(np.float32))
        predictions.append(scaler.inverse_transform(pred.reshape(-1, 1))[0, 0])
    
    df['predictions'] = [np.nan]*window_size + predictions
    
    # Nueva lógica de trading
    df['position'] = 0
    long_condition = (
        (df['close'] > df['predictions']) & 
        (df['close'] > df['psar']) & 
        (df['close'] > df['sma']) & 
        (df['rsi'] < 60) &  # Condición RSI menos estricta
        (df['ha_close'] > df['ha_open']) &
        (df['ha_close'].shift(1) > df['ha_open'].shift(1)) &
        (df['trend'] == 1)  # Solo comprar en tendencia alcista
    )
    short_condition = (
        (df['close'] < df['predictions']) & 
        (df['close'] < df['psar']) & 
        (df['close'] < df['sma']) & 
        (df['rsi'] > 40) &  # Condición RSI menos estricta
        (df['ha_close'] < df['ha_open']) &
        (df['ha_close'].shift(1) < df['ha_open'].shift(1)) &
        (df['trend'] == -1)  # Solo vender en tendencia bajista
    )
    
    df.loc[long_condition, 'position'] = 1  # Compra
    df.loc[short_condition, 'position'] = -1  # Venta
    
    # Implementar stop-loss y take-profit adaptativos
    sl_atr_multiple = 2
    tp_atr_multiple = 3
    
    for i in range(window_size, len(df)):
        if df['position'].iloc[i-1] != 0:
            entry_price = df['close'].iloc[i-1]
            current_atr = df['atr'].iloc[i-1]
            if df['position'].iloc[i-1] == 1:  # Posición larga
                sl_price = entry_price - sl_atr_multiple * current_atr
                tp_price = entry_price + tp_atr_multiple * current_atr
                if df['low'].iloc[i] < sl_price or df['high'].iloc[i] > tp_price:
                    df.loc[df.index[i], 'position'] = 0
            else:  # Posición corta
                sl_price = entry_price + sl_atr_multiple * current_atr
                tp_price = entry_price - tp_atr_multiple * current_atr
                if df['high'].iloc[i] > sl_price or df['low'].iloc[i] < tp_price:
                    df.loc[df.index[i], 'position'] = 0
    
    df['returns'] = df['close'].pct_change()
    df['strategy_returns'] = df['position'].shift(1) * df['returns']
    
    # Calcular balance
    df['cumulative_returns'] = (1 + df['strategy_returns']).cumprod()
    df['balance'] = initial_balance * df['cumulative_returns']
    
    return df

スクリプトは、戦略パフォーマンスの簡易的なスナップショットを提供します。今回のテストでは、合計収益が1.35%に達し、10,000ドルの初期資金が10,135.02ドルになりました。これは大きな利益ではありませんが、肯定的な結果であり、この戦略に一定の可能性があることを示しています。また、リスク調整後の収益の指標として一般的に用いられるシャープレシオは0.39でした。この値から、利益は出たものの、相応のリスクが伴っていたことがわかります。簡易テストの場合、この比率は、戦略をさらに検討する価値があるかどうかの判断材料となります。 

Retorno total: 1.35%
Ratio de Sharpe: 0.39
Balance final: $10135.02

グラフ価格と戦略

平均足

バランス

スクリプトによって生成された視覚化は、戦略パフォーマンスの概要を視覚的にわかりやすく示しています。最初のグラフでは、市場価格、モデルの予測、売買シグナルの相互関係が表示されます。平均足チャートを使用することで市場の動向がより明確に示され、戦略が適切なタイミングで取引を開始・終了しているかどうかを確認するのに役立ちます。最後に、エクイティカーブが時間の経過に伴う口座残高の変動を示し、成長局面とドローダウン局面の両方が視覚的に強調されています。

まとめると、このスクリプトは迅速な実験を目的に設計されており、微調整に深入りせずとも戦略の可能性を評価する手助けをします。戦略が一定の利益を生む可能性を示している一方で、控えめなシャープレシオは改善の余地があることを示しています。したがって、これは実用的な出発点として機能し、戦略をさらに深く開発・最適化する価値があるかどうかを迅速に判断する方法となります。


エキスパートアドバイザー(EA)

このMetaTrader 5スクリプトは、テクニカル分析とディープラーニングモデルを組み合わせて、取引戦略が利益を生む可能性を迅速に検証するために設計されています。ここでの目的は、完璧な取引システムを作成することではなく、従来の取引指標と機械学習を組み合わせることで、市場での優位性を確認することにあります。

このスクリプトはEUR/USD通貨ペアを対象とし、6時間(H6)の時間枠を使用しています。まず、RSI、SMA、PSAR、ATRなどの一般的なテクニカル指標をいくつか設定します。これらの指標は、トレーダーが市場の勢いやトレンドの方向、ボラティリティを把握するために広く利用されているツールです。また、平均足ローソク足を使用し、価格変動を滑らかにして市場のノイズを減らすことでトレンドを把握しやすくしています。この初期設定がスクリプトの基本を形作り、売買の機会を体系的に見つけるための基礎となっています。

int    handleRSI, handleSMA, handlePSAR, handleATR;
double rsiBuffer[], smaBuffer[], psarBuffer[], atrBuffer[];
double haOpen[], haClose[], haHigh[], haLow[];
CTrade trade;
   handleRSI = iRSI(Symbol, Timeframe, RSIPeriod, PRICE_CLOSE);
   handleSMA = iMA(Symbol, Timeframe, SMAPeriod, 0, MODE_SMA, PRICE_CLOSE);
   handlePSAR = iSAR(Symbol, Timeframe, PSARStep, PSARMaximum);
   handleATR = iATR(Symbol, Timeframe, ATRPeriod);

   if(handleRSI == INVALID_HANDLE || handleSMA == INVALID_HANDLE ||
      handlePSAR == INVALID_HANDLE || handleATR == INVALID_HANDLE)
     {
      Print("Error creating indicators");
      return(INIT_FAILED);
     }

   ArraySetAsSeries(rsiBuffer, true);
   ArraySetAsSeries(smaBuffer, true);
   ArraySetAsSeries(psarBuffer, true);
   ArraySetAsSeries(atrBuffer, true);

   ArrayResize(haOpen, 3);
   ArrayResize(haClose, 3);
   ArrayResize(haHigh, 3);
   ArrayResize(haLow, 3);
   ArraySetAsSeries(haOpen, true);
   ArraySetAsSeries(haClose, true);
   ArraySetAsSeries(haHigh, true);
   ArraySetAsSeries(haLow, true);
   IndicatorRelease(handleRSI);
   IndicatorRelease(handleSMA);
   IndicatorRelease(handlePSAR);
   IndicatorRelease(handleATR);

このスクリプトの特徴は、事前訓練済みのONNXディープラーニングモデルを使用している点です。従来のテクニカル分析だけに依存するのではなく、機械学習の予測力を活用して価格変動を予測します。このモデルを取引環境に直接読み込み、最新の市場データを正規化して取り込み、価格が上昇するか、下降するか、または変動しないかを予測します。予測結果は「上昇」「下降」「変動なし」の3カテゴリに分類されます。このようにテクニカル指標と予測を組み合わせることで、スクリプトはより情報に基づいた取引判断をおこなうことを目指しています。

#define SAMPLE_SIZE 120

long     ExtHandle=INVALID_HANDLE;
int      ExtPredictedClass=-1;
datetime ExtNextBar=0;
datetime ExtNextDay=0;
float    ExtMin=0.0;
float    ExtMax=0.0;
CTrade   ExtTrade;
int dlsignal=-1;

//--- price movement prediction
#define PRICE_UP   0
#define PRICE_SAME 1
#define PRICE_DOWN 2

#resource "/Files/EURUSD_D1_2024.onnx" as uchar ExtModel[]
//--- create a model from static buffer
   ExtHandle=OnnxCreateFromBuffer(ExtModel,ONNX_DEFAULT);
   if(ExtHandle==INVALID_HANDLE)
     {
      Print("OnnxCreateFromBuffer error ",GetLastError());
      return(INIT_FAILED);
     }

//--- since not all sizes defined in the input tensor we must set them explicitly
//--- first index - batch size, second index - series size, third index - number of series (only Close)
   const long input_shape[] = {1,SAMPLE_SIZE,1};
   if(!OnnxSetInputShape(ExtHandle,ONNX_DEFAULT,input_shape))
     {
      Print("OnnxSetInputShape error ",GetLastError());
      return(INIT_FAILED);
     }
//--- check new day
   if(TimeCurrent() >= ExtNextDay)
   {
      GetMinMax();
      //--- set next day time
      ExtNextDay = TimeCurrent();
      ExtNextDay -= ExtNextDay % PeriodSeconds(PERIOD_D1);
      ExtNextDay += PeriodSeconds(PERIOD_D1);
   }

   //--- check new bar
   if(TimeCurrent() < ExtNextBar)
      return;
   
   //--- set next bar time
   ExtNextBar = TimeCurrent();
   ExtNextBar -= ExtNextBar % PeriodSeconds();
   ExtNextBar += PeriodSeconds();
   
   //--- check min and max
   float close = (float)iClose(_Symbol, _Period, 0);
   if(ExtMin > close)
      ExtMin = close;
   if(ExtMax < close)
      ExtMax = close;
void PredictPrice(void)
  {
   static vectorf output_data(1);            // vector to get result
   static vectorf x_norm(SAMPLE_SIZE);       // vector for prices normalize

//--- check for normalization possibility
   if(ExtMin>=ExtMax)
     {
      Print("ExtMin>=ExtMax");
      ExtPredictedClass=-1;
      return;
     }
//--- request last bars
   if(!x_norm.CopyRates(_Symbol,_Period,COPY_RATES_CLOSE,1,SAMPLE_SIZE))
     {
      Print("CopyRates ",x_norm.Size());
      ExtPredictedClass=-1;
      return;
     }
   float last_close=x_norm[SAMPLE_SIZE-1];
//--- normalize prices
   x_norm-=ExtMin;
   x_norm/=(ExtMax-ExtMin);
//--- run the inference
   if(!OnnxRun(ExtHandle,ONNX_NO_CONVERSION,x_norm,output_data))
     {
      Print("OnnxRun");
      ExtPredictedClass=-1;
      return;
     }
//--- denormalize the price from the output value
   float predicted=output_data[0]*(ExtMax-ExtMin)+ExtMin;
//--- classify predicted price movement
   float delta=last_close-predicted;
   if(fabs(delta)<=0.00001)
      ExtPredictedClass=PRICE_SAME;
   else
     {
      if(delta<0)
         ExtPredictedClass=PRICE_UP;
      else
         ExtPredictedClass=PRICE_DOWN;
     }
  }



//+------------------------------------------------------------------+
//| Gets Min and Max values                                          |
//+------------------------------------------------------------------+
void GetMinMax(void)
  {
   vectorf close;
   close.CopyRates(_Symbol,PERIOD_D1,COPY_RATES_CLOSE,0,SAMPLE_SIZE);
   ExtMin=close.Min();
   ExtMax=close.Max();
  }

取引ロジックは単純でありながら洗練されており、買い(ロング)シグナルの条件を複数の要素で確認します。具体的には、現在の価格がPSARとSMAの両方を上回り、RSIが60未満で(市場が買われ過ぎていないことを示唆)、平均足ローソク足が上昇傾向を示している必要があります。さらに、ディープラーニングモデルも価格の上昇を予測していることが必須条件です。これらすべての要素が揃ったとき、スクリプトは買いポジションを開きます。売り(ショート)ポジションのエントリー条件も同様に、反対の要素が揃ったときに成立します。この階層化アプローチにより、ノイズや誤ったシグナルを排除し、複数の指標とモデルが方向について一致した場合のみ取引がおこなわれるように設計されています。

void CalculateHeikinAshi()
{
   double close[], open[], high[], low[];
   ArraySetAsSeries(close, true);
   ArraySetAsSeries(open, true);
   ArraySetAsSeries(high, true);
   ArraySetAsSeries(low, true);

   int copied = CopyClose(Symbol(), Timeframe, 0, 3, close);
   copied = MathMin(copied, CopyOpen(Symbol(), Timeframe, 0, 3, open));
   copied = MathMin(copied, CopyHigh(Symbol(), Timeframe, 0, 3, high));
   copied = MathMin(copied, CopyLow(Symbol(), Timeframe, 0, 3, low));

   if(copied < 3)
   {
      Print("Not enough data for Heikin Ashi calculation");
      return;
   }

   // Calculate Heikin Ashi values for the last 3 candles
   for(int i = 2; i >= 0; i--)
   {
      haClose[i] = (open[i] + high[i] + low[i] + close[i]) / 4;
      
      if(i == 2)
      {
         haOpen[i] = (open[i] + close[i]) / 2;
      }
      else
      {
         haOpen[i] = (haOpen[i+1] + haClose[i+1]) / 2;
      }
      
      haHigh[i] = MathMax(high[i], MathMax(haOpen[i], haClose[i]));
      haLow[i] = MathMin(low[i], MathMin(haOpen[i], haClose[i]));
   }

   // Debug print
   Print("Heikin Ashi values:");
   for(int i = 0; i < 3; i++)
   {
      Print("Candle ", i, ": Open=", haOpen[i], " High=", haHigh[i], " Low=", haLow[i], " Close=", haClose[i]);
   }
}
   // Copy indicator data
   if(CopyBuffer(handleRSI, 0, 0, 3, rsiBuffer) <= 0 ||
      CopyBuffer(handleSMA, 0, 0, 3, smaBuffer) <= 0 ||
      CopyBuffer(handlePSAR, 0, 0, 3, psarBuffer) <= 0 ||
      CopyBuffer(handleATR, 0, 0, 3, atrBuffer) <= 0)
   {
      Print("Failed to copy indicator data");
      return;
   }

   CalculateHeikinAshi();

   if(ArraySize(haOpen) < 3 || ArraySize(haClose) < 3)
   {
      Print("Not enough Heikin Ashi data");
      return;
   }

   PredictPrice();
   int trend = GetTrend();

   bool longCondition = close > psarBuffer[1] &&
                        close > smaBuffer[1] &&
                        rsiBuffer[1] < 60 &&
                        haClose[1] > haOpen[1] &&
                        haClose[2] > haOpen[2] &&
                        ExtPredictedClass == PRICE_UP &&
                        trend == 1;

   bool shortCondition = close < psarBuffer[1] &&
                         close < smaBuffer[1] &&
                         rsiBuffer[1] > 40 &&
                         haClose[1] < haOpen[1] &&
                         haClose[2] < haOpen[2] &&
                         ExtPredictedClass == PRICE_DOWN &&
                         trend == -1;

   // Debug printing
   Print("--- Debug Info ---");
   Print("Close: ", close, " PSAR: ", psarBuffer[1], " SMA: ", smaBuffer[1]);
   Print("RSI: ", rsiBuffer[1], " HA Close[1]: ", haClose[1], " HA Open[1]: ", haOpen[1]);
   Print("HA Close[2]: ", haClose[2], " HA Open[2]: ", haOpen[2]);
   Print("ExtPredictedClass: ", ExtPredictedClass, " Trend: ", trend);
   Print("Long Condition: ", longCondition, " Short Condition: ", shortCondition);

   if(longCondition)
   {
      Print("Long Condition Met");
      if(PositionsTotal() == 0)
      {
         double atrStopLoss = SLATRMultiple * atrBuffer[1];
         double minStopLoss = GetMinimumStopLoss();
         double sl = close - MathMax(atrStopLoss, minStopLoss);
         double tp = close + TPATRMultiple * atrBuffer[1];
         
         bool result = trade.Buy(0.01, Symbol(), 0, sl, tp);
         if(result)
            Print("Buy order placed successfully. SL: ", sl, " TP: ", tp);
         else
            Print("Failed to place buy order. Error: ", GetLastError());
      }
      else
      {
         Print("Position already open. Skipping buy order.");
      }
   }
   else if(shortCondition)
   {
      Print("Short Condition Met");
      if(PositionsTotal() == 0)
      {
         double atrStopLoss = SLATRMultiple * atrBuffer[1];
         double minStopLoss = GetMinimumStopLoss();
         double sl = close + MathMax(atrStopLoss, minStopLoss);
         double tp = close - TPATRMultiple * atrBuffer[1];
         
         bool result = trade.Sell(0.01, Symbol(), 0, sl, tp);
         if(result)
            Print("Sell order placed successfully. SL: ", sl, " TP: ", tp);
         else
            Print("Failed to place sell order. Error: ", GetLastError());
      }
      else
      {
         Print("Position already open. Skipping sell order.");
      }
   }
   
   ManageTrailingStop();
}

スクリプトにはリスク管理も組み込まれており、ATR (Average True Range)を用いて動的なストップロスとテイクプロフィットのレベルを設定しています。これにより、市場のボラティリティに応じたリスク調整が可能となり、変化する市場状況に適応しながら、損失を最小限に抑えつつ利益を確保することを目指しています。さらに、トレーリングストップメカニズムも搭載されており、取引が有利な方向に進む際にはストップロスが自動で調整され、利益をさらに保護できるようになっています。

double GetMinimumStopLoss()
{
   double spread = SymbolInfoInteger(Symbol(), SYMBOL_SPREAD) * SymbolInfoDouble(Symbol(), SYMBOL_POINT);
   double minStopLevel = SymbolInfoInteger(Symbol(), SYMBOL_TRADE_STOPS_LEVEL) * SymbolInfoDouble(Symbol(), SYMBOL_POINT);
   return MathMax(spread * 2, minStopLevel); // Usamos el doble del spread como mínimo
}

// Agregar esta función para manejar el trailing stop
void ManageTrailingStop()
{
   for(int i = PositionsTotal() - 1; i >= 0; i--)
   {
      if(PositionSelectByTicket(PositionGetTicket(i)))
      {
         if(PositionGetString(POSITION_SYMBOL) == Symbol())
         {
            double positionOpenPrice = PositionGetDouble(POSITION_PRICE_OPEN);
            double currentStopLoss = PositionGetDouble(POSITION_SL);
            double currentTakeProfit = PositionGetDouble(POSITION_TP);
            
            if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
            {
               double newStopLoss = NormalizeDouble(SymbolInfoDouble(Symbol(), SYMBOL_BID) - TrailingStop * SymbolInfoDouble(Symbol(), SYMBOL_POINT), Digits());
               if(newStopLoss > currentStopLoss + TrailingStep * SymbolInfoDouble(Symbol(), SYMBOL_POINT))
               {
                  if(trade.PositionModify(PositionGetTicket(i), newStopLoss, currentTakeProfit))
                  {
                     Print("Trailing stop ajustado para posición larga. Nuevo SL: ", newStopLoss);
                  }
               }
            }
            else if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL)
            {
               double newStopLoss = NormalizeDouble(SymbolInfoDouble(Symbol(), SYMBOL_ASK) + TrailingStop * SymbolInfoDouble(Symbol(), SYMBOL_POINT), Digits());
               if(newStopLoss < currentStopLoss - TrailingStep * SymbolInfoDouble(Symbol(), SYMBOL_POINT))
               {
                  if(trade.PositionModify(PositionGetTicket(i), newStopLoss, currentTakeProfit))
                  {
                     Print("Trailing stop ajustado para posición corta. Nuevo SL: ", newStopLoss);
                  }
               }
            }
         }
      }
   }
}

要するに、このスクリプトは、取引戦略の実行可能性を迅速にテストするための実験的なツールとして機能します。完璧なソリューションを目指すのではなく、テクニカル分析とディープラーニングモデルを組み合わせることで、収益性のある取引機会を見つけられるかどうかを検証することを目的としています。利益を保証するものではありませんが、人間の直感と機械学習の洞察を融合させることで、より精緻で情報に基づいた取引決定をサポートするアプローチを提供します。


結果

設定

入力

バックテスト

グラフEA

EAの結果は、バックテスト期間中の戦略のパフォーマンスに関する貴重な洞察を提供します。初期預金10,000ドルからスタートし、この戦略は17ユニットの控えめな純利益を達成しました。これは、利益は生まれたものの、初期投資に対して比較的小さいものであったことを示しています。バランスカーブはこの緩やかな成長を反映しており、時間が経過するにつれて、着実に上昇する傾向を示しています。

注目すべきメトリクスの1つは、4.39のプロフィットファクターです。これは堅実な数字で、リスク1単位に対して戦略が4.39単位のリターンを得たことを示しています。この数字は、EAが損失に対して利益を最大化するのに効果的であったことを示唆しています。また、4.48の回復係数もこれを裏付けており、戦略がドローダウンから効果的に回復したことを示しています。これらの指標は、EAが堅牢であることを示しています。しかし、5.25のシャープレシオは注目に値します。この比率は比較的高いものの、全体的な利益が小さいため、戦略が取るリスクは非常に少なく、絶対的な利益が限定的である可能性を示唆しています。

取引統計を詳しく見ると、EAは合計16回の取引を実行し、ロングポジションとショートポジションはほぼ均等に分かれています。買い取引の勝率は62.5%と高く、売り取引の勝率は37.5%でした。この差は、戦略が上昇市場を捉えることに成功したことを示しています。興味深いことに、利益を出した取引は全体の50%を占めており、EAの収益性は勝率の高さではなく、勝者と敗者の規模に基づいていることがわかります。最大利益取引と平均利益取引がともにプラスであり、市場の好調時にEAが利益を確保できたことを示しています。バランスカーブもこれらの結果を反映しており、ドローダウン後に利益が続くことから、慎重かつ着実な取引アプローチが見て取れます。

ドローダウンメトリックメトリクスは、EAが低いリスクエクスポージャーを維持していることを示しています。残高ドローダウンの絶対値は2ユニットで非常に小さく、相対ドローダウンは0.02%というわずかな値でした。この極めて低いドローダウンは、EAの戦略が保守的で、積極的な利益追求よりも資本保全を優先していることを示唆しています。この保守的アプローチは、不安定な市場で有益ですが、その結果として純利益が控えめであることが説明されています。

要約すると、このEAは、慎重で体系的な取引アプローチを通じて、高いプロフィットファクターを維持しつつ、ドローダウンを最小限に抑えることに成功しています。総利益は比較的小さいものの、高いプロフィットファクターと低いドローダウンは、急速な利益創出よりもリスク管理を重視した戦略を示しています。そのため、このEAは、積極的で高リスクな戦略よりも、着実で低リスクな成長を重視するトレーダーに適しています。ただし、より高い絶対利益を目指すトレーダーには、EAの堅実なリスク管理フレームワークを維持しながら、利益の可能性を高めるために、調整や強化が必要かもしれません。


結論

ディープラーニングモデルと従来のテクニカル分析を統合したアプローチには、有望な結果が見られました。Pythonスクリプトを通じて、履歴データ、主要な指標、機械学習を活用した体系的な手法が、潜在的な取引機会の特定にどのように貢献できるかを確認しました。結果は控えめではありますが一貫性があり、このアプローチが制約があるものの、肯定的な結果をもたらす可能性を示唆しています。また、MetaTrader 5のEAも、この慎重な取引アプローチの有効性を確認する結果となりました。バックテスト中のEAのパフォーマンスは、最小限のドローダウンで小規模ながら着実な成長を示し、利益の最大化ではなく、リスク管理に重点を置いた戦略を強調しています。この保守的なアプローチは、着実で低リスクの成長を重視するトレーダーにとって魅力的です。しかし、より高いリターンを目指すトレーダーにとっては、さらなる改良と最適化が求められるでしょう。最終的に、これらのツールは、トレーダーが人間の直感とAIによる分析力を融合させ、複雑な外国為替市場をナビゲートするための出発点となる基盤を提供します。これにより、トレーダーは微妙かつ情報に基づいたアプローチで取引の意思決定をおこなうことができます。

MetaQuotes Ltdにより英語から翻訳されました。
元の記事: https://www.mql5.com/en/articles/15868

添付されたファイル |
EURUSD_D1_2024.onnx (884.41 KB)
004.py (8.06 KB)
PHD_Final.mq5 (27.7 KB)
005.py (8.14 KB)
EAのサンプル EAのサンプル
一般的なMACDを使ったEAを例として、MQL4開発の原則を紹介します。
MQL5で取引管理者パネルを作成する(第3回):ビジュアルスタイリングによるGUIの強化(I) MQL5で取引管理者パネルを作成する(第3回):ビジュアルスタイリングによるGUIの強化(I)
この記事では、MQL5を使用して、取引管理パネルのグラフィカルユーザーインターフェイス(GUI)を視覚的にスタイル設定することに焦点を当てます。MQL5で利用できるさまざまなテクニックと機能について説明します。これらのテクニックと機能により、インターフェイスのカスタマイズと最適化が可能になり、魅力的な外観を維持しながらトレーダーのニーズを満たすことができます。
エラー 146 (「トレードコンテキスト ビジー」) と、その対処方法 エラー 146 (「トレードコンテキスト ビジー」) と、その対処方法
この記事では、MT4において複数のEAの衝突をさける方法を扱います。ターミナルの操作、MQL4の基本的な使い方がわかる人にとって、役に立つでしょう。
MQL5エキスパートアドバイザーに自動最適化を実装する方法 MQL5エキスパートアドバイザーに自動最適化を実装する方法
エキスパートアドバイザー(EA)のためのMQL5の自動最適化のためのステップバイステップガイド。堅牢な最適化ロジック、パラメーター選択のベストプラクティス、バックテストを通じた戦略の再構築方法について解説します。さらに、ウォークフォワード最適化などの高レベルな手法を紹介し、取引アプローチの強化を目指します。