EAで偽の為替介入を察知してエントリーする(サンプルソース)

kainyu1-1
EA・MT4ノウハウ集

今回は、為替介入後に出る急落と戻りの値動きを題材に、MT4のEAでどのように条件判定するかを検証したサンプル記事です。

完成EAとして実運用をすすめるものではなく、相場イベントをきっかけにしたロジック検証の読み物として見てください。

はじめに

為替介入時によくある偽介入(瞬間40pipsぐらい下げてすぐ戻す動きのやつ)でストンと下げた所をロングでとっていけるEAでも作ってみよかなという事で作ってみました。

この記事では、急落幅、当日の値動きフィルター、TP/SL設定をどのようにEAコードへ落とし込んでいるかを見ていきます。

作ろうと思ったきっかけ

為替介入後はまた為替介入がきたか!?みたいな偽の為替介入の動きが頻発します。

EAで偽の為替介入を察知してエントリーする(サンプルソース)

1分足でこんな感じの動きです。裁量で1回は取れたのですが、PC前でずっとチャートに張り付いている状態になったので、EAで何とかできないかという事で作りました。

今回は検証用として形にしたサンプルを掲載します。条件判定の流れと注文処理が追える形にしているので、コードを読みながら仕組みを確認してみてください。

EAの仕様

それでは、今回のソースコードのエントリー/決済/パラメータ設定についての仕様を説明します。

このEAで検証しているロジック

  • USDJPYの1分足で、直近の終値から一定pips以上急落したら買いエントリー候補にする
  • 当日高値が始値から大きく上がっている日は、介入警戒としてエントリーを見送る
  • 決済はEA内で成行決済せず、エントリー時に設定するTPとSLで管理する
  • 直近で決済したばかりの場合は、連続して入り直さないようにする

ざっくり言うと、「急落後の戻りを狙う条件」と「本物の介入っぽい日は避ける条件」を組み合わせた検証用ロジックです。

エントリータイミング

ロングエントリー

前回のローソク足の終わり値から、現在の値段が設定の『Entry_Pips』以上下げた場合エントリーします。

例)1分前の終値が145.500なのに今の値が145.000だったらロングエントリー

今回は、当時確認した直近2回の為替介入をもとに、当日150pips以上上げている場合はエントリーしない条件を入れています。

ショートエントリー

ショートエントリーはしません。

エントリー例

EAで偽の為替介入を察知してエントリーする(サンプルソース)

私がよく作っているローソク足が確定したらエントリーする形ではなく、常に値動きを監視するタイプです。Entry_Pips以上下げた場合はロングエントリーし、エントリー時にTP(指値)とSL(逆指値)を設定しています。

決済タイミング

今回は決済処理は無く、TP(指値)とSL(逆指値)のみで管理しています。

その他の仕様

このEAは米ドル円の1分足で動かして確認しました。

設定値は以下の通りです。

  1. MagicNumber:このEAの注文を識別する番号です。
  2. LotSize:ロット数です。コード上の初期値と、検証で使うロットを確認します。
  3. Entry_Spread:エントリー時に許容するスプレッドです。
  4. Close_Spread:決済時や監視時に使うスプレッド条件です。
  5. Entry_Pips:直近の終値からどれだけ下げたらエントリー候補にするかの値です。
  6. Order_TP:エントリー時に設定する利確幅です。
  7. Order_SL:エントリー時に設定する損切り幅です。

保持するポジションは最大1つです。

エントリー後、10分間は次のエントリーをしません。(速攻でTPやSL食らったら追加でエントリーしていたのでしないようにしました)

EAレポート結果

EAで偽の為替介入を察知してエントリーする(サンプルソース)

検証期間は、為替介入1回目があった2022年9月23日の前日から、2回目があった2022年10月21日までです。

トレード件数は8件です。件数は多くありませんが、想定した値動きに対してEAがどう反応するかを確認する検証例としては、ロジックの流れを追いやすい結果になっています。

1トレード目(唯一の負けトレード)

EAで偽の為替介入を察知してエントリーする(サンプルソース)

最初はすぐに負けていますが、これは為替介入前の別要因による大きな上下動でした。検証時は、狙っているイベントの値動きなのか、別要因の値動きなのかを分けて見る必要があります。

2,3トレード目

EAで偽の為替介入を察知してエントリーする(サンプルソース)

取り合えずここの1つ目は裁量で運よく取れたのでここをイメージして作りました。ここベースでエントリーpipsの設定値やらTPとSLの設定値を決めています。

