English 中文 Español Deutsch 日本語 Português
preview
Реализация фактора Януса в MQL5

Реализация фактора Януса в MQL5

MetaTrader 5Примеры | 16 мая 2023, 14:06
1 277 0
Francis Dube
Francis Dube

Введение

Каждый трейдер знает, что рыночные цены следуют одной из двух общих моделей. Цены либо формируют тренды, либо движутся горизонтально. В результате стратегии участников рынка могут быть в значительной степени сведены либо к следованию тренду, либо к развороту. Фактор Януса — это теория поведения рынка, которая отражает эту двойственность. В статье мы раскроем его основы, а также продемонстрируем реализацию индикаторов, облегчающих этот метод анализа.

Обратная связь

Согласно теории Януса, рынки управляются взаимодействием между ценами и тем, как на них реагируют трейдеры. Андерсон сравнил это взаимодействие с системой обратной связи. Подробнее о ней можно узнать в Википедии. Когда рынки находятся в тренде, участники рынка демонстрируют уверенность, позволяя своей прибыли расти. При восходящем тренде более высокие цены подтверждают ожидания участников относительно тренда, что, в свою очередь, подстегивает покупки. Это приводит к еще большему росту цен. 

Рис. 1. Положительная обратная связь при восходящем тренде

Рисунок 1 иллюстрирует положительную обратную связь при восходящем тренде


При нисходящем тренде падающие цены могут вселить страх в трейдеров, заставляя их продавать, чтобы минимизировать убытки. Увеличение продаж оказывает давление на цены, толкая их дальше вниз. Таким образом, рыночные тренды являются примером положительной обратной связи. Движение цен будут ускоряться, пока не изменится реакция трейдеров на цену. 

Рис. 2. Положительная обратная связь при нисходящем тренде

На рисунке 2 показана положительная обратная связь при нисходящем тренде

Отрицательная обратная связь наблюдается, когда трейдеры мало доверяют рынку, поэтому они предпочитают фиксировать прибыль сразу после любого движения цены. Ранняя фиксация прибыли ослабляет рыночный импульс, ограничивая величину движения цены. Добавьте к этому постоянную борьбу между быками и медведями, и вы получите стабилизацию цен.

Рис. 3. Отрицательная обратная связь

Рис. 3. Отрицательная обратная связь на рынке

Движение капитала

Важным элементом этого понятия обратной связи являются типы символов, которые трейдеры предпочитают, когда преобладают разные условия. Анализ акций, проведенный Андерсоном, показывает, что при восходящем тренде трейдеры отдавали предпочтение акциям, показавшим относительно лучшие результаты, в то же время избавляясь от своих менее успешных аналогов. При нисходящем тренде акции с низкими доходами потеряли наибольшую стоимость, поскольку трейдеры нацеливались на них для фиксации прибыли. 

В отсутствие тренда было немного различий между более сильными и более слабыми акциями, поскольку трейдеры выбирали определенные ценовые уровни для входа и выхода. Учитывая это, он предположил, что, анализируя относительную эффективность набора акций, можно будет обнаружить периоды отрицательной и положительной обратной связи.


Расчеты Януса

Для определения производительности рассчитываются периодические доходы. На основе доходности набора изучаемых акций выводится среднее значение, которое будет использоваться в качестве базовой меры. Ее можно назвать эталонным доходом. Совокупность оцениваемых акций называется индексом.

В книге Фактор Януса - Руководство по диалектике рынка для следующих за трендом Андерсон описывает различные показатели акций, рассчитанные на основе доходности, которые он использует, чтобы получить представление о поведении рынка. 


Библиотека janus.mqh

