ソースコードのトレーシング デバッギング 構造分析

29 10月 2015, 08:36
---
---
0
354


はじめに

この記事では、実行中にコールスタックを作成するメソッドの一つを紹介しています。以下の機能が述べられています。

  • 使用済みクラス、関数、ファイルのストラクチャーの作成
  • すべてのスタックを保持するコールスタックの作成それらを呼ぶ順序.
  • 実行中のWatchパラメーターの状態を見る
  • コードの段階的な実行
  • グループ化、取得されたスタックのソート、「外部情報」の取得


開発の主な規則

ツリーの形で表示するという共通の手法を、ストラクチャーの表現方法として選んでいます。このために、二つのクラスが必要です。CNode - スタックに関してすべての情報を記述するために使用される「ノード」CTreeCtrl - すべてのノードを処理する「ツリー」ツリーを処理するためのトレーサー - CTraceCtrl

そのクラスは、以下のヒエラルキーに沿って実行されます。


CNodeBaseCTreeBaseクラスは、基礎的なプロパティやノードやツリーを扱うメソッドを記述しています。

継承クラス CNodeCNodeBaseの基本的な機能を引き継ぎ、CTreeBaseは派生クラスCNodeを扱います。それは、CNodeBaseクラスは、そのほか標準のノードの親クラスであり、ヒエラルキーや継承の利便性のために独立したクラスとして孤立しているためです。

標準ライブラリのCTreeNode と異なり、CNodeBaseは、ポインターの配列をノードに含んでおり、このノードの「ブランチ」数は、無限です。

CNodeBaseとCNodeクラス

class CNode; // forward declaration
//------------------------------------------------------------------    class CNodeBase
class CNodeBase
  {
public:
   CNode   *m_next[]; // list of nodes it points to
   CNode   *m_prev; // parent node
   int     m_id; // unique number
   string  m_text; // text

public:
          CNodeBase() { m_id=0; m_text=""; } // constructor
          ~CNodeBase(); // destructor
  };

//------------------------------------------------------------------    class CNode
class CNode : public CNodeBase
  {
public:
   bool    m_expand; // expanded
   bool    m_check; // marked with a dot
   bool    m_select; // highlighted

   //--- run-time information
   int     m_uses; // number of calls of the node
   long    m_tick; // time spent in the node
   long    m_tick0; // time of entering the node
   datetime    m_last; // time of entering the node
   tagWatch   m_watch[]; // list of name/value parameters
   bool    m_break; // debug-pause

   //--- parameters of the call
   string   m_file; // file name
   int      m_line; // number of row in the file
   string   m_class; // class name
   string   m_func; // function name
   string   m_prop; // add. information

public:
           CNode(); // constructor
           ~CNode(); // destructor
   void    AddWatch(string watch,string val);
  };

すべてのクラスの実装が添付ファイルにて見ることができます。この記事では、ヘッダーと重要な部分のみ記載しています。

分類によれば、 CTreeBase は、悲観式のグラフを代表するものです。派生クラス CTreeCtrlCNodeを使用し、すべての機能を持ちます; CNodeノードの追加、変更、削除です。

CTreeCtrl CNodeは、わずかにより広い機能性を持っているため、標準ライブラリの一致するクラス取り替えられています。

CTreeBaseとCTreeCtrlクラス

//------------------------------------------------------------------    class CTreeBase
class CTreeBase
  {
public:
   CNode   *m_root; // first node of the tree
   int     m_maxid;    // counter of ID

   //--- base functions
public:
           CTreeBase(); // constructor
           ~CTreeBase(); // destructor
   void    Clear(CNode *root=NULL); // deletion of all nodes after a specified one
   CNode   *FindNode(int id,CNode *root=NULL); // search of a node by its ID starting from a specified node
   CNode   *FindNode(string txt,CNode *root=NULL); // search of a node by txt starting from a specified node
   int     GetID(string txt,CNode *root=NULL); // getting ID for a specified Text, the search starts from a specified node
   int     GetMaxID(CNode *root=NULL); // getting maximal ID in the tree
   int     AddNode(int id,string text,CNode *root=NULL); // adding a node to the list, search is performed by ID starting from a specified node
   int     AddNode(string txt,string text,CNode *root=NULL); // adding a node to the list, search is performed by text starting from a specified node
   int     AddNode(CNode *root,string text); // adding a node under root
  };

