English Русский 中文 Español Deutsch Português
preview
キャンバスベースのインジケーター:チャネル内を透明にする

キャンバスベースのインジケーター:チャネル内を透明にする

MetaTrader 5 | 24 5月 2023, 10:06
232 0
Samuel Manoel De Souza
Samuel Manoel De Souza

はじめに

この記事では、標準ライブラリのCCanvasクラスを使用して描画されるカスタムインジケーターを作成する方法を紹介します。特に、2本の線の間の領域を単色で塗りつぶす必要があるインジケーターに取り組みます。開始する前に、この種のインジケーターで現在利用可能なオプションよりもキャンバスを使用することが最良の選択である理由を確認します。その後、座標を計算するために必要ないくつかのチャートプロパティと、CCanvasの操作を含む基本的なプロセスについて説明します。

最終的な目標は、これまでに見たすべてを使用して、透明性を適用したインジケーターを構築することです。すべての作業では、メインチャートウィンドウのみを考慮します。目標が達成されたら、それをサブウィンドウインジケーターでの作業に拡張できます。

この記事のトピックは次のとおりです。

キャンバスを使用する理由

カスタムインジケーターで既に利用可能なDRAW_FILLINGの代わりにキャンバスを使用するのには、少なくとも2つの理由があります。

  1. インジケーターの色が他のインジケーター、ローソク足、チャートオブジェクトの色と混ざっている
  2. DRAW_FILLINGでは透明を使用できない

2つのインジケーターと1つのオブジェクトを含むチャート

チャートウィンドウのプロパティ

カスタムチャートの描画を開始するには、いくつかのチャートプロパティについて考える必要があります。すべてのプロパティはドキュメントにあります。これらのプロパティ値を取得するには、適切なChartGetInteger関数とChartGetDouble関数を使用する必要があります。ChartGetStringもありますが、ここでは扱いません。

次は、使用するプロパティと簡単な説明です。さらに必要な場合は、後で指摘します。

  • CHART_WIDTH_IN_PIXELS:価格スケールを含まないチャートウィンドウの幅
  • CHART_HEIGTH_IN_PIXELS:日付スケールを含まないサブウィンドウの高さ
  • CHART_PRICE_MIN:サブウィンドウの上部に対応する価格
  • CHART_PRICE_MAX:サブウィンドウの下部に対応する価格
  • CHART_SCALE:バー間の間隔(いくらかテストした後、値がpow(2,CHART_SCALE)によって与えられる2のべき乗であることを発見しました)
  • CHART_FISRT_VISIBLE_BAR:チャートの最初に表示されるバー(左から右)
  • CHART_VISIBLE_BARS:チャートに表示されるバーの量

チャートウィンドウのプロパティについて

これらのプロパティは、次の図で簡単に確認できます。

座標に関連するチャートプロパティ

使用するCHART_WIDTH_IN_PIXELSプロパティとCHART_HEIGTH_IN_PIXELSプロパティは、描画のために作成する必要があるキャンバスオブジェクトのサイズを決定します。チャートウィンドウが変更されてこれらのプロパティが変更された場合は、キャンバスサイズを調整する必要があります。

理解を深めるために、プロパティと価格の変化とユーザーの操作に基づいてプロパティがどのように変化するかを示す簡単なインジケーターを作成します。キャンバスの描画プロセスを理解するために、既にキャンバスの使用を開始します。


チャートプロパティビューアインジケーター

この時点で、読者がカスタムインジケーターの作成方法を既にご存じであるることを前提としていますが、ご存じでない場合は、まず、「MQL5:自分のインジケーターの作成」稿と「多色ローソク足を作成するためのオプションの探究」稿をご覧ください。始めましょう。

このパスにインジケーターを作成しました。整理目的で同じにすることをお勧めします。

インジケーターの骨組みの準備ができたら、CCanvasライブラリをファイルに追加します。それには、#includeディレクティブを使用します。

次に、CCanvasクラスのインスタンスを作成します。これらはすべて、インジケーターの#propertyディレクティブの直後にあります。

#property copyright "Copyright 2023, Samuel Manoel De Souza"
#property link      "https://www.mql5.com/ja/users/samuelmnl"
#property version   "1.00"
#property indicator_chart_window

