バックテストのビジュアルモードを使い負けるエントリーポイントを減らす方法を解説
前回の第12回で作成したEAでは、パラメータの最適化により精度を上げました。
精度を上げるには「勝てるエントリーポイントを増やす」ことと「負けるエントリーポイントを減らす」という二種類のアプローチがあります。
今回の第13回では、バックテストをビジュアルモードで実施し、損益がマイナスになっているポイントを分析して指標を追加することで、「負けるエントリーポイントを減らす」のを目標にします。
ビジュアルモードの実行方法
「ビジュアルモード」とは、バックテストの最中に実際のチャートを早送りしながら具体的にローソク足のどの位置でエントリーが行われたのかを確認できるモードです。
実行方法は非常に簡単で、バックテスター設定の「ビジュアルモード」にチェックを入れるだけです。
負けたポイントを分析する
では実際にビジュアルモードを実行して、負けたポイントをいくつか挙げ分析していきます。
プログラムは第12回で作成したEAを使用します。
5年間の取引回数が約1100回、勝率が約70%なので、負けた回数は約330回あります。
これらの負けポイントをすべて分析することは現実的ではないので、数か所に絞って分析を行います。
バックテストが終了すると、以下の画像のように使用しているRSIが表示されます。
今回は分析を行いやすいように、さらに期間350の移動平均線を追加しています。
では負けポイントをいくつか挙げていきます。
本EAのロジックは、RSIの買われすぎ売られすぎによる逆張りですが、上記2か所は売られすぎで反発せずそのまま損切りしている箇所です。
移動平均線と合わせて見ると、下降トレンド中で下方向の勢いが強く、買いエントリーに失敗していることが分かります。
そのため、長期の移動平均線が上方向の時のみ買いエントリー、下方向の時のみ売りエントリーするようにプログラムを書き換えます。
移動平均線の条件を追加する
それでは移動平均線の条件を追加していきましょう。
フィールド
input int MAGICMA = 23498721; // マジックナンバー
input double Lots =0.01; // 1ロット十万通貨単位
input int Slippage = 4; // エントリー見送りスリッページ
input double MaxSpread = 5; // エントリー見送りスプレッド
input double TakeProfit = 5.0; // 利益確定幅(pips)
input double LossCut = 20.0; // 損切確定幅(pips)
input int RSIPeriod=4; // 期間(RSI)
input ENUM_APPLIED_PRICE RSIAppliedPrice = PRICE_CLOSE; // 適用価格(RSI)
input int UpLine = 90; // 上の線(RSI)
input int DownLine = 20; // 下の線(RSI)
input int MAPeriod=350; // 期間(MA)
input ENUM_APPLIED_PRICE MAAppliedPrice = PRICE_CLOSE; // 適用価格(MA)
input int TradeTime = 0; // トレードを行う時間
input int NanpinWidth = 7; //ナンピンの幅(pips)
//曜日設定
input int TradeDayOfWeek = 5; // トレード曜日(1:月曜、2火曜、3:水曜、4:木曜、5:金曜)
移動平均線の適用価格と期間を指定するパラメータを追加しました。
デフォルトの期間は350に設定しています。
CheckForOpen関数
void CheckForOpen()
{
int res;
double RSI = iRSI(Symbol(), 0, RSIPeriod, RSIAppliedPrice, 1);
double MA0 = iMA(Symbol(), Period(), MAPeriod, 0, MODE_EMA, RSIAppliedPrice, 0);
double MA1 = iMA(Symbol(), Period(), MAPeriod, 0, MODE_EMA, RSIAppliedPrice, 1);
if(TradeTime == TimeHour(Time[1]))
{
if(RSI < DownLine && MA0 > MA1)
{
res=OrderSend(Symbol(), OP_BUY, Lots, Ask, Slippage, 0, 0,"", MAGICMA, 0, Red);
}
if(RSI > UpLine && MA0 < MA1)
{
res=OrderSend(Symbol(), OP_SELL, Lots, Bid, Slippage, 0, 0, "", MAGICMA, 0, Blue);
}
}
}
移動平均線の条件を追加しました。
変数MA0に現在足の移動平均線の値を、変数MA1に1つ前の確定足の移動平均線の値を格納しています。
そしてMA0がMA1よりも大きければ上昇トレンド、小さければ下降トレンドと判断し、買いエントリーは上昇トレンド中、売りエントリーは下降トレンド中のみ実行するようにします。
移動平均線追加前
移動平均線追加後
移動平均線を追加したことで、取引回数は減少しましたが、プロフィットファクターを2.39から3.02にできました。
今回の目的である「負けるエントリーポイントを減らす」のを実現できました。
注意点
今回のバックテストは、スプレッドを固定で行っています。
日本時間早朝の相場が緩やかになる時間帯は、スプレッドが広がりやすいという特徴があります。
実運用を行うにはスプレッド制限をかけるなど、十分に注意しましょう。
また、マーチンゲールは自己資金を一瞬で失ってしまうリスクがあります。
プログラム全文(最適化後)
#property copyright "Copyright(C) 2023, OANDA"
#property link "https://www.mql5.com"
#property version "1.00"
#property strict
input int MAGICMA = 23498721; // マジックナンバー
input double Lots =0.01; // 1ロット十万通貨単位
input int Slippage = 4; // エントリー見送りスリッページ
input double MaxSpread = 5; // エントリー見送りスプレッド
input double TakeProfit = 5.0; // 利益確定幅(pips)
input double LossCut = 20.0; // 損切確定幅(pips)
input int RSIPeriod=4; // 期間(RSI)
input ENUM_APPLIED_PRICE RSIAppliedPrice = PRICE_CLOSE; // 適用価格(RSI)
input int UpLine = 90; // 上の線(RSI)
input int DownLine = 20; // 下の線(RSI)
input int MAPeriod=350; // 期間(MA)
input ENUM_APPLIED_PRICE MAAppliedPrice = PRICE_CLOSE; // 適用価格(MA)
input int TradeTime = 0; // トレードを行う時間
input int NanpinWidth = 7; //ナンピンの幅(pips)
//曜日設定
input int TradeDayOfWeek = 5; // トレード曜日(1:月曜、2火曜、3:水曜、4:木曜、5:金曜)
double dSpread;
int OnInit()
{
return(INIT_SUCCEEDED);
}
void OnTick()
{
dSpread = (Ask - Bid) / (Point * 10);
if(CalculateCurrentOrders()==0 && dSpread < MaxSpread && TimeDayOfWeek(Time[0]) != TradeDayOfWeek) CheckForOpen();
if(CalculateCurrentOrders()==1 && dSpread < MaxSpread) CheckForNanpin();
CheckForClose();
}
void CheckForOpen()
{
int res;
double RSI = iRSI(Symbol(), 0, RSIPeriod, RSIAppliedPrice, 1);
double MA0 = iMA(Symbol(), Period(), MAPeriod, 0, MODE_EMA, RSIAppliedPrice, 0);
double MA1 = iMA(Symbol(), Period(), MAPeriod, 0, MODE_EMA, RSIAppliedPrice, 1);
if(TradeTime == TimeHour(Time[1]))
{
if(RSI < DownLine && MA0 > MA1)
{
res=OrderSend(Symbol(), OP_BUY, Lots, Ask, Slippage, 0, 0,"", MAGICMA, 0, Red);
}
if(RSI > UpLine && MA0 < MA1)
{
res=OrderSend(Symbol(), OP_SELL, Lots, Bid, Slippage, 0, 0, "", MAGICMA, 0, Blue);
}
}
}
void CheckForNanpin()
{
int res;
double buyProfitPips = 0;
double SellProfitPips = 0;
for( int i=0; i<OrdersTotal(); i++ ){
if( OrderSelect(i, SELECT_BY_POS) == true && OrderMagicNumber() == MAGICMA && OrderSymbol() == Symbol() ){
if( OrderType() == OP_BUY ){
buyProfitPips = (MarketInfo(Symbol(),MODE_BID) - OrderOpenPrice()) / (Point * 10);
break;
}else if( OrderType() == OP_SELL ){
SellProfitPips = (OrderOpenPrice() - MarketInfo(Symbol(),MODE_ASK)) / (Point * 10);
break;
}
}
}
if(SellProfitPips == 0 && buyProfitPips < NanpinWidth * -1)
{
res=OrderSend(Symbol(), OP_BUY, Lots, Ask, Slippage, 0, 0,"", MAGICMA, 0, Red);
}
if(buyProfitPips == 0 && SellProfitPips < NanpinWidth * -1)
{
res=OrderSend(Symbol(), OP_SELL, Lots, Bid, Slippage, 0, 0, "", MAGICMA, 0, Blue);
}
}
void CheckForClose(){
int res;
double buyProfitPips = 0;
double SellProfitPips = 0;
for( int i=0; i<OrdersTotal(); i++ ){
if( OrderSelect(i, SELECT_BY_POS) == true && OrderMagicNumber() == MAGICMA && OrderSymbol() == Symbol() ){
if( OrderType() == OP_BUY ){
buyProfitPips = (MarketInfo(Symbol(),MODE_BID) - OrderOpenPrice()) / (Point * 10);
}else if( OrderType() == OP_SELL ){
SellProfitPips = (OrderOpenPrice() - MarketInfo(Symbol(),MODE_ASK)) / (Point * 10);
}
}
}
if(TakeProfit <= buyProfitPips || LossCut <= buyProfitPips * -1){
for( int i=OrdersTotal()-1; i>=0; i-- ){
if( OrderSelect(i, SELECT_BY_POS) == true ){
if( OrderType() == OP_BUY && OrderMagicNumber() == MAGICMA && OrderSymbol() == Symbol() ){
res = OrderClose(OrderTicket(), OrderLots(), Bid, Slippage * 10,Green);
}
}
}
}
if(TakeProfit <= SellProfitPips || LossCut <= SellProfitPips * -1){
for( int i=OrdersTotal()-1; i>=0; i-- ){
if( OrderSelect(i, SELECT_BY_POS) == true ){
if( OrderType() == OP_SELL && OrderMagicNumber() == MAGICMA && OrderSymbol() == Symbol() ){
res = OrderClose(OrderTicket(),OrderLots(),Ask,Slippage * 10,Green);
}
}
}
}
}
int CalculateCurrentOrders()
{
int positions = 0;
for(int i=0;i<OrdersTotal();i++)
{
if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES)==false) break;
if(OrderSymbol()==Symbol() && OrderMagicNumber()==MAGICMA)
{
positions++;
}
}
return positions;
}
EA(自動売買)を学びたい方へオススメコンテンツ

OANDAではEA(自動売買)を稼働するプラットフォームMT4/MT5の基本的な使い方について、画像や動画付きで詳しく解説しています。MT4/MT5のインストールからEAの設定方法までを詳しく解説しているので、初心者の方でもスムーズにEA運用を始めることが可能です。またOANDAの口座をお持ちであれば、独自開発したオリジナルインジケーターを無料で利用することもできます。EA運用をお考えであれば、ぜひ口座開設をご検討ください。
本ホームページに掲載されている事項は、投資判断の参考となる情報の提供を目的としたものであり、投資の勧誘を目的としたものではありません。投資方針、投資タイミング等は、ご自身の責任において判断してください。本サービスの情報に基づいて行った取引のいかなる損失についても、当社は一切の責を負いかねますのでご了承ください。また、当社は、当該情報の正確性および完全性を保証または約束するものでなく、今後、予告なしに内容を変更または廃止する場合があります。なお、当該情報の欠落・誤謬等につきましてもその責を負いかねますのでご了承ください。