2011/11/23

ボリンジャーバンドを作ってみました。■■■

ATC参戦用のEAを作るのに時間を取られ、後回しにしてきたことをコツコツと行っています。その中の一つがRの使い方の習得でした。今回は、Rの学習の一環としてボリンジャーバンドもどきを作ってみました。

ヒストグラムの作成

今回は、MQL4のスクリプトでUSDJPYH1をヒストグラム化してみました。Rの勉強になってない。。
以下がコードです。
//+------------------------------------------------------------------+
//|                                                    Copyprice.mq4 |
//|                                        Copyright ゥ 2009, bighope |
//|                       http://expertadviser-bighope.blogspot.com/ |
//+------------------------------------------------------------------+
#property copyright "Copyright ゥ 2009, bighope"
#property link      "http://expertadviser-bighope.blogspot.com/"
#property show_inputs
extern string Filename  = "USDJPYH1"; 

int bufindex(){
   int hiindex,lowindex,sd;
   double st;
   hiindex  = iHighest(NULL,0,MODE_HIGH);
   lowindex = iLowest(NULL,0,MODE_LOW);
   st = High[hiindex] - Low[lowindex];
   sd = st/Point*0.1;
   return(sd);
}

//+------------------------------------------------------------------+
//| script program start function                                    |
//+------------------------------------------------------------------+
int start()
  {
//----
   int handle,index;
   bool ret;
   int HL;
   double Ds[];
   index = bufindex();
   
   ArrayResize(Ds,index);
   handle=FileOpen(Filename+".csv",FILE_CSV|FILE_WRITE,',');
   if(handle<1) return(false);
  
   for(int i=0;i<Bars;i++){
      HL=MathRound((High[i]-Low[i])*MathPow(10,Digits)) ;
      Ds[HL]++;
   }

   for(i=0;i<index;i++){
      ret = FileWrite(handle,i,Ds[i]/Bars);
      if(ret==false)return(0);
   }
   FileClose(handle);
   Comment("Get Data OK");
//----
   return(0);
  }
//+------------------------------------------------------------------+

スクリプトを作動させると、・・・//experts/filesにCSVファイルが作られます。
※これをRに読み取らせる訳ですが、Rのアイコンを右クリック→プロパティを選択し、作業フォルダーのアドレスを変更しておくと、ひと手間省けて便利です。
syo-to
Rを起動し以下のコードでヒストグラムが表示されます。
#データの読み込み(リスト形式)
 d<-read.csv("UJH1.csv") #データの代入
 x<-d[[1]]
y<-d[[2]]
#表示
plot(x,y,main="USDJPU1時間足(Hi-Low)",xlab="pips",ylab="頻度%",xlim=c(0,80), ylim=c(0,0.06),col=1)
r
※Rはデータの種類がいろいろあって、はじめは、ここでつまずきました。 リストとは


確立密度関数の推定

当初は、ガンマ分布を当てはめようと考えていましたが、ある方から、対数正規分布を教えて頂いたので今回は、対数正規分布を当てはめてみました。回帰方法は、nls() 関数を使用しましたが、初期値設定を導くために以下のコードを追加しています。
rt
結果をグラフ化したものが以下の通りです。
r
対数正規分布の性質から平均と分散(偏差)が求まります。
平均=EXP(meanlog+(sdlog^2)/2)=19.01149(pips)
分散=EXP(2×meanlog+sdlog^2)×(EXP(sdlog^2)-1)=148.6416
偏差=√分散=12.19187

時間単位での変化

