MQL4MQL5 EA(エキスパートアドバイザー) 売買サイン・相場の判定
ゴールデンクロス・デッドクロスで売買したい
結論:短期 MA と長期 MA の値を「2本前」と「1本前」で比べ、上下が入れ替わったらクロスと判定します。
考え方
ゴールデンクロスは「短期の移動平均線が長期の移動平均線を下から上へ抜けること」、デッドクロスはその逆です。判定は、確定した2本の足(2本前と1本前)で「2本前は短期 ≦ 長期、1本前は短期 > 長期」ならゴールデンクロスとします。ここでは、ゴールデンクロスで買い・デッドクロスで売りに切り替える(ドテンする)EA を例にしています。
- 短期 MA と長期 MA の値を、1本前と2本前の2本分取得する(MQL5 はハンドルと CopyBuffer)
- 2本前と1本前で上下関係が入れ替わったかを比べる
- ゴールデンクロスなら売りポジションを決済して買い、デッドクロスなら買いを決済して売る
- 同じ足で何度も注文しないよう、新しい足ができたときだけ判定する
そのまま使えるコード
MetaEditor の「新規作成」で「エキスパートアドバイザ(テンプレート)」を選び、できたファイルの中身をすべてこのコードに置き換えてコンパイル(F7)します。ファイルは Experts フォルダに保存され、ナビゲーターからチャートにドラッグして使います。
MQL4(MT4)
#property strict
// ゴールデンクロスで買い・デッドクロスで売り(ドテン)の EA(MT4)
input int InpFast = 5; // 短期 MA の期間
input int InpSlow = 20; // 長期 MA の期間
input double InpLots = 0.1; // ロット
input int InpMagic = 1001; // マジックナンバー
datetime lastBar = 0;
int OnInit()
{
if(InpFast >= InpSlow) return INIT_PARAMETERS_INCORRECT;
return INIT_SUCCEEDED;
}
// この EA のポジションを、指定した種類だけ決済する
void ClosePositions(int type)
{
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
if(!OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) continue;
if(OrderSymbol() != _Symbol || OrderMagicNumber() != InpMagic || OrderType() != type) continue;
if(!OrderClose(OrderTicket(), OrderLots(), OrderClosePrice(), 30))
Print("決済失敗 error=", GetLastError());
}
}
int CountPositions(int type)
{
int n = 0;
for(int i = OrdersTotal() - 1; i >= 0; i--)
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES) && OrderSymbol() == _Symbol
&& OrderMagicNumber() == InpMagic && OrderType() == type)
n++;
return n;
}
void OnTick()
{
// 新しい足ができたときだけ判定する
datetime t = iTime(_Symbol, PERIOD_CURRENT, 0);
if(t == lastBar) return;
lastBar = t;
double fast1 = iMA(_Symbol, 0, InpFast, 0, MODE_SMA, PRICE_CLOSE, 1);
double fast2 = iMA(_Symbol, 0, InpFast, 0, MODE_SMA, PRICE_CLOSE, 2);
double slow1 = iMA(_Symbol, 0, InpSlow, 0, MODE_SMA, PRICE_CLOSE, 1);
double slow2 = iMA(_Symbol, 0, InpSlow, 0, MODE_SMA, PRICE_CLOSE, 2);
bool golden = (fast2 <= slow2 && fast1 > slow1);
bool dead = (fast2 >= slow2 && fast1 < slow1);
if(golden)
{
ClosePositions(OP_SELL);
if(CountPositions(OP_BUY) == 0)
if(OrderSend(_Symbol, OP_BUY, InpLots, Ask, 30, 0, 0, "GC", InpMagic, 0, clrBlue) < 0)
Print("買い失敗 error=", GetLastError());
}
if(dead)
{
ClosePositions(OP_BUY);
if(CountPositions(OP_SELL) == 0)
if(OrderSend(_Symbol, OP_SELL, InpLots, Bid, 30, 0, 0, "DC", InpMagic, 0, clrRed) < 0)
Print("売り失敗 error=", GetLastError());
}
}MQL5(MT5)
// ゴールデンクロスで買い・デッドクロスで売り(ドテン)の EA(MT5)
#include <Trade\Trade.mqh>
input int InpFast = 5; // 短期 MA の期間
input int InpSlow = 20; // 長期 MA の期間
input double InpLots = 0.1; // ロット
input ulong InpMagic = 1001; // マジックナンバー
CTrade trade;
int hFast = INVALID_HANDLE, hSlow = INVALID_HANDLE;
datetime lastBar = 0;
int OnInit()
{
if(InpFast >= InpSlow) return INIT_PARAMETERS_INCORRECT;
hFast = iMA(_Symbol, PERIOD_CURRENT, InpFast, 0, MODE_SMA, PRICE_CLOSE);
hSlow = iMA(_Symbol, PERIOD_CURRENT, InpSlow, 0, MODE_SMA, PRICE_CLOSE);
if(hFast == INVALID_HANDLE || hSlow == INVALID_HANDLE) return INIT_FAILED;
trade.SetExpertMagicNumber(InpMagic);
return INIT_SUCCEEDED;
}
void OnDeinit(const int reason)
{
IndicatorRelease(hFast);
IndicatorRelease(hSlow);
}
// この EA のポジションを、指定した向きだけ決済する
void ClosePositions(ENUM_POSITION_TYPE type)
{
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(ticket == 0) continue;
if(PositionGetString(POSITION_SYMBOL) != _Symbol || (ulong)PositionGetInteger(POSITION_MAGIC) != InpMagic) continue;
if(PositionGetInteger(POSITION_TYPE) != type) continue;
if(!trade.PositionClose(ticket))
Print("決済失敗 ", trade.ResultRetcodeDescription());
}
}
int CountPositions(ENUM_POSITION_TYPE type)
{
int n = 0;
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
if(PositionGetTicket(i) == 0) continue;
if(PositionGetString(POSITION_SYMBOL) == _Symbol && (ulong)PositionGetInteger(POSITION_MAGIC) == InpMagic
&& PositionGetInteger(POSITION_TYPE) == type)
n++;
}
return n;
}
void OnTick()
{
// 新しい足ができたときだけ判定する
datetime t = iTime(_Symbol, PERIOD_CURRENT, 0);
if(t == lastBar) return;
double fast[], slow[];
ArraySetAsSeries(fast, true); // [0]=1本前、[1]=2本前
ArraySetAsSeries(slow, true);
if(CopyBuffer(hFast, 0, 1, 2, fast) < 2 || CopyBuffer(hSlow, 0, 1, 2, slow) < 2)
return; // まだ計算中なら次のティックで再挑戦
lastBar = t;
bool golden = (fast[1] <= slow[1] && fast[0] > slow[0]);
bool dead = (fast[1] >= slow[1] && fast[0] < slow[0]);
if(golden)
{
ClosePositions(POSITION_TYPE_SELL);
if(CountPositions(POSITION_TYPE_BUY) == 0 && !trade.Buy(InpLots, _Symbol, 0, 0, 0, "GC"))
Print("買い失敗 ", trade.ResultRetcodeDescription());
}
if(dead)
{
ClosePositions(POSITION_TYPE_BUY);
if(CountPositions(POSITION_TYPE_SELL) == 0 && !trade.Sell(InpLots, _Symbol, 0, 0, 0, "DC"))
Print("売り失敗 ", trade.ResultRetcodeDescription());
}
}MT4 版・MT5 版とも MetaEditor でコンパイル(エラー・警告なし)を確認済みです。実際の口座で使う前に、デモ口座やストラテジーテスターで動作を確かめてください。
カスタマイズのポイント
- EMA(指数平滑移動平均)にしたいときは MODE_SMA を MODE_EMA に変えます。
- 注文せずにアラートだけ出したいときは、注文の部分を Alert() に置き換えます。
- 損切り・利確を付けたいときは、「損切り・利確を pips で指定する」レシピの計算を組み合わせます。
注意点
- 現在の足(shift=0)で判定すると、足の途中でクロスしたり戻ったりしてサインが何度も出ます。確定足で判定するのが基本です。
- MT5 で iMA() を OnTick() の中で毎回呼ぶと重くなります。ハンドルは OnInit() で1回だけ作ります。
このコードで使っている関数
うまく動かないときに出やすいエラー
あわせて読みたいレシピ
- チャートに売買サイン(矢印)を出したいインジケーターのバッファを2本(買い・売り)用意し、描画を DRAW_ARROW にして、サインの足だけ価格を入れます。
- 新しい足ができたときだけ処理したい今の足の開始時刻 iTime(_Symbol, PERIOD_CURRENT, 0) を前回の値と比べ、変わったら新しい足と判定します。
- ポジションは1つだけにしたい(重複エントリーを防ぎたい)注文の前に、この EA のポジションがすでにあるかを数え、あれば注文しないようにします。
- 損切り・利確を pips で指定して注文したい1pips の大きさ(5桁・3桁口座は _Point × 10)を求め、損切り・利確の価格を計算して NormalizeDouble() で丸めます。