4、5トレード目

EAで偽の為替介入を察知してエントリーする(サンプルソース)
EAで偽の為替介入を察知してエントリーする(サンプルソース)

こちらもうまく入れています。

本物の為替介入時

EAで偽の為替介入を察知してエントリーする(サンプルソース)

ここは2022年10月21日の2回目の為替介入時です。この例では、当日150pips以上上げていたためフィルターでエントリーを避けています。

このEAサンプルを使う前の注意点

注意点はこの章にまとめておきます。この記事のコードは、相場イベント時の条件判定を学ぶための教材用サンプルとして扱ってください。

  • 検証件数は8件なので、この結果だけでロジックの優位性を判断しない
  • 為替介入や急変相場では、スプレッド拡大・滑り・約定拒否が起きやすい
  • 複数通貨や複数EAで使う場合は、MagicNumberだけでなくSymbolの絞り込みも確認する
  • OrderSend、OrderClose、OrderModifyを追加・変更する場合は、戻り値とエラー番号を確認する
  • 実運用前には、バックテストだけでなくデモ口座でも挙動を確認する

ダウンロード

EAファイルのダウンロードはこちら👇

📥 偽為替介入を察知EA をダウンロード

\ 自作前に市販EAの実績も確認 /
GogoJungle市販EAウォッチで、フォワード成績・PF・最大DD・前週比をチェックできます。

▶ 市販EAの実績を見る

ソースコード

コードから読み取れる処理の流れ

  • iClose(NULL, 0, 1) で直近の終値を確認し、現在値がEntry_Pips分下がっているかを見る
  • iHigh(NULL, PERIOD_D1, 0)iOpen(NULL, PERIOD_D1, 0) で当日の値幅フィルターを作る
  • エントリー候補になったら、TPは Bid + Order_TP * Point、SLは Bid - Order_SL * Point で設定する
  • funcOrder_Select() で直近の決済履歴を見て、短時間で入り直さないようにする
  • funcOrder_Send() でスプレッド条件を確認しながら新規注文を出す

注文処理で見るポイント

  • MagicNumberは、EAの注文を見分けるための基本です。詳しくは EAのマジックナンバーとは? でも整理しています。
  • Symbolは、どの通貨ペアを対象にするかを見るポイントです。このコードではOrderSendの通貨ペアに NULL を使っているため、チャートの通貨ペアで注文します。
  • OrderSendは新規注文を出す関数です。このコードでは失敗時にエラー番号を出し、少し待ってから再試行する流れになっています。
  • OrderCloseによる成行決済は、このサンプル内では使っていません。決済はTP/SLに任せ、過去の決済時刻は OrderCloseTime() で見ています。

OrderSend、OrderModify、OrderCloseをまとめて確認したい場合は、OrderSend・OrderModify・OrderCloseのサンプル関数 も参考になります。

//+------------------------------------------------------------------+
//|                                                   NiseKainyu.mq4 |
//|                        Copyright 2020, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
 
 
 
extern string p01="--- Settings ---";
input int MagicNumber = 77777;
input double LotSize = 1;
 
input int Entry_Spread = 100;     //エントリー時の許容スプレッド
input int Close_Spread = 100;     //決済時及び監視する許容スプレッド
 
input double Entry_Pips = 420;
 
input double Order_TP = 420;
input double Order_SL = 900;
 
 
int Main_ea_ticket[2]; //ポジションのチケット番号保有用(2だと3ポジまで) 5ポジ持たせたい場合は4にすること
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
    
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
    
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
int start()
  {
//---
int OrderKekka;
int orderPtn = 0;
double ea_order_stop_price,ea_order_good_price;
    
   //前回1分足の値を取得する
   double rosokuValueCloseP = iClose(NULL, 0, 1);   
 
   //その日の高値、始値を取得する
   double rosokuValueHighD  = iHigh(NULL, PERIOD_D1, 0);
   double rosokuValueOpenD  = iOpen(NULL, PERIOD_D1, 0);
    
   //Entry_Pips分急激に下げた場合
   if(Bid <= rosokuValueCloseP - Entry_Pips  * Point){
    
      //当日既に150pips以上あがっていたら介入かもしれないのでエントリーしない
      if(rosokuValueHighD >= rosokuValueOpenD + 1500  * Point){
         Print("介入警戒:",rosokuValueHighD,">=",rosokuValueOpenD + 1500  * Point);      
         orderPtn = 0;
      }else{
         orderPtn = 1;
         ea_order_stop_price = Bid - Order_SL * Point;
         ea_order_good_price = Bid + Order_TP * Point ;
      }
   }
 
   if(funcOrder_Select(1,0,MagicNumber,Main_ea_ticket) == true)
   {
      //10分以内にトレードした場合は様子見
      if(OrderCloseTime() > iTime(NULL, 0, 10)){
         orderPtn = 0;
      }
   }
    
   if(orderPtn > 0)   
   {
      //新規注文
      OrderKekka = funcOrder_Send(Entry_Spread,0,orderPtn - 1,ea_order_stop_price,ea_order_good_price,"",MagicNumber,LotSize);
   }
    
 
      return(0);
}
//+------------------------------------------------------------------+
 
