見出し画像

ハイロー用サインツールを作ってみた④ シグナル表示の実装

バイナリーオプションで使うためのサインツールを作っていく第4弾です。

今回は、サインの表示部分を実装していきます。

前回までの記事はこちらから。


これまでの振り返り

前回までに作成した内容は、過去のチャートに対して、ある条件でHighもしくはLowにエントリーした場合に、成功するか失敗するかの確率を求めることまでが含まれています。

つまり、バックテストにおける勝率計算部分になります。

陽線のローソク足が2本続いたら、次が陰線になる確率はどれくらいかな?みたいなことを調べることができます。


しかし、実際のトレードでは、エントリーするかもの予告演出が無いと準備ができません。YouTubeみながらボーっとしているとき、急にエントリーですって言われても対応できないですよね。

しかも、時間切り替わりでのエントリーならば、少しの遅れが命取りになり、過去検証通りの結果が得られないかもしれません。

そこで、今回実装する予告演出が必要になってきます。


本当にエントリーしたいローソク足の一本前の足で判定させ、次の足でエントリーしても良さそうならHighかLowを表示します。


残り時間表示の下に予告シグナルを表示


コード内容

#property indicator_chart_window

input datetime startDate = D'2024/4/15 0:00';      // 計算する期間の開始日付

double highWin,highLost,lowWin,lowLost;

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);
   
   
   return(INIT_SUCCEEDED);
}

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


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=rates_total-startBar; i<rates_total; i++){
         MqlDateTime dt;
         TimeToStruct(time[i],dt);
         // 陽線 → 陽線 → 陰線
         if(open[i-2]<close[i-2] && open[i-1]<close[i-1] && open[i]>close[i]){
            makeArray(int(time[i]), time[i], high[i], 161, clrAqua, ANCHOR_BOTTOM);
            lowWin++;
         }else if(open[i-2]<close[i-2] && open[i-1]<close[i-1] && open[i]<close[i]){
            makeArray(int(time[i]), time[i], high[i], 174, clrRed, ANCHOR_BOTTOM);
            lowLost++;
         }
         
         // 陰線 → 陰線 → 陽線
         if(open[i-2]>close[i-2] && open[i-1]>close[i-1] && open[i]<close[i]){
            makeArray(int(time[i]), time[i], low[i], 161, clrAqua, ANCHOR_TOP);
            highWin++;
         }else if(open[i-2]>close[i-2] && open[i-1]>close[i-1] && open[i]>close[i]){
            makeArray(int(time[i]), time[i], low[i], 174, clrRed, ANCHOR_TOP);
            highLost++;
         }
      }
   }else{
      // 最新のバー番号
      int lastBar = rates_total-1;
      
      // 勝率計算に必要な値を減算
      if(ObjectFind(0,"BoTest_"+string(int(time[lastBar])))==0){
         if(ObjectGetInteger(0,"BoTest_"+string(int(time[lastBar])),OBJPROP_ARROWCODE)==161 && 
            ObjectGetInteger(0,"BoTest_"+string(int(time[lastBar])),OBJPROP_ANCHOR)==ANCHOR_BOTTOM){
            lowWin--;
         }else if(ObjectGetInteger(0,"BoTest_"+string(int(time[lastBar])),OBJPROP_ARROWCODE)==174 && 
            ObjectGetInteger(0,"BoTest_"+string(int(time[lastBar])),OBJPROP_ANCHOR)==ANCHOR_BOTTOM){
            lowLost--;
         }
         if(ObjectGetInteger(0,"BoTest_"+string(int(time[lastBar])),OBJPROP_ARROWCODE)==161 && 
            ObjectGetInteger(0,"BoTest_"+string(int(time[lastBar])),OBJPROP_ANCHOR)==ANCHOR_TOP){
            highWin--;
         }else if(ObjectGetInteger(0,"BoTest_"+string(int(time[lastBar])),OBJPROP_ARROWCODE)==174 && 
            ObjectGetInteger(0,"BoTest_"+string(int(time[lastBar])),OBJPROP_ANCHOR)==ANCHOR_TOP){
            highLost--;
         }
         
         // すでにオブジェクトがあった場合は、削除
         ObjectDelete(0,"BoTest_"+string(int(time[lastBar])));
      }
   
      // 最新のバーの動きに合わせて、結果変更
      // 陽線 → 陽線 → 陰線
      if(open[lastBar-2]<close[lastBar-2] && open[lastBar-1]<close[lastBar-1] && open[lastBar]>close[lastBar]){
         makeArray(int(time[lastBar]), time[lastBar], high[lastBar], 161, clrAqua, ANCHOR_BOTTOM);
         lowWin++;
      }else if(open[lastBar-2]<close[lastBar-2] && open[lastBar-1]<close[lastBar-1] && open[lastBar]<close[lastBar]){
         makeArray(int(time[lastBar]), time[lastBar], high[lastBar], 174, clrRed, ANCHOR_BOTTOM);
         lowLost++;
      }
      
      // 陰線 → 陰線 → 陽線
      if(open[lastBar-2]>close[lastBar-2] && open[lastBar-1]>close[lastBar-1] && open[lastBar]<close[lastBar]){
         makeArray(int(time[lastBar]), time[lastBar], low[lastBar], 161, clrAqua, ANCHOR_TOP);
         highWin++;
      }else if(open[lastBar-2]>close[lastBar-2] && open[lastBar-1]>close[lastBar-1] && open[lastBar]>close[lastBar]){
         makeArray(int(time[lastBar]), time[lastBar], low[lastBar], 174, clrRed, ANCHOR_TOP);
         highLost++;
      }
      
      
      // 予告サインの表示
      // 陽線 → 陽線 なら点灯
      if(open[lastBar-1]<close[lastBar-1] && open[lastBar]<close[lastBar]){
         ObjectSetString(0,"BoTest_Signal",OBJPROP_TEXT,"Low");
         ObjectSetInteger(0,"BoTest_Box",OBJPROP_BGCOLOR,clrRed);
      // 陰線 → 陰線 なら点灯
      }else if(open[lastBar-1]>close[lastBar-1] && open[lastBar]>close[lastBar]){
         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",
            "| TotalWin : ", 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);
}

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) ;
}

