ラベル MT4プログラミング の投稿を表示しています。 すべての投稿を表示
ラベル MT4プログラミング の投稿を表示しています。 すべての投稿を表示

2020年4月18日土曜日

[MT4]オーダーを触るときはチケット指定が安全

EAにてポジションをクローズする処理を書く場合、素直に書くとこのようなコードになるかと思います。
これでもたぶん問題になりません。

   for( int i = OrdersTotal() - 1; i >= 0; i-- )
   {
      if( !OrderSelect(i, SELECT_BY_POS) ) continue;
      if( OrderMagicNumber() != MagicNumber ) continue;
      if( OrderSymbol() != Symbol() ) continue;
      
      int order = OrderType();
      
      if( order == OP_BUY )
      {
         //クローズ条件の算出
         if( 買い側クローズ条件なら )
         {
            OrderClose(OrderTicket(), OrderLots(), Ask, 1);
         }
      }
      else if ( order == OP_SELL )
      {
         //クローズ条件の算出
         if( 売り側クローズ条件なら )
         {
            OrderClose(OrderTicket(), OrderLots(), Bid, 1);
         }
      }
   }

ただ、OrderCloseの処理がそれなりに時間がかかる処理なので、その間に他の条件でポジション数が変更になってしまうと、インデックスずれなどで想定しないポジションに対しての処理や、ポジションが処理されないなどの不安があります。

そこで、このように一回チケットIDの一覧を取得して、その取得したチケットIDに対してOrderSelectをかけてやることで、その不安をある程度低減可能です。OrderCloseが100ms台の処理なのに対してOrderTicketなどの情報取得系は数ns台で処理されるためです。

#include <Arrays/ArrayInt.mqh>
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
   CArrayInt positions;
   int orderCount = OrdersTotal();
   
   for( int i = 0; i < orderCount ; i++)
   {
      if( OrderSelect(i, SELECT_BY_POS) )
      {
         if( OrderMagicNumber() == MagicNumber && OrderSymbol() == Symbol())
         {
            positions.Add(OrderTicket());
         }
      }
   }
   
   int targetCount = positions.Total();
   for( int i = 0; i < targetCount; i++ )
   {
      int ticket = positions.At(i);
      if( OrderSelect(ticket, SELECT_BY_TICKET) )
      {
         int order = OrderType();
         if(order  == OP_BUY)
         {
             //クローズ条件の算出
             if( 買い側クローズ条件なら )
             {
                OrderClose(ticket, OrderLots(), Ask, 1);
             }
         }
         else if(order  == OP_SELL)
         {
             //クローズ条件の算出
             if( 売り側クローズ条件なら )
             {
                OrderClose(ticket, OrderLots(), Bid, 1);
             }
         }
      }   
   }
}

なお、このような形で、forの中をcontinueで次の条件に飛ばすような書き方にするとインデントが深くなるのが抑えられて見やすくなりますが、処理には影響ありません。

   for( int i = 0; i < orderCount ; i++)
   {
      if( !OrderSelect(i, SELECT_BY_POS) ) continue;
      if( OrderMagicNumber() != MagicNumber) continue;
      if( OrderSymbol() != Symbol() ) continue;

      positions.Add(OrderTicket());
   }

ちょっとTwitterで話題になったので私なりの書き方をご紹介してみました。もし問題があればぜひご指摘お願いします。

本ブログは、投資に対する利益を約束する物ではありません。最終的には自己責任によるご判断よろしくお願いいたします。

2017年12月13日水曜日

[MT4インジケータ]ちょっと分けて考えてみよう。半分だけのボリンジャーバンド

最近LSMAを研究していて、わりかしいい感じなのですが、その中で、バンドは分けて考えたほうがいいのではないか?ということに気が付きました。
本家のLSMAの方は、二乗平均誤差をベースにあれこれしていますが、メジャーインジケータであるボリンジャーバンドも、分けて考えると違った物が見えてくるかもしれません。

ということで、早速サラッと作ってみました。標準的なボリンジャーバンドの計算方法を移動平均より上のデータと下のデータを分けて計算して描画しています。
データ量が1/2になっっている前提で、バンド幅のσ値を通常の2倍にしています。


わりあい機能するような・・・しないような??
M1とかの逆張りとかのほうがいいのかもしれません。

ソースコードはブログの最後です。

ボリンジャーバンド特有の価格が動いた時に逆方向にもバンド幅が伸びてしまう症状が無いだけでも優秀かもしれません。

せっかく上と下に分けるんだから、high/low見た方がいいかな?ということで、下はlow、上はhighと分けて計算しています。

「MT4でFXを勝ち抜く研究をするブログ」で公開している無料インジケータは、こちらの一覧から。
インジケータ一覧

Twitterもよろしくお願いします。
https://twitter.com/mt4program
Trading View プロフィール

ブログランキングにご協力よろしくお願いします。m(._.)m
にほんブログ村 為替ブログ FX テクニカルトレード派へ
にほんブログ村

お約束ですが、本ブログは、投資に対する利益を約束する物ではありません。最終的には自己責任によるご判断よろしくお願いいたします。

//------------------------------------------------------------------
// +と-側のボリンジャーバンドを別に計算してみた。
#property copyright "Copyright 2017,  Daisuke"
#property link      "http://mt4program.blogspot.jp/"
#property version   "1.00"
#property strict
#property indicator_chart_window

//バッファーを指定する。
#property indicator_buffers 5

//プロット数を指定する。
#property indicator_plots   5

#property indicator_label1  "Band1"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrAqua
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1

#property indicator_label2  "Band2"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrAqua
#property indicator_style2  STYLE_DOT
#property indicator_width2  1

#property indicator_label3  "Band3"
#property indicator_type3   DRAW_LINE
#property indicator_color3  clrIndianRed
#property indicator_style3  STYLE_SOLID
#property indicator_width3  1

#property indicator_label4  "Band4"
#property indicator_type4   DRAW_LINE
#property indicator_color4  clrIndianRed
#property indicator_style4  STYLE_SOLID
#property indicator_width4  1

#property indicator_label5  "MA"
#property indicator_type5   DRAW_LINE
#property indicator_color5  clrYellow
#property indicator_style5  STYLE_DOT
#property indicator_width5  2

double m_buffer1[];
double m_buffer2[];
double m_buffer3[];
double m_buffer4[];
double m_buffer5[];

input int MaPeriod = 21;   // 移動平均期間
input ENUM_APPLIED_PRICE PriceMode = PRICE_TYPICAL;   // 対象価格
input double BandWidth1 = 2.0; // バンド幅1
input double BandWidth2 = 4.0; // バンド幅2

//------------------------------------------------------------------
//初期化
int OnInit()
{
   //インジケーターバッファを初期化する。
   SetIndexBuffer(0,m_buffer1);
   SetIndexBuffer(1,m_buffer2);
   SetIndexBuffer(2,m_buffer3);
   SetIndexBuffer(3,m_buffer4);
   SetIndexBuffer(4,m_buffer5);
   
   string short_name = "HBB(" + IntegerToString(MaPeriod) + ")";
   IndicatorShortName(short_name);
   
   return(INIT_SUCCEEDED);
}


//------------------------------------------------------------------
//計算イベント
int OnCalculate(const int rates_total,          //各レート要素数
                const int prev_calculated,      //計算済み要素数
                const datetime &time[],         //要素ごとの時間配列
                const double &open[],           //オープン価格配列
                const double &high[],           //高値配列
                const double &low[],            //安値配列
                const double &close[],          //クローズ価格配列
                const long &tick_volume[],      //ティック数(要素の更新回数)
                const long &volume[],           //実ボリューム(?)
                const int &spread[])            //スプレット
{
   double upDiff[];
   double downDiff[];
   ArrayResize(upDiff, MaPeriod);
   ArrayResize(downDiff, MaPeriod);
   for( int i = rates_total - prev_calculated - 1; i >= 0 && !IsStopped(); i-- )
   {
      if( i > rates_total - MaPeriod ) continue;
      
      m_buffer5[i] = iMA(NULL, PERIOD_CURRENT, MaPeriod, 0, MODE_SMA, PriceMode, i);
      
      ArrayInitialize(upDiff, 0);
      ArrayInitialize(downDiff, 0);
      for( int j = 0; j < MaPeriod; j++ )
      {
         if( high[i + j] > m_buffer5[i + j] ) upDiff[j] = high[i + j] - m_buffer5[i + j];
         if( low[i + j] < m_buffer5[i + j] ) downDiff[j] = m_buffer5[i + j] - low[i + j];
      }
      
      double upStd = StdDev(upDiff, MaPeriod, 0);
      double downStd = StdDev(downDiff, MaPeriod, 0);
      
      m_buffer1[i] = m_buffer5[i] + BandWidth1 * upStd;
      m_buffer2[i] = m_buffer5[i] + BandWidth2 * upStd;
      m_buffer3[i] = m_buffer5[i] - BandWidth1 * downStd;
      m_buffer4[i] = m_buffer5[i] - BandWidth2 * downStd;
   }

   ArrayFree(upDiff);
   ArrayFree(downDiff);

   return rates_total - 1;
}

//------------------------------------------------------------------
//標準偏差を求める。
// return 標準偏差
double StdDev(
   const double &values[],      //元となる配列
   int count,                   //計算対象数
   int shift                    //シフト
)
{
   if( (count + shift) > ArraySize(values) ) return 0;

   double avg = 0 ;
   double mu = 0 ;
   for( int i = shift; i < shift + count; i++ )
   {
      avg += values[i];
   }
   avg = avg / count;

   for( int i = shift; i < shift + count; i++ )
   {
      mu += MathPow(values[i] - avg, 2);
   }

   // 標本標準偏差
   return MathSqrt(mu / (count - 1));
}

