見出し画像

RSIとローソク足で検証 ハイロー用サインツールを作ってみた⑥

前回までは、チャート上にサインを表示する部分を作成してきました。

今回からはロジックというか、条件の部分を作成し、結果を検証していきます。

前回までの内容はこちらから。


前回のコード内容における条件部分

// HighLow判定用の条件関数 trueとfalseを返す
bool HLCondition(string HL, int num)
{
   // numから過去に3本分取得
   CopyOpen(_Symbol,PERIOD_CURRENT,num,3,mOpen);
   CopyHigh(_Symbol,PERIOD_CURRENT,num,3,mHigh);
   CopyLow(_Symbol,PERIOD_CURRENT,num,3,mLow);
   CopyClose(_Symbol,PERIOD_CURRENT,num,3,mClose);
   CopyTime(_Symbol,PERIOD_CURRENT,num,3,mTime);
   
   bool HLC=false;
   // Lowになる条件
   if(HL=="Low" && mOpen[1]<mClose[1] && mOpen[0]<mClose[0]){
      HLC=true;
   // Highになる条件
   }else if(HL=="High" && mOpen[1]>mClose[1] && mOpen[0]>mClose[0]){
      HLC=true;
   }
   return (HLC);
}


今回のコード内容における条件部分

// 初期化処理
int OnInit()
{
   // RSIインジケータ 期間14
   RSIhandle = iRSI(_Symbol,PERIOD_CURRENT0,20,PRICE_CLOSE);
   ArraySetAsSeries(RSI,true);
   return(INIT_SUCCEEDED);
}

初期化部分で、RSIインジケータを呼び出します。期間20本です。

// HighLow判定用の条件関数 trueとfalseを返す
bool HLCondition(string HL, int num)
{
   // numから過去に3本分取得
   CopyOpen(_Symbol,PERIOD_CURRENT,num,3,mOpen);
   CopyHigh(_Symbol,PERIOD_CURRENT,num,3,mHigh);
   CopyLow(_Symbol,PERIOD_CURRENT,num,3,mLow);
   CopyClose(_Symbol,PERIOD_CURRENT,num,3,mClose);
   CopyTime(_Symbol,PERIOD_CURRENT,num,3,mTime);
   
   // RSIの条件を配列に取得
   CopyBuffer(RSIhandle,0,num,3,RSI);
   
   bool HLC=false;
   // Lowになる条件 RSIが60以上 2つ前が陽線 1つ前が陽線
   if(HL=="Low" && RSI[0]>60 && mOpen[1]<mClose[1] && mOpen[0]<mClose[0]){
      HLC=true;
   // Highになる条件 RSIが40以上 2つ前が陰線 1つ前が陰線
   }else if(HL=="High" && RSI[0]<40 && mOpen[1]>mClose[1] && mOpen[0]>mClose[0]){
      HLC=true;
   }
   return (HLC);
}

条件関数内で、新たにRSIの配列を作成し、条件に含めます。

今回は、以下の条件にしました。

  • High条件:RSIが40以下、2本前が陰線、1本前が陰線

  • Low条件:RSIが60以上、2本前が陽線、1本前が陽線


全コード内容

#property indicator_chart_window

// 外部入力パラメータ
input datetime startDate = D'2024/4/15 0:00';      // 計算する期間の開始日付
input int      AFbars = 1;                         // 何本先までを判定するか

// 変数
double highWin,highLost,lowWin,lowLost;
double Open[],High[],Low[],Close[];
double mOpen[],mHigh[],mLow[],mClose[];
datetime Time[], mTime[];

int AFnum;

int RSIhandle;
double RSI[];

// 初期化処理
int OnInit()
{
   // 0.1秒ごとにタイマーイベントを動かす
   EventSetMillisecondTimer(100);
   
   // オブジェクトの初期化処理
   setLabel("BoTest_Timer", clrWhite, CORNER_RIGHT_UPPER, 100, 30, "00:00:00", 16);
   setBox("BoTest_Box",clrNONE,CORNER_RIGHT_UPPER,100,60,90,30);
   setLabel("BoTest_Signal", clrWhite, CORNER_RIGHT_UPPER, 80, 65, "-", 16);
   
   // CopyArrayを現在足を0として並べる
   ArraySetAsSeries(Open,true);
   ArraySetAsSeries(High,true);
   ArraySetAsSeries(Low,true);
   ArraySetAsSeries(Close,true);
   ArraySetAsSeries(Time,true);

   // 条件関数用に作成
   ArraySetAsSeries(mOpen,true);
   ArraySetAsSeries(mHigh,true);
   ArraySetAsSeries(mLow,true);
   ArraySetAsSeries(mClose,true);
   ArraySetAsSeries(mTime,true);
   
   // RSIインジケータ 期間14
   RSIhandle = iRSI(_Symbol,PERIOD_CURRENT,20,PRICE_CLOSE);
   ArraySetAsSeries(RSI,true);
   
   // ローソク1本分は0
   AFnum = AFbars - 1;

   return(INIT_SUCCEEDED);
}

