Dr. Tradelove ou como parei de me preocupar e criei um Expert Advisor para autotreinamento
Conceito
Depois de criar o Expert Advisor todos nós podemos usar o built-in Strategy Tester para selecionar os parâmetros ideais. Após selecioná-los, executamos o Expert Advisor uma vez e assim que qualquer mudança significativa ocorra, o Expert Advisor é então parado e otimizado repetidamente usando o Strategy Tester, e assim por diante.
Podemos atribuir a reotimização e tomada de decisões de reotimização como um processo para o Expert Advisor sem, naturalmente, interromper seu trabalho?
Uma das soluções para esse problema foi proposta pela Quantum em seu artigo "Adaptive Trading Systems and Their Use in MetaTrader5 Terminal", dedicado ao uso de um sistema de negociação real juntamente com algumas (em número ilimitado) estratégias de negociação virtuais, onde a estratégia selecionada foi até agora a que trouxe o maior lucro. A decisão de mudar a estratégia de negociação é adotada depois de um certo valor de barra fixa ter sido superado.
Eu proponho usar um código algorítimo (GA) genético selecionado pelo joo no artigo "Genetic Algorithms - It's Easy!". Veremos a implementação de tal Expert Advisor (um dos exemplos abaixo é um EA proposto para participação no Automated Trading Championship 2011).
Trabalho em progresso
Por isso, precisamos definir o que o Expert Advisor deve ser capaz de fazer. Em primeiro lugar, e nem é preciso dizer, negociar usando a estratégia selecionada. Em segundo lugar, tomar uma decisão: se é hora de reotimizar (para realizar uma nova otimização dos parâmetros de entrada). E terceiro, para reotimizar utilizando GA. Para começar, vamos rever a reotimização mais simples - existe é uma estratégia que basta selecionarmos os novos parâmetros. Veremos então se pudermos, utilizando GA, selecionar uma outra estratégia em um ambiente de mercado que mudou e nesse caso - como isso pode ser feito.
Além disso, para facilitar a simulação na função de aptidão nós tomamos a decisão de trocar apenas barras concluídas em um único instrumento. Não haverá a adição de posições e fechamentos parciais. Aqueles que preferem usar paradas fixas e também usar trailing stops, por favor, consulte o artigo "Tick Generation Algorithm in MetaTrader5 Strategy Tester" a fim de implementar as verificações Stop Loss e Take Profit na função de aptidão. Vou expandir a inteligente frase abaixo:
Na função de aptidão eu simulei um modo de teste conhecido no Testador como "Apenas preços abertos". MAS! Isso não significa que esta é a única simulação possível do processo de teste na função de aptidão. Pessoas mais cautelosas podem querer implementar um teste de função de aptidão usando o modo "Cada Tick". Para não reinventar a roda, ou fazer "cada tick", gostaria de chamar a sua atenção para um algoritmo existente desenvolvido pela MetaQuotes. Em outras palavras, depois de ter lido este artigo uma pessoa será capaz de simular o modo "Cada Tick" na função de aptidão, que é uma condição necessária para a correta simulação de paradas e tomadas em FF.
Antes de prosseguir para o ponto principal - a implementação da estratégia - vamos rever brevemente os aspectos técnicos e implementar funções auxiliares que definem a abertura de uma nova barra, bem como abertura e fechamento de posições:
//+------------------------------------------------------------------+ //| Define whether a new bar has opened | //+------------------------------------------------------------------+ bool isNewBars() { CopyTime(s,tf,0,1,curBT); TimeToStruct(curBT[0],curT); if(tf==PERIOD_M1|| tf==PERIOD_M2|| tf==PERIOD_M3|| tf==PERIOD_M4|| tf==PERIOD_M5|| tf==PERIOD_M6|| tf==PERIOD_M10|| tf==PERIOD_M12|| tf==PERIOD_M15|| tf==PERIOD_M20|| tf==PERIOD_M30) if(curT.min!=prevT.min) { prevBT[0]=curBT[0]; TimeToStruct(prevBT[0],prevT); return(true); }; if(tf==PERIOD_H1|| tf==PERIOD_H2|| tf==PERIOD_H3|| tf==PERIOD_H4|| tf==PERIOD_H6|| tf==PERIOD_H8|| tf==PERIOD_M12) if(curT.hour!=prevT.hour) { prevBT[0]=curBT[0]; TimeToStruct(prevBT[0],prevT); return(true); }; if(tf==PERIOD_D1|| tf==PERIOD_W1) if(curT.day!=prevT.day) { prevBT[0]=curBT[0]; TimeToStruct(prevBT[0],prevT); return(true); }; if(tf==PERIOD_MN1) if(curT.mon!=prevT.mon) { prevBT[0]=curBT[0]; TimeToStruct(prevBT[0],prevT); return(true); }; return(false); } //+------------------------------------------------------------------+ //| ClosePosition | //+------------------------------------------------------------------+ void ClosePosition() { request.action=TRADE_ACTION_DEAL; request.symbol=PositionGetSymbol(0); if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY) request.type=ORDER_TYPE_SELL; else request.type=ORDER_TYPE_BUY; request.type_filling=ORDER_FILLING_FOK; if(SymbolInfoInteger(PositionGetSymbol(0),SYMBOL_TRADE_EXEMODE)==SYMBOL_TRADE_EXECUTION_REQUEST|| SymbolInfoInteger(PositionGetSymbol(0),SYMBOL_TRADE_EXEMODE)==SYMBOL_TRADE_EXECUTION_INSTANT) { request.sl=NULL; request.tp=NULL; request.deviation=100; } while(PositionsTotal()>0) { request.volume=NormalizeDouble(MathMin(PositionGetDouble(POSITION_VOLUME),SymbolInfoDouble(PositionGetSymbol(0),SYMBOL_VOLUME_MAX)),2); if(SymbolInfoInteger(PositionGetSymbol(0),SYMBOL_TRADE_EXEMODE)==SYMBOL_TRADE_EXECUTION_REQUEST|| SymbolInfoInteger(PositionGetSymbol(0),SYMBOL_TRADE_EXEMODE)==SYMBOL_TRADE_EXECUTION_INSTANT) { if(request.type==ORDER_TYPE_SELL) request.price=SymbolInfoDouble(s,SYMBOL_BID); else request.price=SymbolInfoDouble(s,SYMBOL_ASK); } OrderSend(request,result); Sleep(10000); } } //+------------------------------------------------------------------+ //| OpenPosition | //+------------------------------------------------------------------+ void OpenPosition() { double vol; request.action=TRADE_ACTION_DEAL; request.symbol=s; request.type_filling=ORDER_FILLING_FOK; if(SymbolInfoInteger(s,SYMBOL_TRADE_EXEMODE)==SYMBOL_TRADE_EXECUTION_REQUEST|| SymbolInfoInteger(s,SYMBOL_TRADE_EXEMODE)==SYMBOL_TRADE_EXECUTION_INSTANT) { request.sl=NULL; request.tp=NULL; request.deviation=100; } vol=MathFloor(AccountInfoDouble(ACCOUNT_FREEMARGIN)*optF*AccountInfoInteger(ACCOUNT_LEVERAGE) /(SymbolInfoDouble(s,SYMBOL_TRADE_CONTRACT_SIZE)*SymbolInfoDouble(s,SYMBOL_VOLUME_STEP)))*SymbolInfoDouble(s,SYMBOL_VOLUME_STEP); vol=MathMax(vol,SymbolInfoDouble(s,SYMBOL_VOLUME_MIN)); vol=MathMin(vol,GetPossibleLots()*0.95); if(SymbolInfoDouble(s,SYMBOL_VOLUME_LIMIT)!=0) vol=NormalizeDouble(MathMin(vol,SymbolInfoDouble(s,SYMBOL_VOLUME_LIMIT)),2); request.volume=NormalizeDouble(MathMin(vol,SymbolInfoDouble(s,SYMBOL_VOLUME_MAX)),2); while(PositionSelect(s)==false) { if(SymbolInfoInteger(s,SYMBOL_TRADE_EXEMODE)==SYMBOL_TRADE_EXECUTION_REQUEST|| SymbolInfoInteger(s,SYMBOL_TRADE_EXEMODE)==SYMBOL_TRADE_EXECUTION_INSTANT) { if(request.type==ORDER_TYPE_SELL) request.price=SymbolInfoDouble(s,SYMBOL_BID); else request.price=SymbolInfoDouble(s,SYMBOL_ASK); } OrderSend(request,result); Sleep(10000); PositionSelect(s); } while(PositionGetDouble(POSITION_VOLUME)<vol) { request.volume=NormalizeDouble(MathMin(vol-PositionGetDouble(POSITION_VOLUME),SymbolInfoDouble(s,SYMBOL_VOLUME_MAX)),2); if(SymbolInfoInteger(PositionGetSymbol(0),SYMBOL_TRADE_EXEMODE)==SYMBOL_TRADE_EXECUTION_REQUEST|| SymbolInfoInteger(PositionGetSymbol(0),SYMBOL_TRADE_EXEMODE)==SYMBOL_TRADE_EXECUTION_INSTANT) { if(request.type==ORDER_TYPE_SELL) request.price=SymbolInfoDouble(s,SYMBOL_BID); else request.price=SymbolInfoDouble(s,SYMBOL_ASK); } OrderSend(request,result); Sleep(10000); PositionSelect(s); } } //+------------------------------------------------------------------+
Após cuidadosa consideração, você pode notar três parâmetros importantes na função de abertura posição: as variáveis s e optF e a chamada da função GetPossibleLots():
- s - instrumento de negociação, uma das variáveisotimizadas pelo GA,
- optF - parte do depósito a ser usado para negociação (outra variável otimizada pelo GA),
- Função GetPossibleLots() - devolve a parte do depósito a ser usado para negociação:
//+------------------------------------------------------------------+ //| GetPossibleLots | //+------------------------------------------------------------------+ double GetPossibleLots() { request.volume=1.0; if(request.type==ORDER_TYPE_SELL) request.price=SymbolInfoDouble(s,SYMBOL_BID); else request.price=SymbolInfoDouble(s,SYMBOL_ASK); OrderCheck(request,check); return(NormalizeDouble(AccountInfoDouble(ACCOUNT_FREEMARGIN)/check.margin,2)); }
Ligeiramente quebrando a ordem da narrativa, apresentamos mais duas funções comuns a todos os Expert Advisors e essenciais no estágio dois:
//+------------------------------------------------------------------+ //| InitRelDD | //+------------------------------------------------------------------+ void InitRelDD() { ulong DealTicket; double curBalance; prevBT[0]=D'2000.01.01 00:00:00'; TimeToStruct(prevBT[0],prevT); curBalance=AccountInfoDouble(ACCOUNT_BALANCE); maxBalance=curBalance; HistorySelect(D'2000.01.01 00:00:00',TimeCurrent()); for(int i=HistoryDealsTotal();i>0;i--) { DealTicket=HistoryDealGetTicket(i); curBalance=curBalance+HistoryDealGetDouble(DealTicket,DEAL_PROFIT); if(curBalance>maxBalance) maxBalance=curBalance; } } //+------------------------------------------------------------------+ //| GetRelDD | //+------------------------------------------------------------------+ double GetRelDD() { if(AccountInfoDouble(ACCOUNT_BALANCE)>maxBalance) maxBalance=AccountInfoDouble(ACCOUNT_BALANCE); return((maxBalance-AccountInfoDouble(ACCOUNT_BALANCE))/maxBalance); }
O que podemos ver aqui? A primeira função determina o valor máximo do saldo de conta, a segunda função calcula o rebaixamento atual relativo da conta. Suas peculiaridades serão definidas em detalhes na descrição do estágio dois.
- um negocia utilizando intersecções de médias móveis (Golden Cross - compramos um instrumento, Death Cross - vendemos);
- o outro é uma simples rede neural que recebe alterações de preços no intervalo de [0..1], ao longo das últimas cinco sessões de negociação.
Algoritmicamente, o trabalho de um Expert Advisor de auto-otimização pode ser exemplificado da seguinte forma:
-
Inicialização de variáveis utilizadas pelo Expert Advisor: define e inicializa os buffers indicadores ou configura a topologia de rede neural (número de camadas/neurônios em uma camada; uma rede neural simples, onde o número de neurônios é o mesmo em todas as camadas é dado como um exemplo), define o período de trabalho. Além disso, provavelmente o passo mais importante - nós chamamos a função Otimização Genética que por sua vez aborda a função mais importante - função de aptidão (adiante - FF).
IMPORTANTE! Há uma nova FF para cada estratégia de negociação, ou seja, é criada cada vez uma nova, por exemplo, FF para uma única média móvel é completamente diferente da FF para duas médias móveis e difere significativamente da rede neural FF.
O resultado de desempenho FF em meus Expert Advisors é um equilíbrio máximo desde que o levantamento relativo não ultrapasse o valor crítico definido como uma variável externa (nos nossos exemplos - 0,5). Em outras palavras, se a próxima execução GA der o saldo de 100.000, enquanto que o saldo de abaixamento relativo é -0,6, então FF = 0,0. No seu caso, meu caro leitor, o resultado FF pode trazer critérios completamente diferentes.
Recolha os resultados de performance do algoritmo genético: por intersecção de médias móveis estes, obviamente, estarão movendo períodos médios, no caso de uma rede neural haverá pesos de sinapse, e o resultado comum para ambos (e para meus outros Expert Advisors) é de um instrumento a ser comercializados até a próxima reotimização e já familiar para nós optF, ou seja, uma parte do depósito a ser usado para negociação. Você é livre para adicionar parâmetros otimizados para sua FF a seu próprio critério, por exemplo, você também pode selecionar prazo ou outros parâmetros...
O último passo na inicialização é descobrir o valor de saldo máximo da conta. Por que é importante? Porque este é um ponto de partida para a tomada de decisão de reotimização.
IMPORTANTE! Como a decisão sobre a reotimização é tomada: uma vez que o levantamento relativo de SALDO atinge um certo valor crítico definido como uma variável externa (nos nossos exemplos - 0,2), precisamos reotimizar. Em ordem de não ter um Expert Advisor para implementar a reotimização em cada barra, ao atingir o levantamento crítico, o valor de saldo máximo é substituído por um valor atual.
- Negociação em andamento.
- Após cada posição fechada vamos verificar se o levantamento de equilíbrio atingiu o valor crítico. Se o valor crítico foi atingido, executamos um GA e coletamos os seus resultados de performance (isso é reotimização!)
-
E nós estamos esperando tanto por um telefonema do diretor estrangeiro pedindo para não falir o mundo, ou (o que é mais frequente) por Stop Out, Chamada de margem, ambulância de emergência...
Veja abaixo uma implementação programada do descrito acima para o Expert Advisor usando a estratégia de médias móveis (código fonte também está disponível) e usando a rede neural - tudo como um código fonte.
O código está disponível sob termos e condições da licença GPL.
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { tf=Period(); //---for bar-to-bar test... prevBT[0]=D'2001.01.01'; //---... long ago TimeToStruct(prevBT[0],prevT); //--- historical depth (should be set since the optimisation is based on historical data) depth=10000; //--- copies at a time (should be set since the optimisation is based on historical data) count=2; ArrayResize(LongBuffer,count); ArrayResize(ShortBuffer,count); ArrayInitialize(LongBuffer,0); ArrayInitialize(ShortBuffer,0); //--- calling the neural network genetic optimisation function GA(); //--- getting the optimised neural network parameters and other variables GetTrainResults(); //--- getting the account drawdown InitRelDD(); return(0); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { if(isNewBars()==true) { bool trig=false; CopyBuffer(MAshort,0,0,count,ShortBuffer); CopyBuffer(MAlong,0,0,count,LongBuffer); if(LongBuffer[0]>LongBuffer[1] && ShortBuffer[0]>LongBuffer[0] && ShortBuffer[1]<LongBuffer[1]) { if(PositionsTotal()>0) { if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY) { ClosePosition(); trig=true; } } } if(LongBuffer[0]<LongBuffer[1] && ShortBuffer[0]<LongBuffer[0] && ShortBuffer[1]>LongBuffer[1]) { if(PositionsTotal()>0) { if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_SELL) { ClosePosition(); trig=true; } } } if(trig==true) { //--- if the account drawdown has exceeded the allowable value: if(GetRelDD()>maxDD) { //--- calling the neural network genetic optimisation function GA(); //--- getting the optimised neural network parameters and other variables GetTrainResults(); //--- readings of the drawdown will from now on be based on the current balance instead of the maximum balance maxBalance=AccountInfoDouble(ACCOUNT_BALANCE); } } CopyBuffer(MAshort,0,0,count,ShortBuffer); CopyBuffer(MAlong,0,0,count,LongBuffer); if(LongBuffer[0]>LongBuffer[1] && ShortBuffer[0]>LongBuffer[0] && ShortBuffer[1]<LongBuffer[1]) { request.type=ORDER_TYPE_SELL; OpenPosition(); } if(LongBuffer[0]<LongBuffer[1] && ShortBuffer[0]<LongBuffer[0] && ShortBuffer[1]>LongBuffer[1]) { request.type=ORDER_TYPE_BUY; OpenPosition(); } }; } //+------------------------------------------------------------------+ //| Preparing and calling the genetic optimizer | //+------------------------------------------------------------------+ void GA() { //--- number of genes (equal to the number of optimised variables), //--- all of them should be specified in the FitnessFunction()) GeneCount =OptParamCount+2; //--- number of chromosomes in a colony ChromosomeCount=GeneCount*11; //--- minimum search range RangeMinimum =0.0; //--- maximum search range RangeMaximum =1.0; //--- search pitch Precision =0.0001; //--- 1 is a minimum, anything else is a maximum OptimizeMethod =2; ArrayResize(Chromosome,GeneCount+1); ArrayInitialize(Chromosome,0); //--- number of epochs without any improvement Epoch =100; //--- ratio of replication, natural mutation, artificial mutation, gene borrowing, //--- crossingover, interval boundary displacement ratio, every gene mutation probabilty, % UGA(100.0,1.0,1.0,1.0,1.0,0.5,1.0); } //+------------------------------------------------------------------+ //| Fitness function for neural network genetic optimizer: | //| selecting a pair, optF, synapse weights; | //| anything can be optimised but it is necessary | //| to carefully monitor the number of genes | //+------------------------------------------------------------------+ void FitnessFunction(int chromos) { int b; //--- is there an open position? bool trig=false; //--- direction of an open position string dir=""; //--- opening price double OpenPrice=0; //--- intermediary between a gene colony and optimised parameters int z; //--- current balance double t=cap; //--- maximum balance double maxt=t; //--- absolute drawdown double aDD=0; //--- relative drawdown double rDD=0.000001; //--- fitness function proper double ff=0; //--- GA is selecting a pair z=(int)MathRound(Colony[GeneCount-1][chromos]*12); switch(z) { case 0: {s="AUDUSD"; break;}; case 1: {s="AUDUSD"; break;}; case 2: {s="EURAUD"; break;}; case 3: {s="EURCHF"; break;}; case 4: {s="EURGBP"; break;}; case 5: {s="EURJPY"; break;}; case 6: {s="EURUSD"; break;}; case 7: {s="GBPCHF"; break;}; case 8: {s="GBPJPY"; break;}; case 9: {s="GBPUSD"; break;}; case 10: {s="USDCAD"; break;}; case 11: {s="USDCHF"; break;}; case 12: {s="USDJPY"; break;}; default: {s="EURUSD"; break;}; } MAshort=iMA(s,tf,(int)MathRound(Colony[1][chromos]*MaxMAPeriod)+1,0,MODE_SMA,PRICE_OPEN); MAlong =iMA(s,tf,(int)MathRound(Colony[2][chromos]*MaxMAPeriod)+1,0,MODE_SMA,PRICE_OPEN); dig=MathPow(10.0,(double)SymbolInfoInteger(s,SYMBOL_DIGITS)); //--- GA is selecting the optimal F optF=Colony[GeneCount][chromos]; leverage=AccountInfoInteger(ACCOUNT_LEVERAGE); contractSize=SymbolInfoDouble(s,SYMBOL_TRADE_CONTRACT_SIZE); b=MathMin(Bars(s,tf)-1-count-MaxMAPeriod,depth); //--- for a neural network using historical data - where the data is copied from for(from=b;from>=1;from--) { CopyBuffer(MAshort,0,from,count,ShortBuffer); CopyBuffer(MAlong,0,from,count,LongBuffer); if(LongBuffer[0]>LongBuffer[1] && ShortBuffer[0]>LongBuffer[0] && ShortBuffer[1]<LongBuffer[1]) { if(trig==false) { CopyOpen(s,tf,from,count,o); OpenPrice=o[1]; dir="SELL"; trig=true; } else { if(dir=="BUY") { CopyOpen(s,tf,from,count,o); if(t>0) t=t+t*optF*leverage*(o[1]-OpenPrice)*dig/contractSize; else t=0; if(t>maxt) {maxt=t; aDD=0;} else if((maxt-t)>aDD) aDD=maxt-t; if((maxt>0) && (aDD/maxt>rDD)) rDD=aDD/maxt; OpenPrice=o[1]; dir="SELL"; trig=true; } } } if(LongBuffer[0]<LongBuffer[1] && ShortBuffer[0]<LongBuffer[0] && ShortBuffer[1]>LongBuffer[1]) { if(trig==false) { CopyOpen(s,tf,from,count,o); OpenPrice=o[1]; dir="BUY"; trig=true; } else { if(dir=="SELL") { CopyOpen(s,tf,from,count,o); if(t>0) t=t+t*optF*leverage*(OpenPrice-o[1])*dig/contractSize; else t=0; if(t>maxt) {maxt=t; aDD=0;} else if((maxt-t)>aDD) aDD=maxt-t; if((maxt>0) && (aDD/maxt>rDD)) rDD=aDD/maxt; OpenPrice=o[1]; dir="BUY"; trig=true; } } } } if(rDD<=trainDD) ff=t; else ff=0.0; AmountStartsFF++; Colony[0][chromos]=ff; } //+---------------------------------------------------------------------+ //| getting the optimized neural network parameters and other variables | //| should always be equal to the number of genes | //+---------------------------------------------------------------------+ void GetTrainResults() { //--- intermediary between a gene colony and optimised parameters int z; MAshort=iMA(s,tf,(int)MathRound(Chromosome[1]*MaxMAPeriod)+1,0,MODE_SMA,PRICE_OPEN); MAlong =iMA(s,tf,(int)MathRound(Chromosome[2]*MaxMAPeriod)+1,0,MODE_SMA,PRICE_OPEN); CopyBuffer(MAshort,0,from,count,ShortBuffer); CopyBuffer(MAlong,0,from,count,LongBuffer); //--- save the best pair z=(int)MathRound(Chromosome[GeneCount-1]*12); switch(z) { case 0: {s="AUDUSD"; break;}; case 1: {s="AUDUSD"; break;}; case 2: {s="EURAUD"; break;}; case 3: {s="EURCHF"; break;}; case 4: {s="EURGBP"; break;}; case 5: {s="EURJPY"; break;}; case 6: {s="EURUSD"; break;}; case 7: {s="GBPCHF"; break;}; case 8: {s="GBPJPY"; break;}; case 9: {s="GBPUSD"; break;}; case 10: {s="USDCAD"; break;}; case 11: {s="USDCHF"; break;}; case 12: {s="USDJPY"; break;}; default: {s="EURUSD"; break;}; } //--- saving the best optimal F optF=Chromosome[GeneCount]; } //+------------------------------------------------------------------+
Vejamos a função principal do algoritmo - a função de aptidão.
A ideia por trás de um Expert Advisor de auto-otimização é baseada na simulação do processo de negociação (como no padrão Tester por MetaQuotes) dentro de um período de tempo (digamos, um histórico de 10.000 barras) na função de aptidão, o qual recebe uma entrada de variáveisotimizadas a partir do Algoritmo Genético (função GA()). No caso de um algoritmo baseado na intersecção de médias móveis, as variáveis otimizadas incluem:
- instrumento (no estrangeiro - um par de moedas); sim, é um Expert Advisor de múltiplas moedas e o Algoritmo Genético seleciona um instrumento (uma vez que o código foi retirado do Expert Advisor proposto para a participação no Campeonato, os pares que tem correspondência com os pares de moedas do Campeonato, em geral, não pode haver qualquer instrumento citado por um corretor).
Nota: infelizmente, o Expert Advisor não pode obter a lista par da janela MarketWatch no modo de teste (esclarecemos aqui graças aos usuários MetaQuotes - Sem chance!). Portanto, se você deseja executar o Expert Advisor no testador separadamente para estrangeiros e ações, apenas especifique os seus próprios instrumentos na FF e função GetTrainResults ().
- parte do depósito para ser usado na negociação;
- períodos de duas médias móveis.
Os exemplos abaixo mostram uma variável de um Expert Advisor PARA TESTE!
O código NEGOCIAçãO REAL pode ser significativamente simplificado usando a lista de instrumentos a partir da janela Market Watch.
A fim de fazer isso na FF e função GetTrainResults () com comentários "// --- GA selecionando par" e "//--- salvando o melhor par" basta escrever:
//--- GA is selecting a pair z=(int)MathRound(Colony[GeneCount-1][chromos]*(SymbolsTotal(true)-1)); s=SymbolName(z,true);
Assim, no início da FF nós especificamos e inicializaremos, se necessário, as variáveis para simulação da negociação baseada em histórico. Na próxima fase, coletaremos diferentes valores das variáveis otimizadas a partir do Algoritmo Genético, por exemplo desta linha "OoptF=Colony [GeneCount] [cromos]; o valor da parte de depósito é transferido para a FF de GA.
Verificamos ainda, o número disponível de barras no histórico e iniciando com 10000 barras ou a primeira barra disponível simulamos o processo de recebimento de cotas no modo "Apenas preços abertos" e tomada de decisões comerciais:
- Copie os valores de médias móveis de buffers;
- Verifique, se é uma Death Cross.
- Se existe uma Death Cross e não há posições abertas (if(trig==false)) - abrir uma posição de venda SELL virtual (basta lembrar o preço de abertura e direção);
- Se existe uma Death Cross e uma posição de compra BUY aberta (if(dir=="BUY")) - assumir o preço de abertura da barra e prestar a atenção para as três linhas muito importantes, como segue:
- Simular fechamento de uma posição e mudar no saldo: o saldo atual é aumentado por um valor de saldo atual, que é multiplicado pela parte do depósito a ser negociado, multiplicado pela diferença entre os preços aberto e fechado, e multiplicado pela preço pip (bruto);
- Verifique se o saldo atual atingiu o máximo ao longo da histórico da simulação de negociação, se não, calcular o levantamento de saldo máximo em dinheiro;
- Converta o levantamento de saldo calculado anteriormente em dinheiro em levantamento de saldo relativo;
- Abra uma posição virtual de venda SELL (apenas lembre o preço aberto e a direção);
- Faça verificações e cálculos semelhantes para a Golden Cross.
Depois de passar por todo o histórico disponível e simulação da negociação virtual, calcule o valor final de FF: se o levantamento de saldo relativo calculado for inferior do que o conjunto de testes, então FF = saldo, caso contrário FF = 0. Algoritmo Genético visa a maximização da função de aptidão!
Depois de tudo, dando vários valores dos instrumentos, partes de depósito e períodos de médias móveis, o Algoritmo Genético encontrará os valores que maximizam o saldo ao levantamento relativo mínimo (mínimo é definido pelo usuário).
Conclusão
Aqui está uma breve conclusão: é fácil criar um Expert Advisor de autotreinamento, a parte difícil é descobrir o que inserir (o importante é uma ideia, a implementação é apenas uma questão de técnica).
Em antecipação à pergunta de pessimistas - "Isto funciona?", eu tenho uma resposta - sim; a minha palavra para os otimistas - este não é o Santo Graal.
Qual é a diferença fundamental entre o método proposto e pelo Quantum? Ele pode ser melhor exemplificado pela comparação dos Expert Advisors que utilizam MA's:
- A decisão sobre os períodos de MA está no Sistema de Negociação Adaptável e deve ser realizada antes da compilação e rigorosamente codificada, e a escolha só é possível fora deste número limitado de variáveis; não tomamos qualquer decisão sobre os períodos antes da compilação no Expert Advisor Geneticamente Otimizado, esta decisão será feita por GA e o número de variáveis é limitado apenas pelo senso comum.
- A negociação virtual no Sistema de Negociação Adaptável é bar-to-bar (barra a barra); mas raramente é assim no Expert Advisor Geneticamente Otimizado - e somente após a ocorrência de condições para reotimização. O desempenho do computador sobre o aumento do número de estratégias, parâmetros, instrumentos pode ser um fator limitante para o Sistema de negociação adaptável.
Anexo
Aqui está o que nós temos se a rede neural for executada no Testador sem qualquer otimização e com base em gráficos diários, a partir de 01.01.2010:
Relatório do testador de estratégia |
||||||||||||
MetaQuotes-Demo (Build 523) |
||||||||||||
Expert Advisor: | ANNExemplo | |||||||||||
Símbolo: | EURUSD | |||||||||||
Período: | Diário (01.01.2010 - 30.09.2011) | |||||||||||
Parâmetros de entrada: | trainDD=0.9 | |||||||||||
maxDD=0.1 | ||||||||||||
Corretor: | Alpari NZ Limitado | |||||||||||
Moeda: | USD | |||||||||||
Depósito inicial: | 10 000.00 | |||||||||||
Alavancagem: | 1:100 | |||||||||||
Resultados |
||||||||||||
Qualidade do histórico: | 100% | |||||||||||
Barras: | 454 | Ticks: | 2554879 | |||||||||
Lucro líquido total: | -9 094.49 | Lucro bruto: | 29 401.09 | Perda bruta: | -38 495.58 | |||||||
Fator de lucro: | 0,76 | Retorno esperado: | -20,53 | Nível de margem: | 732,30% | |||||||
Fator de recuperação: | -0,76 | índice Sharpe: | -0,06 | Resultado onTester: | 0 | |||||||
Levantamento de saldo: | ||||||||||||
Levantamento de saldo abs.: | 9 102.56 | Levantamento de saldo máximo: | 11 464.70 (92.74%) | Levantamento de saldo relativo: | 92.74% (11 464.70) | |||||||
Levantamento de patrimônio: | ||||||||||||
Levantamento de patrimônio abs.: | 9 176.99 | Levantamento máximo de patrimônio: | 11 904.00 (93.53%) | Levantamento de patrimônio relativo: | 93.53% (11 904.00) | |||||||
Total de negociações: | 443 | Negociações curtas (vencidas, %): | 7 (14.29%) | Negociações longas (vencidas, %): | 436 (53.44%) | |||||||
Total de negociações: | 886 | Negócios lucrativos (% do total): | 234 (52.82%) | Negócios perdidos (% do total): | 209 (47.18%) | |||||||
Negócio com maior lucro: | 1 095.57 | Negociação com maior perda: | -1 438.85 | |||||||||
Lucro comercial médio: | 125,65 | Perda comercial média: | -184,19 | |||||||||
Máximos ganhos consecutivos (lucro em dinheiro): | 8 (397.45) | Máximas perdas consecutivas (perdas em dinheiro): | 8 (-1 431.44) | |||||||||
Máximo lucro consecutivo (contagem de vitórias): | 1 095.57 (1) | Máximas perdas consecutivas (contagem de perdas): | -3 433.21 (6) | |||||||||
Médias de ganhos consecutivos: | 2 | Médias de perdas consecutivas: | 2 |
e aqui abaixo são as três variáveis da reotimização para escolher:
primeiro...
Tempo | Negócio | Símbolo | Tipo | Direção | Volume | Preço | Ordem | Troca | Lucro | Saldo |
01.01.2010 00:00 | 1 | saldo | 0,00 | 10 000.00 | 10 000.00 | |||||
04.01.2010 00:00 | 2 | AUDUSD | compra | em | 0,90 | 0,89977 | 2 | 0,00 | 0,00 | 10 000.00 |
05.01.2010 00:00 | 3 | AUDUSD | venda | fora | 0,90 | 0,91188 | 3 | 5,67 | 1 089.90 | 11 095.57 |
05.01.2010 00:00 | 4 | AUDUSD | compra | em | 0,99 | 0,91220 | 4 | 0,00 | 0,00 | 11 095.57 |
06.01.2010 00:00 | 5 | AUDUSD | venda | fora | 0,99 | 0,91157 | 5 | 6,24 | -62,37 | 11 039.44 |
06.01.2010 00:00 | 6 | AUDUSD | compra | em | 0,99 | 0,91190 | 6 | 0,00 | 0,00 | 11 039.44 |
07.01.2010 00:00 | 7 | AUDUSD | venda | fora | 0,99 | 0,91924 | 7 | 18,71 | 726,66 | 11 784.81 |
segundo...
Tempo | Negócio | Símbolo | Tipo | Direção | Volume | Preço | Ordem | Comissão | Troca | Lucro | Saldo |
19.05.2010 00:00 | 189 | AUDUSD | venda | fora | 0,36 | 0,86110 | 189 | 0,00 | 2,27 | -595,44 | 4 221.30 |
19.05.2010 00:00 | 190 | EURAUD | venda | em | 0,30 | 1,41280 | 190 | 0,00 | 0,00 | 0,00 | 4 221.30 |
20.05.2010 00:00 | 191 | EURAUD | compra | fora | 0,30 | 1,46207 | 191 | 0,00 | 7,43 | -1 273.26 | 2 955.47 |
20.05.2010 00:00 | 192 | AUDUSD | compra | em | 0,21 | 0,84983 | 192 | 0,00 | 0,00 | 0,00 | 2 955.47 |
terceiro
Tempo | Negócio | Símbolo | Tipo | Direção | Volume | Preço | Ordem | Troca | Lucro | Saldo |
16.06.2010 00:00 | 230 | GBPCHF | compra | em | 0,06 | 1,67872 | 230 | 0,00 | 0,00 | 2 128.80 |
17.06.2010 00:00 | 231 | GBPCHF | venda | fora | 0,06 | 1,66547 | 231 | 0,13 | -70,25 | 2 058.68 |
17.06.2010 00:00 | 232 | GBPCHF | compra | em | 0,06 | 1,66635 | 232 | 0,00 | 0,00 | 2 058.68 |
18.06.2010 00:00 | 233 | GBPCHF | venda | fora | 0,06 | 1,64705 | 233 | 0,04 | -104,14 | 1 954.58 |
18.06.2010 00:00 | 234 | AUDUSD | compra | em | 0,09 | 0,86741 | 234 | 0,00 | 0,00 | 1 954.58 |
21.06.2010 00:00 | 235 | AUDUSD | venda | fora | 0,09 | 0,87184 | 235 | 0,57 | 39,87 | 1 995.02 |
21.06.2010 00:00 | 236 | AUDUSD | compra | em | 0,09 | 0,88105 | 236 | 0,00 | 0,00 | 1 995.02 |
22.06.2010 00:00 | 237 | AUDUSD | venda | fora | 0,09 | 0,87606 | 237 | 0,57 | -44,91 | 1 950.68 |
22.06.2010 00:00 | 238 | AUDUSD | compra | em | 0,09 | 0,87637 | 238 | 0,00 | 0,00 | 1 950.68 |
23.06.2010 00:00 | 239 | AUDUSD | venda | fora | 0,09 | 0,87140 | 239 | 0,57 | -44,73 | 1 906.52 |
23.06.2010 00:00 | 240 | AUDUSD | compra | em | 0,08 | 0,87197 | 240 | 0,00 | 0,00 | 1 906.52 |
24.06.2010 00:00 | 241 | AUDUSD | venda | fora | 0,08 | 0,87385 | 241 | 1,51 | 15,04 | 1 923.07 |
24.06.2010 00:00 | 242 | AUDUSD | compra | em | 0,08 | 0,87413 | 242 | 0,00 | 0,00 | 1 923.07 |
25.06.2010 00:00 | 243 | AUDUSD | venda | fora | 0,08 | 0,86632 | 243 | 0,50 | -62,48 | 1 861.09 |
25.06.2010 00:00 | 244 | AUDUSD | compra | em | 0,08 | 0,86663 | 244 | 0,00 | 0,00 | 1 861.09 |
28.06.2010 00:00 | 245 | AUDUSD | venda | fora | 0,08 | 0,87375 | 245 | 0,50 | 56,96 | 1 918.55 |
28.06.2010 00:00 | 246 | AUDUSD | compra | em | 0,08 | 0,87415 | 246 | 0,00 | 0,00 | 1 918.55 |
29.06.2010 00:00 | 247 | AUDUSD | venda | fora | 0,08 | 0,87140 | 247 | 0,50 | -22,00 | 1 897.05 |
29.06.2010 00:00 | 248 | AUDUSD | compra | em | 0,08 | 0,87173 | 248 | 0,00 | 0,00 | 1 897.05 |
01.07.2010 00:00 | 249 | AUDUSD | venda | fora | 0,08 | 0,84053 | 249 | 2,01 | -249,60 | 1 649.46 |
01.07.2010 00:00 | 250 | EURGBP | venda | em | 0,07 | 0,81841 | 250 | 0,00 | 0,00 | 1 649.46 |
02.07.2010 00:00 | 251 | EURGBP | compra | fora | 0,07 | 0,82535 | 251 | -0,04 | -73,69 | 1 575.73 |
02.07.2010 00:00 | 252 | EURGBP | venda | em | 0,07 | 0,82498 | 252 | 0,00 | 0,00 | 1 575.73 |
05.07.2010 00:00 | 253 | EURGBP | compra | fora | 0,07 | 0,82676 | 253 | -0,04 | -18,93 | 1 556.76 |
05.07.2010 00:00 | 254 | EURGBP | venda | em | 0,06 | 0,82604 | 254 | 0,00 | 0,00 | 1 556.76 |
06.07.2010 00:00 | 255 | EURGBP | compra | fora | 0,06 | 0,82862 | 255 | -0,04 | -23,43 | 1 533.29 |
P.S. Como lição de casa: não selecione apenas os parâmetros de um determinado sistema, mas também selecione o sistema que melhor se adapte ao mercado em um dado momento (sugestão - a partir do banco de sistemas).
Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/334
- 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
1
MetaQuotes Software Corp.:Novo artigo Dr. Tradelove ou como parei de me preocupar e criei um Expert Advisor para autotreinamento foi publicado:
Autor: Roman Zamozhnyy
Boa tarde!
Grande matéria!
Ao ler seu post me deu um estalo!
Depois de passar um bom tempo otimizando resultados do robô percebi o que era óbvio, uma estratégia somente será válida por um determinado tempo, o tempo em que o mercado se mantiver, mas com o tempo o mercado muda e a estratégia que há um mês atrás era boa já não é tanto, me obrigando a realizar novos testes com parâmetros para encontrar os períodos e outras configurações mais adaptáveis ao mercado atual...
A minha ideia é que o robô possa realizar sozinho em um período de tempo novas configurações mais adaptáveis ao mercado e ele escolher a melhor delas e essa pré definida por mim.
Minha dúvida é se tem como fazer com que o robô faça esse "auto-treinamento" a cada xxx semanas ou xx meses e ele mesmo se atualize com o melhor valor escolhido mediante as variaveis externas escolhidas nas configurações de parâmetros.