//+------------------------------------------------------------------+
//|【関数】オーダーセレクト関数
//| 
//|【引数】 mode 0:最新ポジション選択(ポジション数も取得) 1:1つ前の決済ポジ 2:2つ前の決済ポジ ・・・ 
//|【引数】 ticket 0:チケット番号を選択したい場合チケット番号を渡すと保持してくれる
//|【引数】 マジックナンバー
//|【引数(参照)】 mode 0の場合、最新のチケット番号群を更新する
//|
//|【戻値】ポジション数(過去の場合はあったINDEX)
//| 
int funcOrder_Select(int mode,int ticket,int ea_order_MagicNo,int &o_Ticket[2]){
int total=0,cnt=0,ticket_buf=0;
bool OrderKekka =false;
 
   total = 0;
 
   if(mode==0){
 
 
      //チケット配列のクリア
      for(int i = 0;i<3;i++){
         o_Ticket[i]=0;
      }
 
      for(cnt = 0; cnt < OrdersTotal(); cnt++){
         if(OrderSelect(cnt,SELECT_BY_POS,MODE_TRADES) == true){
            if(OrderMagicNumber() == ea_order_MagicNo){
               o_Ticket[total] = OrderTicket();
               ticket_buf = OrderTicket();
                
               total = total+1;
 
            }
         }
      }
 
      //件数が会った場合、最新のポジションを選択する     
      if(ticket == 0 && ticket_buf !=0){
         OrderKekka = OrderSelect(ticket_buf,SELECT_BY_TICKET,MODE_TRADES); 
      }
       
      //チケット番号が渡されている場合戻す
      if(ticket != 0){
         OrderKekka = OrderSelect(ticket,SELECT_BY_TICKET,MODE_TRADES); 
      }
       
   }else{
 
      for(cnt = OrdersHistoryTotal(); cnt > 0; cnt--){
         if(OrderSelect(cnt-1,SELECT_BY_POS,MODE_HISTORY) == true){
            if(OrderMagicNumber() == ea_order_MagicNo){
               total = total+1;
                
               if(mode <= total){
                  break;
               }
            }
         }
      }
   }
 
 
 
   return(total);
    
}
 