2017年12月10日日曜日

[MT4プログラミング]通貨強弱インジをEURベースに変更する。

twitterで質問がありましたので、公開している通貨強弱インジをEURベースに変更する方法です。

通貨強弱インジケータの元ファイルはこちらからもダウンロード可能です。
FX-ONのダウンロードはこちらから。
ダウンロード

ソースコードを修正出来る方ならかんたんに変更可能です。

OnInit関数と、OnCalculate関数内の”USD”と”USDUSD”となっている部分を”EUR”と"EUREUR"に変更するだけです。

なお、後半のCurrencyIndex3/4とCurrencyMultiKairiはこの変更をしてもほとんど表示は変わりません。(USDの線が100から動くやつです。)
システムトラブル等でクロスとドルストレートの間に比率の乖離が起きない限り同じ同じ値となります。これは、最後に通貨間比率の平均で割ってしまっている為です。

■USDを基準に計算した乖離率チャート(上)と、EURを基準にした乖離率チャート(下) (CurrencyMultiKairi)


■USDを基準としたN本前100チャート(上)、EURを基準としたN本前100チャート(下)(CurrencyIndex1)

通貨間の平均で割ったものは相対的な通貨の強弱がはっきり分かる利点があります。USDのラインが動くのが特徴です。しかし、たとえば、USDJPYをトレードしている際、円安になっているのに、他にもっと弱い通貨があって、CurrencyIndex上では円高になっている事もあります。(その場合は逃げ頃なんだと思われますが・・・)

あと、EURをベースにした場合、EURCHFやEURNZDなど、EURをベースとするペアのチャートをある程度過去まで表示しておく必要が有ることにご注意ください。

あ、もしかっこよく改造するのであれば、基準通貨を引数として定義して、パラメータで変更出来る方がいいかもしれません。

サンプルとしてCurrencyIndex1の改造例をブログ最後に乗せておきます。
ナビゲーターからCurrencyIndex1を選択した後、マウス右クリックメニューの修正を選び、赤字の部分を修正してみてください。

※もう少し補足します。
これは私のブログタイトルの付け方が悪かったかもしれません。
CurenncyIndex3,4及びMultiKairiはUSDストレートから計算していますが、最終表示値にはUSD成分は(USD値以外は)含まれていません。

 次の計算を行っています。
・7通貨ペアの対USDインデックス値を計算する。
例EUR/USDインデックス

・8通貨ペア(USDUSDを含む)平均値を簡易USDインデックスとする。
   USDインデックス=100/通貨ペアのインデックス平均値

 ※EURUSDインデックスが101だった場合、EURがUSDに対して基準点か101%になったという意味。EURからみて、USD100/101で99%になった事になる。この場合ドルインデックスは99

・7通貨ペアインデックス値を再度にUSDインデックスを掛け合わせる
 EURインデックス=EUR/USDインデックス ☓ USDインデックス
 EURインデックス=EUR/USDインデックス ☓ (100/通貨ペアの平均値)

ということで、どの通貨を基準に計算しても、ほぼおなじ値となってしまいます。
ほぼというのは、スプレット差などあるので全く同じにはなりません。


その為、各通貨のフローの想定は、線の方向性で見ることになります。
ある2つの通貨の幅が広がっている場合、下方向の通過から上方向の通貨に資金が流れていると想定出来ます。
上記はわかりやすくEUR,USD,JPY,GBPの4通貨のみを表示しています。

JPYが売られてGBPが買われていることがわかるかと思います。
EURも微妙に売られています。EURとJPYだけ比較すると差が広がっていることが分かります。
JPYが売られて、EURも少し売られて、USDとGBPが買われている感じです。

このインジケータは通貨の流通量までは見ていません。例えば、ドル円が1円動くのと、ポンド円が1円動くのとでは、動くお金の量が全く異なります。あくまで価格ベースの参考値となります。
例えば、1ドル=100兆ジンバブエドルになったとしても、1ドル=10兆円なるわけではありません。
しかし、このインジケータの計算方法だと、平均的には50兆倍になった計算になってしまします。

その意味では、このインジケータの初期設定のままだと、GBPに全体が釣られすぎているかもしれません。GBP大暴れです(笑)

そのうち正確性が重要になってきた場合は、2016の取引シェアとかの値を参考に取引量補正も掛けたインジケータを作成したいと思います。

[MT4インジケータ]通貨の強弱をUSDを基準に表示する。その2
[MT4インジケータ]通貨の強弱をUSDを基準に表示する。その3
[MT4インジケータ]通貨の強弱をUSDを基準にその4とFX-ONへのアップロード

「MT4でFXを勝ち抜く研究をするブログ」で公開している無料インジケータは、こちらの一覧から。
インジケータ一覧

Twitterもよろしくお願いします。
https://twitter.com/mt4program
Trading View プロフィール

ブログランキングにご協力よろしくお願いします。m(._.)m
にほんブログ村 為替ブログ FX テクニカルトレード派へ
にほんブログ村

お約束ですが、本ブログは、投資に対する利益を約束する物ではありません。最終的には自己責任によるご判断よろしくお願いいたします。
//------------------------------------------------------------------
// 通貨強弱 インデックス比較インジケータ(N本前開始)
#property copyright "Copyright 2017,  Daisuke"
#property link      "http://mt4program.blogspot.jp/"
#property version   "1.00"
#property strict
#property indicator_separate_window

#include <Arrays/ArrayString.mqh>

//インデックス開始位置タイプ
enum ENUM_INDEX_TYPE
{
   // 固定値
   IDX_TYPE_FIX,
   
   // 前日終値
   IDX_TYPE_DAY,

   // 前週終値
   IDX_TYPE_WEEKEND,

   // 前月終値
   IDX_TYPE_MONTHEND,
};

//バッファーを指定する。
#property indicator_buffers 9

input string sep00 = "";            //【通貨】
input string Currency1 = "EUR";     // 対象通貨1
input color Color1 = clrDodgerBlue; // 対象通貨1色

input string Currency2 = "GBP";     // 対象通貨2
input color Color2 = clrMagenta;    // 対象通貨2色

input string Currency3 = "AUD";     // 対象通貨3
input color Color3 = clrGreen ;     // 対象通貨3色

input string Currency4 = "NZD";     // 対象通貨4
input color Color4 = clrSilver;     // 対象通貨4色

input string Currency5 = "USD";     // 対象通貨5
input color Color5 = clrAqua;       // 対象通貨5色

input string Currency6 = "CAD";     // 対象通貨6
input color Color6 = clrPink;       // 対象通貨6色

input string Currency7 = "CHF";     // 対象通貨7
input color Color7 = clrPaleGreen;  // 対象通貨7色

input string Currency8 = "JPY";     // 対象通貨8
input color Color8 = clrRed;        // 対象通貨8色

input string BaseCurrency = "USD";  // 基準通貨

input string sep10 = "";            //【計算設定】
input ENUM_INDEX_TYPE IndexType = IDX_TYPE_FIX;   // インデックス開始位置タイプ
input int IndexLength = 200;        // インデックス化開始本数(タイプIDX_TYPE_FIX時のみ)
input int MaPeriodBase = 4;         // 比較元移動平均期間(ある程度大きな値でもOK)
input int MaPeriodTarget = 4;       // 比較先移動平均期間(小さい値推奨)

input string sep20 = "";            //【その他】
input string CurrencyPrefix = "";   // 通貨ペア名の前に付く文字列
input string CurrencyPostfix = "";  // 通貨ペア名の後に付く文字列
input int LabelDistance = 3;        // 通貨名ラベル描画スペース

//バッファ
double m_buffer1[];
double m_buffer2[];
double m_buffer3[];
double m_buffer4[];
double m_buffer5[];
double m_buffer6[];
double m_buffer7[];
double m_buffer8[];

//通貨一覧
CArrayString m_currencies;

//通貨ペア一覧
CArrayString m_currencyPairs;

//USDインデックス
int m_usdIndex = -1;

//オブジェクト名
#define OBJECT_NAME "OBJ_CURR_HL"

//色配列
color m_colors[8];

