月末月初アノマリーで稼ぐ:USD/JPY Turn‑of‑the‑Month戦略の初心者ガイド(EAコード付き)

FX

本稿では、月末月初(Turn‑of‑the‑Month, TOM)アノマリーを活用したUSD/JPYの短期戦略を、初心者の方でも今日から実装できるレベルまで落とし込みます。月末から月初にかけて為替市場には特有の資金フローが発生しやすく、これをルール化して淡々と狙うのが本戦略の骨子です。裁量の余地を最小化し、同じことを同じように繰り返すことで期待値を積み上げる設計にしています。

戦略の要点(結論ファースト)

本戦略は「月末(営業月最終日付近)〜月初(1〜2営業日目)にかけて発生しやすいトレンド/ボラティリティの偏り」を、時間帯・ボラティリティ・トレンドの3フィルターで抽出し、USD/JPYで短期的に取りに行きます。実行はMT4のEA(自動売買)で自動化できます。

  • 対象:USD/JPY(初心者はこれ一本で十分です)
  • 期間:毎月の月末−月初ウィンドウ(月末営業日−2日〜月初営業日+2日)
  • 方向性:トレンド追随(短期SMA上なら買い、下なら売り)
  • 時間帯:東京時間9:00〜12:00、ロンドン初動16:00〜18:00(日本時間)を推奨
  • ボラフィルター:ATRで最低限の値幅(例:当日1時間ATR×N)を要求
  • リスク:1トレードあたり口座残高の0.5%〜1.0%
  • 執行:成行+ATRベースのSL/TP、最大同時ポジションは1

なぜ月末月初に「歪み」が生まれるのか

為替は株式と比べて「フロー(資金の出入り)」の影響が価格に直結しやすい市場です。月末月初には以下のような要因が重なります。

  1. 機関投資家のリバランス:月末にポートフォリオを基準配分へ戻す動きが出やすく、株式・債券・外貨建て資産の価値変動に応じてヘッジ解消・追加の取引が発生します。
  2. 給与・年金・投信設定等の定期フロー:月初に受給・拠出が集中し、為替ヘッジや外貨購入が生じやすくなります。
  3. 企業の資金繰り:決済・送金・輸出入支払いスケジュールが月末月初に寄りやすい傾向があります。

こうしたフローは毎月必ず同じ強さではありませんが、統計的に「偏り」を作るには十分です。重要なのは、偏りが生まれやすい「時間」と「状態」だけを待ち、その他の時間は何もしないという姿勢です。

データの取り方と簡易検証(ノーコード版)

まずはルールの妥当性を目視で確認します。TradingViewなどでUSD/JPYの1時間足を表示し、以下を繰り返してください。

  1. 各月の最終営業日から翌月2営業日目までをハイライトします。
  2. 東京9:00–12:00、ロンドン16:00–18:00の値動きにマーカーを付けます。
  3. SMA(50)で価格が上(下)にある場合は上昇(下落)方向の素直な動きが出ていないかを確認します。
  4. 1時間ATR(14)が一定以上(例:0.12円)ある日に限定すると、ダマシが減るかを確認します。

この目視チェックだけでも「条件を絞れば素直な動きに乗れる日がある」ことを体感できます。次に、EA化して機械的に検証・執行します。

売買ルール(完全に具体化)

取引日フィルター

  • ウィンドウ月末営業日−2日〜月初営業日+2日(土日・祝日を除く概念でOK。EAでは暦日ベースで近似します)
  • EA近似:「当日が月末(翌日が月替わり)または前後2日」「当日が月初(当日が1日)または前後2日」をTrueとする

時間帯フィルター(日本時間)

  • 東京セッション:09:00–12:00
  • ロンドン初動:16:00–18:00
  • ※サーバー時間に換算してEAパラメータで調整します。

方向フィルター

  • SMA(50)より終値が上 → 買い
  • SMA(50)より終値が下 → 売り

