本文へスキップ
EA開発・自作

MQL5の基本構造|OnInit・OnTick・注文関数を最小コードで理解

MQL5でEAを書くときに必ず登場するOnInit・OnTick・OnDeinitの役割と、注文を出すCTrade/MqlTradeRequestの使い方を、動く最小サンプルコードで解説します。

執筆: Redington 公開: 最終更新:

PR当ページにはプロモーション(アフィリエイト広告)が含まれます。順位・評価は公開の評価基準に基づき編集部が独立して決定しています。EAの成績はすべて過去の検証結果であり、将来の利益を保証するものではありません。海外FX業者は金融庁未登録です。

この記事の目次
  1. MQL5プログラムの3つの型
  2. イベントハンドラ一覧と、最初に使う3つ
  3. 注文を出す2つの方法
    1. 方法1: MqlTradeRequest 構造体を直接組む(低レベル)
    2. 方法2: CTrade クラスを使う(標準ライブラリ)
  4. 最小サンプル:移動平均クロスEA
    1. コードの読みどころ
  5. このコードに足りないもの(実運用との距離)
  6. MQL4との違いを1枚で
  7. よくある失敗
  8. まとめ

MQL5のEAは、覚えるべき骨格がとても小さい言語です。実際には「起動時に1回呼ばれる OnInit()」「値動きのたびに呼ばれる OnTick()」「終了時に呼ばれる OnDeinit()」の3つを押さえ、そこに注文の出し方を1種類足せば、最小限のEAは完成します。この記事では、その骨格と最小サンプルコードを示し、初学者がつまずきやすい箇所を先回りして整理します。

✅ 結論

MQL5のEAは「イベントハンドラ(OnInit / OnTick / OnDeinit)」+「注文処理」の2層で理解できます。注文は標準ライブラリの CTrade クラスを使えば数行で書けます。MQL4のコードは移植ではなく書き直しが必要です。学習用コードをそのまま実口座で動かさないでください。

MQL5プログラムの3つの型

MetaEditorで新規作成すると、作るものを3種類から選びます。最初に混乱しやすいので、役割を分けて覚えます。

種別

拡張子

置き場所

呼ばれ方

主な用途

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

.mq5 / .ex5

MQL5\Experts\

ティックごと(OnTick)

自動売買

カスタムインジケーター

.mq5 / .ex5

MQL5\Indicators\

再計算ごと(OnCalculate)

チャートへの描画

スクリプト

.mq5 / .ex5

MQL5\Scripts\

実行時に1回(OnStart)

一括決済などの単発処理

EAだけが OnTick() を持ちます。ファイルの配置先を間違えるとナビゲーターに表示されないため、必ずMT5の「ファイル」→「データフォルダを開く」から辿ってください。

イベントハンドラ一覧と、最初に使う3つ

MQL5のEAは「イベントが起きたら関数が呼ばれる」という設計です。公式リファレンスでは次のように定義されています(出典: MQL5 Reference - Program Running / Event Handling 、2026年9月24日確認)。

ハンドラ

シグネチャ

呼ばれるタイミング(公式の記述)

OnInit

int OnInit() / void OnInit()

「EAまたはインジケーターが読み込まれた直後にInitイベントが生成される」

OnDeinit

void OnDeinit(const int reason)

終了処理。reason に終了理由コードが入る

OnTick

void OnTick()

「銘柄の新しいティックを受信したときにEAに対してのみ生成される」

OnTimer

void OnTimer()

EventSetTimer() で設定した間隔ごと

OnTrade

void OnTrade()

注文・ポジション・履歴に変化があったとき

OnTradeTransaction

void OnTradeTransaction(const MqlTradeTransaction&, const MqlTradeRequest&, const MqlTradeResult&)

取引トランザクションの詳細を受け取る

OnChartEvent

void OnChartEvent(const int, const long&, const double&, const string&)

キーボード・マウス・オブジェクト操作

OnTester

double OnTester()

バックテスト終了後。カスタム最適化基準を返す

最初に書くEAで必要なのは上3つだけです。OnTimer 以下は、通知機能やポジション監視を作り込む段階で登場します。

💡 ポイント

OnInit() が INIT_FAILED を返すと、EAはチャートから外れます。逆にここで何も検証しないと、インジケーターのハンドル生成に失敗したまま OnTick() が回り続け、「エラーは出ないが一度も注文しない」という最も気づきにくい状態になります。

注文を出す2つの方法

MQL5で発注する方法は大きく2つあります。

方法1: MqlTradeRequest 構造体を直接組む(低レベル)