//------------------------------------------------------------------
// 初期化
int OnInit()
{
   ClearObjects();

   IndicatorBuffers(8);

   int count = 0 ;
   SetIndexStyle(count, DRAW_LINE, STYLE_SOLID, 1, Color1);
   SetIndexBuffer(count++, m_buffer1);
   
   SetIndexStyle(count, DRAW_LINE, STYLE_SOLID, 1, Color2);
   SetIndexBuffer(count++, m_buffer2);

   SetIndexStyle(count, DRAW_LINE, STYLE_SOLID, 1, Color3);
   SetIndexBuffer(count++, m_buffer3);

   SetIndexStyle(count, DRAW_LINE, STYLE_SOLID, 1, Color4);
   SetIndexBuffer(count++, m_buffer4);

   SetIndexStyle(count, DRAW_LINE, STYLE_SOLID, 1, Color5);
   SetIndexBuffer(count++, m_buffer5);

   SetIndexStyle(count, DRAW_LINE, STYLE_SOLID, 1, Color6);
   SetIndexBuffer(count++, m_buffer6);

   SetIndexStyle(count, DRAW_LINE, STYLE_SOLID, 1, Color7);
   SetIndexBuffer(count++, m_buffer7);

   SetIndexStyle(count, DRAW_LINE, STYLE_SOLID, 1, Color8);
   SetIndexBuffer(count++, m_buffer8);
   
   m_currencies.Add(Currency1);
   m_currencies.Add(Currency2);
   m_currencies.Add(Currency3);
   m_currencies.Add(Currency4);
   m_currencies.Add(Currency5);
   m_currencies.Add(Currency6);
   m_currencies.Add(Currency7);
   m_currencies.Add(Currency8);
   
   m_colors[0] = Color1;
   m_colors[1] = Color2;
   m_colors[2] = Color3;
   m_colors[3] = Color4;
   m_colors[4] = Color5;
   m_colors[5] = Color6;
   m_colors[6] = Color7;
   m_colors[7] = Color8;

   m_usdIndex = -1;
   for( int i = 0; i < m_currencies.Total(); i++)
   {
      if(m_currencies.At(i) == BaseCurrency)
      {
         m_usdIndex = i;
         break;
      }
   }
   if( m_usdIndex < 0 )
   {
      //USDが見つからない場合、パラメータ異常
      return INIT_PARAMETERS_INCORRECT;
   }

   //USDに対する通貨ペアを生成する。   
   for( int i = 0; i < m_currencies.Total(); i++)
   {
      if( m_currencies.At(i) == "" )
      {
         m_currencyPairs.Add("");
         continue;
      }
      
      if( i < m_usdIndex )
      {
         m_currencyPairs.Add(CurrencyPrefix + m_currencies.At(i) + BaseCurrency + CurrencyPostfix);
      }
      else if( i > m_usdIndex )
      {
         m_currencyPairs.Add(CurrencyPrefix + BaseCurrency + m_currencies.At(i) + CurrencyPostfix);
      }
      else
      {
         m_currencyPairs.Add(BaseCurrency + BaseCurrency);
      }
      //線のラベルを設定する。
      SetIndexLabel(i, m_currencies.At(i));
   }
   
   ChartSetInteger(0, CHART_SHIFT, true);
   ChartSetDouble(0, CHART_SHIFT_SIZE, 12);

   return(INIT_SUCCEEDED);
}

//------------------------------------------------------------------
//終了処理
void OnDeinit( const int reason )
{
   ClearObjects();
}

//------------------------------------------------------------------
//計算イベント
int OnCalculate(
   const int rates_total,          //各レート要素数
 const int prev_calculated,      //計算済み要素数
 const datetime &time[],         //要素ごとの時間配列
 const double &open[],           //オープン価格配列
 const double &high[],           //高値配列
 const double &low[],            //安値配列
 const double &close[],          //クローズ価格配列
 const long &tick_volume[],      //ティック数(要素の更新回数)
 const long &volume[],           //実ボリューム(?)
 const int &spread[])            //スプレット
{
   int total = m_currencyPairs.Total();
   if( IndexType == IDX_TYPE_FIX )
   {
      //新しいバーが生成されたとき。
      if(prev_calculated != rates_total - 1)
      {
         //いったんバッファを初期化する。
         ArrayInitialize(m_buffer1, EMPTY_VALUE);
         ArrayInitialize(m_buffer2, EMPTY_VALUE);
         ArrayInitialize(m_buffer3, EMPTY_VALUE);
         ArrayInitialize(m_buffer4, EMPTY_VALUE);
         ArrayInitialize(m_buffer5, EMPTY_VALUE);
         ArrayInitialize(m_buffer6, EMPTY_VALUE);
         ArrayInitialize(m_buffer7, EMPTY_VALUE);
         ArrayInitialize(m_buffer8, EMPTY_VALUE);
      }
   
      for( int i = IndexLength - 1; i >= 0; i--)
      {
         if( m_buffer1[i] == EMPTY_VALUE || i == 0 )
         {
            for( int j = 0; j < total; j++)
            {
               string pair = m_currencyPairs.At(j);
               if(pair == BaseCurrency + BaseCurrency )
               {
                  SetValue(j, 100, i);
               }
               else
               {
                  double baseValue = iMA(pair, PERIOD_CURRENT, MaPeriodBase, 0, MODE_LWMA, PRICE_CLOSE, IndexLength - 1);
                  double targetValue = iMA(pair, PERIOD_CURRENT, MaPeriodTarget, 0, MODE_LWMA, PRICE_CLOSE, i);
   
                  if( j < m_usdIndex )
                  {
                     if( baseValue != 0 && baseValue != EMPTY_VALUE )
                     {
                        SetValue(j, targetValue / baseValue * 100, i);
                     }
                  }
                  else
                  {
                     if( targetValue != 0 && targetValue != EMPTY_VALUE )
                     {
                        SetValue(j,  baseValue / targetValue * 100, i);
                     }
                  }
               }
            }
         }
      }
   }
   else
   {
      // 特定の区切りをタイミングとする場合
      // 日にち区切りの場合、1時間足以下の必要あり。
      if( IndexType == IDX_TYPE_DAY && Period() > 60 ) return 0;
      // 週区切りの場合、日足以下の必要あり。
      if( IndexType == IDX_TYPE_WEEKEND && Period() > 60 * 24 ) return 0;
      
      //GMT補正とかは考えない・・。
      for( int i = rates_total - prev_calculated - 1; i >= 0; i-- )
      {
         for( int j = 0; j < total; j++)
         {
            string pair = m_currencyPairs.At(j);
            if(pair == BaseCurrency + BaseCurrency )
            {
               SetValue(j, 100, i);
            }
            else
            {
               //おおちゃくw
               //ちゃんと出ない場合は、日足と週足と月足のチャートを一度表示してね。
               //平滑化をかけているのと、終値で判断(始値ではない)のでぴったり100スタートではないので注意。
               double baseValue = 0;
               if( IndexType == IDX_TYPE_DAY )
               {
                  baseValue = iClose( pair, PERIOD_D1, iBarShift(pair, PERIOD_D1, time[i]) + 1);
               }
               else if( IndexType == IDX_TYPE_WEEKEND )
               {
                  baseValue = iClose( pair, PERIOD_W1, iBarShift(pair, PERIOD_W1, time[i]) + 1);
               }
               else if( IndexType == IDX_TYPE_WEEKEND )
               {
                  baseValue = iClose( pair, PERIOD_MN1, iBarShift(pair, PERIOD_MN1, time[i]) + 1);
               }
               double targetValue = iMA(pair, PERIOD_CURRENT, MaPeriodTarget, 0, MODE_LWMA, PRICE_CLOSE, i);

               if( j < m_usdIndex )
               {
                  if( baseValue != 0 && baseValue != EMPTY_VALUE )
                  {
                     SetValue(j, targetValue / baseValue * 100, i);
                  }
               }
               else
               {
                  if( targetValue != 0 && targetValue != EMPTY_VALUE )
                  {
                     SetValue(j,  baseValue / targetValue * 100, i);
                  }
               }
            }
         }
      }      
   }

   
   DrawCurrencyLabels();

   return rates_total - 1;
}


//------------------------------------------------------------------
//通貨ラベルを描画する。
void DrawCurrencyLabels()
{
   //ソート行う。
   //とりあえずバブルソート
   int index[8];
   double rates[8];
   ArrayInitialize(index, 0);
   ArrayInitialize(rates, 0);

   int total = m_currencyPairs.Total();

   for( int i = 0 ; i < total; i++ )
   {
      index[i] = i;
      rates[i] = GetValue(i, 0);
   }
   
   //ソート行う。
   //とりあえずバブルソート
   for( int b = 1 ; b < total; b++ )
   {
      for( int j = 0; j < total - b; j++ )
      {
         if( rates[j] > rates[j + 1]  )
         {
            double work = rates[j + 1];
            rates[j + 1] = rates[j];
            rates[j] = work;
            
            int indexWork = index[j + 1];
            index[j + 1] = index[j];
            index[j] = indexWork;
         }
      }
   }

   //通貨ラベルを更新する。
   ClearObjects();
   int sub = WindowOnDropped();

   double max = ChartGetDouble(0, CHART_PRICE_MAX, sub);
   double min = ChartGetDouble(0, CHART_PRICE_MIN, sub);
   
   double distance = ( max - min ) / (total + 2);
   datetime writeTime = iTime(NULL, PERIOD_CURRENT, 0) + PeriodSeconds() * LabelDistance ;

   for( int j = 0; j < total; j++)
   {
      //描画対象通貨(インデックス)
      int currencyIndex = (int)index[j];
   
      //価格テキストを追加する。
      string objectName = StringFormat("%s_TXT_%d_%d", OBJECT_NAME, sub, j);

      if( ObjectCreate(0, objectName, OBJ_TEXT, WindowOnDropped(),writeTime , min + distance * (j + 1)) )
      {
         // 表示文字列
         ObjectSetString(0, objectName, OBJPROP_TEXT, m_currencies.At(currencyIndex));
         
         // アンカー
         ObjectSetInteger(0, objectName, OBJPROP_ANCHOR, ANCHOR_BOTTOM);
         
         ObjectSetInteger(0, objectName, OBJPROP_COLOR, m_colors[currencyIndex]);
      }
   }
}

//------------------------------------------------------------------
//値を設定する。
void SetValue(
   int i,            //配列番号0~7
   double value,     //値
   int shift)        //シフト
{
   switch(i)
   {
      case 0:
         m_buffer1[shift] = value;
         break;
      case 1:
         m_buffer2[shift] = value;
         break;
      case 2:
         m_buffer3[shift] = value;
         break;
      case 3:
         m_buffer4[shift] = value;
         break;
      case 4:
         m_buffer5[shift] = value;
         break;
      case 5:
         m_buffer6[shift] = value;
         break;
      case 6:
         m_buffer7[shift] = value;
         break;
      case 7:
         m_buffer8[shift] = value;
         break;
   }
}

