PSAR、平均足、ディープラーニングを組み合わせて取引に活用する
はじめに
優位性を見つけることは、成功と失敗を分ける重要な要素です。利用可能なデータ量が膨大であるため、トレーダーは競争優位を得るために、ディープラーニングのような高度なテクノロジーをますます活用しています。この過程では、従来のテクニカル分析ツールと最新の人工知能(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技術を組み合わせて使用します。
- 畳み込みニューラルネットワーク(CNN):人間の目が画像内のパターンを見つけるのと同じように、シーケンス内のパターンを見つけるのに優れています。
- 長期短期記憶(LSTM)ネットワーク:データの長期的な依存関係を理解するのに優れており、時間の経過に伴う傾向を把握するのに最適です。
- 密結合層:モデルが最終的な予測をおこなうのに役立ちます。
また、モデルが自信過剰になり、データに「過剰適合」するのを防ぐために、ドロップアウト層も追加します。
# 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の結果は、バックテスト期間中の戦略のパフォーマンスに関する貴重な洞察を提供します。初期預金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
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索