NYカット・オプション磁石戦略(FX)— 個人でも回せるEA完全ガイド

FX

本記事では、FX市場の「NYカット(New York Cut)」オプションに伴う価格の“磁石効果(ピンニング)”を、個人投資家でも再現しやすいルールに落とし込み、実運用まで一気通貫で解説します。初心者の方でも理解できるよう、理屈→ルール→検証→EA化→運用手順の順に整理し、すぐに実装できるサンプルEA(MQL4)を完全掲載します。

戦略の概要:NYカットと価格の“磁石効果”

FXオプションは毎営業日、ニューヨーク時間10:00(通称「NYカット」)で権利行使判定が確定します。巨額のストライク(例:USDJPYの150.00、EURUSDの1.1000など)が市場に存在すると、オプションディーラーはポジションのデルタを中立化するためのヘッジ売買(スポット売買)を行います。その結果、判定時刻が近づくほどスポット価格が大きなストライクに吸い寄せられる(=磁石効果)現象が観測されることがあります。

本戦略は、この需給由来の短期的な“吸引”を素直に利用し、NYカットの60~15分前に、価格がストライクから一定距離だけ離れている側であれば、ストライク方向へ逆張り(回帰)するのが基本方針です。

前提知識(初学者向けに要点だけ)

  1. ストライク(Strike):オプションの権利行使価格です。数字がキリ番(00、50など)に集まりやすい傾向があります。
  2. NYカット(10:00 NY):オプションの清算・判定の基準時刻です。JSTでは夏時間で23時、冬時間で翌日0時が目安です(ブローカー表示時刻に合わせてEAで調整します)。
  3. 磁石効果(ピンニング):判定が近づくと、ディーラーのヘッジが増えてスポット価格が大きいストライクに近づく現象の俗称です。毎日必ず起きるわけではなく、ニュースやボラティリティによって崩れる日もあります。

戦略のコア・アイデア

オプション勢のヘッジは、価格がストライクから離れるほど強まり、近づくほど弱まる傾向があります。よって、適度に離れているときほど回帰のリスクリワードが良くなりやすいのが直感的理解です。逆に、重大ニュースや強いトレンドが発生しているときは、ヘッジの効果が相対的に弱まり、磁石効果が出にくい(または逆に突き抜ける)ため、指標発表直前直後は除外し、時間で手仕舞いするルールを徹底します。

具体的な取引ルール(最小構成)

  1. 監視時間:NYカットの60分前~15分前のみを取引ウィンドウにします。
  2. 対象通貨:USDJPY、EURUSDなど主要通貨。スプレッドが狭く、約定が安定している通貨を優先します。
  3. ストライク指定:当日の大きめのストライク(例:149.50、150.00など)を1~2本、外部入力で指定。EAは距離(pips)に応じて自動判断します。
  4. エントリー:価格がストライクからEntryDistance以上離れており、かつストライク方向へ向かうモメンタム(直近バーの高安更新や価格>移動平均など)を確認できたら、ストライク方向に成行エントリーします。
  5. イグジット:①価格がBandPips以内までストライクに近づいたら利確、②ウィンドウ終了(NYカット直前)で時間決済、③逆行したら固定SLで損切り。TPは固定にしなくてもOK(距離到達で利確)。
  6. フィルター:重要指標(雇用統計、FOMC等)は手動で取引停止。また、スプレッドが普段より広い場合もスキップします。

推奨パラメータの目安

  • EntryDistance(最低エントリー距離):8~20 pips(ペアの1時間ATRに応じ調整)
  • BandPips(到達判定帯):2~5 pips
  • SL:12~20 pips(時間切れ決済があるため、致命傷を避ける堅さで)
  • 時間:60分前に監視開始、15分前で新規停止、1~2分前に全決済
  • ロット:口座残高の0.3~0.8%リスク相当(固定Lotでも可)

検証方法(初心者でもできる手順)

  1. データ準備:M1データをブローカーから取得し、NY夏時間・冬時間を跨ぐ期間を含めます。
  2. ストライク履歴の近似:実データがない場合は、キリ番と50刻み(…149.00, 149.50, 150.00…)を「大きめストライク候補」として仮置きし、EA外部パラメータで日々入力した前提で代替検証します。
  3. サンプル外テスト:直近3か月を除外してパラメータ調整→最後に除外期間で前向き検証。
  4. 手数料とスリッページ:バックテスト設定で必ず考慮します。
  5. 損益分布の確認:勝率・PFより、リスクリワード連敗深さに注目。時間決済が効いているかも確認します。

実運用のポイント

  • 1日最大1~2トレードに制限:勝ちやすいセットアップだけ拾い、欲張りを抑制します。
  • 価格がストライクへ向かう“向き”を重視:離れているだけではNG。回帰の初動シグナルを待つとダマシが減ります。
  • 時間切れの徹底:NYカット直前にシステム決済。引きずらないことがドローダウン抑制に直結します。
  • イベント除外:経済指標カレンダーを必ず確認。疑わしい日は回避しても年間の機会は十分あります。

