「固定ロットと複利ロット、結局どちらで取引すればいいのか」を、
既刊の記事では1つのEAで切り替えて2回バックテストし、
CSVログで比較する方法を解説しました。
ただ、2回のバックテストを毎回実行するのは手間です。
この記事では、チャート上のSLラインをドラッグするだけで、
「今この瞬間の」固定ロットと複利ロットの推奨数値を並べて表示する、
発注を一切行わない確認専用のインジケーターを作ります。
・固定ロットと複利ロットを同時に計算して並べる設計
・既刊EA版・既刊リスク%インジケーター版との役割の違い
・OBJ_HLINEとOBJ_LABELを組み合わせたリアルタイム比較表示
・実際に動くコード全体
設計思想: なぜ「今すぐ並べて見る」インジケーターにするのか
まず、既刊の2つの記事と何が違うのかを整理します。
既刊EA版(バックテスト比較)のおさらい
既刊のEA版記事は、
1つのEAにENUMで切り替えられる2つのロット計算を用意し、
同じ条件で2回バックテストした結果をCSVログで比較する設計でした。
既刊リスク%ロット計算インジケーターとの違い
| 記事 | 表示する数値 | 比較の仕方 |
|---|---|---|
| 既刊リスク%ロット計算インジケーター | 複利ロットのみ | 単独の数値を確認する |
| 本記事(固定vs複利 比較インジケーター) | 固定ロット+複利ロット | 2つを並べて差を見る |
既刊のリスク%インジケーターは複利側の数値だけを表示していましたが、
本記事はそこに固定ロットの数値も並べ、「比較」そのものを目的にしています。
発注は一切行わない、確認専用という設計
・このインジケーターは画面に数値を表示するだけで、注文は一切出さない
・バックテストを2回実行する手間がなく、リアルタイムの数値をすぐ確認できる
・実際の発注方法(固定ロットか複利ロットか)は自分で選ぶ前提
OBJ_HLINEでドラッグ可能なSLラインを作る
まず、複利ロット側の計算の基準になるラインを用意します。
OBJ_HLINEの作成
if(ObjectFind(0, LineName) < 0) ObjectCreate(0, LineName, OBJ_HLINE, 0, 0, Bid - 200 * Point); ObjectSetInteger(0, LineName, OBJPROP_COLOR, clrRed); ObjectSetInteger(0, LineName, OBJPROP_STYLE, STYLE_DASH); ObjectSetInteger(0, LineName, OBJPROP_SELECTABLE, true);
水平線オブジェクトを1本だけ作り、マウスでドラッグできるようにします。
ラインの現在価格を取得する
double linePrice = ObjectGetDouble(0, LineName, OBJPROP_PRICE, 0); double stopLossPoints = MathAbs(Bid - linePrice) / Point;
ラインの価格と現在値の差を、損切り幅(ポイント数)に変換します。
固定ロットと複利ロットを同時に計算する
2つのロットの決め方を、同じ関数の中で両方計算します。
固定ロットはそのまま使う
double fixedLot = InpFixedLot;
固定ロット側は、入力値をそのまま表示に使うだけです。
複利ロット(リスク%ベース)の計算
double tickValue = MarketInfo(Symbol(), MODE_TICKVALUE); double tickSize = MarketInfo(Symbol(), MODE_TICKSIZE); double pointValue = tickValue * (Point / tickSize); double riskAmount = AccountBalance() * (InpRiskPercent / 100.0); double rawLot = riskAmount / (stopLossPoints * pointValue);
口座残高にリスク%を掛けた金額を、損切り幅×1ポイントの価値で割って求めます。
LOTSTEPで丸め、MIN/MAXの範囲に収める
double lotStep = MarketInfo(Symbol(), MODE_LOTSTEP); double minLot = MarketInfo(Symbol(), MODE_MINLOT); double maxLot = MarketInfo(Symbol(), MODE_MAXLOT); double compoundLot = MathFloor(rawLot / lotStep) * lotStep; if(compoundLot < minLot) compoundLot = minLot; if(compoundLot > maxLot) compoundLot = maxLot; compoundLot = NormalizeDouble(compoundLot, 2);
切り捨てで丸めるのは、繰り上げるとリスク%をわずかに超えてしまうためです。
OBJ_LABELで2つのロットを並べて表示する
計算した2つの数値を、1つのラベルにまとめます。
2行ラベルの作成
string text = "固定ロット: " + DoubleToString(fixedLot, 2) + "\n" +
"複利ロット(リスク" + DoubleToString(InpRiskPercent, 1) + "%): " +
DoubleToString(compoundLot, 2) + "\n" +
"差: " + DoubleToString(compoundLot - fixedLot, 2);
ObjectSetString(0, LabelName, OBJPROP_TEXT, text);
固定ロット・複利ロット・その差分を、1つのラベルの中に3行で表示します。
EventSetTimerで1秒ごとに更新する
・口座残高はティックが来なくても変動する(スワップ等)
・EventSetTimer(1)で1秒ごとにも更新し、最新の数字を保つ
実際に動くコード全体
ここまでの要素をまとめた、実際にMetaEditorでコンパイルできるコードです。
//+------------------------------------------------------------------+
//| FixedVsCompoundLotIndicator.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
#property indicator_chart_window
#property indicator_buffers 0
input double InpFixedLot = 0.10; //固定ロット数
input double InpRiskPercent = 2.0; //複利ロット計算に使うリスク%
string LineName = "LotCompareSLLine";
string LabelName = "LotCompareLabel";
//+------------------------------------------------------------------+
//| 表示内容を更新する |
//+------------------------------------------------------------------+
void UpdatePanel()
{
double linePrice = ObjectGetDouble(0, LineName, OBJPROP_PRICE, 0);
double stopLossPoints = MathAbs(Bid - linePrice) / Point;
double fixedLot = InpFixedLot;
double compoundLot = 0;
if(stopLossPoints >= 1)
{
double tickValue = MarketInfo(Symbol(), MODE_TICKVALUE);
double tickSize = MarketInfo(Symbol(), MODE_TICKSIZE);
double pointValue = tickValue * (Point / tickSize);
double riskAmount = AccountBalance() * (InpRiskPercent / 100.0);
double rawLot = riskAmount / (stopLossPoints * pointValue);
double lotStep = MarketInfo(Symbol(), MODE_LOTSTEP);
double minLot = MarketInfo(Symbol(), MODE_MINLOT);
double maxLot = MarketInfo(Symbol(), MODE_MAXLOT);
compoundLot = MathFloor(rawLot / lotStep) * lotStep;
if(compoundLot < minLot) compoundLot = minLot;
if(compoundLot > maxLot) compoundLot = maxLot;
compoundLot = NormalizeDouble(compoundLot, 2);
}
string text = "固定ロット: " + DoubleToString(fixedLot, 2) + "\n" +
"複利ロット(リスク" + DoubleToString(InpRiskPercent, 1) + "%): " +
DoubleToString(compoundLot, 2) + "\n" +
"差: " + DoubleToString(compoundLot - fixedLot, 2);
ObjectSetString(0, LabelName, OBJPROP_TEXT, text);
}
//+------------------------------------------------------------------+
//| Custom indicator initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
if(ObjectFind(0, LineName) < 0)
ObjectCreate(0, LineName, OBJ_HLINE, 0, 0, Bid - 200 * Point);
ObjectSetInteger(0, LineName, OBJPROP_COLOR, clrRed);
ObjectSetInteger(0, LineName, OBJPROP_STYLE, STYLE_DASH);
ObjectSetInteger(0, LineName, OBJPROP_SELECTABLE, true);
ObjectCreate(0, LabelName, OBJ_LABEL, 0, 0, 0);
ObjectSetInteger(0, LabelName, OBJPROP_CORNER, CORNER_LEFT_UPPER);
ObjectSetInteger(0, LabelName, OBJPROP_XDISTANCE, 10);
ObjectSetInteger(0, LabelName, OBJPROP_YDISTANCE, 20);
EventSetTimer(1);
UpdatePanel();
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
EventKillTimer();
ObjectDelete(0, LineName);
ObjectDelete(0, LabelName);
}
void OnTimer()
{
UpdatePanel();
}
//+------------------------------------------------------------------+
//| Custom indicator iteration function |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
const int prev_calculated,
const datetime &time[],
const double &open[],
const double &high[],
const double &low[],
const double &close[],
const long &tick_volume[],
const long &volume[],
const int &spread[])
{
UpdatePanel();
return(rates_total);
}
//+------------------------------------------------------------------+
//| ChartEvent function |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
const long &lparam,
const double &dparam,
const string &sparam)
{
if(id == CHARTEVENT_OBJECT_DRAG && sparam == LineName)
UpdatePanel();
}
//+------------------------------------------------------------------+
ラインをドラッグするたびに、2つのロット数値がその場で再計算されます。
MetaEditorでのコンパイル・動作確認手順
実際に動かすまでの手順です。
- MetaEditorで新規インジケーター「FixedVsCompoundLotIndicator」を作成し、上のコードを貼り付けてコンパイルする
- 「0 error(s), 0 warning(s)」になることを確認する
- チャートへ適用し、左上に赤い点線のSLラインと3行のラベルが表示されることを確認する
- ラインをドラッグし、複利ロットと差の行が変わり、固定ロットの行は変わらないことを確認する
よくあるエラーと注意点
実装・運用でつまずきやすいポイントをまとめます。
ラインを動かしても固定ロットは変わらない
・固定ロットはInpFixedLotの入力値をそのまま表示しているだけ
・ラインをドラッグしても固定ロットの行は変化しない(仕様)
・変わるのは複利ロットと差の行だけ
損切り幅が小さすぎると複利ロットが異常に大きくなる
ラインを現在値の近くまでドラッグすると、
stopLossPointsが極端に小さくなり、
計算上の複利ロットが異常に大きくなることがあります。
MODE_MAXLOTでの上限はありますが、
現実的な損切り幅を使っているか目視でも確認してください。
既刊EA版・リスク%インジケーター版と混同する
既刊EA版はバックテストを2回実行してCSVで比較する設計、
既刊リスク%インジケーターは複利側の数値だけを表示する設計で、
本記事はその中間にあたる「2つをリアルタイムで並べて見る」設計です。
よくある質問
固定ロットと複利ロットの比較インジケーターについて、よくある疑問に回答します。
このインジケーターは発注まで行いますか?
行いません。
表示専用で、注文は一切出しません。
実際にどちらのロットで発注するかは、表示された数値を見て自分で判断します。
既刊EA版のバックテスト比較とどちらを使うべきですか?
目的が異なります。
過去データでどちらが有利だったかを検証したい場合は既刊EA版、
今この瞬間の数値をすぐ確認したい場合は本記事のインジケーター版が向いています。
複数の通貨ペアで同時に使えますか?
各チャートに個別に適用すれば使えますが、
通貨ペアごとに独立して計算するため、
複数ペアを同時に持つ場合の合計リスクは別途自分で管理する必要があります。
【MQL4】固定ロットと複利ロットの推奨数値を同時に表示する確認専用インジケーターの作り方(発注は行わない比較ツール):まとめ
この記事では、既刊EA版の「2回バックテストしてCSVで比較する」設計を、
発注を一切行わないインジケーターの形に作り直すことで、
今この瞬間の固定ロットと複利ロットの数値をすぐに比較できるようにしました。
既刊リスク%ロット計算インジケーターが複利側の数値だけを表示していたのに対し、
本記事は固定ロットも並べて表示し、差分まで一目で分かる設計にしています。
OBJ_HLINEのドラッグ検知とOBJ_LABELでの常時表示という、
既刊の2つの技術を組み合わせれば、このような比較ツールに発展させられます。
コメント