ACオシレーターの検証

今回はACオシレーターを調べてみる。

環境

  • OANDA MetaTrader 5
  • Version: 5.00 build 3211

ストラテジーテスターの設定

  • 銘柄:USDJPY
  • 足の種類:Daily
  • 日付:期間限定
  • 開始日:2012.01.01
  • 終了日:2022.01.01
  • フォワードテスト:キャンセル
  • 延滞:遅延ゼロ、理想的な実行
  • モデル:1分足 OHLC
  • より高速計算のためのピップ単位利益:チェックあり
  • 入金:100000
  • レバレッジ:1:25
  • オプティマイズ:無効化
  • チャート、指標、取引を表示するビジュアルモード:チェックなし

順張り戦略

エントリー

エグジット

検証結果

f:id:fxst24:20220219214911p:plain

結果は全然だめ。

逆張り戦略

エントリー

エグジット

検証結果

f:id:fxst24:20220219214940p:plain

結果はいまいち。

サンプルコード

(警告)バックテスト用のサンプルコードなので、リアルトレードに使用しないこと。

//2022年2月19日に更新
//ライブラリー
#include <Trade\Trade.mqh>
//Input変数
input int TradingStrategy=1;//トレード戦略のナンバー
input double Lot=0.1;//ロット数
input double MaxAcceptableLoss=1.0;//許容可能な最大損失(総資産を1とした場合の割合)
//オンティック関数
void OnTick(void)
{
  //トレードに使う変数
   string id="AC_"+Symbol();//注文のID
   bool buy_entry=false;//買いエントリー
   bool sell_entry=false;//売りエントリー
   bool buy_exit=false;//買いエグジット
   bool sell_exit=false;//売りエグジット
   double sl=0.0;//損切り価格。使用しないときは0.0
   double tp=0.0;//利食い価格。使用しないときは0.0
   //順張り戦略
   if(TradingStrategy==1)
   {
      int handle=iAC(NULL,0);//テクニカル指標のハンドル
      double ac[10];//テクニカル指標を格納する配列
      CopyBuffer(handle,0,0,10,ac);//テクニカル指標を配列に格納
      ArrayReverse(ac,0,10);//テクニカル指標を逆に並べ替え
      //売買ルール
      buy_entry=ac[1]>ac[2] && ac[2]>ac[3] && ac[2]>0.0;
      sell_entry=ac[1]<ac[2] && ac[2]<ac[3] && ac[2]<0.0;
      buy_exit=ac[1]<ac[2];
      sell_exit=ac[1]>ac[2];
   }
   //逆張り戦略
   if(TradingStrategy==2)
   {
      int handle=iAC(NULL,0);//テクニカル指標のハンドル
      double ac[10];//テクニカル指標を格納する配列
      CopyBuffer(handle,0,0,10,ac);//テクニカル指標を配列に格納
      ArrayReverse(ac,0,10);//テクニカル指標を逆に並べ替え
      //売買ルール
      buy_entry=ac[1]>ac[2] && ac[2]>ac[3] && ac[3]>ac[4] && ac[1]<0.0;
      sell_entry=ac[1]<ac[2] && ac[2]<ac[3] && ac[3]<ac[4] && ac[1]>0.0;
      buy_exit=ac[1]<ac[2];
      sell_exit=ac[1]>ac[2];
   }
   //市場が開いていればエントリー、エグジットを行う
   if(IsMarketOpen())
   {
      CTrade trade;//取引関数にアクセスするためのクラス
      //買いエグジット成立、または許容最大損失を超えたら買いポジションを決済
      if(buy_exit || MaxIntradayLoss(MaxAcceptableLoss))
      {
         StrategyExit(trade,POSITION_TYPE_BUY,id);
      }
      //売りエグジット成立、または許容最大損失を超えたら売りポジションを決済
      if(sell_exit || MaxIntradayLoss(MaxAcceptableLoss))
      {
         StrategyExit(trade,POSITION_TYPE_SELL,id);
      }
      //買いエントリー成立、かつ許容最大損失を超えていなければ買いポジションを保有
      if(buy_entry && MaxIntradayLoss(MaxAcceptableLoss)==false)
      {
         StrategyEntry(trade,ORDER_TYPE_BUY,Lot,sl,tp,id);
      }
      //売りエントリー成立、かつ許容最大損失を超えていなければ売りポジションを保有
      if(sell_entry && MaxIntradayLoss(MaxAcceptableLoss)==false)
      {
         StrategyEntry(trade,ORDER_TYPE_SELL,Lot,sl,tp,id);
      }
   }
}
//ここから下はおまじない