//------------------------------------------------------------------    class CTreeCtrl
class CTreeCtrl : public CTreeBase
  {
   //--- base functions
public:
          CTreeCtrl() { m_root.m_file="__base__"; m_root.m_line=0; 
                        m_root.m_func="__base__"; m_root.m_class="__base__"; } // constructor
          ~CTreeCtrl() { delete m_root; m_maxid=0; } // destructor
   void    Reset(CNode *root=NULL); // reset the state of all nodes
   void    SetDataBy(int mode,int id,string text,CNode *root=NULL); // changing text for a specified ID, search is started from a specified node
   string  GetDataBy(int mode,int id,CNode *root=NULL); // getting text for a specified ID, search is started from a specified node

   //--- processing state
public:
   bool    IsExpand(int id,CNode *root=NULL); // getting the m_expand property for a specified ID, search is started from a specified node
   bool    ExpandIt(int id,bool state,CNode *root=NULL); // change the m_expand state, search is started from a specified node
   void    ExpandBy(int mode,CNode *node,bool state,CNode *root=NULL); // expand node of a specified node

   bool    IsCheck(int id,CNode *root=NULL); // getting the m_check property for a specified ID, search is started from a specified node
   bool    CheckIt(int id,bool state,CNode *root=NULL); // change the m_check state to a required one starting from a specified node
   void    CheckBy(int mode,CNode *node,bool state,CNode *root=NULL); // mark the whole tree

   bool    IsSelect(int id,CNode *root=NULL); // getting the m_select property for a specified ID, search is started from a specified node
   bool    SelectIt(int id,bool state,CNode *root=NULL); // change the m_select state to a required one starting from a specified node
   void    SelectBy(int mode,CNode *node,bool state,CNode *root=NULL); // highlight the whole tree

   bool    IsBreak(int id,CNode *root=NULL); // getting the m_break property for a specified ID, search is started from a specified node
   bool    BreakIt(int id,bool state,CNode *root=NULL); // change the m_break state, search is started from a specified node
   void    BreakBy(int mode,CNode *node,bool state,CNode *root=NULL); // set only for a selected one 

   //--- operations with nodes
public:
   void    SortBy(int mode,bool ascend,CNode *root=NULL); // sorting by a property
   void    GroupBy(int mode,CTreeCtrl *atree,CNode *node=NULL); // grouping by a property
  };

このアーキテクチャは二つのクラスで終了します:CTraceCtrl - このクラスのインスタンスは、トレーシングのために直接使用され、関数の構造を作成するクラスCTreeCtrl の3つのインスタンスを含みます。そして、一時的に保持するCInクラスこれは、新しいノードをCTraceCtrlに追加するために使用される補助的なクラスです。

CTraceCtrlとCIn Classes

class CTraceView; // provisional declaration
//------------------------------------------------------------------    class CTraceCtrl
class CTraceCtrl
  {
public:
   CTreeCtrl   *m_stack; // object of graph
   CTreeCtrl   *m_info; // object of graph
   CTreeCtrl   *m_file; // grouping by files
   CTreeCtrl   *m_class; // grouping by classes
   CTraceView  *m_traceview; // pointer to displaying of class

   CNode   *m_cur; // pointer to the current node
           CTraceCtrl() { Create(); Reset(); } // tracer created
           ~CTraceCtrl() { delete m_stack; delete m_info; delete m_file; delete m_class; } // tracer deleted
   void    Create(); // tracer created
   void    In(string afile,int aline,string aname,int aid); // entering a specified node 
   void    Out(int aid); // exit from a specified node
   bool    StepBack(); // exit from a node one step higher (going to the parent)
   void    Reset() { m_cur=m_stack.m_root; m_stack.Reset(); m_file.Reset(); m_class.Reset(); } // resetting all nodes
   void    Clear() { m_cur=m_stack.m_root; m_stack.Clear(); m_file.Clear(); m_class.Clear(); } // resetting all nodes

public:
   void    AddWatch(string name,string val); // checking the debug mode for a node
   void    Break(); // pause for a node
  };

