MQL5でSL・TPを設定する方法|OrderSend・PositionModify・invalid stops対策まで完全解説

目次

1. MQL5におけるSL・TP設定の基本

このセクションで書く要点
・SL(損切り)とTP(利確)の役割
・MQL5における設定方法の全体像
・なぜ初心者がつまずくのか


1.1 SL(Stop Loss)とTP(Take Profit)とは

SL(Stop Loss)とは、損失を一定の価格で自動的に確定する注文です。
TP(Take Profit)は、利益を一定の価格で確定する注文です。

例えば、以下のようなイメージです。

  • BUY(買い)の場合
    • SL:現在価格より下に設定(損切り)
    • TP:現在価格より上に設定(利確)
  • SELL(売り)の場合
    • SL:現在価格より上に設定
    • TP:現在価格より下に設定

この2つは単なる補助機能ではなく、EA(自動売買)のリスク管理の中核です。
特にSLが未設定の場合、相場が逆行した際に損失が無制限に拡大するリスクがあります。


1.2 なぜSL/TP設定が重要なのか(リスク管理の観点)

MQL5でのSL/TP設定は、単なる「機能」ではなく、期待値とドローダウン(最大損失)を制御するための必須要素です。

主な目的は以下です。

  • 損失の上限を固定する(リスク制御)
  • 利益確定を自動化する(感情排除)
  • EAの再現性を担保する(ルール化)

例えば、SLを設定しないEAは以下の問題を抱えます。

  • 想定外のドローダウン増大
  • ロット管理と整合しない
  • フォワードテストと実運用の乖離

逆に、適切なSL/TPを設定することで

  • PF(プロフィットファクター)の安定
  • DD(ドローダウン)の抑制
  • 戦略の再現性向上

といった効果が得られます。


1.3 MQL5でのSL/TP設定方法の全体像(3パターン)

MQL5では、SL/TPの設定方法は大きく3つに分かれます。

① 注文時に設定(最も基本)

注文を出すタイミングで同時に設定する方法です。

  • OrderSendsl / tp を使用
  • 最もシンプルで推奨される方法
request.sl = price - 100 * _Point;
request.tp = price + 100 * _Point;

② 注文後に設定・変更

既に保有しているポジションに対して設定・変更します。

  • PositionModify を使用
  • 動的な戦略(途中変更)に対応可能

③ トレーリングストップ

価格に追従してSLを動かす方法です。

  • 利益を伸ばしつつ損失を抑える
  • 固定TPとは別概念

つまずきやすいポイント・注意点

初心者が最もハマるポイントは以下です。

■ BUYとSELLで方向が逆

  • BUY:SLは下、TPは上
  • SELL:SLは上、TPは下
    → これを間違えると「invalid stops」エラーになります

■ pipsとpointの混同

  • _Point:最小価格単位
  • pips:通貨ごとの慣習単位
    → 計算ミスの原因No.1

■ SL/TP未設定で注文

  • 後から設定できるが、リスクが高い
    → 原則「注文時設定」が安全

■ ブローカー制約を無視

  • 最低ストップ距離(StopLevel)が存在
    → 近すぎるとエラーになる

※これらは環境(ブローカー・通貨ペア)により異なるため、実行前の確認が必要です。


よくある失敗

  • SL/TPの方向を逆に設定
  • 値幅が小さすぎて注文拒否
  • Ask/Bidを考慮していない
  • 価格計算を固定値で書いてしまう

2. MQL5でSL・TPを設定する3つの方法

このセクションで書く要点
・注文時・後から変更・トレーリングの違い
・初心者はどれを使うべきか


2.1 注文時にSL/TPを同時設定する(基本)

最も基本かつ推奨される方法は、注文と同時にSL/TPを設定する方法です。
MQL5では MqlTradeRequest 構造体の sl / tp に価格を指定します。

手順(基本フロー)

  • 現在価格(Ask / Bid)を取得
  • SL / TPの価格を計算
  • request.sl / request.tp に設定
  • OrderSend を実行

コード例(BUY)

double price = SymbolInfoDouble(_Symbol, SYMBOL_ASK);