時間単位で調べてみると以下の表のようになりました。
時間帯 meanlog sdlog 平均 偏差
0 2.875508 0.508818 20.18533 10.9727
1 2.706802 0.567246 17.59625 10.84084
2 2.462878 0.565203 13.77155 8.448786
3 2.356058 0.561678 12.35177 7.522623
4 2.440356 0.562824 13.4468 8.209028
5 2.593056 0.522776 15.32834 8.593275
6 2.947423 0.468463 21.2669 10.53519
7 3.016413 0.479638 22.90693 11.65029
8 2.948864 0.466359 21.27663 10.48733
9 2.849121 0.495672 19.53029 10.30681
10 2.80227 0.487687 18.56335 9.619049
11 2.857566 0.47229 19.47429 9.735056
12 3.191692 0.516832 27.80594 15.38629
13 3.113179 0.503577 25.53306 13.71777
14 3.242793 0.51251 29.19876 16.00329
15 3.034412 0.475586 23.27787 11.72715
16 2.822379 0.50759 19.12895 10.36999
17 2.655229 0.517096 16.2635 9.004569
18 2.662298 0.519839 16.40218 9.136312
19 2.436341 0.53617 13.1982 7.616876
20 2.371655 0.582975 12.69978 8.079605
21 2.4068 0.499943 12.57576 6.701269
22 2.522685 0.492323 14.06762 7.367471
23 2.745907 0.502226 17.67272 9.465938
※時間は、GMT時間を採用
平均値(期待値)が、時間帯によって倍以上の違いがあることがわかります。


ボリンジャーバンドもどき

平均と偏差と聞いて思い出すのがボリンジャーバンド!と言うことで、上記の内容を踏まえて指標の作成です。
tty
//+----------------------------------------------------------------------------+
//|                                                              BBands.mq4|
//|                                                  Copyright ゥ 2009, bighope | 
//|                                 http://expertadviser-bighope.blogspot.com/ |
//+----------------------------------------------------------------------------+ 
#property copyright "Copyright ゥ 2011, bighope" 
#property link http://expertadviser-bighope.blogspot.com/ 
#property indicator_chart_window
#property indicator_buffers 2
#property indicator_color1 Yellow
#property indicator_color2 Yellow
extern int    GMT      =   1;
extern double Multiple = 1.0;
double Average[24]   = {0.202,0.176,0.138,0.124,0.134,0.153,0.213,0.229,0.213,0.195,0.186,0.195,0.278,0.255,0.292,0.233,0.191,0.163,0.164,0.132,0.127,0.126,0.141,0.177};
double Deviation[24] = {0.110,0.108,0.084,0.075,0.082,0.086,0.105,0.117,0.105,0.103,0.096,0.097,0.154,0.137,0.160,0.117,0.104,0.090,0.091,0.076,0.081,0.067,0.074,0.095}; 

double  Hibands[];
double  Lowbands[];
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int init()
  {
//---- indicators
   IndicatorBuffers(2);
//---- indicator line
   SetIndexStyle(0,DRAW_LINE);
   SetIndexStyle(1,DRAW_LINE);
   SetIndexBuffer(0,Hibands);
   SetIndexBuffer(1,Lowbands);
//----
   return(0);
  }
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
int deinit()
  {
//----
   
//----
   return(0);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int start()
  {
   int i,h,counted_bars=IndicatorCounted();
   double hig,lowg,cg;
//----
   if(Multiple<0)return(0);
   if(counted_bars>0) counted_bars--;
   int limit=Bars-counted_bars;
   
   for(i=0; i<limit; i++){
      h    = TimeHour(Time[i]);
      hig  = High[iHighest(NULL,0,MODE_HIGH,12,i)];
      lowg = Low[iLowest(NULL,0,MODE_LOW,12,i)];
      h = h-GMT;
      if(h<0)h=24+h;
      cg=(hig+lowg)/2;
      Hibands[i]  = cg + (Average[h]/2) + (Deviation[h] * Multiple);
      Lowbands[i] = cg  - (Average[h]/2) - (Deviation[h] * Multiple);  
   }
//----
   return(0);
  }
//+------------------------------------------------------------------+


※USDJPY M5用に作成しています。
 

まとめ

かなり飛ばしてまとめてしまいました。^^;
 

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さんと匿名さんありがとうございました。
今後に生かしたいと思います。