原油トレードにかなりハマっています。どういう手法が効果的なのか、裁量であれこれ試しているんですが、仮想通貨なみに価格が上下して、通常の指標では判断しづらい。
あれこれ試した結果、RVIがかなり良さげ。RVI自体はボラティリティを判断するオシレーター系の指標でも、かなりマイナーな指標のようです。でも、名のあるトレーダーも使ってる様子。RVIのトレードの基本となるEAのロジックを組んでみました。
RVIとは?
Relative Vigor Indexの略で、直訳すると相対的変動率指数。日本語にすると、なんのこっちゃ分かりませんが、オシレーター系指標で、RSIを応用した分析手法です。
特徴的なのが、ボラティリティ(変動率)が増えているか、減っているか分かり、その転換点が分かります。同じボラティリティの判断で、ボリンジャーバンドがよく使われますが、それではわからない部分もわかります。逆張りの判断にも使えます。
RVIの基本ルール
MQL4でロジック組むにしても、基本ルールはわかっていないと正しく組めません。
RVIの売買ルールをまとめると以下のとおり。
RVIが0より上でボラティリティ拡大
ボラティリティが拡大するので、つまり、流れに乗って買い。
0から上に上がる部分が価格上昇タイミング。
RVIが0より下でボラティリティ縮小
ボラティリティが縮小を始めたので売り。
0から下に下がる部分が価格下落タイミングと言われてます。
逆張りタイミングはRVIとシグナルのクロス
RVIはボラティリティ判定と別に、RVIとシグナルの2本の線のクロスで、売買の転換が分かります(騙しもあり)。
タイミングはシグナル(赤線)に対して、RVIが下から上に抜けたタイミングがゴールデンクロス。シグナルに対して、上から下に抜けるタイミングがデッドクロスです。
ここまでの内容で、プログラムを組んでみました。
RVI、シグナル取得のプログラムコード
取得用の関数はiRVI。MQ4であれば、使い方もかんたんです。
注意:プログラムコードは公開してますが、リスクはこちらでは負いません。ご参考程度にとらえてください。
用意する変数
取得値は正負の小数点なので、必ずdouble(浮動小数点)の変数です。RVIメインとシグナルでそれぞれ。あと、1つまえのティックの情報も取得したいので、全部で4つ宣言します。
1 2 3 4 5 6 7 |
//現在の値 double now_rvi; //RVI double now_signal; //シグナル //1ティック前の値 double before_rvi; //RVI double before_signal; //シグナル |
現在値の取得ロジック
第一引数のNULLで、現在開いているチャートの通貨の値をみます。2番めの引数は、現在表示している時間軸の値をみます。最後から2番めの引数でメイン(RVI)かシグナルの判定です。
1 2 3 4 5 |
//RVI now_rvi = iRVI(NULL,PERIOD_CURRENT,average_period,MODE_MAIN,0); //シグナル now_signal = iRVI(NULL,PERIOD_CURRENT,average_period,MODE_SIGNAL,0); |
順張りはRVIの値の正負
順張りはRVIの正負で判定しますが、直前までのRVIの値がマイナスで、0以上になるタイミングでロング。
1 2 3 4 5 6 7 8 9 10 |
//順張り //ボラティリティ拡大なのでロング if(before_rvi < 0 && now_rvi >= 0){ // レートのリフレッシュ RefreshRates(); //ロングエントリー処理 OrderSend(Symbol(),OP_BUY,Lots,Ask,Slippage,Ask*(1-SLrate),0,"",MAGIC,0,Blue); } |
その逆の直前までのRVIの値がプラスで、0以下になるタイミングでショート。
1 2 3 4 5 6 7 8 9 |
//ボラティリティ縮小なのでショート if(before_rvi > 0 && now_rvi <= 0){ // レートのリフレッシュ RefreshRates(); //ロングエントリー処理 OrderSend(Symbol(),OP_BUY,Lots,Ask,Slippage,Ask*(1-SLrate),0,"",MAGIC,0,Blue); } |
RVIとシグナルのクロス判定での逆張り
変数でも宣言しましたが、クロス判定するには1ティック前の情報が必要になります。
1ティック前の情報取得ロジック
最後の引数を1とすれば、1ティック前の情報が取得できます。2とすれば2ティック前。
1 2 3 4 5 |
//RVI before_rvi = iRVI(NULL,PERIOD_CURRENT,average_period,MODE_MAIN,1); //シグナル before_signal = iRVI(NULL,PERIOD_CURRENT,average_period,MODE_SIGNAL,1); |
RVIとシグナルのゴールデンクロス判定
ロングエントリーのタイミングですね。RVIがシグナルに対して下から上に抜けたというのをプログラムに起こすと、1ティック前のシグナルがRVIより大きくて、現在の値がRVIがシグナル以上という書き方になります。
1 2 3 4 5 6 7 8 9 10 |
//メインとシグナルのゴールデンクロス if( (before_signal > before_rvi) && //1ティック前がシグナルのほうが大きい? (now_signal <= now_rvi) ){ //クロスした? // レートのリフレッシュ RefreshRates(); //ロングエントリー処理 OrderSend(Symbol(),OP_BUY,Lots,Ask,Slippage,Ask*(1-SLrate),0,"",MAGIC,0,Blue); } |
RVIとシグナルのデッドクロス判定
ショートエントリーのタイミング。
シグナルに対して、上から下に抜けるというのをプログラムに起こすと、1ティック前のシグナルがRVIより小さくて、現在の値がRVIがシグナルより大という書き方になります。
1 2 3 4 5 6 7 8 9 10 |
//メインとシグナルのデッドクロス if ( (before_signal < before_rvi) && //1ティック前がメインのほうが大きい? (now_signal >= now_rvi) ){ //クロスした? // レートのリフレッシュ RefreshRates(); //ショートエントリー処理 OrderSend(Symbol(),OP_SELL,Lots,Bid,Slippage,Bid*(1+SLrate),0,"",MAGIC,0,Red); } |
プログラムコード全体
5分足か15分足でのトレードを想定してます。なので、RVIの期間は7を標準設定。時間軸をそれより大きくするなら、期間は見直す必要あり。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 |
//+------------------------------------------------------------------+ //| RVI_trade.mq4 | //+------------------------------------------------------------------+ #property copyright "Copyright 2020,Yano" #property link "https://www.fx-doremi.com/" #property version "1.00" #property strict #define MAGIC 202005240933 extern int average_period = 7; //RVIの期間 extern double Lots = 0.1; //ロット extern int Slippage = 30; //スリッページ extern double Profitrate = 0.0005; //利確(0.00025=0.025%) extern double SLrate = 0.0005; //損切り(0.00025=0.025%) double before_closs_rvi = 0; //フラグ int Buy_flg = 0; int Sell_flg = 0; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- before_closs_rvi = 0; Buy_flg = 0; Sell_flg = 0; //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- //変数 //現在の値 double now_rvi; //RVI double now_signal; //シグナル double now_rvi_1min; double now_signal_1min; //1ティック前の値 double before_rvi; //RVI double before_signal; //シグナル double before_rvi_1min; //RVI double before_signal_1min; //シグナル //トレード可能かチェック // if(Volume[0]>1 || IsTradeAllowed()==false){ // Print("トレード不可"); // return; // } //現在値取得 //RVI now_rvi = iRVI(NULL,PERIOD_CURRENT,average_period,MODE_MAIN,0); now_rvi_1min = iRVI(NULL,PERIOD_MN1,average_period,MODE_MAIN,0); //シグナル now_signal = iRVI(NULL,PERIOD_CURRENT,average_period,MODE_SIGNAL,0); now_signal_1min = iRVI(NULL,PERIOD_MN1,average_period,MODE_SIGNAL,0); //1ティック前の情報取得 //RVI before_rvi = iRVI(NULL,PERIOD_CURRENT,average_period,MODE_MAIN,1); before_rvi_1min = iRVI(NULL,PERIOD_MN1,average_period,MODE_MAIN,1); //シグナル before_signal = iRVI(NULL,PERIOD_CURRENT,average_period,MODE_SIGNAL,1); before_signal_1min = iRVI(NULL,PERIOD_MN1,average_period,MODE_SIGNAL,1); //★2020-5-25 追記 ボリンジャーバンド2σ上下の幅が指定値以下? int check_bb_band2 = Check_BB_bands2_width(0.05); //順張り //ボラティリティ拡大なのでロング if(before_signal < 0 && now_signal >= 0 && Buy_flg == 0 && check_bb_band2 == 1){ // レートのリフレッシュ RefreshRates(); //ロングエントリー処理 OrderSend(Symbol(),OP_BUY,Lots,Ask,Slippage,Ask*(1-SLrate),Ask*(1+Profitrate),"Buy order",MAGIC,0,Blue); Buy_flg = 1; } //ボラティリティ縮小なのでショート if(before_signal > 0 && now_signal <= 0 && Sell_flg == 0 && check_bb_band2 == 1){ // レートのリフレッシュ RefreshRates(); //shortエントリー処理 OrderSend(Symbol(),OP_SELL,Lots,Bid,Slippage,Bid*(1+SLrate),Bid*(1-Profitrate),"Sell order",MAGIC,0,Red); Sell_flg = 1; } //メインとシグナルのゴールデンクロス if( (before_signal > before_rvi) && //1ティック前がシグナルのほうが大きい? (now_signal <= now_rvi) //クロスした? ){ //利確していなかったらポジション決済 ClosePositions_SELL(); Sell_flg = 0; } //メインとシグナルのデッドクロス if ( (before_signal < before_rvi) && //1ティック前がメインのほうが大きい? (now_signal >= now_rvi) //クロスした? ){ //利確していなかったらポジション決済 ClosePositions_BUY(); Buy_flg = 0; } /* --- 騙し判定未実装のためコメントアウト //逆張り //メインとシグナルのゴールデンクロス if( (before_signal > before_rvi) && //1ティック前がシグナルのほうが大きい? (now_signal <= now_rvi) && //クロスした? (check_bb_band2 == 1) //★追記 ボリンジャーバンドの幅が狭くない? ){ //利確していなかったらポジション決済 ClosePositions_SELL(); // レートのリフレッシュ RefreshRates(); //ロングエントリー処理 OrderSend(Symbol(),OP_BUY,Lots,Ask,Slippage,Ask*(1-SLrate),Ask*(1+Profitrate),"Buy order",MAGIC,0,Blue); //★追記 クロスした時のRVIを格納 before_closs_rvi = now_rvi; } //メインとシグナルのデッドクロス if ( (before_signal < before_rvi) && //1ティック前がメインのほうが大きい? (now_signal >= now_rvi) && //クロスした? (check_bb_band2 == 1) //★追記 ボリンジャーバンドの幅が狭くない? ){ //利確していなかったらポジション決済 ClosePositions_BUY(); // レートのリフレッシュ RefreshRates(); //ショートエントリー処理 OrderSend(Symbol(),OP_SELL,Lots,Bid,Slippage,Bid*(1+SLrate),Bid*(1-Profitrate),"Sell order",MAGIC,0,Red); //★追記 クロスした時のRVIを格納 before_closs_rvi = now_rvi; } */ } //+------------------------------------------------------------------+ //| ポジション決済 | //+------------------------------------------------------------------+ void ClosePositions_BUY(){ int i; for(i=0;i<OrdersTotal();i++){ if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES) == false){ break; } if(OrderMagicNumber() != MAGIC || OrderSymbol() != Symbol()){ continue; } //買いポジションのチェック if(OrderType() == OP_BUY){ OrderClose(OrderTicket(),OrderLots(),Bid,Slippage,White); break; } // ループを抜ける break; } } void ClosePositions_SELL(){ int i; for(i=0;i<OrdersTotal();i++){ if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES) == false){ break; } if(OrderMagicNumber() != MAGIC || OrderSymbol() != Symbol()){ continue; } //売りポジションのチェック if(OrderType() == OP_SELL){ OrderClose(OrderTicket(),OrderLots(),Ask,Slippage,White); break; } // ループを抜ける break; } } //+------------------------------------------------------------------+ //| ボリンジャーバンド幅のチェック //| 引数の指定値以上の場合 1を返す | //+------------------------------------------------------------------+ int Check_BB_bands2_width(double my_max){ int my_ret = 0; //2σボリンジャーバンド double bb_2 = iBands(NULL,PERIOD_CURRENT,20,2,0,PRICE_CLOSE,1,0); //-2σボリンジャーバンド double bb_M2 = iBands(NULL,PERIOD_CURRENT,20,2,0,PRICE_CLOSE,2,0); //バンド幅計算 double bb_haba = bb_2 - bb_M2; //一定値かどうかのチェック if(bb_haba > my_max){ my_ret = 1; } return my_ret; } //+------------------------------------------------------------------+ |
メインとシグナルのクロス処理は、騙しが回避できないからコメントアウトしてます。
現状は順張りのみ動作させてこんな結果
USDJPYの5分足で実トレードでテスト中。テストなのでロットを、0.03と、めちゃ下げてやってますが、概ね成果は出てます。
課題はメインとシグナルのクロス判定の騙し
RVIメインとシグナルのクロス判定は現在コメントアウトして動作させていません。現状のままだと、以下のような、チョロチョロしたタイミングでも全て売買を繰り返してしまいます。恐ろしい・・・。
目視による裁量だと、絶対エントリーしないタイミングですが、このままだとやっちゃいますね。上記場面で、6回も売買しちゃいます。
どーやって判断するか、今検討中。。
ご質問はコメント欄からお願いします