//------------------------------------------------------------------
//値を取得する。
double GetValue(
   int i,            //配列番号0~7
   int shift)        //シフト
{
   switch(i)
   {
      case 0:
         return m_buffer1[shift];
      case 1:
         return m_buffer2[shift];
      case 2:
         return m_buffer3[shift];
      case 3:
         return m_buffer4[shift];
      case 4:
         return m_buffer5[shift];
      case 5:
         return m_buffer6[shift];
      case 6:
         return m_buffer7[shift];
      case 7:
         return m_buffer8[shift];
   }
   return EMPTY_VALUE;
}

//------------------------------------------------------------------
//オブジェクトをすべて削除する。
void ClearObjects()
{
   long chartId = ChartID();   
   int sub = WindowOnDropped();
   int total = ObjectsTotal( chartId, sub );
   //生成したオブジェクトを削除する。
   //0から削除するとインデックス位置がずれて
   //正しく削除できないため、後ろから削除するようにする。
   for( int i = total - 1; i >= 0 ; i--)
   {
      string name = ObjectName( chartId, i, sub );
      
      // 先頭文字列がRangeRectangleNameと一致する場合、削除する。
      if ( StringFind( name, OBJECT_NAME ) == 0 )
      {
         ObjectDelete( chartId, name );
      }
   }
}

2017年11月29日水曜日

[MT4インジケータ]通貨の強弱をUSDを基準に表示する。その2

昨日の続きです。
[MT4インジケータ]通貨の強弱をUSDを基準に表示する。その1



上が昨日作成した現在時刻からN本前の点を100とした通貨の推移を描画したものです。
下が、描画対象のバーのN本前を100とした通貨の推移を描画したものです。

バーの最終値のみをひたすら書き続けた結果となります。上はN本前からどんどん幅が広がっていく感じの描画がされますが、下はドルに対して通貨全体の値動きがどのように推移したのかが延々描画されていく形です。

USDを基準にした時に、どのように動いているのかは眺めて、新しい戦略が思い浮かぶといいなぁ。例えばこんな感じ。

■USD,EUR,JPY,GBP H4
ふらーと眺めてみると、JPYとGBPの逆相関が結構目立ちます。
円高=ポンド安、円安=ポンド高

です。よっぽどGBPJPYのクロス通貨でのトレード量が多いのか、島国だからなのかはわかりませんが、EURUSDなどに比べると、USDJPYとGBPUSDが逆方向(チャート的には同じ方向、USDJPYにおける円高と、GBPUSDにおけるポンド安はどちらも下方向に動く)によく動いている事がわかります。具体化はできませんが、円とポンドのインデックスラインのクロスは、なかなか色々示唆していそうです。

一点注意点です。複数通貨の履歴が必要ですので、履歴が足りていない通貨があると表示がおかしくなります。
下のチャートは、USDJPYの履歴が足りなくて描画がおかしくなった例です。

このような場合は、対象となっている通貨ペアをチャートで開いて、スクロールバーを一番最後まで持っていて過去データをダウンロードするようにした後に再度インジケータをセットしてみてください。

ここまで来るとオリジナルっぽいインジケータになってきたでしょうか?
さて、後一本あります。正直役に立つかどうかわかってないのですが、価格の行き過ぎを見ようとおもった場合、インデックス化だけではなくドルに対する強さの中心値に対するばらつきでも見てみたいなーと。8つの通貨のインデックス値の平均からの乖離が見えるインジケータを最後に作りたいと思います。
作ろうとインジケータのいい所は、他の過去データからの乖離率と比較すると、最新時点で8つも対象となるデータがある所です。より乖離率を元にした逆張りが有効ではないか!と期待している所です。

今日のアップしているインジケータで結構作り終えていますので、MT4プログラムの勉強をされている方は例題の代わりにチャレンジしてみてください^^


[MT4インジケータ]通貨の強弱をUSDを基準に表示する。その2
[MT4インジケータ]通貨の強弱をUSDを基準に表示する。その3
[MT4インジケータ]通貨の強弱をUSDを基準にその4とFX-ONへのアップロード


「MT4でFXを勝ち抜く研究をするブログ」で公開している無料インジケータは、こちらの一覧から。
インジケータ一覧

Twitterもよろしくお願いします。
https://twitter.com/mt4program
Trading View プロフィール

ブログランキングにご協力よろしくお願いします。m(._.)m
にほんブログ村 為替ブログ FX テクニカルトレード派へ
にほんブログ村

お約束ですが、本ブログは、投資に対する利益を約束する物ではありません。最終的には自己責任によるご判断よろしくお願いいたします。


ソースコードはこちら

2017年11月24日金曜日

[MT4プログラム]単純移動平均はループ不要

EAとかで単純移動平均を利用していてなるべく早く計算したい場合などがあります。
まぁMT4標準関数のiMAOnArrayがアホほど遅いというのもありますが・・。

単純移動平均は N期間のデータの合計をNで割った値です。
単純に実装するとループでN個分合計を求めて、最後にNで割ると行ったコードです。

         double total = 0.0;
         for( int j = i + 1; j < i + Period1 + 1; j++)
         {
            total += close[j];
         }
         sma1[i + 1] = total / Period1;

こんな感じですね。
ところが、すでに前のバーの値を計算済みの場合、こんなループは不要です。

移動平均の算出計算式を書き出すとこんな感じです。
sma[i] = (value[i] + value[i + 1] ..... + value[i + N - 1]) / N

これを分解すると次の形です。
sma[i] = value[i]/ N + value[i + 1]/ N ..... + value[i + N - 1]/ N

次のsma[i + 1]の値は、このようになります。
sma[i + 1] = value[i + 1]/ N + value[i + 2]/ N .....+ value[i + N - 1]/ N + value[i + N]/ N

さて、sma[i]とsma[i+1]の差分は次の通りです。
sma[i] - sma[i + 1] = value[ i ]/N - value[i + N]/N

両辺にsma[i + 1]を足せば、次の式となります。
sma[i] = sma[i + 1] + value[ i ]/N - value[i + N]/N

つまり、sma[i] は sma[i + 1]の値がわかっていれば、足し算と引き算と割り算で求めることが可能です。

で、終値の単純移動平均を出すプログラムはこんな感じなります。
ふと、どの記事か忘れましたが、移動平均ってループしなくても求められるんだ〜という返事を見た覚えがあったため、案外知られてないのかな?と思い記事にしてみました。


//------------------------------------------------------------------
// 単純移動平均
#property copyright "Copyright 2017,  Daisuke"
#property link      "http://mt4program.blogspot.jp/"
#property version   "1.00"
#property strict
#property indicator_chart_window

//バッファーを指定する。
#property indicator_buffers 1

//プロット数を指定する。
#property indicator_plots   1

#property indicator_label1  "SMA"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrAqua
#property indicator_style1  STYLE_SOLID
#property indicator_width1  1

input int Period1 = 21; // SMA1期間

double sma1[];

//------------------------------------------------------------------
//初期化
int OnInit()
{
   //インジケーターバッファを初期化する。
   int count = 0 ;
   SetIndexBuffer(count++, sma1);
   string short_name = "SMA";
   IndicatorShortName(short_name);

   return INIT_SUCCEEDED;
}

//------------------------------------------------------------------
//計算イベント
int OnCalculate(const int rates_total,          //各レート要素数
                const int prev_calculated,      //計算済み要素数
                const datetime &time[],         //要素ごとの時間配列
                const double &open[],           //オープン価格配列
                const double &high[],           //高値配列
                const double &low[],            //安値配列
                const double &close[],          //クローズ価格配列
                const long &tick_volume[],      //ティック数(要素の更新回数)
                const long &volume[],           //実ボリューム(?)
                const int &spread[])            //スプレット
{
   for( int i = rates_total - prev_calculated - 1; i >= 0  && !IsStopped(); i-- )
   {
      //計算する長さを算出
      if( i > rates_total - Period1 - 1)
      {
         continue;
      }
      
      if( MathIsValidNumber(sma1[i + 1]) )
      {
         //最初の一つ目はふつうにループして計算する。
         double total = 0.0;
         for( int j = i + 1; j < i + Period1 + 1; j++)
         {
            total += close[j];
         }
         sma1[i + 1] = total / Period1;
      }
      else
      {
         sma1[i] = sma1[i + 1] - close[i + Period1] / Period1 + close[i] / Period1;  
      }
   }
   
   return(rates_total - 1);
}
「MT4でFXを勝ち抜く研究をするブログ」で公開している無料インジケータは、こちらの一覧から。
インジケータ一覧

Twitterもよろしくお願いします。
https://twitter.com/mt4program
Trading View プロフィール

ブログランキングにご協力よろしくお願いします。m(._.)m
にほんブログ村 為替ブログ FX テクニカルトレード派へ
にほんブログ村

お約束ですが、本ブログは、投資に対する利益を約束する物ではありません。最終的には自己責任によるご判断よろしくお願いいたします。

2017年5月1日月曜日

[MT4プログラミング]ダブルクリックで指定時間のチャートをテンプレート付きで開く


グリッドの部分をダブルクリックすると、チャートを開いてくれるインジケータです。

複数通貨と時間軸のインジケータ状態を一挙表示するインジケータは結構ありますが、ダブルクリックするとチャートを開いてくれるやつってあまりないですよね?普段は閉じて置いてハーモニックが出たらダブルクリックですぐにチャート表示とかできたら便利かな?と思いできるかどうかのテストも兼ねて作ってみました。

項目の部分がダブルクリックされると、その項目のチャートをテンプレート付きで開きます。

さて、MQLのチャートイベントにはダブルクリック判定はありません。
そこで、チャート更新のClickイベントの時間を保持しておき、ある一定数以下ならダブルクリックと判定するような処理にしています。

