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として作成した条件関数の中身だけです。今後、条件を変更する場合にも、この関数内を変更するだけで無限に作成可能です。
長期検証結果と本日の検証結果
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
この記事が気に入ったらサポートをしてみませんか?