Neo traderBot

Neo traderBot

Você sabia?

Um backtesting adequado deve buscar simular situações práticas do mercado (slippage, custo de operação, etc...)

Códigos de exemplo? Tome Snippets!

Outros Snippets

Leitura estimada: 11 minutos 1363 views

Introdução

Esta seção visa apresentar outros exemplos de trechos de códigos que não se enquadram nas categorias anteriores.

Você pode acessar os Snippets diretamente pelo menu lateral direito, ou fazendo CRL+F (CTRL+L) para localizar algum texto específico na página, uma vez que o conteúdo tende a crescer ao longo do tempo, dificultando a navegação pelo menu.

Caso tenham sugestões de código para acrescentar à lista, gentileza deixar o código nos comentários com o link para seu perfil em rede social (para devido crédito de autoria).

Snippets

Escrevendo no Console com Timestamp para facilitar Debug

A partir da versão beta 215, tornou-se possível no Backtesting escrever texto no Console, o que facilita a depuração de estratégias.

Segue abaixo um snippet no qual há uma função que retorna o timestamp da barra. O corpo principal da estratégia apenas gera na aba “Compilação” o log com mensagem “Teste” precedido do timestamp no formato “DD/MM/AAAA HH:MM”.

				
					var
  horario: integer;
  timestamp : string;


//---------------------------------------------------
function getTimestamp: string;
var
  dia, mes_ano, hora: string;
begin

  if Month(Date) < 10 then 
    mes_ano := "0" + Month(Date) + "/" + Year(Date)
  else
    mes_ano := Month(Date) + "/" + Year(Date);    

  if DayofMonth(Date) < 10 then
    dia := "0" + DayofMonth(Date) + "/" + mes_ano
  else 
    dia := DayofMonth(Date) + "/" + mes_ano;

  if Floor(Time/100) < 10 then
    hora := "0" + Floor(Time/100)
  else
    hora := Floor(Time/100);

  if Mod(Time,100) < 10 then
    hora := hora + ":" + "0" + Mod(Time,100)
  else
    hora := hora + ":" + Mod(Time,100);    

  Result :=  dia + " " + hora + " - ";
end;
//---------------------------------------------------

begin
  if horario <> Time then
  begin
    timestamp := getTimestamp;
    ConsoleLog(getTimestamp() + "Teste", clBlue);
    horario := Time;
  end;
  
end;





				
			

Arrendondamento de valores para N casas decimais

Reproduzir vídeo

Como o Profit não tem nativamente uma função para arrendondar um float para uma determinada quantidade de casas decimais (N), a função abaixo foi implementada com esta finalidade.

				
					var
  fValorOriginal: float;
  fValorArred: float;

function NTB_ArredondarFloat(Value: float; nCasas: integer):float;
var
    fValor: float;
    fParteInteira: float;
    fParteDecimal: float;
begin
  fValor := Value;
  fParteInteira := IntPortion(fValor);
  fParteDecimal := Round((fValor - fParteInteira)*Power(10,nCasas));

  Result := fParteInteira + fParteDecimal/Power(10,nCasas);

end;

begin
  fValorOriginal := 12.345819;
  fValorArred :=  NTB_ArredondarFloat(fValorOriginal,2);

  if LastBarOnChart then
    PlotText(fValorArred,clWhite, - 1,8);
end;
				
			

Restrição de estratégia pelo código do ativo

O código abaixo visa identificar o código do ativo no qual a estratégia ou robô está sendo aplicado. A estratégia apenas é executada se o ticker do ativo for igual a constante “cAtivoEstrategia”.

Isto evita que as estratégias sejam rodadas de forma equivocada em ativos para os quais os parâmetros da estratégia não foram otimizados.

				
					const
  cAtivoEstrategia = "PETR4";
begin
  if GetAsset = cAtivoEstrategia then
  begin
    //TODO CÓDIGO FONTE DA SUA ESTRATÉGIA DEVE ESTAR AQUI DENTRO
    PaintBar(clGreen);
  end
  else
    if LastBarOnChart then
      plotText("Usar em " + cAtivoEstrategia,clWhite,-1,12);
end
				
			

Restrição de estratégia pelo tipo/frequência do gráfico

Reproduzir vídeo