//------------------------------------------------------------------
//チャート更新イベント
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_CLICK )
   {
      static uint beforeClick = 0;
      if( GetTickCount() - beforeClick < ChartDoubleClick )
      {
            //ダブルクリック時の処理
      }
      beforeClick = GetTickCount();
   }
}

チャートのオープンとテンプレートの設定は
ChartOpen関数とChartApplyTemplate関数で割と簡単に行えます。

注意が必要なのは通貨名です。基本的に通貨名は6文字で表されますがブローカーによっては、6文字の後ろに文字列が付く場合があるようです。
EURUSD-small
みたいな感じです。

対策としてチャート通貨名が6文字以上なら7文字目以降を開きたいチャートのお尻につけるという処理を行っています。この部分です。
         string postFix = Symbol();
         if( StringLen(postFix) > 6 )
         {
            postFix = StringSubstr(postFix, 6);
         }
         else
         {
            postFix = "";
         }
         
         //チャートを開く
         long chartId = ChartOpen(m_currency.At(row) + postFix, m_period.At(column));


//------------------------------------------------------------------
// ダブルクリックでチャートを開く

#property copyright "Copyright 2017,  Daisuke"
#property link      "http://mt4program.blogspot.jp/"
#property version   "1.00"
#property strict
#property indicator_chart_window

#include <Canvas/Canvas.mqh>
#include <Arrays/ArrayString.mqh>
#include <Arrays/ArrayInt.mqh>
#include <Controls/Button.mqh>

input string sep00 = "";      //【監視対象通貨】
input string cerrency01 = "USDJPY";     // 通貨01
input string cerrency02 = "EURUSD";     // 通貨02
input string cerrency03 = "EURJPY";     // 通貨03
input string cerrency04 = "GBPUSD";     // 通貨04
input string cerrency05 = "EURGBP";     // 通貨05
input string cerrency06 = "GBPJPY";     // 通貨06
input string cerrency07 = "AUDUSD";     // 通貨07
input string cerrency08 = "EURAUD";     // 通貨08
input string cerrency09 = "AUDJPY";     // 通貨09
input string cerrency10 = "USDCAD";     // 通貨10
input string cerrency11 = "EURCAD";     // 通貨11
input string cerrency12 = "USDCHF";     // 通貨12
input string cerrency13 = "EURCHF";     // 通貨13
input string cerrency14 = "";     // 通貨14
input string cerrency15 = "";     // 通貨15
input string cerrency16 = "";     // 通貨16
input string cerrency17 = "";     // 通貨17
input string cerrency18 = "";     // 通貨18
input string cerrency19 = "";     // 通貨19
input string cerrency20 = "";     // 通貨20

input string sep10 = "";      //【監視対象時間軸】
input bool targetM1 = false;          // M1
input bool targetM5 = true;           // M5
input bool targetM15 = true;          // M15
input bool targetM30 = true;          // M30
input bool targetH1 = true;           // H1
input bool targetH4 = true;           // H4
input bool targetD1 = true;           // D1
input bool targetW1 = false;          // W1
input bool targetMN1 = false;         // MN1

input string sep30 = "";      //【描画設定】
input int FontSize = 16;            // フォントサイズ
input int LabelColor = clrWhite;    // ラベル色
input int BackColor = clrBlack;     // 背景色
input int LineColor = clrGray;      // ライン色

input string sep40 = "";      //【チャートオープン設定】
input bool OpenDoubleClick = true;  // ダブルクリックでチャートを開く
input uint ChartDoubleClick = 250;  // ダブルクリック判定時間(ms)
input string TemplateName = "ADX.tpl";     // 定型チャート名

//オブジェクト名
#define OBJECT_NAME "OBJ_OPEN_CHART_"

// グローバル名
string m_globalName = IntegerToString(ChartID()) + "_OPEN_CHART_";

// キャンバス
CCanvas m_canvas;

//通貨一覧
CArrayString m_currency;

//時間一覧
CArrayInt m_period;

//列幅(文字数)
#define COLUMN_CHAR_SIZE 4

//------------------------------------------------------------------
// 初期化
int OnInit()
{
   //2重起動防止
   if( GlobalVariableCheck(m_globalName) )
   {
      Print("open chart double run.");
      return INIT_FAILED;
   }
   GlobalVariableSet(m_globalName, 1);

   ObjectsDeleteAll(ChartID(), 0);
   
   int chartWidth = (int)ChartGetInteger(ChartID(), CHART_WIDTH_IN_PIXELS, 0 );
   int chartHeight = (int)ChartGetInteger(ChartID(), CHART_HEIGHT_IN_PIXELS, 0);
   
   //固定ビットマップを生成する。
   m_canvas.CreateBitmapLabel(0, 0, OBJECT_NAME + "_BACK", 0, 0, chartWidth, chartHeight);
   ObjectSetInteger(0,OBJECT_NAME,OBJPROP_BACK,false); 
   m_canvas.FontSizeSet(10);
   m_canvas.FontAngleSet(0);

   //背景
   m_canvas.FillRectangle(0, 0, chartWidth, chartHeight, COLOR2RGB(clrBlack));

   //設定から通貨一覧と時間一覧を生成する。
   if( cerrency01 != "" ) m_currency.Add(cerrency01);
   if( cerrency02 != "" ) m_currency.Add(cerrency02);
   if( cerrency03 != "" ) m_currency.Add(cerrency03);
   if( cerrency04 != "" ) m_currency.Add(cerrency04);
   if( cerrency05 != "" ) m_currency.Add(cerrency05);
   if( cerrency06 != "" ) m_currency.Add(cerrency06);
   if( cerrency07 != "" ) m_currency.Add(cerrency07);
   if( cerrency08 != "" ) m_currency.Add(cerrency08);
   if( cerrency09 != "" ) m_currency.Add(cerrency09);
   if( cerrency10 != "" ) m_currency.Add(cerrency10);
   if( cerrency11 != "" ) m_currency.Add(cerrency11);
   if( cerrency12 != "" ) m_currency.Add(cerrency12);
   if( cerrency13 != "" ) m_currency.Add(cerrency13);
   if( cerrency14 != "" ) m_currency.Add(cerrency14);
   if( cerrency15 != "" ) m_currency.Add(cerrency15);
   if( cerrency16 != "" ) m_currency.Add(cerrency16);
   if( cerrency17 != "" ) m_currency.Add(cerrency17);
   if( cerrency18 != "" ) m_currency.Add(cerrency18);
   if( cerrency19 != "" ) m_currency.Add(cerrency19);
   if( cerrency20 != "" ) m_currency.Add(cerrency20);

   if( targetM1 ) m_period.Add(PERIOD_M1);
   if( targetM5 ) m_period.Add(PERIOD_M5);
   if( targetM15 ) m_period.Add(PERIOD_M15);
   if( targetM30 ) m_period.Add(PERIOD_M30);
   if( targetH1 ) m_period.Add(PERIOD_H1);
   if( targetH4 ) m_period.Add(PERIOD_H4);
   if( targetD1 ) m_period.Add(PERIOD_D1);
   if( targetW1 ) m_period.Add(PERIOD_W1);
   if( targetMN1 ) m_period.Add(PERIOD_MN1);
   
   if( m_currency.Total() == 0 || m_period.Total() == 0 ) return INIT_PARAMETERS_INCORRECT;
   
   return INIT_SUCCEEDED;
}

//------------------------------------------------------------------
//終了処理
void OnDeinit(const int reason)
{
   ObjectsDeleteAll(ChartID(), 0);
   GlobalVariableDel(m_globalName);
}

//------------------------------------------------------------------
//チャート更新イベント
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( OpenDoubleClick && id == CHARTEVENT_CLICK )
   {
      static uint beforeClick = 0;
      if( GetTickCount() - beforeClick < ChartDoubleClick )
      {
         int row, column;
         GetPointToCell((int)lparam, (int)dparam, row, column);
         
         string postFix = Symbol();
         if( StringLen(postFix) > 6 )
         {
            postFix = StringSubstr(postFix, 6);
         }
         else
         {
            postFix = "";
         }
         
         //チャートを開く
         long chartId = ChartOpen(m_currency.At(row) + postFix, m_period.At(column));
         
         //テンプレートを開く
         if(TemplateName != "" )
         {
            ChartApplyTemplate(chartId, TemplateName);
         } 
      }
      beforeClick = GetTickCount();
   }
   if( id == CHARTEVENT_CHART_CHANGE )
   {
      DrawCanvas();
   }
}

//------------------------------------------------------------------
//情報を描画する
void DrawCanvas()
{
   int chartWidth = (int)ChartGetInteger(ChartID(), CHART_WIDTH_IN_PIXELS, 0 );
   int chartHeight = (int)ChartGetInteger(ChartID(), CHART_HEIGHT_IN_PIXELS, 0);

   //固定ビットマップを生成する。
   m_canvas.CreateBitmapLabel(0, 0, OBJECT_NAME, 0, 0, chartWidth, chartHeight);
   ObjectSetInteger(0,OBJECT_NAME,OBJPROP_BACK, false); 
   m_canvas.FontSizeSet(FontSize);

   //背景
   m_canvas.FillRectangle(0, 0, chartWidth, chartHeight, COLOR2RGB(BackColor));

   int topMargin = FontSize;
   int leftMargin = FontSize / 2;
   int space = FontSize / 3;
   int fontHarf = FontSize / 2 + 1;
   int row = m_currency.Total();
   int column = m_period.Total();

   m_canvas.TextOut(leftMargin, topMargin, "Open chart " + TemplateName, COLOR2RGB(LabelColor));
   
   for( int i = 0 ; i < row; i++ )
   {
      int y = topMargin + (FontSize + space) * (i + 2);
      m_canvas.TextOut(leftMargin, y, m_currency.At(i), COLOR2RGB(LabelColor));
      m_canvas.Line(leftMargin, y - space / 2, leftMargin + fontHarf * 8 + column * fontHarf * COLUMN_CHAR_SIZE, y - space / 2, COLOR2RGB(LineColor));
   }

   for( int i = 0 ; i < column; i++ )
   {
      int x = leftMargin + fontHarf * 8 + fontHarf * COLUMN_CHAR_SIZE * i;
      m_canvas.TextOut(x, topMargin + (FontSize + space), PeriodToString(m_period.At(i)), COLOR2RGB(LabelColor));
      m_canvas.Line(x - space, topMargin + (FontSize + space), x - space, topMargin + (FontSize + space) * (row + 2), COLOR2RGB(LineColor));
   }
   
   m_canvas.Update();
}