よくある失敗と回避策

  • (失敗)遠すぎる距離で突撃→(回避)EntryDistanceはATR基準で現実的に。遠すぎると時間切れ未達が増えます。
  • (失敗)トレンド対抗で逆張りしすぎ→(回避)直近の戻り/押しが始まってから入る。簡易モメンタム条件をEAに実装。
  • (失敗)ニュース直撃→(回避)重要指標日は運用停止。裁量で外すことをルール化。
  • (失敗)手仕舞い遅れ→(回避)ウィンドウ終了時刻で強制時間決済

リスク管理の型

1トレードの許容損失を口座残高の0.5%前後に統一し、SLに基づきロットを計算します。連敗が続いたらロットを機械的に微減し、最大ドローダウンをコントロールします。相関通貨での同時建ては極力避け、一度に1ポジションを基本にすると、初心者でも破綻確率を大きく下げられます。

サンプルEA(MQL4:完全自動売買)

以下は、NYカット前の限定ウィンドウで「ストライク方向の回帰」を狙う簡易EAです。Strike1/Strike2ブローカー時間のNYカット時刻を外部入力し、時間帯・距離・帯で機械的に売買します。重要指標日は手動で無効化してください。


//+------------------------------------------------------------------+
//|  NYCut Magnet Revert EA (Simple)                                 |
//|  Pair: Major FX (e.g., USDJPY, EURUSD)                           |
//|  Logic: Move toward option strike before NY cut                  |
//+------------------------------------------------------------------+
#property strict

extern double Lots               = 0.10;
extern bool   UseRiskPercent     = false;
extern double RiskPercent        = 0.5;      // % per trade
extern double Strike1            = 150.00;   // e.g., USDJPY
extern double Strike2            = 0.00;     // optional second strike
extern bool   UseStrike2         = false;
extern int    CutHour            = 23;       // broker time (JST summer ~23, winter ~0 next day) adjust!
extern int    CutMinute          = 0;
extern int    WindowStartMin     = 60;       // minutes before cut to start trading
extern int    NewEntryStopMin    = 15;       // minutes before cut to stop new entries
extern int    CloseBeforeMin     = 2;        // minutes before cut to force close
extern int    EntryDistancePips  = 10;
extern int    BandPips           = 3;
extern int    SL_Pips            = 15;
extern int    MaxPositions       = 1;
extern int    Magic              = 40510;
extern int    Slippage           = 2;
extern int    MA_Period          = 20;       // simple momentum filter (price vs MA)

double Pip()
{
   if(Digits==3 || Digits==5) return(Point*10);
   return(Point);
}

double ToPriceFromPips(int p){ return p*Pip(); }

bool TradingWindowOpen(bool allowNew)
{
   datetime now = TimeCurrent();
   datetime cut = StringToTime(TimeToString(now, TIME_DATE)+" "+IntegerToString(CutHour)+":"+IntegerToString(CutMinute));
   if(cut < now) cut += 24*60*60; // if already passed, assume next day
   int mins_to_cut = (int)((cut - now)/60);
   if(allowNew)
      return (mins_to_cut <= WindowStartMin && mins_to_cut >= NewEntryStopMin);
   else
      return (mins_to_cut <= WindowStartMin);
}

double SMA(int period)
{
   double sum=0;
   for(int i=0;i<period;i++) sum += iClose(Symbol(), PERIOD_M1, i);
   return sum/period;
}

int CountPositions()
{
   int c=0;
   for(int i=0;i<OrdersTotal();i++){
      if(!OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) continue;
      if(OrderSymbol()==Symbol() && OrderMagicNumber()==Magic) c++;
   }
   return c;
}

bool CloseAll()
{
   bool ok=true;
   for(int i=0;i<OrdersTotal();i++){
      if(!OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) continue;
      if(OrderSymbol()!=Symbol() || OrderMagicNumber()!=Magic) continue;
      if(OrderType()==OP_BUY)   ok &= OrderClose(OrderTicket(), OrderLots(), Bid, Slippage);
      if(OrderType()==OP_SELL)  ok &= OrderClose(OrderTicket(), OrderLots(), Ask, Slippage);
   }
   return ok;
}

void PlaceOrder(int type, double sl_pips)
{
   double lot = Lots;
   if(UseRiskPercent){
      double acc = AccountBalance();
      double risk = acc * RiskPercent/100.0;
      double stop = sl_pips * Pip();
      double tickValue = MarketInfo(Symbol(), MODE_TICKVALUE);
      if(stop > 0 && tickValue > 0){
         lot = MathMax(0.01, NormalizeDouble(risk / (stop / Point * tickValue), 2));
      }
   }
   double price = (type==OP_BUY)? Ask : Bid;
   double sl = (type==OP_BUY)? price - ToPriceFromPips(SL_Pips) : price + ToPriceFromPips(SL_Pips);
   OrderSend(Symbol(), type, lot, price, Slippage, sl, 0, "NYCutMagnet", Magic, 0, clrNONE);
}