O código abaixo visa identificar o tipo de gráfico no qual a estratégia ou robô está sendo aplicado. Caso não corresponda ao tipo de gráfico para o qual a estratégia foi otimizada, é exibida mensagem com informação sobre o tipo de gráfico adequado.

Isto evita que as estratégias sejam rodadas de forma equivocada para tipos de gráficos e frequências temporais diferentes daquelas para as quais foi validada.

				
					var
  bStarted, bGraficoOK: boolean;
  iTipoGrafico: integer;
  strMensagemErro: string;
begin
  if Not bStarted then
  begin
    bStarted := true;
    iTipoGrafico := GraphicInterval;

    //Verificação de Renko 3R
    if (iTipoGrafico = itRenko) and (GraphicOffset = 3) then bGraficoOK := true
    else strMensagemErro := "Estratégia para 3R"; 
    
    //Verificação de Gráfico de 5 minutos    
    //if (iTipoGrafico = itMinute) and (GraphicOffset = 5) then bGraficoOK := true
    //else strMensagemErro := "Estratégia para 5 Minutos";
         
  end;

  // Robô/Estratégia só irá ser executada se o gráfico estiver configurado
  // adequadamente
  if bGraficoOK then
  begin


  end
  // Se gráfico não estiver na configuração adequada, pinta todas as barras de vermelho
  // e exibe texto na última barra
  else
  begin
  paintBar(clRed);
  if LastBarOnChart then
    plotText(strMensagemErro,clWhite,-1,9);
  end;

end
				
			

Ignorar boxes de Gap em gráficos de Renko

Reproduzir vídeo

É bastante comum, principalmente na operação de contratos futuros, a ocorrência de gaps na abertura. Quando se está em um gráfico de Renko, o código da estratégia é processado para cada box incluído no gráfico devido ao gap.

Este código apresenta uma forma simples de não executar lógica sobre boxes referentes à gap. Os boxes de gap possuem duração igual a zero. Para realizar a verificação da duração temporal em minutos de um box, basta utilizar o comando BarDurationF.

				
					var
  bLocalizouBoxesGap : boolean;
  iDataUltimoBox, iUltimoBoxDiaAnterior, iBoxDeGAP: integer;
begin
  
  // Código é aplicável apenas à gráficos do tipo Renko
  if GraphicInterval = itRenko then
  begin  
    // Identificação de Box relacionados a GAP
    if Date <> iDataUltimoBox then
    begin
      iUltimoBoxDiaAnterior := CurrentBar - 1;
      iBoxDeGAP := CurrentBar;
      bLocalizouBoxesGap := false;
    end;
  
   iDataUltimoBox := Date;
   
   if Not bLocalizouBoxesGap then
    if BarDurationF = 0 then 
    begin
      iBoxDeGAP := CurrentBar;
      PaintBar(clRed);
    end
    else
    begin
      //Aqui foi identificado o primeiro box que não é de GAP
      bLocalizouBoxesGAP := true;
    end;


    //Aqui executa a estratégia em boxes que não são devido à
    //gap de abertura
    if bLocalizouBoxesGAP then
    begin
      PaintBar(clGreen);
    end;



  end
  // O Gráfico não é do tipo Renko!
  else
    if LastBarOnChart then PlotText("Aplicável apenas a Renko",clWhite,-1,10);

end
				
			

Cálculo de Gap em gráficos de Renko em termos de boxes

É bastante comum, principalmente na operação de contratos futuros, a ocorrência de gaps na abertura. Este código visa calcular o tamanho do gap nos gráficos de Renko em termos da quantidade de boxes.

OBS: É importante ressaltar que devido à própria natureza do gráfico de Renko, o cálculo de gap não é preciso quanto o cálculo de gap pelo gráfico de Candle.

OBS2: Os comandos OpenD(0) e CloseD(1) também podem ser utilizados em gráficos de renko, reduzindo o código abaixo a gap := (OpenD(0) – CloseD(1))/((GraphicOffset-1)*MinPriceIncrement). No entanto, os valores encontrados divergem em alguns dias de gap por 1 box. Pela minha apuração, o código abaixo apresenta os resultados corretos.

				
					var
  bLocalizouBoxesGap : boolean;
  fTamanhoBox : float;
  iDataUltimoBox, iUltimoBoxDiaAnterior, iBoxDeGAP: integer;
  fGAPdoDiaBox, fAberturaBox, fFechamentoDiaAnteriorBox: float;