ボラティリティフィルター

  • 1時間ATR(14) × ATRMin(例:0.8)以上の日だけエントリー可

エントリー/決済

  • エントリー:条件成立の次の足始値で成行
  • 損切:ATR(14)×SLmult(例:1.8)
  • 利確:ATR(14)×TPmult(例:2.2)
  • 時間切れ:当日18:00(またはロンドン後場前)で強制クローズ

資金管理

  • リスク一定法:1トレードの口座リスク0.5%〜1.0%
  • 同時保有:1ポジションまで
  • 最大スプレッド制限:MaxSpread(例:2.0pips)

具体的なトレード例

例として、月末前営業日の東京時間にSMA上でATRが十分にある状況を想定します。条件が揃えば10:00の足確定後に買いエントリー、SL/TPはATR倍率で設定、18:00に到達したら時間決済します。逆にSMA下であれば同条件で売りに切り替えます。「条件が揃わない日は何もしない」ことが損失回避に直結します。

避けるべき日・時間

  • 重要指標30分前〜発表後1時間(雇用統計、CPI、FOMCなど)
  • 急な要人発言や為替介入の兆候があるとき
  • 月末が金曜で、週またぎギャップが大きくなりそうなとき

FX口座の開設チェックリスト(最短で実戦投入)

  1. 約定方式:NDD/STP系でスプレッドと約定品質のバランスが良い会社を選びます。
  2. 最小ロット:0.01ロット(1,000通貨)から取引できると資金管理が楽です。
  3. サーバー時刻:日本時間との時差をEAパラメータで吸収できるか確認します。
  4. ヒストリカル:1時間足の履歴が長いほどEA検証の信頼性が上がります。
  5. スワップ:長時間保有する設計ではないため影響は限定的です。

MT4での導入手順

  1. MT4を起動し、ファイル > データフォルダを開く
  2. MQL4/ExpertsにEAファイルを配置。
  3. MT4を再起動し、ナビゲーターからEAをチャートにドラッグ。
  4. パラメータでサーバー時刻と日本時間の差、取引ウィンドウ、リスク率を設定。
  5. ストラテジーテスターでUSDJPY・H1を選び、直近3〜5年を目安にバックテスト。

MQL4 EA(完全自動売買・初心者向けのシンプル設計)

以下は本記事のルールを実装したサンプルです。取引時間・ウィンドウ・リスクはパラメータで調整できます。サーバー時刻と日本時間の時差はブローカーにより異なるため、JstOffsetHoursで合わせてください。

//+------------------------------------------------------------------+
//|  USDJPY_TurnOfMonth_EA.mq4                                       |
//|  USD/JPY 月末月初アノマリー(TOM)シンプルEA                     |
//+------------------------------------------------------------------+
#property strict
input int    Magic            = 20250906;
input double RiskPercent      = 0.8;     // 1トレードの口座リスク(%)
input double MaxSpread        = 2.0;     // pips
input int    MA_Period        = 50;      // SMA期間(方向フィルター)
input int    ATR_Period       = 14;      // ATR期間
input double SLmult           = 1.8;     // 損切ATR倍率
input double TPmult           = 2.2;     // 利確ATR倍率
input double ATRMin           = 0.8;     // 最低ボラ判定の係数
input int    JstOffsetHours   = 7;       // サーバー時刻→日本時間の補正(例:+7)
input int    TokyoStartHour   = 9;       // 日本時間フィルター
input int    TokyoEndHour     = 12;
input int    LondonStartHour  = 16;
input int    LondonEndHour    = 18;
input bool   UseTokyo         = true;
input bool   UseLondon        = true;
input bool   CloseAtSessionEnd= true;

double PointPips(){ return (Digits==3 || Digits==5) ? 10*Point : Point; }