MqlTradeRequest request;
MqlTradeResult result;

ZeroMemory(request);

request.action = TRADE_ACTION_DEAL;
request.symbol = _Symbol;
request.volume = 0.1;
request.type   = ORDER_TYPE_BUY;
request.price  = price;

// SL / TP設定
request.sl = price - 100 * _Point;
request.tp = price + 100 * _Point;

OrderSend(request, result);

ポイント

  • _Point は最小価格単位(例:0.00001)
  • BUY時は「SLは下・TPは上」
  • SELL時は逆になる

初心者への結論

まずはこの方法だけ使えばOK
最もシンプルでエラーが少なく、実運用にも適しています。


2.2 注文後にSL/TPを変更する(PositionModify)

すでに保有しているポジションに対して、後からSL/TPを設定・変更する方法です。
動的な戦略(条件に応じて変更)で使われます。

手順

  • 対象ポジションを選択(PositionSelect)
  • 現在のSL/TPを取得
  • 新しい値で PositionModify を実行

コード例

if(PositionSelect(_Symbol))
{
    double new_sl = SymbolInfoDouble(_Symbol, SYMBOL_BID) - 100 * _Point;
    double new_tp = SymbolInfoDouble(_Symbol, SYMBOL_BID) + 100 * _Point;

    trade.PositionModify(_Symbol, new_sl, new_tp);
}

tradeCTrade クラスのインスタンス

注意点

  • ポジション未選択だと失敗する
  • _Symbol ではなくチケット指定の方が安全なケースあり
  • 変更頻度が高すぎると拒否される場合あり(環境依存)

2.3 トレーリングストップとの違い

トレーリングストップは、価格に応じてSLを自動的に引き上げる仕組みです。
TPとは異なり、「利益を伸ばすための動的なSL」です。

比較

項目固定SL/TPトレーリング
性質固定動的
利益確定TPで固定SLで追従
実装難易度

基本ロジック

if(PositionSelect(_Symbol))
{
    double current_price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
    double new_sl = current_price - 50 * _Point;

    // 現在のSLより有利な場合のみ更新
    if(new_sl > PositionGetDouble(POSITION_SL))
    {
        trade.PositionModify(_Symbol, new_sl, PositionGetDouble(POSITION_TP));
    }
}

つまずきやすいポイント・注意点

■ 注文時に設定しない問題

→ 「後で設定すればいい」と考えがち
→ 実運用では瞬間的な暴落で未設定状態が致命傷になる

■ トレーリングとTPの混同

→ TPは固定、トレーリングはSLの更新
→ 役割が完全に異なる

■ PositionModifyの失敗

  • ポジション未選択
  • 値が近すぎる(StopLevel違反)
  • 同値で更新しようとして無効

よくある失敗

  • SL/TPを0のまま注文(未設定)
  • BUYなのにSLを上に設定
  • トレーリングでSLが下がる(逆方向更新)
  • 更新条件を入れていないため無限ループ

初心者向けの結論

  • まずは 注文時設定(OrderSend)だけ使う
  • 慣れてから PositionModify
  • トレーリングは最後

3. OrderSendでSL・TPを設定する実装詳細

このセクションで書く要点
・MqlTradeRequestの構造と必須項目
・SL/TP設定の正しい書き方
・実務でそのまま使えるコード


3.1 MqlTradeRequest構造体の基本

MQL5では、注文は MqlTradeRequest 構造体に情報を詰めて OrderSend に渡します。
この中にSL/TPも含めて指定します。

主な項目

項目内容
action注文タイプ(成行・指値など)
symbol通貨ペア
volumeロット数
typeBUY / SELL
price注文価格
sl損切り価格
tp利確価格

最低限必要な項目

request.action
request.symbol
request.volume
request.type
request.price

SL/TPは任意ですが、実務では必須レベルです。


3.2 SL/TPの正しい設定方法(BUY/SELL別)

SL/TPは「価格」で指定します。
ポイント数ではなく価格値である点が重要です。

BUYの場合

request.sl = price - 100 * _Point;
request.tp = price + 100 * _Point;

SELLの場合