begin
  
  // Código é aplicável apenas à gráficos do tipo Renko
  if GraphicInterval = itRenko then
  begin  
    fTamanhoBox := (GraphicOffset-1)*MinPriceIncrement;
    
    // Identificação de Box relacionados a GAP
    if Date <> iDataUltimoBox then
    begin
      iUltimoBoxDiaAnterior := CurrentBar - 1;
      iBoxDeGAP := CurrentBar;
      bLocalizouBoxesGap := false;
    end;
  
   iDataUltimoBox := Date;
   
   if Not bLocalizouBoxesGap then
    if BarDurationF = 0 then iBoxDeGAP := CurrentBar
    else
    begin
      //Aqui foi identificado o primeiro box que não é de GAP
      bLocalizouBoxesGAP := true;
  
      //Coloque abaixo o que deseja fazer no primero box do dia que não é GAP
      // --------------------------------------------------------------------
      if (Open < Close[1]) Or (Open > Close[1]) then fAberturaBox := Open[1]
      else fAberturaBox := Close[1];
      fFechamentoDiaAnteriorBox := Close[CurrentBar - iUltimoBoxDiaAnterior];
       
      fGAPdoDiaBox := (fAberturaBox -  fFechamentoDiaAnteriorBox)/fTamanhoBox;
  
      if fGAPdoDiaBox <> 0 then 
      begin
        if fGAPdoDiaBox > 0 then
          PlotText("GAP:" + Floor(fGAPdoDiaBox) + " Box",clGreen,-1,9)
        else
          PlotText("GAP:" + Floor(fGAPdoDiaBox) + " Box",clRed,-1,9);
        PaintBar(clRed);
      end;
      // --------------------------------------------------------------------
    end;
  end
  // O Gráfico não é do tipo Renko!
  else
    if LastBarOnChart then PlotText("Aplicável apenas a Renko",clWhite,-1,10);

end
				
			

Execução de estratégia em frequência superamostrada

Reproduzir vídeo

É de conhecimento de quem opera gráficos com tempo gráfico maior os impactos de se executar ordens apenas no fechamento dos candles/boxes.

Este Snippet visa tornar as ordens mais tempestivas, exemplificando uma forma de executar uma estratégia/robô em um tempo gráfico menor do que o original. Naturalmente que isso exige um código fonte mais complexo para tratar de forma adequada os cálculos de indicadores utilizados na estratégia.

O código de exemplo abaixo, demonstra como utilizar uma média móvel em um tempo gráfico superamostrado equivalente ao tempo gráfico original da estratégia (e com valor superior). Bem como também demonstra como seria o cálculo para obter a média de fechamento em tempos gráficos superiores ao gráfico supermostrado.

OBS: Embora não seja um requisito, sugere-se que o tempo gráfico original seja um múltiplo do tempo gráfico superamostrado.

				
					//LIMITAÇÃO IMPORTANTE: Não dá para fazer em escala de segundos
//devido a função time não retornar segundos
const
  // Estratégia originalmente rodava em gráfico de 15 min
  cTempoGraficoOriginal = 15;
  //Qtde de periodos no tempo gráfico original
  cQtdePeriodosMedia = 20;
  //Horario inicial de negociação do ativo
  //0900 para futuros, 1000 para mercado a vista
  cHoraInicioNegociacao = 0900;
var
  bStarted: boolean;

  iTempoGraficoAtual: integer;
  iQtdePeriodosEquivalente: integer;
  fMediaSuperAmostradaEquivalente: float;

  fInicializacaoMedia: float;
  fMediaTempoGraficoOriginal: float;
  iQtdePeriodosCalculados: integer;

  iHorarioEmMinutos: integer;
  iHorarioGrafSuperior: integer;
  bCalculouTempoGrafSuperior: boolean;