新たにsetBoxって関数を作って、Highなら緑の四角、Lowなら赤の四角が表示されるようにしています。


      // 予告サインの表示
      // 陽線 → 陽線 なら点灯
      if(open[lastBar-1]<close[lastBar-1] && open[lastBar]<close[lastBar]){
         ObjectSetString(0,"BoTest_Signal",OBJPROP_TEXT,"Low");
         ObjectSetInteger(0,"BoTest_Box",OBJPROP_BGCOLOR,clrRed);
      // 陰線 → 陰線 なら点灯
      }else if(open[lastBar-1]>close[lastBar-1] && open[lastBar]>close[lastBar]){
         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);
      }     

予告サインの表示って形で作成しています。

今回のサンプルは陽線→陽線ならLowサイン、陰線→陰線ならHighサインが出るって単純な条件しか設定していません。

今後、この条件部分だけをいじるだけでサインツールが作れるようなものを作っていこうと考えています。


ちなみに、陰線→陰線の次が陽線になる確率と、陽線→陽線の次が陰線になる確率は以下の通りです。


確率

4年分のローソク足で検証したのですが、BTCUSD以外は、なかなか渋い結果ですね。


条件を増やせば確率は上がりますが、エントリー回数が減少します。

1日に10回前後のエントリー回数、月に200回くらい、年間2000回くらいで勝率57%以上あれば、十分な条件だと思います。

私自身運用しているのが、それくらいの条件なので。

ってことで、今日はここまで。

次回は、ロジック部分をもう少し綺麗にして、インジケータを入れた場合などの検証結果も紹介していきますね。

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

お疲れさまでした。

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

https://twitter.com/highlow_oyaji

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