request.sl = price + 100 * _Point;
request.tp = price - 100 * _Point;

なぜこうなるのか

  • BUY:価格上昇で利益 → TPは上
  • SELL:価格下落で利益 → TPは下

方向を間違えると即エラー(invalid stops)


3.3 実務で使える完全コード例

以下は、エラー対策も含めた実用コードです。

#include <Trade/Trade.mqh>
CTrade trade;

void OpenBuy()
{
    double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);

    // 最低ストップ距離取得
    int stopLevel = (int)SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL);

    double sl = ask - (stopLevel + 10) * _Point;
    double tp = ask + (stopLevel + 10) * _Point;

    MqlTradeRequest request;
    MqlTradeResult result;

    ZeroMemory(request);

    request.action = TRADE_ACTION_DEAL;
    request.symbol = _Symbol;
    request.volume = 0.1;
    request.type   = ORDER_TYPE_BUY;
    request.price  = ask;
    request.sl     = NormalizeDouble(sl, _Digits);
    request.tp     = NormalizeDouble(tp, _Digits);
    request.deviation = 10;

    if(!OrderSend(request, result))
    {
        Print("OrderSend failed: ", result.retcode);
    }
}

3.4 NormalizeDoubleと桁数の重要性

価格は必ず、通貨ペアの桁数に合わせて丸める必要があります。

NormalizeDouble(price, _Digits);

理由

  • 不正な桁数 → 注文拒否
  • ブローカー仕様に依存

  • EURUSD(5桁)→ _Digits = 5
  • USDJPY(3桁)→ _Digits = 3

つまずきやすいポイント・注意点

■ SL/TPをポイントで指定してしまう

→ 必ず「価格」で指定する
_Point を掛けて変換する

■ stopLevelを無視

→ 最低距離未満は拒否される
→ 環境ごとに異なる

■ Ask/Bidの使い分けミス

  • BUY:Ask
  • SELL:Bid
    → 逆にすると価格ズレ

■ NormalizeDouble未使用

→ 桁エラーで失敗するケースあり


よくある失敗

  • SL/TPを0のまま送信
  • 桁数を合わせずにエラー
  • stopLevelを考慮せず注文拒否
  • priceとSL/TPの整合性が崩れる

実務上のベストプラクティス

  • SL/TPは必ず注文時に設定
  • stopLevel + αで余裕を持たせる
  • NormalizeDoubleで整形
  • エラーコードを必ずログ出力

4. 「invalid stops」エラーの原因と対処法

このセクションで書く要点
・invalid stopsの発生条件
・具体的な原因の分類
・再現性のある対処方法


4.1 invalid stopsとは何か

invalid stops は、SL(損切り)またはTP(利確)の設定が不正な場合に発生するエラーです。
MQL5では、OrderSendPositionModify 実行時に返されます。

主な特徴:

  • 注文自体は有効でも、SL/TPのみが不正
  • ブローカー側の制約に違反しているケースが多い
  • 初心者が最も遭遇しやすいエラー

4.2 主な原因(5パターン)

① SL/TPの方向ミス

最も多い原因です。

  • BUYなのにSLが上にある
  • SELLなのにTPが上にある

価格の上下関係が逆だと即エラー


② 最低ストップ距離(StopLevel)違反

ブローカーには、SL/TPを現在価格から一定距離以上離す必要がある制約があります。

int stopLevel = (int)SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL);
  • 単位:ポイント(point)
  • 通貨ペア・ブローカーごとに異なる

→ これ未満だと invalid stops


③ スプレッド未考慮

特にBUY注文で発生しやすい問題です。

  • BUYはAskで約定
  • SL/TPはBidベースで判定されるケースあり

→ スプレッド分を考慮しないと距離不足になる


④ 価格の桁数ミス

NormalizeDouble(price, _Digits);
  • 桁数不一致 → 不正価格扱い
  • 特にUSDJPYなどで発生しやすい

⑤ 現在価格に近すぎる

理論上正しくても、以下のケースでNGになります。

  • 約定直後にSLを近距離で設定
  • 急変動中で価格がずれる

実行タイミング依存のエラー


4.3 対処法(実務テンプレート)

