見出し画像

ハイロー用サインツールを作ってみた⑤ HighLow条件の関数化処理

あぁ、ビットコインが下げてますね。。。

先月のハイロー収支ですが、もう少しで100でしたが、昨日30くらい負けたので、70くらいで終わりました。統計下振れだと思って受け入れます。悲しい。。。

そういえば、ハイローオーストラリアのキャッシュバックが今月も5万円ありました。毎月ありがたいですね。

この5万円ですが、Exportした取引履歴csvにはキャッシュバックとして表示されないですよね。純粋に『購入』と『ペイアウト』しか表示されてないので、取引履歴と口座残高に乖離があるy、、、まぁいいや。

ってことで、昨晩は致命傷で済んだので、今日も続けてサインツール作っていこうと思います。


これまでの内容は、下記をご覧ください。


前回のnoteでは、次の時間切り替わりでエントリーって、予告エントリーシグナルを表示するところまで作りました。

今回は、バックテスト部分とサイン部分で重複してごちゃごちゃしていた、エントリー判定の条件部分を関数化して、コードをすっきりさせていこうと思います。

条件を変更する場合、何か所も変えないとダメなコードより、一か所を変えるだけでいいコードの方が管理もしやすいですしね。

今後、条件を複雑にするにしても、シグナル判定を行う関数部分を変えるだけで良いので手間も減ります。


また、追加で、判定足の本数も変更できるようにしました。

1本後のローソク足の終値判定だけでなく、2本後のローソク足の終値判定が知りたいなどの時に使えます。

5分足のチャートを表示しながら、15分後の判定なら3本後みたいな感じです。1分足で3分後とか、5分後とかもできます。


コード内容

#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 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);
   
   // ローソク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);
   
   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);
}


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


これまでOnCalculateで引数として設定されたopen[]の形を使っていましたが、別関数での汎用性を考え、CopyOpenを使用して新たなOpen[]って配列を作成しました。

openは過去から未来へ数が増えていきます。open[0]が一番古い。
逆にOpen[]の場合、ArraySetAsSeries(Open,true)とすることで、Open[0]が現在足になります。

また、AFnumで判定足の本数を変更できるようにしました。


何本先までで、1なら1本で判定


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

次回は、条件部分にインジケータ等を入れたコードを作成していきます。

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

お疲れさまでした。

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

https://twitter.com/highlow_oyaji

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