English Русский 中文 Deutsch 日本語 Português
preview
Implementando el factor Janus en MQL5

Implementando el factor Janus en MQL5

MetaTrader 5Ejemplos | 26 julio 2023, 16:09
324 0
Francis Dube
Francis Dube

Introducción

Todo tráder sabe que los precios de mercado siguen uno de dos patrones generales. Los precios o bien forman tendencias o bien se mueven horizontalmente. Como resultado, las estrategias de los participantes del mercado pueden reducirse en gran medida a seguir una tendencia o revertirla. El factor Janus es una teoría del comportamiento del mercado que refleja esta dualidad. En el presente artículo, revelaremos sus conceptos básicos, y también demostraremos la implementación de indicadores que facilitan este método de análisis.

Realimentación

Según la teoría de Janus, los mercados están impulsados ​​por la interacción entre los precios y la forma en que los tráders reaccionan ante ellos. Anderson comparó dicha interacción con un sistema de realimentación. Podrá obtener más información al respecto en la Wikipedia. Cuando los mercados están en tendencia, sus participantes muestran confianza, permitiendo que sus ganancias aumenten. En una tendencia alcista, los precios más altos confirman las expectativas de los participantes sobre la tendencia, lo que a su vez fomenta la compra, y esto provoca un aumento todavía mayor de los precios. 

Fig. 1. Realimentación positiva en una tendencia alcista

La figura 1 ilustra la realimentación positiva en una tendencia alcista


En una tendencia bajista, la caída de los precios puede infundir miedo en los tráders, lo cual hace que vendan para minimizar las pérdidas. El aumento de las ventas ejerce presión sobre los precios, impulsándolos aún más a la baja. Por consiguiente, las tendencias del mercado son un ejemplo de realimentación positiva. Los movimientos del precio se acelerarán hasta que se dé una reacción de los tráders a los cambios de precio. 

Fig. 2. Realimentación positiva en una tendencia bajista

La figura 2 muestra una realimentación positiva en una tendencia bajista

La realimentación negativa ocurre cuando los tráders tienen poca confianza en el mercado, por lo que prefieren obtener ganancias inmediatamente después de cualquier movimiento del precio. La fijación prematura del beneficio debilita el impulso del mercado al limitar el número de movimientos del precio. Si añadimos a esto la lucha constante entre alcistas y bajistas, y tendremos la estabilización de precios.

Fig. 3. Realimentación negativa

Fig. 3. Realimentación negativa en el mercado

Los flujos de capital

Un elemento importante de esta noción de realimentación son los tipos de símbolos que prefieren los tráders cuando prevalecen diferentes condiciones. El análisis de acciones de Anderson muestra que en una tendencia alcista, los tráders favorecían las acciones con un rendimiento relativamente mejor mientras se desprendían de sus pares con un rendimiento menor. En una tendencia bajista, las acciones de baja rentabilidad perdieron la mayor parte del valor, pues los tráders las seleccionaron para obtener ganancias. 

En ausencia de una tendencia, se dieron pocas diferencias entre las acciones más fuertes y las más débiles, ya que los tráders eligieron ciertos niveles de precios para entrar y salir. Con esto en mente, sugirió que analizando el rendimiento relativo de un conjunto de acciones, sería posible detectar periodos de realimentación negativa y positiva.


Cálculos de Janus

Para determinar el rendimiento, se calculan los ingresos periódicos. Usando como base el retorno del conjunto de acciones analizado, se deriva un valor promedio que se utilizará como medida básica. Podemos llamarlo el ingreso de referencia. La totalidad de las acciones que se valoran se denomina índice.

En The Janus Factor - A Guide to Market Dialectics for Trend Followers, Anderson describe diversos indicadores de acciones basados en los retornos utilizados para obtener información sobre el comportamiento del mercado. 


Biblioteca janus.mqh

