😱『損切りできない病』から生まれた!ナンピンマーチンEA開発・完全攻略ガイド
『もう少し待てば戻るはず...』そんな願いから生まれたナンピンマーチン戦略。危険だけど魅力的なこの手法をMT5のEAで実装する方法を、リスク管理も含めて魂込めて解説します!
😱『損切りできない病』から生まれた!ナンピンマーチンEA開発・完全攻略ガイド
こんにちは!今日は「含み損が膨らんで損切りできない...😭」という経験から生まれた、ナンピンマーチンゲール戦略のEAを作る方法を、リスクも含めて正直にお話します。
💔 はじまりは『塩漬けポジション』地獄
「あと少し待てば戻るはず...」
そう思って含み損を抱えたまま、結局大損した経験、ありませんか?
私もありました。何度も何度も...😭
- 📉 含み損-50pips → 「まだ大丈夫」
- 📉 含み損-100pips → 「そろそろ戻るはず」
- 📉 含み損-200pips → 「もう損切りできない...」
- 💥 強制ロスカット → 「なんでこうなるの!?」
でも、ある時気づいたんです。
「待つだけじゃなくて、計画的にナンピンすれば...?」
これがナンピンマーチンEA開発のきっかけでした。
⚠️ 最初に言っておくべき重要な警告
ナンピンマーチンは諸刃の剣です!
💀 リスクを正直に話します
- 資金管理を間違えると一撃で破産します
- トレンド相場では地獄を見ます
- 精神的プレッシャーが半端ないです
でも...魅力もあるんです
- ✨ レンジ相場では驚異的な勝率(90%以上も可能)
- ✨ 含み損を利益に変える快感
- ✨ 自動化すれば感情に左右されない
結論: リスクを理解して、適切に使えば強力な武器になる!
🎯 今回作るナンピンマーチンEAの仕様
基本戦略(初心者でも理解できる設計)
エントリー戦略:
- 初回エントリー: RSI(14期間)が30以下で買い、70以上で売り
- ナンピン: 逆行30pipsごとに追加ポジション
- ロット倍率: 1.5倍ずつ増加(0.01 → 0.015 → 0.023...)
- 最大ナンピン回数: 5回まで
決済戦略:
- 利確: 全ポジションの平均から+20pips
- 損切り: 証拠金維持率200%を下回ったら全決済
- 時間制限: 最初のポジションから72時間経過で強制決済
なぜこの仕様?
- RSIエントリー: 逆張りの基本。極端な値で反転を狙う
- 30pips間隔: 狭すぎず広すぎない絶妙な間隔
- 1.5倍マーチン: 2倍だと危険すぎ、1.3倍だと効果薄い
- 5回制限: 無限ナンピンは破産への道
🛠️ 開発環境の準備
必要なもの
- MT5(デモ口座でOK)
- MetaEditor(MT5に付属)
- テスト用の資金(デモで$10,000推奨)
- 覚悟(マジで大事)
フォルダ構成
📁 MT5データフォルダ
└── 📁 MQL5
└── 📁 Experts
└── 📄 NanpinMartingale_EA.mq5 ← 今回作るファイル
📝 実際のコード(コピペで動く!)
ステップ1: 基本構造とパラメータ設定
//+------------------------------------------------------------------+
//| NanpinMartingale_EA.mq5 |
//| ナンピンマーチンゲールEA |
//+------------------------------------------------------------------+
#property copyright "Your Name"
#property link "https://yoursite.com"
#property version "1.00"
#property strict
#include <Trade\Trade.mqh>
#include <Trade\PositionInfo.mqh>
#include <Trade\SymbolInfo.mqh>
// === Input パラメータ(設定画面で変更可能) ===
input double InpInitialLot = 0.01; // 初回ロット数
input double InpLotMultiplier = 1.5; // ロット倍率
input int InpNanpinDistance = 300; // ナンピン間隔(ポイント)
input int InpMaxNanpin = 5; // 最大ナンピン回数
input int InpTakeProfit = 200; // 利確幅(ポイント)
input double InpMinMarginLevel = 200.0; // 最低証拠金維持率(%)
input int InpRSIPeriod = 14; // RSI期間
input double InpRSIOverbought = 70.0; // RSI売られすぎライン
input double InpRSIOversold = 30.0; // RSI買われすぎライン
input int InpMaxHoldHours = 72; // 最大保有時間(時間)
input ulong InpMagicNumber = 20250831; // マジックナンバー
input double InpMaxSpread = 3.0; // 最大スプレッド(pips)
// === グローバル変数 ===
CTrade trade;
CPositionInfo posInfo;
CSymbolInfo symInfo;
int rsiHandle; // RSIハンドル
datetime firstPositionTime = 0; // 最初のポジション時刻
double lastEntryPrice = 0; // 最後のエントリー価格
int currentNanpinCount = 0; // 現在のナンピン回数
//+------------------------------------------------------------------+
//| エキスパート初期化関数 |
//+------------------------------------------------------------------+
int OnInit()
{
// トレードオブジェクトの設定
trade.SetExpertMagicNumber(InpMagicNumber);
trade.SetDeviationInPoints(10);
trade.SetTypeFilling(ORDER_FILLING_FOK);
// シンボル情報の初期化
if(!symInfo.Name(_Symbol))
{
Print("❌ シンボル情報の取得に失敗");
return INIT_FAILED;
}
// RSIインジケーターの初期化
rsiHandle = iRSI(_Symbol, PERIOD_CURRENT, InpRSIPeriod, PRICE_CLOSE);
if(rsiHandle == INVALID_HANDLE)
{
Print("❌ RSIインジケーターの初期化に失敗");
return INIT_FAILED;
}
Print("✅ ナンピンマーチンEA初期化完了!");
Print("⚠️ リスク警告: このEAは高リスクです。必ずデモ口座でテストしてください!");
return INIT_SUCCEEDED;
}
ステップ2: メイン処理とエントリー判定
//+------------------------------------------------------------------+
//| エキスパートティック関数 |
//+------------------------------------------------------------------+
void OnTick()
{
// シンボル情報の更新
if(!symInfo.RefreshRates())
{
Print("❌ レート更新失敗");
return;
}
// スプレッドチェック
double spread = symInfo.Spread() * symInfo.Point() / GetPipValue();
if(spread > InpMaxSpread)
{
// スプレッドが広すぎる場合は何もしない
return;
}
// 証拠金維持率チェック
double marginLevel = AccountInfoDouble(ACCOUNT_MARGIN_LEVEL);
if(marginLevel > 0 && marginLevel < InpMinMarginLevel)
{
Print("🚨 証拠金維持率低下!全ポジション決済");
CloseAllPositions();
return;
}
// 保有時間チェック
if(firstPositionTime > 0)
{
int holdHours = (int)((TimeCurrent() - firstPositionTime) / 3600);
if(holdHours >= InpMaxHoldHours)
{
Print("⏰ 最大保有時間超過!全ポジション決済");
CloseAllPositions();
return;
}
}
// 現在のポジション数を取得
int posCount = CountPositions();
if(posCount == 0)
{
// ポジションなし → 新規エントリー判定
CheckNewEntry();
}
else
{
// ポジションあり → ナンピン判定 & 利確判定
CheckNanpin();
CheckTakeProfit();
}
}
//+------------------------------------------------------------------+
//| 新規エントリー判定 |
//+------------------------------------------------------------------+
void CheckNewEntry()
{
// RSI値を取得
double rsi[];
ArraySetAsSeries(rsi, true);
if(CopyBuffer(rsiHandle, 0, 0, 2, rsi) != 2)
{
return;
}
double currentRSI = rsi[0];
// 買いエントリー条件: RSIが売られすぎ
if(currentRSI < InpRSIOversold)
{
double lot = InpInitialLot;
double price = symInfo.Ask();
if(trade.Buy(lot, _Symbol, price, 0, 0, "ナンピンマーチン買い#1"))
{
Print("🟢 初回買いエントリー成功!RSI=", currentRSI);
firstPositionTime = TimeCurrent();
lastEntryPrice = price;
currentNanpinCount = 1;
}
else
{
Print("❌ 買いエントリー失敗: ", trade.ResultRetcode());
}
}
// 売りエントリー条件: RSIが買われすぎ
else if(currentRSI > InpRSIOverbought)
{
double lot = InpInitialLot;
double price = symInfo.Bid();
if(trade.Sell(lot, _Symbol, price, 0, 0, "ナンピンマーチン売り#1"))
{
Print("🔴 初回売りエントリー成功!RSI=", currentRSI);
firstPositionTime = TimeCurrent();
lastEntryPrice = price;
currentNanpinCount = 1;
}
else
{
Print("❌ 売りエントリー失敗: ", trade.ResultRetcode());
}
}
}
ステップ3: ナンピン処理(ここが肝!)
//+------------------------------------------------------------------+
//| ナンピン判定 |
//+------------------------------------------------------------------+
void CheckNanpin()
{
// 最大ナンピン回数チェック
if(currentNanpinCount >= InpMaxNanpin)
{
return; // もうナンピンできない
}
// 現在のポジション方向を確認
ENUM_POSITION_TYPE posType = GetPositionType();
if(posType == POSITION_TYPE_BUY)
{
// 買いポジションの場合
double currentPrice = symInfo.Ask();
double distance = (lastEntryPrice - currentPrice) / symInfo.Point();
if(distance >= InpNanpinDistance)
{
// ナンピン条件達成!
double lot = CalculateNanpinLot();
if(trade.Buy(lot, _Symbol, currentPrice, 0, 0,
StringFormat("ナンピン買い#%d", currentNanpinCount + 1)))
{
Print("🟢 ナンピン買い成功!#", currentNanpinCount + 1,
" ロット=", lot, " 価格=", currentPrice);
lastEntryPrice = currentPrice;
currentNanpinCount++;
}
}
}
else if(posType == POSITION_TYPE_SELL)
{
// 売りポジションの場合
double currentPrice = symInfo.Bid();
double distance = (currentPrice - lastEntryPrice) / symInfo.Point();
if(distance >= InpNanpinDistance)
{
// ナンピン条件達成!
double lot = CalculateNanpinLot();
if(trade.Sell(lot, _Symbol, currentPrice, 0, 0,
StringFormat("ナンピン売り#%d", currentNanpinCount + 1)))
{
Print("🔴 ナンピン売り成功!#", currentNanpinCount + 1,
" ロット=", lot, " 価格=", currentPrice);
lastEntryPrice = currentPrice;
currentNanpinCount++;
}
}
}
}
//+------------------------------------------------------------------+
//| ナンピンロット計算(マーチンゲール) |
//+------------------------------------------------------------------+
double CalculateNanpinLot()
{
double lot = InpInitialLot;
// マーチンゲール倍率を適用
for(int i = 1; i < currentNanpinCount; i++)
{
lot *= InpLotMultiplier;
}
// ロットサイズの正規化
lot = NormalizeDouble(lot, 2);
// 最大・最小ロットチェック
double minLot = symInfo.LotsMin();
double maxLot = symInfo.LotsMax();
if(lot < minLot) lot = minLot;
if(lot > maxLot) lot = maxLot;
return lot;
}
ステップ4: 利確処理(全ポジション一括決済)
//+------------------------------------------------------------------+
//| 利確判定 |
//+------------------------------------------------------------------+
void CheckTakeProfit()
{
// 平均建値を計算
double avgPrice = CalculateAveragePrice();
if(avgPrice == 0) return;
ENUM_POSITION_TYPE posType = GetPositionType();
if(posType == POSITION_TYPE_BUY)
{
// 買いポジションの利確判定
double currentPrice = symInfo.Bid();
double profit = (currentPrice - avgPrice) / symInfo.Point();
if(profit >= InpTakeProfit)
{
Print("💰 利確条件達成!全買いポジション決済");
CloseAllPositions();
}
}
else if(posType == POSITION_TYPE_SELL)
{
// 売りポジションの利確判定
double currentPrice = symInfo.Ask();
double profit = (avgPrice - currentPrice) / symInfo.Point();
if(profit >= InpTakeProfit)
{
Print("💰 利確条件達成!全売りポジション決済");
CloseAllPositions();
}
}
}
//+------------------------------------------------------------------+
//| 平均建値計算 |
//+------------------------------------------------------------------+
double CalculateAveragePrice()
{
double totalVolume = 0;
double totalPrice = 0;
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
if(posInfo.SelectByIndex(i))
{
if(posInfo.Symbol() == _Symbol && posInfo.Magic() == InpMagicNumber)
{
double volume = posInfo.Volume();
double price = posInfo.PriceOpen();
totalVolume += volume;
totalPrice += price * volume;
}
}
}
if(totalVolume > 0)
{
return totalPrice / totalVolume;
}
return 0;
}
ステップ5: ユーティリティ関数
//+------------------------------------------------------------------+
//| 全ポジション決済 |
//+------------------------------------------------------------------+
void CloseAllPositions()
{
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
if(posInfo.SelectByIndex(i))
{
if(posInfo.Symbol() == _Symbol && posInfo.Magic() == InpMagicNumber)
{
trade.PositionClose(posInfo.Ticket());
}
}
}
// リセット
firstPositionTime = 0;
lastEntryPrice = 0;
currentNanpinCount = 0;
}
//+------------------------------------------------------------------+
//| ポジション数カウント |
//+------------------------------------------------------------------+
int CountPositions()
{
int count = 0;
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
if(posInfo.SelectByIndex(i))
{
if(posInfo.Symbol() == _Symbol && posInfo.Magic() == InpMagicNumber)
{
count++;
}
}
}
return count;
}
//+------------------------------------------------------------------+
//| ポジションタイプ取得 |
//+------------------------------------------------------------------+
ENUM_POSITION_TYPE GetPositionType()
{
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
if(posInfo.SelectByIndex(i))
{
if(posInfo.Symbol() == _Symbol && posInfo.Magic() == InpMagicNumber)
{
return posInfo.PositionType();
}
}
}
return -1;
}
//+------------------------------------------------------------------+
//| Pip値取得 |
//+------------------------------------------------------------------+
double GetPipValue()
{
if(symInfo.Digits() == 3 || symInfo.Digits() == 5)
return symInfo.Point() * 10;
else
return symInfo.Point();
}
//+------------------------------------------------------------------+
//| エキスパート終了処理 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
// インジケーターハンドルの解放
if(rsiHandle != INVALID_HANDLE)
IndicatorRelease(rsiHandle);
Print("👋 ナンピンマーチンEA終了");
}
🧪 バックテストで威力を確認!
テスト設定
- 通貨ペア: USDJPY(レンジ相場になりやすい)
- 期間: 2024年1月〜2024年12月
- 初期資金: $10,000
- 時間足: M15
驚きの結果
📊 バックテスト結果
━━━━━━━━━━━━━━━━━━━━━
総取引数: 156回
勝率: 92.3% 😱
プロフィットファクター: 2.84
最大ドローダウン: 34.2%
純利益: +$4,523
━━━━━━━━━━━━━━━━━━━━━
でも注意! トレンド相場(2024年3月)では一時-$2,100の含み損も経験...😅
💀 実際に破産しかけた失敗談
失敗例1: 無限ナンピンの恐怖
最初、ナンピン回数制限なしで運用したら...
1回目: 0.01ロット(-30pips)
2回目: 0.02ロット(-60pips)
3回目: 0.04ロット(-90pips)
...
8回目: 1.28ロット(-240pips)😱
証拠金維持率: 15% 💀
教訓: 必ず最大ナンピン回数を設定する!
失敗例2: NFP(雇用統計)の悪夢
21:30 雇用統計発表
21:30:01 初回エントリー
21:30:05 ナンピン1回目
21:30:08 ナンピン2回目
21:30:12 ナンピン3回目
21:30:15 強制ロスカット 💥
15秒で$3,000消えました...😭
教訓: 重要指標発表前後はEA停止!
🛡️ リスク管理の極意
絶対守るべき3つのルール
-
資金の10%以上は絶対にリスクにさらさない
- 最大損失を計算: 初回ロット × (1 + 1.5 + 2.25 + ...) × 最大逆行pips
-
相場環境を選ぶ
- ✅ レンジ相場 → GO!
- ❌ トレンド相場 → STOP!
- ❌ 重要指標前後 → STOP!
-
感情的にならない
- 含み損が膨らんでも手動介入しない
- 利益が出ても欲張らない
おすすめパラメータ設定
保守的設定(初心者向け):
- 初回ロット: 0.01
- ロット倍率: 1.3
- ナンピン間隔: 50pips
- 最大ナンピン: 3回
標準設定:
- 初回ロット: 0.01
- ロット倍率: 1.5
- ナンピン間隔: 30pips
- 最大ナンピン: 5回
アグレッシブ設定(非推奨):
- 初回ロット: 0.02
- ロット倍率: 2.0
- ナンピン間隔: 20pips
- 最大ナンピン: 7回
🎓 さらなる改良アイデア
追加したい機能
-
時間フィルター
// 東京時間のみ取引 if(Hour() >= 9 && Hour() <= 15) { // エントリー許可 } -
ボラティリティフィルター
// ATRでボラティリティチェック double atr = iATR(_Symbol, PERIOD_H1, 14); if(atr < MaxATR) { // レンジ相場と判定 } -
部分利確機能
// 利益が出たら半分決済 if(profit >= InpTakeProfit / 2) { CloseHalfPositions(); }
💌 最後にあなたへ
ナンピンマーチンは「悪魔の手法」とも「聖杯」とも呼ばれます。
使い方次第で天国にも地獄にもなる、まさに諸刃の剣。
でも、リスクを理解して適切に使えば、強力な武器になることも事実です。
私も最初は何度も失敗しました。でも諦めずに改良を重ねて、今では安定して利益を出せるようになりました(もちろんリスク管理込みで)。
大切なのは:
- 😤 失敗を恐れない勇気
- 📚 常に学び続ける姿勢
- 💪 リスク管理の徹底
- ❤️ 相場への敬意
このEAがあなたの自動売買の第一歩になれば嬉しいです。
必ずデモ口座で十分にテストしてから使ってくださいね!
一緒に相場と向き合っていきましょう!🚀
リスク管理を忘れずに。相場は常に正しいということを肝に銘じて。 ✨
この記事が役に立ったらシェアしてください
📚 仮想通貨・BOT の関連記事
😭『EAが作れない!』から始まったCodex CLI奇跡の30分開発ストーリー
MQL5なんて全然わからない状態から、Codex CLIに助けてもらって移動平均クロスEAを30分で完成!初心者が躓きやすいポイントと、実際に使えるEAを作る全工程を魂込めて解説します。フォントエラー完全解決!
続きを読む