複利EA(fukuri.md)の記事では、口座残高とStopLoss幅から「損失がRisk%に収まるロット数」

を計算するLotSize()関数を作りました。

ですがこの計算式は、あくまで理屈の上で妥当なロット数を求めているだけで、

実際にそのロット数で発注したときに証拠金が足りるかどうかは一切確認していません。

含み損を抱えて証拠金が減っている状況で複利ロットが計算上大きくなってしまうと、

発注そのものが「エラーコード134(ERR_NOT_ENOUGH_MONEY、

資金不足)」で失敗する、という事故につながります。

`AccountFreeMarginCheck()`を使うと、

実際に`OrderSend()`を送る前に「このロット数で発注したら証拠金はいくら残るか」

を事前に計算でき、不足しそうな発注を未然に見送ることができます。

この記事では、この関数の使い方と、fukuri.mdのリスクベースロット計算に組み合わせる実装を、

実際に動くコードで解説します。

AccountFreeMarginCheck関数の書式

double AccountFreeMarginCheck(
   string symbol,   //通貨ペア
   int    cmd,      //売買の種類(OP_BUY・OP_SELL等)
   double volume    //ロット数
   );

戻り値は「指定した内容で発注した場合に、口座に残るはずの証拠金余力(口座通貨)」です。

**証拠金が不足する場合は、不足額を表す負の値が返り、`GetLastError()`でエラーコード134(ERR_NOT_ENOUGH_MONEY)が確認できます。

** 実際に発注する前に、まずこの関数で結果を確認するのが正しい使い方です。

double freeMarginAfter = AccountFreeMarginCheck(Symbol(), OP_BUY, 0.1);
if(freeMarginAfter < 0)
  {
   Print("証拠金不足のため発注できません。不足額(口座通貨)=", MathAbs(freeMarginAfter));
  }

AccountFreeMargin()との違い

似た名前の`AccountFreeMargin()`(引数なし)は、

「今この瞬間の口座の証拠金余力」をそのまま返すだけの関数です。

一方`AccountFreeMarginCheck()`は、

「これから発注する予定の内容(通貨ペア・売買方向・ロット数)を実際に発注したと仮定して、

その後に残る証拠金余力」をシミュレーションして返します。

通貨ペアごとに必要証拠金(レバレッジ・証拠金通貨換算レート)が異なるため、

「今の余力が10万円ある」ことと「このロット数で発注しても大丈夫」

であることは同じではありません。

発注前チェックには`AccountFreeMarginCheck()`を使う必要があります。

パラメーターの解説

  • symbol: 発注しようとしている通貨ペア。`Symbol()`で現在チャートの通貨ペアを渡すのが基本
  • cmd: 売買の種類。実際に`OrderSend()`で使う予定の値(`OP_BUY`・`OP_SELL`等)と必ず一致させる。買いと売りで必要証拠金が変わるブローカー・口座タイプもあるため、チェック時と発注時でcmdをずらさないこと
  • volume: ロット数。実際に`OrderSend()`に渡す予定のロット数(fukuri.mdで言えばLotSize()の計算結果)と同じ値を渡す

fukuri.mdのLotSize()と組み合わせる

fukuri.mdのLotSize()は、StopLoss幅から「損失がRisk%に収まるロット数」

を計算しますが、この計算はブローカーが要求する証拠金額を一切考慮していません。

ロット数を計算したあと、実際に発注する直前に`AccountFreeMarginCheck()`で証拠金の余力を確認し、

不足するなら発注そのものを見送る関数を用意します。

bool HasEnoughMargin(int cmd, double volume)
  {
   double freeMarginAfter = AccountFreeMarginCheck(Symbol(), cmd, volume);
   if(freeMarginAfter < 0)
     {
      Print("証拠金不足のため発注を見送りました。不足額(口座通貨)=", MathAbs(freeMarginAfter),
            " エラーコード=", GetLastError());
      return(false);
     }
   return(true);
  }

`OrderSend()`を呼ぶ直前に、この関数で`false`が返ったら発注処理そのものをスキップするだけで、

エラー134によるログの汚染や、発注失敗時のリトライ処理の複雑化を防げます。

実際に動くコード全体

gc-dc.md(ゴールデンクロス・デッドクロスEA)のポジション重複防止・損切り設定に、

証拠金チェックを追加した、実際にMetaEditorでコンパイルできるコードです。

//+------------------------------------------------------------------+
//|                                       MarginCheckDemo-EA.mq4      |
//|                     Copyright 2026, FX-EA System Project Creator |
//|                        https://creator.fx-ea-system-project.com/ |
//+------------------------------------------------------------------+
#property copyright "Copyright 2026, FX-EA System Project Creator"
#property link      "https://creator.fx-ea-system-project.com/"
#property version   "1.00"
#property strict

input int    TakeProfit  = 5;//利確(pips)
input int    StopLoss    = 10;//損切り(pips)
input double Lots        = 0.01;//ロット数
input int    MagicNumber = 12345;//マジックナンバー
input int    Slippage    = 3;//スリッページ