// 終了処理
void OnDeinit(const int  reason)
{
   // BoTestで始まるオブジェクトを全削除
   ObjectsDeleteAll(0,"BoTest_");
   // タイマーイベントを削除
   EventKillTimer();
   // コメント消す
   Comment("");
}

// HighLow判定用の条件関数 trueとfalseを返す
bool HLCondition(string HL, int num)
{
   // numから過去に3本分取得
   CopyOpen(_Symbol,PERIOD_CURRENT,num,3,mOpen);
   CopyHigh(_Symbol,PERIOD_CURRENT,num,3,mHigh);
   CopyLow(_Symbol,PERIOD_CURRENT,num,3,mLow);
   CopyClose(_Symbol,PERIOD_CURRENT,num,3,mClose);
   CopyTime(_Symbol,PERIOD_CURRENT,num,3,mTime);
   
   // RSIの条件を配列に取得
   CopyBuffer(RSIhandle,0,num,3,RSI);
   
   bool HLC=false;
   // Lowになる条件 RSIが60以上 2つ前が陽線 1つ前が陽線
   if(HL=="Low" && RSI[0]>60 && mOpen[1]<mClose[1] && mOpen[0]<mClose[0]){
      HLC=true;
   // Highになる条件 RSIが40以上 2つ前が陰線 1つ前が陰線
   }else if(HL=="High" && RSI[0]<40 && mOpen[1]>mClose[1] && mOpen[0]>mClose[0]){
      HLC=true;
   }
   return (HLC);
}