Podrá encontrar los datos y subrutinas comunes a todos los cálculos relacionados con Janus en janus.mqh. El archivo de inclusión contiene las declaraciones para tres tipos personalizados:

    La clase CSymboldata procesa los datos de los símbolos y las operaciones relacionadas con los mismos. 

    //+------------------------------------------------------------------+
    //|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 es un contenedor de objetos CSymboldata que supone el conjunto de símbolos analizados. Hemos adaptado el código de ambas clases de la lista de código básico de MQL5. Asimismo, hemos realizado cambios en la dirección de indexación de los búferes básicos. Debemos señalar que todas las clases implementan el formato de indexación de derecha a izquierda con un índice cero que indica la última barra. 

    Para demostrar los métodos del factor Janus, aplicaremos este al análisis de parejas de divisas. Anderson desarrolló originalmente este método para analizar acciones, pero la disponibilidad de datos sobre los símbolos de las acciones no es consistente con la mayoría de los brókeres.

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

     

    El código comienza con tres enumeraciones de usuario que describimos a continuación.

    #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
      };
    
    
    Enumeración
    Detalles
    Ajustes
    ENUM_INDEX_TYPE
    Tipo de símbolos incluidos en la colección analizada
    INDEX_FOREX - crea una colección de símbolos que consta de todas las principales divisas de Fórex. Conjunto de 28 símbolos.
    INDEX_CUSTOM - indicar un conjunto de símbolos.
    ENUM_PRICE
    permitir la selección de una serie de precios para su uso en los cálculos. La información sobre los precios se almacena como serie con los últimos precios con el índice 0.

    CLOSE - precio de cierre
    MEDIAN - precio mediano - high+low/2
    TYPICAL - precio típico - high+low+close/3
    WEIGHTED - precio ponderado high+low+close+close/4

     ENUM_DIFF_TYPE
     Distintos métodos de diferenciación que se pueden aplicar a la serie de precios seleccionada.
    DIFF_LOG - usar diferencia logarítmica
    DIFF_PERCENT - usar diferencia porcentual

    Para usar la clase CJanus, deberemos comprender cinco métodos. Después de crear un objeto CJanus y antes de llamar a cualquier método, primero tendremos que llamar a la función miembro Initialize. El método hace dos cosas importantes: inicializa el objeto CSymbolCollection y lo rellena con los símbolos seleccionados que formarán nuestro índice. Luego verifica y sincroniza las barras de la historia de los símbolos en la colección.

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

    Los parámetros de entrada del método Initialize() se explican en el siguiente recuadro.

    Parámetros
    Detalles
     Tipo de datos
    set_price_type
    Enumeración que establece la serie de precios básica que se utilizará para todos los cálculos
    ENUM_PRICE
    set_diff_type
    Método de diferenciación aplicado a la serie de precios
    ENUM_DIFF_TYPE
    set_index_type
    Tipo de colección de símbolos, ya sea personalizado o solo las principales divisas Fórex. Si seleccionamos personalizado, deberemos especificar los símbolos en los parámetros symbol_list
    ENUM_INDEX_TYPE
    set_timeframe
    Marco temporal de las barras que se utilizarán en los cálculos
    ENUM_TIMEFRAME

    history_size
    Número máximo de barras a solicitar. Como estamos tratando con varios símbolos, esta será una forma de garantizar que todos los símbolos de la colección tengan la misma cantidad de datos históricos disponibles de las barras.
    integer

     symbol_list
    Si se establece set_index_type en INDEX_CUSTOM, el usuario deberá proporcionar una lista con los símbolos (separados por comas) que formarán el conjunto a analizar.
     string
    Otros métodos importantes:
    •  El método Update() actualiza los datos del símbolo para obtener las cotizaciones más recientes.
    • El método HistorySize() retorna el número de barras disponibles de la historia. Debemos tener en cuenta que este número puede ser inferior al especificado al llamar al método Initialize. Esto se debe a que la historia de todos los símbolos puede distinguirse, por lo que la función retornará el tamaño de la historial sincronizada.
    • GetSymbolsTotals() retorna el número de símbolos añadidos y verificados como disponibles en el terminal.
    • GetSymbolAt() obtiene el nombre del símbolo según el índice.

    Los métodos restantes con el prefijo Calculate realizan los cálculos correspondientes y retornan uno o dos valores. Todos ellos tienen como primer parámetro de entrada el desplazamiento de barra para el que se realizará el cálculo.
    Cada método se describirá a medida que se implementen los distintos indicadores.


    Medición del desempeño

    Como ya hemos mencionado, el desempeño se mide calculando los ingresos. Para nuestra implementación, tenemos la opción de elegir el precio aplicado y el método utilizado para calcular el beneficio. La llamada al método CalculateReturns() calcula el retorno para una barra y un cambio de símbolo dados.

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

    El siguiente código del indicador IndexReturns muestra el retorno del símbolo del gráfico, así como el retorno de referencia.

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

    Indicador IndexReturns

    Indicador IndexReturns

    Referencia

    Para calcular el retorno de referencia, utilizaremos el retorno del símbolo individual para cada momento relevante para llegar al valor medio. Este cálculo se realiza usando el método 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));
      }


    Ataque y defensa

    Para comprender mejor el rendimiento relativo, a Anderson se le ocurrió el concepto de puntos de ataque (offense) y defensa (defense) para el símbolo. Los puntos de ataque son el rendimiento que se logra cuando los retornos de referencia están por encima del promedio. 

    Los puntos de protección se calculan de forma similar utilizando la rentabilidad obtenida cuando el retorno del índice de referencia es inferior a la media. Este valor indica cómo de bien se mantiene el símbolo al fluctuar los precios del mercado. 

    Para calcular estos valores, los retornos de referencia se dividen en dos partes: retornos de referencia de ataque y defensa. Estos se calculan seleccionando una longitud de ventana adecuada, a partir de la cual se calcula la referencia media. 

    En la clase CJanus, el valor mediano se usa como el promedio de la ventana especificada. El retorno del índice de referencia de ataque se logra acumulando la diferencia entre el retorno logrado y el retorno del índice de referencia promedio, siendo el retorno conseguido mayor o igual que el promedio.

    El retorno de referencia de defensa se calcula de manera similar, utilizando valores de referencia inferiores al promedio de la ventana.

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

    Una vez calculados los retornos de referencia de ataque y defensa, estos se utilizan para calcular los puntos de ataque y defensa del símbolo. En la misma ventana utilizada para los indicadores de referencia, cada vez que los retornos de referencia son mayores o iguales a la media de la ventana, los retornos alcanzados se suman. Luego, esta suma se expresa como una fracción del indicador de referencia de ataque correspondiente y se multiplica por 100. 

    Se realizan cálculos similares utilizando el retorno de referencia de defensa. El método CalculateSymbolOffenseDefense() implementa el cálculo tanto de los puntos de ataque como de los puntos de defensa.

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

    Trazando los puntos de ataque y defensa en el gráfico, Anderson notó patrones que surgían dependiendo de las condiciones predominantes del mercado. Durante la realimentación positiva, los puntos estaban muy dispersos, mostrando una diferencia significativa entre los símbolos con mejor y peor desempeño. Durante los periodos de realimentación negativa, la diferencia entre los mejores y los peores se reducía. Anderson llamó a esto expansión y contracción del índice.

    En el siguiente código se describe el script OffensiveDefensiveScatterPlot. Tiene los mismos datos de entrada que el indicador anterior y dibuja en forma de animación los gráficos de puntuación de ataque y defensa.

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

    A continuación, le mostramos el gráfico generado por el script.

    Diagrama de dispersión de ataque/defensa


    Fuerza relativa

    Si relacionamos las puntuaciones de ataque y defensa de un símbolo usando la fórmula mostrada a continuación, obtendremos la fuerza relativa del símbolo.

    Fórmula de fuerza relativa

     La derivación de esta fórmula está documentada en el libro de Anderson. La fuerza relativa se calcula usando el método 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));
      }

    El siguiente código de indicador muestra la fuerza relativa del símbolo del gráfico.

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

    Indicador de fuerza relativa

    Indicador de fuerza relativa

    Usando los valores de fuerza relativa, obtenemos una imagen más clara de la expansión y la contracción que sucede con el tiempo. Esto se muestra a continuación en el gráfico del indicador de los valores más altos y bajos de fuerza relativa. 

    Puntos altos y bajos de la fuerza relativa

    A continuación le mostramos el código del indicador.

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


    Líderes y rezagados en la fuerza relativa

    Al promediar el desempeño relativo de los mejores y los peores, obtenemos la fuerza relativa de los líderes y la fuerza relativa de los rezagados, respectivamente. Estos valores indican el mejor momento para comerciar según el estado de la realimentación. La fuerza relativa (rs) de los líderes y los rezagados se calcula indicando la cantidad de los mejores y los peores para calcular el promedio. 

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

    El parámetro de entrada rs_percent_top para el método CalculateRelativeStrengthLeadersLaggards() indica el porcentaje de símbolos usados para calcular la fuerza relativa promedio. Por ejemplo, si establecemos rs_percent_top en 0,1 significa que se usará el 10% superior e inferior de los símbolos al calcular los líderes y los rezagados. 

    A continuación, le mostramos una captura de pantalla del indicador que muestra los líderes y los rezagados (Leaders and Laggards)

    Líderes y rezagados


    Dispersión

    Cuando los mercados se encuentran en realimentación positiva, los valores más débiles y más fuertes divergen según la dirección de la tendencia. En una tendencia alcista, los tráders prefieren valores más fuertes, mientras que en una tendencia bajista, venden valores más débiles.

    Si los precios se atascan en un rango, la diferencia entre los valores más débiles y los más fuertes será relativamente pequeña, es decir, se producirá una convergencia. Para reconocer estos cambios, calcularemos la diferencia promedio en la fuerza relativa entre el valor más débil y el más fuerte. Esto es lo que mide la dispersión de la fuerza relativa.

    La dispersión de la fuerza relativa se implementa en el método 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);
      }

    Indicador de dispersión de fuerza relativa

    Indicador de dispersión de la fuerza relativa

    Podemos utilizar una combinación de indicadores de dispersión y líderes/rezagados para desarrollar reglas comerciales específicas. Según Anderson, las mejores oportunidades se abren al comerciar con símbolos que se encuentran cerca o en los límites superior e inferior de la fuerza relativa del índice en cuestió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;
      }


    Cuando la tendencia del spread sube en combinación con los líderes rs, esto indica una realimentación positiva, así como una compra neta por parte de los ejecutores más fuertes. Este es un buen momento para comprar los símbolos que muestren la mayor fuerza relativa. Si la rs rezagada sube, entonces deberemos centrarnos los símbolos más débiles para vender. En cualquiera de estas situaciones, podría resultar conveniente combinar estas reglas con un método de entrada eficiente que se aplique a los símbolos seleccionados. 

    Otra opción es usar el indicador de dispersión como filtro de riesgo. Cuando existe realimentación positiva, el mercado se somete a menos riesgos, mientras que la realimentación negativa indica posibles condiciones volátiles. En esos momentos, resulta mejor mantenerse fuera del mercado. 


      Conclusión

      Aunque es cierto que el análisis de símbolos Fórex no es la mejor aplicación del factor Janus, el objetivo de este artículo consiste exclusivamente en presentar el método a los lectores. Podrá encontrar más información al respecto en el libro de Anderson, cuyo enlace se ofrece a continuación. 

      El archivo zip adjunto contiene el código completo de las herramientas mencionadas en el artículo. La siguiente tabla enumera todas las herramientas.

      Para que los indicadores funcionen, los usuarios primero deberán asegurarse de que los datos históricos de todos los símbolos que componen el índice estén disponibles.

      Nombre del archivo Tipo Descripción
      Mql5/include/Janus.mqh Archivo de inclusión  Archivo de inclusión con las definiciones de las clases CSymbolData, CSymbolCollection y CJanus
      Mql5/scripts/OffensiveDefensiveScatterPlot.mq5 Script Script que dibuja un gráfico punteado animado con los puntos de ataque/defensa
      Mql5/indicators/IndexReturns.mq5 Indicador Código del indicador que muestra los símbolos y el retorno de referencia
      Mql5/indicators/RelativeStrengthBestWorst.mq5
      Indicador Indicador que muestra un gráfico con los valores más altos y más bajos de fuerza relativa.
      Mql5/indicadores/RelativeStrength.mq5
      Indicador Indicador de fuerza relativa del símbolo
      Mql5/indicators/RelativeStrengthSpread.mq5 Indicador Indicador de dispersión de fuerza relativa
      Mql5/indicatorr/RssLeaderlaggards.mq5 Indicador Indicador que muestra los líderes y los rezagados en la fuerza relativa



      Traducción del inglés realizada por MetaQuotes Ltd.
      Artículo original: https://www.mql5.com/en/articles/12328

      Redes neuronales: así de sencillo (Parte 37): Atención dispersa (Sparse Attention) Redes neuronales: así de sencillo (Parte 37): Atención dispersa (Sparse Attention)
      En el artículo anterior, analizamos los modelos relacionales que utilizan mecanismos de atención en su arquitectura. Una de las características de dichos modelos es su mayor uso de recursos informáticos. Este artículo propondrá uno de los posibles mecanismos para reducir el número de operaciones computacionales dentro del bloque Self-Attention o de auto-atención, lo cual aumentará el rendimiento del modelo en su conjunto.
      Encontrando patrones de velas con la ayuda de MQL5 Encontrando patrones de velas con la ayuda de MQL5
      En este artículo, hablaremos sobre cómo detectar automáticamente patrones de velas con la ayuda de MQL5.
      Estrategia comercial con el indicador de mejora de reconocimiento de velas Doji Estrategia comercial con el indicador de mejora de reconocimiento de velas Doji
      El indicador sobre metabarras ha detectado más velas que el clásico. Veamos si aporta un beneficio real en el trading automatizado.
      Teoría de Categorías en MQL5 (Parte 5): Ecualizadores Teoría de Categorías en MQL5 (Parte 5): Ecualizadores
      La teoría de categorías es un apartado diverso y en expansión de las matemáticas, que solo recientemente ha comenzado a ser trabajado por la comunidad MQL5. Esta serie de artículos tiene por objetivo repasar algunos de sus conceptos para crear una biblioteca abierta y seguir usando este maravilloso apartado en la creación de estrategias comerciales.