English Русский 中文 Español Deutsch Português
preview
MQL5でJanus factorを実装する

MQL5でJanus factorを実装する

MetaTrader 5 | 12 6月 2023, 10:50
251 0
Francis Dube
Francis Dube

はじめに

トレーダーなら誰でも知っていることですが、市場価格は大きく分けて2つのパターンのどちらかに従っています。価格は、トレンドを形成するか、水平方向に動くかのどちらかです。その結果、市場参加者の戦略は、大きく分けて、トレンドフォローか逆張りかのどちらかに絞られることになります。Janus factorは、この二面性を捉えた市場行動論です。今回は、その基礎を紐解くと同時に、この分析方法を促進する指標の実装を紹介します。

フィードバック

Janus factorによれば、市場は価格とそれに対するトレーダーの反応との相互作用によって動きます。アンダーソンは、この相互作用をフィードバックシステムに例えました。フィードバックシステムについては、ウィキペディアで詳しく知ることができます。市場がトレンドにあるとき、市場参加者は利益を乗せることで自信を示します。上昇トレンドでは、価格が上昇することで参加者のトレンドへの期待が確認され、買いが増えます。これは、価格をさらに高くする効果があります。 

図1:上昇トレンドにおけるポジティブフィードバック

図1:上昇トレンドにおけるポジティブフィードバック


下降トレンドでは、価格の下落がトレーダーに恐怖心を与え、トレーダーが損失を最小限に抑えるために売りに走ることがあります。売りが増えれば、圧力がかかって価格が下がります。したがって、市場の動向はポジティブフィードバックの一例です。トレーダーが価格変動に反応するまで、価格は加速し続けます。 

図2:下降トレンドにおけるポジティブフィードバック

図2:下降トレンドにおけるポジティブフィードバック

ネガティブフィードバックは、トレーダーが市場に対する信頼が薄いため、価格が変動した後に早期に利益を確定することを選択した場合に見られます。早期の利益確定はモメンタムを失わせ、値動きの大きさを制限します。これに強気と弱気のせめぎ合いが加わり、価格が安定するのです。

図3:ネガティブフィードバック

図3:市場におけるネガティブフィードバック

資本流出

このフィードバックという概念で重要なのは、異なる条件が重なったときにトレーダーが好む銘柄のタイプです。アンダーソンの銘柄分析によると、トレーダーは上昇トレンドの中で、相対的にパフォーマンスの良い銘柄を支持し、パフォーマンスの悪い銘柄を売っていることがわかります。下降トレンドでは、業績不振の銘柄がトレーダーの空売りの対象となり、最も価値を失いました。 

トレンドがない場合、トレーダーは特定の価格水準を選んでエントリーしたりエグジットしたりするので、強い株と弱い株を分けるものはあまりありませんでした。そこで彼は、ある銘柄の相対的なパフォーマンスを分析することで、ネガティブフィードバックとポジティブフィードバックの時期を検出することができるという仮説を立てました。


Janus計算

パフォーマンスを把握するために、定期的なリターンを算出します。調査対象の銘柄のリターンを合算して平均値を出し、基準値として使用します。これは、ベンチマークリターンと呼ばれます。評価対象銘柄の集合体をインデックスと呼びます。

Janus Factor -  Trend Follower's Guide to Market Dialectics』の中で、アンダーソンは、リターンをベースに計算された銘柄の様々な指標を紹介し、マーケットの挙動を洞察するために利用しています。 


ジJanusライブラリ - janus.mqh