int OnInit(){ return(INIT_SUCCEEDED); }
int OnDeinit(){ return(0); }

int start()
{
   // Force close near cut
   if(TradingWindowOpen(false)==true){
      datetime now = TimeCurrent();
      datetime cut = StringToTime(TimeToString(now, TIME_DATE)+" "+IntegerToString(CutHour)+":"+IntegerToString(CutMinute));
      if(cut < now) cut += 24*60*60;
      int mins_to_cut = (int)((cut - now)/60);
      if(mins_to_cut <= CloseBeforeMin) CloseAll();
   }

   if(!TradingWindowOpen(true)) return(0);
   if(CountPositions() >= MaxPositions) return(0);

   // Momentum filter
   double ma = SMA(MA_Period);
   double bid = Bid;
   double ask = Ask;
   double mid = (bid+ask)/2.0;

   // Check strikes
   double strikes[2];
   strikes[0] = Strike1;
   strikes[1] = (UseStrike2? Strike2 : 0.0);

   for(int k=0;k<2;k++){
      double s = strikes[k];
      if(s <= 0.0) continue;

      double dist_pips = MathAbs((mid - s))/Pip();
      if(dist_pips < EntryDistancePips) continue; // too close for entry

      // Direction toward strike
      if(mid < s){
         // price below strike - aim BUY if momentum up
         if(mid > ma){
            PlaceOrder(OP_BUY, SL_Pips);
            break;
         }
      } else {
         // price above strike - aim SELL if momentum down
         if(mid < ma){
            PlaceOrder(OP_SELL, SL_Pips);
            break;
         }
      }
   }

   // Take profit when within band
   for(int i=0;i<OrdersTotal();i++){
      if(!OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) continue;
      if(OrderSymbol()!=Symbol() || OrderMagicNumber()!=Magic) continue;
      double s1 = Strike1;
      double s2 = (UseStrike2? Strike2 : 0.0);
      double s = (MathAbs(iClose(Symbol(), PERIOD_M1, 0)-s1) < MathAbs(iClose(Symbol(), PERIOD_M1, 0)-s2) || s2==0.0)? s1 : s2;
      double midp = (Bid+Ask)/2.0;
      if(MathAbs(midp - s)/Pip() <= BandPips){
         if(OrderType()==OP_BUY)  OrderClose(OrderTicket(), OrderLots(), Bid, Slippage);
         if(OrderType()==OP_SELL) OrderClose(OrderTicket(), OrderLots(), Ask, Slippage);
      }
   }

   return(0);
}
//+------------------------------------------------------------------+

設置と使い方

  1. MT4の「MQL4/Experts」へ上記コードを保存・コンパイルし、対象通貨のM1~M5チャートに適用します。
  2. その日の大きめストライクを入力(Strike1/2)。不明な日は、キリ番/50刻みのうち当日意識されやすい価格を選びます。
  3. ブローカー時間に合わせてCutHour/CutMinuteを調整します(夏冬時間で差異が出る点に注意)。
  4. 重要指標日はEAを外す/パラメータで停止する運用ルールを徹底します。

視覚化補助(TradingView Pine Script v5・任意)

チャート上で「ストライク」と「到達帯」を可視化し、裁量検証に使える簡易スクリプトです。


//@version=5
indicator("NY Cut Strike Visual", overlay=true)
strike  = input.float(defval=150.0, title="Strike")
band    = input.float(defval=0.03,  title="Band (e.g., 0.03 for 3 pips on JPY pairs)")
startH  = input.int(defval=22, title="Cut Hour (broker/JST adjusted)")
startM  = input.int(defval=0,  title="Cut Minute")
winMin  = input.int(defval=60, title="Window Minutes Before Cut")

cut     = timestamp("GMT+9", year, month, day, startH, startM)
winBeg  = cut - winMin*60*1000

inWin = time >= winBeg and time <= cut
plot(strike, color=inWin? color.new(color.orange, 0):color.new(color.orange, 80), linewidth=2)
plot(strike + band, color=color.new(color.gray, 60))
plot(strike - band, color=color.new(color.gray, 60))
bgcolor(inWin? color.new(color.teal, 85): na)

運用チェックリスト

  • その日の大きめストライクをメモ(例:USDJPY 150.00、EURUSD 1.1000)。
  • 指標カレンダーを確認。該当時刻±30分は基本ノートレード。
  • スプレッドが平常値かを確認。広い日は回避。
  • NYカット60分前から監視開始。回帰の初動を待つ。
  • ストライク到達帯で機械的に利確。Cut直前は時間決済。
  • 1日の最大損失上限を守る(例:口座0.8%)。

まとめ

NYカットの磁石効果は「毎日かならず」ではないものの、時間・距離・向き・手仕舞いの4点を守れば、初心者でも再現性のある短時間戦略として活用できます。まずは小ロットで挙動を把握し、バックテスト→フォワードテスト→小資金の実弾と段階を踏んでください。過度なレバレッジを避け、1トレードの損失を口座の0.5%前後に抑える習慣が、長く生き残るための最短ルートです。

コメント

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