MqlTradeRequest に必要な項目を詰めて OrderSend() に渡します。主なフィールドは次の通りです(出典: MQL5 Reference - MqlTradeRequest 、2026年9月24日確認)。

フィールド

型

公式の説明

action

ENUM_TRADE_REQUEST_ACTIONS

Trade operation type(取引操作の種別)

magic

ulong

Expert Advisor ID (magic number)

symbol

string

Trade symbol

volume

double

Requested volume for a deal in lots

price

double

Price

sl / tp

double

Stop Loss level / Take Profit level of the order

deviation

ulong

Maximal possible deviation from the requested price

type

ENUM_ORDER_TYPE

Order type

type_filling

ENUM_ORDER_TYPE_FILLING

Order execution type

comment

string

Order comment

MQL4の OrderSend() が引数の羅列だったのに対し、MQL5は構造体を組み立ててから送る設計です。この違いが、MQL4とMQL5が互換でない最大の理由のひとつです。

方法2: CTrade クラスを使う(標準ライブラリ)

公式リファレンスは CTrade を「取引関数へ簡単にアクセスするためのクラス(a class for easy access to the trade functions)」と説明しています(出典: MQL5 Reference - CTrade 、2026年9月24日確認)。#include <Trade\Trade.mqh> で読み込み、Buy() / Sell() / PositionClose() などを呼ぶだけで発注できます。学習の最初は、こちらから入るほうが挫折しにくい構成です。

よく使うメソッド

役割

Buy() / Sell()

成行で新規ポジションを持つ

BuyLimit() / SellStop() ほか

待機注文を置く

PositionModify()

SL/TPを変更する

PositionClose() / PositionClosePartial()

決済・部分決済

SetExpertMagicNumber()

マジックナンバーを設定する

SetDeviationInPoints()

許容スリッページを設定する

SetTypeFillingBySymbol()

銘柄が許可する約定タイプを自動選択する

⚠️ 注意

MT5は注文ごとに約定ポリシー(FOK / IOC / RETURN)を指定します。業者が許可していないポリシーを指定すると、リターンコード 10030 TRADE_RETCODE_INVALID_FILL で必ず弾かれます。海外FX業者でMT5のEAが動かない原因として最頻出のため、SetTypeFillingBySymbol() を使うか、SYMBOL_FILLING_MODE を自分で確認してください。

最小サンプル:移動平均クロスEA

ここまでの要素を1本にまとめた学習用コードです。実運用を想定したものではありません。

//+------------------------------------------------------------------+
//| MinimalMaCross.mq5  学習用サンプル(実運用向けではありません)     |
//+------------------------------------------------------------------+
#property copyright "EA総合研究所"
#property version   "1.00"

#include <Trade\Trade.mqh>

input int    InpFastPeriod = 12;        // 短期EMAの期間
input int    InpSlowPeriod = 48;        // 長期EMAの期間
input double InpLots       = 0.01;      // ロット
input int    InpStopPoints = 300;       // 損切り幅(ポイント)
input int    InpTakePoints = 600;       // 利確幅(ポイント)
input ulong  InpMagic      = 20260924;  // マジックナンバー

CTrade   trade;
int      hFast = INVALID_HANDLE;
int      hSlow = INVALID_HANDLE;
datetime lastBarTime = 0;

//--- 起動時に1回だけ呼ばれる
int OnInit()
  {
   hFast = iMA(_Symbol, PERIOD_CURRENT, InpFastPeriod, 0, MODE_EMA, PRICE_CLOSE);
   hSlow = iMA(_Symbol, PERIOD_CURRENT, InpSlowPeriod, 0, MODE_EMA, PRICE_CLOSE);
   if(hFast == INVALID_HANDLE || hSlow == INVALID_HANDLE)
     {
      Print("インジケーターハンドルの作成に失敗しました");
      return(INIT_FAILED);          // ここで止めないと空回りする
     }
   trade.SetExpertMagicNumber(InpMagic);
   trade.SetDeviationInPoints(20);
   trade.SetTypeFillingBySymbol(_Symbol);
   return(INIT_SUCCEEDED);
  }

//--- 終了時に1回だけ呼ばれる
void OnDeinit(const int reason)
  {
   IndicatorRelease(hFast);
   IndicatorRelease(hSlow);
   PrintFormat("EAを終了します(理由コード: %d)", reason);
  }