// 毎tickで計算
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
{
   // 開始日付のバー番号を取得する
   int startBar = iBarShift(_Symbol,PERIOD_CURRENT,startDate);
  
   if(prev_calculated==0){
      // 開始日付のバー番号から計算を開始する
      for(int i=1; i<startBar+AFnum; i++){
         // numから過去に3本分取得
         CopyOpen(_Symbol,PERIOD_CURRENT,i,3+AFnum,Open);
         CopyHigh(_Symbol,PERIOD_CURRENT,i,3+AFnum,High);
         CopyLow(_Symbol,PERIOD_CURRENT,i,3+AFnum,Low);
         CopyClose(_Symbol,PERIOD_CURRENT,i,3+AFnum,Close);
         CopyTime(_Symbol,PERIOD_CURRENT,i,3+AFnum,Time);
         
              
         MqlDateTime dt;
         TimeToStruct(Time[0+AFnum],dt);

         //Print(HLCondition("High",i));
         // 陽線 → 陽線 → 陰線
         if(HLCondition("Low",i+1+AFnum)==true && Open[0+AFnum]>Close[0]){
            makeArray(int(Time[0+AFnum]), Time[0+AFnum], High[0+AFnum], 161, clrAqua, ANCHOR_BOTTOM);
            lowWin++;
         }else if(HLCondition("Low",i+1+AFnum)==true && Open[0+AFnum]<Close[0]){
            makeArray(int(Time[0+AFnum]), Time[0+AFnum], High[0+AFnum], 174, clrRed, ANCHOR_BOTTOM);
            lowLost++;
         }
         
         // 陰線 → 陰線 → 陽線
         if(HLCondition("High",i+1+AFnum)==true && Open[0+AFnum]<Close[0]){
            makeArray(int(Time[0+AFnum]), Time[0+AFnum], Low[0+AFnum], 161, clrAqua, ANCHOR_TOP);
            highWin++;
         }else if(HLCondition("High",i+1+AFnum)==true && Open[0+AFnum]>Close[0]){
            makeArray(int(Time[0+AFnum]), Time[0+AFnum], Low[0+AFnum], 174, clrRed, ANCHOR_TOP);
            highLost++;
         }
      }
   }else{ 
      // 現在足から過去に3本分取得
      CopyOpen(_Symbol,PERIOD_CURRENT,0,3+AFnum,Open);
      CopyHigh(_Symbol,PERIOD_CURRENT,0,3+AFnum,High);
      CopyLow(_Symbol,PERIOD_CURRENT,0,3+AFnum,Low);
      CopyClose(_Symbol,PERIOD_CURRENT,0,3+AFnum,Close);
      CopyTime(_Symbol,PERIOD_CURRENT,0,3+AFnum,Time);
      
      // 勝率計算に必要な値を減算
      if(ObjectFind(0,"BoTest_"+string(int(Time[0+AFnum])))==0){
         if(ObjectGetInteger(0,"BoTest_"+string(int(Time[0+AFnum])),OBJPROP_ARROWCODE)==161 && 
            ObjectGetInteger(0,"BoTest_"+string(int(Time[0+AFnum])),OBJPROP_ANCHOR)==ANCHOR_BOTTOM){
            lowWin--;
         }else if(ObjectGetInteger(0,"BoTest_"+string(int(Time[0+AFnum])),OBJPROP_ARROWCODE)==174 && 
            ObjectGetInteger(0,"BoTest_"+string(int(Time[0+AFnum])),OBJPROP_ANCHOR)==ANCHOR_BOTTOM){
            lowLost--;
         }
         if(ObjectGetInteger(0,"BoTest_"+string(int(Time[0+AFnum])),OBJPROP_ARROWCODE)==161 && 
            ObjectGetInteger(0,"BoTest_"+string(int(Time[0+AFnum])),OBJPROP_ANCHOR)==ANCHOR_TOP){
            highWin--;
         }else if(ObjectGetInteger(0,"BoTest_"+string(int(Time[0+AFnum])),OBJPROP_ARROWCODE)==174 && 
            ObjectGetInteger(0,"BoTest_"+string(int(Time[0+AFnum])),OBJPROP_ANCHOR)==ANCHOR_TOP){
            highLost--;
         }
         
         // すでにオブジェクトがあった場合は、削除
         ObjectDelete(0,"BoTest_"+string(int(Time[0+AFnum])));
      }
   
      // 最新のバーの動きに合わせて、結果変更
      // 陽線 → 陽線 → 陰線
      if(HLCondition("Low",1+AFnum)==true && Open[0+AFnum]>Close[0]){
         makeArray(int(Time[0+AFnum]), Time[0+AFnum], High[0+AFnum], 161, clrAqua, ANCHOR_BOTTOM);
         lowWin++;
      }else if(HLCondition("Low",1+AFnum)==true && Open[0+AFnum]<Close[0]){
         makeArray(int(Time[0+AFnum]), Time[0+AFnum], High[0+AFnum], 174, clrRed, ANCHOR_BOTTOM);
         lowLost++;
      }
      
      // 陰線 → 陰線 → 陽線
      if(HLCondition("High",1+AFnum)==true && Open[0+AFnum]<Close[0]){
         makeArray(int(Time[0+AFnum]), Time[0+AFnum], Low[0+AFnum], 161, clrAqua, ANCHOR_TOP);
         highWin++;
      }else if(HLCondition("High",1+AFnum)==true && Open[0+AFnum]>Close[0]){
         makeArray(int(Time[0+AFnum]), Time[0+AFnum], Low[0+AFnum], 174, clrRed, ANCHOR_TOP);
         highLost++;
      }
      
      
      // 予告サインの表示
      // 陽線 → 陽線 なら点灯
      if(HLCondition("Low",0)==true){
         ObjectSetString(0,"BoTest_Signal",OBJPROP_TEXT,"Low");
         ObjectSetInteger(0,"BoTest_Box",OBJPROP_BGCOLOR,clrRed);
      // 陰線 → 陰線 なら点灯
      }else if(HLCondition("High",0)==true){
         ObjectSetString(0,"BoTest_Signal",OBJPROP_TEXT,"High");
         ObjectSetInteger(0,"BoTest_Box",OBJPROP_BGCOLOR,clrGreen);
      // それ以外はなし
      }else{
         ObjectSetString(0,"BoTest_Signal",OBJPROP_TEXT,"-");
         ObjectSetInteger(0,"BoTest_Box",OBJPROP_BGCOLOR,clrNONE);
      }     
   }

   
   showComment();
   return(rates_total);
}

// サインオブジェクト表示の関数
void makeArray(int barnum, datetime time, double price, int code, color clr, int anch)
{
   ObjectCreate(0,"BoTest_"+string(barnum), OBJ_ARROW, 0, time, price);
   ObjectSetInteger(0, "BoTest_"+string(barnum), OBJPROP_ARROWCODE, code);
   ObjectSetInteger(0, "BoTest_"+string(barnum), OBJPROP_COLOR, clr);
   ObjectSetInteger(0, "BoTest_"+string(barnum), OBJPROP_ANCHOR, anch);
}

// 勝率計算表示の関数
void showComment()
{ 
   string HW = DoubleToString( 100* highWin / (highWin+highLost), 1);
   string LW = DoubleToString( 100* lowWin / (lowWin+lowLost), 1);
   string TW = DoubleToString( 100* (highWin+lowWin) / (highWin+highLost+lowWin+lowLost), 1);
   string Hsum = DoubleToString( highWin+highLost, 0);
   string Lsum = DoubleToString( lowWin+lowLost, 0);
   string Tsum = DoubleToString( highWin+highLost+lowWin+lowLost, 0);


   Comment( "│ High Win : ", HW, " %  ( " +Hsum+ " ) |\n",
            "| Low  Win : ", LW, " %  ( " +Lsum+ " ) |\n",
            "| Total Win : ", TW, " %  ( " +Tsum+ " ) |\n");
}

