Implementando o fator Janus em MQL5
Introdução
Todo trader sabe que os preços de mercado seguem um dos dois padrões amplos. Os preços ou seguem tendências ou se movem horizontalmente. Como resultado disso, as estratégias dos participantes do mercado podem ser amplamente reduzidas a seguir a tendência ou ser contrárias em algum grau. O fator Janus é uma teoria do comportamento de mercado que captura essa dualidade. Neste artigo, iremos explorar suas bases e também demonstrar a implementação de indicadores que facilitam esse método de análise.
Feedback
De acordo com a teoria de Janus, os mercados são impulsionados pela interação entre os preços e a forma como os traders reagem a eles. Anderson comparou essa interação a um sistema de feedback. Os leitores podem aprender mais sobre sistemas de feedback na Wikipedia. Quando os mercados estão em tendência, os participantes do mercado mostram confiança, deixando seus lucros aumentarem. Em uma tendência de alta, preços mais altos confirmam as expectativas dos participantes em relação à tendência, o que por sua vez induz a mais compras. Isso tem o efeito de impulsionar ainda mais os preços.
A Fig.1 ilustra o feedback positivo em uma tendência de alta
Em uma tendência de baixa, a queda nos preços pode instilar medo nos traders, levando-os a vender para minimizar as perdas. Mais vendas exercem pressão sobre os preços, empurrando-os para baixo. As tendências nos mercados são, portanto, um exemplo de feedback positivo. Os preços continuarão a acelerar até que a reação dos traders às mudanças nos preços.
A Fig.2 representa o feedback positivo em uma tendência de baixa
O feedback negativo é observado quando os traders têm pouca confiança no mercado, então eles optam por realizar lucros cedo após qualquer movimento no preço. A realização antecipada de lucros interrompe o momentum, o que limita a magnitude dos movimentos de preço. Quando combinado com a constante disputa entre touros e ursos, o efeito é a estabilização dos preços.
A Fig. 3 representa o feedback negativo no mercado
Fluxo de capital
Um elemento importante dessa noção de feedback são os tipos de símbolos que os traders preferem quando diferentes condições prevalecem. A análise de Anderson das ações sugere que, em uma tendência de alta, os traders preferem ações que apresentam desempenho relativamente melhor, enquanto desinvestem de suas contrapartes com desempenho inferior. Em uma tendência de baixa, as ações com desempenho inferior perdem mais valor, pois os traders as visam para obter lucros com vendas a descoberto.
Na ausência de uma tendência, não havia muita diferença entre ações mais fortes e mais fracas, pois os traders escolhiam níveis de preço específicos para entrar e sair. Com isso em mente, ele formulou a hipótese de que, ao analisar o desempenho relativo de uma coleção de ações, seria possível detectar períodos de feedback negativo e positivo.
Cálculos de Janus
Para determinar o desempenho, são calculados os retornos periódicos. Os retornos de uma coleção de ações em estudo são combinados para produzir uma média a ser usada como medida de referência, chamada de retorno de referência (benchmark). A coleção de ações em avaliação é referida como um índice.
No livro "The Janus Factor - Trend Follower's Guide to Market Dialectics", Anderson descreve várias métricas de ações calculadas com base nos retornos, que ele utiliza para melhorar a compreensão sobre o comportamento do mercado.
A Biblioteca Janus - janus.mqh
Os dados e rotinas comuns a todos os cálculos relacionados a Janus estão contidos em janus.mqh. O arquivo de inclusão possui declarações para três tipos personalizados:
A classe CSymboldata manipula dados de símbolos e operações relacionadas.
//+------------------------------------------------------------------+ //|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 é um contêiner de objetos CSymboldata que representa a coleção de símbolos sendo analisados. O código para ambas as classes foi adaptado de um exemplo do CodeBase MQL5. Foram feitas modificações na direção de indexação dos buffers subjacentes. É importante notar que todas as classes implementam um formato de indexação da direita para a esquerda, com o índice zero apontando para a barra mais recente.
Para demonstrar as técnicas do fator Janus, aplicaremos a análise a pares de moedas. Anderson originalmente desenvolveu o método para analisar ações, mas a disponibilidade de dados de símbolos de ações é inconsistente na maioria das corretoras.
//+------------------------------------------------------------------+ //| 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; } }; //+------------------------------------------------------------------+
O código começa com três enumerações personalizadas documentadas abaixo.
#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 };
Enumeração | Detalhes | Configurações |
---|---|---|
ENUM_INDEX_TYPE | Tipo de símbolos incluídos na coleção analisada | INDEX_FOREX - criação de uma coleção de símbolos que consiste em todas as principais moedas Forex. Conjunto de 28 símbolos. INDEX_CUSTOM - especificação de um conjunto de símbolos. |
ENUM_PRICE | Seleção da série de preços a ser usada nos cálculos. As informações sobre os preços são armazenadas como séries com os preços mais recentes com índice 0. | CLOSE - preço de fechamento MEDIAN - preço médio - high+low/2 TYPICAL - preço típico - high+low+close/3 WEIGHTED - preço ponderado high+low+close+close/4 |
ENUM_DIFF_TYPE | Métodos de diferenciação que podem ser aplicados à série de preços selecionada. | DIFF_LOG - uso da diferença logarítmica DIFF_PERCENT - uso da diferença percentual |
Para usar a classe CJanus, existem 5 métodos que devem ser compreendidos. Após criar um objeto CJanus e antes de chamar qualquer método, primeiro deve ser chamada a função membro Initialize. O método realiza duas coisas importantes: primeiro, inicializa o objeto CSymbolCollection e o preenche com os símbolos selecionados que comporão nosso índice. Em seguida, verifica e sincroniza o histórico de barras dos símbolos na coleção.
//+------------------------------------------------------------------+ //|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); };
Os parâmetros de entrada do método Initialize() são explicados na tabela abaixo.
Parâmetros | Detalhes | Tipo de dados |
---|---|---|
set_price_type | Enumeração que define a série de preços base a ser usada para todos os cálculos | ENUM_PRICE |
set_diff_type | Método de diferenciação aplicado à série de preços | ENUM_DIFF_TYPE |
set_index_type | Tipo de coleção de símbolos - personalizado ou apenas as principais moedas Forex. Se personalizado, os símbolos devem ser especificados nos parâmetros symbol_list | ENUM_INDEX_TYPE |
set_timeframe | Período gráfico das barras a serem usadas nos cálculos | ENUM_TIMEFRAME |
history_size | Número máximo de barras a serem solicitadas. Como temos vários símbolos, assim garantimos que todos os símbolos da coleção tenham a mesma quantidade de dados históricos de barras disponíveis. | integer |
symbol_list | Se set_index_type for definido como INDEX_CUSTOM, o usuário deverá fornecer a lista de símbolos (separada por vírgulas) que formarão o conjunto a ser analisado. | string |
- O método Update() atualiza os dados do símbolo para obter as cotações mais recentes.
- O método HistorySize() retorna o número de barras de histórico disponíveis. Observe que esse número pode ser menor do que o especificado ao chamar o método Initialize. Isso ocorre porque é possível que o histórico em todos os símbolos possa variar, portanto, a função retorna o tamanho de histórico sincronizado.
- GetSymbolsTotals() retorna o número de símbolos que foram adicionados e confirmados como disponíveis no terminal.
- GetSymbolAt() obtém o nome do símbolo pelo índice.
Os demais métodos que têm o prefixo Calculate realizam cálculos e retornam um ou dois valores. Todos eles têm como primeiro parâmetro de entrada o deslocamento de barra (barshift) para o qual será realizado um cálculo.
Cada método será explicado à medida que vários indicadores forem implementados.
Medindo o desempenho
Como já mencionado, o desempenho é medido calculando os retornos. Para nossa implementação, temos a opção de selecionar o preço aplicado e o método usado para calcular os retornos. Chamar o método CalculateReturns() calcula os retornos para o deslocamento de barra e símbolo especificados.
//+------------------------------------------------------------------+ //|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); }
O código para o indicador IndexReturns abaixo exibe os retornos para o símbolo do gráfico e também os retornos de referência.
//+------------------------------------------------------------------+ //| 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); } //+------------------------------------------------------------------+
O indicador IndexReturns
Padrão de referência
Para calcular o retorno de referência (benchmark), usamos os retornos individuais dos símbolos para cada ponto de tempo correspondente para obter o valor mediano. Esse cálculo é implementado pelo 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)); }
Ofensiva e defensiva
Para ter uma noção melhor de desempenho relativo, Anderson desenvolveu a noção de pontuações ofensivas e defensivas para um símbolo. A pontuação ofensiva é o desempenho alcançado quando os retornos de referência estão acima da média.
A pontuação defensiva é calculada de forma semelhante usando os retornos alcançados quando os retornos de referência estão abaixo da média. Esse valor mostra o quão bem um símbolo se mantém quando os preços de mercado estão voláteis.
Para calcular esses valores, os retornos de referência são divididos em dois: retornos de referência ofensivo e defensivo. Eles são calculados selecionando um comprimento de janela adequado a partir do qual é calculada uma referência média.
Na classe CJanus, o valor mediano é usado como média ao longo da janela especificada. O retorno de referência ofensivo é obtido acumulando a diferença entre os retornos alcançados e os retornos de referência médios para aquelas referências maiores ou iguais à média.
O retorno de referência defensivo é calculado de forma semelhante usando valores de referência inferiores à média da janela.
//+------------------------------------------------------------------+ //|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; }
Uma vez que os retornos de referência ofensivo e defensivo tenham sido calculados, eles são usados para calcular as pontuações ofensivas e defensivas para um símbolo. Ao longo da mesma janela usada para os valores de referência, em cada ponto de tempo em que o retorno de referência é maior ou igual à média da janela, os retornos alcançados são somados. Essa soma é então expressa como uma fração do valor de referência ofensivo correspondente e multiplicada por 100.
Um cálculo semelhante é feito usando o retorno de referência defensivo. O método CalculateSymbolOffenseDefense() implementa o cálculo das pontuações ofensivas e defensivas.
//+------------------------------------------------------------------+ //|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; }
Ao plotar as pontuações ofensivas e defensivas, Anderson observou os padrões que surgiriam dependendo das condições de mercado predominantes. Durante períodos de feedback positivo, as pontuações estavam amplamente dispersas, com uma diferença significativa entre os símbolos com melhor e pior desempenho. Ao contrário dos períodos de feedback negativo, quando a diferença entre os melhores e piores desempenhos se estreitava. Anderson se referiu a isso como expansão e contração do índice.
O código abaixo descreve o script OffensiveDefensiveScatterPlot, ele tem entradas semelhantes ao indicador acima e desenha gráficos das pontuações ofensivas e defensivas como uma animação.
//+------------------------------------------------------------------+ //| 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); } //+------------------------------------------------------------------+
O script produz o gráfico abaixo
Força Relativa
Quando as pontuações ofensivas e defensivas de um símbolo são vinculadas usando a fórmula abaixo, obtemos a força relativa de um símbolo.
A derivação desta fórmula é documentada no livro de Anderson. A força relativa é calculada pelo 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)); }
O código do indicador abaixo plota a força relativa para um símbolo do 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); } //+------------------------------------------------------------------+
O Indicador de Força Relativa
Usando os valores de força relativa, obtemos uma imagem mais clara da expansão e contração à medida que ocorrem ao longo do tempo. Isso é mostrado abaixo pelo gráfico do indicador dos valores máximos e mínimos de força relativa de força relativa.
O código para este indicador é mostrado abaixo.
//+------------------------------------------------------------------+ //| 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 e retardatários de força relativa
Quando o desempenho relativo, dado pelos retornos, dos melhores e piores desempenhadores são calculados, obtemos os líderes de força relativa e os retardatários de força relativa, respectivamente. Esses valores indicam os melhores momentos para negociar, dependendo do estado do feedback. Os líderes de força relativa (rs) e os retardatários de força relativa são calculados especificando o número dos melhores e piores desempenhadores para obter uma média.
//+------------------------------------------------------------------+ //|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; }
A entrada rs_percent_top para o método CalculateRelativeStrengthLeadersLaggards() denota a fração de símbolos usada para calcular a força relativa média. Por exemplo, definir rs_percent_top como 0,1 significa que os 10% principais e inferiores dos símbolos serão usados no cálculo dos líderes e retardatários.
Abaixo está uma captura de tela do indicador líderes e retardatários (Leaders and Laggards).
O diferencial
Quando os mercados estão impulsionados por feedback positivo, os títulos mais fracos e mais fortes se divergem dependendo da direção da tendência. Em uma tendência de alta, os traders preferem títulos mais fortes e, em uma tendência de baixa, vendem títulos mais fracos.
Se os preços estiverem em uma faixa, há relativamente pouca diferença entre os títulos mais fracos e mais fortes, ocorrendo convergência. Para reconhecer esses desenvolvimentos, calculamos a diferença média na força relativa entre os títulos mais fracos e mais fortes. Isso é o que mede o diferencial de força relativa.
O diferencial de força relativa é implementado no 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); }
O indicador de diferencial de força relativa
A combinação dos indicadores de diferencial e líderes/retardatários de força relativa pode ser usada para desenvolver regras de negociação específicas. De acordo com Anderson, as melhores oportunidades vêm da negociação de símbolos que estão próximos ou nos limites superiores e inferiores de força relativa para o índice em estudo.
//+------------------------------------------------------------------+ //|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; }
Quando a tendência do spread está subindo em combinação com os líderes de força relativa, é um indicativo de feedback positivo junto com uma compra líquida dos desempenhadores fortes. É um momento oportuno para comprar símbolos que apresentam a maior força relativa. Se os retardatários de força relativa estiverem subindo, então direcione os símbolos mais fracos para uma venda a descoberto. Em qualquer uma dessas situações, pode ser prudente combinar essas regras com um método de entrada eficaz, que será aplicado ao(s) símbolo(s) escolhido(s).
Outra opção é usar o indicador de diferencial como um filtro de risco. Quando há feedback positivo, o mercado é menos arriscado, enquanto o feedback negativo indica possíveis condições voláteis. É um momento provável para ficar fora do mercado.
Considerações finais
Embora a análise de instrumentos Forex possa não ser a melhor opção na hora de implementar o fator Janus, o objetivo deste artigo é apresentar o método aos leitores. Eles podem encontrar mais informações sobre o método no livro de Anderson, cujo link está abaixo.O arquivo zip anexado contém todo o código das ferramentas mencionadas no artigo. A tabela abaixo lista todas as ferramentas.
Para que os indicadores funcionem, os usuários devem primeiro garantir que os dados históricos de todos os símbolos que compõem o índice estejam disponíveis.
Nome do arquivo | Tipo | Descrição |
---|---|---|
Mql5/include/Janus.mqh | Arquivo de inclusão | Arquivo de inclusão com definições de classe CSymbolData, CSymbolCollection e CJanus |
Mql5/scripts/OffensiveDefensiveScatterPlot.mq5 | Script | Script que desenha um gráfico animado de pontos de ataque/defesa |
Mql5/indicators/IndexReturns.mq5 | Indicador | Código do indicador que mostra símbolos e retornos de referência |
Mql5/indicators/RelativeStrengthBestWorst.mq5 | Indicador | Indicador que mostra um gráfico dos valores mais altos e mais baixos de força relativa. |
Mql5/indicators/RelativeStrength.mq5 | Indicador | Indicador de força relativa do símbolo |
Mql5/indicators/RelativeStrengthSpread.mq5 | Indicador | O indicador de diferencial de força relativa |
Mql5/indicatorr/RssLeaderlaggards.mq5 | Indicador | Indicador que mostra líderes e retardatários para a força relativa |
Traduzido do Inglês pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/en/articles/12328
- Aplicativos de negociação gratuitos
- 8 000+ sinais para cópia
- Notícias econômicas para análise dos mercados financeiros
Você concorda com a política do site e com os termos de uso