Modelando séries temporais usando símbolos personalizados de acordo com as leis de distribuição especificadas
Aleksey Zinovik | 20 novembro, 2018
Sumário
Introdução
O terminal de negociação MetaTrader 5 permite criar e usar símbolos personalizados. O trader tem a oportunidade de testar seus próprios pares de moedas e outros instrumentos financeiros. No artigo, são apresentadas maneiras de criar e excluir símbolos personalizados, gerar ticks e barras de acordo com as leis de distribuição especificadas.
Também serão propostas maneiras para formar tendências e vários padrões gráficos. Para trabalhar com símbolos personalizados, estarão disponíveis scripts prontos com configurações mínimas, permitindo que os traders que não possuem habilidades de programação em MQL5 usem todo o de potência dos símbolos personalizados.
Criação e exclusão de símbolos personalizados
O artigo mostra como criar símbolos personalizados na janela "Símbolos" do MetaTrader 5, com base nos símbolos já existentes.
Sugerimos automatizar esse processo usando um script simples com configurações mínimas.
O script tem 4 parâmetros de entrada:
- nome do símbolo personalizado
- nome abreviado do par de moedas ou do instrumento financeiro
- nome completo do par de moedas ou do instrumento financeiro
- nome abreviado da moeda base ou do instrumento financeiro, se o símbolo for criado com base no símbolo base.
//+------------------------------------------------------------------+ //| CreateSymbol.mq5 | //| Aleksey Zinovik | //| | //+------------------------------------------------------------------+ #property copyright "Aleksey Zinovik" #property script_show_inputs #property version "1.00" //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ input string SName="ExampleCurrency"; input string CurrencyName="UCR"; input string CurrencyFullName="UserCurrency"; input string BaseName="EURUSD"; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void OnStart() { ResetLastError(); //crie o símbolo if(!CustomSymbolCreate(SName,"\\Forex")) { if(SymbolInfoInteger(SName,SYMBOL_CUSTOM)) Print("O símbolo ",SName," já existe!"); else Print("Erro ao criar símbolo. Código de erro: ",GetLastError()); } else { if(BaseName=="")//crie um novo { //propriedade do tipo String if((SetProperty(SName,SYMBOL_CURRENCY_BASE,CurrencyName,"")) && //unidade monetária base (SetProperty(SName,SYMBOL_CURRENCY_PROFIT,"USD",""))&& //moeda de lucro (SetProperty(SName,SYMBOL_CURRENCY_MARGIN,"USD",""))&& //moeda de margem (SetProperty(SName,SYMBOL_DESCRIPTION,CurrencyName,""))&& //descrição do caractere de string (nome completo) (SetProperty(SName,SYMBOL_BASIS,"","")) && //nome do ativo subjacente para o instrumento derivado (SetProperty(SName,SYMBOL_FORMULA,"","")) && //fórmula para construir o preço de um símbolo personalizado (SetProperty(SName,SYMBOL_ISIN,"","")) && //nome do símbolo de negociação no sistema ISIN (SetProperty(SName,SYMBOL_PAGE,"","")) && //endereço da página da web com informações sobre o símbolo //propriedades do tipo Integer (SetProperty(SName,SYMBOL_CHART_MODE,SYMBOL_CHART_MODE_BID,"")) && //plotagem do grafico segundo o preço Bid (SetProperty(SName,SYMBOL_SPREAD,3,"")) && //spread (SetProperty(SName,SYMBOL_SPREAD_FLOAT,true,"")) && //spread flutuante (SetProperty(SName,SYMBOL_DIGITS,5,"")) && //precisão (número de casas decimais) (SetProperty(SName,SYMBOL_TICKS_BOOKDEPTH,10,"")) && //tamanho do livro de ofertas (SetProperty(SName,SYMBOL_BACKGROUND_COLOR,White,""))&& //cor de fundo em que é destacado o símbolo na Observação do mercado (SetProperty(SName,SYMBOL_TRADE_MODE,SYMBOL_TRADE_MODE_FULL,""))&& //tipo de execução de ordens: acesso completo (SetProperty(SName,SYMBOL_TRADE_EXEMODE,SYMBOL_TRADE_EXECUTION_INSTANT,""))&& //modo de transações: execução imediata (SetProperty(SName,SYMBOL_ORDER_GTC_MODE,SYMBOL_ORDERS_GTC,""))&& //validade das ordens StopLoss e TakeProfit: validas até serem canceladas (SetProperty(SName,SYMBOL_FILLING_MODE,SYMBOL_FILLING_FOK,""))&& //modo de execução da ordem: tudo ou nada (SetProperty(SName,SYMBOL_EXPIRATION_MODE,SYMBOL_EXPIRATION_GTC,""))&& //modo de expiração da ordem: ilimitado no tempo, até o cancelamento explícito (SetProperty(SName,SYMBOL_ORDER_MODE,127,"")) && //tipos de ordens: todos os tipos de ordens (SetProperty(SName,SYMBOL_TRADE_CALC_MODE,SYMBOL_CALC_MODE_FOREX,""))&& //método de cálculo do valor do contrato (SetProperty(SName,SYMBOL_MARGIN_HEDGED_USE_LEG,false,""))&& //modo de cálculo da margem coberta pelo maior lado (SetProperty(SName,SYMBOL_SWAP_MODE,SYMBOL_SWAP_MODE_POINTS,""))&& //modelo de cálculo de swap: cálculo de swap em pontos (SetProperty(SName,SYMBOL_SWAP_ROLLOVER3DAYS,WEDNESDAY,"")) && //dia da semana para o swap triplo (SetProperty(SName,SYMBOL_OPTION_MODE,0,"")) && //tipo de opção (SetProperty(SName,SYMBOL_OPTION_RIGHT,0,"")) && //direito de opção (SetProperty(SName,SYMBOL_TRADE_STOPS_LEVEL,0,"")) && //recuo mínimo em pontos a partir do preço atual de fechamento para posicionamento de ordens Stop (SetProperty(SName,SYMBOL_TRADE_FREEZE_LEVEL,0,"")) && //distância de congelamento de operações de negociação (em pontos) (SetProperty(SName,SYMBOL_START_TIME,0,"")) && //data de início da negociação do instrumento (geralmente usado para futuros) (SetProperty(SName,SYMBOL_EXPIRATION_TIME,0,"")) && //data do final da negociação do instrumento (geralmente usado para futuros) //propriedades do tipo Double (SetProperty(SName,SYMBOL_OPTION_STRIKE,0,"")) && //preço de exercício da opção (SetProperty(SName,SYMBOL_SESSION_PRICE_LIMIT_MAX,0,"")) && //preço mínimo permitido para a sessão (SetProperty(SName,SYMBOL_SESSION_PRICE_LIMIT_MIN,0,"")) && //preço máximo permitido para a sessão (SetProperty(SName,SYMBOL_SESSION_PRICE_SETTLEMENT,0,"")) && //preço de entrega para a sessão atual (SetProperty(SName,SYMBOL_TRADE_ACCRUED_INTEREST,0,"")) && //juros acumulados (para títulos) (SetProperty(SName,SYMBOL_TRADE_FACE_VALUE,0,"")) && //valor nominal (para títulos) (SetProperty(SName,SYMBOL_TRADE_LIQUIDITY_RATE,0,"")) && //índice de liquidez (usado para instrumentos colaterais) (SetProperty(SName,SYMBOL_TRADE_TICK_SIZE,0.00001,"")) && //mudança mínima do preço (SetProperty(SName,SYMBOL_TRADE_TICK_VALUE,1,"")) && //valor do tick (SetProperty(SName,SYMBOL_TRADE_CONTRACT_SIZE,100000,"")) && //tamanho do contrato (SetProperty(SName,SYMBOL_POINT,0.00001,"")) && //valor de um ponto (SetProperty(SName,SYMBOL_VOLUME_MIN,0.01,"")) && //volume mínimo para realização da transação (SetProperty(SName,SYMBOL_VOLUME_MAX,500.00,"")) && //volume máximo para realizar a transação (SetProperty(SName,SYMBOL_VOLUME_STEP,0.01,"")) && //incremento mínimo de alteração do volume para realizar a transação (SetProperty(SName,SYMBOL_VOLUME_LIMIT,0,"")) && //volume total máximo da posição aberta e das ordens pendentes segundo um único símbolo numa só direção (compra ou venda). (SetProperty(SName,SYMBOL_MARGIN_INITIAL,0,"")) && //margem inicial (SetProperty(SName,SYMBOL_MARGIN_MAINTENANCE,0,"")) && //margem de manutenção (SetProperty(SName,SYMBOL_MARGIN_HEDGED,100000,"")) && //tamanho do contrato ou da margem para um lote de posições multidirecionais segundo um único símbolo (SetProperty(SName,SYMBOL_SWAP_LONG,-0.7,"")) && //valor do swap na compra (SetProperty(SName,SYMBOL_SWAP_SHORT,-1,""))) //valor do swap na venda Print("Símbolo ",SName," criado com sucesso"); else Print("Erro ao definir as propriedades do símbolo. Código de erro: ",GetLastError()); } else//criamos com base no ativo base { if((SetProperty(SName,SYMBOL_CURRENCY_BASE,CurrencyName,"")) && (SetProperty(SName,SYMBOL_CURRENCY_PROFIT,"",BaseName)) && (SetProperty(SName,SYMBOL_CURRENCY_MARGIN,"",BaseName)) && (SetProperty(SName,SYMBOL_DESCRIPTION,CurrencyFullName,"")) && (SetProperty(SName,SYMBOL_BASIS,"",BaseName)) && (SetProperty(SName,SYMBOL_FORMULA,"",BaseName)) && (SetProperty(SName,SYMBOL_ISIN,"",BaseName)) && (SetProperty(SName,SYMBOL_PAGE,"",BaseName)) && (SetProperty(SName,SYMBOL_CHART_MODE,0,BaseName)) && (SetProperty(SName,SYMBOL_SPREAD,0,BaseName)) && (SetProperty(SName,SYMBOL_SPREAD_FLOAT,0,BaseName)) && (SetProperty(SName,SYMBOL_DIGITS,0,BaseName)) && (SetProperty(SName,SYMBOL_TICKS_BOOKDEPTH,0,BaseName)) && (SetProperty(SName,SYMBOL_BACKGROUND_COLOR,0,BaseName)) && (SetProperty(SName,SYMBOL_TRADE_MODE,0,BaseName)) && (SetProperty(SName,SYMBOL_TRADE_EXEMODE,0,BaseName)) && (SetProperty(SName,SYMBOL_ORDER_GTC_MODE,0,BaseName)) && (SetProperty(SName,SYMBOL_FILLING_MODE,0,BaseName)) && (SetProperty(SName,SYMBOL_EXPIRATION_MODE,0,BaseName)) && (SetProperty(SName,SYMBOL_ORDER_MODE,0,BaseName)) && (SetProperty(SName,SYMBOL_TRADE_CALC_MODE,0,BaseName)) && (SetProperty(SName,SYMBOL_MARGIN_HEDGED_USE_LEG,0,BaseName)) && (SetProperty(SName,SYMBOL_SWAP_MODE,0,BaseName)) && (SetProperty(SName,SYMBOL_SWAP_ROLLOVER3DAYS,0,BaseName)) && (SetProperty(SName,SYMBOL_OPTION_MODE,0,BaseName)) && (SetProperty(SName,SYMBOL_OPTION_RIGHT,0,BaseName)) && (SetProperty(SName,SYMBOL_TRADE_STOPS_LEVEL,0,BaseName)) && (SetProperty(SName,SYMBOL_TRADE_FREEZE_LEVEL,0,BaseName)) && (SetProperty(SName,SYMBOL_START_TIME,0,BaseName)) && (SetProperty(SName,SYMBOL_EXPIRATION_TIME,0,BaseName)) && (SetProperty(SName,SYMBOL_OPTION_STRIKE,0,BaseName)) && (SetProperty(SName,SYMBOL_SESSION_PRICE_LIMIT_MAX,0,BaseName)) && (SetProperty(SName,SYMBOL_SESSION_PRICE_LIMIT_MIN,0,BaseName)) && (SetProperty(SName,SYMBOL_SESSION_PRICE_SETTLEMENT,0,BaseName)) && (SetProperty(SName,SYMBOL_TRADE_ACCRUED_INTEREST,0,BaseName)) && (SetProperty(SName,SYMBOL_POINT,0,BaseName)) && (SetProperty(SName,SYMBOL_TRADE_CONTRACT_SIZE,0,BaseName)) && (SetProperty(SName,SYMBOL_TRADE_FACE_VALUE,0,BaseName)) && (SetProperty(SName,SYMBOL_TRADE_LIQUIDITY_RATE,0,BaseName)) && (SetProperty(SName,SYMBOL_TRADE_TICK_SIZE,0,BaseName)) && (SetProperty(SName,SYMBOL_TRADE_TICK_VALUE,0,BaseName)) && (SetProperty(SName,SYMBOL_VOLUME_MIN,0,BaseName)) && (SetProperty(SName,SYMBOL_VOLUME_MAX,0,BaseName)) && (SetProperty(SName,SYMBOL_VOLUME_STEP,0,BaseName)) && (SetProperty(SName,SYMBOL_VOLUME_LIMIT,0,BaseName)) && (SetProperty(SName,SYMBOL_MARGIN_INITIAL,0,BaseName)) && (SetProperty(SName,SYMBOL_MARGIN_MAINTENANCE,0,BaseName)) && (SetProperty(SName,SYMBOL_MARGIN_HEDGED,0,BaseName)) && (SetProperty(SName,SYMBOL_SWAP_LONG,0,BaseName)) && (SetProperty(SName,SYMBOL_SWAP_SHORT,0,BaseName))) Print("Símbolo ",SName," criado com sucesso"); else Print("Erro ao definir as propriedades do símbolo. Código de erro: ",GetLastError()); } if(SymbolSelect(SName,true)) Print("Símbolo ",SName," selecionado na Observação do Mercado"); else Print("Erro ao selecionar o símbolo na Observação do Mercado. Código de erro: ",GetLastError()); } } //função para definir as propriedades do símbolo bool SetProperty(string SymName,ENUM_SYMBOL_INFO_STRING SProp,string PropValue,string BaseSymName) { ResetLastError(); if(BaseSymName=="") { if(CustomSymbolSetString(SymName,SProp,PropValue)) return true; else Print("Erro ao definir a propriedade do símbolo: ",SProp," .Código de erro: ",GetLastError()); } else { string SValue=SymbolInfoString(BaseSymName,SProp); if(CustomSymbolSetString(SymName,SProp,SValue)) return true; else Print("Erro ao definir a propriedade do símbolo: ",SProp," .Código de erro: ",GetLastError()); } return false; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool SetProperty(string SymName,ENUM_SYMBOL_INFO_INTEGER IProp,long PropValue,string BaseSymName) { ResetLastError(); if(BaseSymName=="") { if(CustomSymbolSetInteger(SymName,IProp,PropValue)) return true; else Print("Erro ao definir a propriedade do símbolo: ",IProp," .Código de erro: ",GetLastError()); } else { long IValue=SymbolInfoInteger(BaseSymName,IProp); if(CustomSymbolSetInteger(SymName,IProp,IValue)) return true; else Print("Erro ao definir a propriedade do símbolo: ",IProp," .Código de erro: ",GetLastError()); } return false; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ bool SetProperty(string SymName,ENUM_SYMBOL_INFO_DOUBLE DProp,double PropValue,string BaseSymName) { ResetLastError(); if(BaseSymName=="") { if(CustomSymbolSetDouble(SymName,DProp,PropValue)) return true; else Print("Erro ao definir a propriedade do símbolo: ",DProp," .Código de erro: ",GetLastError()); } else { double DValue=SymbolInfoDouble(BaseSymName,DProp); if(CustomSymbolSetDouble(SymName,DProp,DValue)) return true; else Print("Erro ao definir a propriedade do símbolo: ",DProp," .Código de erro: ",GetLastError()); } return false; } //+------------------------------------------------------------------+
Considere o código do script em mais detalhes. Primeiro, é feita uma tentativa de criar um símbolo usando a função CustomSymbolCreate:
if(!CustomSymbolCreate(SName,"\\Forex")) { if(SymbolInfoInteger(SName,SYMBOL_CUSTOM)) Print("O símbolo ",SName," já existe!"); else Print("Erro ao criar símbolo. Código de erro: ",GetLastError()); }
O símbolo é criado na pasta Custom/Forex. Se você criar sua própria subpasta (grupo personalizado) na pasta Custom, especifique seu nome no segundo parâmetro da função CustomSymbolCreate.
Em seguida, são definidas as propriedades do símbolo criado. Quando não definido o parâmetro BaseName, é executada a definição dos parâmetros do símbolo estabelecidos pelo usuário. Por exemplo, vejas propriedades definidas do par de moedas EURUSD:
//propriedade do tipo String if((SetProperty(SName,SYMBOL_CURRENCY_BASE,CurrencyName,"")) && //unidade monetária base (SetProperty(SName,SYMBOL_CURRENCY_PROFIT,"USD",""))&& //moeda de lucro (SetProperty(SName,SYMBOL_CURRENCY_MARGIN,"UCR",""))&& //moeda da margem ...
Por conveniência, as propriedades são divididas em grupos, primeiro são definidas as propriedades do tipo String, Integer e, finalmente, Double. No caso de configuração bem-sucedida de propriedades, no log é registrada uma mensagem sobre a criação bem-sucedida do símbolo, caso contrário, no log é registrado o código de erro ao definir as propriedades do símbolo.
Se o valor do parâmetro BaseName não estiver vazio, as propriedades do símbolo que está sendo criado são copiadas das propriedades do símbolo base cujo nome é especificado pelo parâmetro BaseName, por exemplo, podem ser os pares de moedas EURUSD, USDCAD, GBPUSD e outros.
A função SetProperty, descrita após o código da função base do script, é responsável por definir as propriedades dos símbolos. Esta função não é usada em outros scripts, portanto, não a coloque numa classe plug-in separada.
Para propriedades do tipo String, Integer e Double, são criadas instâncias separadas da função SetProperty. Para definir as propriedades do símbolo personalizado, são usadas as funções CustomSymbolSetString, CustomSymbolSetInteger, CustomSymbolSetDouble. Para obter as propriedades do símbolo base, são usadas as funções SymbolInfoString, SymbolInfoInteger, SymbolInfoDouble.
Após a configuração bem-sucedida das propriedades, o símbolo personalizado criado é selecionado na Observação do mercado usando a função SymbolSelect:
if(SymbolSelect(SName,true)) Print("Símbolo ",SName," selecionado na Observação do Mercado"); else Print("Erro ao selecionar o símbolo na Observação do Mercado. Código de erro: ",GetLastError());
Para abrir o gráfico do símbolo criado, você precisa carregar no símbolo ticks ou barras. O script para gerar ticks e barras será discutido abaixo.
Agora, considere o processo para excluir um símbolo personalizado. Se você quiser excluir um símbolo personalizado selecionando-o na guia Símbolos, nem sempre será possível conseguir isso:
Fig. 1. Tentando excluir um símbolo quando selecionado na Observação do mercado.
Para excluir um símbolo, é necessário removê-lo da Observação do mercado clicando no símbolo duas vezes na janela Símbolos. O símbolo que você deseja desabilitar não deve ter gráficos abertos e posições, portanto, é necessário fechar todos os gráficos e posições para este símbolo manualmente. Este processo não é muito rápido, especialmente se você tiver muitos gráficos nesse símbolo. Mostraremos como implementar a exclusão de símbolo usando um script pequeno (o script está anexado ao artigo no arquivo DeleteSymbol.mq5):
//+------------------------------------------------------------------+ //| DeleteSymbol.mq5 | //| Aleksey Zinovik | //| | //+------------------------------------------------------------------+ #property copyright "Aleksey Zinovik" #property script_show_inputs #property version "1.00" //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ input string SName="ExampleCurrency"; //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { ResetLastError(); if(SymbolInfoInteger(SName,SYMBOL_CUSTOM))//se o símbolo existir { if(!CustomSymbolDelete(SName))//tentamos remover { if(SymbolInfoInteger(SName,SYMBOL_SELECT))//se estiver selecionado na Observação do mercado { if(SymbolSelect(SName,false))//Tentamos desativar e remover { if(!CustomSymbolDelete(SName)) Print("Erro ao remover o símbolo ",SName," Código de erro: ",GetLastError()); else Print("Símbolo ",SName," removido com sucesso"); } else { //tentamos fechar gráficos com um símbolo int i=0; long CurrChart=ChartFirst(); int i_id=0; long ChartIDArray[]; while(CurrChart!=-1) { //iteramos a lista de gráficos e salvamos os identificadores de gráficos abertos do símbolo SName if(ChartSymbol(CurrChart)==SName) { ArrayResize(ChartIDArray,ArraySize(ChartIDArray)+1); ChartIDArray[i_id]=CurrChart; i_id++; } CurrChart=ChartNext(CurrChart); } //fechamos todos os gráficos do símbolo SName for(i=0;i<i_id;i++) { if(!ChartClose(ChartIDArray[i])) { Print("Erro ao fechar o gráfico do símbolo ",SName,". Código de erro: ",GetLastError()); return; } } //desativamos e removemos o símbolo if(SymbolSelect(SName,false)) { if(!CustomSymbolDelete(SName)) Print("Erro ao remover o símbolo ",SName," Código de erro: ",GetLastError()); else Print("Símbolo ",SName," removido com sucesso"); } else Print("Erro ao desativar o símbolo ",SName," na Observação do mercado. Código de erro: ",GetLastError()); }//end else SymbolSelect } //end if(SymbolSelect(SName,false)) else Print("Erro ao remover o símbolo ",SName," Código de erro: ",GetLastError()); } else Print("Símbolo ",SName," removido com sucesso"); } else Print("O símbolo ",SName," não existe"); } //+------------------------------------------------------------------+
Considere o funcionamento do script:
- primeiro, é verificada a presença de um símbolo com o nome SName,
- se o símbolo for encontrado, é feita uma tentativa para removê-lo usando a função CustomSymbolDelete,
- se o símbolo não puder ser excluído, tente desativá-lo na Observação do mercado usando a função SimbolSelect,
- se não for possível desabilitar o símbolo na Observação do mercado, feche todos os gráficos abertos para este símbolo.
Para fazer isso, passe por todos os gráficos abertos e salve os identificadores dos gráficos abertos no símbolo com o nome SName:
while(CurrChart!=-1) { //iteramos a lista de gráficos e salvamos os identificadores de gráficos abertos do símbolo SName if(ChartSymbol(CurrChart)==SName) { ArrayResize(ChartIDArray,ArraySize(ChartIDArray)+1); ChartIDArray[i_id]=CurrChart; i_id++; } CurrChart=ChartNext(CurrChart); }
Feche todos os gráficos com identificadores armazenados na matriz ChartIDArray:
for(i=0;i<i_id;i++) { if(!ChartClose(ChartIDArray[i])) { Print("Erro ao fechar o gráfico do símbolo ",SName,". Código de erro: ",GetLastError()); return; } }
- depois que todos os gráficos são fechados, tente desabilitar o símbolo novamente na Observação do mercado e exclui-lo, caso contrário, uma mensagem de erro será registrada no log.
Como você pode ver, o script não toma em consideração o fechamento automático de posições no símbolo selecionado. Isso é feito para que a inicialização aleatória do script não afete as operações de negociação do usuário. Se você quiser excluir um símbolo cujas posições estão abertas, feche-as antecipadamente.
Geração de ticks e barras
Após criar o símbolo, você precisa carregar nele o histórico de transações: você pode carregar barras e testar EAs e indicadores usando o modo de geração de ticks incorporado ao Testador de Estratégia, ou carregar ticks e barras e realizar testes com os ticks carregados. O método de carregamento de ticks e barras com base nos dados de preços existentes é mostrado no artigo, daí que ofereceremos scripts para geração automática de ticks e barras de acordo com as leis de distribuição.
Aqui está o código do script para gerar barras (GetCandle.mq5, arquivo do script):
//+------------------------------------------------------------------+ //| GetCandle.mq5 | //| Aleksey Zinovik | //| | //+------------------------------------------------------------------+ #property copyright "Aleksey Zinovik" #property link "" #property version "1.00" #property script_show_inputs #include </Math/Stat/Beta.mqh> #include </Math/Stat/Binomial.mqh> #include </Math/Stat/Cauchy.mqh> #include </Math/Stat/ChiSquare.mqh> #include </Math/Stat/Exponential.mqh> #include </Math/Stat/F.mqh> #include </Math/Stat/Gamma.mqh> #include </Math/Stat/Geometric.mqh> #include </Math/Stat/Hypergeometric.mqh> #include </Math/Stat/Logistic.mqh> #include </Math/Stat/Lognormal.mqh> #include </Math/Stat/NegativeBinomial.mqh> #include </Math/Stat/NoncentralBeta.mqh> #include </Math/Stat/NoncentralChiSquare.mqh> #include </Math/Stat/NoncentralF.mqh> #include </Math/Stat/NoncentralT.mqh> #include </Math/Stat/Normal.mqh> #include </Math/Stat/Poisson.mqh> #include </Math/Stat/T.mqh> #include </Math/Stat/Uniform.mqh> #include </Math/Stat/Weibull.mqh> //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ enum Distribution { Beta, Binomial, Cauchy, ChiSquare, Exponential, F, Gamma, Geometric, Hypergeometric, Logistic, Lognormal, NegativeBinomial, NoncentralBeta, NoncentralChiSquare, NoncentralF, NoncentralT, Normal, Poisson, T, Uniform, Weibull }; //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ /*input params*/ input string SName="ExampleCurrency"; input datetime TBegin=D'2018.01.01 00:00:00'; //Hora de início da geração de barras input datetime TEnd=D'2018.02.01 00:00:00'; //Hora final da geração de barras input int BarForReplace=1000; //Número de barras após o qual são substituídas as barras input double BaseOCHL=1; //Valor base do preço OCHL input double dOCHL=0.001; //Taxa de variação do preço OCHL input ulong BaseRealVol=10000; //Valor base do volume input ulong dRealVol=100; //Taxa de variação do volume real input ulong BaseTickVol=100; //Valor base do volume input ulong dTickVol=10; //Taxa de variação do volume de ticks input ulong BaseSpread=0; //Valor base do spread input ulong dSpread=1; //Taxa de variação do spread input Distribution DistOCHL=Normal; //Tipo de distribuição para os preços OCHL input Distribution DistRealVol = Normal; //Tipo de distribuição para volume real input Distribution DistTickVol = Normal; //Tipo de distribuição para o volume de ticks input Distribution DistSpread = Uniform; //Tipo de distribuição para ob spread input bool DiffCandle=false; //Gerando velas de vários tipos input double DistOCHLParam1=0; //Parâmetro 1 da distribuição para os preços OCHL input double DistOCHLParam2=1; //Parâmetro 2 da distribuição para os preços OCHL input double DistOCHLParam3=0; //Parâmetro 3 da distribuição para os preços OCHL input double DistRealParam1=0; //Parâmetro 1 da distribuição para o volume real input double DistRealParam2=1; //Parâmetro 2 da distribuição para o volume real input double DistRealParam3=0; //Parâmetro 3 da distribuição para o volume real input double DistTickParam1=0; //Parâmetro 1 da distribuição para o volume de ticks input double DistTickParam2=1; //Parâmetro 2 da distribuição para o volume de ticks input double DistTickParam3=0; //Parâmetro 3 da distribuição para o volume de ticks input double DistSpreadParam1=0; //Parâmetro 1 da distribuição para o spread input double DistSpreadParam2=50; //Parâmetro 2 da distribuição para o spread input double DistSpreadParam3=0; //Parâmetro 3 da distribuição para o spread input bool FiveDayOfWeek=true; //true - não gerar ticks nos fins de semana /*----input params----*/ int i_bar=0; //contador, barras de minutos MqlRates MRatesMin[]; //matriz para armazenar barras como barras MqlDateTime StructCTime; //estrutura para trabalhar com o tempo int DistErr=0; //número do erro bool IsErr=false; //erro double DistMass[4]; //matriz para armazenar os valores OCHL gerados int ReplaceBar=0; //número de barras substituídas double BValue[1]; //matriz para copiar o último preço Close //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void OnStart() { int i=0; //contador de loops double MaxVal,MinVal; //valores High e Low int i_max,i_min; //índices dos maiores e menores valores da matriz DistMass datetime TCurrent=TBegin; BValue[0]=BaseOCHL; if(SymbolInfoInteger(SName,SYMBOL_CUSTOM))//se o símbolo existir { while(TCurrent<=TEnd) { if(FiveDayOfWeek) { TimeToStruct(TCurrent,StructCTime); if(!((StructCTime.day_of_week!=0) && (StructCTime.day_of_week!=6))) { if(StructCTime.day_of_week==0) TCurrent=TCurrent+86400-(StructCTime.hour*3600+StructCTime.min*60+StructCTime.sec); else TCurrent=TCurrent+2*86400-(StructCTime.hour*3600+StructCTime.min*60+StructCTime.sec); if(TCurrent>=TEnd) { if(ReplaceBar==0) Print("Não há trades no intervalo especificado"); return; } } } ArrayResize(MRatesMin,ArraySize(MRatesMin)+1); MRatesMin[i_bar].open=0; MRatesMin[i_bar].close=0; MRatesMin[i_bar].high=0; MRatesMin[i_bar].low=0; //preenchemos Open if(i_bar>0) MRatesMin[i_bar].open=MRatesMin[i_bar-1].close; else { if((CopyClose(SName,PERIOD_M1,TCurrent-60,1,BValue)==-1)) MRatesMin[i_bar].open=NormalizeDouble(BaseOCHL+dOCHL*GetDist(DistOCHL,DistOCHLParam1,DistOCHLParam2,DistOCHLParam3),_Digits); else MRatesMin[i_bar].open=BValue[0]; } //geramos os preços High, Low MaxVal=2.2250738585072014e-308; MinVal=1.7976931348623158e+308; i_max=0; i_min=0; for(i=0;i<3;i++) { DistMass[i]=NormalizeDouble(BaseOCHL+dOCHL*GetDist(DistOCHL,DistOCHLParam1,DistOCHLParam2,DistOCHLParam3),_Digits); if(IsErrCheck(DistErr)) return; if(MaxVal<DistMass[i]) { MaxVal=DistMass[i]; i_max=i; } if(MinVal>DistMass[i]) { MinVal=DistMass[i]; i_min=i; } } if(MaxVal<MRatesMin[i_bar].open) MRatesMin[i_bar].high=MRatesMin[i_bar].open; else MRatesMin[i_bar].high=MaxVal; if(MinVal>MRatesMin[i_bar].open) MRatesMin[i_bar].low=MRatesMin[i_bar].open; else MRatesMin[i_bar].low=MinVal; //preenchemos Close for(i=0;i<3;i++) if((i!=i_max) && (i!=i_min)) { MRatesMin[i_bar].close=DistMass[i]; break; } //geramos o volume, spread MRatesMin[i_bar].real_volume=(long)(BaseRealVol+dRealVol*GetDist(DistRealVol,DistRealParam1,DistRealParam2,DistRealParam3)); if(IsErrCheck(DistErr)) return; MRatesMin[i_bar].tick_volume=(long)(BaseTickVol+dTickVol*GetDist(DistTickVol,DistTickParam1,DistTickParam2,DistTickParam3)); if(IsErrCheck(DistErr)) return; MRatesMin[i_bar].spread=(int)(BaseSpread+dSpread*GetDist(DistSpread,DistSpreadParam1,DistSpreadParam2,DistSpreadParam3)); if(IsErrCheck(DistErr)) return; //registramos o tempo MRatesMin[i_bar].time=TCurrent; if(DiffCandle) { i=MathRand()%5; switch(i) { case 0://Doji { MRatesMin[i_bar].close=MRatesMin[i_bar].open; break; } case 1://Hammer { if(MRatesMin[i_bar].open>MRatesMin[i_bar].close) MRatesMin[i_bar].high=MRatesMin[i_bar].open; else { if(MRatesMin[i_bar].open<MRatesMin[i_bar].close) MRatesMin[i_bar].high=MRatesMin[i_bar].close; } break; } case 2://Star { if(MRatesMin[i_bar].open>MRatesMin[i_bar].close) MRatesMin[i_bar].low=MRatesMin[i_bar].close; else { if(MRatesMin[i_bar].open<MRatesMin[i_bar].close) MRatesMin[i_bar].low=MRatesMin[i_bar].open; } break; } case 3://Maribozu { if(MRatesMin[i_bar].open>MRatesMin[i_bar].close) { MRatesMin[i_bar].high=MRatesMin[i_bar].open; MRatesMin[i_bar].low=MRatesMin[i_bar].close; } else { if(MRatesMin[i_bar].open<MRatesMin[i_bar].close) { MRatesMin[i_bar].high=MRatesMin[i_bar].close; MRatesMin[i_bar].low=MRatesMin[i_bar].open; } } break; } default: break; } } //verificamos se é hora de substituir as barras if(i_bar>=BarForReplace-1) { ReplaceHistory(MRatesMin[0].time,MRatesMin[i_bar].time); TCurrent=TCurrent+60; BValue[0]=MRatesMin[i_bar].close; i_bar=0; ArrayFree(MRatesMin); } else { i_bar++; TCurrent=TCurrent+60; } } if(i_bar>0) { i_bar--; ReplaceHistory(MRatesMin[0].time,MRatesMin[i_bar].time); } } else Print("O símbolo ",SName," não existe"); } //+------------------------------------------------------------------+ void ReplaceHistory(datetime DBegin,datetime DEnd) { ReplaceBar=CustomRatesReplace(SName,DBegin,DEnd,MRatesMin); if(ReplaceBar<0) Print("Erros ao substituir barras. Código de erro: ",GetLastError()); else PrintFormat("Histórico de preços para o período: %s - %s formado com sucesso. %i barras criadas, %i barras adicionadas (substituídas)",TimeToString(DBegin),TimeToString(DEnd),i_bar+1,ReplaceBar); } //+------------------------------------------------------------------+ double GetDist(Distribution d,double p1,double p2,double p3) { double res=0; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ switch(d) { /*Distribuição beta*/ //p1,p2 - o primeiro e o segundo parâmetro case Beta: {res=MathRandomBeta(p1,p2,DistErr); break;} /*Distribuição binomial*/ //p1 - número de testes, p2 - probabilidade de sucesso para cada teste case Binomial: {res=MathRandomBinomial(p1,p2,DistErr); break;}; /*Distribuição de Cauchy*/ //p1 - coeficiente de deslocamento, p2 - fator de escala case Cauchy: {res=MathRandomCauchy(p1,p2,DistErr); break;}; /*Distribuição de qui ao quadrado*/ //p1 - número de graus de liberdade case ChiSquare: {res=MathRandomChiSquare(p1,DistErr); break;}; /*Distribuição exponencial*/ //p1 - parâmetro (lambda) da distribuição case Exponential: {res=MathRandomExponential(p1,DistErr); break;}; /*Distribuição de Fisher*/ //p1, p2 - número de graus de liberdade case F: {res=MathRandomF(p1,p2,DistErr); break;}; /*Distribuição gama*/ //p1 - parâmetro de distribuição (inteiro), p2 - fator de escala case Gamma: {res=MathRandomGamma(p1,p2,DistErr); break;}; /*Distribuição geométrica*/ //p1 - taxa de sucesso (ocorrência do evento no teste) case Geometric: {res=MathRandomGeometric(p1,DistErr); break;}; /*Distribuição hipergeométrica*/ //p1 - número total de objetos, p2 - número de objetos com a característica desejada, p3 - número de objetos na amostra case Hypergeometric: {res=MathRandomHypergeometric(p1,p2,p3,DistErr); break;}; /*Distribuição logística*/ //p1 - valor esperado, p2 - fator de escala case Logistic: {res=MathRandomLogistic(p1,p2,DistErr); break;}; /*Distribuição logarítmico-normal*/ //p1 - logaritmo do valor esperado, p2 - logaritmo de desvio padrão case Lognormal: {res=MathRandomLognormal(p1,p2,DistErr); break;}; /*Distribuição binomial negativa*/ //p1 - número de testes bem sucedidos, p2 - probabilidade de sucesso case NegativeBinomial: {res=MathRandomNegativeBinomial(p1,p2,DistErr); break;}; /*Distribuição beta não-central*/ //p1,p2 - primeiro e o segundo parâmetro, p3 - parâmetro de não centralidade case NoncentralBeta: {res=MathRandomNoncentralBeta(p1,p2,p3,DistErr); break;}; /*Qui-quadrado não-central*/ //p1 - número de graus de liberdade, p2 - parâmetro de não centralidade case NoncentralChiSquare: {res=MathRandomNoncentralChiSquare(p1,p2,DistErr); break;}; /*Distribuição F de Fisher-Snedecor não-central*/ //p1, p2 - número de graus de liberdade, p3 - parâmetro de não centralidade case NoncentralF: {res=MathRandomNoncentralF(p1,p2,p3,DistErr); break;}; /*Distribuição t de Student não-central*/ //p1 - número de graus de liberdade, p2 - parâmetro de não centralidade case NoncentralT: {res=MathRandomNoncentralT(p1,p2,DistErr); break;}; /*Distribuição normal*/ //p1 - valor esperado, p2 - desvio padrão case Normal: {res=MathRandomNormal(p1,p2,DistErr); break;}; /*Distribuição de Poisson*/ //p1 - valor esperado case Poisson: {res=MathRandomPoisson(p1,DistErr); break;}; /*Distribuição t de Student*/ //p1 - número de graus de liberdade case T: {res=MathRandomT(p1,DistErr); break;}; /*Distribuição uniforme*/ //p1 - limite inferior do intervalo, p2 - limite superior do intervalo case Uniform: {res=MathRandomUniform(p1,p2,DistErr); break;}; /*Distribuição de Weibull*/ //p1 - parâmetro do formulário, p2 - parâmetro de escala case Weibull: {res=MathRandomWeibull(p1,p2,DistErr); break;}; } if(DistErr!=0) return -1; else return res; } //+------------------------------------------------------------------+ bool IsErrCheck(int Err) { //verificamos se há erro ao gerar números pseudo-aleatórios switch(DistErr) { case(1): { MessageBox("Os parâmetros de distribuição especificados não são números reais","Erro nos parâmetros de entrada",MB_ICONWARNING); return true; } case(2): { MessageBox("Os parâmetros de distribuição especificados não são válidos","Erro nos parâmetros de entrada",MB_ICONWARNING); return true; } case(4): { MessageBox("Divisão de erro por zero","Erro nos parâmetros de entrada",MB_ICONWARNING); return true; } } return false; } //+------------------------------------------------------------------+
Consideremos o funcionamento do script. O script usa arquivos da biblioteca padrão - localizada no diretório Estatísticas - que implementam várias distribuições estatísticas. Para selecionar a lei (tipo) de distribuição de acordo com a qual serão gerados números pseudo-aleatórios para formar os parâmetros de cada barra, é criada a enumeração Distribution.
Usando números pseudo-aleatórios, são gerados os preços Close, High, Low, volume real, volume de tick, spread usando a seguinte fórmula:
(1)
Onde P(i) - valor do parâmetro, Base - valor base do parâmetro, step - fator de incremento (passo) da variável pseudo-aleatória, DistValue(i) - variável pseudo-aleatória gerada distribuída de acordo com a lei definida. O parâmetros Base e step são definidos pelo usuário. Como exemplo, veja o código para formar o valor do preço de abertura:
if(i_bar>0) MRatesMin[i_bar].open=MRatesMin[i_bar-1].close; else { if((CopyClose(SName,PERIOD_M1,TCurrent-60,1,BValue)==-1)) MRatesMin[i_bar].open=NormalizeDouble(BaseOCHL+dOCHL*GetDist(DistOCHL,DistOCHLParam1,DistOCHLParam2,DistOCHLParam3),_Digits); else MRatesMin[i_bar].open=BValue[0]; }
Se, antes de iniciar o script, faltarem barras ou por algum motivo a função CopyClose não conseguir copiar a última barra do histórico de preços, o preço de abertura da primeira barra será formado usando a fórmula descrita acima:
- BaseOCHL - valor de base para os preços OCHL,
- dOCHL - taxa de variação do preço OCHL.
Para as barras subsequentes, o preço Open é igual ao preço Close anterior, ou seja, a nova barra é aberta no final da anterior.
A função GetDist é responsável pela a geração do valor da variável pseudo-aleatória.
Para lidar com erros que ocorrem ao gerar a variável pseudo-aleatória, é criada a função IsErrCheck. Na entrada da função é recebido um código de erro, que é determinado durante a execução da função GetDist. Se ocorrer um erro, é interrompida a execução do script e é registrada uma mensagem de erro. O código das funções Getdist e Iserrcheck é dado no final do script.
O script gera ticks de minutos e possui os seguintes recursos:
1) Os ticks são gerados apenas no intervalo de tempo especificado, é possível não gerar ticks nos fins de semana (parâmetro de entrada FiveDayOfWeek=true)
A desativação da geração de ticks nos fins de semana é implementada no seguinte código:
if(FiveDayOfWeek) { TimeToStruct(TCurrent,StructCTime); if(!((StructCTime.day_of_week!=0) && (StructCTime.day_of_week!=6))) { if(StructCTime.day_of_week==0) TCurrent=TCurrent+86400-(StructCTime.hour*3600+StructCTime.min*60+StructCTime.sec); else TCurrent=TCurrent+2*86400-(StructCTime.hour*3600+StructCTime.min*60+StructCTime.sec); if(TCurrent>=TEnd) { if(ReplaceBar==0) Print("Não há trades no intervalo especificado"); return; } } }
Se a hora atual coincidir com um dia de folga, ele muda para o próximo dia de negociação.
2) O script permite substituir barras por partes, liberando memória após substituir as barras geradas
O número de barras que, após serem substituídas, faz com que seja redefinida a matriz de ticks gerados é definido como variável BarForReplace. A substituição de barras é implementada no seguinte código:
if(i_bar>=BarForReplace) { ReplaceHistory(MRatesMin[0].time,MRatesMin[i_bar-1].time); i_bar=0; ArrayFree(MRatesMin); }
Para substituir barras, é criada a função ReplaceHistory cujo código é dado no final do script. A substituição de barras é realizada pela função CustomRatesReplace. Após a substituição de barras, é zerado o contador de barras e é liberado o buffer da matriz dinâmica MRatesMin que armazena as barras criadas.
3) O script permite gerar velas de vários tipos
A geração de vários tipos de velas é implementada da seguinte forma (parâmetro DiffCande = true):
if(DiffCandle) { i=MathRand()%5; switch(i) { case 0://Doji { MRatesMin[i_bar].close=MRatesMin[i_bar].open; break; } case 1://Hammer { if(MRatesMin[i_bar].open>MRatesMin[i_bar].close) MRatesMin[i_bar].high=MRatesMin[i_bar].open; else { if(MRatesMin[i_bar].open<MRatesMin[i_bar].close) MRatesMin[i_bar].high=MRatesMin[i_bar].close; } break; } case 2://Star { if(MRatesMin[i_bar].open>MRatesMin[i_bar].close) MRatesMin[i_bar].low=MRatesMin[i_bar].close; else { if(MRatesMin[i_bar].open<MRatesMin[i_bar].close) MRatesMin[i_bar].low=MRatesMin[i_bar].open; } break; } case 3://Maribozu { if(MRatesMin[i_bar].open>MRatesMin[i_bar].close) { MRatesMin[i_bar].high=MRatesMin[i_bar].open; MRatesMin[i_bar].low=MRatesMin[i_bar].close; } else { if(MRatesMin[i_bar].open<MRatesMin[i_bar].close) { MRatesMin[i_bar].high=MRatesMin[i_bar].close; MRatesMin[i_bar].low=MRatesMin[i_bar].open; } } break; } default: break; } }
Assim, o script permite gerar as habituais velas longas ou curtas "doji", "hammer", "star" e "mariboza" com a mesma probabilidade.
Vamos mostrar o trabalho do script, para isso, inicie o script com os seguintes parâmetros de entrada:
Fig. 1. Parâmetros de entrada do script
Como resultado, aparece um novo símbolo ExampleCurrency. Durante a operação do script, são geradas 33 121 barras de minutos. A Figura 2 mostra um fragmento do gráfico de minutos do símbolo ExampleCurrency.
Fig. 2. Gráfico de minutos do símbolo ExampleCurrency
Às vezes, as barras de minutos podem não ser suficientes para testar um EA ou indicador, e o teste é realizado em ticks reais ou modelados.
Considere um script que simula ticks e formula barras de minutos com base em ticks modelados. O código completo do script é fornecido no arquivo GetTick.mq5 anexado ao artigo. Aqui está o código da função OnStart():
void OnStart() { if(SymbolInfoInteger(SName,SYMBOL_CUSTOM))//se o símbolo existir { MqlDateTime StructCTime; long TBeginMSec=(long)TBegin*1000; //tempo de início da geração de ticks em ms long TEndMSec=(long)TEnd*1000; //hora de término da geração de ticks em ms int ValMsec=0; //variável para gerar um deslocamento de tempo aleatório em ms int SumSec=0; //contador de segundos int SumMSec=0; //contador de milissegundos int PrevTickCount=0; //variável para armazenar os ticks anteriores num minuto datetime TCurrent=TBegin; bool NewMinute=false; //copiamos o valor do preço no qual começará a geração de ticks if(CopyClose(SName,PERIOD_M1,TCurrent-60,1,BValue)==-1) BValue[0]=Base; //preenchemos a estrutura LastTick LastTick.ask=BValue[0]; LastTick.bid=BValue[0]; LastTick.last=BValue[0]; LastTick.volume=baseVol; while(TBeginMSec<=TEndMSec) { if(FiveDayOfWeek) { TimeToStruct(TCurrent,StructCTime); if((StructCTime.day_of_week==0) || (StructCTime.day_of_week==6)) { if(StructCTime.day_of_week==0) { TCurrent=TCurrent+86400; TBeginMSec=TBeginMSec+86400000; } else { TCurrent=TCurrent+2*86400; TBeginMSec=TBeginMSec+2*86400000; } if(TBeginMSec>=TEndMSec) break; } } GetTick(TCurrent,TBeginMSec); if(IsErrCheck(DistErr)) return; i_tick++; if(RandomTickTime) { //geramos o deslocamento aleatório de tempo ValMsec=(int)((MathRand()%30000)/(MaxTickInMinute*0.25)+1); SumSec=SumSec+ValMsec; SumMSec=SumMSec+ValMsec; if(i_tick-PrevTickCount>=MaxTickInMinute) { TimeToStruct(TCurrent,StructCTime); StructCTime.sec=0; TCurrent=StructToTime(StructCTime)+60; TBeginMSec=TBeginMSec+60000-SumSec+ValMsec; SumSec=0; SumMSec=0; NewMinute=true; } else { if(SumSec>=60000) { //redefinimos o contador de ticks por minuto SumSec=SumSec-60000*(SumSec/60000); NewMinute=true; } //formação de um novo tempo de tick TBeginMSec=TBeginMSec+ValMsec; if(SumMSec>=1000) { TCurrent=TCurrent+SumMSec/1000; SumMSec=SumMSec-1000*(SumMSec/1000); } } } else { TBeginMSec=TBeginMSec+60000/MaxTickInMinute; SumSec=SumSec+60000/MaxTickInMinute; SumMSec=SumMSec+60000/MaxTickInMinute; if(SumMSec>=1000) { TCurrent=TCurrent+SumMSec/1000; SumMSec=SumMSec-1000*(SumMSec/1000); } if(SumSec>=60000) { SumSec=SumSec-60000*(SumSec/60000); NewMinute=true; } } if(NewMinute) { //adicionamos a nova barra à matriz ArrayResize(MRatesMin,ArraySize(MRatesMin)+1); if(ArraySize(MRatesMin)==1)//se o primeiro minuto { MRatesMin[i_bar].open=NormalizeDouble(LastTick.bid,_Digits); MRatesMin[i_bar].tick_volume=(long)i_tick; } else { MRatesMin[i_bar].open=NormalizeDouble(MTick[PrevTickCount-1].bid,_Digits); MRatesMin[i_bar].tick_volume=(long)i_tick-PrevTickCount; } MRatesMin[i_bar].close=NormalizeDouble(MTick[i_tick-1].bid,_Digits); if(ValHigh>MRatesMin[i_bar].open) MRatesMin[i_bar].high=NormalizeDouble(ValHigh,_Digits); else MRatesMin[i_bar].high=MRatesMin[i_bar].open; if(ValLow<MRatesMin[i_bar].open) MRatesMin[i_bar].low=NormalizeDouble(ValLow,_Digits); else MRatesMin[i_bar].low=MRatesMin[i_bar].open; MRatesMin[i_bar].real_volume=(long)MTick[i_tick-1].volume; MRatesMin[i_bar].spread=(int)MathRound(MathAbs(MTick[i_tick-1].bid-MTick[i_tick-1].ask)/_Point); TimeToStruct(MTick[i_tick-1].time,StructCTime); StructCTime.sec=0; MRatesMin[i_bar].time=StructToTime(StructCTime); i_bar++; PrevTickCount=i_tick; ValHigh=2.2250738585072014e-308; ValLow=1.7976931348623158e+308; NewMinute=false; if(i_bar>=BarForReplace) { ReplaceHistory(MRatesMin[0].time,MRatesMin[i_bar-1].time); LastTick.bid=MTick[i_tick-1].bid; LastTick.ask=MTick[i_tick-1].ask; LastTick.last=MTick[i_tick-1].last; LastTick.volume=MTick[i_tick-1].volume; i_tick=0; i_bar=0; PrevTickCount=0; ArrayFree(MRatesMin); ArrayFree(MTick); } } }//end while if(i_bar>0) ReplaceHistory(MRatesMin[0].time,MRatesMin[i_bar-1].time); } else Print("O símbolo ",SName," não existe"); }
No início da função OnStart(), são inicializadas as variáveis. Geração de ticks e barras é executada no ciclo principal do script:
while(TBeginMSec<=TEndMSec)
{
...
}
No início do ciclo, caso o parâmetro FiveDayOfWeek = true, é desativada a geração de ticks nos fins de semana, adicionalmente, a hora do tick é alterado em 1 dia (se o horário do tick corresponder ao domingo) ou 2 dias (se o horário do tick corresponder ao sábado):
if(FiveDayOfWeek)
{
...
}
Em seguida, um tick é gerado usando a função GetTick:
GetTick(TCurrent,TBeginMSec); if(IsErrCheck(DistErr)) return; i_tick++;
Se ocorrer um erro durante a geração do tick (o valor da função IsErrCheck = true), o script será finalizado. A função IsErrCheck é descrita acima no código do script GetCandle.
Considere a função GetTick:
void GetTick(datetime TDate,long TLong) { ArrayResize(MTick,ArraySize(MTick)+1); //preenchemos o novo tempo MTick[i_tick].time=TDate; MTick[i_tick].time_msc=TLong; //preenchemos o tick atual com o valor do anterior if(ArraySize(MTick)>1) { MTick[i_tick].ask=MTick[i_tick-1].ask; MTick[i_tick].bid=MTick[i_tick-1].bid; MTick[i_tick].volume=MTick[i_tick-1].volume; MTick[i_tick].last=MTick[i_tick-1].last; } else { MTick[i_tick].ask=LastTick.ask; MTick[i_tick].bid=LastTick.bid; MTick[i_tick].last=LastTick.last; MTick[i_tick].volume=LastTick.volume; } //preenchemos o tick atual if(RandomTickValue) { double RBid=MathRandomUniform(0,1,DistErr); double RAsk=MathRandomUniform(0,1,DistErr); double RVolume=MathRandomUniform(0,1,DistErr); if(RBid>=0.5) { if(i_tick>0) MTick[i_tick].bid=Base+GetDist(DistOCHL,DistOCHLParam1,DistOCHLParam2,DistOCHLParam3)*dStep; MTick[i_tick].last=MTick[i_tick].bid; MTick[i_tick].flags=10; if(RAsk>=0.5) { MTick[i_tick].ask=Base+GetDist(DistOCHL,DistOCHLParam1,DistOCHLParam2,DistOCHLParam3)*dStep; MTick[i_tick].flags=MTick[i_tick].flags+4; } if(RVolume>=0.5) { MTick[i_tick].volume=(ulong)(baseVol+GetDist(DistVolume,DistVolumeParam1,DistVolumeParam2,DistVolumeParam3)*dStepVol); MTick[i_tick].flags=MTick[i_tick].flags+16; } } else { if(RAsk>=0.5) { MTick[i_tick].ask=Base+GetDist(DistOCHL,DistOCHLParam1,DistOCHLParam2,DistOCHLParam3)*dStep; MTick[i_tick].flags=4; if(RVolume>=0.5) { MTick[i_tick].volume=(ulong)(baseVol+GetDist(DistVolume,DistVolumeParam1,DistVolumeParam2,DistVolumeParam3)*dStepVol); MTick[i_tick].flags=MTick[i_tick].flags+16; } } } }//end if(RandomTickValue) //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ else { MTick[i_tick].bid=Base+GetDist(DistOCHL,DistOCHLParam1,DistOCHLParam2,DistOCHLParam3)*dStep; MTick[i_tick].ask=Base+GetDist(DistOCHL,DistOCHLParam1,DistOCHLParam2,DistOCHLParam3)*dStep; MTick[i_tick].last=MTick[i_tick].bid; MTick[i_tick].volume=(ulong)(baseVol+GetDist(DistVolume,DistVolumeParam1,DistVolumeParam2,DistVolumeParam3)*dStepVol); MTick[i_tick].flags=30; }//end if(RandomTickValue) //armazenamos os valores mais altos e mais baixos para a formação de barras de minutos if(MTick[i_tick].bid>ValHigh) ValHigh=MTick[i_tick].bid; if(MTick[i_tick].bid<ValLow) ValLow=MTick[i_tick].bid; }//end
A entrada da função recebe o tempo do tick no formato datetime e em milissegundos. Primeiro, o tick atual é preenchido com os valores do anterior, depois, os valores do tick atual são alterados da seguinte forma:
1) Se o valor do parâmetro RandomTickValue = true, cada um dos parâmetros Ask, Bid e Volume será alterado com uma probabilidade de 0.5. Para isso, são geradas 3 variáveis aleatórias uniformemente distribuídas :
double RBid=MathRandomUniform(0,1,DistErr); double RAsk=MathRandomUniform(0,1,DistErr); double RVolume=MathRandomUniform(0,1,DistErr);
Se RBid>0.5, RAsk>0.5 - os preços Bid e/ou Ask são alterados de acordo com a fórmula (1), descrita acima para o script GetCandle. O volume é alterado de acordo com a fórmula (1), caso mude o preço Ask ou Bid e o parâmetro RVolume > 0.5. O valor do preço Bid é atribuído ao preço Last quando ele muda.
2) Se o valor do parâmetro RandomTickValue = false, os valores dos parâmetros Ask, Bid e Volume são calculados de acordo com a fórmula (1).
O valor do sinalizador dos ticks (flags) é definido da seguinte maneira:
- alteração no preço Bid - flags=flags+2
- alteração no preço Ask - flags=flags+4
- alteração no preço Last - flags=flags+8
- alteração no preço Volume - flags=flags+16
Alterados os preços Ask, Bid ou Volume, nas variáveis ValHigh e ValLow são armazenados os valores do preço Bid máximo e mínimo. Os valores das variáveis ValHigh e ValLow são utilizados para formar os preços High e Low da barra de minutos.
Continuemos estudando o código da função OnStart().
Após gerar o tick atual, é realizada a formação do tempo do aparecimento do tick novo:
1) Se o parâmetro RandomTickTime = true, o novo tempo do tick é formado da seguinte maneira:
ValMsec=(int)((MathRand()%30000)/(MaxTickInMinute*0.25)+1);
Em seguida, é verificada a ocorrência de uma nova barra de minutos e é ajustada a hora atual para o número de segundos gerado. O número de ticks que podem ser formados dentro de um minuto é limitado pela variável MaxTickInMinute. Se o número de ticks gerados exceder o valor da variável MaxTickInMinute, são redefinidos os contadores de segundos (Sumsec) e de milissegundos (SumMSec) e é formada uma nova barra de minutos (NewMinute = true).
2) Se o parâmetro RandomTickTime = false, em cada barra de minutos é gerado o mesmo número de ticks, definido na variável MaxTickInMinute.
A formação da barra de minutos baseada nos ticks gerados ocorre da seguinte maneira:
- matriz de barras de minuto aumenta 1
ArrayResize(MRatesMin,ArraySize(MRatesMin)+1);
- são formados valor do preço de abertura da barra atual e o volume de ticks:
if(ArraySize(MRatesMin)==1)//se o primeiro minuto { MRatesMin[i_bar].open=NormalizeDouble(LastTick.bid,_Digits); MRatesMin[i_bar].tick_volume=(long)i_tick; } else { MRatesMin[i_bar].open=NormalizeDouble(MTick[PrevTickCount-1].bid,_Digits); MRatesMin[i_bar].tick_volume=(long)i_tick-PrevTickCount; }
Ao formar a primeira barra de minutos, ao preço de abertura é atribuído o preço Bid do último tick da substituição de tick anterior (ticks e barras são substituídos usando a função ReplaceHistory) ou o valor base do preço Bid (parâmetro Base), se a substituição de ticks e barras não tiver sido realizada anteriormente. Ao formar as seguintes barras de minutos, o preço de abertura é atribuído ao valor normalizado do preço Bid do último tick (preço de fechamento) do minuto anterior. A normalização é entendida como arredondamento conforme ao preço do símbolo do gráfico atual no qual o script está sendo executado.
- preço Close formado - preço Bid normalizado do último tick:
MRatesMin[i_bar].close=NormalizeDouble(MTick[i_tick-1].bid,_Digits);
- valores dos preços High e Low formados. Isso leva em conta que o valor do preço de abertura pode ser maior que o maior (Valhigh) ou menor que o menor (Vallow) preço Bid dos ticks gerados:
if(ValHigh>MRatesMin[i_bar].open) MRatesMin[i_bar].high=NormalizeDouble(ValHigh,_Digits); else MRatesMin[i_bar].high=MRatesMin[i_bar].open; if(ValLow<MRatesMin[i_bar].open) MRatesMin[i_bar].low=NormalizeDouble(ValLow,_Digits); else MRatesMin[i_bar].low=MRatesMin[i_bar].open;
- são formados os valores de volume e spread:
MRatesMin[i_bar].real_volume=(long)MTick[i_tick-1].volume; MRatesMin[i_bar].spread=(int)MathRound(MathAbs(MTick[i_tick-1].bid-MTick[i_tick-1].ask)/_Point);
O volume do último tick é atribuído ao volume da barra atual, o spread é calculado como a diferença entre os preços Bid e Ask do último tick.
- valor do tempo de criação do último tick com os segundos zerados é atribuído ao tempo de abertura da barra:
TimeToStruct(MTick[i_tick-1].time,StructCTime); StructCTime.sec=0; MRatesMin[i_bar].time=StructToTime(StructCTime);
Neste ponto, termina o processo de formação do parâmetro da barra de minutos atual, é aumentado o contador de barras de minutos (variável i_bar), às variáveis ValHigh e ValLow são atribuídos os valores mínimo e máximo do tipo de dado double, é redefinido o sinalizador da barra de minutos(NewMinute). Além disso, verifica-se se é hora de substituir o número formado de barras de minutos e ticks. O número de barras que desencadeia sua substituição é definido na variável BarForReplace.
Após sair do ciclo principal, são substituídas as barras restantes, se houver:
if(i_bar>0) ReplaceHistory(MRatesMin[0].time,MRatesMin[i_bar-1].time);
Na função ReplaceHistory, é implementada a substituição de ticks e de barras. A função CustomTicksReplace substitui os ticks, enquanto a função CustomRatesReplace - as barras.
Assim, o script GetTick descrito acima permite gerar ticks como definido, formar barras de minutos e carregar os dados obtidos no histórico de preços do símbolo personalizados.
Modelando a tendência
Os scripts GetCandle e GetTick considerados na seção anterior permitem gerar um histórico de preços sem fortes flutuações (fase de correção). Para formação de situações de mercado mais complexas e testes de EAs e indicadores, é necessário modelar o movimento do preço na tendência.
Para fazer isso, foram escritos os scripts GetCandleTrend (anexado ao artigo no arquivo GetCandleTrend.mq5) e GetTickTrend (anexado ao artigo no arquivo GetTickTrend.mq5) para modelar tendências altistas ou baixistas de acordo com um determinado padrão de movimento de preços. O script GetCandleTrend é projetado para gerar barras de minuto crescentes ou decrescentes, o script Getticktrend gera ticks crescentes ou decrescentes, e, similarmente ao script GetCandleTrend, forma barras de minutos.
Considere o funcionamento do script GetCandleTrend. A formação de barras de minutos é semelhante ao script Getcandle, portanto, considere apenas o modo como se forma a tendência. Os dados de entrada do script contêm os seguintes parâmetros de tendência:
input TrendModel TModel = Linear; //Modelo da tendência input TrendType TType = Increasing; //Tipo de tendência (crescente, decrescente, aleatória) input double RandomTrendCoeff=0.5; //Coeficiente de tendência (se RandomTrendCoeff<0.5 a tendência decrescente é mais forte, se RandomTrendCoeff>0.5 - a crescente) input double Coeff1=0.1; //Coeficiente k1 do modelo de tendência input double Coeff2=0.1; //Coeficiente k2 do modelo de tendência input double Coeff3=0.1; //Coeficiente k3 do modelo de tendência input int CountCandle=60; //Intervalo de mudança aleatória da direção da tendência (em barras)
O usuário é solicitado a selecionar o modelo de tendência especificado na enumeração TrendModel:
enum TrendModel
{
Linear,
Hyperbolic,
Exp,
Power,
SecondOrderPolynomial,
LinearAndPeriodic,
LinearAndStochastic
};
e o tipo de tendência definido na enumeração TrendType:
enum TrendType
{
Increasing,
Decreasing,
Random
};
A tendência é formada de acordo com os seguintes modelos: linear, hiperbólico, exponencial, de potência, parabólica, linear-periódica e linear-estocástica. As fórmulas para a formação da tendência são mostradas na tabela 1:
Modelo da tendência | Fórmula |
---|---|
Linear | |
Hiperbólico | |
Exponencial | |
Potencial | |
Parabólico | |
Periódico linear | |
Estocástico linear |
T(i) - valor atual da tendência, k1, k2, k3 - coeficientes que influenciam a taxa de crescimento (baixista) da tendência, N(0,1) - variável aleatória, distribuída de acordo com a lei normal com valor esperado zero e uma variância unitária.
Como o tipo de tendência, propõe-se escolher ascendente, descendente ou aleatória. Uma tendência aleatória é uma tendência cuja direção muda após um determinado número de velas (o número de velas é definido pelo parâmetro CountCandle).
Formação da tendência implementada em função ChooseTrend:
double ChooseTrend() { switch(TType) { case 0: return NormalizeDouble(BValue[0]+dOCHL*(GetTrend()+GetDist(DistOCHL,DistOCHLParam1,DistOCHLParam2,DistOCHLParam3)),_Digits); case 1: return NormalizeDouble(BValue[0]+dOCHL*(-GetTrend()+GetDist(DistOCHL,DistOCHLParam1,DistOCHLParam2,DistOCHLParam3)),_Digits); case 2: { if((i_trend%CountCandle==0) && (i_trend!=0)) { if(i_bar!=0) BValue[0]=MRatesMin[i_bar-1].close; LastRand=MathRandomUniform(0,1,DistErr); i_trend=0; } if(LastRand>RandomTrendCoeff) return NormalizeDouble(BValue[0]+dOCHL*(GetModelTrend()+GetDist(DistOCHL,DistOCHLParam1,DistOCHLParam2,DistOCHLParam3)),_Digits); else return NormalizeDouble(BValue[0]+dOCHL*(-GetModelTrend()+GetDist(DistOCHL,DistOCHLParam1,DistOCHLParam2,DistOCHLParam3)),_Digits); } default:return 0; } }
No caso de uma tendência aleatória, para cada N velas de minutos definidas pelo parâmetro CountCandle, é gerada a variável aleatória LastRand uniformemente distribuída na faixa de 0 a 1. Se o valor LastRand for maior do que o parâmetro RandomTrendCoeff, a tendência será altista, caso contrário, baixista. Parâmetro RandomTrendCoeff permite variar a probabilidade de uma mudança na tendência. Se RandomTrendCoeff<0.5 for mais forte do que a tendência altista, se RandomTrendCoeff>0.5 - tendência baixista.
A tendência deste modelo é formada na função GetModelTrend:
double GetModelTrend() { switch(TModel) { case Linear: return Coeff1+Coeff2*i_trend; case Hyperbolic: { if(i_trend==0) return Coeff1; else return Coeff1+Coeff2/i_trend; } case Exp: { if(i_trend==0) return Coeff1; else return Coeff1+MathExp(Coeff2*i_trend); } case Power:return Coeff1+MathPow((double)i_trend,Coeff2); case SecondOrderPolynomial:return Coeff1+Coeff2*i_trend+Coeff3*i_trend*i_trend; case LinearAndPeriodic: return Coeff1*i_trend+sin(Coeff2*i_trend)+cos(Coeff3*i_trend); case LinearAndStochastic: { LastValue=Coeff1*i_trend+MathSqrt(Coeff2*(1-MathPow(exp(-Coeff3),2)))*MathRandomNormal(0,1,DistErr)+exp(-Coeff3)*LastValue; return LastValue; } default: return -1; } }
Essa função implementa os modelos de tendência mostrados na tabela 1.
Considere a formação de gráficos de preços para vários modelos de tendência. Crie uma tendência linear, para isso inicie o script GetTrendCandle com os seguintes parâmetros:
Fig. 3. Parâmetros do script GetTrendCandle
Iniciado o script, abra o gráfico de minutos do símbolo ExampleCurrency:
Fig. 4. Gráfico de minutos do símbolo ExampleCurrency para um modelo de tendência linear
No gráfico, vemos que foi gerada uma tendência linear, você pode alterar o ângulo de inclinação (taxa de aumento/diminuição) da tendência variando os coeficientes k1 e k2.
Nos parâmetros do script, especifique o modelo de tendência hiperbólica: TModel = Hyperbolic, Coeff1 = 1, Coeff2 = 1000. Iniciado o script, obtemos o seguinte gráfico:
Fig. 4. Gráfico de minutos do símbolo ExampleCurrency para o modelo de tendência hiperbólica
Uma característica deste modelo é que, como a função hiperbólica é uma função inversa, ao escolher uma tendência crescente (TType = Increasing), a tendência será decrescente.
Considere o modelo de tendência exponencial: TModel =Exp, Coeff1 = 1, Coeff2 = 0,1. Iniciado o script, obtemos o seguinte gráfico:
Fig. 5. Gráfico de minutos do símbolo ExampleCurrency para um modelo de tendência exponencial
Como seria de esperar, o gráfico mostra uma tendência crescente exponencial com um aumento do tamanho de velas.
Considere outros modelos de tendência:
Modelo de tendência de potência: TModel =Power, Coeff1 = 1, Coeff2 = 2.
Fig. 6. Gráfico de minutos do símbolo ExampleCurrency para o modelo de tendência de potência
Vemos que o gráfico é semelhante ao modelo de tendência exponencial, mas aumenta mais suavemente.
Modelo de tendência parabólica: TModel = SecondOrderPolynomial, Coeff1 = 1, Coeff2 = 0,05, Coeff3 = 0,05.
Fig. 7. Gráfico de minutos do símbolo ExampleCurrency para o modelo de tendência parabólica
O modelo parabólico é semelhante ao modelo de potência, mas tem uma taxa maior de aumento/diminuição da tendência.
Modelo de tendência periódica linear: TModel = LinearAndPeriodic, Coeff1 = 0.05, Coeff2 = 0,1, Coeff3 = 0,1.
Fig. 8. Gráfico de minutos do símbolo ExampleCurrency para um modelo de tendência periódico linear
No modelo de tendência periódica linear, muda de direção de acordo com a lei periódica.
Modelo de tendência linear-estocástico: TModel = LinearAndStochastic, Coeff1 = 0.05, Coeff2 = 1, Coeff3 = 1.
Fig. 8. Gráfico de minutos do símbolo ExampleCurrency para um modelo de tendência linear-estocástico
No modelo de tendência linear-estocástico aumenta ou diminui, fazendo oscilações aleatórias em torno de uma linha reta cuja inclinação é determinada pelo coeficiente k1.
Os modelos de tendência acima têm propriedades especificadas apenas em intervalos de minutos, em intervalos de M15, M30, H1 e superior, os gráficos de preços formados nesses modelos parecem funções lineares. Para obter uma tendência que mude sua direção em outros intervalos, exceto em M1, você deve selecionar o tipo de tendência aleatória (TType = Random) e especificar o número de velas de minutos nas quais será feita a tentativa de alterar a direção da tendência.
Inicie o script com os seguintes parâmetros: TModel = LinearAndStochastic, TType = Random, Coeff1 = 0.05, Coeff2 = 1, Coeff3 = 1, RandomTrendCoeff=0.5, CountCandle=60. Obtenha o seguinte gráfico no intervalo H1:
Fig. Gráfico de 9 horas do símbolo ExampleCurrency com mudança aleatória de tendência
Defina o parâmetro RandomTrendCoeff = 0.7 e inicie o script:
Fig. Gráfico de 10 horas do símbolo ExampleCurrency com uma mudança aleatória na tendência com RandomTrendCoeff = 0.7
Vemos que há uma tendência decrescente, mude RandomTrendCoeff = 0.3 e obtenha uma tendência crescente:
Fig. Gráfico de 10 horas do símbolo ExampleCurrency com uma mudança aleatória na tendência com RandomTrendCoeff = 0.3
Assim, usando o script GetCandleTrend é possível formar uma tendência em diferentes intervalos de tempo, gerando, ao mesmo tempo, barras de minutos.
O script GetTickTrend permite gerar ticks e, a partir deles, formar barras de minutos e ter os mesmos recursos que o script GetCandleTrend.
Modelagem de padrões gráficos
Padrões gráficos são amplamente utilizados na análise técnica de mercado. Muitos traders usam padrões típicos para procurar pontos de entrada ou de saída do mercado, além disso, vários indicadores e EAs estão sendo desenvolvidos para analise de padrões no gráfico de preços.
Nesta seção, mostraremos como criar padrões gráficos usando os scripts descritos acima. Como exemplo, considere o processo de criação dos padrões "Topo Duplo" e "Fundo Duplo". Estes são os padrões:
Fig. 11. Padrão "Topo Duplo"
Fig. 12. Padrão "Fundo Duplo"
Para criar padrões, será usado o script GetCandleTrend. Padrões serão formados no período H1. Para formar cada padrão, você precisa iniciar o script GetCandleTrend quatro vezes com vários parâmetros de entrada. Escolha os seguintes intervalos de tempo, indicados nas figuras 11 e 12 como t1-t5:
- t1 - 00:00 02.01.2018
- t2 - 13:00 02.01.2018
- t3 - 13:00 03.01.2018
- t4 - 13:00 04.01.2018
- t5 - 00:00 05.01.2018
Para gerar o padrão "Topo Duplo", defina as seguintes configurações de script:
- Hora de início da geração das barras: 00:00 02.01.2018
- Hora final da geração de barras: 12:00 02.01.2018
- Modelo de tendência: LinearAndStochastic
- Tipo de tendência: Random
- Coeficiente de tendência: 0.15
- Coeficiente k1 do modelo de tendência: 0.15
- Coeficiente k2 do modelo de tendência: 1
- Coeficiente k3 do modelo de tendência: 1
- Intervalo de mudança aleatória da direção da tendência: 60
Inicialização №2:
- Hora de início da geração das barras: 13:00 02.01.2018
- Hora final da geração de barras: 12:00 03.01.2018
- Coeficiente de tendência: 0.85
- Hora de início da geração das barras: 13:00 03.01.2018
- Hora final da geração de barras: 12:00 04.01.2018
- Coeficiente de tendência: 0.15
- Hora de início da geração das barras: 13:00 04.01.2018
- Hora final da geração de barras: 00:00 05.01.2018
- Coeficiente de tendência: 0.85
Como resultado, após quatro inicializações do script GetCandleTrend, é obtido o gráfico de preços mostrado na Figura 13.
Fig. 13. Padrão "Topo Duplo" modelado no período H1
Da mesma forma, modele o padrão "Fundo duplo". Para fazer isso, incie o script GetCandleTrend quatro vezes com as configurações especificadas para o padrão "Topo Duplo", alterando apenas o coeficiente de tendência: 0,85 para a primeira execução, 0,15, 0,85, 0,15 para a próxima. O resultado do script é mostrado na Figura 14.
Fig. 14. Padrão "Fundo Duplo" modelado no período H1
Da mesma forma, você pode modelar outros padrões. Os padrões mais realistas são obtidos no gráfico de minutos. Para formar padrões em outros intervalos de tempo, é necessário - no parâmetro "Intervalo de mudança aleatória na direção da tendência"do script GetCandleTrend - especificar o número de velas de minutos contidas no intervalo selecionado, por exemplo, para o intervalo H1 - 60, para o intervalo H4 - 240.
Fim do artigo
Os símbolos personalizados são uma ferramenta conveniente e útil para testar EAs e indicadores. No artigo, criamos e revisamos scripts com os seguintes recursos:
1) Criação e exclusão de símbolos personalizados
Foi mostrado como criar um símbolo personalizado com base num existente ou novo com propriedades especificadas manualmente. O script que implementa a remoção de símbolo permite fechar todos os gráficos contendo um símbolo e desativá-lo na "Observação do mercado".
2) Geração de ticks e barras
Os scripts permitem gerar barras de minutos num determinado intervalo de tempo, com a capacidade de desativar a geração de barras nos fins de semana dias. Foram implementadas a geração de várias velas (longas ou curtas, doji, martelo, estrela e mariboza) e a substituição de barras e ticks por partes para economizar memória ao gerar grandes matrizes de barras e ticks.
3) Modelagem de tendências
Os scripts permitem modelar uma tendência usando vários modelos: linear, hiperbólico, exponencial, de potência, parabólico, linear-periódico e linear-estocástico. Também é possível modelar uma tendência para diferentes períodos.
4) Modelagem de padrões gráficos
Foi exemplificado o uso do script GetCandleTrend para criar os padrões "Topo Duplo" e "Fundo Duplo".
Os scripts apresentados podem ser usados para criar seu próprio histórico de preços, a partir de barras de minutos e ticks, para testes e para otimização de EAs e indicadores.