本稿では、RSI×ボリンジャーバンドを組み合わせたFXスキャルピング戦略を、初心者でも即日で運用開始できるレベルまで分解して解説します。裁量の再現性を高めるため、完全自動売買(MQL4 EA)のフルコードを掲載し、発注・損益管理・検証・改善の各工程を具体化します。余計な理屈は最小化し、「どうやって日次損益を安定させるか」にフォーカスします。
想定読者は、MT4口座を開設済みで、M5やM15の短期足でUSD/JPY等の主要通貨を取引したい個人投資家です。記事のゴールは、1)勝率×損益比×取引頻度の期待値設計、2)EAで実装、3)小規模資金でもロット過多を避けて生存率を上げるの3点です。
1. 戦略の骨子:なぜRSI×BBか
短期足のスキャルでは、勢いと行き過ぎの両面を見ると期待値が安定します。RSIは「行き過ぎ(短期モメンタムの過熱/冷え込み)」、ボリンジャーバンド(以下BB)は「価格分布(σレンジ)」を示します。バンド端でのオシレーター極端値は平均回帰の起点になりやすく、片側に寄ったバンド傾きが強い場合は順張り優位に転じることがあります。本戦略は基本は逆張り、強トレンドは順張りへ自動スイッチします。
2. 売買ルール(機械可読な定義)
2.1 監視足・通貨
M5(5分足)を基本。通貨はUSD/JPY、EUR/USD、GBP/JPYなど流動性が高く、スプレッドが安いもの。
2.2 指標設定
- RSI期間=14、基準=30/70(可変)。
- BB期間=20、偏差=2.0(価格はClose)。
2.3 買い(ロング)条件
- ローソク終値が下側BBにタッチまたは下抜け。
- 同バー終値のRSIが30以下。
- 直近N本(例:50本)でのATRが閾値以上(過小ボラ相場を除外)。
- 上位足(M15など)で価格が中間線(BBミドル)を下回り過ぎていない(行き過ぎの反発余地がある)。
上記を満たしたら次バー始値で成行買い。
2.4 売り(ショート)条件
- ローソク終値が上側BBにタッチまたは上抜け。
- 同バー終値のRSIが70以上。
- 直近N本のATRが閾値以上。
- 上位足で価格が中間線を上回り過ぎていない。
上記を満たしたら次バー始値で成行売り。
3. 相場条件フィルター(やらない相場を決める)
- 重要指標30分前〜30分後は取引停止(雇用統計、CPI、FOMC等)。
- 東京早朝・週明けのスプレッド拡大時間帯は停止。
- ATRフィルター:M5のATR(14)が最低閾値未満のときは新規禁止。
- 連敗ストップ:連続3敗で当日停止。
4. 損益管理:損切り・利確・時間切れ・トレール
短期は損失限定・利益は素早く確保が原則。
- 初期SL:エントリーバーの±(1.5×ATR)。
- TP:1.2〜1.6×ATRの固定幅。勝率とRRRのバランスを事前に最適化。
- 時間切れExit:エントリー後15本経過で未決済なら成行クローズ。
- トレーリング:利益が+1.0×ATRに到達したら、建値+0.2×ATRへ引き上げ、その後はBBミドルや直近安値/高値で追随。
5. ポジションサイジング:生存率重視のロット設計
1トレードのリスク=口座残高の0.5%〜1.0%を原則。ATRベースSL幅に合わせてロットを自動算出します。複利はドローダウン深掘りを招くため、新高値更新時のみ残高基準を切り上げる運用を推奨。
6. 執行品質:スリッページとスプレッド耐性
スキャルの死因はコスト負けです。次の対処をEAに組み込みます。
- 最大許容スプレッドをパラメータ化(例:1.2pips以下)。
- 成行専用・IFD予約を避け、リクオート対策に最大スリッページを設定。
- 同時保有は最大1ポジション(オーバーラップ禁止)。
7. バックテスト手順(罠回避ガイド)
- スプレッドは固定低値にしない(現実的な平均〜悪化時を想定)。
- モデリング品質はできるだけ高品質なティックデータで。
- 最適化はパラメータの荒取り→狭域微調整の二段階。
- ウォークフォワード(6ヶ月学習→3ヶ月運用)でロバスト性検証。
- 評価指標はシャープレシオ、PF、MaxDD、連敗数を重視。
8. フォワード運用:モニタリングKPI
- 実現スプレッド(約定価格差の実測)
- 滑り(エントリー/クローズ両方)
- 勝率・平均損益・RRR・連敗・日次損益分布
- 相場局面別(トレンド/レンジ)の成績
9. MQL4 EA(フルコード)
以下のEAは、RSI×BBルール、ATRベースのSL/TP、時間切れ、トレーリング、スプレッド/スリッページ制御、1ポジ制限を実装した最小構成です。
//+------------------------------------------------------------------+
//| RSI x Bollinger Scalping EA (MQL4) |
//| Timeframe: M5 (recommended) |
//+------------------------------------------------------------------+
#property strict
input ENUM_TIMEFRAMES InpTF = PERIOD_M5;
input int RSI_Period = 14;
input double RSI_Buy = 30.0;
input double RSI_Sell = 70.0;
input int BB_Period = 20;
input double BB_Dev = 2.0;
input int ATR_Period = 14;
input double ATR_Min = 0.0005; // min ATR to allow entry (in price)
input double RiskPct = 0.7; // per-trade risk % of balance
input double MaxSpread = 1.2; // pips
input int Slippage = 3; // points
input double SL_ATR = 1.5; // StopLoss = SL_ATR * ATR
input double TP_ATR = 1.3; // TakeProfit = TP_ATR * ATR
input int BarsTimeout= 15; // time-based exit bars
input double BE_ATR = 1.0; // move to BE+ buffer when profit >= 1.0*ATR
input double BE_Buffer = 0.2; // +0.2*ATR
input bool OnePosition = true;
//--- globals
datetime last_bar_time = 0;
double Pip()
{
if(Digits==3 || Digits==5) return Point*10;
return Point;
}
bool SpreadOK()
{
double spread_pips = (MarketInfo(Symbol(), MODE_SPREAD)) * Pip() / Point;
return (spread_pips <= MaxSpread + 1e-8);
}
bool HasOpenPosition()
{
for(int i=OrdersTotal()-1;i>=0;i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol()==Symbol() && OrderMagicNumber()==123456) return true;
}
}
return false;
}
int CountBarsSinceOpen()
{
datetime opentime = 0;
for(int i=OrdersTotal()-1;i>=0;i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol()==Symbol() && OrderMagicNumber()==123456)
{
opentime = OrderOpenTime();
break;
}
}
}
if(opentime==0) return 0;
int bars=0;
for(int i=0;i<Bars;i++)
{
if(iTime(Symbol(), InpTF, i) > opentime) bars++;
else break;
}
return bars;
}
double LotByRisk(double stop_points)
{
if(stop_points<=0) return 0.01;
double risk_money = AccountBalance() * (RiskPct/100.0);
double tickValue = MarketInfo(Symbol(), MODE_TICKVALUE);
double lot_step = MarketInfo(Symbol(), MODE_LOTSTEP);
double min_lot = MarketInfo(Symbol(), MODE_MINLOT);
double lot = risk_money / (stop_points*Point*tickValue);
lot = MathFloor(lot/lot_step)*lot_step;
if(lot<min_lot) lot=min_lot;
return lot;
}
void CloseAll()
{
for(int i=OrdersTotal()-1;i>=0;i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol()==Symbol() && OrderMagicNumber()==123456)
{
int type = OrderType();
double price = (type==OP_BUY)?Bid:Ask;
OrderClose(OrderTicket(), OrderLots(), price, Slippage, clrNONE);
}
}
}
}
void TrailOrBE(double atr)
{
for(int i=OrdersTotal()-1;i>=0;i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol()!=Symbol() || OrderMagicNumber()!=123456) continue;
if(OrderType()==OP_BUY)
{
double profit_points = (Bid - OrderOpenPrice())/Point;
double be_price = OrderOpenPrice() + (BE_Buffer*atr);
if((Bid - OrderOpenPrice()) >= (BE_ATR*atr))
{
if(OrderStopLoss() < be_price - 2*Point)
{
OrderModify(OrderTicket(), OrderOpenPrice(), NormalizeDouble(be_price, Digits), OrderTakeProfit(), 0, clrNONE);
}
}
}
if(OrderType()==OP_SELL)
{
double profit_points = (OrderOpenPrice() - Ask)/Point;
double be_price = OrderOpenPrice() - (BE_Buffer*atr);
if((OrderOpenPrice() - Ask) >= (BE_ATR*atr))
{
if(OrderStopLoss() > be_price + 2*Point || OrderStopLoss()==0)
{
OrderModify(OrderTicket(), OrderOpenPrice(), NormalizeDouble(be_price, Digits), OrderTakeProfit(), 0, clrNONE);
}
}
}
}
}
}
int OnInit(){ return(INIT_SUCCEEDED); }
int OnDeinit(){ return(0); }
int start()
{
// new bar check
datetime ct = iTime(Symbol(), InpTF, 0);
if(ct==last_bar_time) { return(0); }
last_bar_time = ct;
// data
int shift=1; // use closed bar
double rsi = iRSI(Symbol(), InpTF, RSI_Period, PRICE_CLOSE, shift);
double bb_mid = iBands(Symbol(), InpTF, BB_Period, BB_Dev, 0, PRICE_CLOSE, MODE_MAIN, shift);
double bb_up = iBands(Symbol(), InpTF, BB_Period, BB_Dev, 0, PRICE_CLOSE, MODE_UPPER, shift);
double bb_lo = iBands(Symbol(), InpTF, BB_Period, BB_Dev, 0, PRICE_CLOSE, MODE_LOWER, shift);
double close = iClose(Symbol(), InpTF, shift);
double atr = iATR(Symbol(), InpTF, ATR_Period, shift);
if(!SpreadOK()) return(0);
// Manage existing position
if(HasOpenPosition())
{
// time-out exit
if(CountBarsSinceOpen() >= BarsTimeout) { CloseAll(); return(0); }
TrailOrBE(atr);
return(0);
}
if(atr < ATR_Min) return(0);
// signals
bool touchLower = (close <= bb_lo);
bool touchUpper = (close >= bb_up);
// calculate stops in points
double sl_points = (SL_ATR*atr)/Point;
double tp_points = (TP_ATR*atr)/Point;
double lots = LotByRisk(sl_points);
int ticket;
if(touchLower && rsi <= RSI_Buy)
{
double sl=0, tp=0;
sl = NormalizeDouble(Ask - sl_points*Point, Digits);
tp = NormalizeDouble(Ask + tp_points*Point, Digits);
ticket = OrderSend(Symbol(), OP_BUY, lots, Ask, Slippage, sl, tp, "RSI_BB_Long", 123456, 0, clrBlue);
}
else if(touchUpper && rsi >= RSI_Sell)
{
double sl=0, tp=0;
sl = NormalizeDouble(Bid + sl_points*Point, Digits);
tp = NormalizeDouble(Bid - tp_points*Point, Digits);
ticket = OrderSend(Symbol(), OP_SELL, lots, Bid, Slippage, sl, tp, "RSI_BB_Short", 123456, 0, clrRed);
}
return(0);
}
//+------------------------------------------------------------------+
10. 推奨パラメータと調整方法
- RSI閾値:レンジ優位なら25/75に拡張、トレンド優位なら30/70を維持。
- BB偏差:スプレッド広い業者では2.2に上げてダマシ減。
- ATR閾値:通貨別の平均ATRを把握し、低ボラの時間帯を排除。
- TP/SL:PFと連敗数のバランスが最重要。TP1.2〜1.6×ATR、SL1.4〜1.8×ATR付近で探索。
11. チェックリスト(即運用可)
- 口座通貨と必要証拠金を確認、1回のリスク0.5〜1.0%を厳守。
- 最大スプレッドとスリッページを設定。
- 重要指標のカレンダー同期(取引停止)
- ウォークフォワードでロバスト性確認後、小ロットでパイロット運用開始。
12. FAQ:よくある失敗
Q1. 勝率は高いのに資金が増えない。
A. コスト過多の可能性。平均スプレッドと滑りを実測し、TP/SLやBB偏差を再調整。
Q2. トレンドで逆張りが刺さる。
A. ATRと上位足の傾きでフィルターを厳格化。TPを控えめにし、時間切れを短縮。
Q3. 連敗でメンタルが崩れる。
A. 連敗停止ルールをEAで強制。複利は新高値更新時のみ。
本戦略は、再現性の高い「小さく勝つ」型です。ロットを膨らませる誘惑に抗い、コスト管理と撤退基準の徹底で生存率を最優先してください。EAのコードは基礎実装なので、指標回避や上位足フィルターの強化等、環境に合わせて拡張していきましょう。
コメント