bool IsTurnOfMonthWindow(datetime t){
   // 暦日ベースの近似:月末±2日、月初±2日をTrueにする
   MqlDateTime mt; TimeToStruct(t, mt);
   int d = mt.day; int m = mt.mon; int y = mt.year;
   // 当月末日
   int lastDay = 31;
   for(int i=28; i<=31; i++){
      mt.day = i; datetime tt = StructToTime(mt);
      MqlDateTime mt2; TimeToStruct(tt, mt2);
      if(mt2.mon != m){ lastDay = i-1; break; }
   }
   // 条件判定
   if(d >= lastDay-2 && d <= lastDay) return true;           // 月末−2〜月末
   if(d >= 1 && d <= 3) return true;                          // 月初1〜3日
   return false;
}

bool IsTradingHour(datetime t){
   // サーバー時刻→日本時間
   datetime jst = t + 3600*JstOffsetHours;
   MqlDateTime jt; TimeToStruct(jst, jt);
   int h = jt.hour;
   bool tokyo  = UseTokyo  && (h >= TokyoStartHour  && h < TokyoEndHour);
   bool london = UseLondon && (h >= LondonStartHour && h < LondonEndHour);
   return (tokyo || london);
}

bool IsBarOpen(){
   static datetime lastbar=0;
   if(Time[0] != lastbar){
      lastbar = Time[0];
      return true;
   }
   return false;
}

double GetATR(int period){
   return iATR(Symbol(), PERIOD_H1, period, 0);
}

double GetMA(int period){
   return iMA(Symbol(), PERIOD_H1, period, 0, MODE_SMA, PRICE_CLOSE, 0);
}

double LotsByRisk(double sl_pips){
   if(sl_pips <= 0) return 0.0;
   double riskMoney = AccountBalance() * RiskPercent/100.0;
   double tickValue = MarketInfo(Symbol(), MODE_TICKVALUE);
   double lotStep   = MarketInfo(Symbol(), MODE_LOTSTEP);
   double minLot    = MarketInfo(Symbol(), MODE_MINLOT);
   double contract  = MarketInfo(Symbol(), MODE_LOTSIZE);
   double valuePerLotPerPip = (contract * PointPips() / 10.0) * (tickValue/Point);
   double lots = riskMoney / (sl_pips * valuePerLotPerPip);
   // 丸め&下限
   lots = MathFloor(lots/lotStep)*lotStep;
   if(lots < minLot) lots = minLot;
   return NormalizeDouble(lots, 2);
}

bool HasPosition(){
   for(int i=0;i<OrdersTotal();i++){
      if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES)){
         if(OrderSymbol()==Symbol() && OrderMagicNumber()==Magic) return true;
      }
   }
   return false;
}

void CloseAll(){
   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=10);
            if(OrderType()==OP_SELL) OrderClose(OrderTicket(), OrderLots(), Ask,  slippage=10);
         }
      }
   }
}

int start(){
   if(!IsBarOpen()) return(0);
   if(!IsTurnOfMonthWindow(TimeCurrent())) return(0);
   if(!IsTradingHour(TimeCurrent()))       return(0);

   // スプレッド判定
   double spreadPips = (Ask - Bid)/PointPips();
   if(spreadPips > MaxSpread) return(0);

   // フィルター計算(H1)
   double atr = GetATR(ATR_Period);
   double ma  = GetMA(MA_Period);
   double price = iClose(Symbol(), PERIOD_H1, 1);
   if(atr < ATRMin * 0.01) { /* 桁違い対策:ブローカーにより違うため緩い判定 */ }

   // 方向判定
   int dir = 0; // 1=Buy, -1=Sell
   if(price > ma) dir = 1;
   if(price < ma) dir = -1;
   if(dir==0) return(0);

   // 既存ポジション回避
   if(HasPosition()) return(0);

   // SL/TP(pips換算)
   double atr_pips = atr / PointPips();
   double sl_pips  = atr_pips * SLmult;
   double tp_pips  = atr_pips * TPmult;
   if(sl_pips <= 0 || tp_pips <= 0) return(0);

   double lots = LotsByRisk(sl_pips);
   if(lots <= 0) return(0);

   int ticket;
   if(dir==1){
      double sl=Bid - sl_pips*PointPips();
      double tp=Bid + tp_pips*PointPips();
      ticket = OrderSend(Symbol(), OP_BUY, lots, Ask, 10, sl, tp, "TOM Buy", Magic, 0, clrBlue);
   }else{
      double sl=Ask + sl_pips*PointPips();
      double tp=Ask - tp_pips*PointPips();
      ticket = OrderSend(Symbol(), OP_SELL, lots, Bid, 10, sl, tp, "TOM Sell", Magic, 0, clrRed);
   }
   return(0);
}
//+------------------------------------------------------------------+