// タイマー処理関数
void OnTimer()
{
   int now = TimeCurrent();
   int PerInt = PeriodToInteger()*60;
   int amari = PerInt-now%PerInt;
   int rhour = amari>3600 ? amari/3600 : 0;
   int rmin = amari-rhour*3600>60 ? (amari-rhour*3600)/60 : 0;
   int rsec = amari-3600*rhour-60*rmin!=60 ? amari-3600*rhour-60*rmin : 0;

   string txt = StringFormat("%02d:%02d:%02d", rhour, rmin, rsec);
   ObjectSetString(0,"BoTest_Timer",OBJPROP_TEXT,txt);
}

// ラベルオブジェクト作成用関数
void setLabel(string name, color clr, int corner, int x, int y, string txt, int FontSize)
{
   ObjectCreate(0,name,OBJ_LABEL,0,0,0);
   ObjectSetInteger(0,name,OBJPROP_FONTSIZE,FontSize);
   ObjectSetInteger(0,name,OBJPROP_COLOR,clr);
   ObjectSetInteger(0,name,OBJPROP_CORNER,corner);
   ObjectSetInteger(0,name,OBJPROP_XDISTANCE,x);
   ObjectSetInteger(0,name,OBJPROP_YDISTANCE,y);
   ObjectSetString(0,name,OBJPROP_TEXT,txt);
   ObjectSetInteger(0,name,OBJPROP_BACK,false);
}

// シグナルアラート表示用 カラーブロック関数
void setBox(string name, color clr, int corner, int xdist, int ydist, int xsize, int ysize)
{
   ObjectCreate(0,name,OBJ_RECTANGLE_LABEL,0,0,0);
   ObjectSetInteger(0,name,OBJPROP_XDISTANCE,xdist);
   ObjectSetInteger(0,name,OBJPROP_YDISTANCE,ydist);
   ObjectSetInteger(0,name,OBJPROP_XSIZE,xsize);
   ObjectSetInteger(0,name,OBJPROP_YSIZE,ysize);
   ObjectSetInteger(0,name,OBJPROP_BGCOLOR,clr);
   ObjectSetInteger(0,name,OBJPROP_CORNER,corner);
   ObjectSetInteger(0,name,OBJPROP_BACK,true);
}

// Period変換関数
int PeriodToInteger()
{
   int PerInt = _Period;
   if(_Period==16385)  PerInt=60;
   if(_Period==16386)  PerInt=120;
   if(_Period==16387)  PerInt=180;
   if(_Period==16388)  PerInt=240;
   if(_Period==16390)  PerInt=360;
   if(_Period==16392)  PerInt=480;
   if(_Period==16396)  PerInt=720;
   if(_Period==16408)  PerInt=1440;
   if(_Period==32769)  PerInt=10080;
   if(_Period==49153)  PerInt=43200;
   return (PerInt) ;
}


前回と変更した部分は、HLConditionとして作成した条件関数の中身だけです。今後、条件を変更する場合にも、この関数内を変更するだけで無限に作成可能です。


長期検証結果と本日の検証結果

2020年1月1日から2024年12月31日までを検証した結果


ドル円5分足 2024年5月2日


54.7%の勝率

54.7%なのでまずまずですね。

ここからさらに条件を付けていくことで、57%以上までなれば十分ハイローオーストラリアで実用できそうですね。


2024年5月現在、ハイローオーストラリアにおけるドル円5分足のペイアウト率は1.87%です。

1÷1.87=0.534

つまり53.4%以上の勝率であれば、長期的にプラスにすることは可能です。


次回は、条件部分を変更可能なオブジェクトをチャート上に表示し、サイン位置や勝率の変化を可視化していく予定です。

例えば、今回のRSIを40と60ではなく、35と65で検証するとか、30と70で検証する時に便利です。いちいちパラメータウィンドウを開くのって面倒ですよね。そこで、チャート上にスライドバーみたいなものを設置して、動かすだけでパラメータ値を変更し、チャートに反映するようにします。

こうすることで、最適な条件を簡単に見つけることができますよね。

では、今日はこのあたりでお疲れ様です。


ここまで読んでいただき、ありがとうございます。

こんな感じのサインツール欲しいみたいなんがあれば、XにDMください。

https://twitter.com/highlow_oyaji


この記事が気に入ったらサポートをしてみませんか?