以下は、実務でそのまま使える対策コードです。

double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);

int stopLevel = (int)SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL);

// 安全マージンを追加
double buffer = (stopLevel + 10) * _Point;

// BUY例
double sl = NormalizeDouble(ask - buffer, _Digits);
double tp = NormalizeDouble(ask + buffer, _Digits);

4.4 実務でのチェックリスト

注文前に以下を確認するだけで、ほぼ防げます。

  • SL < price < TP(BUYの場合)
  • TP < price < SL(SELLの場合)
  • stopLevel以上の距離を確保
  • NormalizeDouble済み
  • Ask/Bidの整合性OK

つまずきやすいポイント・注意点

■ StopLevelは常に同じではない

→ 時間帯や銘柄で変わる場合あり
→ 毎回取得するのが安全

■ スプレッド拡大時の誤動作

→ 指標発表時などで急拡大
→ SLが無効になるケースあり

■ 成行と指値で挙動が違う

→ pending注文では条件がさらに厳しい


よくある失敗

  • stopLevelを0と誤認(実際は内部制約あり)
  • SL/TPをギリギリに設定しすぎる
  • スプレッドを考慮しない
  • デバッグログを見ない

実務上のベストプラクティス

  • stopLevel + α(10〜20ポイント)で設定
  • NormalizeDoubleを必ず使用
  • エラーコード(result.retcode)をログ出力
  • 環境差(ブローカー差)を前提に設計

5. pips・point・価格の違いとSL/TP計算の正しい方法

このセクションで書く要点
・pipsとpointの違いを明確化
・SL/TP計算で混乱しやすいポイントの整理
・実務で使える安全な計算方法


5.1 pipsとpointの違い(最重要)

MQL5で最も混乱しやすいのが、pipsとpointの違いです。

