固定ロット(常に0.1ロットなど)でトレードを続けていると、

口座残高が増えても減っても、1回の負けで受けるダメージの「割合」

が変わり続けます。

残高が減ってきた場面ほど、同じ0.1ロットの損失が相対的に重くのしかかります。

この記事では、口座残高の常に一定%だけをリスクにさらす、「リスク%ベースのロット自動計算」

をMQL4で実装する方法を解説します。

固定ロットの何が問題なのか

固定ロットの最大の問題は、残高とロットサイズが連動しないことです。

残高10万円で0.1ロットも、残高5万円に減った後の0.1ロットも、

証拠金に対する負担も損失額も同じままです。

要点: リスク%ベースのロット計算は、

「残高の2%だけ失う」というリスク量を一定に保つ考え方です。

残高が減れば自動的にロットも減り、

連敗によるダメージの拡大を抑えられます。

複利ロットとの違い

似た発想に複利ロット(残高に比例してロットを増やす)がありますが、

複利は「増えた分を積極的に使う」設計です。

リスク%ベースは「一定のリスク量を守る」ことが目的で、

攻めよりも守りに重心がある点が異なります。

計算に使うMQL4関数: MarketInfo

ロット計算には、通貨ペアごとに異なる「1ポイントあたりの価値」を知る必要があります。

これを取得するのが`MarketInfo()`関数です。

double MarketInfo(
   string symbol,   //対象の通貨ペア
   int    type      //取得したい情報の種類
   );

1ポイントの価値を返すMODE_TICKVALUEとMODE_TICKSIZE

`MODE_TICKVALUE`は、1ロットにおける最小価格変動(ティック)1回分の、

損益額を預入通貨で返します。

double tickValue = MarketInfo(Symbol(), MODE_TICKVALUE);//1ティックあたりの価値
double tickSize  = MarketInfo(Symbol(), MODE_TICKSIZE); //1ティックの価格幅

注意: 多くの4桁ブローカーでは1ティック=1ポイントですが、

5桁ブローカーではティックサイズがPointと異なる場合があります。

`MODE_TICKVALUE`をそのまま「1ポイントの価値」として使うと、

ずれた計算になることがあります。

正確には、1ポイントあたりの価値は次の式で求めます。

double tickValue     = MarketInfo(Symbol(), MODE_TICKVALUE);
double tickSize      = MarketInfo(Symbol(), MODE_TICKSIZE);
double pointValue    = tickValue * (Point / tickSize);//1ポイントあたりの価値

ロットを丸めるMODE_LOTSTEP・MODE_MINLOT・MODE_MAXLOT

計算したロット数は、そのままでは注文できません。

ブローカーが許可するロット単位(`MODE_LOTSTEP`)に丸め、

最小・最大ロット(`MODE_MINLOT`/`MODE_MAXLOT`)の、

範囲に収める必要があります。

double lotStep = MarketInfo(Symbol(), MODE_LOTSTEP);
double minLot  = MarketInfo(Symbol(), MODE_MINLOT);
double maxLot  = MarketInfo(Symbol(), MODE_MAXLOT);

リスク許容額からロット数を計算する式

計算の流れは、次の3ステップです。

ステップ1: リスク許容額を求める

口座残高に、許容するリスク%を掛けます。

double riskAmount = AccountBalance() * (RiskPercent / 100.0);

ステップ2: 損切り幅(ポイント数)で割る

このリスク許容額を、損切り幅(ポイント数)×1ポイントの価値で割ると、

ロット数が求まります。

double pointValue = tickValue * (Point / tickSize);
double rawLot = riskAmount / (StopLossPoints * pointValue);

ステップ3: LOTSTEPで丸め、MIN/MAXの範囲に収める

double lot = MathFloor(rawLot / lotStep) * lotStep;
if(lot < minLot) lot = minLot;
if(lot > maxLot) lot = maxLot;
lot = NormalizeDouble(lot, 2);

補足: `MathFloor`で切り捨てているのは、

計算上のロットを繰り上げると、

想定したリスク%をわずかに超えてしまうためです。

安全側に倒すなら切り捨てが基本です。

実際に動くコード全体(汎用ロット計算関数)

ここまでの内容をまとめた、リスク%と損切り幅を渡すとロット数を返す関数と、

実際に発注する例です。

実際にMetaEditorでコンパイルできます。

//+------------------------------------------------------------------+
//|                                    RiskPercentLotSize.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 double RiskPercent    = 2.0;//口座残高に対するリスク%
input int    StopLossPoints = 300;//損切り幅(ポイント)
input int    MagicNumber    = 20260927;

//+------------------------------------------------------------------+
//| リスク%と損切り幅からロット数を計算する汎用関数                    |
//+------------------------------------------------------------------+
double CalcLotByRisk(string symbol, double riskPercent, int slPoints)
  {
   double tickValue = MarketInfo(symbol, MODE_TICKVALUE);
   double tickSize  = MarketInfo(symbol, MODE_TICKSIZE);
   double point     = MarketInfo(symbol, MODE_POINT);
   double lotStep   = MarketInfo(symbol, MODE_LOTSTEP);
   double minLot    = MarketInfo(symbol, MODE_MINLOT);
   double maxLot    = MarketInfo(symbol, MODE_MAXLOT);

   if(tickValue <= 0 || tickSize <= 0 || lotStep <= 0 || slPoints <= 0)
     {
      Print("MarketInfoの取得に失敗、またはslPointsが不正です。");
      return(0);
     }

   double pointValue = tickValue * (point / tickSize);
   double riskAmount = AccountBalance() * (riskPercent / 100.0);
   double rawLot      = riskAmount / (slPoints * pointValue);

   double lot = MathFloor(rawLot / lotStep) * lotStep;
   if(lot < minLot) lot = minLot;
   if(lot > maxLot) lot = maxLot;

   return(NormalizeDouble(lot, 2));
  }