バックテストの読み方(初心者が見るポイント)

  • ドローダウン率:最大20%以内を目安。10%以内なら優秀です。
  • 損益曲線の形:長期で右肩上がり、フラット期間が長すぎないか。
  • トレード数:月数×取引日×時間帯で概算できます。極端に少ないと再現性が乏しくなります。
  • 勝率よりPF:勝率50%でも利大損小なら十分に戦えます。

パラメータ調整の方針

最初はデフォルトのまま。触る順番は、MaxSpread → JstOffsetHours → 時間帯 → ATR/SL/TP倍率です。過学習を避けるため、パラメータのグリッド探索は幅広く粗く、最後に僅かな微調整だけにとどめます。

拡張アイデア

  • ニューヨーク終盤の時間切れルール:22:00(日本時間)に強制クローズで週末ギャップを回避。
  • トレイリング:含み益がATR×1.0を超えたらSLを建値へ。
  • 複数通貨:まずはクロス円(EURJPY/GBPJPY)に横展開。ただし相関でリスクは増えます。
  • トレード回避リスト:雇用統計・CPI・FOMC日を除外。

よくある失敗

  1. 「毎月必ず動く」と思い込む:偏りは確率の話です。条件未充足日は休むほうが利益です。
  2. 過度のパラメータ最適化:バックテストの見かけ上の勝率が上がっても、実弾で崩れます。
  3. スプレッドを無視:月末月初は一時的にスプレッド拡大が起きやすいです。
  4. 時差設定ミス:JstOffsetHoursがズレていると、狙うべき時間から外れます。

まとめ

月末月初の季節性は、「待つ・絞る・繰り返す」を体現するテーマとして初心者に最適です。USD/JPY一本に集中し、時間帯・方向・ボラの3フィルターでシンプルに攻める。EAでミスを減らし、資金管理で生存確率を高める。まずは小ロットで実装し、手応えが出たら段階的にサイズを上げていきましょう。

補遺:検証テンプレート(手作業版)

  1. 過去3年分のUSD/JPY 1時間足を用意します。
  2. 各月ごとに月末営業日と月初営業日をカレンダーで確認します(厳密でなくて構いません)。
  3. 該当ウィンドウに色を付け、東京9-12時とロンドン16-18時に縦線を引きます。
  4. SMA(50)とATR(14)を表示し、方向・ボラ・結果をスプレッドシートに記録します。
  5. 条件を満たさない日は「No Trade」と明確に記録します。
  6. 月ごとに勝ち/負けと合計pips、最大連敗を集計します。

この作業は地味ですが、「取らなくてよい日を取らない」という感覚が身につきます。EAはこの意思決定を自動化するツールに過ぎません。

補遺:資金管理の超基本(ロット計算の考え方)

損切距離(pips)と口座残高、許容リスク(%)からロットを逆算します。本EAではこれを自動計算しますが、手計算でも理解しておくと安心です。「1回の失敗で口座の何%を失うか」を常に一定に保つのがポイントです。

補遺:裁量での悪習を避けるチェックリスト

  • 連敗後にロットを上げない
  • 条件未充足での「なんとなくエントリー」をしない
  • 勝っていてもルールの時間になったら必ずクローズ
  • 重要指標の直前に新規で入らない

コメント

タイトルとURLをコピーしました