用語の定義

  • point:最小価格単位(_Point
  • pips:一般的な価格変動単位(通貨ペアごとに異なる)

具体例

通貨ペア_Point1 pips
EURUSD(5桁)0.000010.0001
USDJPY(3桁)0.0010.01

つまり、

  • EURUSD:1 pips = 10 point
  • USDJPY:1 pips = 10 point

→ 多くの環境で「1 pips = 10 point」だが、例外もあるため注意


5.2 なぜ混乱が起きるのか

MQL5ではすべて「point基準」で処理されます。
しかし、トレーダーは通常「pips」で考えます。

このズレにより、

  • 設定値が10倍ズレる
  • SLが近すぎてエラー
  • TPが遠すぎて非現実的

といった問題が発生します。


5.3 pipsを使った安全なSL/TP計算

実務では、pips入力 → point変換 → 価格計算の流れが安全です。

例:20pipsのSL/TPを設定

double pips = 20;

// 1 pips = 10 point(一般的な前提)
double pip_value = 10 * _Point;

double sl = ask - pips * pip_value;
double tp = ask + pips * pip_value;

5.4 汎用的なpips計算関数(推奨)

通貨ペアごとの差異を吸収するため、以下のような関数を用意すると安全です。

double GetPipValue()
{
    if(_Digits == 3 || _Digits == 5)
        return 10 * _Point;
    else
        return _Point;
}

使用例

double pip = GetPipValue();

double sl = NormalizeDouble(ask - 20 * pip, _Digits);
double tp = NormalizeDouble(ask + 20 * pip, _Digits);

5.5 SELL時の計算(注意)

SELLは方向が逆になります。

double pip = GetPipValue();

double sl = NormalizeDouble(bid + 20 * pip, _Digits);
double tp = NormalizeDouble(bid - 20 * pip, _Digits);

つまずきやすいポイント・注意点

■ pipsをそのまま使う

→ MQL5では通用しない
→ 必ずpoint換算が必要

■ 通貨ペアごとの差を無視

→ JPY系とそれ以外で桁が違う
_Digits を必ず確認

■ 固定値で書く

→ 0.0001などのハードコードは危険
→ 銘柄変更で崩壊する


よくある失敗

  • 20pipsのつもりが2pipsになる
  • SLが近すぎてinvalid stops
  • TPが遠すぎて現実的でない
  • 通貨ペア変更でロジック崩壊

実務上のベストプラクティス

  • pips → point変換関数を必ず用意
  • _Digits ベースで設計
  • NormalizeDoubleで整形
  • SL/TP計算は共通関数化

6. 実務でそのまま使えるSL・TP設定テンプレート(再利用コード)

このセクションで書く要点
・再利用可能な関数設計
・BUY/SELL両対応のテンプレート
・実運用で壊れにくい実装


6.1 テンプレート設計の考え方

SL/TP設定は毎回個別に書くと、以下の問題が発生します。

  • バグ混入(方向ミス・桁ミス)
  • 通貨ペア変更で崩壊
  • メンテナンス性低下

→ 解決策は、共通関数化(テンプレート化)です。


6.2 汎用SL/TP計算関数

まずは、pipsベースで安全に計算する関数を定義します。

double GetPipValue()
{
    if(_Digits == 3 || _Digits == 5)
        return 10 * _Point;
    else
        return _Point;
}

6.3 SL/TP計算テンプレート(BUY/SELL対応)

bool CalculateSLTP(bool isBuy, double price, double sl_pips, double tp_pips, double &sl, double &tp)
{
    double pip = GetPipValue();

    if(isBuy)
    {
        sl = price - sl_pips * pip;
        tp = price + tp_pips * pip;
    }
    else
    {
        sl = price + sl_pips * pip;
        tp = price - tp_pips * pip;
    }

    sl = NormalizeDouble(sl, _Digits);
    tp = NormalizeDouble(tp, _Digits);

    return true;
}

6.4 StopLevel対応込みテンプレート(実務向け)

さらに安全性を高めるため、最低ストップ距離(StopLevel)を考慮します。

bool AdjustToStopLevel(double price, double &sl, double &tp)
{
    int stopLevel = (int)SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL);
    double min_distance = (stopLevel + 10) * _Point;

    // SL距離チェック
    if(MathAbs(price - sl) < min_distance)
    {
        if(sl < price)
            sl = price - min_distance;
        else
            sl = price + min_distance;
    }

    // TP距離チェック
    if(MathAbs(price - tp) < min_distance)
    {
        if(tp > price)
            tp = price + min_distance;
        else
            tp = price - min_distance;
    }

    sl = NormalizeDouble(sl, _Digits);
    tp = NormalizeDouble(tp, _Digits);

    return true;
}

6.5 実際の注文コード(完全版)

void OpenTrade(bool isBuy)
{
    double price = isBuy ? 
        SymbolInfoDouble(_Symbol, SYMBOL_ASK) : 
        SymbolInfoDouble(_Symbol, SYMBOL_BID);

    double sl, tp;

    // SL/TP計算
    CalculateSLTP(isBuy, price, 20, 40, sl, tp);

    // StopLevel調整
    AdjustToStopLevel(price, sl, tp);

    MqlTradeRequest request;
    MqlTradeResult result;

    ZeroMemory(request);

    request.action = TRADE_ACTION_DEAL;
    request.symbol = _Symbol;
    request.volume = 0.1;
    request.type   = isBuy ? ORDER_TYPE_BUY : ORDER_TYPE_SELL;
    request.price  = price;
    request.sl     = sl;
    request.tp     = tp;
    request.deviation = 10;

    if(!OrderSend(request, result))
    {
        Print("Order failed: ", result.retcode);
    }
}

つまずきやすいポイント・注意点

■ SL/TPを関数外で直接書く

→ バグの温床
→ 必ず共通関数にまとめる

■ StopLevel未対応

→ 環境依存で突然エラー
→ 実運用では必須

■ pip変換を省略

→ 通貨ペア変更で崩壊


よくある失敗

  • BUY/SELLの条件分岐ミス
  • NormalizeDoubleを忘れる
  • StopLevelを考慮しない
  • 同一コードをコピペして分散管理

実務上のベストプラクティス

  • SL/TPは関数化して再利用
  • pipsベースで設計
  • StopLevel + バッファを必ず入れる
  • すべての価格をNormalizeDouble

7. 初心者向け:最短で動くシンプル実装(最小コード)