//許容可能な最大日中損失(含み損益込み)を超えていないかチェック
bool MaxIntradayLoss(double max_acceptable_loss)
{
   static double equity_at_open=0.0;//1日の開始時の資産
   double equity_now;//現在の資産
   double pl;//損益
   MqlDateTime mdt;//日付時刻の構造体
   TimeCurrent(mdt);//現在の日付時刻を格納
   //1日の開始時の資産を格納
   if((mdt.hour==0 && mdt.min==0) || equity_at_open==0.0)
   {
      equity_at_open=AccountInfoDouble(ACCOUNT_EQUITY);
   }
   equity_now=AccountInfoDouble(ACCOUNT_EQUITY);//現在の資産を格納
   //損益を計算
   if(MathAbs(equity_at_open)>0.0)
   {
      pl=(equity_now-equity_at_open)/equity_at_open;
   }
   else
   {
      pl=0.0;
   }
   //損失が許容可能な最大日中損失を超えていたら真を返す
   if(pl<-max_acceptable_loss)
   {
      return true;
   }
   //超えていなければ偽を返す
   else
   {
      return false;
   }
}
//市場が開いているかをチェック
//OANDAサーバー用の設定であり、他のサーバーでは不要または要修正
bool IsMarketOpen(void)
{
   MqlDateTime mdt;//日付時刻の構造体
   TimeCurrent(mdt);//現在の日付時刻を格納
   //日曜日は開いていない
   if(mdt.day_of_week==0)
   {
      return false;
   }
   //月曜日は0:03より前、23:55以降は開いていない
   else if(mdt.day_of_week==1 && ((mdt.hour==0 && mdt.min<3) || (mdt.hour==23 && mdt.min>=55)))
   {
      return false;
   }
   //火曜日から金曜日は0:10より前、23:55以降は開いていない
   else if(mdt.day_of_week>=2 && mdt.day_of_week<6 && ((mdt.hour==0 && mdt.min<10) || (mdt.hour==23 && mdt.min>=55)))
   {
      return false;
   }
   //土曜日は開いていない
   else if(mdt.day_of_week==6)
   {
      return false;
   }
   //それ以外は開いている
   else
   {
      return true;
   }
}
//新規注文を送信
void StrategyEntry(CTrade &trade,
                   ENUM_ORDER_TYPE type,
                   double lot,
                   double sl,
                   double tp,
                   string id)
{
   trade.SetTypeFillingBySymbol(Symbol());//なくても問題なさそうだが一応
   bool is_position_open=false;//ポジションを持っているかどうかチェック
   uint total=PositionsTotal();//口座のポジション数を求める。
   //銘柄とIDが一致するポジションを探す
   for(uint i=0;i<total;i++)
   {
      string position_symbol=PositionGetSymbol(i);
      //見つかればポジションがあると判断
      if(Symbol()==position_symbol && id==PositionGetString(POSITION_COMMENT))
      {
         is_position_open=true;
         break;
      }
   }
   //ポジションがなければ新規注文を送信
   if(is_position_open==false){
      double ask=SymbolInfoDouble(Symbol(),SYMBOL_ASK);//買値
      double bid=SymbolInfoDouble(Symbol(),SYMBOL_BID);//売値
      double price=0.0;
      //買うときは買値で注文
      if(type==ORDER_TYPE_BUY)
      {
         price=ask;
      }
      //売るときは売値で注文
      if(type==ORDER_TYPE_SELL)
      {
         price=bid;
      }
      trade.PositionOpen(Symbol(),type,lot,price,sl,tp,id);}
}
//決済注文を送信
void StrategyExit(CTrade &trade,
                  long type,
                  string id)
{
   trade.SetTypeFillingBySymbol(Symbol());//なくても問題なさそうだが一応
   uint total=PositionsTotal();//口座のポジション数を求める
   //銘柄とIDが一致するポジションを探す
   for(uint i=0;i<total;i++)
   {
      string position_symbol=PositionGetSymbol(i);
      if(Symbol()==position_symbol && id==PositionGetString(POSITION_COMMENT))
      {
         long ticket=PositionGetInteger(POSITION_TICKET);//ポジションのチケット
         //ポジションのタイプが一致したら決済する
         if(PositionGetInteger(POSITION_TYPE)==type)
         {
            trade.PositionClose(ticket);
            break;
         }
      }
   }
}