#include <Canvas/Canvas.mqh>
CCanvas Canvas;

CCanvasを使用する際にまずしなければいけないのは、OBJ_BITMAP_LABELを作成し、それにリソースをリンクすることです。これはチャートに追加したい場合に必要です。通常はインジケーターの初期化CreateBitampLabel(...)メソッドを使用します。最後に、OBJ_BITMAP_LABELとそれに関連付けられているリソースを削除します。チャートから削除したい場合、通常はインジケーターの初期化解除で、Destory(void)メソッドを使用します。それまでの間、基本的な描画プロセスを実行します。これは、描画の消去(リソースのピクセル値をクリアするまたはデフォルトのピクセル値を設定する)、描画の作成、およびリソースの更新で構成されます。キャンバスプロセスの完全なライフサイクルは、次の図のようになります。

canvas_process

簡単にするために、Erase、Draw、UpdateをRedrawという単一の関数に保持します。コードにすべてを書くと、次の構造が得られます。

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
   Canvas.CreateBitmapLabel(0, 0, "Canvas", 0, 0, 200, 150, COLOR_FORMAT_ARGB_NORMALIZE);
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   Canvas.Destroy();
  }
//+------------------------------------------------------------------+
//| Custom indicator redraw function                                 |
//+------------------------------------------------------------------+
void Redraw(void)
  {
   uint default_color = ColorToARGB(clrBlack);
   uint text_color = ColorToARGB(clrWhite);
//--- canvas erase
   Canvas.Erase(default_color);
//--- add first draw

//--- add second draw

//--- add ... draw

//--- add last draw

//--- canvas update
   Canvas.Update();
  }

プロパティを表示するには、TextOutメソッドを使用して書き込みます。これらのプロパティ値は、structarray変数に文字列として格納されます。 

struct StrProperty
  {
   string name;
   string value;
  };
構造体は次のようになります。次に、それらをループで簡単に出力できます。まだ配列がないので、配列をRedraw関数のパラメータとして渡します。Redraw関数は次のようになります。
void Redraw(StrProperty &array[])
  {
   uint default_color = ColorToARGB(clrBlack);
   uint text_color = ColorToARGB(clrWhite);
//--- canvas erase
   Canvas.Erase(default_color);
//--- add first draw
   int total = ArraySize(array);
   for(int i=0;i<total;i++)
     {
      int padding = 2;
      int left = padding, right = Canvas.Width() - padding, y = i * 20 + padding;
      Canvas.TextOut(left, y, array[i].name, text_color, TA_LEFT);
      Canvas.TextOut(right, y, array[i].value, text_color, TA_RIGHT);
     }
//--- canvas update
   Canvas.Update();
  }
最後に、プロパティ値を取得して出力できます。コードにOnChartEvent関数ハンドラがない場合は、追加します。そこで、CHARTEVENT_CHART_CHANGEイベントIDを確認します。イベントが発生したら、いくつかの変数を宣言してプロパティ値を取得し、それらを構造体配列に渡し、Redraw関数を呼び出します。それで完成です。インジケーターをコンパイルしてチャートに追加し、チャートを操作してキャンバスの更新を確認できます。
//+------------------------------------------------------------------+
//| Custom indicator chart event handler function                    |
//+------------------------------------------------------------------+
void OnChartEvent(const int id, const long& lparam, const double& dparam, const string& sparam)
  {
   if(id != CHARTEVENT_CHART_CHANGE)
      return;
   int chart_width         = (int)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS);
   int chart_height        = (int)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS);
   int chart_scale         = (int)ChartGetInteger(0, CHART_SCALE);
   int chart_first_vis_bar = (int)ChartGetInteger(0, CHART_FIRST_VISIBLE_BAR);
   int chart_vis_bars      = (int)ChartGetInteger(0, CHART_VISIBLE_BARS);
   double chart_prcmin     = ChartGetDouble(0, CHART_PRICE_MIN);
   double chart_prcmax     = ChartGetDouble(0, CHART_PRICE_MAX);