このセクションで書く要点
・余計な要素を削ぎ落とした最小構成
・まず動かすことを優先した実装
・どこを後で改善すべきか


7.1 最小構成の考え方

初心者はまず、「正しく動く最小コード」から始めるべきです。
最初から汎用化・最適化を行うと、以下の問題が起きます。

  • エラー原因が特定できない
  • ロジックが複雑化
  • 学習コストが上がる

→ まずは「1通貨・固定pips・注文時設定」で十分です。


7.2 最小BUY注文コード(SL/TP付き)

#include <Trade/Trade.mqh>

void OnTick()
{
    static bool executed = false;
    if(executed) return;

    double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);

    // 固定値(20pips / 40pips)
    double sl = ask - 200 * _Point;
    double tp = ask + 400 * _Point;

    MqlTradeRequest request;
    MqlTradeResult result;

    ZeroMemory(request);

    request.action = TRADE_ACTION_DEAL;
    request.symbol = _Symbol;
    request.volume = 0.1;
    request.type   = ORDER_TYPE_BUY;
    request.price  = ask;
    request.sl     = NormalizeDouble(sl, _Digits);
    request.tp     = NormalizeDouble(tp, _Digits);
    request.deviation = 10;

    OrderSend(request, result);

    executed = true;
}

7.3 このコードのポイント

  • 1回だけ実行(static変数)
  • SL/TPは固定値(まずは理解優先)
  • NormalizeDoubleで整形

なぜ200・400なのか

  • 5桁環境の場合
    • 200 point = 20 pips
    • 400 point = 40 pips

→ シンプルに「20pips / 40pips」を表現


7.4 この段階でやらなくていいこと

初心者は以下を無理に入れる必要はありません。

  • 汎用関数化
  • StopLevel調整
  • トレーリングストップ
  • 複雑な条件分岐

→ まずは「動くこと」が最優先


つまずきやすいポイント・注意点

■ 何度も注文される

OnTick は毎ティック実行
→ staticフラグで制御必須

■ SL/TPが逆方向

→ BUY:SL下・TP上

■ pointとpipsの混同

→ 200 = 20pips(5桁の場合)

■ デモ環境と実環境の違い

→ スプレッド・StopLevelが異なる


よくある失敗

  • 無限に注文が発行される
  • SL/TP未設定で注文
  • 数値の意味を理解せずコピペ
  • いきなり複雑なEAを作ろうとする

次にやるべき改善(ステップアップ)

このコードが理解できたら、次にやるべきは以下です。

  • pips変換関数の導入
  • StopLevel対応
  • SELL対応
  • PositionModifyの追加

8. SL/TP設計の戦略的考え方(EA設計)

このセクションで書く要点
・SL/TPは単なる設定値ではなく戦略設計そのもの
・期待値、再現性、ドローダウン管理との関係
・固定幅と動的幅の使い分け


8.1 SL/TPは「注文の付属設定」ではなく戦略本体

MQL5でSL(損切り)とTP(利確)を設定する場面では、つい「注文時に必要な値」として扱いがちです。
しかし実務では、SL/TPはエントリー条件と同じか、それ以上に重要な戦略パラメータです。

なぜなら、最終的な損益は以下の組み合わせで決まるからです。

  • どこで入るか
  • どこで切るか(SL)
  • どこで利益確定するか(TP)
  • 何ロットで入るか

このうち、SL/TPは損益分布を直接決める要素です。
エントリー精度が同じでも、SL/TP設計が違えば、PF(プロフィットファクター)やDD(ドローダウン)は大きく変わります。

例えば、同じBUYシグナルでも

  • SL 10pips / TP 20pips
  • SL 30pips / TP 30pips
  • SL 50pips / TP 100pips

では、必要勝率も損益の揺れ方もまったく異なります。


8.2 リスクリワード比(RR)を先に決める

SL/TP設計で最初に考えるべきなのは、RR(Risk Reward Ratio:損失に対する利益の比率)です。

  • SL 20pips / TP 40pips → RR 1:2
  • SL 30pips / TP 30pips → RR 1:1
  • SL 40pips / TP 20pips → RR 2:1