//--- ティックごとに呼ばれる
void OnTick()
  {
   // (1) 足が確定したときだけ判定する
   datetime bar = iTime(_Symbol, PERIOD_CURRENT, 0);
   if(bar == lastBarTime)
      return;
   lastBarTime = bar;

   // (2) 直近3本分の移動平均を配列に取り出す
   double fast[], slow[];
   if(CopyBuffer(hFast, 0, 0, 3, fast) < 3) return;
   if(CopyBuffer(hSlow, 0, 0, 3, slow) < 3) return;
   ArraySetAsSeries(fast, true);
   ArraySetAsSeries(slow, true);

   // index 1 = 直前の確定足、index 2 = そのひとつ前
   bool goldenCross = (fast[2] <= slow[2] && fast[1] > slow[1]);
   bool deadCross   = (fast[2] >= slow[2] && fast[1] < slow[1]);

   // (3) 自分のポジションがあれば何もしない
   if(HasMyPosition())
      return;

   // (4) 発注する
   double ask   = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
   double bid   = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);

   if(goldenCross)
      trade.Buy(InpLots, _Symbol, ask,
                ask - InpStopPoints * point,
                ask + InpTakePoints * point, "MA cross long");
   else if(deadCross)
      trade.Sell(InpLots, _Symbol, bid,
                 bid + InpStopPoints * point,
                 bid - InpTakePoints * point, "MA cross short");
  }

//--- このEAのマジックナンバーを持つポジションがあるか
bool HasMyPosition()
  {
   for(int i = PositionsTotal() - 1; i >= 0; i--)
     {
      ulong ticket = PositionGetTicket(i);
      if(ticket == 0) continue;
      if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue;
      if(PositionGetInteger(POSITION_MAGIC) != (long)InpMagic) continue;
      return(true);
     }
   return(false);
  }

コードの読みどころ

  1. iMA() はOnInit()で1回だけ呼ぶ。 MQL5のインジケーター関数は値ではなく「ハンドル(整数ID)」を返します。OnTick() の中で毎回呼ぶとハンドルが増え続け、メモリを圧迫します。値の取得は CopyBuffer() で行います。
  2. ArraySetAsSeries(arr, true) で時系列順にする。 これを入れないと配列のindex 0が最も古い足になり、判定が逆転します。初学者の典型的なバグです。
  3. index 1 を見る。 index 0は形成中の足で、値が刻々と変わります。確定足で判定するために index 1 と index 2 を比べています。
  4. マジックナンバーで自分の注文だけを数える。 同一口座で複数のEAや裁量トレードを併用する場合、この判定がないとロット計算や決済が崩壊します。設計の考え方は マジックナンバーの決め方 で整理しています。

このコードに足りないもの(実運用との距離)

学習用サンプルと実運用EAの差は、ロジックではなく「例外処理の量」です。最低限、次の項目が必要になります。

不足している処理

何が起きるか

対処の方向

ロットの丸め

最小ロット・刻み値に合わず 10014(Invalid volume)で弾かれる

SYMBOL_VOLUME_MIN / _STEP / _MAX を取得して丸める

ストップレベルの確認

SL/TPが近すぎて 10016(Invalid stops)で弾かれる

SYMBOL_TRADE_STOPS_LEVEL を取得して下限を確保する

価格の正規化

桁数が合わず注文が通らない

NormalizeDouble(price, _Digits)

発注失敗時のリトライ

一時的な回線断・リクオートで取引機会を落とす

リターンコードを見て限定的に再送する

取引時間・ニュースのフィルタ

指標発表時やロールオーバー時に想定外の約定をする

サーバー時刻とGMTオフセットを踏まえて時間帯を制御する

最大ポジション数・最大DDの上限

想定外のロット膨張を止められない

口座全体の安全弁として実装する

サーバー時刻の扱いは、時間帯フィルタを作るときに必ず引っかかる論点です。 MT4/MT5のサーバー時間とGMTオフセット を先に読んでおくと、手戻りが減ります。

MQL4との違いを1枚で

すでにMQL4を触ったことがある人向けに、書き換えが必要な箇所をまとめます。

論点

MQL4

MQL5

発注

OrderSend() に引数を羅列

MqlTradeRequest を組み立てて OrderSend()、または CTrade

ポジションの参照

OrderSelect()(注文と一体)

PositionSelect() / PositionGetTicket()(注文と分離)

インジケーター値

iMA(...) が値を返す

iMA(...) はハンドルを返し、CopyBuffer() で値を取る

両建て

常に可能

口座タイプ(ヘッジング/ネッティング)に依存

約定ポリシー

概念なし

FOK / IOC / RETURN を指定

言語機能

手続き型中心

クラス・継承・テンプレートを全面採用