//---
   StrProperty array[]
     {
        {"Width", (string)chart_width},
        {"Height", (string)chart_height},
        {"Scale", (string)chart_scale},
        {"First Vis. Bar", (string)chart_first_vis_bar},
        {"Visible Bars", (string)chart_vis_bars},
        {"Price Min", (string)chart_prcmin},
        {"Price Max", (string)chart_prcmax},
     };
   Redraw(array);
  }

座標変換

この時点で、日時またはバーインデックスからピクセル単位のxへの変換、価格からピクセル単位のyへの変換、xからバーインデックスへの変換、yから価格への変換をおこなうためのいくつかの基本的な関数が必要です。今全部使用するわけではありませんが、一度にすべてを作成します。そのため、チャートプロパティ変数をグローバルスコープに移動しますが、OnChartEvent関数では値を更新するだけで、必要に応じてRedraw関数を呼び出します。理想的な解決策は変数と変換関数をクラスまたは構造体にカプセル化することですが、ここでは単純にしておきましょう。ただし、「オブジェクト指向プログラミング」とドキュメントの関連トピック(「オブジェクト指向プログラミング」)を読んで、OOPの学習を開始することをお勧めします。それを次の機会に生かしていきます。

関数は基本的に比例関係にあるため、説明に時間と言葉を費やすことはありません。それらは次のとおりです。

//+------------------------------------------------------------------+
//| Converts the chart scale property to bar width/spacing           |
//+------------------------------------------------------------------+
int BarWidth(int scale) {return (int)pow(2, scale);}
//+------------------------------------------------------------------+
//| Converts the bar index(as series) to x in pixels                 |
//+------------------------------------------------------------------+
int ShiftToX(int shift) {return (chart_first_vis_bar - shift) * BarWidth(chart_scale) - 1;}
//+------------------------------------------------------------------+
//| Converts the price to y in pixels                                |
//+------------------------------------------------------------------+
int PriceToY(double price)
  {
// avoid zero divider
   if(chart_prcmax - chart_prcmin == 0.)
      return 0.;
   return (int)round(chart_height * (chart_prcmax - price) / (chart_prcmax - chart_prcmin) - 1);
  }
//+------------------------------------------------------------------+
//| Converts x in pixels to bar index(as series)                     |
//+------------------------------------------------------------------+
int XToShift(int x)
  {
// avoid zero divider
   if(BarWidth(chart_scale) == 0)
      return 0;
   return chart_first_vis_bar - (x + BarWidth(chart_scale) / 2) / BarWidth(chart_scale);
  }
//+------------------------------------------------------------------+
//| Converts y in pixels to price                                    |
//+------------------------------------------------------------------+
double YToPrice(int y)
  {
// avoid zero divider
   if(chart_height == 0)
      return 0;
   return chart_prcmax - y * (chart_prcmax - chart_prcmin) / chart_height;
  }

透明なDRAW_FILLING

これで、CCanvasを使用してDRAW_FILLINGを実装するために必要なものがすべて揃いました。

新しいインジケーターの作成に時間はかかりません。代わりに、すべてのMetaTrader 5プラットフォームに存在する例を取り上げて、2つの行の間に塗りつぶしを追加してみましょう。ターミナルデータフォルダの「\\MQL5\\Indicators\\Examples\\」にあるエンベロープを使用します。Envelopes.mq5を、ChartPropertiesViewerインジケーターを作成したのと同じディレクトリにコピーします。任意のインジケーターを選択できますが、この記事で説明されている手順に従って同じインジケーターを使用することをお勧めします.

最初に必要なのは、ChartPropertiesViewerインジケーターでおこなったすべてをエンベロープにコピーすることです。

上記のように、2つの線の間のチャネルを埋めます。この目的のために、これらの行の値に対応する配列を渡す関数を作成します。エンベロープインジケーターでは、配列はExtUpBuffer変数およびExtMABuffer変数によって指定されます。

double                   ExtUpBuffer[];
double                   ExtDownBuffer[];

配列とともに、2つの色を使用し、透明度を設定し、インジケーターをチャートの左または右に移動できるようにするいくつかの変数を渡します。

パラメータ 変数の記述
 serie1  最初の行に対応する値の配列
 serie2  2行目に対応する値の配列
 clr1  serie1>=serie2の場合の色
 clr2  serie1<serie2の場合の色
 alpha  チャネルの透明度の値
 plot_shift  チャートの右または左のインジケーターをシフト