一般に、RRが高いほど1回の利益は大きくなりますが、利確到達率は下がりやすいです。
逆に、RRが低いほど勝率は上がりやすいものの、1回の負けの重みが増します。

実務上の見方

  • 短期メリット
    • RRを固定するとバックテスト比較がしやすい
    • EAの設計が単純になる
  • 長期メリット
    • ロット管理と結びつけやすい
    • 損益の再現性が上がる

初心者向けの目安

まずは以下のどちらかから始めると整理しやすいです。

  • 保守型:1:1〜1:1.5
  • 利益伸ばし型:1:2前後

ただし、正解は固定ではありません。
勝率、スプレッド、約定滑り、通貨ペア特性により最適値は変わります。


8.3 固定SL/TPと動的SL/TPの違い

SL/TP設計は大きく分けて2種類あります。

固定幅型

例:常にSL 20pips、TP 40pips

メリット

  • 実装が簡単
  • バックテスト比較が明確
  • 初心者でも管理しやすい

デメリット

  • 相場のボラティリティ変化に弱い
  • 低ボラ時は遠すぎ、高ボラ時は近すぎることがある

動的型

例:ATR(平均的な値幅)に応じてSL/TPを変える

メリット

  • 相場環境に適応しやすい
  • 異常な近すぎSLを避けやすい
  • 通貨ペアごとの差を吸収しやすい

デメリット

  • 実装が複雑
  • テスト条件を揃えにくい
  • 初学者には原因分析が難しい

実務上の推奨順

  • 最初は 固定幅型
  • その後、必要に応じて ATR型などの動的型

この順番の方が、どこで成績が変わったのかを追いやすくなります。


8.4 PF・DDへの影響をどう見るか

SL/TPの設計は、見た目の勝率だけでなく、PFとDDの形に直結します。

固定SLが狭すぎる場合

  • 小さなノイズで損切りされやすい
  • 勝率低下
  • 連敗が増えやすい

固定SLが広すぎる場合

  • 勝率は見かけ上改善しやすい
  • 1回の損失が重くなる
  • DDが深くなりやすい

TPが近すぎる場合

  • 小さな利益は取りやすい
  • コスト(スプレッド・手数料)の影響を受けやすい
  • PFが伸びにくい

TPが遠すぎる場合

  • 一撃利益は大きい
  • 到達率が下がる
  • 利益のばらつきが大きくなる

つまり、SL/TPは「安全か危険か」の二択ではなく、損益分布をどう設計するかの問題です。


8.5 初心者が避けるべき設計ミス

■ 勝率だけで判断する

勝率70%でも、負け1回が勝ち3回分なら期待値は悪化します。

■ 通貨ペアを変えても同じ値幅を使う

EURUSDとGBPJPYでは値動き特性が違います。
固定pipsをそのまま流用すると、設計が崩れやすくなります。

■ SLだけ、またはTPだけを最適化する

片側だけ調整すると、全体のバランスを失います。
必ずセットで検証します。

■ バックテストの最良値をそのまま採用する

過度な最適化(過剰適合)により、将来の再現性が落ちることがあります。


8.6 実務でのおすすめ設計手順

SL/TP設計で迷う場合は、次の順で進めると整理しやすいです。

  • まず固定SL/TPで試作する
  • RRを1:1、1:1.5、1:2程度で比較する
  • PF、DD、勝率を同時に見る
  • 通貨ペアごとの差を確認する
  • 必要ならATR連動に進む

この順なら、再現性を維持しながら改善できます。


つまずきやすいポイント・注意点

  • RRが高ければ必ず有利、ではない
  • 勝率が高ければ優秀、でもない
  • 通貨ペアと時間足で最適値は変わる
  • バックテスト最適値は将来も再現するとは限らない

よくある失敗

  • 10pips刻みで雑に調整して終わる
  • DDを見ずにPFだけで判断する
  • 固定SLで負けた後すぐ動的SLへ逃げる
  • そもそもリスク許容度を決めていない

9. MQL5のSL/TP設定チェックリストと最終整理