//+------------------------------------------------------------------+
//|【関数】新規注文関数
//|
//|【引数】 EntrySpread:エントリー時の許容スプレッド
//|【引数】 ea_order_ryoudate:両建て有無(0:無 1:有)
//|【引数】 ea_order_entry_Type:売買(0:買 1:売)
//|【引数】 order_stop_price:損切値  order_good_price:利確値
//|【引数】 orderComment:オーダーコメント    ea_order_MagicNo:マジックNo
//|
//|【戻値】True:成功 False:失敗
//|
//|
bool funcOrder_Send(int EntrySpread, int ea_order_ryoudate, int ea_order_entry_Type, double order_stop_price, double order_good_price,string orderComment,int ea_order_MagicNo,double Lots)
{
 
   int order_resend_num,ea_ticket_res;        // エントリー試行回数,チケットNo
 
   double ea_order_entry_price; // エントリーレート
   color order_Color;
   bool kekka;
   int ea_ticket[2]; //ポジションのチケット番号保有用(2だと3ポジまで) 5ポジ持たせたい場合は4にすること
    
   kekka=false;
 
   int total = funcOrder_Select(0,0,ea_order_MagicNo,ea_ticket);
       
   if(total > 0){
      if(ea_order_entry_Type==0 && OrderType() == OP_BUY){
         //ポジション保持中
         return kekka;
      }
      else if(ea_order_entry_Type==1 && OrderType() == OP_SELL){
         //ポジション保持中
         return kekka;
      }
             
      if(ea_order_ryoudate == 0){
         return kekka;
      }
   }
 
 
 
    
   for( order_resend_num = 0; order_resend_num < 3; order_resend_num++ ) {    // エントリー試行回数上限:3回
 
      //スプレッドが1銭未満の場合のみ新規注文する(2銭未満にしたい場合は20にする)
      if(MarketInfo(NULL,MODE_SPREAD) <= EntrySpread){
 
         if(ea_order_entry_Type == OP_BUY){   
            ea_order_entry_price = Ask;               // 現在の買値でエントリー
            order_Color = clrBlue;
         }else if(ea_order_entry_Type == OP_SELL){        
            ea_order_entry_price = Bid;               // 現在の売値でエントリー
            order_Color = clrRed;            
         }
 
         if(orderComment == 25){
            order_Color = clrGreenYellow;            
         }
 
         // FXCMでは新規エントリー時にストップ/リミットを設定出来ない。
         ea_ticket_res = OrderSend(   // 新規エントリー注文
            NULL,                     // 通貨ペア
            ea_order_entry_Type,      // オーダータイプ[OP_BUY / OP_SELL]
            Lots,                      // ロット[0.01単位](FXTFは1=10Lot)
            ea_order_entry_price,     // オーダープライスレート
            20,                       // スリップ上限    (int)[分解能 0.1pips]
            order_stop_price,      // ストップレート
            order_good_price,      // リミットレート
            orderComment,             // オーダーコメント
            ea_order_MagicNo,         // マジックナンバー(識別用)
            0,                        // オーダーリミット時間
            order_Color               // オーダーアイコンカラー
            );
          
         if ( ea_ticket_res == -1) { 
            Print("新規注文失敗 エラーNo:",GetLastError()," リトライ回数:",order_resend_num+1);
             
            Sleep(1000);                                           // msec待ち
            RefreshRates();                                        // レート更新
    
         } else {    // 注文約定
            Print("新規注文完了 レート:",ea_order_entry_price," ロット数:",Lots," オーダーコメント:",orderComment);
             
            Sleep(500);                                           // 500msec待ち
            kekka=true;
            break;
         }
 
      }else{
          Print("スプレッド拡大中(銭)=",MarketInfo(NULL,MODE_SPREAD) / 10," リトライ回数=",order_resend_num+1);
           
          Sleep(5000);                                           // msec待ち
          RefreshRates();                                        // レート更新
      }
 
   }
   return kekka;   
}

なお、コード上では Entry_Pips という名前を使っていますが、実際の計算では Point を掛けています。 USDJPYの3桁表示口座では、420 はおおよそ42.0pips相当として見る必要があります。 口座の桁数によって見え方が変わるため、実際に使う場合は必ずテスト環境で値幅を確認してください。

さいごに

以上、為替介入後の急変動を題材にしたEAサンプルでした。

このサンプルは、急落後の戻りをどのように条件化するかを見るための検証例です。ロジックそのものだけでなく、パラメータ、注文処理、過去決済の確認方法まで合わせて読むと、EA作成の練習として使いやすくなります。

他のサンプルコードも見ながら、単体のロジックだけでなく、注文管理やリスク管理の作り方も少しずつ確認していきましょう。

💡 このサンプルをベースにしたカスタマイズ相談も可能です。
仕様相談・EA作成の詳細はこちら


✅ 今回のロジックをベースにしたEAサンプルも多数公開中

今回紹介したようなEAの実装例・考え方をベースに、
当サイトではさまざまなFX自動売買EAのサンプルコードを公開しています。

ロジックの違いや設計の考え方を比較しながら、
自分に合ったEA構成を探したい方はぜひチェックしてみてください。


📊 EA運用・検証フェーズに進みたい方へ

今回のような仕組みを理解したうえで、
「実際にどのEAが安定しているのか」「検証データではどんな差が出ているのか」
を確認したい方は、以下の記事も参考になります。


⚙️ 同じ条件でバックテストしたい方へ(検証環境)

当サイトのバックテストは、同一の検証環境で比較できるように統一しています。
EAの再現テストやパラメータ検証を行う場合は、
検証環境の作り方もあわせて確認しておくと理解が深まります。


EA開発初心者向けに、今後も実践的に使えるMQL4関数や実装例を紹介していきます。
気になる機能やロジックがあれば、用途別に整理した関連記事もぜひあわせてご覧ください。

コメント

  1. sutemaruさんのアイコン sutemaru より:

    為替介入の為だけでは、使い道が無いので、口座情報を付けると使えるかな?
    と思いました。

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