目次
- 1 1. MQL5の「invalid volume」とは何か
- 2 2. invalid volumeの主な原因一覧(優先度順)
- 3 3. invalid volumeの解決方法(コピペ可能コード付き)
- 4 4. よくある失敗パターン(実務で多い)
- 5 5. invalid volumeを防ぐ設計ベストプラクティス
- 6 6. まとめと最短解決チェックリスト
- 7 FAQ(よくある質問)
1. MQL5の「invalid volume」とは何か
1.1 invalid volumeエラーの定義
MQL5における「invalid volume」エラーは、注文時に指定したロットサイズ(volume)がブローカーの取引条件に適合していない場合に発生するエラーです。 具体的には、以下のような処理で発生します。OrderSend()(注文送信)OrderCheck()(事前検証)CTradeクラスの売買関数(Buy(),Sell()など)
TRADE_RETCODE_INVALID_VOLUME
意味はシンプルで、
「指定したロットサイズが不正」ということです。
ただし注意点として、この「不正」は単純なミスではなく、
ブローカーごとの厳密なルール違反を指しています。
1.2 なぜ発生するのか(根本原因)
invalid volumeエラーは、以下のいずれかに該当すると発生します。- 最小ロット未満(例:0.01未満)
- ロット刻み違反(例:0.015など中途半端な値)
- 最大ロット超過(例:許容上限を超える)
- 証拠金に対してロットが過大(実質的に不正扱い)
| 項目 | 内容 |
|---|---|
| 最小ロット | 取引できる最小単位 |
| 最大ロット | 1回の注文で許可される上限 |
| ロット刻み | どの単位で増減できるか |
- FX → 正常動作
- CFD → エラー
- 仮想通貨 → エラー
1.3 エラーコードと発生タイミング
invalid volumeエラーは、主に以下のタイミングで検出されます。① OrderCheck(事前検証)
MqlTradeCheckResult check;
OrderCheck(request, check);
- 注文を実際に送る前にチェック
- この段階でエラーが出るのが理想
② OrderSend(実際の注文)
MqlTradeResult result;
OrderSend(request, result);
- 実際に注文したときにエラー発生
- ログにエラーが出る
③ CTradeクラス
CTrade trade;
trade.Buy(volume);
- 内部的にOrderSendを使用
- エラー時は戻り値やログで確認
つまずきやすいポイント(重要)
初心者が特にハマるのは以下です。■ 見た目は正しいのにエラーになる
例:double lot = 0.01;
→ 一見正しいが、実際は
- 最小ロットが0.1のブローカー
- stepが0.1単位
■ NormalizeDoubleで解決できると思っている
NormalizeDouble(lot, 2);
→ これは小数点の丸めであり、ロット刻みの保証ではない
■ バックテストでは動くのに本番で失敗
原因:- テスターと実口座の仕様差
- 証拠金条件の違い
■ シンボル情報を取得していない
SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
を使っていない場合、
ロット制約を無視した実装になりやすい
注意点(実務的に重要)
- ロット仕様は固定ではない(ブローカー依存)
- 同一ブローカーでも銘柄ごとに異なる
- VPS移行や口座変更で突然エラーが出ることもある
2. invalid volumeの主な原因一覧(優先度順)
このセクションで書く要点: 原因を網羅し、「自分のケースがどれか」を即特定できる構造にする。上から順に確認すれば解決できる設計。2.1 最小ロット未満(SYMBOL_VOLUME_MIN違反)
最も多い原因がこれです。 指定したロットが最小ロット未満の場合、即エラーになります。例
double lot = 0.001;
しかしブローカー仕様が以下の場合:
- 最小ロット:0.01
正しい取得方法
double min_lot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
よくある失敗
- 固定値でロットを決めている
- 通貨ペア変更で破綻
- 仮想通貨やCFDで特に発生しやすい
注意点
- 最小ロットは口座タイプでも変わる
- マイクロ口座と通常口座で異なるケースあり
2.2 ロット刻み違反(SYMBOL_VOLUME_STEP)
これは初心者〜中級者が最もハマるポイントです。 ロットは自由な小数ではなく、 決められた刻み(step)でしか指定できません。例
- step:0.01
- 指定ロット:0.015
正しい取得
double step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
NGパターン
double lot = 0.015; // 一見OKだがNG
よくある誤解
- 「小数点2桁ならOK」 → ❌
- 「NormalizeDoubleすればOK」 → ❌
実務的な注意
- 金(GOLD)や指数はstepが0.1のことがある
- 仮想通貨はさらに特殊なstepになることもある
2.3 最大ロット超過(SYMBOL_VOLUME_MAX)
意外と見落とされがちですが、 最大ロットを超えてもエラーになります。例
double lot = 200.0;
しかし
- 最大ロット:100.0
取得方法
double max_lot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
よくある発生パターン
- ナンピンロジック(ロット増加)
- マーチンゲール
- 複利ロジック
注意点
- 最大ロットはブローカー依存
- 通貨ペアごとに異なる
2.4 フリー証拠金不足(実質volume不正)
一見するとvolumeは正しいのに、 証拠金不足でinvalid volume扱いになることがあります。発生パターン
double lot = 1.0;
- 証拠金が足りない → OrderCheckで弾かれる
確認方法
MqlTradeCheckResult check;
OrderCheck(request, check);
よくある誤解
- 「証拠金不足は別エラー」 → 環境によってはvolume扱いになる
実務ポイント
- バックテストでは通る
- 本番で失敗する典型パターン
2.5 シンボル仕様未取得(SymbolInfo未使用)
根本的な設計ミスです。 シンボル情報を取得せずにロットを決めている場合、ほぼ確実に問題が発生します。NG例
double lot = 0.01; // 固定値
正しい方向性
double min = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
double step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
double max = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
なぜ危険か
- ブローカーごとに仕様が違う
- 同じEAでも環境で壊れる
- 長期運用で事故る
つまずきやすいポイント(まとめ)
- stepを考慮していない → 最頻出
- 固定ロット → 環境依存で破綻
- 証拠金無視 → 本番でエラー
- NormalizeDoubleに依存 → 不完全
実務での優先チェック順(重要)
エラーが出たら以下の順で確認:- SYMBOL_VOLUME_MIN
- SYMBOL_VOLUME_STEP
- SYMBOL_VOLUME_MAX
- OrderCheck(証拠金)
- シンボル仕様取得の有無
3. invalid volumeの解決方法(コピペ可能コード付き)
このセクションで書く要点: ブローカー依存の制約をすべて吸収し、常に有効なロットを生成する実装を提示する。再現性重視。3.1 解決の基本方針(重要)
invalid volume対策は、以下の3ステップで確実に解決できます。- シンボル仕様を取得
- ロットを制約内に補正
- OrderCheckで検証
3.2 ロット補正関数(最重要コード)
以下は実務レベルで使える「安全なロット補正関数」です。double NormalizeLot(double lot)
{
double min = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
double max = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
double step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
// ① 最小・最大制限
lot = MathMax(min, lot);
lot = MathMin(max, lot);
// ② stepに合わせて切り捨て
lot = MathFloor(lot / step) * step;
// ③ 念のため丸め
lot = NormalizeDouble(lot, 8);
return lot;
}
3.3 使用例(実践コード)
double lot = 0.015; // 元のロット
lot = NormalizeLot(lot);
// 発注
CTrade trade;
trade.Buy(lot);
ポイント解説
MathFloor()→ step違反を防ぐMathMax/Min()→ 範囲外を防ぐNormalizeDouble()→ 浮動小数誤差対策
3.4 OrderCheckによる事前検証(推奨)
本番運用では必須です。MqlTradeRequest request;
MqlTradeCheckResult check;
request.volume = lot;
request.symbol = _Symbol;
request.action = TRADE_ACTION_DEAL;
request.type = ORDER_TYPE_BUY;
if(!OrderCheck(request, check))
{
Print("OrderCheck failed: ", check.comment);
}
メリット
- 発注前にエラー検出
- ログで原因が明確になる
- デバッグ効率が大幅向上
3.5 証拠金を考慮したロット計算(応用)
より安全にする場合は、証拠金も考慮します。double free_margin = AccountInfoDouble(ACCOUNT_FREEMARGIN);
// 簡易例(環境により調整必要)
double lot = free_margin / 100000.0;
lot = NormalizeLot(lot);
注意点
- 証拠金計算はブローカー依存
- レバレッジにより結果が変わる
- 厳密には
OrderCalcMargin()を使うのが安全
3.6 よくあるNG実装(必ず避ける)
❌ NG① NormalizeDoubleだけ使う
lot = NormalizeDouble(lot, 2);
→ step違反は防げない
❌ NG② 固定ロット
double lot = 0.01;
→ 環境依存で破綻
❌ NG③ minだけチェック
if(lot < min) lot = min;
→ step違反でエラー
つまずきやすいポイント
- stepの存在を忘れる → 最頻出ミス
- MathRoundを使う → 切り上げでmax超過の可能性
- CFDや仮想通貨で崩れる
- VPS移行後に突然エラー
実務での最適構成(推奨)
以下をセットで実装:- NormalizeLot関数
- OrderCheck
- SymbolInfo取得
- 再現性:高
- リスク:低
- 保守性:高
補足(設計レベル)
本質的には「ロットは入力ではなく計算結果」です。 つまり- ユーザー入力 → NG
- 環境依存 → 動的取得
4. よくある失敗パターン(実務で多い)
このセクションで書く要点: invalid volumeは「ロット値そのものの誤り」だけでなく、設計や実装の甘さで再発します。ここでは、実務で特に多い失敗を整理します。4.1 NormalizeDoubleだけで処理している
最も多い失敗の一つが、小数点の桁数だけ合わせれば正しいロットになると考えることです。 たとえば、以下のようなコードです。double lot = 0.015;
lot = NormalizeDouble(lot, 2);
一見すると 0.02 になり、問題なさそうに見えます。
しかし実際には、これはロット刻み(SYMBOL_VOLUME_STEP)に基づく補正ではありません。
たとえば次のような環境では注意が必要です。
- step = 0.1
- min = 0.1
- max = 50.0
0.02 は依然として無効です。
つまり NormalizeDouble() は表示上の丸めに近く、取引条件への適合判定そのものはしていないということです。
よくある誤解
- 小数点2桁ならFXは全部通る → 誤り
NormalizeDouble()を使えば安全 → 不十分
注意点
- step は
0.01とは限らない - CFD、指数、仮想通貨では特に崩れやすい
4.2 固定ロットをそのまま使っている
次に多いのが、ロットを固定値でハードコードしているケースです。double lot = 0.01;
trade.Buy(lot);
この実装は、検証環境では通っても、本番で失敗しやすいです。理由は単純で、ブローカーや銘柄ごとに取引条件が違うからです。
たとえば 0.01 ロットが有効なのは、あくまでそのシンボル・その口座タイプで最小ロットが 0.01 の場合だけです。
別の環境では、次のような違いがあり得ます。
- 最小ロットが
0.1 - step が
0.1 - 一部銘柄だけ
1.0単位
よくある失敗
- テストで通ったから本番でも通ると思う
- 通貨ペアだけ見て、他銘柄でも同じ設定を使う
- 口座変更後に不具合が出てもEA側を疑わない
実務上の結論
ロットは固定値で扱わず、必ずシンボル情報から補正する設計にするべきです。4.3 証拠金を考慮していない
ロットの min / max / step だけ見ていても、証拠金不足で発注失敗することがあります。 このとき、環境や実装によっては volume 周辺の問題として見えるため、原因を誤認しやすいです。 たとえば、次のようなケースです。- 計算上は step に適合
- min / max も範囲内
- しかし必要証拠金が足りない
確認すべき処理
MqlTradeCheckResult check;
OrderCheck(request, check);
よくある失敗
- volume補正だけ実装して満足する
OrderCheck()を省略する- バックテストで通るので証拠金を軽視する
注意点
- 実口座ではレバレッジ条件の影響が大きい
- 同じロットでも口座残高や保有ポジション数で結果が変わる
- ナンピンや複利EAでは特に発生しやすい
4.4 通貨ペアごとの仕様差を無視している
初心者が見落としやすいのが、銘柄ごとにロット仕様が異なることです。 EAを_Symbol 前提で書いていても、実際にはその _Symbol の性質が毎回同じとは限りません。
特に差が出やすいのは以下です。
- FX通貨ペア
- ゴールド
- 株価指数
- 仮想通貨CFD
- エネルギー系CFD
SYMBOL_VOLUME_MINSYMBOL_VOLUME_STEPSYMBOL_VOLUME_MAX
ありがちな失敗例
- EURUSDで作ったEAを、そのままGOLDに使う
- BTC系銘柄で0.01ロット前提のまま発注する
- stepを共通化してしまう
実務的な対策
- シンボルごとに毎回仕様を取得する
- OnInit時にログ出力して確認する
- 複数銘柄EAでは銘柄別に補正する
4.5 ロット補正後の値を確認していない
補正関数を入れていても、補正後の最終ロットをログで確認していないと、原因特定が難しくなります。 たとえば、補正後に次のようなことが起きます。- 期待は
0.015 - 実際は
0.01 - あるいは
0.1に補正されていた
推奨ログ例
double min = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
double max = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
double step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
Print("min=", min, " max=", max, " step=", step, " lot=", lot);
注意点
- 本番障害の多くは「情報不足」で調査が長引く
- ログがあれば再現性の確認がしやすい
つまずきやすい点のまとめ
invalid volumeが再発するEAには、次の特徴があります。NormalizeDouble()に依存している- ロットを固定値で持っている
OrderCheck()を使っていない- シンボルごとの差を吸収していない
- ログがなく、補正結果を確認できない
5. invalid volumeを防ぐ設計ベストプラクティス
このセクションで書く要点: 単発の修正ではなく、再発しない設計にする。環境差・長期運用・複数口座を前提にした実務レベルの対策を整理。5.1 ロットは「入力値」ではなく「計算結果」として扱う
最も重要な考え方はこれです。ロットは固定値ではなく、環境に応じて決まる「結果」である
NG設計
double lot = 0.01;
これは「入力値」であり、
環境が変わると即破綻します。
OK設計(基本構造)
double base_lot = 0.01; // 基準値
double lot = NormalizeLot(base_lot);
もしくは
double lot = CalculateLotByRisk();
lot = NormalizeLot(lot);
実務ポイント
- ユーザー入力をそのまま使わない
- 必ず補正レイヤーを挟む
- 「最終ロット」を別変数で管理する
5.2 シンボル仕様は毎回取得する(キャッシュしない)
一見効率化のためにキャッシュしたくなりますが、 シンボル仕様は変わる可能性があります。取得すべき値
SYMBOL_VOLUME_MIN
SYMBOL_VOLUME_MAX
SYMBOL_VOLUME_STEP
NGパターン
- OnInitで1回だけ取得
- グローバル変数に固定
- 複数銘柄で共通化
推奨
double min = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
double max = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
double step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
必要なタイミングで都度取得する
理由(構造的)
- ブローカー仕様変更
- 銘柄切替
- サーバ再接続
- VPS移行
5.3 OrderCheckを必ず通す(本番前提)
本番運用では必須です。推奨フロー
lot = NormalizeLot(lot);
MqlTradeCheckResult check;
if(!OrderCheck(request, check))
{
Print("Check error: ", check.comment);
return;
}
なぜ必要か
- volume以外のエラーも検出できる
- 証拠金不足を事前に回避
- 本番事故を防ぐ
よくある誤解
- 「テストで通るから不要」 → ❌
- 「軽量化のため外す」 → ❌
5.4 ログ設計を組み込む(デバッグ前提)
運用中のトラブルは必ず発生します。 そのため、ログを前提に設計する必要があります。最低限出すべきログ
Print("Symbol=", _Symbol);
Print("min=", min, " max=", max, " step=", step);
Print("lot(before)=", raw_lot, " lot(after)=", lot);
重要ポイント
- 補正前後の差分を記録
- シンボル情報を記録
- エラーコードも出す
効果
- 原因特定が即できる
- 再現性が上がる
- 外部環境の影響を切り分け可能
5.5 複数銘柄対応を前提に設計する
EAは単一銘柄だけで動かすとは限りません。 むしろ実運用では複数銘柄対応が標準です。NG設計
double min = 0.01; // 固定
OK設計
double min = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
注意点
_Symbol固定は危険- 引数でsymbolを渡す設計が望ましい
- マルチシンボルEAでは必須
5.6 テスト環境と本番環境を分離して考える
多くの事故はここで起きます。よくある問題
- バックテスト → OK
- デモ口座 → OK
- 本番口座 → invalid volume
原因
- ロット仕様が違う
- レバレッジが違う
- 証拠金条件が違う
対策
- 本番と同条件のデモで検証
- ログ比較
- OrderCheck結果の確認
5.7 リスク管理とロットの関係(重要)
ロットは単なる技術パラメータではなく、 リスク管理そのものです。推奨設計
double risk_percent = 1.0; // 1%
double lot = CalculateLotByRisk(risk_percent);
lot = NormalizeLot(lot);
メリット
- DD(ドローダウン)制御
- 資金変動への追従
- 長期運用の安定性向上
注意点
- 計算ロジックはブローカー依存
- 通貨ペアごとに調整が必要
つまずきやすいポイント
- 「動いたからOK」で設計を止める
- ロットを固定値で持ち続ける
- OrderCheckを省略
- ログを出さない
実務的まとめ(重要)
invalid volumeを防ぐ最短ルートは以下です。- NormalizeLotを実装
- SymbolInfoを毎回取得
- OrderCheckを通す
- ログを出す
6. まとめと最短解決チェックリスト
このセクションで書く要点: ここまでの内容を「即使える判断基準」に圧縮し、初心者でも迷わず解決できる状態にする。6.1 invalid volumeの本質(再確認)
invalid volumeは単なるエラーではなく、 **「ブローカーの取引ルール違反」**です。 つまり、- コードが間違っているというより
- 環境に適合していない
6.2 最短で解決するチェックリスト
エラーが出たら、以下を上から順に確認してください。✔ ① 最小ロットを満たしているか
double min = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
- lot >= min か?
- 口座タイプの違いも確認
✔ ② ロット刻みに合っているか
double step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
- lot % step == 0 になっているか?
- NormalizeDoubleではなく、step基準で補正しているか
✔ ③ 最大ロットを超えていないか
double max = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
- lot <= max か?
- ナンピン・複利で超えていないか
✔ ④ OrderCheckを通しているか
OrderCheck(request, check);
- check.comment を確認
- 証拠金不足の可能性
✔ ⑤ ロット補正を実装しているか
lot = NormalizeLot(lot);
- 固定値のまま使っていないか
- step対応しているか
6.3 最短解決コード(再掲・実務用)
迷った場合はこれを使えばOKです。double NormalizeLot(double lot)
{
double min = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
double max = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
double step = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
lot = MathMax(min, lot);
lot = MathMin(max, lot);
lot = MathFloor(lot / step) * step;
return NormalizeDouble(lot, 8);
}
6.4 よくある誤解(再整理)
- NormalizeDoubleだけでOK → ❌
- 0.01ならどこでも使える → ❌
- テストで通れば問題ない → ❌
- FXは全部同じ仕様 → ❌
6.5 実務視点での重要ポイント
■ 再現性
- 環境が変わっても動くか?
- VPS移行後も問題ないか?
■ リスク管理
- ロットが暴走しないか?
- 証拠金不足にならないか?
■ 保守性
- 他人が見ても理解できるか?
- ログで原因追跡できるか?
6.6 結論(重要)
invalid volume対策は単純です。ロットを「固定値」ではなく「環境適合値」として扱うこれだけです。 この設計に変えることで、
- エラー率:大幅減少
- デバッグコスト:大幅削減
- 長期運用安定性:向上