begin
  //1a opção: Dar preferencia sempre que possível
  // Identifica tempo gráfico utilizado e qtde de periodos equivalente para média superamostrada
  iTempoGraficoAtual := GraphicOffset;
  iQtdePeriodosEquivalente := Floor(cTempoGraficoOriginal/iTempoGraficoAtual)*cQtdePeriodosMedia;

  // Obtem média superamostrada equivalente
  fMediaSuperAmostradaEquivalente := Media(iQtdePeriodosEquivalente,Close);

  //Plot da série para inspeção
  PlotN(1,fMediaSuperAmostradaEquivalente);
  SetPlotColor(1, clWhite);
  SetPlotWidth(1,2);
  SetPlotStyle(1, psDash);



  //2a opção: Exige mais linhas de código para gerenciar execução
  //Amostrando apenas os valores de fechamento do Tempo gráfico original
  iHorarioEmMinutos := Floor(Time/100)*60+Mod(Time,100)
                     - (Floor(cHoraInicioNegociacao/100)*60+Mod(cHoraInicioNegociacao,100));
  
  if Not bStarted then
  begin
    iHorarioGrafSuperior := CalcTime(Time,-Mod(iHorarioEmMinutos, cTempoGraficoOriginal));
    bStarted := true;
  end;

  // Bloco para criar mecanismo de calcular apenas uma vez para o gráfico superior
  if (CalcTime(Time,-Mod(iHorarioEmMinutos, cTempoGraficoOriginal)) <> iHorarioGrafSuperior) then
  begin
    iHorarioGrafSuperior := CalcTime(Time,-Mod(iHorarioEmMinutos, cTempoGraficoOriginal));
    bCalculouTempoGrafSuperior := false;
  end;


  // Cálculo da média no tempo gráfico original
  if (Mod(iHorarioEmMinutos, cTempoGraficoOriginal) = 0) and Not bCalculouTempoGrafSuperior then
  begin
    bCalculouTempoGrafSuperior := true;
    if (iQtdePeriodosCalculados < cQtdePeriodosMedia) then 
    begin
      iQtdePeriodosCalculados := iQtdePeriodosCalculados + 1;
      fInicializacaoMedia := fInicializacaoMedia + Close[1];
    end
    else
      if (iQtdePeriodosCalculados = cQtdePeriodosMedia) then
      begin
        fMediaTempoGraficoOriginal := fInicializacaoMedia/iQtdePeriodosCalculados;
        iQtdePeriodosCalculados := iQtdePeriodosCalculados + 1;
      end
      else
        fMediaTempoGraficoOriginal := (fMediaTempoGraficoOriginal*cQtdePeriodosMedia
                                     - Close[1+cQtdePeriodosMedia*Floor(cTempoGraficoOriginal/iTempoGraficoAtual)]
                                     + Close[1])/cQtdePeriodosMedia;
  end;


  //Plot da série para inspeção
  PlotN(2,fMediaTempoGraficoOriginal);
  SetPlotColor(2, clRed);
  SetPlotWidth(2,2);
  SetPlotStyle(2, psSolid);

end
				
			

Como utilizar dados de preço de outros ativos

O código abaixo apenas exemplifica como obter os dados OHLCV de outras ativos em uma estratégia, o que pode ser feito via parâmetro ou constante. Caso o ativo desejado seja uma ação, deve-se atentar para modificar o feedBMF para feedBovespa.

				
					const
  cAtivo1 = Asset("INDFUT",feedBMF);
input
  pAtivo2("WINFUT",feedBMF);
  pQtdePeriodos(50);
var
  fMediaAtivo1, fMediaAtivo2: float;
begin
  fMediaAtivo1 := Media(pQtdePeriodos,cAtivo1.Close);
  fMediaAtivo2 := Media(Floor(pQtdePeriodos*1/3),pAtivo2.Close);
  plot(fMediaAtivo1);
  plot2(fMediaAtivo2);

end;
				
			

3 Comments

  • Sidney Rocha

    Fevereiro 19, 2023

    Fantástico! quanto mais pesquiso nos snippets mais me impressiono.

    Reply
    • Johnathas

      Fevereiro 20, 2023

      Olá Sidney!
      Fico feliz que o site da Comunidade NeoTraderBot, em especial, os snippets de NTSL estejam sendo úteis.
      Grande abs!

      Reply
  • sidneyrochagomes

    Fevereiro 22, 2023

    Olá Jhon, continuando com minha saga de fazer uma superamostragem das máximas ou mínimas. Copiei diretamente esse codigo da superamostragem e o plot da media branca funcionou como deveria mas a da média vermelha não. Tenho print e vou colcoar la no fórum pra add esse print e podermos encontrar o que estaria dando erro.

    Reply

Leave a Comment

CONTENTS