Данные и подпрограммы, общие для всех вычислений, связанных с Janus, содержатся в janus.mqh. Включаемый файл содержит объявления для трех пользовательских типов:

    Класс 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. Были внесены изменения в направление индексации базовых буферов. Следует отметить, что все классы реализуют формат индексации справа налево с нулевым индексом, указывающим на последний бар. 

    Чтобы продемонстрировать методы фактора Януса, мы применим его к анализу валютных пар. Первоначально Андерсон разработал этот метод для анализа акций, но доступность данных по символам акций у большинства брокеров непостоянна.

    //+------------------------------------------------------------------+
    //| 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;
         }
      };
    //+------------------------------------------------------------------+
    

     

    Код начинается с трех пользовательских перечислений, описанных ниже.

    #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 - создать коллекцию символов, состоящую из всех основных форекс-валют. Набор из 28 символов.
    INDEX_CUSTOM - указать набор символов.
    ENUM_PRICE
    разрешить выбор ценового ряда для использования в расчетах. Информация о ценах хранится в виде рядов с последними ценами с индексом 0.

    CLOSE - цена закрытия
    MEDIAN - медианная цена - high+low/2
    TYPICAL - типичная цена - high+low+close/3
    WEIGHTED - взвешенная цена high+low+close+close/4

     ENUM_DIFF_TYPE
     Различные методы дифференцирования, которые можно применить к выбранному ценовому ряду.
    DIFF_LOG - использовать логарифмическую разность
    DIFF_PERCENT - использовать разность в процентах

    Чтобы использовать класс CJanus, необходимо освоить пять методов. После создания объекта CJanus и перед вызовом любого метода сначала должна быть вызвана функция-член Initialize. Метод делает две важные вещи - инициализирует объект 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
    Тип коллекции символов - либо пользовательский, либо только основные валюты форекс. Если выбран пользовательский, символы должны быть указаны в параметрах symbol_list
    ENUM_INDEX_TYPE
    set_timeframe
    Таймфрейм баров, которые будут использоваться в расчетах
    ENUM_TIMEFRAME

    history_size
    Максимальное количество баров, которое должно быть запрошено. Поскольку мы имеем дело с несколькими символами, это способ гарантировать, что все символы в коллекции имеют одинаковое количество доступных исторических данных баров.
    integer

     symbol_list
    Если для set_index_type установлено значение INDEX_CUSTOM, пользователь должен указать разделенный запятыми список символов, которые составят набор символов для анализа.
     string
    Другие важные методы:
    •  Метод Update() обновляет данные символа для получения последних котировок.
    • Метод HistorySize() возвращает количество доступных баров истории. Обратите внимание, что это число может быть меньше указанного при вызове метода Initialize. Это связано с тем, что история по всем символам может различаться, поэтому функция возвращает синхронизированный размер истории.
    • GetSymbolsTotals() возвращает количество символов, которые были добавлены и подтверждены как доступные в терминале.
    • GetSymbolAt() получает имя символа по индексу.

    Остальные методы с префиксом Calculate выполняют вычисления и возвращают одно или два значения. Все они имеют в качестве первого входного параметра сдвиг бара, на котором будет выполняться расчет.
    Каждый метод будет описан по мере реализации различных индикаторов.


    Измерение производительности

    Как уже упоминалось, производительность измеряется путем расчета дохода. Для нашей реализации у нас есть возможность выбрать применяемую цену и метод, используемый для расчета прибыли. Вызов метода 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));
      }


    Атака и защита

    Чтобы лучше понять относительную производительность, Андерсон придумал для символа понятие очков атаки (offense) и защиты (defense). Очки атаки — это производительность, достигнутая, когда эталонная доходность выше средней. 

    Очки защиты рассчитываются аналогичным образом с использованием доходности, полученной, когда эталонная доходность ниже средней. Это значение показывает, насколько хорошо символ держится, когда рыночные цены колеблются. 

    Чтобы рассчитать эти значения, эталонные доходности делятся на две части: эталонные доходности атаки и защиты. Они рассчитываются путем выбора подходящей длины окна, на основе которой рассчитывается средний ориентир. 

    В классе 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);
      }
    //+------------------------------------------------------------------+
    

    Индикатор относительной силы

    Индикатор относительной силы

    Используя значения относительной силы, мы получаем более четкое представление о расширении и сжатии, происходящих с течением времени. Это показано ниже на графике индикатора самых высоких и самых низких значений относительной силы. 

    Максимумы и минимумы относительной силы

    Код индикатора показан ниже.

    //+------------------------------------------------------------------+
    //|                                    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;
      }

    Входной параметр rs_percent_top для метода CalculateRelativeStrengthLeadersLaggards() обозначает долю символов, используемых для расчета усредненной относительной силы. Например, установка rs_percent_top на 0,1 означает, что верхние и нижние 10% символов будут использоваться при расчете лидеров и отстающих. 

    Ниже скриншот индикатора, отображающего лидеров и отстающих (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);
      }

    Индикатор разброса относительной силы

    Индикатор разброса относительной силы

    Комбинация индикаторов разброса и лидеров/отстающих может быть использована для разработки конкретных правил торговли. По мнению Андерсона, наилучшие возможности открываются при торговле символами, которые находятся вблизи или на верхней и нижней границах относительной силы рассматриваемого индекса.

    //+------------------------------------------------------------------+
    //|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, то нацельтесь на более слабые символы для продажи. В любой из этих ситуаций может быть целесообразно объединить эти правила с эффективным методом входа, который будет применяться к выбранному(ым) символу(ам). 

    Другой вариант — использовать индикатор разброса в качестве фильтра риска. Когда есть положительная обратная связь, рынок менее рискован, а отрицательная обратная связь является индикатором возможных изменчивых условий. В такое время лучше оставаться вне рынка. 


      Заключение

      Хотя анализ форекс-символов может быть не лучшим применением фактора Януса, весь смысл статьи в том, чтобы познакомить читателей с методом. Более подробную информацию о нем можно найти в книге Андерсона, ссылка на которую приведена ниже. 

      Прилагаемый 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 Индикатор Индикатор, отображающий лидеров и отстающих по относительной силе



      Перевод с английского произведен MetaQuotes Ltd.
      Оригинальная статья: https://www.mql5.com/en/articles/12328

      Прикрепленные файлы |
      IndexReturns.mq5 (4.12 KB)
      Janus.mqh (34.47 KB)
      mql5.zip (14.56 KB)
      Нейросети — это просто (Часть 42): Прокрастинация модели, причины и методы решения Нейросети — это просто (Часть 42): Прокрастинация модели, причины и методы решения
      Прокрастинация модели в контексте обучения с подкреплением может быть вызвана несколькими причинами, и решение этой проблемы требует принятия соответствующих мер. В статье рассмотрены некоторые из возможных причин прокрастинации модели и методы их преодоления.
      Поиск свечных паттернов с помощью MQL5 Поиск свечных паттернов с помощью MQL5
      В этой статье мы поговорим о том, как автоматически определять свечные паттерны с помощью MQL5.
      Как подключить MetaTrader 5 к PostgreSQL Как подключить MetaTrader 5 к PostgreSQL
      В статье описываются четыре метода подключения кода MQL5 к базе данных Postgres и предоставляется пошаговое руководство по настройке среды разработки для одного из них, REST API, с использованием подсистемы Windows для Linux (WSL). Показано демонстрационное приложение для API с соответствующим кодом MQL5 для вставки данных и запросов к соответствующим таблицам, а также демонстрационный советник для использования этих данных.
      Теория категорий в MQL5 (Часть 5): Эквалайзеры Теория категорий в MQL5 (Часть 5): Эквалайзеры
      Теория категорий представляет собой разнообразный и расширяющийся раздел математики, который лишь недавно начал освещаться в MQL5-сообществе. Эта серия статей призвана рассмотреть некоторые из ее концепций для создания открытой библиотеки и дальнейшему использованию этого замечательного раздела в создании торговых стратегий.