//------------------------------------------------------------------
//座標をセルに変換する。
void GetPointToCell(
   int x,
   int y,
   int &row,
   int &column )
{
   row = -1;
   column = -1;

   int topMargin = FontSize;
   int leftMargin = FontSize / 2;
   int space = FontSize / 3;
   int fontHarf = FontSize / 2 + 1;
   int rowTotal = m_currency.Total();
   int columnTotal = m_period.Total();

   int top = topMargin + (FontSize + space) * 2;
   int bottom = topMargin + (FontSize + space) * (rowTotal + 3);
   int left = leftMargin + fontHarf * 8;
   int right = leftMargin + fontHarf * 8 + fontHarf * COLUMN_CHAR_SIZE * (columnTotal + 1);
   
   if( left <= x && x <= right && top <= y && y <= bottom )
   {
      //行列の計算
      row = (y - top) / (FontSize + space);
      column = (x - left ) / (fontHarf * 4);
   }
}

//------------------------------------------------------------------
//計算イベント
int OnCalculate(
   const int rates_total,          //各レート要素数
 const int prev_calculated,      //計算済み要素数
 const datetime &time[],         //要素ごとの時間配列
 const double &open[],           //オープン価格配列
 const double &high[],           //高値配列
 const double &low[],            //安値配列
 const double &close[],          //クローズ価格配列
 const long &tick_volume[],      //ティック数(要素の更新回数)
 const long &volume[],           //実ボリューム(?)
 const int &spread[])            //スプレット
{

   return rates_total;
}

//------------------------------------------------------------------
//期間を文字列に変更する
string PeriodToString(int timeframe)
{
   if( timeframe == 0 )
   {
      timeframe = Period();
   }
   switch(timeframe)
   {
      case PERIOD_M1:
         return "M1";
      case PERIOD_M5:
         return "M5";
      case PERIOD_M15:
         return "M15";
      case PERIOD_M30:
         return "M30";
      case PERIOD_H1:
         return "H1";
      case PERIOD_H4:
         return "H4";
      case PERIOD_D1:
         return "D1";
      case PERIOD_W1:
         return "W1";
      case PERIOD_MN1:
         return "MN1";
   }
   return "";
}


「MT4でFXを勝ち抜く研究をするブログ」で公開している無料インジケータは、こちらの一覧から。
インジケータ一覧

Twitterもよろしくお願いします。
https://twitter.com/mt4program

ブログランキングにご協力よろしくお願いします。m(._.)m
にほんブログ村 為替ブログ FX テクニカルトレード派へ
にほんブログ村

お約束ですが、本ブログは、投資に対する利益を約束する物ではありません。最終的には自己責任によるご判断よろしくお願いいたします。

2017年3月31日金曜日

[MT4プログラミング]MT4のバグ??iCustomを2回コールしないと最新値が取得できない。

EA開発中です。

毎Tickとか動かしているといつまでたってもテストが終わらないため、M5の確定足で動作するような設計にしています。

で、自作のインジケータをiCustomで呼び出しているのですが、1回目は正しい値が返ってきません。2回目だと帰ってきます。

・・・・・。なんで??
以前こんなこともありましたが、これと同じように毎Tickコールしないと正しく値がとれないのでしょうか??
[MT4プログラム]EA内でマルチタイムフレームを見る場合、常時取得しないと値がずれる

うーん。

