本稿では、ロンドン時間のUSD/JPYに的を絞った「押し目順張り」戦略を、初心者でもそのまま再現できるように解説します。単なる概念紹介ではなく、MQL4の完全EAコード、バックテストと最適化の手順、日次オペレーションまでを一気通貫で提示します。ボラティリティが立ち上がるロンドン時間に限定し、トレンド方向の押し目だけを狙うことで、無駄なトレードを極力排除します。
戦略の全体像
対象はUSD/JPYの5分足または15分足です。上位トレンド=EMA50とEMA200の上下関係で規定し、短期の押し目=価格とEMA20の関係でエントリータイミングを決めます。モメンタムの確認にRSIを用い、ATRベースのストップ・利確・ポジションサイズで一貫したリスク管理を行います。取引時間はブローカーのサーバー時間でロンドン時間帯に限定し、00/50の切り番(ラウンドナンバー)に近い約定は避ける設計です。
なぜロンドン時間×USD/JPYか
ロンドン時間(日本時間で夕方以降)は、欧州勢のフローで流動性が厚くなり、東京時間のレンジを離れる動きが生まれやすい時間帯です。USD/JPYは実需・政策ニュース・債券金利の影響を強く受け、方向感が出た際の押し目回帰→再加速が比較的明確に観測されます。加えてスプレッドがタイトで、執行コストが読めることも初心者に適しています。
ルール(確定版)
以下はEA実装と完全一致するルールです。裁量の余地はありません。
トレンド判定
EMA50 > EMA200で上昇トレンド、EMA50 < EMA200で下降トレンドとします。トレンド方向以外の新規ポジションは取りません。
押し目・戻りのトリガー
上昇トレンド時は、直近で終値がEMA20を下回った後、再び終値がEMA20を上抜け、かつRSI(14)が55以上でロング。下降トレンド時は逆条件(EMA20再下抜け、RSIが45以下)でショートです。
フィルター
(1)取引時間:サーバー時間で指定の開始時刻~終了時刻のみ新規建て。(2)最大スプレッド:閾値超過時は新規不可。(3)ラウンドナンバー回避:約定価格が「xx.00」「xx.50」に近すぎる場合は見送り。(4)曜日回避:必要に応じて月曜の寄り付き/金曜の引け前を除外。(5)終盤クローズ:終了時刻の一定分前になったら保有ポジションを整理します。
リスク管理
ATR(14)×係数でストップ(SL)と利確(TP)を固定幅で設定します。既定値はSL=ATR×1.2、TP=ATR×1.5です。ロットは口座残高の0.5%リスクを上限に、SL距離から逆算して自動計算します。含み益が+1R到達で建値ストップへ引き上げ、以降はEMA20を追う簡易トレーリングで利益を残しやすくします。
パラメータ設計の考え方
EMA20は短期の押し目・戻りの「均衡点」として機能します。EMA50/200はトレンドの骨格で、方向判定の安定性を優先しました。RSIは閾値を55/45のわずかなバイアスに留め、過剰フィルタリングを回避します。ATR係数はスプレッド・平均波動・時間帯で変動するため、1.0~1.8の範囲で通貨ペアや足種に合わせて再調整してください。最大スプレッドはボラティリティの低い時間帯で厳しめ、高い時間帯で緩めに設定します。
バックテスト手順(MT4)
①MT4の「ファイル > データフォルダ」からMQL4/Experts配下にEAを保存し、再起動します。②ストラテジーテスターで「USDJPY」「M5またはM15」「Every tick(全ティック)」を選択。③スプレッドは「現在値」ではなく固定値を推奨(ロンドン時間の平均スプレッドに合わせる)。④テスト期間は最低でも2~3年、できれば4~5年。⑤次にパラメータの微調整のみを行い、過学習を避けます(例:ATR係数、RSI閾値、セッション時刻)。⑥ウォークフォワード:期間を2分割し、前半で調整・後半で検証、さらに年ごとローテーションで安定度を評価します。
実運用フロー
VPSで24時間稼働を基本とし、ロンドン時間前にMT4のログを確認します。日次では(1)スプレッド・板の荒れ具合、(2)重要指標の時間、(3)前日からの未決済ポジション管理、(4)終盤の自動クローズの作動確認を行います。週次では(1)約定履歴のR分布、(2)勝ち負けの連続性、(3)手仕舞いの妥当性、(4)サーバー時間の夏時間切替をチェックします。
初心者が躓きやすいポイント
①ロット過大(リスク%を守る)。②スプレッドの見落とし(最大スプレッドを数値で管理)。③時間帯の誤認(サーバー時間で設定)。④バックテストの「現在スプレッド」使用(固定値にする)。⑤切り番の手前で約定し捕まる(フィルター厳格化)。
MQL4完全EAコード(コピペで可)
以下を「Experts」フォルダに LondonPullbackEA.mq4
として保存してください。
//+------------------------------------------------------------------+
//| LondonPullbackEA |
//| Simple London Session Pullback|
//+------------------------------------------------------------------+
#property strict
input int MagicNumber = 24090401;
input double RiskPercent = 0.5; // 口座残高に対するリスク%
input bool UseFixedLots = false;
input double FixedLots = 0.10;
input int EMA_Fast = 20;
input int EMA_Mid = 50;
input int EMA_Slow = 200;
input int RSI_Period = 14;
input double RSI_Long_Thresh = 55.0;
input double RSI_Short_Thresh = 45.0;
input int ATR_Period = 14;
input double ATR_SL_Mult = 1.2;
input double ATR_TP_Mult = 1.5;
input int SessionStartHour = 8; // ブローカー時間
input int SessionEndHour = 18; // 例)8-18がロンドン中心帯
input int CloseBeforeEndMin = 10;
input bool AvoidMonday = true;
input bool AvoidFriday = true;
input double MaxSpreadPoints = 30; // ポイント(例:3.0pips相当)
input double RoundFilterPips = 5.0; // 00/50からの回避幅(pips)
datetime lastAction=0;
// ユーティリティ
double PipPoints() { return (Digits==3 || Digits==5) ? 0.01 : 0.0001; }
double PointsPerPip() { return PipPoints()/Point; }
double GetSpreadPoints(){ return (MarketInfo(Symbol(), MODE_ASK) - MarketInfo(Symbol(), MODE_BID)) / Point; }
bool IsTradingHour(){
int h = TimeHour(TimeCurrent());
if(SessionStartHour <= SessionEndHour) return (h >= SessionStartHour && h < SessionEndHour);
// 24h跨ぎ
return (h >= SessionStartHour || h < SessionEndHour);
}
bool IsDayAllowed(){
int dow = TimeDayOfWeek(TimeCurrent());
if(AvoidMonday && dow==1) return false;
if(AvoidFriday && dow==5) return false;
return true;
}
bool NearRoundNumber(double price){
// USDJPY向け:xx.00 / xx.50 から一定pips以内なら回避
double pips_per_point = PointsPerPip();
double price_pips = price / PipPoints(); // pip単位へ
double mod50 = MathMod(MathRound(price_pips), 50); // 50pips刻み
if(mod50 <= RoundFilterPips) return true;
if(50 - mod50 <= RoundFilterPips) return true;
return false;
}
bool SpreadOK(){ return GetSpreadPoints() <= MaxSpreadPoints; }
bool FlatTimeCloseSoon(){
datetime now = TimeCurrent();
int h = TimeHour(now);
int m = TimeMinute(now);
// 終了時刻のCloseBeforeEndMin分前以降は新規禁止、保有は手仕舞い
int end = SessionEndHour;
int minutes_to_end = ((end - h + 24) % 24) * 60 - m;
if(SessionStartHour <= SessionEndHour){
minutes_to_end = (end*60 - (h*60 + m));
}
return (minutes_to_end >= 0 && minutes_to_end <= CloseBeforeEndMin);
}
int CountOpenByMagic(int magic){
int c=0;
for(int i=0;i<OrdersTotal();i++){
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)){
if(OrderSymbol()==Symbol() && OrderMagicNumber()==magic) c++;
}
}
return c;
}
double GetEMA(int period, int shift){ return iMA(Symbol(), 0, period, 0, MODE_EMA, PRICE_CLOSE, shift); }
double GetRSI(int period, int shift){ return iRSI(Symbol(), 0, period, PRICE_CLOSE, shift); }
double GetATR(int period, int shift){ return iATR(Symbol(), 0, period, shift); }
double CalcLotsByRisk(double sl_pips){
if(UseFixedLots) return FixedLots;
if(sl_pips <= 0.0) return 0.0;
double risk_amount = AccountBalance() * RiskPercent / 100.0;
double tickval = MarketInfo(Symbol(), MODE_TICKVALUE);
double pip_value_per_lot = tickval * PointsPerPip();
if(pip_value_per_lot <= 0) return 0.0;
double lots = risk_amount / (sl_pips * pip_value_per_lot);
double minlot = MarketInfo(Symbol(), MODE_MINLOT);
double lotstep = MarketInfo(Symbol(), MODE_LOTSTEP);
double maxlot = MarketInfo(Symbol(), MODE_MAXLOT);
lots = MathFloor(lots/lotstep)*lotstep;
if(lots < minlot) lots = minlot;
if(lots > maxlot) lots = maxlot;
return NormalizeDouble(lots, 2);
}
void TrailAndBE(int ticket, double entry_price, double risk_pips){
if(!OrderSelect(ticket, SELECT_BY_TICKET)) return;
double ema20 = GetEMA(EMA_Fast, 0);
double sl = OrderStopLoss();
double tp = OrderTakeProfit();
double price = (OrderType()==OP_BUY) ? Bid : Ask;
double profit_pips = (OrderType()==OP_BUY) ? (price - entry_price)/PipPoints() : (entry_price - price)/PipPoints();
// BE化(+1R)
if(profit_pips >= risk_pips && ((OrderType()==OP_BUY && sl < entry_price) || (OrderType()==OP_SELL && sl > entry_price))){
double newSL = entry_price;
if(OrderType()==OP_BUY) OrderModify(ticket, OrderOpenPrice(), newSL, tp, 0);
else OrderModify(ticket, OrderOpenPrice(), newSL, tp, 0);
}
// EMA20トレーリング
double newSLtrail;
if(OrderType()==OP_BUY){
newSLtrail = ema20 - 1.5*PipPoints(); // わずかに余裕
if(newSLtrail > sl) OrderModify(ticket, OrderOpenPrice(), newSLtrail, tp, 0);
}else if(OrderType()==OP_SELL){
newSLtrail = ema20 + 1.5*PipPoints();
if(newSLtrail < sl) OrderModify(ticket, OrderOpenPrice(), newSLtrail, tp, 0);
}
}
void CloseAllByMagic(int magic){
for(int i=OrdersTotal()-1;i>=0;i--){
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)){
if(OrderSymbol()==Symbol() && OrderMagicNumber()==magic){
if(OrderType()==OP_BUY) OrderClose(OrderTicket(), OrderLots(), Bid, slippage:3);
if(OrderType()==OP_SELL) OrderClose(OrderTicket(), OrderLots(), Ask, slippage:3);
}
}
}
}
int OnInit(){ return(INIT_SUCCEEDED); }
int OnDeinit(){ return(0); }
void OnTick(){
if(TimeCurrent()==lastAction) return;
lastAction = TimeCurrent();
double ask = Ask, bid = Bid;
double ema20 = GetEMA(EMA_Fast,0);
double ema50 = GetEMA(EMA_Mid,0);
double ema200= GetEMA(EMA_Slow,0);
double rsi = GetRSI(RSI_Period,0);
double atr = GetATR(ATR_Period,0);
double sl_pips = (atr * ATR_SL_Mult)/PipPoints();
double tp_pips = (atr * ATR_TP_Mult)/PipPoints();
double pippts = PipPoints();
// 終盤は全決済
if(FlatTimeCloseSoon()) CloseAllByMagic(MagicNumber);
// 新規条件チェック
if(!IsTradingHour() || !IsDayAllowed() || !SpreadOK()) return;
if(CountOpenByMagic(MagicNumber)>0) return;
// ラウンドナンバー回避
if(NearRoundNumber(ask) || NearRoundNumber(bid)) return;
// ロング条件
bool uptrend = (ema50 > ema200);
bool dntrend = (ema50 < ema200);
double ema20_prev = GetEMA(EMA_Fast,1);
double close_0 = iClose(Symbol(),0,0);
double close_1 = iClose(Symbol(),0,1);
bool longTrig = (uptrend && close_1 < ema20_prev && close_0 > ema20 && rsi >= RSI_Long_Thresh);
bool shortTrig = (dntrend && close_1 > ema20_prev && close_0 < ema20 && rsi <= RSI_Short_Thresh);
int ticket;
if(longTrig){
double lots = CalcLotsByRisk(sl_pips);
double sl = bid - sl_pips * pippts;
double tp = bid + tp_pips * pippts;
ticket = OrderSend(Symbol(), OP_BUY, lots, Ask, 3, sl, tp, "LSE Pullback Long", MagicNumber, 0, clrBlue);
if(ticket>0) TrailAndBE(ticket, OrderOpenPrice(), sl_pips);
}else if(shortTrig){
double lots = CalcLotsByRisk(sl_pips);
double sl = ask + sl_pips * pippts;
double tp = ask - tp_pips * pippts;
ticket = OrderSend(Symbol(), OP_SELL, lots, Bid, 3, sl, tp, "LSE Pullback Short", MagicNumber, 0, clrRed);
if(ticket>0) TrailAndBE(ticket, OrderOpenPrice(), sl_pips);
}
// 既存ポジのトレーリング
for(int i=0;i<OrdersTotal();i++){
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)){
if(OrderSymbol()==Symbol() && OrderMagicNumber()==MagicNumber){
double entry = OrderOpenPrice();
TrailAndBE(OrderTicket(), entry, sl_pips);
}
}
}
}
//+------------------------------------------------------------------+
インストール手順
(1)上記コードを LondonPullbackEA.mq4
として保存。(2)MT4で「ナビゲーター > Expert Advisors」に表示されたらチャートにドラッグ。(3)パラメータは既定のまま開始し、まずはデモ口座で2週間のフォワード観測を行います。(4)約定ログと損益のR分布を確認し、ATR係数・セッション時刻のみ軽微に調整します。
パラメータのガイドライン
・時間帯:サーバー時間に合わせ、ロンドン前後で8~18時(例)。夏時間はブローカーの案内に合わせて調整します。・足種:M5はエントリー頻度が高く、M15はノイズが少なめ。・RSI閾値:上55/下45から±2程度の微調整が妥当。・ATR係数:スプレッド広がりがちな環境ではTP係数をやや大きく。
口座開設と環境整備(概要)
国内FXはスプレッドが安定し、約定力・入出金の利便性に優れます。海外業者はレバレッジやボーナスが特徴ですが、各種リスク・規制を個別に確認してください。口座開設は、(1)本人確認・マイナンバー提出、(2)審査、(3)取引ツールのダウンロード、(4)入金、(5)デモで動作検証、という標準フローです。EA運用はVPS常時稼働が現実的です。
運用チェックリスト
1)サーバー時間と実際のロンドン時間の整合。2)最大スプレッドの実測更新。3)重要指標(雇用統計・政策金利)前後の挙動。4)00/50付近での約定回避が機能しているか。5)連敗時のドローダウン推移(口座残高に対する%)とロット調整。6)週次でのRマトリクス(勝率×損益比)。
拡張アイデア
①ニュースフィルターの自動化(外部CSV読込)。②ボラ急伸時のブレークアウトモード切替。③Multi-Symbol化(EURUSD/GBPUSDへ展開)。④トレード日記の自動生成(CSV出力)。⑤切り番テーブルを25pipsグリッドに拡張。
限界と期待値の読み方
本戦略はトレンドが明確な日に強く、膠着・逆行日にはドローダウンが発生します。特定年のボラティリティ・政策相場では一時的に優位性が低下します。ドローダウンは「最大想定の2倍」を資金管理の安全域として、長期でのR合計で判断するのが実務的です。
まとめ
ロンドン時間×USD/JPYの押し目順張りは、シンプルなロジックで再現性が高いため、システムトレード入門として適しています。EAコード・検証手順・運用フローを揃えました。まずはデモで動かし、数量管理と時間帯の精度を高めてから少額でフェーズインしてください。
コメント