このセクションで書く要点
・実務でミスを防ぐチェック項目
・再現性を担保するための確認ポイント
・本記事の要点を短く整理


9.1 注文前チェックリスト(必須)

実務では、SL/TPの設定ミスは即損失または注文拒否に直結します。
以下のチェックをルーチン化することで、ほぼすべてのトラブルを防げます。

基本チェック

  • SLとTPが0ではない
  • BUY:SL < price < TP
  • SELL:TP < price < SL
  • _Digits に合わせて NormalizeDouble済み

距離チェック

  • stopLevel以上の距離がある
  • buffer(+10〜20point)を確保している
  • スプレッドを考慮している

計算チェック

  • pips → point変換済み
  • 通貨ペアに依存しない設計
  • ハードコード値を使っていない

9.2 実行後チェック(デバッグ)

注文が通っても、意図通りの値か確認する習慣が重要です。

ログ出力例

Print("price=", request.price,
      " SL=", request.sl,
      " TP=", request.tp);

確認ポイント

  • 設定値が想定通りか
  • 桁数が崩れていないか
  • SL/TPの方向が正しいか

9.3 エラー発生時の確認手順

エラー時は感覚で修正せず、順序立てて確認します。

手順

  1. result.retcode を確認
  2. SL/TPの価格をログ出力
  3. stopLevelを確認
  4. Ask/Bidとの位置関係を確認

Print("retcode=", result.retcode);

9.4 実務での最重要ルール(再現性重視)

SL/TP設定で最も重要なのは、再現性と一貫性です。

守るべきルール

  • 必ず関数化する
  • pipsベースで設計する
  • NormalizeDoubleを徹底する
  • stopLevel + bufferを前提にする

なぜ重要か

  • 環境差(ブローカー差)を吸収できる
  • バックテストと実運用の乖離を減らす
  • バグの局所化ができる

9.5 本記事の要点整理

本記事の内容を、実務視点で簡潔にまとめます。

技術面

  • SL/TPは「価格」で設定する
  • _Point_Digits を正しく扱う
  • stopLevel違反はエラーの主因

実装面

  • 注文時設定(OrderSend)が基本
  • PositionModifyで後から変更可能
  • 共通関数化でバグを防ぐ

戦略面

  • SL/TPはリスク設計そのもの
  • RR(リスクリワード)を先に決める
  • 固定→動的の順で改善

9.6 最短で安定させるための結論

初心者〜中級者が最短で安定させるには、以下の構成が最適です。

  • 固定pips(例:SL20 / TP40)
  • 注文時にSL/TP設定
  • stopLevel + buffer対応
  • NormalizeDouble徹底

この状態でまず安定稼働させ、その後に最適化へ進むのが再現性の高い進め方です。


つまずきやすいポイント・注意点

  • 一部だけ修正して全体の整合性が崩れる
  • エラーを無視して進める
  • 通貨ペア変更で破綻する設計
  • バックテスト結果を過信する

よくある失敗

  • SL/TP未設定で本番運用
  • ログを見ずに原因不明のまま修正
  • StopLevel無視でエラー連発
  • 設計を固めずに最適化に進む

FAQ(よくある質問)

Q1. SL/TPは必ず設定すべきですか?

A. はい。未設定は損失無制限のリスクがあるため、実務では必須です。


Q2. invalid stopsエラーはどうすれば防げますか?

A. stopLevel以上の距離確保、方向の正しさ、NormalizeDoubleを徹底すればほぼ防げます。


Q3. pipsとpointの違いは何ですか?

A. pointは最小単位、pipsは慣習単位です。MQL5ではpoint基準で計算します。


Q4. 注文後にSL/TPを変更できますか?

A. 可能です。PositionModifyを使用します。


Q5. トレーリングストップとTPの違いは?

A. TPは固定利確、トレーリングはSLを動的に更新する仕組みです。


Q6. 最適なSL/TPはどう決めますか?

A. RR(リスクリワード)とバックテスト結果を基に、PFとDDのバランスで判断します。


Q7. 通貨ペアごとに設定は変えるべきですか?

A. はい。ボラティリティが異なるため、同一設定は非推奨です。