DTオシレーターの転換でラインを描き、理想的取引時の利益を示すインジケーター

FX

あらら、なんか更新止まってしまいました。

先週末4連休とって旅行に行ってたのと、
その間Gmailが調子悪くてメールが届かず「シグナルないけどまぁいっかー」的な状態になっててトレードをお休みする感じになってしまいました。

ただ何もしてなかったわけではなく、取引に使うツールを色々改善していたのです(言い訳)。

インジケーターの変数使わんともったいない

さて、前にDTオシレーターの転換でラインを書くインジケーターを作りましたが、
この時はDTOのSK,SDはiCustomで外部インジケーターから引っ張っていました。

実運用ではDTオシレーターのインジケーターはチャートに必ず出しておくので、
それに加えてiCustomで呼び出しを行うのはスマートではありませんね。
特にCPUリソースの限られているAWSなどで稼働していると、こういうのの積み重ねで動作が不安定になったりしてしまいます。

ということで、DTオシレーターのインジケーター自体にトレンドライン描画機能を加えました。

   for (i = 0; i < count; i++) {
      if (SK[i+1] < SD[i+1] && SK[i] > SD[i]) {
         crossIdx[crossCount] = i;
         ObjectCreate("DTO__CROSS_" + crossCount, OBJ_TEXT, 0, Time[i], Low[i]);
         ObjectSetText("DTO__CROSS_" + crossCount, "B" , 10, "Verdana", Tomato);
         if (crossCount > 0) {
            ObjectDelete("DTO_Trend_" + crossCount);
            ObjectCreate("DTO_Trend_" + crossCount, OBJ_TREND, 0, Time[i], Low[i], Time[i2], High[i2]);
            ObjectSet("DTO_Trend_" + crossCount, OBJPROP_RAY, false);
            ObjectSet("DTO_Trend_" + crossCount, OBJPROP_WIDTH, 3);
            ObjectSet("DTO_Trend_" + crossCount, OBJPROP_COLOR, Tomato);   
   ...

こんなかんじです。簡単ですね。
特に説明するべきところもありません。

利益の出るパターンを可視化する

これだけでもいいのですが、
実際にDTオシレーターの転換が価格の上下を正確に捉えているかの検証を目視でやると見誤る可能性があります。

そこで、このラインの上下でどれだけの利益を取れるのかを計算するロジックを作成しました。
一応想定するストップロスにかかった場合はその分をマイナスし、
そうでない場合はこのラインの最大利益を足します。

            double slSet = Low[iLowest(NULL, 0, MODE_LOW, FirstStopLossBarCount, i)];
            double slBetween = Low[iLowest(NULL, 0, MODE_LOW, i - i2, i2)];
            if (slSet > slBetween) {
               p = slSet - Low[i];
               //ObjectSetText("DTO__CROSS_" + crossCount, "Stop!!" +  DoubleToStr(p, Digits) , 10, "Verdana", Pink);
            } else {
               p = High[i2] -   Low[i] ;
               //ObjectSetText("DTO__CROSS_" + crossCount, DoubleToStr(p, Digits) , 10, "Verdana", Pink);
            }
            profit += p;  

ぶっちゃけ天底取ることなんてできないのでこの数値自体にあまり意味は無いのですが、
基本的にパターン0が最大利益になりやすいところでパターン1のほうが利益が出ていると「おっ」と気づけます。

パターンをボタンでワンタッチ設定

また、結構前に実装して紹介してなかったのですが、
DTオシレーターの設定をボタンで変更できるようにしています。

フィボナッチブレイクアウト売買法」では出てくるパターンは限られていて、
せいぜい以下の4パターンです。

  • パターン0: 8,5,3,3
  • パターン1: 13,8,5,5
  • パターン2: 21,13,8,8
  • パターン3: 34,21,13,13

で、これもフィボナッチ数なので 8 = 5 + 3 と、隣の数を足せば次の数ができるようになっています。
そこで、パターンを渡せばDTオシレーターの設定値を算出できるようにしました。

void SetPattern() {
   PeriodRSI  =8;
   PeriodStoch= 5;
   PeriodSK   = 3;
   PeriodSD   = 3;
   for (int i = 0; i < pattern; i++) {
      int NewRSI = PeriodRSI + PeriodStoch;
      PeriodSD = PeriodStoch;
      PeriodSK = PeriodStoch;
      PeriodStoch = PeriodRSI;
      PeriodRSI = NewRSI;
   }   
}

さらに、ボタンを配置してそのボタンを押せばパターンを0,1,2,3,0...と切り替えられるようにしました。
ボタンを押した時のイベントハンドラはOnChartEventで設定します。

void OnChartEvent(const int id,         // Event ID
                  const long& lparam,   // Parameter of type long event
                  const double& dparam, // Parameter of type double event
                  const string& sparam  // Parameter of type string events
  ) {
   if (id == CHARTEVENT_OBJECT_CLICK) {
      if (sparam == "DTOpsBtn" ) {
         pattern = pattern + 1;
         if (pattern > 3) {
            pattern = 0;
         }
         ObjectSetString(0, "DTOpsBtn", OBJPROP_TEXT, IntegerToString(pattern));
         ObjectSetString(0, "DTOEALBtn", OBJPROP_TEXT, IntegerToString(pattern));
         ChartSetInteger(0, CHART_EVENT_OBJECT_CREATE, 0, true);
         ObjectCreate(0, "ChangeShortDTOEA", OBJ_TEXT, 0, 0, 0);
         ObjectDelete(0, "ChangeShortDTOEA");
         ChartSetInteger(0, CHART_EVENT_OBJECT_CREATE, 0, false);
         SetPattern();
         execute(100);
      }
   }
}

表示してみる

表示するとこんなかんじになります。
大体パターン1で結構いい感じに上下をとれているように見えます。

この通りにトレードできるわけがないのでFXは難しいんですけどねー。

2014-09-27

ソース全文

(2018.10.07追記)
投稿後にいろいろ修正したのでソースを更新しました。
//+------------------------------------------------------------------+
//|                                                 #dtoWithLine.mq4 |
//|                                                             tono |
//+------------------------------------------------------------------+
#property copyright "tono"
#property link      "http://tono-n-chi.com/blog"

#property indicator_separate_window
#property indicator_buffers 2
#property indicator_minimum 0
#property indicator_maximum 100
#property indicator_color1 Pink
#property indicator_color2 LightBlue
#property indicator_width1 2
#property indicator_width2 2
#property indicator_level1 25
#property indicator_level2 75

   extern string TimeFrame  = "Current Time Frame";
   extern int pattern = 1;
   extern int FirstStopLossBarCount = 8;
   extern int count = 100;
   extern bool showProfit = false;
   
   int    PeriodRSI  =8;
   int    PeriodStoch= 5;
   int    PeriodSK   = 3;
   int    PeriodSD   = 3;
         //    0 = SMA
         //    1 - EMA
         //    2 - SMMA
         //    3 - LWMA
   int MAMode=0;
double SK[];
double SD[];
double StoRSI[];
double RSI[];
string IndicatorFileName;
int    timeFrame;
bool   returnBars=false;

string ShortName;

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+

int init()
{
   IndicatorBuffers(4);
      SetIndexBuffer(0,SK);
      SetIndexBuffer(1,SD);
      SetIndexBuffer(2,StoRSI);
      SetIndexBuffer(3,RSI);

   if (TimeFrame == "getBarsCount")
   {
      returnBars=true;
      return(0);
   }   
   timeFrame = stringToTimeFrame(TimeFrame);  
   
   ShortName = "DT Oscillator " + TimeFrame + " " + DoubleToStr(pattern, 0);
   
   IndicatorShortName(ShortName);
   IndicatorFileName = WindowExpertName();

   SetPattern();   
   
   SetButtons();
   return(0);
}

void SetPattern() {
   PeriodRSI  =8;
   PeriodStoch= 5;
   PeriodSK   = 3;
   PeriodSD   = 3;
   for (int i = 0; i < pattern; i++) {
      int NewRSI = PeriodRSI + PeriodStoch;
      PeriodSD = PeriodStoch;
      PeriodSK = PeriodStoch;
      PeriodStoch = PeriodRSI;
      PeriodRSI = NewRSI;
   }   
}

void SetButtons() {
   int winIndex = 1;
   // int winIndex = WindowFind(ShortName);
   // if( winIndex == -1 ) {
   //    winIndex = 2;
   //}
   ObjectCreate(0, "DTOpsBtn", OBJ_BUTTON, winIndex, 0, 0);
   ObjectSetInteger(0, "DTOpsBtn", OBJPROP_XDISTANCE, 0);
   ObjectSetInteger(0, "DTOpsBtn", OBJPROP_YDISTANCE, 0);
   ObjectSetInteger(0, "DTOpsBtn", OBJPROP_XSIZE, 20);
   ObjectSetInteger(0, "DTOpsBtn", OBJPROP_YSIZE, 20);
   ObjectSetString(0, "DTOpsBtn", OBJPROP_TEXT, IntegerToString(pattern));
}

void OnChartEvent(const int id,         // Event ID
                  const long& lparam,   // Parameter of type long event
                  const double& dparam, // Parameter of type double event
                  const string& sparam  // Parameter of type string events
  ) {
   if (id == CHARTEVENT_OBJECT_CLICK) {
      if (sparam == "DTOpsBtn" ) {
         pattern = pattern + 1;
         if (pattern > 3) {
            pattern = 0;
         }
         ObjectSetString(0, "DTOpsBtn", OBJPROP_TEXT, IntegerToString(pattern));
         ChartSetInteger(0, CHART_EVENT_OBJECT_CREATE, 0, true);
         ObjectCreate(0, "ChangeShortDTOEA", OBJ_TEXT, 0, 0, 0);
         ObjectDelete(0, "ChangeShortDTOEA");
         ChartSetInteger(0, CHART_EVENT_OBJECT_CREATE, 0, false);
         SetPattern();
         execute(100);
      }
   }
}
int deinit() {
   ObjectDelete(0, "DTOpsBtn");
   for (int i = 0; i < count; i++) {
         ObjectDelete("DTO__CROSS_" + i);
            ObjectDelete("DTO_Trend_" + i);
   }
   ObjectDelete(0, "DTO_PROFIT");
   return(0); 
}

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int start()
{
   int counted_bars = IndicatorCounted();
   return execute(counted_bars);
}

int execute(int counted_bars) {
   int i,limit;
   if(counted_bars < 0) return(-1);
   if(counted_bars > 0) counted_bars--;
   limit=Bars-counted_bars-1;
   if (returnBars) { SK[0] = limit; return(0); }
   if (timeFrame != Period())
   {
      limit = MathMax(limit,MathMin(Bars,iCustom(NULL,timeFrame,IndicatorFileName,"getBarsCount",0,0)*timeFrame/Period()));
         
      for(i=0; i=0; i--)
   {
      int index = MathMin(i, (ArraySize(RSI) - 1));
      RSI[index] = iRSI(NULL,0,PeriodRSI,PRICE_CLOSE,index);
      double LLV = RSI[ArrayMinimum(RSI,PeriodStoch,index)];
      double HHV = RSI[ArrayMaximum(RSI,PeriodStoch,index)];
      if ((HHV-LLV)!=0)
            StoRSI[i] = 100.0*((RSI[index] - LLV)/(HHV - LLV));
      else  StoRSI[i] = 0;
   }    
   for(i=limit; i>=0; i--) SK[i]=iMAOnArray(StoRSI,0,PeriodSK,0,MAMode,i);
   for(i=limit; i>=0; i--) SD[i]=iMAOnArray(    SK,0,PeriodSD,0,MAMode,i);
   int crossCount = 0;
   int crossIdx[100];
   double profit = 0;
   for (i = 0; i < count; i++) {
         ObjectDelete("DTO__CROSS_" + i);
            ObjectDelete("DTO_Trend_" + i);
   }
   double p;
   for (i = 0; i < count; i++) {
      if (SK[i+1] < SD[i+1] && SK[i] > SD[i]) {
         crossIdx[crossCount] = i;
         int i2 = crossIdx[crossCount - 1];
         ObjectCreate("DTO__CROSS_" + crossCount, OBJ_TEXT, 0, Time[i], Low[i]);
         ObjectSetText("DTO__CROSS_" + crossCount, "B" + (i - i2 + 1) , 10, "Verdana", Tomato);
         if (crossCount > 0) {
            ObjectCreate("DTO_Trend_" + crossCount, OBJ_TREND, 0, Time[i], Low[i], Time[i2], High[i2]);
            
            if (showProfit) {
               double slSet = Low[iLowest(NULL, 0, MODE_LOW, FirstStopLossBarCount, i)];
               double slBetween = Low[iLowest(NULL, 0, MODE_LOW, i - i2, i2)];
               if (slSet > slBetween) {
                  p = slSet - Low[i];
               } else {
                  p = High[i2] -   Low[i] ;
               }
               profit += p;
            }  
         } else {
            ObjectCreate("DTO_Trend_" + crossCount, OBJ_TREND, 0, Time[i], Low[i], Time[0], High[0]);
         }
         ObjectSet("DTO_Trend_" + crossCount, OBJPROP_RAY, false);
         ObjectSet("DTO_Trend_" + crossCount, OBJPROP_WIDTH, 3);
         ObjectSet("DTO_Trend_" + crossCount, OBJPROP_COLOR, Tomato);
         crossCount++;
      } else if (SK[i+1] > SD[i+1] && SK[i] < SD[i]) {
         crossIdx[crossCount] = i;
         i2 = crossIdx[crossCount - 1];
         ObjectCreate("DTO__CROSS_" + crossCount, OBJ_TEXT, 0, Time[i], High[i] + iATR(NULL, 0, 14, 0));
         ObjectSetText("DTO__CROSS_" + crossCount, "S" + (i - i2 + 1) , 10, "Verdana", RoyalBlue);
         if (crossCount > 0) {
            if (showProfit) {
               slSet = High[iHighest(NULL, 0, MODE_HIGH, FirstStopLossBarCount, i)];
               slBetween = High[iHighest(NULL, 0, MODE_HIGH, i - i2, i2)];
               if (slSet < slBetween) {
                  p = High[i] - slSet;
               } else {
                  p = High[i] - Low[i2];
               }
               profit += p;
            }
            ObjectCreate("DTO_Trend_" + crossCount, OBJ_TREND, 0, Time[i], High[i], Time[i2], Low[i2]);
         } else {
            ObjectCreate("DTO_Trend_" + crossCount, OBJ_TREND, 0, Time[i], High[i], Time[0], Low[0]);
         }
         ObjectSet("DTO_Trend_" + crossCount, OBJPROP_RAY, false);
         ObjectSet("DTO_Trend_" + crossCount, OBJPROP_WIDTH, 3);
         ObjectSet("DTO_Trend_" + crossCount, OBJPROP_COLOR, RoyalBlue);
         crossCount++;
      }
   }
   if (showProfit) {
     string nm = "DTO_PROFIT";
     ObjectCreate(nm, OBJ_LABEL, 0, 0, 0);
     ObjectSetText(nm, "profit:" + DoubleToStr(profit, Digits), 10, "Verdana", clrWhite);
     ObjectSet(nm, OBJPROP_CORNER, 0);
     ObjectSet(nm, OBJPROP_XDISTANCE, 10);
     ObjectSet(nm, OBJPROP_YDISTANCE, 60);
   }
   return(0);
}

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int stringToTimeFrame(string tfs)
{
   for(int l = StringLen(tfs)-1; l >= 0; l--)
   {
      int chara = StringGetChar(tfs,l);
          if((chara > 96 && chara < 123) || (chara > 223 && chara < 256))
               tfs = StringSetChar(tfs, l, chara - 32);
          else 
              if(chara > -33 && chara < 0)
                  tfs = StringSetChar(tfs, l, chara + 224);
   }
   
   int tf=0;
         if (tfs=="M1" || tfs=="1")     tf=PERIOD_M1;
         if (tfs=="M5" || tfs=="5")     tf=PERIOD_M5;
         if (tfs=="M15"|| tfs=="15")    tf=PERIOD_M15;
         if (tfs=="M30"|| tfs=="30")    tf=PERIOD_M30;
         if (tfs=="H1" || tfs=="60")    tf=PERIOD_H1;
         if (tfs=="H4" || tfs=="240")   tf=PERIOD_H4;
         if (tfs=="D1" || tfs=="1440")  tf=PERIOD_D1;
         if (tfs=="W1" || tfs=="10080") tf=PERIOD_W1;
         if (tfs=="MN" || tfs=="43200") tf=PERIOD_MN1;
         if (tf
      

コメント

  1. リム より:

    素晴らしいインジケーターの投稿ありがとうございます。
    さっそく使わせていただいたのですが、メインチャートのラインは表示されるのですが
    サブウインドウのラインが表示されません。
    iCustomを使用されてないとのことで元のインジケーターは必要ないと思いますが

    何か解決法がございましたらご教授下さい。よろしくお願いいたします。

    • との より:

      ご指摘ありがとうございます!
      投稿後にいろいろ修正したのでソース全文を更新しました。
      こちらの環境ではBuild 1090のMT4で動作していますので、試してみてください。

  2. リム より:

    コードの更新ありがとうございます。
    さっそく試してみましたが…状況変わらずでした。

    うーん。謎です。

タイトルとURLをコピーしました