Janusに関連するすべての計算に共通するデータとルーチンは、janus.mqhに含まれています。インクルードファイルには、3つのカスタムタイプの宣言があります。

    CSymboldataクラスは、銘柄データおよび関連する操作を処理します。 

    //+------------------------------------------------------------------+
    //|Class which manage the single Symbol                              |
    //+------------------------------------------------------------------+
    class CSymbolData
      {
    
    private:
       string            m_name;    // name of the symbol
       ENUM_TIMEFRAMES   m_timeframe;// timeframe
       int               m_length;  // length for copy rates
       MqlRates          m_rates[]; // store rates
       datetime          m_first;   // first date on server or local history
    
       datetime          SetFirstDate(void)
         {
          datetime first_date=-1;
          if((datetime)SymbolInfoInteger(m_name,SYMBOL_TIME)>0)
             first_date=(datetime)SeriesInfoInteger(m_name,m_timeframe,SERIES_FIRSTDATE);
          //---
          if(first_date==WRONG_VALUE || first_date==0)
            {
             if(TerminalInfoInteger(TERMINAL_CONNECTED))
               {
                while(!SeriesInfoInteger(m_name,m_timeframe,SERIES_SERVER_FIRSTDATE,first_date) && !IsStopped())
                   Sleep(10);
               }
            }
          //---
    #ifdef DEBUG
          Print(m_name," FirstDate ",first_date);
    #endif
          return first_date;
         }
    
    public:
                         CSymbolData(string name,ENUM_TIMEFRAMES tf=PERIOD_CURRENT)
         {
          m_name = name;
          m_length = 0;
          m_timeframe = tf;
          SymbolSelect(m_name,true);
         }
    
                        ~CSymbolData(void)
         {
          ArrayFree(m_rates);
         }
       datetime          GetFirstDate(void)
         {
          m_first = SetFirstDate();
          return m_first;
         }
       string            GetName(void)
         {
          return m_name;
         }
    
       int               GetLength(void)
         {
          return m_length;
         }
    
       void              SetLength(const int set_length)
         {
          if(set_length>0)
            {
             m_length=set_length;
             ArrayResize(m_rates,m_length,m_length);
             ArraySetAsSeries(m_rates,true);
            }
         }
    
       bool              Update(void)
         {
          int copied = CopyRates(m_name,m_timeframe,0,m_length,m_rates);
    #ifdef DEBUG
          Print("copied ", copied, " requested ", m_length);
    #endif
          //--
          return copied == m_length;
         };
    
       MqlRates          GetRateAtPos(const int i)
         {
          if(i<0 || i>=m_length)
            {
    #ifdef DEBUG
             Print("Array out of range ",i,".Size of array is ",m_length);
    #endif
             return (i<0)?m_rates[0]:m_rates[m_length-1];
            }
          return m_rates[i];
         }
      };
    CSymbolCollectionは CSymboldata オブジェクトのコンテナで、解析対象の銘柄のコレクションを表します。両クラスのコードは、Mql5のコードベースからのものを流用しました。下層バッファのインデックス付けの方向性を修正しました。なお、すべてのクラスで右から左へのインデックス形式が採用されており、ゼロインデックスは最新のバーを指しています。 

    Janus factorの技術を実証するために、FXペアの分析に応用してみましょう。アンダーソンはもともと株式分析のためにこの方法を考案しましたが、株式銘柄データの入手は多くの証券会社で一貫していません。

    //+------------------------------------------------------------------+
    //| Class that mange the collection of symbols                       |
    //+------------------------------------------------------------------+
    class CSymbolCollection
      {
    
    private:
    
       int               m_calculation_length; // global length
       ENUM_TIMEFRAMES   m_collection_timeframe;  // timeframe of data
       string            m_raw_symbols;    // delimited symbol list
       datetime          m_synced_first;  // synced first bar opentime for all symbols
       bool              m_synced;        // flag of whether all symbols are synchronized
       CSymbolData       *m_collection[]; // Collection of Symbol Pointer
       int               m_collection_length; // Collection of Symbol Length
       //+------------------------------------------------------------------+
       //|                                                                  |
       //+------------------------------------------------------------------+
       bool               CheckSymbolBars(const string __sym)
         {
          int bars=-1;
          bars=iBarShift(__sym,m_collection_timeframe,m_synced_first)+1;//SeriesInfoInteger(__sym,PERIOD_CURRENT,SERIES_BARS_COUNT);
    #ifdef DEBUG
          Print("Bars found in history for ",__sym," ",bars);
    #endif
          if(bars>=m_calculation_length)
             return(true);
          //---
          return(SyncSymbol(__sym));
         }
       //+------------------------------------------------------------------+
       //|                                                                  |
       //+------------------------------------------------------------------+
       bool               SyncSymbol(const string __sym)
         {
          //--- load data step by step
          bool downloaded=false;
          datetime times[1];
          int bars=-1;
          
         /* if(MQLInfoInteger(MQL_PROGRAM_TYPE)==PROGRAM_INDICATOR)
            {
    #ifdef DEBUG
             Print(" cannot download ",__sym," history from an indicator");
    #endif
             return(downloaded);
            }*/
              
    #ifdef DEBUG
          Print(" downloading ",__sym," history");
    #endif
          while(!IsStopped() && !downloaded && TerminalInfoInteger(TERMINAL_CONNECTED))
            {
             //---
             while(!SeriesInfoInteger(__sym,m_collection_timeframe,SERIES_SYNCHRONIZED) && !IsStopped())
                Sleep(5);
             //---
             bars=Bars(__sym,PERIOD_CURRENT);
             if(bars>=m_calculation_length)
               {
                downloaded=true;
                break;
               }
             //--- copying of next part forces data loading
             if(CopyTime(__sym,m_collection_timeframe,m_calculation_length-1,1,times)==1)
               {
                downloaded=true;
                break;
               }
             //---
             Sleep(5);
            }
    #ifdef DEBUG
          if(downloaded)
             Print(bars," ",__sym," bars downloaded ");
          else
             Print("Downloading ",__sym," bars failed");
    #endif
          return(downloaded);
         }
    
    public:
    
                         CSymbolCollection(const ENUM_TIMEFRAMES tf=PERIOD_CURRENT)
         {
          m_raw_symbols="";
          m_collection_length = 0;
          m_calculation_length = -1;
          m_synced_first=0;
          m_synced=false;
          m_collection_timeframe=tf;
         }
    
                        ~CSymbolCollection(void)
         {
    
          for(int i=0; i<m_collection_length; i++)
            {
             if(CheckPointer(m_collection[i])==POINTER_DYNAMIC)
                delete m_collection[i];
            }
         }
       //+------------------------------------------------------------------+
       //|return the set timeframe for bars stored in the collection        |
       //+------------------------------------------------------------------+
       ENUM_TIMEFRAMES   GetTimeFrame(void)
         {
          return(m_collection_timeframe);
         }
       
       //+------------------------------------------------------------------+
       //|Checks the history available and syncs it across all symbols      |
       //+------------------------------------------------------------------+
       bool              CheckHistory(const int size)
         {
          if(size<=0)
             return(false);
    
          int available=iBarShift(NULL,m_collection_timeframe,m_synced_first)+1;
    
          if(available<size)
             m_calculation_length=available;
          else
             m_calculation_length=size;
    
    #ifdef DEBUG
          Print("synced first date is ", m_synced_first);
          Print("Proposed size of history ",m_calculation_length);
    #endif
    
          if(m_calculation_length<=0)
             return(false);
    
          ResetLastError();
    
          for(int i=0; i<m_collection_length; i++)
            {
             m_synced=CheckSymbolBars(m_collection[i].GetName());
             if(!m_synced)
               {
                Print("Not Enough history data for ", m_collection[i].GetName(), " > ", GetLastError());
                return(m_synced);
               }
             m_collection[i].SetLength(m_calculation_length);
            }
    
          return m_synced;
    
         }
    
    
       //+------------------------------------------------------------------+
       //| Add a symbol by name to the collection                           |
       //+------------------------------------------------------------------+
       int               Add(string name)
         {
          CSymbolData *ref = new CSymbolData(name,m_collection_timeframe);
          datetime f=ref.GetFirstDate();
          int found=GetIndex(name);
          if(f==WRONG_VALUE || found>-1)
            {
    #ifdef DEBUG
             if(f==WRONG_VALUE)
                Print("Failed to retrieve information for symbol ",name,". Symbol removed from collection");
             if(found>-1)
                Print("Symbol ",name,"already part of collection");
    #endif
             delete ref;
             return(m_collection_length);
            }
          ArrayResize(m_collection, m_collection_length + 1,1);
          m_collection[m_collection_length] = ref;
          //---
          if(f>m_synced_first)
             m_synced_first=f;
          //---
    
          return(++m_collection_length);
    
         }
       //+------------------------------------------------------------------+
       //|Return symbol name                                                |
       //+------------------------------------------------------------------+
       string            GetSymbolNameAtPos(int pos)
         {
          return m_collection[pos].GetName();
         }
       //+------------------------------------------------------------------+
       //|return index of symbol                                            |
       //+------------------------------------------------------------------+
       int               GetIndex(const string symbol_name)
         {
          for(int i=0; i<m_collection_length; i++)
            {
             if(symbol_name==m_collection[i].GetName())
                return(i);
            }
          //---fail
          return(-1);
         }
       //+------------------------------------------------------------------+
       //| Return Collection length                                         |
       //+------------------------------------------------------------------+
       int               GetCollectionLength(void)
         {
          return m_collection_length;
         }
    
       //+------------------------------------------------------------------+
       //| Update every currency rates                                      |
       //+------------------------------------------------------------------+
       bool              Update(void)
         {
    
          int i;
          for(i = 0; i < m_collection_length; i++)
            {
             bool res = m_collection[i].Update();
             if(res==false)
               {
                Print("missing data on " + m_collection[i].GetName());
                return false;
               }
            }
          return true;
         }
       //+------------------------------------------------------------------+
       //|                                                                  |
       //+------------------------------------------------------------------+
       int               GetHistoryBarsLength(void)
         {
          return m_calculation_length;
         }
       //+------------------------------------------------------------------+
       //| Return MqlRates of currency at position                          |
       //+------------------------------------------------------------------+
       MqlRates          GetRateAtPos(int pos, int i)
         {
          return m_collection[pos].GetRateAtPos(i);
         }
    
       //+------------------------------------------------------------------+
       //| Return Open price of currency at position                        |
       //+------------------------------------------------------------------+
       double            GetOpenAtPos(int pos, int i)
         {
          return m_collection[pos].GetRateAtPos(i).open;
         }
    
       //+------------------------------------------------------------------+
       //| Return Close price of currency at position                       |
       //+------------------------------------------------------------------+
       double            GetCloseAtPos(int pos, int i)
         {
          return m_collection[pos].GetRateAtPos(i).close;
         }
    
       //+------------------------------------------------------------------+
       //| Return High price of currency at position                        |
       //+------------------------------------------------------------------+
       double            GetHighAtPos(int pos, int i)
         {
          return m_collection[pos].GetRateAtPos(i).high;
         }
    
       //+------------------------------------------------------------------+
       //| Return Low price of currency at position                         |
       //+------------------------------------------------------------------+
       double            GetLowAtPos(int pos, int i)
         {
          return m_collection[pos].GetRateAtPos(i).low;
         }
    
       //+------------------------------------------------------------------+
       //| Return Median price of currency at position                      |
       //+------------------------------------------------------------------+
       double            GetMedianAtPos(int pos, int i)
         {
          return (GetHighAtPos(pos,i) + GetLowAtPos(pos, i))/2;
         }
    
       //+------------------------------------------------------------------+
       //| Return Typical price of currency at position                     |
       //+------------------------------------------------------------------+
       double            GetTypicalAtPos(int pos, int i)
         {
          return (GetHighAtPos(pos,i) + GetLowAtPos(pos, i) + GetCloseAtPos(pos,i))/3;
         }
    
       //+------------------------------------------------------------------+
       //| Return Weighted price of currency at position                    |
       //+------------------------------------------------------------------+
       double            GetWeightedAtPos(int pos, int i)
         {
          return (GetHighAtPos(pos,i) + GetLowAtPos(pos, i) + GetCloseAtPos(pos,i) * 2)/4;
         }
      };
    //+------------------------------------------------------------------+
    

     

    このコードは、以下に示す3つのカスタム列挙から始まります。

    #include<Math\Stat\Math.mqh>
    #include <Arrays\ArrayObj.mqh>
    //+------------------------------------------------------------------+
    //|                                                                  |
    //+------------------------------------------------------------------+
    enum ENUM_INDEX_TYPE
      {
       INDEX_FOREX_MAJORS=0,//forex majors only list
       INDEX_CUSTOM,//custom symbol list
      };
    //+------------------------------------------------------------------+
    //|                                                                  |
    //+------------------------------------------------------------------+
    enum ENUM_PRICE
      {
       CLOSE=0,//close price
       MEDIAN,//median price
       TYPICAL,//typical price
       WEIGHTED//weighted price
      };
    //+------------------------------------------------------------------+
    //|                                                                  |
    //+------------------------------------------------------------------+
    enum ENUM_DIFF_TYPE
      {
       DIFF_PERCENT=0,//percent difference
       DIFF_LOG//log difference
      };
    
    
    列挙
    詳細
    オプション
    ENUM_INDEX_TYPE
    解析対象のコレクションに含まれる銘柄の種類
    INDEX_FOREX:すべてのFXメジャーで構成される銘柄のコレクションを構築する。28個の銘柄のセット。
    INDEX_CUSTOM:銘柄のコレクションの指定が必要。
    ENUM_PRICE
    計算で使用する価格系列を選択できるようにする。なお、価格情報は、インデックス0を最新価格とするシリーズとして保存される。

    CLOSE:終値
    MEDIAN:価格の中央値((高値+安値)/2)
    TYPICAL:典型的な価格((高値+安値+終値)/3)
    WEIGHTED:加重された価格((高値+安値+終値+終値)/4)

     ENUM_DIFF_TYPE
     選択した価格系列に適用可能な異なる差分法
    DIFF_LOG:対数の差分
    DIFF_PERCENT:割合の差分

    CJanusクラスを使用するには、5つのメソッドを理解する必要があります。CJanusオブジェクトを作成した後、どのメソッドも呼び出す前に、まずInitializeメンバー関数を呼び出す必要があります。このメソッドは2つの重要なことをおこないます。まず、CsymbolCollectionオブジェクトを初期化し、インデックスを構成する選択された銘柄でそれを埋めるのです。そして、コレクション内の銘柄の履歴バーをチェックし、同期させます。

    //+------------------------------------------------------------------+
    //|Janus class for calculating janus family of indicators.           |
    //+------------------------------------------------------------------+
    class CJanus
      {
    private:
       CSymbolCollection* m_symbol_list;    //object container of symbols
       ENUM_PRICE         m_price_type;     //applied price for calculations
       ENUM_DIFF_TYPE     m_diff_type;      //method of differencing applied
       ENUM_INDEX_TYPE    m_index_type;     //type of index
       int                m_hist_size;      // synchronized size of history across all selected symbols in collection
       ENUM_TIMEFRAMES    m_list_timeframe; //timeframe for bars to be used in calculations
       //---private methods
       double            market_return(const uint barshift, const uint symbolshift);
       void              market_offense_defense(const uint barshift,const uint symbolshift,const uint rs_period,double& out_offense,double& out_defense);
       double            rs(const uint barshift,const uint symbolshift,const uint rs_period,uint lag=0);
       double            rs_off_def(const uint barshift, const uint symbolshift,const uint lag,const double median,const double index_offense, const double index_defense, double &array[]);
       //---
    public:
       //constructor
                         CJanus(void):m_symbol_list(NULL),
                         m_price_type(WRONG_VALUE),
                         m_diff_type(WRONG_VALUE),
                         m_index_type(WRONG_VALUE),
                         m_list_timeframe(WRONG_VALUE),
                         m_hist_size(0)
         {
    
         }
       // destructor
                        ~CJanus(void)
         {
          if(CheckPointer(m_symbol_list)==POINTER_DYNAMIC)
             delete m_symbol_list;
         }
       //public methods
       bool              Initialize(const ENUM_PRICE set_price_type, const ENUM_DIFF_TYPE set_diff_type, const ENUM_INDEX_TYPE set_index_type, ENUM_TIMEFRAMES set_timeframe,const int history_size, const string symbol_list);
       bool              Update(void);
       int               HistorySize(void);
       string            GetSymbolAt(const int sym_ind);
       int               GetSymbolsTotal(void);
       double            CalculateReturn(const uint barshift, const string symbol__);
       double            CalculateBenchMarkReturn(const uint barshift);
       double            CalculateSummedIndexReturn(const uint barshift);
       void              CalculateBenchMarkOffenseDefense(const uint barshift,const uint rs_period,double& out_offense,double& out_defense);
       void              CalculateMarketOffenseDefense(const uint barshift,const string symbol__,const uint rs_period,double& out_offense,double& out_defense);
       double            CalculateRelativeStrength(const uint barshift,const string symbol__,const uint rs_period,uint lag=0);
       void              CalculateRelativeStrengthLeaderLaggard(const uint barshift,const uint rs_period,const double rs_percent_top,double& leader,double& laggard);
       double            CalculateRelativeStrengthSpread(const uint barshift,const uint rs_period,const double rs_percent_top);
       double            CalculateRelativeStrengthSpreadChange(const uint barshift,const uint rs_period,const double rs_percent_top);
    
      };

    Initialize()メソッドの入力パラメータについて、下表に説明します。

    パラメータ
    詳細
     データ型
    set_price_type
    すべての計算に使用される基礎となる価格系列を設定する列挙型
    ENUM_PRICE
    set_diff_type
    価格系列に適用される差分法を設定
    ENUM_DIFF_TYPE
    set_index_type
    銘柄コレクションの種類をカスタムまたはFXメジャーのみに設定。カスタムを選択した場合、symbol_listパラメータで銘柄を指定する必要
    ENUM_INDEX_TYPE
    set_timeframe
    計算に使用するバーの時間枠を指定
    ENUM_TIMEFRAME

    履歴サイズ
    要求されるべきバーの最大数を定義複数の銘柄を扱っているため、コレクション内のすべての銘柄が同じ数の履歴バーデータを利用できるようにするための方法です。
    integer

     symbol_list
    set_index_typeがINDEX_CUSTOM に設定されている場合、ユーザーは分析する銘柄のコレクションを構成する銘柄のコンマ区切りリストを指定する必要があります。
     string
    その他、注目すべきメソッドは次の通りです。
    •  Update()メソッドは、銘柄データを更新し、最新の相場を取得します。
    • HistorySize()メソッドは、利用可能な履歴バーの数を返します。この数値は、Initializeメソッドを呼び出す際に指定された数値よりも小さくすることができます。これは、すべての銘柄における履歴が変化する可能性があるため、この関数は同期した履歴のサイズを返します。
    • GetSymbolsTotals()は、追加され、ターミナルで利用可能であることが確認された銘柄の数を返します。
    • GetSymbolAt()は、インデックスで銘柄名を取得します。

    Calculateを先頭に持つ残りのメソッドは、計算を実行し、1つまたは2つの値を返します。これらはすべて、最初の入力パラメータとして、計算が実行される小節のバーシフトを持ちます。
    各メソッドについては、各種指標を実施しながら解説していきます。


    パフォーマンスを測定する

    すでに述べたように、パフォーマンスはリターンを計算することで測定されます。私たちの実装では、適用価格とリターンの計算方法を選択するオプションがあります。CalculateReturns() メソッドを呼び出すと、指定されたバーシフトと銘柄のリターンが計算されます。

    //+------------------------------------------------------------------+
    //|private method that calculates the returns                        |
    //+------------------------------------------------------------------+
    double CJanus::market_return(const uint barshift, const uint symbolshift)
      {
       double curr,prev;
       curr=0;
       prev=1.e-60;
    
       switch(m_price_type)
         {
          case CLOSE:
             curr=m_symbol_list.GetCloseAtPos(symbolshift,barshift);
             prev=m_symbol_list.GetCloseAtPos(symbolshift,barshift+1);
             break;
          case MEDIAN:
             curr=m_symbol_list.GetMedianAtPos(symbolshift,barshift);
             prev=m_symbol_list.GetMedianAtPos(symbolshift,barshift+1);
             break;
          case TYPICAL:
             curr=m_symbol_list.GetTypicalAtPos(symbolshift,barshift);
             prev=m_symbol_list.GetTypicalAtPos(symbolshift,barshift+1);
             break;
          case WEIGHTED:
             curr=m_symbol_list.GetWeightedAtPos(symbolshift,barshift);
             prev=m_symbol_list.GetWeightedAtPos(symbolshift,barshift+1);
             break;
          default:
             return WRONG_VALUE;
         }
    
       if(prev==0)
          return(WRONG_VALUE);
    
       switch(m_diff_type)
         {
          case DIFF_PERCENT:
             return(((curr-prev)/prev)*100);
          case DIFF_LOG:
             return(MathLog(curr/prev));
          default:
             return(WRONG_VALUE);
         }
      }
    //+------------------------------------------------------------------+
    //|public method to calculate returns for single bar                 |
    //+------------------------------------------------------------------+
    double CJanus::CalculateReturn(const uint barshift, const string symbol_)
      {
       int sshift=m_symbol_list.GetIndex(symbol_);
       if(sshift>-1)
          return(market_return(barshift,sshift));
       else
          return(WRONG_VALUE);
      }

    以下のIndexReturns指標のコードは、チャート銘柄のリターンと、ベンチマークのリターンを表示します。

    //+------------------------------------------------------------------+
    //|                                                 IndexReturns.mq5 |
    //|                        Copyright 2023, MetaQuotes Software Corp. |
    //|                                             https://www.mql5.com |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2023, MetaQuotes Software Corp."
    #property link      "https://www.mql5.com"
    #property version   "1.00"
    #property indicator_separate_window
    #property indicator_buffers 2
    #property indicator_plots   2
    //--- plot IndexReturns
    #property indicator_label1  "IndexReturns"
    #property indicator_type1   DRAW_LINE
    #property indicator_color1  clrRed
    #property indicator_style1  STYLE_SOLID
    #property indicator_width1  1
    //--- plot Returns
    #property indicator_label2  "Returns"
    #property indicator_type2   DRAW_LINE
    #property indicator_color2  clrBlue
    #property indicator_style2  STYLE_SOLID
    #property indicator_width2  1
    #include<Janus.mqh>
    //inputs
    input ENUM_PRICE AppliedPrice=CLOSE;
    input ENUM_DIFF_TYPE AppliedDiffType=DIFF_LOG;
    input ENUM_INDEX_TYPE SelectIndexType=INDEX_FOREX_MAJORS;
    input string BenchMarkSymbols="";
    input int MaxBars = 300;
    //--- indicator buffers
    double         IndexReturnsBuffer[];
    double         ReturnsBuffer[];
    CJanus *janus;
    
    //+------------------------------------------------------------------+
    //| Custom indicator initialization function                         |
    //+------------------------------------------------------------------+
    int OnInit()
      {
    //--- indicator buffers mapping
       SetIndexBuffer(0,IndexReturnsBuffer,INDICATOR_DATA);
       SetIndexBuffer(1,ReturnsBuffer,INDICATOR_DATA);
      
       ArraySetAsSeries(IndexReturnsBuffer,true);
       ArraySetAsSeries(ReturnsBuffer,true);  
    //--- 
       PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,0.0);
       PlotIndexSetDouble(1,PLOT_EMPTY_VALUE,0.0);
       PlotIndexSetString(1,PLOT_LABEL,_Symbol+" Returns");
    //---
       IndicatorSetInteger(INDICATOR_DIGITS,8);
       IndicatorSetString(INDICATOR_SHORTNAME,"IndexReturns("+_Symbol+")");
    //---
       janus=new CJanus();   
    //--- 
       if(!janus.Initialize(AppliedPrice,AppliedDiffType,SelectIndexType,PERIOD_CURRENT,MaxBars,BenchMarkSymbols))
         return(INIT_FAILED);     
    //---
       return(INIT_SUCCEEDED);
      }
    //+------------------------------------------------------------------+
    //|Custom indicator deinitialization function                        |
    //+------------------------------------------------------------------+
    void OnDeinit(const int reason)
     {
    //--- 
      switch(reason)
       {
        case REASON_INITFAILED:
          ChartIndicatorDelete(ChartID(),ChartWindowFind(),"IndexReturns("+_Symbol+")");
           break;
        default:
           break;
       } 
    //---
       if(CheckPointer(janus)==POINTER_DYNAMIC)
          delete janus;       
     }   
    //+------------------------------------------------------------------+
    //| Custom indicator iteration function                              |
    //+------------------------------------------------------------------+
    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 limit;
       if(prev_calculated<=0)
         {
          limit=janus.HistorySize()-2;
          PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,rates_total-limit+1);
          PlotIndexSetInteger(1,PLOT_DRAW_BEGIN,rates_total-limit+1);
         } 
       else 
         limit=rates_total-prev_calculated;
       //---  
       if(!janus.Update())
         return(prev_calculated); 
         
       for(int bar=limit;bar>=1;bar--)
        {
         ReturnsBuffer[bar]=janus.CalculateReturn(bar,_Symbol);
         IndexReturnsBuffer[bar]=janus.CalculateBenchMarkReturn(bar);
        }    
    //--- return value of prev_calculated for next call
       return(rates_total);
      }
    //+------------------------------------------------------------------+
    

    IndexReturns指標

    IndexReturns指標

    ベンチマーク

    ベンチマークリターンを算出するために、対応する各時点の銘柄の個別リターンを使用し、中央値を求めます。この計算は、CalculateBenchmarkReturns()メソッドで実装されています。 

    //+------------------------------------------------------------------+
    //|public method to calculate index returns                          |
    //+------------------------------------------------------------------+
    double CJanus::CalculateBenchMarkReturn(const uint barshift)
      {
       double sorted[];
       int size=m_symbol_list.GetCollectionLength();
    
       if(size<=0)
          return(WRONG_VALUE);
    
       ArrayResize(sorted,size);
    
       for(int i=0; i<size; i++)
         {
          sorted[i]=market_return(barshift,i);
         }
    
       if(!ArraySort(sorted))
         {
          Print("sorting error ",__LINE__," ",__FUNCTION__);
          return(0);
         }
    
       return(MathMedian(sorted));
      }


    攻守

    相対的なパフォーマンスを知るために、アンダーソンは銘柄の攻守のスコアという概念を発想しました。攻めのスコアは、ベンチマークのリターンが平均を上回ったときに達成されるパフォーマンスです。 

    守りのスコアは、ベンチマークのリターンが平均を下回った場合に達成されるリターンを用いて同様に算出されます。この値は、市場価格が乱高下しているときに、銘柄がどれだけ持ちこたえることができるかを示しています。 

    これらの値を算出するために、ベンチマークリターンは、攻めのベンチマークリターンと守りのベンチマークリターンの2つに分割されます。これらは、適切なウィンドウ長を選択し、そこから平均ベンチマークを計算することで算出されます。 

    CJanusクラスでは、指定されたウィンドウの平均値として中央値が使用されます。攻めのベンチマークリターンは、達成されたリターンと平均ベンチマークリターンとの差を、平均より大きいか等しいベンチマークについて累積することによって得られます。

    守りのベンチマークリターンは、ウィンドウの平均値よりも低いベンチマーク値を用いて同様に計算されます。

    //+------------------------------------------------------------------+
    //|public method to calculate Index offense and defense scores       |
    //+------------------------------------------------------------------+
    void CJanus::CalculateBenchMarkOffenseDefense(const uint barshift,const uint rs_period,double& out_offense,double& out_defense)
      {
       out_defense=out_offense=WRONG_VALUE;
    
       double index[],sorted[],median,i_offense,i_defense;
       median=i_offense=i_defense=0;
    
       ArrayResize(index,rs_period);
       ArrayResize(sorted,rs_period);
    
       int begin=0;
    
       for(int i=0; i<(int)rs_period; i++)
         {
          index[i]=CalculateBenchMarkReturn(barshift+i);
          if(i>=begin)
             sorted[i-begin]=index[i];
         }
    
       if(!ArraySort(sorted))
         {
          Print("sorting error ",__LINE__," ",__FUNCTION__);
          return;
         }
    
       median=MathMedian(sorted);
    
       i_offense=1.e-30;
       i_defense=-1.e-30;
    
       for(int i=begin; i<(int)rs_period; i++)
         {
          if(index[i]>=median)
             i_offense+=index[i]-median;
          else
             i_defense+=index[i]-median;
         }
    
       if(i_offense<0 || i_defense>0)
         {
    #ifdef DEBUG
          Print("error invalid figures ","Offensive ",i_offense," Defensive ",i_defense);
    #endif
          return;
         }
    
       out_offense=i_offense;
       out_defense=i_defense;
    
       return;
      }

    攻めおよび守りのベンチマークリターンが算出された時点で、銘柄の攻守の点数を算出するために使用されます。ベンチマークに使用したのと同じウィンドウで、ベンチマークのリターンがウィンドウ平均より大きいか等しいすべてのタイムポイントで、達成されたリターンが一緒に加算されます。そして、この合計値を対応する攻撃型ベンチマークの分数で表し、100を乗じます。 

    同様の計算を、守りのベンチマークリターンを用いておこないます。CalculateSymbolOffenseDefense()メソッドは、攻守両方のスコアを計算することを実装しています。

    //+------------------------------------------------------------------+
    //|public method to calculate market offense and defense                                                                  |
    //+------------------------------------------------------------------+
    void CJanus::CalculateSymbolOffenseDefense(const uint barshift,const string symbol__,const uint rs_period,double &out_offense,double &out_defense)
      {
       out_defense=out_offense=0.0;
       int sshift=m_symbol_list.GetIndex(symbol__);
       if(sshift>-1)
          market_offense_defense(barshift,sshift,rs_period,out_offense,out_defense);
       else
          return;
      }
    //+------------------------------------------------------------------+
    //|private method that calculates market defense and offense values  |
    //+------------------------------------------------------------------+
    void CJanus::market_offense_defense(const uint barshift,const uint symbolshift,const uint rs_period,double& out_offense,double& out_defense)
      {
       out_defense=out_offense=0.0;
    
       double index[],sorted[],median,i_offense,i_defense;
       median=i_offense=i_defense=0;
    
       ArrayResize(index,rs_period);
       ArrayResize(sorted,rs_period);
    
       int begin=0;
    
       for(int i=0; i<(int)rs_period; i++)
         {
          index[i]=CalculateBenchMarkReturn(barshift+i);
          if(i>=begin)
             sorted[i-begin]=index[i];
         }
    
       if(!ArraySort(sorted))
         {
          Print("sorting error ",__LINE__," ",__FUNCTION__);
          return;
         }
    
       median=MathMedian(sorted);
    
       i_offense=1.e-30;
       i_defense=-1.e-30;
    
       for(int i=begin; i<(int)rs_period; i++)
         {
          if(index[i]>=median)
             i_offense+=index[i]-median;
          else
             i_defense+=index[i]-median;
         }
    
       if(i_offense<0 || i_defense>0)
         {
    #ifdef DEBUG
          Print("error invalid figures ","Offensive ",i_offense," Defensive ",i_defense);
    #endif
          return;
         }
    
       double m_offense,m_defense;
       m_offense=m_defense=0;
    
       for(int i=0; i<(int)rs_period; i++)
         {
          if(index[i]>=median)
             m_offense+=market_return(barshift+i,symbolshift);
          else
             m_defense+=market_return(barshift+i,symbolshift);
         }
    
       out_defense= (m_defense/i_defense) * 100;
       out_offense= (m_offense/i_offense) * 100;
    
      }

    攻守のスコアをプロットすることで、アンダーソンは市場の状況によって現れるパターンに着目しました。ポジティブフィードバックの時間帯は、スコアが大きく分散しており、パフォーマンスが最も良い銘柄と悪い銘柄の間に大きな差がありました。一方、ネガティブフィードバックの時期には、ベストパフォーマーとワーストパフォーマーの差が縮まりました。アンダーソンはこれをインデックスの膨張と収縮と呼びました。

    以下のコードは、OffensiveDefensiveScatterPlotスクリプトを説明するもので、上記の指標と同様の入力を持ち、攻撃と防御のスコアのプロットをアニメーションとして描画するものです。

    //+------------------------------------------------------------------+
    //|                                OffensiveDefensiveScatterPlot.mq5 |
    //|                        Copyright 2023, MetaQuotes Software Corp. |
    //|                                             https://www.mql5.com |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2023, MetaQuotes Software Corp."
    #property link      "https://www.mql5.com"
    #property version   "1.00"
    #property script_show_inputs
    #include <Graphics\Graphic.mqh>
    #include<Janus.mqh>
    
    input ENUM_PRICE AppliedPrice=CLOSE;
    input ENUM_DIFF_TYPE AppliedDiffType=DIFF_LOG;
    input ENUM_INDEX_TYPE SelectIndexType=INDEX_FOREX_MAJORS;
    input uint AppliedPeriod = 25;
    input string BenchMarkSymbols="";
    input int MaxBars = 50;
    
    CJanus janus;
    //+------------------------------------------------------------------+
    //| Script program start function                                    |
    //+------------------------------------------------------------------+
    void OnStart()
      {
    //---
       if(!janus.Initialize(AppliedPrice,AppliedDiffType,SelectIndexType,PERIOD_CURRENT,MaxBars,BenchMarkSymbols))
         {
          Print("error with janus object");
          return;
         }
    //---
       janus.Update();
    //---
       double y[];
       double x[];
    
       int size=janus.GetSymbolsTotal();
    
       ArrayResize(x,size);
       ArrayResize(y,size);
    
       long chart=0;
       string name="OffenseDefense";
    
       for(int k=MaxBars-(int)AppliedPeriod-1; k>=0; k--)
         {
          for(int i=0; i<size; i++)
            {
             string ssy=janus.GetSymbolAt(i);
             janus.CalculateSymbolOffenseDefense(k,ssy,AppliedPeriod,y[i],x[i]);
            }
    
          CGraphic graphic;
          if(ObjectFind(chart,name)<0)
             graphic.Create(chart,name,0,0,0,780,380);
          else
             graphic.Attach(chart,name);
          //---
          graphic.CurveAdd(x,y,ColorToARGB(clrBlue),CURVE_POINTS,"DefensiveOffensive ");
          //---
          graphic.CurvePlotAll();
          //---
          graphic.Update();
          Sleep(1*1000);
          graphic.Destroy();
          ChartRedraw();
    
         }
    
       ChartSetInteger(0,CHART_SHOW,true);
    
      }
    //+------------------------------------------------------------------+
    

    このスクリプトでは、以下のグラフィックが生成されます。

    攻守の散布図


    相対力

    銘柄の攻撃力と防御力を以下の式で結ぶと、その銘柄の相対力がわかります。

    相対力の計算式

     この式の導出は、アンダーソンの著書に記載されています。相対力は、CalculateRelativeStrength()メソッドで計算されます。

    //+------------------------------------------------------------------+
    //|public method to calculate relative strength                      |
    //+------------------------------------------------------------------+
    double CJanus::CalculateRelativeStrength(const uint barshift,const string symbol__,const uint rs_period,uint lag=0)
      {
       int sshift=m_symbol_list.GetIndex(symbol__);
       if(sshift>-1)
          return(rs(barshift,sshift,rs_period,lag));
       else
          return(WRONG_VALUE);
      }
    


    //+------------------------------------------------------------------+
    //|private method that calculates the relative strength              |
    //+------------------------------------------------------------------+
    double CJanus::rs(const uint barshift,const uint symbolshift,const uint rs_period,uint lag=0)
      {
       if(lag>=rs_period)
          return(WRONG_VALUE);
    
       double index[],sorted[],median,i_offense,i_defense;
       median=i_offense=i_defense=0;
    
       ArrayResize(index,rs_period);
       ArrayResize(sorted,rs_period);
    
       int begin=(int)lag;
    
       for(int i=0; i<(int)rs_period; i++)
         {
          index[i]=CalculateBenchMarkReturn(barshift+i);
          if(i>=begin)
             sorted[i-begin]=index[i];
         }
    
       if(!ArraySort(sorted))
         {
          Print("sorting error ",__LINE__," ",__FUNCTION__);
          return(EMPTY_VALUE);
         }
    
       median=MathMedian(sorted);
    
       i_offense=1.e-30;
       i_defense=-1.e-30;
    
       for(int i=begin; i<(int)rs_period; i++)
         {
          if(index[i]>=median)
             i_offense+=index[i]-median;
          else
             i_defense+=index[i]-median;
         }
    
       if(i_offense<0 || i_defense>0)
         {
    #ifdef DEBUG
          Print("error invalid figures ","Offensive ",i_offense," Defensive ",i_defense);
    #endif
          return(WRONG_VALUE);
         }
    
       return(rs_off_def(barshift,symbolshift,lag,median,i_offense,i_defense,index));
      }

    以下の指標コードは、チャート銘柄の相対力をプロットします。

    //+------------------------------------------------------------------+
    //|                                             RelativeStrength.mq5 |
    //|                        Copyright 2023, MetaQuotes Software Corp. |
    //|                                             https://www.mql5.com |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2023, MetaQuotes Software Corp."
    #property link      "https://www.mql5.com"
    #property version   "1.00"
    #property indicator_separate_window
    #property indicator_buffers 1
    #property indicator_plots   1
    //--- plot RelativeStrength
    #property indicator_label1  "RelativeStrength"
    #property indicator_type1   DRAW_LINE
    #property indicator_color1  clrRed
    #property indicator_style1  STYLE_SOLID
    #property indicator_width1  1
    #include<Janus.mqh>
    //inputs
    input ENUM_PRICE AppliedPrice=CLOSE;
    input ENUM_DIFF_TYPE AppliedDiffType=DIFF_LOG;
    input ENUM_INDEX_TYPE SelectIndexType=INDEX_FOREX_MAJORS;
    input uint AppliedPeriod = 7;
    input string BenchMarkSymbols="";
    input int MaxBars = 300;
    //--- indicator buffers
    double         RelativeStrengthBuffer[];
    //---
    CJanus *janus;
    //+------------------------------------------------------------------+
    //| Custom indicator initialization function                         |
    //+------------------------------------------------------------------+
    int OnInit()
      {
    //--- indicator buffers mapping
       SetIndexBuffer(0,RelativeStrengthBuffer,INDICATOR_DATA);
    //---    
       ArraySetAsSeries(RelativeStrengthBuffer,true);
    //---   
       PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,0.0);
       IndicatorSetString(INDICATOR_SHORTNAME,"RS("+_Symbol+")("+string(AppliedPeriod)+")");
    //---
       janus=new CJanus();   
    //--- 
       if(!janus.Initialize(AppliedPrice,AppliedDiffType,SelectIndexType,PERIOD_CURRENT,MaxBars,BenchMarkSymbols))
         return(INIT_FAILED);     
    //---
       
       return(INIT_SUCCEEDED);
      }
    //+------------------------------------------------------------------+
    //|Custom indicator deinitialization function                        |
    //+------------------------------------------------------------------+
    void OnDeinit(const int reason)
     {
    //--- 
      switch(reason)
       {
        case REASON_INITFAILED:
          ChartIndicatorDelete(ChartID(),ChartWindowFind(),"RS("+_Symbol+")("+string(AppliedPeriod)+")");
           break;
        default:
           break;
       } 
    //---
       if(CheckPointer(janus)==POINTER_DYNAMIC)
          delete janus;       
     }     
    //+------------------------------------------------------------------+
    //| Custom indicator iteration function                              |
    //+------------------------------------------------------------------+
    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[],p
                    const long &tick_volume[],
                    const long &volume[],
                    const int &spread[])
      {
    //---
       int limit;
       if(prev_calculated<=0)
         {
          limit=janus.HistorySize()-int(AppliedPeriod+2);
          PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,rates_total-limit+1);
         } 
       else 
         limit=rates_total-prev_calculated;
       //---  
       if(!janus.Update())
         return(prev_calculated); 
         
       for(int i=limit;i>=1;i--)
        {
         RelativeStrengthBuffer[i]=janus.CalculateRelativeStrength(i,_Symbol,AppliedPeriod);
        } 
    //--- return value of prev_calculated for next call
       return(rates_total);
      }
    //+------------------------------------------------------------------+
    

    RelativeStrength指標

    RelativeStrength指標

    相対力の値を使用すると、時間の経過とともに発生する拡大や縮小をより明確に把握することができます。これは、相対力の最高値と最低値の指標プロットによって以下に示されます。 

    上下RS

    この指標のコードを以下に示します。

    //+------------------------------------------------------------------+
    //|                                    RelativeStrenghtBestWorst.mq5 |
    //|                        Copyright 2023, MetaQuotes Software Corp. |
    //|                                             https://www.mql5.com |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2023, MetaQuotes Software Corp."
    #property link      "https://www.mql5.com"
    #property version   "1.00"
    #property indicator_separate_window
    #property indicator_buffers 2
    #property indicator_plots   2
    //--- plot Upper
    #property indicator_label1  "Upper"
    #property indicator_type1   DRAW_LINE
    #property indicator_color1  clrBlue
    #property indicator_style1  STYLE_SOLID
    #property indicator_width1  1
    //--- plot Lower
    #property indicator_label2  "Lower"
    #property indicator_type2   DRAW_LINE
    #property indicator_color2  clrRed
    #property indicator_style2  STYLE_SOLID
    #property indicator_width2  1
    #include<Janus.mqh>
    //inputs
    input ENUM_PRICE AppliedPrice=CLOSE;
    input ENUM_DIFF_TYPE AppliedDiffType=DIFF_LOG;
    input ENUM_INDEX_TYPE SelectIndexType=INDEX_FOREX_MAJORS;
    input uint AppliedPeriod = 25;
    input string BenchMarkSymbols="";
    input int MaxBars = 300;
    //--- indicator buffers
    double         UpperBuffer[];
    double         LowerBuffer[];
    double rsv[];
    
    CJanus *janus;
    //+------------------------------------------------------------------+
    //| Custom indicator initialization function                         |
    //+------------------------------------------------------------------+
    int OnInit()
      {
    //--- indicator buffers mapping
       SetIndexBuffer(0,UpperBuffer,INDICATOR_DATA);
       SetIndexBuffer(1,LowerBuffer,INDICATOR_DATA);
    //---   
       PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,0.0);
       PlotIndexSetDouble(1,PLOT_EMPTY_VALUE,0.0);
    //--- 
       ArraySetAsSeries(UpperBuffer,true);
       ArraySetAsSeries(LowerBuffer,true);
    //---    
       IndicatorSetString(INDICATOR_SHORTNAME,"RS_Upperlower("+_Symbol+")("+string(AppliedPeriod)+")");
    //---   
       janus=new CJanus();   
    //--- 
       if(!janus.Initialize(AppliedPrice,AppliedDiffType,SelectIndexType,PERIOD_CURRENT,MaxBars,BenchMarkSymbols))
         return(INIT_FAILED);  
         
       ArrayResize(rsv,janus.GetSymbolsTotal());     
    //---
       return(INIT_SUCCEEDED);
      }
    //+------------------------------------------------------------------+
    //|Custom indicator deinitialization function                        |
    //+------------------------------------------------------------------+
    void OnDeinit(const int reason)
     {
    //--- 
      switch(reason)
       {
        case REASON_INITFAILED:
          ChartIndicatorDelete(ChartID(),ChartWindowFind(),"RS_Upperlower("+_Symbol+")("+string(AppliedPeriod)+")");
           break;
        default:
           break;
       } 
    //---
       if(CheckPointer(janus)==POINTER_DYNAMIC)
          delete janus;       
     }       
    //+------------------------------------------------------------------+
    //| Custom indicator iteration function                              |
    //+------------------------------------------------------------------+
    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 limit;
       if(prev_calculated<=0)
         {
          limit=janus.HistorySize()-int(AppliedPeriod+2);
          PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,rates_total-limit+1);
         } 
       else 
         limit=rates_total-prev_calculated;
       //---  
       if(!janus.Update())
         return(prev_calculated); 
         
       for(int i=limit;i>=1;i--)
        {
          for(int k=0;k<ArraySize(rsv);k++)
            {
             string sym=janus.GetSymbolAt(k);
             rsv[k]= janus.CalculateRelativeStrength(i,sym,AppliedPeriod);
            }
            
          ArraySort(rsv);
          
         UpperBuffer[i]=rsv[ArraySize(rsv)-1];
         LowerBuffer[i]=rsv[0];    
        } 
    //--- return value of prev_calculated for next call
       return(rates_total);
      }
    //+------------------------------------------------------------------+
    


    相対力のリーダーとラガード

    ベストパフォーマーとワーストパフォーマーのリターンで示される相対的なパフォーマンスを平均化するとそれぞれが相対力のリーダー、相対力のラガードとなります。これらの値は、フィードバックの状態によって、取引に最適なタイミングを示すものです。相対力(rs)のリーダーとラガードは、ベストパフォーマーとワーストパフォーマーの数を指定し、平均を算出することで算出します。 

    //+------------------------------------------------------------------+
    //|public method to calculate relative strength leaders and laggards |
    //+------------------------------------------------------------------+
    void CJanus::CalculateRelativeStrengthLeaderLaggard(const uint barshift,const uint rs_period,const double rs_percent_top,double& leader,double& laggard)
      {
       leader=laggard=0;
       
       uint lag=rs_period;
    
       double sorted[];
       int iwork[],k,n,isub;
       k=isub=-1;
    
       int size=m_symbol_list.GetCollectionLength();
    
       ArrayResize(sorted,size);
       ArrayResize(iwork,size);
    
       for(int i=0; i<size; i++)
         {
          sorted[i]=rs(barshift,uint(i),rs_period,lag);
          iwork[i]=i;
         }
    
       MathQuickSortAscending(sorted,iwork,0,size-1);
    
       k=(int)(rs_percent_top*(size+1))-1;
       if(k<0)
          k=0;
       n=k+1;
    
       while(k>=0)
         {
          isub=iwork[k];
          for(uint i=0; i<lag; i++)
            {
             laggard+=market_return(barshift+i,isub);
            }
          isub=iwork[size-1-k];
          for(uint i=0; i<lag; i++)
            {
             leader+=market_return(barshift+i,isub);
            }
          --k;
         }
       leader/=n*lag;
       laggard/=n*lag;
    
       return;
      }

    CalculateRelativeStrengthLeadersLaggards()メソッドのrs_percent_top入力は、平均化された相対力を計算するために使用する銘柄の割合を示すものです。例えば、rs_percent_topを0.1に設定すると、上位10%と下位10%の銘柄がリーダー、ラガードの計算に使われることになります。 

    以下は、Leaders and Laggards指標のスクリーンショットです。

    Leaders and Laggards


    スプレッド

    市場がポジティブフィードバックに支配されると、トレンドの方向によって、最も弱い証券と最も強い証券が乖離します。上昇トレンドでは、トレーダーは強い証券を支持し、下降トレンドでは弱い証券を空売りしています。

    価格がレンジに収まっている場合、最も弱い証券と最も強い証券の差が比較的小さく、収束していると言えるでしょう。このような動きを認識するために、最も弱い証券と最も強い証券の間の相対力の差の平均値を計算します。これが相対力スプレッドを測るものです。

    相対力スプレッドは、CalculateRelativeStrengthSpread()メソッドで実装されています。

    //+------------------------------------------------------------------+
    //|public method to calculate the relative strength spread.          |
    //+------------------------------------------------------------------+
    double CJanus::CalculateRelativeStrengthSpread(const uint barshift,const uint rs_period,const double rs_percent_top)
      {
       double sorted[],width,div;
       int k=0;
       int size=m_symbol_list.GetCollectionLength();
    
       width=div=0;
       ArrayResize(sorted,size);
    
       for(int i=0; i<size; i++)
         {
          sorted[i]=rs(barshift,uint(i),rs_period);
         }
    
       if(!ArraySort(sorted))
         {
          Print("sorting error ",__LINE__," ",__FUNCTION__);
          return(WRONG_VALUE);
         }
    
       k=(int)(rs_percent_top*(size+1))-1;
    
       if(k<0)
          k=0;
       double n=double(k+1);
    
       while(k>=0)
         {
          width+=sorted[size-1-k]-sorted[k];
          --k;
         }
    
       return(width/=n);
      }

    相対力スプレッド指標

    相対力を示す指標

    スプレッドとrsリーダー/ラガード指標を組み合わせることで、具体的な売買ルールを策定することができます。アンダーソンによれば、最高のチャンスは、対象となるインデックスの相対力の上限と下限に近い、あるいは下限にある銘柄を取引することです。

    //+------------------------------------------------------------------+
    //|public method to calculate relative strength leaders and laggards |
    //+------------------------------------------------------------------+
    void CJanus::CalculateRelativeStrengthLeaderLaggard(const uint barshift,const uint rs_period,const double rs_percent_top,double& leader,double& laggard)
      {
       leader=laggard=0;
       
       uint lag=1;
    
       double sorted[];
       int iwork[],k,n,isub;
       k=isub=-1;
    
       int size=m_symbol_list.GetCollectionLength();
    
       ArrayResize(sorted,size);
       ArrayResize(iwork,size);
    
       for(int i=0; i<size; i++)
         {
          sorted[i]=rs(barshift,uint(i),rs_period,lag);
          iwork[i]=i;
         }
    
       MathQuickSortAscending(sorted,iwork,0,size-1);
    
       k=(int)(rs_percent_top*(size+1))-1;
       if(k<0)
          k=0;
       n=k+1;
    
       while(k>=0)
         {
          isub=iwork[k];
          for(uint i=0; i<lag; i++)
            {
             laggard+=market_return(barshift+i,isub);
            }
          isub=iwork[size-1-k];
          for(uint i=0; i<lag; i++)
            {
             leader+=market_return(barshift+i,isub);
            }
          --k;
         }
       leader/=n*lag;
       laggard/=n*lag;
    
       return;
      }


    rsリーダーとの組み合わせでスプレッドのトレンドが上昇している場合、強いパフォーマーのネット買いと共にポジティブフィードバックを示すものです。相対力が最も高い銘柄を購入する好機です。代わりにrsのラガーが上昇しているのであれば、弱い銘柄を狙ってショートします。このような状況では、選択した銘柄に適用される効果的なエントリー方法とこれらのルールを組み合わせることが賢明かもしれません。 

    もう1つの選択肢は、リスクのフィルターとして、スプレッド指標を使用することです。ポジティブなフィードバックがある場合、市場のリスクは低く、ネガティブなフィードバックがある場合は、混乱する可能性があることを示す指標となります。おそらく市場には手を出さない方がいい時期です。 


      結論

       FXの銘柄の分析は、Janus factorの最適な応用とは言えないかもしれませんが、この記事の要点は、読者にこの方法を知ってもらうことです。詳しくは後述のアンダーソンの著書に掲載されています。 

      添付のZIPファイルには、記事中で参照したツールの全コードが含まれています。下の表は、それぞれの一覧です。

      指標を機能させるためには、まず、指標を構成するすべての銘柄の履歴データが利用可能であることを確認する必要があります。

      ファイル名 種類 詳細
      Mql5/include/Janus.mqh インクルード  CSymbolData, CSymbolCollection, CJanusクラスの定義を含むインクルードファイル。
      Mql5/scripts/OffensiveDefensiveScatterPlot.mq5 スクリプト 攻守の得点の散布図をアニメーションで描くスクリプト
      Mql5/indicators/IndexReturns.mq5 指標 銘柄とベンチマークのリターンを表示する指標のコード
      Mql5/indicators/RelativeStrengthBestWorst.mq5
      指標 相対力の最高値と最低値のプロットを示す指標
      Mql5/indicators/RelativeStrength.mq5
      指標 銘柄の相対力を表示する指標
      Mql5/indicators/RelativeStrengthSpread.mq5 指標 相対力スプレッドを表示する指標
      Mql5/indicatorr/RssLeaderlaggards.mq5 指標 RSのリーダーとラガードをプロットした指標



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

      MetaTraderのMultibot:1つのチャートから複数のロボットを起動させる MetaTraderのMultibot:1つのチャートから複数のロボットを起動させる
      今回は、個々のチャートにロボットの各インスタンスを設定する必要がなく、1つのチャートにのみ接続された状態で複数のチャートで使用できる汎用MetaTraderロボットを作成するための簡単なテンプレートについて考えてみます。
      ニューラルネットワークの実験(第5回):ニューラルネットワークに渡すための入力の正規化 ニューラルネットワークの実験(第5回):ニューラルネットワークに渡すための入力の正規化
      ニューラルネットワークはトレーダーのツールキットの究極のツールです。この仮定が正しいかどうかを確認してみましょう。MetaTrader 5は、取引でニューラルネットワークを使用するための自立した媒体としてアプローチされています。簡単な説明が記載されています。
      ニューラルネットワークの実験(第6回):価格予測のための自給自足ツールとしてのパーセプトロン ニューラルネットワークの実験(第6回):価格予測のための自給自足ツールとしてのパーセプトロン
      この記事では、パーセプトロンを自給自足の価格予測ツールとして使用する例として、一般的な概念と最もシンプルな既製のエキスパートアドバイザー(EA)を紹介し、その最適化の結果について説明します。
      MetaTrader 5をPostgreSQLに接続する方法 MetaTrader 5をPostgreSQLに接続する方法
      この記事では、MQL5コードをPostgresデータベースに接続するための4つの方法について説明し、そのうちの1つであるREST APIの開発環境をWindows Subsystem For Linux (WSL)を使用して設定するためのステップバイステップのチュートリアルを提供します。APIのデモアプリが、データを挿入してそれぞれのテーブルにクエリを実行するための対応MQL5コード、このデータを使用するためのデモエキスパートアドバイザー(EA)とともに提供されます。