2011/10/21

前回のコメントの内容を確認しました。■■■

前回の記事で、筆者がATC2011に提出したEAのコードを掲示し、貴重なコメントを頂いたので今回は、そのコメントの精査を行いたいと思います。



はじめに

今回のEAで考慮した点の一つが、メモリの使用量の削減です。その為に、バッファを固定し再利用する方法を模索しました。以下が仕様です。ちなみにバッファ固定の取り組み方法は、こちら ですが、記載されているバグは、バージョンアップで改善されたみたい?

使用したDATE
指標名 値(time,open,hi,low,close) 平均足 DeMaker EMA×4本 一定期間の値(高値、中値、安値)
バッファ数 10 6 50 30 80

ちなみに、EAを稼働した時のメモリ増加量は、1300kBほどです。(タスクマネージャー調べ)多いのか少ないのかは、不明?ホントに削減されているのかな?CopyBuffer()と比べてどうなのか確認していません。。orz

今回、iMA()やiCustom()などの関数を使用せずにEAを作成した結果、前処理(事前計算)が必要となりました。

※前処理とは、初期状態で空のバッファに過去のデータを入れる事。

今回使用した方法は、現在のbarからバッファ分過去の値(time,open,…)を取得するために、バッファ分過去のbarのtimeを取得し、そこから値を取得する方法を取りました。以下がそのtimeの取得コードです。

