【自動売買を作ろう】FX自動売買プログラム編 #3
こんにちは!
エルフィールドでエンジニアとして働いている、T.Hと申します。
本シリーズでは、MQL5を使ってFXの自動売買プログラム(EA)を作成していく過程を紹介しています。
第3回となる今回は、実際にEAのプログラミングを行っていきます。
📅 シリーズ構成(全4回)
1. 第1回:自動売買プログラムの概要
2. 第2回:環境構築
3. 第3回:プログラミング(本記事)
4. 第4回:検証(過去の相場データで自動売買を実施)
🧠 今回作成するEAの仕様
今回作成するEAは、200SMAを基準にした逆張りロジックです。
- 上昇トレンド中に一時的に下降し、200SMAに「長い下髭」をつけてタッチしたら買いエントリー
- 下降トレンド中に一時的に上昇し、200SMAに「長い上髭」をつけてタッチしたら売りエントリー
※自動売買プログラムは、英語でExpert Advisorと呼ばれ、本記事ではEAと略します
🛠 テンプレートファイルを開く
まずは、前回作成したEAのテンプレートファイルを開きます。
1.MT5を起動
2.メニューから「ツール」→「MetaQuotes言語エディタ」 を選択
3.作成済みのEAテンプレートを開く
EAには、あらかじめ以下の3つの関数が用意されています。
🔧 EAの基本構造
OnInit()
EAがチャートに適用されたときに最初に1回だけ実行されます。
インジケーターの設定や初期化処理を行います。今回は 200SMA(単純移動平均線) を使用するため、
ここでSMAの設定を行います。
// 200SMAのハンドル
int smaHandle;
int OnInit()
{
smaHandle = iMA(_Symbol, _Period, 200, 0, MODE_SMA, PRICE_CLOSE);
return(INIT_SUCCEEDED);
}
現在表示しているチャート(通貨ペア・時間足)に対応する、200SMAのハンドルを取得しています。
OnDeinit()
EAが削除されたときや、MT5を終了するときに実行されます。
今回は特別な終了処理を行わないため、中身は空のままです。
OnTick()
価格(ティック)が更新されるたびに実行されるメイン処理です。
売買ロジックの判定や注文処理は、すべてこの関数内に記述します。
📈 200SMAにタッチしたかの判定
まずは、1本前のローソク足の情報を取得します。
最新のローソク足は未確定のため、確定済みの1本前を使用します。
double open = iOpen(_Symbol, _Period, 1);
double close = iClose(_Symbol, _Period, 1);
double high = iHigh(_Symbol, _Period, 1);
double low = iLow(_Symbol, _Period, 1);
続いて、同じローソク足に対応する200SMAの値を取得します。
double smaValue[];
CopyBuffer(smaHandle, 0, 1, 1, smaValue);
double smaVal = smaValue[0];
※実装例では、念のためインジケーターの値が取得できなかった場合に備えて、
CopyBuffer() の戻り値チェックを追加しています
高値と安値がSMAを跨いでいなければ、SMAにタッチしていないと判断して処理を終了します。
if(high < smaVal || low > smaVal)
{
return;
}
🕯 長い髭の判定
次に、SMAにタッチしたローソク足に
「長い髭」 が発生しているかを確認します。
長い髭の定義
・実体の 1.5倍以上
・反対側の髭よりも長い
さらに、勢いよくSMAにタッチした足だけを対象にするため、
直近10本のローソク足の平均値幅より 1.5倍以上 あることも条件に加えます。
(※この部分のロジック・コードは掲載用として問題ないため、割愛せずそのまま使用しています)
📊 200SMAタッチ前の値動き確認
最後に、トレンド条件を確認します。
● ロング(下髭): 直前10本が SMAより上で推移
● ショート(上髭):直前10本が SMAより下で推移
これにより、「トレンド中の一時的な押し・戻し」だけを狙います。
条件を満たさない場合はエントリーしません。
💰 エントリー(注文処理)
すべての条件を満たした場合のみ、エントリーを行います。
エントリールール
ロット数:0.1 固定
ロング(買い)
・SL:安値 − 10pips
・TP:SLと同じ値幅
ショート(売り)
・SL:高値 + 10pips
・TP:SLと同じ値幅
未決済ポジションがある場合は、新規エントリーを行いません。
✅ 完成したEAコード
最終的に完成したプログラムが以下です。
Test_SMA_Touch.mq5
// エントリー実行用クラスをinclude
#include <Trade/Trade.mqh>
// 200SMAのハンドル
int smaHandle;
int OnInit()
{
smaHandle = iMA(_Symbol, _Period, 200, 0, MODE_SMA, PRICE_CLOSE);
return(INIT_SUCCEEDED);
}
void OnDeinit(const int reason)
{
}
void OnTick()
{
// 1本前のローソク足取得
double open = iOpen(_Symbol, _Period, 1);
double close = iClose(_Symbol, _Period, 1);
double high = iHigh(_Symbol, _Period, 1);
double low = iLow(_Symbol, _Period, 1);
// 1本前のローソク足に対応するSMAを取得
double smaValue[];
if(CopyBuffer(smaHandle, 0, 1, 1, smaValue) <= 0)
{
return;
}
double smaVal = smaValue[0];
// 1本前のローソク足がSMAにタッチしているか確認
if(high < smaVal || low > smaVal)
{
return;
}
// 各長さを計算
double upperWick = high – MathMax(open, close);
double lowerWick = MathMin(open, close) – low;
double body = MathAbs(close – open);
// 上髭・下髭の判定
bool hasUpperWick = (upperWick > body * 1.5) && (upperWick > lowerWick);
bool hasLowerWick = (lowerWick > body * 1.5) && (lowerWick > upperWick);
if(!hasUpperWick && !hasLowerWick)
{
return;
}
// 200SMAタッチ前の過去10本分のローソク足を取得
MqlRates rates[10];
int touch_index = 1;
if(CopyRates(_Symbol, _Period, touch_index + 1, 10, rates) < 10)
{
return;
}
// 過去10本分のローソク足の平均値幅を計算
double totalSize = 0.0;
for(int i = 0; i < ArraySize(rates); i++)
{
totalSize += rates[i].high – rates[i].low;
}
// 平均の1.5倍以上あるか
if((high – low) < (totalSize / ArraySize(rates) * 1.5))
{
return;
}
// 200SMAタッチ前はSMAの上(下)で推移していたか
bool isEntry = true;
for(int i = 0; i < ArraySize(rates); i++) { // ショート条件(上髭) if(hasUpperWick) { if(rates[i].high > smaVal)
{
isEntry = false;
break;
}
}
// ロング条件(下髭)
if(hasLowerWick)
{
if(rates[i].low < smaVal)
{
isEntry = false;
break;
}
}
}
if(!isEntry)
{
return;
}
// 未決済ポジションがある場合はエントリーしない
if(PositionsTotal() > 0)
{
return;
}
// 1pipsを算出
double point = _Point;
double pip = point * 10;
// エントリー価格
double entryPrice = hasUpperWick
? SymbolInfoDouble(_Symbol, SYMBOL_BID)
: SymbolInfoDouble(_Symbol, SYMBOL_ASK);
// エントリー実行用クラス
CTrade trade;
trade.SetExpertMagicNumber(100000);
trade.SetDeviationInPoints(5);
// ショートエントリー
if(hasUpperWick)
{
double stopLoss = high + pip * 10;
if(MathAbs(stopLoss – entryPrice) < pip * 20)
{
return;
}
double takeProfit = entryPrice - (stopLoss - entryPrice);
trade.Sell(0.1, _Symbol, 0, stopLoss, takeProfit);
}
// ロングエントリー
if(hasLowerWick)
{
double stopLoss = low – pip * 10;
if(MathAbs(stopLoss – entryPrice) < pip * 20)
{
return;
}
double takeProfit = entryPrice + (entryPrice - stopLoss);
trade.Buy(0.1, _Symbol, 0, stopLoss, takeProfit);
}
}
✨ まとめ
今回は、MQL5を使って200SMAタッチ+長い髭を条件としたEA を実装しました。
- インジケーターの取得方法
- ローソク足の判定方法
- エントリー条件の組み立て
といった、EA開発の基本が詰まった内容になっています。
次回はいよいよ、過去データを使った検証(バックテスト) を行います。
お楽しみに!👋