
Análise de Múltiplos Símbolos com Python e MQL5 (Parte II): Análise de Componentes Principais para Otimização de Portfólio
Gerenciar o risco total ao qual um portfólio está exposto é uma tarefa suficientemente desafiadora enfrentada por todos os membros de nossa comunidade de trading. Diante das grandes oportunidades de investimento oferecidas ao investidor moderno, como alguém pode analisar de forma abrangente e decidir as alocações de ativos apropriadas no mundo atual de mercados em constante expansão? Em nossa última discussão, demonstrei como o retorno total do portfólio poderia ser maximizado usando SciPy. Hoje, vou me concentrar em como controlar o risco/variância de qualquer portfólio que você possa ter em mãos. Existem muitos modelos possíveis que podemos usar para controlar o risco do nosso portfólio. Podemos usar uma ferramenta popular da estatística, a Análise de Componentes Principais (PCA), para gerenciar de forma eficaz a variância total do nosso portfólio.
Para membros da nossa comunidade que desejam vender Expert Advisors, este artigo demonstrará como você pode criar uma experiência perfeita para seus usuários finais. Nossa aplicação de trading será flexível e robusta ao mesmo tempo. Mostrarei como criar aplicações de trading que permitirão aos seus clientes alternar facilmente entre modos de risco alto, médio e baixo. Enquanto isso, o algoritmo PCA cuidará da parte mais pesada em segundo plano para seus usuários finais.
Visão Geral da Metodologia
Neste artigo, vamos gerenciar um portfólio de 10 Criptomoedas. As criptomoedas são ativos digitais que existem em um tipo especial de rede conhecida como blockchain. A tecnologia blockchain é um protocolo de segurança que torna quase impossível para qualquer membro da rede cometer transações fraudulentas. Portanto, dado o poder da rede subjacente, um dos primeiros usos populares da tecnologia blockchain foi a moeda digital. No entanto, esses ativos digitais são notórios por serem muito voláteis, e pode ser desafiador para os investidores gerenciar adequadamente seus níveis de risco ao investir em criptoativos. Felizmente, podemos usar métodos estatísticos como PCA para gerenciar a quantidade total de variância à qual desejamos estar expostos ao investir em criptomoedas.A Análise de Componentes Principais é uma técnica de um ramo da estatística multivariada chamada análise fatorial. O PCA encontrou uso em muitos domínios, desde análise de imagens até aprendizado de máquina. Na análise de imagens, o PCA é comumente usado para tarefas como compressão de dados, enquanto no aprendizado de máquina é mais frequentemente empregado para redução de dimensionalidade.
A ideia do PCA é encontrar um coeficiente para cada coluna no conjunto de dados. Se cada coluna for multiplicada pelo seu coeficiente, as novas colunas que obteremos devem maximizar a variância do conjunto de dados. Se concluímos com sucesso esse procedimento, encontramos com sucesso o primeiro componente principal.
O segundo componente principal será ortogonal ao primeiro, o que significa que eles são completamente não correlacionados, enquanto ao mesmo tempo maximizam a variância total do conjunto de dados. Esse padrão continua até termos criado componentes suficientes para explicar toda a variância do conjunto de dados original.
Vou demonstrar como cada componente principal individualmente se mapeia para níveis de risco distintos que podemos usar como configurações em nossas aplicações de trading. Isso o ajudará a gerenciar inteligentemente suas configurações de risco em minutos.
Primeiros Passos
Acredito que uma demonstração visual do algoritmo em funcionamento seria de grande benefício para os leitores que possam estar encontrando o algoritmo pela primeira vez. Vou pegar a imagem do logotipo do MQL5 na Fig 1 e primeiro convertê-la para preto e branco. Esse filtro preto e branco tornará mais fácil aplicar e visualizar o que o algoritmo PCA está fazendo com nossos dados.
Fig 1: Usaremos o logotipo do MQL5 acima para demonstrar o funcionamento do algoritmo PCA
Agora que temos uma imagem para trabalhar, vamos carregar nossas bibliotecas Python.
#Let's go import pandas as pd import numpy as np import MetaTrader5 as mt5 import seaborn as sns import matplotlib.pyplot as plt from sklearn.decomposition import PCA from skimage import io, color
Leia a imagem e converta-a para preto e branco.
# Load and preprocess the image image = io.imread('mql5.png') # Replace with your image path image_gray = image_gray = color.rgb2gray(image) # Convert to grayscale
Achatamos a imagem em 2 dimensões e aplicamos PCA visando 5 diferentes níveis de componentes. A imagem original está à extrema esquerda da Fig 2. O primeiro componente principal maximiza a variância dos dados de entrada. Como podemos ver na imagem rotulada “1 Componente”, a estrutura geral da imagem original é preservada de certa forma. Embora o texto “MQL5” no centro da imagem não seja mais legível, podemos deduzir razoavelmente pela imagem que há um fundo preto com uma estrutura branca no meio.
Ao usar 5 componentes principais, o texto na imagem é legível. No entanto, detalhes finos, como os ícones cinza claro estilizados no fundo, se perdem e exigem mais componentes para serem recuperados.
Este exercício deve lhe dar uma compreensão intuitiva de que o algoritmo PCA está tentando criar representações compactas e não correlacionadas dos dados de entrada originais. Essa tarefa é alcançada criando combinações lineares sucessivas dos dados de entrada que maximizam a variância desses dados.
# Flatten the image h, w = image_gray.shape image_flattened = image_gray.reshape(h, w) # Apply PCA with different numbers of components n_components_list = [1,5,20,50,100] # Number of components to keep fig, axes = plt.subplots(1, len(n_components_list) + 1, figsize=(20, 10)) axes[0].imshow(image_gray, cmap='gray') axes[0].set_title("Original Image") axes[0].axis('off') for i, n_components in enumerate(n_components_list): # Initialize PCA and transform the flattened image pca = PCA(n_components=n_components) pca.fit(image_flattened) # Transform and inverse transform the image transformed_image = pca.transform(image_flattened) reconstructed_image = pca.inverse_transform(transformed_image).reshape(h, w) # Plot the reconstructed image axes[i + 1].imshow(reconstructed_image, cmap='gray') axes[i + 1].set_title(f"{n_components} Components") axes[i + 1].axis('off') plt.tight_layout() plt.show()
Fig 2: Os 2 primeiros componentes principais, resumindo nosso logotipo MQL5
Fig 3: Os 3 últimos componentes principais, resumindo nosso logotipo MQL5
Em vez de uma imagem, estamos mais interessados em maximizar ou minimizar a variância de um portfólio. Podemos realizar isso passando para o algoritmo PCA um conjunto de dados que contenha os retornos de cada ativo do portfólio.
Obtendo Nossos Dados de Mercado
Para começar, primeiro precisamos garantir que nosso Terminal MetaTrader 5 esteja ativo.
mt5.initialize()
Agora liste todos os ativos que manteremos em nosso portfólio.
#List of cryptocurrencies we wish to invest crypto = [ "BCHUSD", #BitcoinCash "EOSUSD", #EOS "BTCUSD", #Bitcoin "ETHUSD", #Etherum "ADAUSD", #Cardano "XRPUSD", #Ripple "UNIUSD", #Monero "DOGUSD", #Dogecoin "LTCUSD", #Litecoin "SOLUSD" #Solana ]
Queremos buscar 6 anos de dados de mercado diários.
fetch = 365 * 6
Nosso modelo fará previsões para 30 dias no futuro.
look_ahead = 30
Crie o data frame que armazenará nossos retornos de criptomoedas.
data = pd.DataFrame(columns=crypto,index=range(fetch))
Busque cotações de mercado para cada um dos Símbolos que temos em nossa lista.
for i in range(0,len(crypto)): data[crypto[i]] = pd.DataFrame(mt5.copy_rates_from_pos(crypto[i],mt5.TIMEFRAME_M1,0,fetch)).loc[:,"close"]
Vamos ver nossos dados de mercado.
dados
Fig 4: Visualizando alguns dos dados de mercado que coletamos, registrando 6 anos de preços históricos de uma coleção de criptomoedas
Análise Exploratória dos Dados
Negociar criptomoedas pode ser desafiador porque os ativos são muito voláteis. A Fig 5 nos ajuda a visualizar o desempenho de uma cesta de 10 criptomoedas ao longo de um período de 6 anos, de 2018 até 2024. A diferença entre os ativos evolui e pode não ser intuitiva para que um ser humano calcule ou utilize de forma eficaz.
data_plotting = data.iloc[:,:]/data.iloc[0,:]
sns.lineplot(data_plotting)
Fig 5: O desempenho de 10 criptomoedas diferentes nas quais poderíamos ter investido
O risco introduzido em nosso portfólio por cada criptomoeda pode ser visualizado como o desvio padrão móvel dos retornos de cada Símbolo. Podemos ver pela Fig 6 que períodos de alta volatilidade tendem a se agrupar. No entanto, alguns mercados exibem comportamento mais volátil do que outros.
plt.plot(data_plotting.iloc[:,0].rolling(14).std()) plt.plot(data_plotting.iloc[:,1].rolling(14).std()) plt.plot(data_plotting.iloc[:,2].rolling(14).std()) plt.plot(data_plotting.iloc[:,3].rolling(14).std()) plt.plot(data_plotting.iloc[:,4].rolling(14).std()) plt.plot(data_plotting.iloc[:,5].rolling(14).std()) plt.plot(data_plotting.iloc[:,6].rolling(14).std()) plt.plot(data_plotting.iloc[:,7].rolling(14).std()) plt.plot(data_plotting.iloc[:,8].rolling(14).std()) plt.plot(data_plotting.iloc[:,9].rolling(14).std()) plt.legend(crypto) plt.title("The Risk Associated With Each of our Cryptocurrencies") plt.xlabel("Time") plt.ylabel("Standard Deviation")
Fig 6: O desvio padrão de cada criptomoeda em nosso portfólio
O algoritmo PCA nos ajudará a minimizar ou maximizar intencionalmente nossa exposição à variância total do portfólio visualizado na Fig 6. Tentar decidir manualmente quais ativos maximizarão a variância do portfólio, dado um conjunto de 10 ativos, pode se mostrar impossível de se fazer sem ajuda.Níveis fortes de correlação são de certa forma esperados quando analisamos ativos da mesma classe de ativos Particularmente interessantes são os níveis de correlação entre os:
- EOSUSD & XRPUSD
- DOGUSD & BCHUSD
#Correlation analysis
sns.heatmap(data.corr(),annot=True)
Fig 7: Visualizando os níveis de correlação em nosso portfólio de 10 criptomoedas
Calcule a variação nos níveis de preço ao longo de 2 semanas.
#Calculate the change over 2 weeks data = data.diff(14).dropna().reset_index(drop=True)
Agora ajuste o scaler.
scaler = RobustScaler() scaled_data = pd.DataFrame(scaler.fit_transform(data), columns=data.columns)
Ajuste o objeto PCA do scikit-learn em nossos retornos diários de mercado.
pca = PCA() pca.fit(scaled_data)
Agora vamos analisar o primeiro componente principal. O sinal do coeficiente nos instrui se devemos ocupar posições compradas (long) ou vendidas (short) em cada mercado. Coeficientes positivos nos dizem para ficar comprados, e coeficientes negativos nos instruem a ficar vendidos. Portanto, para maximizar a variância de nosso portfólio, o algoritmo PCA sugere vender (short) em todos os mercados que selecionamos.
Para traders que procuram construir estratégias de scalping, este primeiro componente principal é o caminho a seguir.
#High risk strategy pca.components_[0]
Além disso, cada componente também nos fornece razões ótimas de alocação de capital que irão maximizar ou minimizar a variância de acordo. No entanto, para obter essa informação, precisamos aplicar algumas transformações ao componente.
Dividiremos o componente pela sua Norma L1. A Norma L1 é a soma dos valores absolutos no componente. Ao dividir cada loading score principal pela soma total, saberemos as proporções ótimas que devemos alocar em cada ativo para maximizar a variância do portfólio.
Após calcular a proporção de cada criptomoeda no componente, podemos multiplicar a razão de alocação de ativos pelo número de posições que queremos abrir, para obter uma aproximação de quantas posições devemos abrir em cada mercado. Calculamos a soma para provar que o total de posições somará 100 se multiplicarmos a razão de alocação de ativos por 100.
#High risk asset allocations can be estimated from the first principal component high_risk_asset_allocation = pca.components_[0] / np.linalg.norm(pca.components_[0],1) np.sum(high_risk_asset_allocation * 100)
Por exemplo, se quisermos abrir 10 posições seguindo a estratégia de alto risco, nosso componente principal sugere que vendamos 1 posição em BCHUSD (Bitcoin Cash). A parte decimal do coeficiente pode ser interpretada como uma posição de tamanho de lote ligeiramente menor. Mas isso pode se mostrar significativamente demorado para considerar com precisão em nosso portfólio. Pode ser mais simples confiar apenas na parte inteira da alocação.
high_risk_asset_allocation * 10
Agora vamos passar para os componentes principais de risco médio.
#Mid risk strategy pca.components_[len(crypto)//2]
E, finalmente, nossos componentes principais de baixo risco sugerem uma estratégia de negociação completamente diferente.
#Low risk strategy pca.components_[len(crypto)-1]
Vamos salvar esses componentes principais em um arquivo de texto, para que possamos usar os resultados em nossa aplicação de trading no MetaTrader 5.
np.savetxt("LOW RISK COMPONENTS.txt",pca.components_[len(crypto)-1]) np.savetxt("MID RISK COMPONENTS.txt",pca.components_[len(crypto)//2]) np.savetxt("HIGH RISK COMPONENTS.txt",pca.components_[0])
Implementando em MQL5
Agora estamos prontos para começar a implementar nossa Aplicação de Trading no MetaTrader 5
Nossa aplicação de trading buscará seguir os resultados de nossas alocações ótimas de ativos. Abriremos apenas 1 trade em cada mercado, de acordo com a configuração de risco que o usuário selecionar. A aplicação de trading vai cronometrar suas entradas usando a média móvel e o Oscilador Estocástico.
Se o componente principal sugerir que devemos entrar em posições compradas no mercado, esperaremos que os níveis de preço subam acima da leitura da média móvel naquele mercado e que o Oscilador Estocástico esteja acima de 50. Assim que ambas as condições forem satisfeitas, assumiremos nossa posição comprada e definiremos nossos níveis de take profit e stop loss usando o ATR. Cronometrar nossas entradas dessa maneira deve, esperamos, nos dar sinais de entrada mais estáveis e consistentes ao longo do tempo.
Para começar, primeiro definiremos o enumerador personalizado que dará ao nosso usuário final o controle sobre os parâmetros de risco de nossa aplicação de trading.
//+------------------------------------------------------------------+ //| PCA For Portfolio Optimization.mq5 | //| Gamuchirai Zororo Ndawana | //| https://www.mql5.com/en/gamuchiraindawa | //+------------------------------------------------------------------+ #property copyright "Gamuchirai Zororo Ndawana" #property link "https://www.mql5.com/en/gamuchiraindawa" #property version "1.00" //+------------------------------------------------------------------+ //| Custom enumerations | //+------------------------------------------------------------------+ enum risk_level { High=0, Mid=1, Low=2 };
Carregue as bibliotecas necessárias.
//+------------------------------------------------------------------+ //| Libraries | //+------------------------------------------------------------------+ #include <Trade\Trade.mqh> CTrade Trade;
Defina valores constantes que não mudarão em nenhum momento.
//+------------------------------------------------------------------+ //| Constants values | //+------------------------------------------------------------------+ const double high_risk_components[] = {#include "HIGH RISK COMPONENTS.txt"}; const double mid_risk_components[] = {#include "MID RISK COMPONENTS.txt"}; const double low_risk_components[] = {#include "LOW RISK COMPONENTS.txt"}; const string crypto[] = {"BCHUSD","EOSUSD","BTCUSD","ETHUSD","ADAUSD","XRPUSD","UNIUSD","DOGUSD","LTCUSD","SOLUSD"};
Nossas variáveis globais que usaremos em todo o programa.
//+------------------------------------------------------------------+ //| Global variables | //+------------------------------------------------------------------+ double current_risk_settings[]; double vol,bid,ask; int atr_handler; int stoch_handler; int ma_handler; double atr_reading[],ma_reading[],stoch_reading[];
Permitiremos que o usuário final controle dinamicamente as configurações de risco da conta de negociação e o tamanho de lote desejado.
//+------------------------------------------------------------------+ //| Inputs | //+------------------------------------------------------------------+ input group "Risk Levels" input risk_level user_risk = High; //Which risk level should we use? input group "Money Management" input int lot_multiple = 1; //How big should out lot size be?
Quando nossa aplicação de trading for carregada, primeiro carregaremos nossos componentes principais de acordo com a configuração de risco que o usuário selecionou. Tudo isso será tratado para nós pela função “setup”.
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Setup our market data setup(); //--- return(INIT_SUCCEEDED); }
Se não estivermos mais usando o Expert Advisor, vamos liberar os recursos que não estamos usando.
//+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- Release the indicators IndicatorRelease(atr_handler); IndicatorRelease(ma_handler); IndicatorRelease(stoch_handler); }
Finalmente, sempre que recebermos preços atualizados, manteremos um portfólio de 1 posição em cada mercado de criptomoedas. A posição que ocuparemos será determinada pelas configurações de risco que o usuário selecionou. Dependendo da configuração de risco, pode ser necessário ocupar uma posição de compra (buy) ou de venda (sell) para controlar a variância do portfólio.
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- Ensure we always have 10 positions if(PositionsTotal() < 10) { //--- Let's see which market we aren't in for(int i = 0; i < 10; i++) { //--- Check if we can now enter that market if(!PositionSelect(crypto[i])) { check_setup(i); } } } }
A função setup determinará qual configuração de risco o usuário final selecionou e então carregará o componente principal correspondente.
//+------------------------------------------------------------------+ //| Setup our market data | //+------------------------------------------------------------------+ void setup(void) { //--- First let us define the current risk settings switch(user_risk) { //--- The user selected high risk case 0: { ArrayCopy(current_risk_settings,high_risk_components,0,0,WHOLE_ARRAY); Comment("EA in high risk mode"); break; } //--- The user selected mid risk case 1: { ArrayCopy(current_risk_settings,mid_risk_components,0,0,WHOLE_ARRAY); Comment("EA in mid risk mode"); break; } //--- The user selected low risk //--- Low risk is also the default setting for safety! default: { ArrayCopy(current_risk_settings,low_risk_components,0,0,WHOLE_ARRAY); Comment("EA in low risk mode"); break; } } }
Como estamos negociando ativamente 10 símbolos diferentes, precisamos de uma função responsável por buscar dados de mercado relacionados a cada símbolo. A função “update” definida abaixo lidará com essa tarefa para nós. Sempre que a função for chamada, ela carregará o preço de compra (bid) e venda (ask) atuais, junto com outras leituras de indicadores técnicos calculados a partir do mercado especificado, na memória. Depois que tivermos usado os dados e decidido qual ação tomar, a função carregará na memória os dados de mercado do próximo Símbolo.
//+------------------------------------------------------------------+ //| Update our system varaibles | //+------------------------------------------------------------------+ void update(string market) { //--- Get current prices bid = SymbolInfoDouble(market,SYMBOL_BID); ask = SymbolInfoDouble(market,SYMBOL_ASK); //--- Get current technical readings atr_handler = iATR(market,PERIOD_CURRENT,14); stoch_handler = iStochastic(market,PERIOD_CURRENT,5,3,3,MODE_EMA,STO_CLOSECLOSE); ma_handler = iMA(market,PERIOD_CURRENT,30,0,MODE_EMA,PRICE_CLOSE); //--- Copy buffer CopyBuffer(atr_handler,0,0,1,atr_reading); CopyBuffer(ma_handler,0,0,1,ma_reading); CopyBuffer(stoch_handler,0,0,1,stoch_reading); //--- }
Por fim, precisamos de uma função que verificará se podemos abrir uma posição alinhada às configurações de risco atuais que o usuário especificou. Caso contrário, se não pudermos, daremos ao usuário final uma explicação significativa do motivo de não podermos abrir uma posição naquele mercado.
//+------------------------------------------------------------------+ //| Open a position if we can | //+------------------------------------------------------------------+ void check_setup(int idx) { //--- The function takes the index of the symbol as its only parameter //--- It will look up the principal component loading of the symbol to determine whether it should buy or sell update(crypto[idx]); vol = lot_multiple * SymbolInfoDouble(crypto[idx],SYMBOL_VOLUME_MIN); if(current_risk_settings[idx] > 0) { if((iClose(crypto[idx],PERIOD_D1,0) > ma_reading[0]) && (stoch_reading[0] > 50)) { Comment("Analyzing: ",crypto[idx],"\nMA: ",ma_reading[0],"\nStoch: ",stoch_reading[0],"\nAction: Buy"); Trade.Buy(vol,crypto[idx],ask,(ask - (atr_reading[0] * 3)),(ask + (atr_reading[0] * 3)),"PCA Risk Optimization"); return; } else { Comment("Waiting for an oppurtunity to BUY: ",crypto[idx]); } } else if(current_risk_settings[idx] < 0) { if((iClose(crypto[idx],PERIOD_D1,0) < ma_reading[0]) && (stoch_reading[0] < 50)) { Comment("Analyzing: ",crypto[idx],"\nClose: ","\nMA: ",ma_reading[0],"\nStoch: ",stoch_reading[0],"\nAction: Sell"); Trade.Sell(vol,crypto[idx],bid,(bid + (atr_reading[0] * 3)),(bid - (atr_reading[0] * 3)),"PCA Risk Optimization"); return; } else { Comment("Waiting for an oppurtunity to SELL: ",crypto[idx]); return; } } Comment("Analyzing: ",crypto[idx],"\nMA: ",ma_reading[0],"\nStoch: ",stoch_reading[0],"\nAction: None"); return; }; //+------------------------------------------------------------------+
Fig 8: Visualizando nosso expert advisor no MetaTrader 5
Fig 9: Ajustando os parâmetros de risco da nossa aplicação de trading
Fig 10: Nossa aplicação de trading sendo testada em tempo real com dados reais de mercado
Conclusão
O PCA normalmente é visto como um tópico um tanto complicado, devido à notação matemática necessária para aprender o conceito. No entanto, este artigo espero ter mostrado a você como usar o PCA de uma forma fácil de aplicar, que você pode começar a usar hoje mesmo, mesmo que seja a primeira vez que esteja aprendendo sobre o assunto.
Gerenciar o risco na alocação do seu portfólio não é uma tarefa simples; ferramentas e modelos matemáticos são a melhor forma de medir e gerenciar o risco ao qual você está exposto nos mercados financeiros. Especialmente quando o número de mercados em que você deseja participar cresce muito.
Quer tenhamos 10 Símbolos ou 100 Símbolos, o PCA sempre nos mostrará quais combinações maximizarão o risco no portfólio e quais combinações minimizarão o risco do portfólio.
Traduzido do Inglês pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/en/articles/16273
Aviso: Todos os direitos sobre esses materiais pertencem à MetaQuotes Ltd. É proibida a reimpressão total ou parcial.
Esse artigo foi escrito por um usuário do site e reflete seu ponto de vista pessoal. A MetaQuotes Ltd. não se responsabiliza pela precisão das informações apresentadas nem pelas possíveis consequências decorrentes do uso das soluções, estratégias ou recomendações descritas.





- 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