//+------------------------------------------------------------------+
//| このEA(マジックナンバー)のポジションを保有しているか判定する      |
//+------------------------------------------------------------------+
bool HasPosition()
  {
   for(int i=OrdersTotal()-1; i>=0; i--)
     {
      if(!OrderSelect(i,SELECT_BY_POS,MODE_TRADES)) continue;
      if(OrderSymbol()==Symbol() && OrderMagicNumber()==MagicNumber)
         return(true);
     }
   return(false);
  }

//+------------------------------------------------------------------+
//| 発注前に証拠金が足りるかを確認する                                 |
//+------------------------------------------------------------------+
bool HasEnoughMargin(int cmd, double volume)
  {
   double freeMarginAfter = AccountFreeMarginCheck(Symbol(), cmd, volume);
   if(freeMarginAfter < 0)
     {
      Print("証拠金不足のため発注を見送りました。不足額(口座通貨)=", MathAbs(freeMarginAfter),
            " エラーコード=", GetLastError());
      return(false);
     }
   return(true);
  }

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   if(HasPosition())
      return;

   double kakoMA50,kakoMA100;
   double genMA50,genMA100;

   kakoMA50 = iMA(NULL,0,50,0,MODE_SMA,PRICE_CLOSE,1);
   kakoMA100 = iMA(NULL,0,100,0,MODE_SMA,PRICE_CLOSE,1);
   genMA50 = iMA(NULL,0,50,0,MODE_SMA,PRICE_CLOSE,0);
   genMA100= iMA(NULL,0,100,0,MODE_SMA,PRICE_CLOSE,0);

   int ticket;

   if(kakoMA50 <= kakoMA100 && genMA50 > genMA100)//ゴールデンクロス
     {
      if(!HasEnoughMargin(OP_BUY, Lots))
         return;//証拠金不足なら発注しない
      ticket = OrderSend(Symbol(),OP_BUY,Lots,Ask,Slippage,Ask-StopLoss*Point*10,Ask+TakeProfit*Point*10,"Order Long",MagicNumber,0,Red);
      if(ticket < 0)
         Print("買い注文に失敗しました。エラーコード=",GetLastError());
     }

   if(kakoMA50 >= kakoMA100 && genMA50 < genMA100)//デッドクロス
     {
      if(!HasEnoughMargin(OP_SELL, Lots))
         return;//証拠金不足なら発注しない
      ticket = OrderSend(Symbol(),OP_SELL,Lots,Bid,Slippage,Bid+StopLoss*Point*10,Bid-TakeProfit*Point*10,"Order Short",MagicNumber,0,Blue);
      if(ticket < 0)
         Print("売り注文に失敗しました。エラーコード=",GetLastError());
     }
  }
//+------------------------------------------------------------------+
Copy

MetaEditorで実際に動かす手順

  1. MT4のMetaEditorで「ファイル」→「新規作成」→「Expert Advisor(テンプレートを使用)」を選び、名前を「MarginCheckDemo-EA」にして作成する
  2. テンプレートの中身を全て削除し、上のコードを丸ごと貼り付ける
  3. F7キー(またはコンパイルボタン)でコンパイルし、「0 error(s), 0 warning(s)」になることを確認する
  4. デモ口座のチャートへドラッグ&ドロップし、ゴールデンクロス・デッドクロスが発生するタイミングで発注されることを確認する
  5. あえてLots(ロット数)を口座残高に対して過大な値に設定してストラテジーテスターで動かし、「証拠金不足のため発注を見送りました」というログが「エキスパート」タブに出力されることを確認する

コピペしてコンパイルが通らない・意図通りに動かないときに確認すること

  • `HasEnoughMargin()`に渡す`cmd`(OP_BUY/OP_SELL)が、直後の`OrderSend()`の`cmd`と一致しているか(買いのつもりで`OP_SELL`を渡すと、必要証拠金の計算がずれる)
  • `HasEnoughMargin()`に渡すロット数が、実際に`OrderSend()`へ渡すロット数と同じ値になっているか(fukuri.mdのようにLotSize()で動的に計算する場合、チェック用とOrderSend用で別々に計算し直すと、わずかな計算結果のズレで不整合が起きる可能性がある。同じ変数を1回だけ計算して両方に使うこと)
  • `AccountFreeMarginCheck()`の戻り値が負でなく`0`に近い小さな正の値の場合、証拠金的には発注できてもロスカット水準に極端に近い状態である可能性がある。マイナスでなければ安全、とは限らないため、必要に応じて余裕を持たせた閾値(例: 残る証拠金が口座残高の20%を下回るなら見送る)を追加で検討する
  • この関数はスプレッドや将来の価格変動による含み損の増加までは考慮しない、あくまで「発注した瞬間」の証拠金計算であることに注意する

まとめ

`AccountFreeMarginCheck()`は、`OrderSend()`を送ってからエラーコード134で失敗を知るのではなく、

発注前に証拠金の余力を予測できる関数です。

fukuri.mdのLotSize()のようにリスク%からロット数を計算するロジックを組んでいても、

その計算結果を実際に発注できるだけの証拠金が口座にあるかどうかは別問題であり、

この関数による事前チェックが抜けていると、資金が減っている局面でまさに必要なタイミングの発注が失敗し続けるという事故につながります。

`OrderSend()`の直前に必ず挟む安全策として組み込んでください。

関連記事