+---------------------------------------------------------------------------------------------------------+
datetime Cindibox_h::Shiftime(datetime time){ 
   datetime startime;
   int rates,i=0;
   startime=time-frametime(timeframes)*60*(range-1);
   datetime getbars[];
   do{
      do{
         if(i>0)Sleep(500);//一度以上取得に失敗したら一時停止
         i++;
         rates = CopyTime(symbl,timeframes,(datetime)startime,(datetime)time,getbars);
         if(rates==range)break;
         }while(i<5);
      if(rates!=range){startime-=(datetime)frametime(timeframes)*60; i=1;}
     }while(rates < range);
return(startime);
+---------------------------------------------------------------------------------------------------------+

この関数で留意した点
1.CopyRates(),CopyTIme()などの関数は、取得に失敗することがあるので対応する。
(特に、異なるペア、異なるタイムフレームを取得する場合、失敗するとこが多かった。)
データを呼び出す前に、関数を抜けている感じがする?ので、sleep(500)を含めて5回試みるようにした。
2.必ず、バッファ数のbarが取得できるtimeを取得する。
barの抜けがある場合の処理を追加してあります。

この関数を元に以下の関数を実行し、バッファに値を取得させています。
ちなみに、Rates以外の値は、各関数内で処理させています。

+---------------------------------------------------------------------------------------------------------+
int Cindibox_h::Run(const datetime nowtime){
   MqlRates Sprice[1];
   int zero=0,befo=0,hlzero=0;
   int rates;
   datetime startime=nowtime;
   //バッファが空の時
   if(bufindex==0)startime=Shiftime(nowtime);
   do{
      rates = CopyRates(symbl,timeframes,(datetime)startime,(int)1,Sprice);
      if(rates>0){
         if(Sprice[0].time != bartimenow){
            bartimenow = Sprice[0].time;
            bufindex++;
         }
         zero=ZeroIndex(bufindex,range);
         hlzero = ZeroIndex(bufindex,HLrange);
         price[zero] = Sprice[0];
         phi[hlzero] = price[zero].high;
         plw[hlzero] = price[zero].low;
        if(MLalfa>0 && MSalfa>0 && ON_MA==true)Run_Ma(bufindex);
        if(ON_Heiken==true) Run_HeikenAshi(bufindex);
        if(ON_DeMark==true) Run_DeMarker(bufindex);
         startime=Shiftimeon(startime);                    
         befo = ZeroIndex(bufindex+1,range);
         }
    } while(price[befo].tick_volume<=0);//バッファが埋まったら脱出
   return(bufindex);
+---------------------------------------------------------------------------------------------------------+





前回のコメント

前回頂いたコメントを確認していきます。

>>datetime Cindibox_h::frametime(ENUM_TIMEFRAMES timef)にPERIOD_M6とかPERIOD_M20とかPERIOD_M12がないような..。時間の計算が人間業とは思えないトリッキーさでよくわかりませんでした..。なぜSleep()が..。この関数のdatetimeという戻り値の型も変ですよね。


そうなんです。CATCtradeクラスの各インスタンスのCATCtrade::InputdateでPERIOD_M6、PERIOD_M20、PERIOD_M12を使用しているにも関わらず、datetime Cindibox_h::frametime(ENUM_TIMEFRAMES timef)に対応するコードを記載していませんでした。以下コード

+---------------------------------------------------------------------------------------------------------+
datetime Cindibox_h::frametime(ENUM_TIMEFRAMES timef){
 switch(timef){
      case PERIOD_M1: return(1);
      case PERIOD_M5: return(5);
      case PERIOD_M10: return(10);
      case PERIOD_M15: return(15);
      case PERIOD_M30: return(30);
      case PERIOD_H1: return(60);
      case PERIOD_H4: return(240);      
      default : return(15);  
   }
}
+---------------------------------------------------------------------------------------------------------+

うん~!初期計画から変更するのを忘れてました。。orz しかし、defaultがなぜ15なの@@?
後でコードを変更しENUM_TIMEFRAMES の値をそのまま使おうと思っていましたが、忘れていました。
以下がENUM_TIMEFRAMESの値 一覧です。なぜ、H1から16385+時間になるんだろう?

PERIOD_M1 M3 M5 M6 M10 M30 H1 H2 H3 H12 D1
1 3 5 6 10 30 16385 16386 16387 16396 16408

そして、以下のコードが、修正版

+---------------------------------------------------------------------------------------------------------+
int Cindibox_h::frametime(ENUM_TIMEFRAMES timef){
   int t;
  t=timef;
  if(t>=16385)t=(t-16384)*60;
  return(t);
}
+----------------------------------------------------------------------------------------------------------+

トリッキーと評されたコードは、上記に記載しました。やっぱりこんなコードは、スタンダードではないんだろうな(・・?

datetime Cindibox_h::frametime(ENUM_TIMEFRAMES timef)
の戻り値もおかしいですね。 int型に変更して、datetime Cindibox_h::Shiftime(datetime time)の以下のコードを変更するようにした方が良いのかな?
startime=time - (datetime)frametime(timeframes)*60*(range-1);






>>if(--retry_count!=0) continue;
リトライが動作しない。
partial_closeがfalseだとwhile(partial_close);でループを抜けてしまいます。


指摘されたコードが以下のコードです。
標準ライブラリー(bool CTrade::PositionClose(const string symbol,ulong deviation))より抜粋し、余分?なところを削除したものです。

+----------------------------------------------------------------------------------------------------------+
bool CATCtrade::PositionClose(void){
   bool   partial_close=false;
   int    retry_count  =10;
   uint   retcode      =TRADE_RETCODE_REJECT;
//--- check stopped
   if(IsStopped()) return(false);
//--- variables
   string action,result;
//--- clean
   ClearStructures();
   do
     {
      //--- checking
      if(PositionSelect(symbl))
        {
         if((ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY)
           {
            //--- prepare request for close BUY position
            m_request.type =ORDER_TYPE_SELL;
            m_request.price=SymbolInfoDouble(symbl,SYMBOL_BID);
           }
         else
           {
            //--- prepare request for close SELL position
            m_request.type =ORDER_TYPE_BUY;
            m_request.price=SymbolInfoDouble(symbl,SYMBOL_ASK);
           }
        }
        else
        {
         //--- position not found
         m_result.retcode=retcode;
         return(false);
        }
      //--- setting request
      m_request.action      =TRADE_ACTION_DEAL;
      m_request.symbol      =symbl;
      m_request.deviation   =deviation;
      m_request.type_filling=ORDER_FILLING_AON;
      m_request.volume      =PositionGetDouble(POSITION_VOLUME);
      //--- check volume
      double max_volume=SymbolInfoDouble(symbl,SYMBOL_VOLUME_MAX);
      if(m_request.volume>max_volume)
        {
         m_request.volume=max_volume;
         partial_close=true;
        }
      else
         partial_close=false;
      //--- order check
      if(!OrderCheck(m_request,m_check_result))return(false);
      
      //--- order send
      if(!OrderSend(m_request,m_result)){
         if(--retry_count!=0) continue;
         return(false);
      }
        
      retcode=TRADE_RETCODE_DONE_PARTIAL;
      if(partial_close) Sleep(1000);
     }
   while(partial_close);
return(true);
}
+----------------------------------------------------------------------------------------------------------+

確認するんだった。。orz デフォルトだとループしないじゃん。勉強になりました。
ループさせる場合は、bool partial_close=true;ですね。。




>>ENUM_ORDER_TYPE CATCtrade::creattype(const int typec)
return(ORDER_TYPE_BUY)とreturn(NULL)を返していますが、どちらも0で同値です。ただ動作には影響ないです。
ENUM_ORDER_TYPEにORDER_TYPE_FLATを作成しなかったMetaQuotes社の仕様決めのいい加減さが原因だと思います。
ポジションの状態を調べるのにPositionSelect()とPositionGetInteger()の2つの関数の組み合わせが必要になりますし。


まず、対象コードの確認
+----------------------------------------------------------------------------------------------------------+
ENUM_ORDER_TYPE CATCtrade::creattype(const int typec){
   switch(typec){
      case  2: return(ORDER_TYPE_BUY);
      case -2: return(ORDER_TYPE_SELL);
      default: return(NULL);
   }
} 
+----------------------------------------------------------------------------------------------------------+

そして、ENUM_ORDER_TYPEの値の確認
_BUY _SELL _BUY_LIMIT _SELL_LIMIT _BUY_STOP _SELL_STOP _BUY_STOP_LIMIT _SELL_STOP_LIMIT
0 1 2 3 4 5 6 7

あらら、指摘の通り_BUYの値が0でした。
さて、どうするか?int型?char型の方が良いかな?にしてdefalt値に-1を入れる手段をとるか?選択に迷います。
それにしても、TYPE_FLATがほしいですね。



>>deviation =10;1pipsは狭すぎではないですか?
とほほです。デフォルトのコードを確認せずに使っちゃダメですね。反省します。




まとめ

情けないほどのバグが発見されました。ご指摘くださった@ahaha_fxtraderさんと匿名さんありがとうございました。
今後に生かしたいと思います。