既存の変数と上記のパラメータを使用した関数は次のとおりです。

//+------------------------------------------------------------------+
//| Fill the area between two lines                                  |
//+------------------------------------------------------------------+
void DrawFilling(double &serie1[], double &serie2[], color clr1, color clr2, uchar alpha = 255, int plot_shift = 0)
  {
   int start  = chart_first_vis_bar;
   int total  = chart_vis_bars + plot_shift;
   uint argb1 = ColorToARGB(clr1, alpha);
   uint argb2 = ColorToARGB(clr2, alpha);
   int limit  = fmin(ArraySize(serie1), ArraySize(serie2));
   int px, py1, py2;
   for(int i = 0; i < total; i++)
     {
      int bar_position = start - i;
      int bar_shift = start - i + plot_shift;
      int bar_index = limit - 1 - bar_shift;
      if(serie1[bar_index] == EMPTY_VALUE || serie1[bar_index] == EMPTY_VALUE || bar_shift >= limit)
         continue;
      int x  = ShiftToX(bar_position);
      int y1 = PriceToY(serie1[bar_index]);
      int y2 = PriceToY(serie2[bar_index]);
      uint argb = serie1[bar_index] < serie2[bar_index] ? argb2 : argb1;
      if(i > 0 && serie1[bar_index - 1] != EMPTY_VALUE && serie2[bar_index - 1] != EMPTY_VALUE)
        {
         if(py1 != py2)
            Canvas.FillTriangle(px, py1, px, py2, x, y1, argb);
         if(y1 != y2)
            Canvas.FillTriangle(px, py2, x, y1, x, y2, argb);
        }
      px  = x;
      py1 = y1;
      py2 = y2;
     }
  }

この時点まで、固定サイズのキャンバスを使用してきました。ただし、インジケーターの描画には、チャートの全領域を埋めるためのキャンバスが必要です。さらに、最大化、最小化、いずれかの側への拡大、またはサブウィンドウインジケーターの追加によってチャートウィンドウのサイズが変更されるたびに、キャンバスが引き続きチャートの全領域を占めるようにする必要があります。そのために、OnChartEvent関数に小さな変更を加えてキャンバスのサイズを変更します。

void OnChartEvent(const int id, const long& lparam, const double& dparam, const string& sparam)
  {
   if(id != CHARTEVENT_CHART_CHANGE)
      return;
   chart_width          = (int)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS);
   chart_height         = (int)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS);
   chart_scale          = (int)ChartGetInteger(0, CHART_SCALE);
   chart_first_vis_bar  = (int)ChartGetInteger(0, CHART_FIRST_VISIBLE_BAR);
   chart_vis_bars       = (int)ChartGetInteger(0, CHART_VISIBLE_BARS);
   chart_prcmin         = ChartGetDouble(0, CHART_PRICE_MIN, 0);
   chart_prcmax         = ChartGetDouble(0, CHART_PRICE_MAX, 0);
   if(chart_width != Canvas.Width() || chart_height != Canvas.Height())
      Canvas.Resize(chart_width, chart_height);

次に、機能させるためにいくつかの小さな更新を追加します。

  1. Redraw関数を更新して、前のインジケーターに追加されたパラメータを削除し、DrawFilling関数を追加する
  2. OnCalculationにRedraw関数を追加して、インジケーターの値が変化したときに描画を更新する
  3. CreateBitmapLabelを呼び出すときにパラメータとして渡されるオブジェクト名を変更する

//+------------------------------------------------------------------+
//| Custom indicator redraw function                                 |
//+------------------------------------------------------------------+
void Redraw(void)
  {
   uint default_color = 0;
   color clrup = (color)PlotIndexGetInteger(0, PLOT_LINE_COLOR, 0);
   color clrdn = (color)PlotIndexGetInteger(1, PLOT_LINE_COLOR, 0);
//--- canvas erase
   Canvas.Erase(default_color);
//--- add first draw
   DrawFilling(ExtUpBuffer, ExtDownBuffer,clrup, clrdn, 128, InpMAShift);
//--- canvas update
   Canvas.Update();
  }
