今回はACオシレーターを調べてみる。
環境
- OANDA MetaTrader 5
- Version: 5.00 build 3211
ストラテジーテスターの設定
- 銘柄:USDJPY
- 足の種類:Daily
- 日付:期間限定
- 開始日:2012.01.01
- 終了日:2022.01.01
- フォワードテスト:キャンセル
- 延滞:遅延ゼロ、理想的な実行
- モデル:1分足 OHLC
- より高速計算のためのピップ単位利益:チェックあり
- 入金:100000
- レバレッジ:1:25
- オプティマイズ:無効化
- チャート、指標、取引を表示するビジュアルモード:チェックなし
順張り戦略
エントリー
エグジット
検証結果
- 損益:-3067
- 取引数:389
- プロフィットファクター:0.77
- 期待利得:-7.89
- リカバリーファクター:-0.94
結果は全然だめ。
逆張り戦略
エントリー
エグジット
検証結果
- 損益:-142
- 取引数:201
- プロフィットファクター:0.98
- 期待利得:-0.71
- リカバリーファクター:-0.07
結果はいまいち。
サンプルコード
(警告)バックテスト用のサンプルコードなので、リアルトレードに使用しないこと。
//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; } } } }