「ストップロスを設定したけれど、いくらまで損しても大丈夫なのか?」 「許容損失額に合わせた“安全なロット数”を自動計算したい」 ──EA開発やリスク管理で、この悩みを持つ方はとても多いです。
この記事では、許容できる損失額(% or 金額)とストップロス幅から最適なロット数を算出するMT4用関数を、 コピペで使えるMQL4コード+初心者向けの丁寧な解説つきで紹介します。
以下のような用途で特に役立ちます:
- 1トレードあたりの損失額を一定(例:口座の5%以内)にしたい
- 余剰証拠金に合わせてロットを可変にしたい
- ストップロス幅(pips)から安全ロット数を自動計算したい
- 複数EAで使い回せる資金管理関数を作りたい
📌 ロット計算・資金管理の関連関数
- 許容損失額からロット数を計算する処理(本記事)
└ 損失額(% or 金額)とSL幅から最適ロットを算出する関数 - 証拠金維持率からロット数を自動調整する関数
└ 維持率を下げすぎないロット管理を実現する関数 - MT4でポジションを半分決済する関数【初心者向け】
└ 損失コントロールに役立つ動的ロット調整ロジック
はじめに:損失額からロットを決める重要性
EA に限らず、FXトレードでは「どれくらいの損失を許容するか」を明確にしないと、 ロットオーバーで一撃ロスカットという事態が起こりやすくなります。
今回紹介する関数は、損失額(%または金額)→ストップロス幅→安全ロット数を自動計算するため、 リスク管理を安定させたい方にとても役立ちます。
【コピペOK】許容損失額からロット数を自動計算する関数
まずは完成形の関数です。EAにそのまま貼り付けて使えます。
//+------------------------------------------------------------------+
//|【関数】許容損失額に対してのロット数を取得する
//|
//|【引数】Type:0か1(0:% 1:額)Pips:ストップロスのPips(50=50pips) PerSec:許容損失額(5% 50000円等)
//|
//|【戻値】ロット数(1000通貨=0.01)
//|
double funcLotGet(int Type,double Pips,double PerSec)
{
double RiyouGaku=0,SaidaiLot=0,KouzaMoney=0,Kekka=0;
if(Pips == 0){
Print("ストップロス値が設定されていません");
return(0);
}
//最大ロット数確認
double server_Lot = MarketInfo(Symbol(),MODE_MAXLOT);
if(SaidaiLot == 0 || SaidaiLot > server_Lot){
SaidaiLot = server_Lot;
}
//口座残高(余剰証拠金)
KouzaMoney = AccountInfoDouble(ACCOUNT_FREEMARGIN);
if(Type == 0){
//利用可能金額算出
RiyouGaku = NormalizeDouble(KouzaMoney * (PerSec / 100),0);
}else{
//利用可能金額算出
RiyouGaku = PerSec;
}
Print("余剰証拠金:" + DoubleToString(KouzaMoney,0) + " 利用可能金額:" + DoubleToString(RiyouGaku,0));
//通貨ペア取得
string Tuuka = Symbol();
//通貨ペア名称保持
int MojiCnt = StringLen(Tuuka);
string subMoji = "";
if(MojiCnt > 6){
subMoji = StringSubstr(Tuuka, 6, MojiCnt - 6);
}
//クロス円
if(StringFind(Tuuka, "JPY",0) != -1){
//ロット数計算
Kekka = RiyouGaku / Pips /1000;
//ドルストレート
}else if(StringFind(Tuuka, "USD",3) != -1){
//double Nedan = MarketInfo("USDJPY"+subMoji, MODE_ASK);
double Nedan = iOpen("USDJPY"+subMoji,PERIOD_CURRENT,0);
//ロット数計算
Kekka = RiyouGaku / Pips / Nedan / 10;
}else if(Tuuka == "GOLD" || Tuuka == "goldmicro" || Tuuka == "GOLDmicro"){
double Nedan1 = iOpen("USDJPY"+subMoji,PERIOD_CURRENT,0);
//ロット数計算
Kekka = RiyouGaku / Pips / Nedan1 / 10;
}else{
Print("計算対象外通貨です:"+Tuuka);
return(0);
}
//少数点を0.01単位までにする
Kekka = NormalizeDouble(Kekka, 2);
//最大ロット数を超えている場合は、最大ロット数にする
if(Kekka > SaidaiLot){
Kekka = SaidaiLot;
}
return Kekka;
}
※今回の関数は、価格差益のみの計算となっておりスワップポイントのマイナスやプラス分までは考慮されていません。
関数の引数と戻り値の解説
引数
引数1はタイプです。タイプ1は需要があるかわかりませんがお好みで設定してください。
- 0の場合 ➡ 余剰証拠金から許容できる損失額をパーセンテージで計算するタイプ(引数3は%で渡す)
- 1の場合 ➡ 引数3に直接損失額を直打ちするタイプ(引数3は金額で渡す)
引数2はストップロス(Pips)です。エントリー時にストップロスを何Pipsに設定するかの際の値を渡すようにしてください
(例)20Pipsの場合は、20を渡す
引数3は許容損失額です。引数1でも説明しましたが、余剰証拠金から許容できる損失額をパーセンテージで渡してください。
- 引数1が0だった場合 ➡ 余剰証拠金から許容できる損失額をパーセンテージで渡す
- 引数1が1だった場合 ➡ 引数3は直接損失許容金額で渡す
例
以下、状況別の引数の例です。
余剰証拠金が50万円あり、1トレード10%(5万円)失ってもいい場合でストップロスは30Pips
↓
funcLotGet(0,30,10)
1トレード7万円失ってもいい場合でストップロスは50Pips
↓
funcLotGet(1,50,70000)
タイプによって引数3の数値の意味合いが変わるのは少し気持ち悪いですが、汎用性を意識しています。
戻り値
許容損失額に応じた、ロット数を返します。 ※1=10万通貨
(ブローカーによる最大ロットをオーバーしている場合は、ブローカーの最大ロット数になります)
また、ストップロス値が設定されていない場合や対円・ドルストレート以外(AUDCAD、GBPNZD等)の場合は0を返しています。
処理内容
まず自分の口座の口座残高(余剰証拠金)を取得し、引数3の許容損失額(%)を使って利用可能金額を算出しています。
Kekka = NormalizeDouble(KouzaMoney * (PerSec / 100),0);
↓
(利用可能金額:50,000)= (余剰証拠金:500,000) * (許容損失額:0.1 )
次に、対円なのかドルストレートかで計算方法が変わってくるので通貨ペアの確認をしています。
対円の場合は計算は簡単ですが、ドルストレートの場合はドル/円の値段が必要になってきます。
※補足情報・・・MarketInfo(Tuuka, MODE_ASK)でドル値段を取得してもよかったのですが、バックテストで値が取れないためiOpen()で開始値を取得しています。
最後に、ロット数の確認をし最大ロット数を超えている場合は最大ロット数にしています。
【使用例】許容損失計算関数
// 新規エントリー注文
int CNo = OrderSend(
NULL, // 通貨ペア
OP_BUY, // オーダータイプ[OP_BUY / OP_SELL]
funcLotGet(0,20,10), // ロット[0.01単位](10%損失許容)
Ask, // オーダープライスレート
・・・OrderSend()関数のロット数のところに書き込む感じです。あとは、funcLotGet()関数をソース内にそのまま貼り付けてください。

使用した結果はこんな感じで、履歴は下から上へ流れているので余剰証拠金が50万円あり、1トレード10%(50,000円)失っても良い場合(ストップロスは30Pips)のロット数は1.67という事がわかりますね。
で、このトレードはストップロスになり50,100円失い余剰証拠金が449,900円になったため、次回のトレードの10%(44,990円)失っても良い場合(ストップロスは30Pips)のロット数は1.50という事がわかります。
実装のポイントと注意事項
- ストップロス幅(pips)がズレるとロット誤差が出る
EA側でSLを設定していない場合、計算が正しく行えません。必ず「固定pips」または「直近安値/高値」などで一貫性を持たせてください。 - 通貨ペアによって1pipsの価値が異なる
クロス円とドルストレートではpips換算が違うため、関数実装時は「TickValue」「Point」などで自動補正されているか確認が必要です。 - 口座残高ではなく“余剰証拠金”を使いたいケースもある
ナンピンEAや複数ポジション運用の場合、BALANCEではなくFreeMarginを使うほうが安全になることがあります。 - 許容損失率は3〜5%以内が推奨
許容リスクを大きくしすぎると、1回の損切りで口座の再起不能率が跳ね上がります。初心者は特に「少なめ」がおすすめです。 - 異常に大きいロットは必ず制限する
FXTFのように最大ロット制限が厳しい業者もあるため、計算結果が上限を超えたら自動で丸める処理は必須です。
📌 ロット計算・資金管理の関連関数
- 許容損失額からロット数を計算する処理(本記事)
└ 損失額(% or 金額)とSL幅から最適ロットを算出する関数 - 証拠金維持率からロット数を自動調整する関数
└ 維持率を下げすぎないロット管理を実現する関数 - MT4でポジションを半分決済する関数【初心者向け】
└ 損失コントロールに役立つ動的ロット調整ロジック
さいごに:EAの共通パーツとして使い回そう
以上、許容損失額からロット数を自動計算する関数(MT4版)を紹介しました。 一度関数化しておけば、複数EAのリスク管理にそのまま流用でき、 トレードの安全性・再現性を大きく高められます。
👉 EA の基礎から学びたい方は FX自動売買(EA)の始め方ガイド もおすすめです。
👉 関数をもっと探したい方は MQL4 関数一覧ページ をどうぞ。
👉 EAロジックやTipsを探したい方は EAプログラミング小ネタ集 も役立ちます。
✅ 今回のロジックをベースにしたEAサンプルも多数公開中
今回紹介したようなEAの売買ロジック・考え方をベースに、
当サイトではさまざまなFX自動売買EAのサンプルコードを公開しています。
ロジックの違いや設計の考え方を比較しながら、
自分に合ったEA構成を探したい方はぜひチェックしてみてください。
📊 EA運用・検証フェーズに進みたい方へ
今回のような仕組みを理解したうえで、
「実際にどのEAが安定しているのか」、「検証データではどんな差が出ているのか」
を確認したい方は、以下の記事も参考になります。
EA開発初心者向けに、今後も実践的に使えるMQL4関数や実装例を紹介していきます。
気になる機能やロジックがあれば、用途別に整理した関連記事もぜひあわせてご覧ください。









コメント
さっそく記事にしてくださって、ありがとうございます!
今日もMQLの勉強をしていますが、本当にいつも参考にさせていただいております。
これからもよろしくお願いします!
MQLの勉強頑張ってくださいね!
こちらこそよろしくお願いします!
お世話になっております。
以前に、教えていただいた許容損失額によってロットサイズが変動する関数を使用して決まった時間に取引するコードを書いてみました。
しかし、爆テストするとロット数量が1.00のままでどこが原因か自分では理解に及んでいません。
もし可能でしたら、ご教授お願いいたしますm(__)m
//+——————————————————————+
//| TokyoTime.mq4 |
//| Copyright 2020, mef Software. |
//| https://fx-prog.com/ |
//+——————————————————————+
#property copyright “Copyright 2020, mef Software.”
#property link “https://fx-prog.com/”
#property version “1.00”
input int A_SPREAD = 10; //エントリー及び決済時に許容するスプレッドの値です(10 = 10pips)
~
//口座残高(余剰証拠金)
KouzaMoney = AccountInfoDouble(ACCOUNT_FREEMARGIN);
if(Type == 0){
//利用可能金額算出
RiyouGaku = NormalizeDouble(KouzaMoney * (PerSec / 100),0);
}else{
//利用可能金額算出
RiyouGaku = PerSec;
}
Print(“余剰証拠金:” + DoubleToString(KouzaMoney,0) + ” 利用可能金額:” + DoubleToString(RiyouGaku,0));
//通貨ペア取得
string Tuuka = Symbol();
//通貨ペア名称保持
int MojiCnt = StringLen(Tuuka);
string subMoji = “”;
if(MojiCnt > 6){
subMoji = StringSubstr(Tuuka, 6, MojiCnt – 6);
}
//クロス円
if(StringFind(Tuuka, “JPY”,0) != -1){
//ロット数計算
Kekka = RiyouGaku / Pips /1000;
//ドルストレート
}else if(StringFind(Tuuka, “USD”,3) != -1){
//double Nedan = MarketInfo(“USDJPY”+subMoji, MODE_ASK);
double Nedan = iOpen(“USDJPY”+subMoji,PERIOD_CURRENT,0);
//ロット数計算
Kekka = RiyouGaku / Pips / Nedan / 10;
}else{
Print(“計算対象外通貨です:”+Tuuka);
return(0);
}
//少数点を0.01単位までにする
Kekka = NormalizeDouble(Kekka, 2);
//最大ロット数を超えている場合は、最大ロット数にする
if(Kekka > SaidaiLot){
Kekka = SaidaiLot;
}
return Kekka;
}
お世話になっております。
ソースコードをコピペしてみたところ、文字列がトリミングされているかなにかでうまい事コンパイルできませんでした。
ですのでソースコードをざっと見た感じでお伝えしますと、
まず、『lotSize = funcLotGet(0, stopLossPips, 2);』でlotSize変数にロット数をセットしています。
その後、『OrderKekka = funcOrder_Send(orderPtn – 1, ea_order_stop_price, 0, lotSize, 0);』
で引数にもlotSize変数があり、ロット数を新規注文関数側に引き渡している所まではOKです。
※ただここも引数が1つ少ないですが取り合えず保留
ただし、新規注文関数(funcOrder_Send)に問題点が2点あります。
●1つ目
『bool funcOrder_Send(int ea_order_entry_Type, double ea_order_stop_price, double ea_order_good_price,int orderComment,int ea_order_MagicNo)
{』
ここでロット数を引き取れていないのでロット数が新規注文関数で分からない状態になっています。
●2つ目
新規注文関数(funcOrder_Send)内の、OrderSend()関数のロット数の部分(3つ目の引数)が1固定になっています。
ここは、lotSize変数の値にしてあげる必要があります。
簡単に修正方法です。
【修正方法①】
[OrderKekka = funcOrder_Send(orderPtn – 1, ea_order_stop_price,]でソースコード検索する。
ヒットした箇所を以下のように不足している引数を追加する。
OrderKekka = funcOrder_Send(orderPtn – 1, ea_order_stop_price, 0, 0,lotSize, 0);
【修正方法②】
[bool funcOrder_Send(]でソースコード検索する。
ヒットした箇所を以下のようにロットサイズ引数を追加する。
bool funcOrder_Send(int ea_order_entry_Type, double ea_order_stop_price, double ea_order_good_price,int orderComment,double ea_lotSize,int ea_order_MagicNo)
【修正方法③】
[// ロット[0.01単位](FXTFは1=10Lot)]でソースコード検索する。
ヒットした箇所を以下のようにea_lotSize値を反映する。
ea_lotSize, // ロット[0.01単位](FXTFは1=10Lot)
こんな感じでいけると思います。
ご丁寧な返信ありがとうございます!
できました!嬉しいです(‘◇’)ゞ
はじめまして。
非常に参考になる記事を有難うございます!
固定損失額からのロット計算はまさに今やりたいことで、こちらを参考に取り組んでいるのですが、本文ソースコード内で何点かお教え下さいませんでしょうか。
>>43行目 : string subMoji = “”;
通貨ペア名称保持と説明が入っていますが、こちらの一行はどのような処理をされているのでしょうか?
subMojiに””という文字列を保持???
>>57行目 : double Nedan = iOpen(“USDJPY”+subMoji,PERIOD_CURRENT,0);
iOpen関数一つ目の引数で「USDJPY」ではなく「”USDJPY”+subMoji」とされているのはどういう狙いでしょうか?
お手すきの際にご返答頂けると幸いです!
とくろさん
初めまして、記事を参考にして頂き有難うございます!
ご質問の件ですが、以下回答します。
>>43行目 : string subMoji = “”;
ここは、subMojiというstring(文字列型)の変数を宣言しております。
その後“”となっているのは何も入れていません(空)というのをあえて明記しています。
理由は次に疑問になっている箇所です。
>>57行目 : double Nedan = iOpen(“USDJPY”+subMoji,PERIOD_CURRENT,0);
ここで
「USDJPY」としていない理由としましては、「USDJPY」で取得できないブローカー
(FXTFはUSDJPY-cd,OANDAはUSDJPY.oj1m等)が存在するためです。
上記ブローカーは「USDJPY-cd」といった通貨名称でiOpen()しなければ米ドル/円の価格を取得できません。
ただし、XM等は「USDJPY」で取得できます。
※MT4画面で「USDJPY」となっているブローカーは取得可能
ですので、45行目~47行目で文字数が6文字を超える通貨名称の場合はsubMoji変数に6文字目以降の文字列を格納しています。
if(MojiCnt > 6){
subMoji = StringSubstr(Tuuka, 6, MojiCnt – 6);
}
FXTFだとsubMoji変数にはEURUSD(-cd)とカッコ内の値がセットされます。
※XMは6文字なので初期値(“”)のままです
そして57行目でそれをくっつけて価格を取得しにいくという作りになっております。
●FXTFだと iOpen(“USDJPY”+subMoji(-cd),PERIOD_CURRENT,0);
●XMだと iOpen(“USDJPY”+subMoji(“”),PERIOD_CURRENT,0);
少々ややこしいので不明点等あれば再度ご質問ください。
なるほどです!
この記述なら様々なブローカーに対応可能ですね。
勉強になりました。
大変ご丁寧に有り難うございます!
いつも勉強させていただいております。
この関数を、ゴールドで使用したいのですがどう書き換えればよいのでしょうか?
らいらいさん
記事を参考にして頂き有難うございます!
記事のソースコードを修正(60~63行目)しましたので参考にしてみてください。
元々はゴールドの名称がXAUUSDの通貨ペアであれば使えていたのですが、
XM等ではXAUUSDではなくGOLDやGOLDmicr表記でしたのでうまく動きませんでした。