「MT4/MT5両対応」と書かれたEAは、実体としては別々に書かれた2本のプログラムです。したがってバックテスト結果も一致しません。購入検討時は両方の実績が個別に提示されているかを見てください。この論点は MT4とMT5の違い でも整理しています。

なお、MetaQuotes社はMT4の保守を縮小しており、国内でもMT4提供を終了する業者が出ています。これから学ぶならMQL5を選ぶ合理性が高い状況です( MT4のサポート終了と今後 )。

よくある失敗

🚨 危険

学習用コードをいきなりリアル口座で動かすことです。MQL5のコンパイルが通ることと、注文が意図通りに出ることはまったく別です。必ずデモ口座で最低1回、エントリーから決済までを通してログを読んでください。

  • バックテストの数字を鵜呑みにする。 パラメータを動かしていれば、過去データに合う組み合わせは必ず見つかります。それが将来も機能する保証はありません。判別方法は カーブフィッティングの見抜き方 にまとめています。
  • エラーを握りつぶす。 trade.Buy() の戻り値を確認せず放置すると、発注が全部失敗していても気づけません。trade.ResultRetcode() をログに出す習慣をつけてください。
  • いきなり複雑にする。 最初の1本は「移動平均クロス+固定SL/TP」で十分です。複雑なロジックほど検証が難しく、どこが悪いのか切り分けられなくなります。
  • コードを書かずに完成品を探し始める。 それ自体は悪くありませんが、目的が「自作」なら順序が逆になります。全体の進め方は EA自作の完全ロードマップ を参照してください。

まとめ

MQL5の学習コストは、想像されているより低い部類です。OnInit() でハンドルを用意し、OnTick() で確定足を見て判断し、CTrade で発注する——この骨格さえ掴めば、あとは例外処理を足していく作業になります。

次のステップとして、コードを書く前に全体像を掴みたい方は EAを自作する完全ロードマップ を、生成AIを使った書き方を試したい方は 生成AIでEAを作る手順と限界 を、コードを書かない選択肢を検討したい方は ノーコードEA作成ツールの比較 をあわせてご覧ください。書いたEAを検証する段階に進んだら、 バックテストの正しいやり方 が次の関門になります。

よくある質問

Q.MQL4のコードをMQL5にコピーすれば動きますか?
動きません。MQL4とMQL5は相互に非互換で、注文APIの設計そのものが違います。MQL4は OrderSend() 一本で発注しますが、MQL5は MqlTradeRequest 構造体を組み立ててから送信し、ポジションは PositionSelect() 系で別管理します。同じ名前のEAでもMT4版とMT5版は別々に書かれた別プログラムです。移植は「翻訳」ではなく「書き直し」になります。
Q.OnTick()の中に処理を全部書いてはいけないのですか?
禁止されているわけではありませんが、OnTick()はティックが来るたびに呼ばれるため、重い処理を書くと取引機会を逃します。実務では「足が確定したときだけ判定する」ガードを先頭に置き、インジケーターのハンドル生成はOnInit()で一度だけ行うのが定石です。サンプルコードの iTime() による新バー判定がその最小形です。
Q.サンプルコードをそのまま実運用に使えますか?
使わないでください。本記事のコードは構造を理解するための学習用で、スプレッド・ストップレベル・約定タイプ・ロット丸め・エラー時のリトライといった実運用に必須の処理を省いています。まずデモ口座で動かし、ログを読み、バックテストの手順に沿って検証してから判断してください。
Q.コンパイルは通るのに注文が出ません。どこを見ればいいですか?
まずMT5の「ツールボックス」→「エキスパート」タブでEAのログ、「操作履歴」タブでサーバー応答を確認します。よくある原因は自動売買ボタンがオフ、ロット単位が銘柄の最小値・刻み値に合っていない、SL/TPがストップレベル以内、約定タイプ(FOK/IOC)が業者の許可外の4つです。詳しくは注文エラーの原因と対処を参照してください。
Q.MQL5を学ぶのに最初に読むべき公式ドキュメントはどれですか?
MQL5 Reference の「Program Running / Event Handling」(https://www.mql5.com/en/docs/basis/function/events)でイベントハンドラの一覧を把握し、次に「MqlTradeRequest」(https://www.mql5.com/en/docs/constants/structures/mqltraderequest)で注文構造体を読むのが最短です。標準ライブラリのCTradeクラス(https://www.mql5.com/en/docs/standardlibrary/tradeclasses/ctrade)まで押さえれば、最小限のEAは書けるようになります。

関連記事