//+------------------------------------------------------------------+
//| Expert tick function                                              |
//+------------------------------------------------------------------+
void OnTick()
  {
   if(OrdersTotal() > 0)
      return;//サンプルのため簡略化、実際は条件を追加してください

   double lot = CalcLotByRisk(Symbol(), RiskPercent, StopLossPoints);
   if(lot <= 0)
     {
      Print("ロット計算に失敗したため発注を見送りました。");
      return;
     }

   double ask = MarketInfo(Symbol(), MODE_ASK);
   double sl  = ask - StopLossPoints * MarketInfo(Symbol(), MODE_POINT);

   int ticket = OrderSend(Symbol(), OP_BUY, lot, ask, 3, sl, 0,
                           "RiskPercentLot", MagicNumber, 0, clrGreen);
   if(ticket < 0)
      Print("発注に失敗しました。エラーコード=", GetLastError());
   else
      Print("発注成功。ロット=", lot);
  }
//+------------------------------------------------------------------+

MetaEditorで実際に動かす手順

  1. MT4のMetaEditorで「ファイル」→「新規作成」→「エキスパートアドバイザー」を選ぶ
  2. 「RiskPercentLotSize」という名前で作成し、テンプレートの中身をすべて削除して上のコードを貼り付ける
  3. F7キーでコンパイルし、「0 error(s), 0 warning(s)」になることを確認する
  4. デモ口座でチャートに適用し、Print()のログでロット計算結果を確認する
  5. `RiskPercent`や`StopLossPoints`を変えて、ロットが意図通りに増減するか確認する

よくあるエラーと対処

  • ロットが常に0になる: `MarketInfo`が0を返している可能性があります。通貨ペア名(`Symbol()`)のスペルやブローカーのサフィックスを確認してください
  • 発注エラー130(Invalid stops): `StopLossPoints`がブローカーの最小ストップ距離より小さい可能性があります。`MarketInfo(Symbol(), MODE_STOPLEVEL)`で確認してください
  • 発注エラー134(証拠金不足): 計算したロットが証拠金に対して大きすぎる可能性があります。`AccountFreeMarginCheck()`で事前チェックする設計に変更してください
  • 計算されるロットが毎回同じ: `AccountBalance()`ではなく固定値を参照していないか確認してください。含み損益も考慮したい場合は`AccountEquity()`に変更します

証拠金維持率ベースの計算との違い

ロット自動計算には、この記事の「リスク%ベース」のほかに、「証拠金維持率ベース」

という考え方もあります。

証拠金維持率ベースは、現在の証拠金維持率が一定水準を下回らない範囲で、

ロットを決める設計です。

リスク%ベースが向く場面: 損切り幅を明確に決めてトレードする、

裁量
・EA問わず一般的な手法

証拠金維持率ベースが向く場面: 損切りを置かない、

または長期保有前提のスワップ運用など、

証拠金の余力そのものを管理したい手法

この記事のリスク%ベースは「いくら失うか」を先に決める考え方であり、

証拠金維持率ベースは「どこまで持ちこたえられるか」を先に決める考え方です。

目的が異なるため、どちらが優れているというものではありません。

この関数を使う際の注意点

この関数は損切り幅(`StopLossPoints`)を先に決めることが前提です。

損切り幅そのものが大きすぎる・小さすぎると、

リスク%を正しく守っていても、

手法自体の期待値が悪ければ意味がありません。

注意: スプレッドが広い通貨ペアや、

深夜
・指標発表前後の急な広がりでは、想定した損切り幅より実際の損失が大きくなることがあります。

よくある質問

リスク%ベースのロット計算でよく検索されている疑問を、3つにまとめて回答します。

リスク%は何%に設定すればいいですか?

一般的には1〜2%が目安とされていますが、これは断定できる正解ではありません。

連敗が続く手法ほど低めに、勝率が高く連敗しにくい手法ほどやや高めに、

自分の手法のバックテスト結果を見ながら調整してください。

AccountBalanceとAccountEquityはどちらを使うべきですか?

含み損の影響を受けずに一定のリスクを守りたいなら`AccountBalance()`、

含み損の分だけリスクを保守的に減らしたいなら、

`AccountEquity()`が向いています。

この記事のコードは前者(`AccountBalance()`)を採用しています。

複数ポジションを同時に持つ場合はどうなりますか?

この記事の関数は1回のエントリーごとのリスク%を計算するものです。

複数ポジションを同時に持つ場合、合計のリスク%が意図せず膨らむため、

保有中のポジション数に応じてリスク%を按分する設計に拡張してください。

【MQL4】固定ロットで消耗していませんか?口座残高のリスク%からロット数を自動計算する関数の作り方:まとめ

この記事では、口座残高の一定%だけをリスクにさらす、「リスク%ベースのロット自動計算」

をMQL4で実装する方法を解説しました。

`MarketInfo()`でティック価値・ロットステップ・最小最大ロットを取得し、

リスク許容額を損切り幅で割ってからLOTSTEPで丸める、という流れが基本です。

証拠金維持率ベースの計算とは目的が異なるため、自分の手法が「損切り幅を決める手法」

か「証拠金の余力を管理したい手法」か、見極めたうえで使い分けてください。

関連記事