//--- the main loop of calculations
   for(int i=start; i<rates_total && !IsStopped(); i++)
     {
      ExtUpBuffer[i]=(1+InpDeviation/100.0)*ExtMABuffer[i];
      ExtDownBuffer[i]=(1-InpDeviation/100.0)*ExtMABuffer[i];
     }
   Redraw();
//--- OnCalculate done. Return new prev_calculated.
   return(rates_total);
   Canvas.CreateBitmapLabel(0, 0, short_name, 0, 0, 200, 150, COLOR_FORMAT_ARGB_NORMALIZE);

これで全部です。期間の異なる2つのエンベロープと1つの長方形オブジェクトで、現在どのように見えるかを確認できます。

alpha = 128のCCanvasを使用したエンベロープ

aplha=255のCCanvasを使用したエンベロープ

ご覧のとおり、インジケーターの問題は解決しましたが、チャートオブジェクトの問題はまだ存在しています。これが次の章の課題です。


サブウィンドウインジケーターで機能するようにメソッドを拡張する

下の図を参照してください。ここでは、DRAW_FILLINGを使用したサブウィンドウインジケーターを見ることができます。この図は、MQLドキュメントから取得したものです。同じことをおこないますが、CCanvasを使用して透過性を許可し、さらに重要なこととして、オーバーラップエリアの問題を回避します。

DRAW_FILLINGを使用したサブウィンドウインジケーター

必要な変更は次のとおりです。

  • インジケーターが配置されている同じサブウィンドウにビットマップラベルを作成する
  • メインチャートウィンドウではなく、サブウィンドウのサイズに基づいてキャンバスのサイズを変更する

同じサブウィンドウでビットマップラベルを作成し、サブウィンドウのサイズを取得するには、インジケーターが配置されているサブウィンドウを見つける必要があります。これは単にチャートの最後のサブウィンドウであると考えることができますが、ターミナルでは、必ずしも最後のサブウィンドウではなく、同じサブウィンドウに2つ以上のインジケーターを配置できます。次に、インジケーターが配置されているサブウィンドウの番号を返す関数が必要です。次の関数を見てください。

//+------------------------------------------------------------------+
//| return the number of the subwindow where the indicator is located|
//+------------------------------------------------------------------+
int ChartIndicatorFind(string shortname)
  {
   int subwin = ChartGetInteger(0, CHART_WINDOWS_TOTAL);
   while(subwin > 0)
     {
      subwin--;
      int total = ChartIndicatorsTotal(0, subwin);
      for(int i = 0; i < total; i++)
        {
         string name = ChartIndicatorName(0, subwin, i);
         if(name == shortname)
            return subwin;
        }
     }
   return -1;
  }

最後の指標では、エンベロープ指標を例として使用しました。ここでの例のソースとして、ドキュメントのDRAW_FILLINGをトピックにあるコードを使用します。以前に2つのインジケーターを作成したのと同じディレクトリに新しいインジケーターを作成できます。「SubwindowIndicator」と名付けましょう。次に、ドキュメントからコードをコピーします。

このインジケーターはDRAW_FILLINGを使用してプロットされます。CCanvasを使用してチャネルを埋めるために、プロットタイプを線に置き換えます。以下は、インジケーターのプロパティの変更です。

#property indicator_plots   2
//--- plot Intersection
#property indicator_label1  "Fast"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrRed
#property indicator_width1  1
#property indicator_label2  "Slow"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrBlue
#property indicator_width2  1

そして、次はOnInit関数の変更です。

//--- indicator buffers mapping
   SetIndexBuffer(0,IntersectionBuffer1,INDICATOR_DATA);
   SetIndexBuffer(1,IntersectionBuffer2,INDICATOR_DATA);
//---
   PlotIndexSetInteger(0,PLOT_SHIFT,InpMAShift);
   PlotIndexSetInteger(1,PLOT_SHIFT,InpMAShift);

また、インジケーターが線の外観を変更する必要はありません。その場合、OnCalculate関数でこの行をコメント化できます。

//--- If a sufficient number of ticks has been accumulated
   if(ticks>=N)
     {
      //--- Change the line properties
      //ChangeLineAppearance();
      //--- Reset the counter of ticks to zero
      ticks=0;
     }