//------------------------------------------------------------------    CIn
class CIn
  {
public:
   void In(string afile,int aline,string afunc)
     {
      if(NIL(m_trace)) return; // exit if there is no graph
      if(NIL(m_trace.m_tree)) return;
      if(NIL(m_trace.m_tree.m_root)) return;
      if(NIL(m_trace.m_cur)) m_trace.m_cur=m_trace.m_tree.m_root;
      m_trace.In(afile,aline,afunc,-1); // entering the next one
     }
   void ~CIn() { if(!NIL(m_trace)) m_trace.Out(-1); } // exiting higher
  };


CInクラスの処理モデル

このクラスは、スタックツリーの作成のために使用されます。

グラフの形成は、CTraceCtrl関数を用い、二つのステージで段階的に実行されます。

void In(string afile, int aline, string aname, int aid); // entering a specified node
void Out(int aid);  // exit before a specified node

つまり、ツリーを作るために、インーアウトーインーアウトーインーアウトの連続的な呼び出しが行われます。

インーアウト のペアは以下のような形で動作します。

1. (関数、サイクル、条件など)ブロックを括弧後に入力します(「{」)
ブロックを入力する際、CInの新しいインスタンスが作成されます。 いくつか前のノードにて開始されたCTraceCtrlを取得します。CTraceCtrl::In関数は CIn内にて呼ばれ、スタック内に新しいノードを作成します。そのノードは、CTraceCtrl::m_curの現在のノード下に作成されます。すべての情報は、その中に記入されており、ファイル名、行数、クラス名、現在時刻、関数などです。

2. 「"}」 括弧に達した際のブロックからの離脱
ブロックから出る際に、MQLは自動的にCIn::~CInデストラクターを呼び出します。CTraceCtrl::Out はそのデストラクター内にて呼ばれます。CTraceCtrl::m_cur の現在のノードのポインターは、ツリーの一つ上でのレベルに上げられます。そのデストラクターは、新しいノードのためには呼ばれず、ノードはツリー内で留まります。

スタックを形成するスキーマ


呼び出しについてのすべての情報が記入されたツリーの形成におけるコールスタックの作成は、CInコンテナを使用し実行されます。



呼び出しをより簡単にするマクロ

CInオブジェクトを作成する長いコードを再度記述し、ノードを入力することを避けるために、マクロの呼び出しで取り替えることが便利です。
#define _IN    CIn _in; _in.In(__FILE__, __LINE__, __FUNCTION__)
ご覧の通り、 CInオブジェクトは作成され、ノードを入力します。


MQLがローカル変数がグローバル変数と同じである場合は警告を出すので、その他の変数名で3から4の類似した定義を以下の形式で作成することがより正確です。

#define _IN1    CIn _in1; _in1.In(__FILE__, __LINE__, __FUNCTION__)
#define _IN2    CIn _in2; _in2.In(__FILE__, __LINE__, __FUNCTION__)
#define _IN3    CIn _in3; _in3.In(__FILE__, __LINE__, __FUNCTION__)
サブブロックに深く進んでいった際には、macros _INxを使用してください。
bool CSampleExpert::InitCheckParameters(int digits_adjust)
  { _IN;
//--- initial data checks
   if(InpTakeProfit*digits_adjust<m_symbol.StopsLevel())
     { _IN1;
      printf("Take Profit must be greater than %d",m_symbol.StopsLevel());

411のマクロの出現に伴い、#defineを用い、パラメーターを完全に移すことができます。

従って、CTraceCtrlクラス内で、以下のマクロの定義を見れるようになります。

#define NIL(p)    (CheckPointer(p)==POINTER_INVALID)

ポインターの有効性のチェックを短縮できます。

例えば:

if (CheckPointer(m_tree))==POINTER_INVALID || CheckPointer(m_cur))==POINTER_INVALID) return;  

より短いものに取り替えられます。

if (NIL(m_tree) || NIL(m_cur)) return;



トレーシングのためにファイルを準備する

スタックを取得し、管理するために、3つのステップを踏まなければなりません。

1. 必要なファイルを追加する
#include <Trace.mqh>

標準ライブラリは、CObjectクラスに基づいています。従って、もしファイル内でベースクラスとして使用された場合、Trace.mqh Object.mqhのみに追加するだけで十分です。

2. _INマクロを必要なブロックに配置してください。

_INマクロの使用例:
bool CSampleExpert::InitCheckParameters(int digits_adjust)
  { _IN;
//--- initial data checks
   if(InpTakeProfit*digits_adjust<m_symbol.StopsLevel())
     { _IN1;
      printf("Take Profit must be greater than %d",m_symbol.StopsLevel());


3. プログラムのメインのモジュールを構成しているOnInitOnTimeOnDeinit関数に、グローバルオブジェクトのCTraceCtrlの作成、修正、削除機能を それぞれに追加してください。挿入における出来上がったコードが以下です。

トレーサーをメインのコードに組み込む

//------------------------------------------------------------------    OnInit
int OnInit()
  {
   //****************
   m_traceview= new CTraceView; // created displaying of the graph
   m_trace= new CTraceCtrl; // created the graph
   m_traceview.m_trace=m_trace; // attached the graph
   m_trace.m_traceview=m_traceview; // attached displaying of the graph
   m_traceview.Create(ChartID()); // created chart
   //****************
   // remaining part of your code…
   return(0);
  }
//------------------------------------------------------------------    OnDeinit
void OnDeinit(const int reason)
  {
   //****************
   delete m_traceview;
   delete m_trace;
   //****************
   // remaining part of your code…
  }
//------------------------------------------------------------------    OnTimer
void OnTimer()
  {
   //****************
   if (m_traceview.IsOpenView(m_traceview.m_chart)) m_traceview.OnTimer();
   else { m_traceview.Deinit(); m_traceview.Create(ChartID()); } // if the window is accidentally closed
   //****************
   // remaining part of your code…
  }
//------------------------------------------------------------------    OnChartEvent
void OnChartEvent(const int id, const long& lparam, const double& dparam, const string& sparam)
  {
   //****************
   m_traceview.OnChartEvent(id, lparam, dparam, sparam);
   //****************
   // remaining part of your code…
  }


トレースの表示クラス

スタックが構成されました。取得した情報の表示について紹介します。

このために二つのクラスを使用しなければなりません。CTreeView – ツリー表示用のクラスです。 CTraceView – ツリーの表示やスタックに関する追加情報を管理するクラスその二つのクラスは、ベースクラスCViewから派生しています。


CTreeViewとCTraceViewクラス

//------------------------------------------------------------------    class CTreeView
class CTreeView: public CView
  {
   //--- basic functions
public:
           CTreeView(); // constructor
           ~CTreeView(); // destructor
   void    Attach(CTreeCtrl *atree); // attached the tree object for displaying it
   void    Create(long chart,string name,int wnd,color clr,color bgclr,color selclr,
                    int x,int y,int dx,int dy,int corn=0,int fontsize=8,string font="Arial");

   //--- functions of processing of state
public:
   CTreeCtrl        *m_tree; // pointer to the tree object to be displayed
   int     m_sid; // last selected object (for highlighting)
   int     OnClick(string name); // processing the event of clicking on an object

   //--- functions of displaying
public:
   int     m_ndx, m_ndy; // size of margins from button for drawing
   int     m_bdx, m_bdy; // size of button of nodes
   CScrollView       m_scroll;
   bool    m_bProperty; // show properties near the node

   void    Draw(); // refresh the view
   void    DrawTree(CNode *first,int xpos,int &ypos,int &up,int &dn); // redraw
   void    DeleteView(CNode *root=NULL,bool delparent=true); // delete all displayed elements starting from a specified node
  };

//------------------------------------------------------------------    class CTreeView
class CTraceView: public CView
  {
   //--- base functions
public:
           CTraceView() { }; // constructor
           ~CTraceView() { Deinit(); } // destructor
   void    Deinit(); // full deinitialization of representation
   void    Create(long chart); // create and activate the representation

   //--- function of processing of state
public:
   int     m_hagent; // handler of the indicator-agent for sending messages
   CTraceCtrl   *m_trace; // pointer to created tracer
   CTreeView    *m_viewstack; // tree for displaying the stack
   CTreeView    *m_viewinfo; // tree for displaying of node properties
   CTreeView    *m_viewfile; // tree for displaying of the stack with grouping by files
   CTreeView    *m_viewclass; // tree for displaying of stack with grouping by classes
   void    OnTimer(); // handler of timer
   void    OnChartEvent(const int,const long&,const double&,const string&); // handler of event

   //--- functions of displaying
public:
   void    Draw(); // refresh objects
   void    DeleteView(); // delete the view

   void    UpdateInfoTree(CNode *node,bool bclear); // displaying the window of detailed information about a node
   string  TimeSeparate(long time); // special function for transformation of time into string
  };

個別のサブウィンドウにてスタックを表示しています。

言い換えれば、CTraceViewクラスがCTraceView::Create内の関数にて作成されたときに、CTraceViewが作成され、別のウィンドウにて動作するにも関わらずチャートウィンドウが作成され、すべてのオブジェクトがその中に描画されます。トレースされるコードの処理やチャート上に情報を表示することを妨害しないようにされます。

しかし、二つのウィンドウ間での連携を可能にするために、インジケーターをウィンドウに追加する必要です。それは、ユーザーのすべてのイベントをトレースされるプログラムを持つベースウィンドウへ送信します。

そのインジケーターは、同じCTraceView::Create関数で作成されます。それは外部パラメーター一つのみを持ちます。すべてのイベントが送られるチャートのIDのみです。

TraceAgentインジケーター

#property indicator_chart_window
input long cid=0; // чарт получателя
//------------------------------------------------------------------    OnCalculate
int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double& price[])
{ return(rates_total); }
//------------------------------------------------------------------    OnChartEvent
void OnChartEvent(const int id,const long& lparam,const double& dparam,const string& sparam)
  {
    EventChartCustom(cid, (ushort)id, lparam, dparam, sparam);
  }

結果として、スタックのストラクチャーを得ることができます。



左に表示されたTRACEツリーでは、最初のスタックが表示されます。

選択したノードが(この例ではCTraceView::OnChartEvent)についての詳細な情報を含むINFOウィンドウが以下にあります。ツリーを持つ二つの隣接したウィンドウは同じスタックを表示しますが、(真ん中CLASSツリー)クラスごと、(右側FILEツリー)ファイルごとで 分類されています。

クラスとファイルのツリーは、便利な管理における手段とともに、主要なスタックのツリーとの同期化のためのメカニズムを持ちます。例えば、クラスツリーのクラス名をクリックすると、このクラスのすべての関数がスタックツリーとファイルツリーの中で選択されます。同様に、ファイル名をクリックすると、そのファイル内のすべての関数やクラスが選択されます。



その仕組みにより、より早く関数の必要なグループを選択し、見ることができます。



スタックの使用特徴

  • Watchパラメーターの追加

ご存知の通り、CNodeのパラメーターは、 tagWatchストラクチャーの配列を含んでいます。情報表示の利便性のためだけに作成されています。変数や式の名前の付けられた値を含みます。

Watchの値のストラクチャー

//------------------------------------------------------------------    struct tagWatch
struct tagWatch
{
    string m_name;     // name
    string m_val;    // value
};

新しいWatchの値を現在のノードに追加するために、CTrace::AddWatch関数を呼び、_WATCHマクロを使用する必要があります。

#define _WATCH(w, v)         if (!NIL(m_trace) && !NIL(m_trace.m_cur)) m_trace.m_cur.AddWatch(w, string(v));


追加された値に関する特別な制限は、名前の一意性を管理することにあります。Watchの値の名前は、 CNode::m_watch[]配列に追加される前に、一意性をチェックされるということです。もしその配列が同じ名前の値を持っていれば、新しい値は追加されず、既存のものの値が更新されます。

すべてのWatchの値がインフォーメーションウィンドウにて表示されます。



  • コードの段階的な実行

MQL5により提供されているその他の便利な機能は、実行中のコードにおけるブレークの作成です。

while (true)の無限ループの使用により、そのブレークが実行されます。MQL5の利便性は、このループからの離脱イベントのハンドリングという部分で、 のボタンをクリックするのみです。実行中にブレークポイントを作成するために、CTrace::Break関数を使用します。


ブレークポイントの実行のための関数

//------------------------------------------------------------------    Break
void CTraceCtrl::Break() // checking the debug mode of a node
  {
   if(NIL(m_traceview)) return; // check of validity
   m_stack.BreakBy(TG_ALL,NULL,false); // removed the m_break flags from all nodes
   m_cur.m_break=true; // activated only at the current one
   m_traceview.m_viewstack.m_sid=m_cur.m_id; // moved selection to it
   m_stack.ExpandBy(TG_UP,m_cur,true,m_cur); // expand parent node if they are closed
   m_traceview.Draw(); // drew everything
   string name=m_traceview.m_viewstack.m_name+string(m_cur.m_id)+".dbg"; // got name of the BREAK button
   bool state=ObjectGetInteger(m_traceview.m_chart,name,OBJPROP_STATE);
   while(!state) // button is not pressed, execute the loop
     {
      Sleep(1000); // made a pause
      state=ObjectGetInteger(m_traceview.m_chart,name,OBJPROP_STATE);  // check its state
      if(!m_traceview.IsOpenView()) break; // if the window is closed, exit
      m_traceview.Draw(); // drew possible changes
     }
   m_cur.m_break=false; // removed the flag
   m_traceview.Draw(); // drew the update
  }


そのようなブレークポイントに達した際、スタックツリーは、このマクロを呼ぶ関数を表示するために同期化します。もしノードがとじられれば、親のノードは、表示するように拡大されます。もし必要であれば、ツリーは、見える場所にノードを持ってくるようスクロールされます。


CTraceCtrl::Breakから脱するために、ノード名の近くの赤色のボタンをクリックしてください。


結論

今、おもしろい「おもちゃ」を得ることができました。この記事を書く間に、CTraceCtrlを使用するたくさんの異なる形を試し、MQL5がエキスパートアドバイザーを管理し、その処理を操作するユニークな特徴を持っていることを確認しました。トレーサーを開発するために使用されるすべての特徴は、MQL4では使用できません。これはMQL5のみの利点であることを示し、その可能性をまた広げるものです。

添付されたコードでは、すこの記事で紹介されていたすべてのクラスをサービスライブラリ(それらが目的ではないため最小限のみ)とともに見ることができます。さらに、すぐに使用できる、_INの場所にある標準ライブラリの更新済みファイルを添付しています。すべては、MACD Sample.mq5.- MetaTrader5の標準デリバリーに含まれるエキスパートアドバイザーにて行われています。


MetaQuotes Software Corp.によりロシア語から翻訳された
元の記事: https://www.mql5.com/ru/articles/272

添付されたファイル |
mql5.zip (24.67 KB)
統計的推定 統計的推定

シーケンスの統計的パラメータの推定はたいへん重要なものです。それはたいていの数学的モデルと手法が異なる前提に基づいているからです。たとえば、分布法則の正常化、分散値、その他パラメータです。よって時系列を分析し推定するとき、主要な統計的パラメータを素早く明確に推定できるシンプルで使い勝手のよいツールが必要です。本稿では、もっともシンプルなランダムシーケンスの統計パラメータとビジュアル分析のメソッドをいくつか取り上げ述べていきます。それにより MQL5 でこれらメソッド、またニュープロットアプリケーションを用いて計算した結果の視覚化メソッドを実装します。

Expert Advisors最適化のカスタム基準作成 Expert Advisors最適化のカスタム基準作成

MetaTrader 5 クライアント端末は Expert Advisor パラメータを最適化する幅広い機会を提供します。またストラレジーテスタに含まれる最適化評価基準に対して、開発者には自身の基準を作成するチャンスが与えられています。これは Expert Advisorsを検証し最適化する数えきれない可能性に導きます。本稿ではそのような基準を、複雑なもの単純なもの双方、作成する実践的方法について記述します。

MQL5での統計確率分布 MQL5での統計確率分布

本稿は、適用統計に使用されるランダム変数の確率分布(標準、対数正規分布、二項分布、ロジスティック分布、指数分布、コーシー分布、ストゥーデンとの t-分布、 ラプラス分布、 ポアソン分布、双曲線正割分布、 ベータ分布、ガンマ分布)について述べます。またこういった分布を処理するクラス特性についても述べます。

直線回帰例によるインディケータスピードアップの3手法 直線回帰例によるインディケータスピードアップの3手法

本稿では、インディケータメソッドの計算アルゴリズムの最適化を取り上げます。みなさんはご自身のニーズにもっとも合うメソッドをお探しのことと思います。本稿では3種類の手法について述べます。その一つはきわめて簡単なものです。次のひとつはしっかりした数学の知識が必要です。そして最後のひとつには多少のウィットが必要です。はここで述べられる手法のほとんどを理解するのにインディケータまたはMetaTrader5 ターミナルのデザイン的特性を利用します。その手法はかなり汎用的で直線回帰計算だけでなくその他のインディケータに対しても使用可能です。