ちなみにNGのコードはこれ
      buySell = CallHarmonic(HM_BUYSELL, shift);
      if( buySell != EMPTY_VALUE )
      {

値が取れるコードはコレ
      buySell = CallHarmonic(HM_BUYSELL, shift);
      buySell = CallHarmonic(HM_BUYSELL, shift);
      if( buySell != EMPTY_VALUE )
      {

(--#
血管浮き出そうです。

とりあえず、たまにHarmonicインジケータを呼び出すときに2回呼び出さないといけないということはわかりました。

呪詛の言葉を吐きそうです。

サンプルコードも見直します。
バックテストの遅さは無視して全Tickコールするしかなさそうです。

「MT4でFXを勝ち抜く研究をするブログ」で公開している無料インジケータは、こちらの一覧から。
インジケータ一覧

Twitterもよろしくお願いします。
https://twitter.com/mt4program

ブログランキングにご協力よろしくお願いします。m(._.)m
にほんブログ村 為替ブログ FX テクニカルトレード派へ
にほんブログ村

お約束ですが、本ブログは、投資に対する利益を約束する物ではありません。最終的には自己責任によるご判断よろしくお願いいたします。

2017年3月15日水曜日

[MT4プログラミング]強制的にTickイベントを発生させるスクリプト

需要があるかどうかはわかりませんが、インジケータなどで、Tickにかかわらず強制的に処理をさせたいという事があるかもしれません。

通常OnCalculate関数は、Tick更新時にのみコールされますが、WindowsのMT4であれば、スクリプトを使用して強制的にWindowMessageを送信することで、Tickイベントを発生させることが可能です。

EAであれば、OnTimer関数など別のやり方がありますが、スクリプトにしておくと、どんなEAやインジケータでも強制的に更新させることが可能という利点があります。

チャート上に何か出てくるようなスクリプトじゃないので、画像はありません。
定時に動作するようなEAでTick更新がなかったがため、ポジションがクローズされないなどの不具合がありそうなやつに有効かも?

//------------------------------------------------------------------
// 強制的に1秒ごとにTickを発生させる
#property copyright "Copyright 2015,  Daisuke"
#property link      "http://mt4program.blogspot.jp/"
#property version   "1.00"
#property strict
#property strict

//------------------------------------------------------------------
// メッセージ送信API
#import "user32.dll"
int PostMessageA(int handle,  uint Msg, uint wParam,  uint lParam );
#import

#define CHART_CMD_UPDATE_DATA 33324
#define WM_COMMAND 0x0111
//------------------------------------------------------------------
// スクリプト本体
void OnStart()
{
   if( !IsDllsAllowed() )
   {
      MessageBox("DLL使用が許可されていません");
      return ;
   }
   
   int handle = WindowHandle(Symbol(), Period());
   while(!IsStopped())
   {
      if( IsConnected() )
      {
         //1秒毎にメッセージを送信する。
         PostMessageA(handle, WM_COMMAND, CHART_CMD_UPDATE_DATA, 1);
      }
      Sleep(1000);
   }
}




「MT4でFXを勝ち抜く研究をするブログ」で公開している無料インジケータは、こちらの一覧から。
インジケータ一覧

Twitterもよろしくお願いします。
https://twitter.com/mt4program

ブログランキングにご協力よろしくお願いします。m(._.)m
にほんブログ村 為替ブログ FX テクニカルトレード派へ
にほんブログ村

お約束ですが、本ブログは、投資に対する利益を約束する物ではありません。最終的には自己責任によるご判断よろしくお願いいたします。

2017年2月22日水曜日

[雑記]プログラマの単価

熱出して寝込んでいる、だいすけです。

明日から客先出張なのですが、それまでになんとかしないと・・・栄養ドリンクと解熱剤で今日中になんとかすべく布団の中に潜り込んでいます。

まぁ、こんなブログを書いていると、FX-ONにて、たまにこんなプログラムを作ってくれませんか?1万円で。

というご依頼があります。

皆様のソフトウェアのイメージだと、大体1万円ぐらいが相場ですので、そのようにおっしゃるのだろうと思います。

ただ、この価格は開発に関する費用を、購入人数で割っている為、実現可能な値段です。

では、プログラムにかかる人件費はどの程度でしょうか?
これは、都市部と地方でかなり異なるのですが、ざっくり行くと1日分の作業で最低3万円です。

ですので、3万円でこの程度のプログラム作れます?っていわれると、あぁこの人1日分の作業依頼してきているのだなぁと感じます。

ハーモニックパターン検出インジケータの場合、どうでしょうか?
大体開発に20日、改造に20日程度かけています。大体120万ぐらいかかっているイメージですね。これを100人程度の方に買っていただける前提で18000円前後の値付けをしています。18000円のうち、宣伝費とFX-ON手数料として35%ぐらいとられますので、100人に売れてやっとトントンといったところです。

お金のイメージついていただけたでしょうか?

ですので、プログラマさんにお願いする時に自分がほしいものを素直にお金を出して作ってもらうのは、個人感覚では、なかなか単価が合いません。

その為、こーいうインジケータがあると売れると思うんだけどどう?あったら買うよとか、こーいうデータくれたら解析結果渡すよみたいな提案の方がじゃぁやっていようかな?ってなること多いです。

あまり落ちのない話ですが、熱のせいってことで。それでは皆様おやすみなさい・・。

「MT4でFXを勝ち抜く研究をするブログ」で公開している無料インジケータは、こちらの一覧から。
インジケータ一覧

Twitterもよろしくお願いします。
https://twitter.com/mt4program

ブログランキングにご協力よろしくお願いします。m(._.)m
にほんブログ村 為替ブログ FX テクニカルトレード派へ
にほんブログ村

お約束ですが、本ブログは、投資に対する利益を約束する物ではありません。最終的には自己責任によるご判断よろしくお願いいたします。

2017年2月18日土曜日

[MT4プログラム]小ネタ、どうしようもなく遅いCObjectArrayを少しでも高速化

注意!
2017/05/16 この手法はMT4 Build1080から使えないようです。

ハーモニックパターン検出インジケータの中で、CObjectArrayを使用しているのですが、
この子があちこちどうしようもなく遅いです。

少しでも早くということで、コードを改造しています。

MQLについているCArrayシリーズのSearchメソッドですが、内部でQuickSearchという配列がソート済みであることを前提とした検索を行っていて、未ソート状態だと、必ず未発見を返してくるというふざけた仕様です。
(ソート済み→QuickSearch、未ソート→全量探査じゃね??)

まぁ試してみた所、試行回数が多い場合、
通常のAdd+全量探査
InsertSort+QuickSearch
だと、後者の方が多少早いようです。

InsertSortは、ソート状態を維持するデータ追加メソッドですが、この子も遅いです。

中ではInsertメソッドを呼び出しています。
MQLでは配列のブロック移動を行うメソッドがなく、ループで回して要素ごとにコピーしているわけですが、その際移動先のポインタが生きている場合Deleteするといったチェック処理が含まれています。
Insert処理ですので、配列を一個拡張してデータが入っていない所へのコピーですから、絶対データがない前提でOKのはずです。

ということで、少しだけ改造してみた結果です。

元コード
//+------------------------------------------------------------------+
//| Inserting an element in the specified position                   |
//+------------------------------------------------------------------+
bool CArrayObj::Insert(CObject *element,const int pos)
  {
//--- check
   if(pos<0 || !CheckPointer(element))
      return(false);
//--- check/reserve elements of array
   if(!Reserve(1))
      return(false);
//--- insert
   m_data_total++;
   if(pos<m_data_total-1)
     {
      MemMove(pos+1,pos,m_data_total-pos-1);
      m_data[pos]=element;
     }
   else
      m_data[m_data_total-1]=element;
   m_sort_mode=-1;
//--- successful
   return(true);
  }


修正後コード
//+------------------------------------------------------------------+
//| Inserting an element in the specified position                   |
//+------------------------------------------------------------------+
bool CCustomArrayObj::Insert(CObject *element,const int pos)
  {
//--- check
   if(pos<0 || !CheckPointer(element))
      return(false);
//--- check/reserve elements of array
   if(!Reserve(1))
      return(false);
//--- insert
   m_data_total++;
   if(pos < m_data_total - 1)
   {
      //--- copy from left to right
      //修正箇所
      int total = m_data_total - pos - 1;
      for(int i = total - 1; i >= 0; i--)
      {
         m_data[pos + i + 1] = m_data[pos + i];
      }
//      MemMove(pos+1,pos,m_data_total - pos - 1);
      m_data[pos]=element;
   }
   else
      m_data[m_data_total-1]=element;
   m_sort_mode=-1;
//--- successful
   return(true);
  }

修正前のメタエディタプロファイリング結果

修正後のメタエディタプロファイリング結果

大体60%ぐらいに処理時間が減りました。
・・メソッド名から想像がつくかと思いますがV9です。苦闘中です。なるべく消えないように探査を増やしたいのですが、ものすごく重くなってしまったため、メタエディタのプロファイリングとにらめっこしながら、遅いところを虱潰ししてます・・・。

CListを使えっていう話もあるのですが、こちらにはInsertSortメソッド無いのですよねぇ。多分Listだとソート処理が遅いんだろうな。インデックス位置を追いかけるのにカレントポインタを移動しなくちゃいかんから・・・。

自分だけで使うなら、もうC#で組んでそうです(笑)

実はその2に続く。
http://mt4program.blogspot.jp/2017/02/mt4cobjectarray2.html


「MT4でFXを勝ち抜く研究をするブログ」で公開している無料インジケータは、こちらの一覧から。
インジケータ一覧

Twitterもよろしくお願いします。
https://twitter.com/mt4program

ブログランキングにご協力よろしくお願いします。m(._.)m
にほんブログ村 為替ブログ FX テクニカルトレード派へ
にほんブログ村

お約束ですが、本ブログは、投資に対する利益を約束する物ではありません。最終的には自己責任によるご判断よろしくお願いいたします。

2017年1月26日木曜日

[MT4インジケータ]EAにとってTick値は重要そう・・。 TickEMAをちょっとだけカスタマイズ。TickEMA2

4時間足以下のチャートにおいて、朝の薄い時間帯の1本と、激しいEU時間帯の1本の価値が同じというのは疑問に思っていました。

EA Gewinn7/8がTick値を使った勢い判定によるエントリー判断をしていますが、Tick値の配信回数によって成績が結構異なるということは、逆説的にTick値には大きな利用価値があるということを証明できた気がします。

ということで以前EMAをベースにTickによる平滑化パラメータを動的に変更するインジケータを作成していますが、ちょっともっと簡単に考え方をいろいろ応用できる形で書き換えたいと思います。

■黄色TickEMA2(21) 赤EMA(21)



考え方を単純にして、平滑化指数をTickからのみ生成しています。

Tick値が伴っているときには過敏に、伴っていないときには緩やかに動くのはTickEMAと変わりありません。

ターゲットラインをベースとしたEAの場合、ライン付近で微振動を起こすと微妙なところでとったりとらなかったりします。かといって平滑化をすると、鈍くなりすぎたり、薄い時間帯での急変で変なポジションを保持したりします。
Tick値を見ることで、平滑化と即応性を両立することを目的にしています。

ちなみに、このインジケータですが、次のEAで移動平均期間の値を2とかにして、ノイズフィルタ替わりに使う予定です。
後乖離率をごにょごにょすると、スキャ系EAのネタになるかなぁ・・・。今後の検証課題です。


2016年8月4日木曜日

[MT4プログラム]定型チャート対策、オブジェクトを生成するインジケータは要注意!

たまにはプログラムネタを書かないと!

MT4には定型チャートという、オブジェクトやインジケータ設定を復元する機能があります。

■定型チャート

チャートの右クリックなどから呼び出せます。

便利な機能なのですが、インジケータ開発者には少し注意が必要です。
この機能、オブジェクトも復元してしまうため、チャート上にオブジェクトを生成するようなインジケータでは、想定していない古いオブジェクトが残った状態になります。

インジケータ内で作成したオブジェクトを管理しているようなものだと、オブジェクトと管理情報が結びつかず正しく動作しないという現象が発生します。

特にCButtonなど、Controlsのクラス群を使用する場合は要注意です。

販売中のハーモニックパターン検出インジケータでは、チャート上のボタンをしても反応しないという症状が発生しました。

この対策として、OnInit時にインジケータで作成すると思われるオブジェクトをいったん削除するコードを入れます。
たとえば、CButtonを使用する場合は、次の形になります。

CButton btnTest;
int OnInit()
{
   ObjectDelete(0, "CTL_BTN_TEST");
   btnTest.FontSize(8);
   btnTest.Text("Test");
   btnTest.Create(ChartID(), "CTL_BTN_TEST", 0, 10, 20, 50, 50);
   
   return(INIT_SUCCEEDED);
}

ただ、これだと、仮にCButtonの中で複数オブジェクトを生成する場合などは、正しく動作しません。MQLのコントロールクラスは、指定された名前+付加文字列というルールでオブジェクトを作成します。

あらかじめインジケータで使用するオブジェクト名の頭の部分を決めて置き、一致するものをすべて削除するという対応を行います。こんな感じです。

#define OBJECT_NAME "OBJ_TEST_"

// テストボタン
CButton btnTest;

//------------------------------------------------------------------
//初期化
int OnInit()
{
   //チャートに残っているかもしれない古いオブジェクトを全削除する
   ClearObjects();
   
   btnTest.FontSize(8);
   btnTest.Text("Test");
   btnTest.Create(ChartID(), OBJECT_NAME + "BTN_TEST", 0, 10, 20, 50, 50);
   
   return(INIT_SUCCEEDED);
}
  
//------------------------------------------------------------------
//オブジェクト全削除
void ClearObjects()
{
   int total = ObjectsTotal();
   //生成したオブジェクトを削除する。
   //0から削除するとインデックス位置がずれて
   //正しく削除できないため、後ろから削除するようにする。
   for( int i = total - 1; i >= 0 ; i--)
   {
      string name = ObjectName( i );
      
      // 先頭文字列が一致する場合、削除する。
      if ( StringFind( name, OBJECT_NAME ) == 0 )
      {
         ObjectDelete( name );
      }
   }   
}


「MT4でFXを勝ち抜く研究をするブログ」で公開している無料インジケータは、こちらの一覧から。
インジケータ一覧

Twitterもよろしくお願いします。
https://twitter.com/mt4program

ブログランキングにご協力よろしくお願いします。m(._.)m
にほんブログ村 為替ブログ MetaTraderへ
にほんブログ村

お約束ですが、本ブログは、投資に対する利益を約束する物ではありません。最終的には自己責任によるご判断よろしくお願いいたします。

2016年7月25日月曜日

[MT4プログラム]EA内でマルチタイムフレームを見る場合、常時取得しないと値がずれる

Gewinn8のコミュニティで、おかしな動き(SELLしかしない)をするという報告がありました。

直接的には、その現象とは関係ないのですが、調査している途中に、不思議な現象を確認しました。

たとえば、5分足でEAを動かしている際、足の期間の違う3つの最新足のClose値を見るとします。

double closeM5 = iClose(NULL, PERIOD_M5, 0);
double closeH1 = iClose(NULL, PERIOD_H1, 0);
double closeD1 = iClose(NULL, PERIOD_D1, 0);

時間足が異なろうが、最新足のクローズ値は基本変わりません。
現在価格が、見る足によって変わったら困りますよね?

・・・・・。この困る現象が発生します(滝汗)

例えば、このプログラムは5分に一回だけ動くEAだとします。M5に新しい足が追加された時だけ動くような形になっている感じです。

その時1時間足、1日足をとると、古い値が帰ってくるのです。

具体的には、次のコードで、if文の中に入ってきてしまいます。
   double closeM5 = iClose(NULL, PERIOD_M5, 0 );
   double closeH1 = iClose(NULL, PERIOD_H1, 0 );
   double closeD1 = iClose(NULL, PERIOD_D1, 0 );
   if( closeM5 != closeH1 || closeM5 != closeD1 )
   {
      PrintFormat("miss match %f %f %f", closeM5, closeH1, closeD1);
   }

ここからは、動作から見た推測になります。
MT4は、価格情報をサーバーから取得しますが、表示していないチャート時間足毎の情報までは構築していません。

iCloseなどの問い合わせがあって、初めて構築します。その際、MT4はいったん古い情報を返した後、改めて内部で構築、サーバーから再取得を行っているようです。

いったん、iCloseがその時間足で呼ばれると、しばらくは取得していますが、ある程度呼び出しがないと再び取得を停止するようです。

そのため、この症状を解消するには、とにかくTickの発生の度にすべての価格情報をいったん読み取る必要がありそうです。

■症状が発生するコード
//------------------------------------------------------------------
// 気配値表示処理   
void OnTick()
{
   //5分に一回だけ動作する
   static datetime lastUpdate = 0 ;
   datetime current = iTime(Symbol(), PERIOD_M5, 0);
   if( lastUpdate == current ) return ;
   lastUpdate = current;

   //ヒストリカルデータは取得する。
   double closeM5 = iClose(NULL, PERIOD_M5, 0 );
   double closeH1 = iClose(NULL, PERIOD_H1, 0 );
   double closeD1 = iClose(NULL, PERIOD_D1, 0 );
   if( closeM5 != closeH1 || closeM5 != closeD1 )
   {
      PrintFormat("miss match %f %f %f", closeM5, closeH1, closeD1);
   }

■症状が発生しないコード
//------------------------------------------------------------------
// 気配値表示処理   
void OnTick()
{
   //ヒストリカルデータは取得する。
   double closeM5 = iClose(NULL, PERIOD_M5, 0 );
   double closeH1 = iClose(NULL, PERIOD_H1, 0 );
   double closeD1 = iClose(NULL, PERIOD_D1, 0 );
   if( closeM5 != closeH1 || closeM5 != closeD1 )
   {
      PrintFormat("miss match %f %f %f", closeM5, closeH1, closeD1);
   }

   //5分に一回だけ動作する
   static datetime lastUpdate = 0 ;
   datetime current = iTime(Symbol(), PERIOD_M5, 0);
   if( lastUpdate == current ) return ;
   lastUpdate = current;



この現象は、マルチタイムフレームだけではなく、多通貨を同時に監視するようなEAにも同じことが言えます。
まとめると下記の通りです。

1.最新値が読めるのは、EAが挿入されているチャートの値のみ。

2.他の通貨や時間は最新値ではない。

3.一回iCloseなどで価格を取りに行くと、そのタイミングで最新値を構築する。
 ただし、その際、iCloseはいったん古いデータを返却する。

4.一度最新値を構築すると、しばらく(1分ぐらい?)は構築し続ける。

ということで、対策は、処理の頭で、使う時間軸と通貨のiCloseを読み込み続けるということになるかと思います。

Gewinn8のように、5分一回しか動作しないし、数秒ずれたところで・・・というEAでは特に問題ないかもしれませんが、スキャル系に1秒を争う場合はちょっと許容しがたい処理遅れになるかしれませんね。このあたりは、処理内容で、価格チェックをする場合と、発注処理をする場合とで切り分けるなど、工夫が必要そうです。

「MT4でFXを勝ち抜く研究をするブログ」で公開している無料インジケータは、こちらの一覧から。
インジケータ一覧

Twitterもよろしくお願いします。
https://twitter.com/mt4program

ブログランキングにご協力よろしくお願いします。m(._.)m
にほんブログ村 為替ブログ MetaTraderへ
にほんブログ村

お約束ですが、本ブログは、投資に対する利益を約束する物ではありません。最終的には自己責任によるご判断よろしくお願いいたします。

2016年6月23日木曜日

[MT4プログラム] 小ネタ チャートに垂直線、水平線、トレンドラインではない、縦線、横線、斜め線を引く

昨日フィボナッチリトレースメントラインインジケータをリリースしました。

■フィボナッチリトレースメントライン一挙表示

980円で販売中~
過去にさかのぼって、フィボナッチを一挙表示でタイミングをつかむ
複数頂点のフィボナッチリトレースメント一挙表示


MT4プログラムをやっている人は、このインジケータをみて、あれー?とおもったかもしれません。

というのもMT4の水平線、垂直線、トレンドラインは、基本チャート幅いっぱいに引かれてしまいます。

■MT4に垂直線、水平線、トレンドラインを引いた場合

リリースしたインジケータのようにある決まった時間/価格の範囲だけに線をひくというオブジェクトは用意されていません。
私も最初やれないのだろうなぁと思っていたら、海外のインジケータで実現しているものがあり、あれ?これ何とかできる???と思い調べてみました。

■MT4 一部分だけ、線を引きたい・・・・。

結論から行くと、四角形と三角形を工夫すると、描画可能です。

まず、OBJPROP_BACKプロパティをFalseにして外形ライン描画にします。
上記のオブジェクトに対して四角形の始点終点の価格を同じにすると、横線。
時間を同じにすると縦線

三角形の2点目3点目を同一の点にすると斜め線が描画できます。

サンプルプログラムはこんな感じになります。

//------------------------------------------------------------------
// 横線・縦線・斜め線

#property copyright "Copyright 2016,  Daisuke"
#property link      "http://mt4program.blogspot.jp/"
#property version   "1.00"
#property strict
#property indicator_chart_window

#define OBJECT_NAME "OBJ_DRAW_LINE"

//------------------------------------------------------------------
//開始処理
int OnInit()
{
   return(INIT_SUCCEEDED);
}
  
//------------------------------------------------------------------
//終了処理
void OnDeinit( const int reason )
{
   ClearObject();
}

//------------------------------------------------------------------
//計算処理
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
{
   ClearObject();

   long chartId = ChartID();
   
   //横線、OBJ_RECTANGLEにおいて、始点終点価格が同一で、OBJPROP_BACK=falseにすると横線になる。
   string name = OBJECT_NAME + "_HLINE";
   if ( ObjectCreate( chartId, name, OBJ_RECTANGLE, 0, time[0], high[0], time[20], high[0] ) == false )
   {
      return false;
   }
   ObjectSetInteger( chartId, name, OBJPROP_COLOR, clrRed );
   ObjectSetInteger( chartId, name, OBJPROP_BACK, false );

   //縦線、OBJ_RECTANGLEにおいて、始点終点時間が同一で、OBJPROP_BACK=falseにすると縦線になる。
   name = OBJECT_NAME + "_VLINE";
   if ( ObjectCreate( chartId, name, OBJ_RECTANGLE, 0, time[1], low[iLowest(NULL,0, MODE_LOW, 20)], time[1], high[iHighest(NULL, 0, MODE_HIGH, 20)] ) == false )
   {
      return false;
   }
   ObjectSetInteger( chartId, name, OBJPROP_COLOR, clrRed );
   ObjectSetInteger( chartId, name, OBJPROP_BACK, false );

   //斜め線、OBJ_TRIANGLEにおいて、2点目と3点目が同一、時間価格でOBJPROP_BACK=falseにすると斜め線になる。
   name = OBJECT_NAME + "_ANGLELINE";
   if ( ObjectCreate( chartId, name, OBJ_TRIANGLE, 0, time[20], low[20], time[1], high[1], time[1], high[1] ) == false )
   {
      return false;
   }
   ObjectSetInteger( chartId, name, OBJPROP_COLOR, clrRed );
   ObjectSetInteger( chartId, name, OBJPROP_BACK, false );

   return(rates_total);
}

//------------------------------------------------------------------
//オブジェクトの全削除
void ClearObject()
{
   long chartId = ChartID();

   int total = ObjectsTotal( chartId );
   //生成したオブジェクトを削除する。
   //0から削除するとインデックス位置がずれて
   //正しく削除できないため、後ろから削除するようにする。
   for( int i = total - 1; i >= 0 ; i--)
   {
      string name = ObjectName( chartId, i );
      
      // 先頭文字列がRangeRectangleNameと一致する場合、削除する。
      if ( StringFind( name, OBJECT_NAME ) == 0 )
      {
         ObjectDelete( chartId, name );
      }
   }
}


これができると、インジケータ表現の幅が広がります。ぜひ皆さまもお試しください。

「MT4でFXを勝ち抜く研究をするブログ」で公開している無料インジケータは、こちらの一覧から。
インジケータ一覧

Twitterもよろしくお願いします。
https://twitter.com/mt4program

ブログランキングにご協力よろしくお願いします。m(._.)m
にほんブログ村 為替ブログ MetaTraderへ
にほんブログ村

お約束ですが、本ブログは、投資に対する利益を約束する物ではありません。最終的には自己責任によるご判断よろしくお願いいたします。