これで、チャートプロパティ変数と、この記事で作成した関数を追加できます。このインジケーターでは、パラメータとしてDrawFilling関数に渡す必要がある配列の名前が異なります。そのため、Redraw関数で変更する必要があります。

double         IntersectionBuffer1[];
double         IntersectionBuffer2[];

Redraw関数は次のようになります。

//+------------------------------------------------------------------+
//| Custom indicator redraw function                                 |
//+------------------------------------------------------------------+
void Redraw(void)
  {
   uint default_color = 0;
   color clrup = (color)PlotIndexGetInteger(0, PLOT_LINE_COLOR, 0);
   color clrdn = (color)PlotIndexGetInteger(1, PLOT_LINE_COLOR, 0);
//--- canvas erase
   Canvas.Erase(default_color);
//--- add first draw
   DrawFilling(IntersectionBuffer1, IntersectionBuffer2, clrup, clrdn, 128, InpMAShift);
//--- canvas update
   Canvas.Update();
  }

コードをコンパイルすると、期待どおりの結果が得られます。

透明なチャンネルが塗りつぶされたサブウィングウィンドウインジケーター


結論

この記事では、CCanvasの操作、いくつかのチャートプロパティ、それらの値を取得し、それらを使用して有用でさまざまな目的に適用できるいくつかの基本的な座標変換をおこなう方法を含む基本的なプロセスを見てきました。その後、透明性のあるインジケーターを開発することができました。そして、サブウィンドウインジケーターを操作する方法を拡張することが、私たちの最後の仕事でした。

この記事で開発されたインジケーターのファイルは、この記事の最後でダウンロードできます。


MetaQuotes Ltdにより英語から翻訳されました。
元の記事: https://www.mql5.com/en/articles/12357

添付されたファイル |
Envelopes.mq5 (10.43 KB)
データサイエンスと機械学習(第12回):自己学習型ニューラルネットワークは株式市場を凌駕することができるのか? データサイエンスと機械学習(第12回):自己学習型ニューラルネットワークは株式市場を凌駕することができるのか?
常に株式市場を予測しようとするのにお疲れでないでしょうか。より多くの情報に基づいた投資判断をするための水晶玉があったらとお思いでしょうか。自己学習型ニューラルネットワークは、あなたが探していたソリューションかもしれません。この記事では、これらの強力なアルゴリズムが、株式市場を凌駕する「波に乗る」のに役立つのかどうかを探ります。膨大な量のデータを分析し、パターンを特定することで、自己訓練されたニューラルネットワークは、しばしば人間のトレーダーよりも精度の高い予測をおこなうことができます。この最先端のテクノロジーを使って、利益を最大化し、よりスマートな投資判断をおこなう方法をご紹介します。
母集団最適化アルゴリズム:SSG(Saplings Sowing and Growing up、苗木の播種と育成) 母集団最適化アルゴリズム:SSG(Saplings Sowing and Growing up、苗木の播種と育成)
SSG(Saplings Sowing and Growing up、苗木の播種と育成)アルゴリズムは、様々な条件下で優れた生存能力を発揮する、地球上で最も回復力のある生物の1つからインスピレーションを得ています。
データサイエンスと機械学習(第13回):主成分分析(PCA)で金融市場分析を改善する データサイエンスと機械学習(第13回):主成分分析(PCA)で金融市場分析を改善する
主成分分析(Principal component analysis、PCA)で金融市場分析に革命を起こしましょう。この強力な手法がどのようにデータの隠れたパターンを解き放ち、潜在的な市場動向を明らかにし、投資戦略を最適化するかをご覧ください。この記事では、PCAが複雑な金融データを分析するための新しいレンズをどのように提供できるかを探り、従来のアプローチでは見逃されていた洞察を明らかにします。金融市場データにPCAを適用することで競争力を高め、時代を先取りする方法をご覧ください。
フィボナッチによる取引システムの設計方法を学ぶ フィボナッチによる取引システムの設計方法を学ぶ
最も人気のあるテクニカル指標を使用して取引システムを設計する方法についての連載を続けます。今回の新しいテクニカルツールはフィボナッチです。このテクニカル指標に基づいて取引システムを設計する方法を学びます。