Um backtesting adequado deve buscar simular situações práticas do mercado (slippage, custo de operação, etc...)
Olá.
Agradeço se alguém puder me ajudar no código abaixo...
Quando o candle atende as duas condições para operação, eu quero memorizar a máxima ou a mínima do 1º candle que atendeu as condições para posicionar a ordem de entrada e manter a ordem até ser acionada ou até que novo candle atenda condições contrárias.
Mas a ordem está acompanhando a mudança do candle.
Input StopLoss(161.8); //161,8% DO TAMANHO DO CANDLE DE GATILHO StopGain(161.8); //161,8% DO TAMANHO DO CANDLE DE GATILHO HorarioInicial(0905); HorarioFinal(1700); HorEncerrDia(1740); Var TamCandle : Real; BBS, BBI, aEst : Real; PosC, PosV, PosGain, PosStop : Real; Inicio BBS := BollingerBands(1.0, 20, 0)|0|; plot(bbs); PLOT2(BBI); BBI := BollingerBands(1.0, 20, 0)|1|; aEst := SlowStochastic(150); //tirei outros parametros , 3,0); If time >= HorEncerrDia then ClosePosition; //encerramento do dia if (time >= horariofinal) or (time <= HorarioInicial) then //FORA DO HORARIO OPERACIONAL begin PaintBar(ClAMARELO); ClosePosition; //encerra posiçoes fora do horario end else if (time < horariofinal) or (time > HorarioInicial) then //HORARIO OPERACIONAL begin //Abre horario operacional //LOGICA OPERACIONAL // BUSCA ENTRADAS if (BuyPosition = 0) e (SellPosition = 0) entao //Verifica se não existe posição //Condição da compra Inicio inicio Se (aEst > 80) and (fechamento < BBI) entao PosC := Maxima; Inicio TamCandle := maxima[1] - minima[1]; PosGain := TamCandle * StopGain; PosStop := TamCandle * StopLoss; BuyStop(PosC, PosC); SellToCoverStop((buyprice - PosStop), (buyprice - PosStop)); //Stop SellToCoverLimit(buyprice + PosGain); //gain fim; fim; //Condição da venda inicio Se (aEst < 20) and (fechamento > BBS) entao PosV := Minima; Inicio // PosV := Minima; TamCandle := maxima[1] - minima[1]; PosGain := TamCandle * StopGain; PosStop := TamCandle * StopLoss; SellShortStop(PosV, PosV); BuyToCoverStop((sellprice + PosStop), (sellprice + PosStop)); //Stop BuyToCoverLimit(sellprice - PosGain); //gain fim; fim; Fim; //BUSCA SAIDAS Inicio Se (IsBought) entao Inicio SellToCoverStop((buyprice - PosStop), (buyprice - PosStop)); //Stop SellToCoverLimit(buyprice + PosGain); //gain Fim; Se (IsSold) entao Inicio BuyToCoverStop((sellprice + PosStop), (sellprice + PosStop)); //Stop BuyToCoverLimit(sellprice - PosGain); //gain Fim; Fim; Fim; Fim;
Eu tambem tenho esse problema, que é armazenar o Candle Sinal para que ele controle os Take e Stop. sempre que faço robos com essa logica, ele acaba ajustando os alvos e stops a cada novo candle. Outra dificuldade, que tenho, para fazer um grid de preço médio, é "armazenar o preço da primeira entrada". Nao vou conseguir te ajudar, mas qdo vc tiver essa resposta, por favor compartilhe pq preciso dela rsrsrsr
Porem, ao avaliar teu codigo eu vi uma logica operacional super interessante mas que não estava rodando de forma capaz de extrair melhores resultados. Entao, tomei liberdade de ajustar alguns parametros:
StopLoss(1.618); //161,8% DO TAMANHO DO CANDLE DE GATILHO
StopGain(1.618); //161,8% DO TAMANHO DO CANDLE DE GATILHO
//++++++++++++ estava 168, alterei para 1.618
HorarioInicial(0905);
HorarioFinal(1700);
HorEncerrDia(1740);
Var
TamCandle : Real;
BBS, BBI, aEst : Real;
PosC, PosV, PosGain, PosStop : Real;
Inicio
BBS := BollingerBands(2.0, 20, 0)|0|;
plot(bbs);
PLOT2(BBI);
BBI := BollingerBands(2.0, 20, 0)|1|;
//++++++++++++ favor testar no grafico de 15min, usando banda desvio 2; periodo 60... fica top)
aEst := SlowStochastic(60); //tirei outros parametros , 3,0);
//++++++ no grafico de 5 min, vc pode testar com SlowStochastic 60 ... metralhadora animal e
//++++++ e vai rodar bem tanto com BB desvio 1.0 e desvio 2.0
//++++++ 5 min: bb 1.0, 20; estocastico 150
//++++++ 5 min: bb 2.0, 20; estocastico 60
If time >= HorEncerrDia then ClosePosition; //encerramento do dia
if (time >= horariofinal) or (time <= HorarioInicial) then //FORA DO HORARIO OPERACIONAL
begin
PaintBar(ClAMARELO);
ClosePosition; //encerra posiçoes fora do horario
end
else
if (time < horariofinal) or (time > HorarioInicial) then //HORARIO OPERACIONAL
begin //Abre horario operacional
//LOGICA OPERACIONAL
// BUSCA ENTRADAS
if (BuyPosition = 0) e (SellPosition = 0) entao //Verifica se não existe posição
//Condição da compra
Inicio
inicio
Se (aEst > 80) and (fechamento < BBI) entao
PosC := Maxima;
Inicio
TamCandle := maxima[1] - minima[1];
PosGain := TamCandle * StopGain;
PosStop := TamCandle * StopLoss;
BuyStop(PosC, PosC);
SellToCoverStop((buyprice - PosStop), (buyprice - PosStop)); //Stop
SellToCoverLimit(buyprice + PosGain); //gain
fim;
fim;
//Condição da venda
inicio
Se (aEst < 20) and
(fechamento > BBS) entao
PosV := Minima;
Inicio
// PosV := Minima;
TamCandle := maxima[1] - minima[1];
PosGain := TamCandle * StopGain;
PosStop := TamCandle * StopLoss;
SellShortStop(PosV, PosV);
BuyToCoverStop((sellprice + PosStop), (sellprice + PosStop)); //Stop
BuyToCoverLimit(sellprice - PosGain); //gain
fim;
fim;
Fim;
//BUSCA SAIDAS
Inicio
Se (IsBought) entao
Inicio
SellToCoverStop((buyprice - PosStop), (buyprice - PosStop)); //Stop
SellToCoverLimit(buyprice + PosGain); //gain
Fim;
Se (IsSold) entao
Inicio
BuyToCoverStop((sellprice + PosStop), (sellprice + PosStop)); //Stop
BuyToCoverLimit(sellprice - PosGain); //gain
Fim;
Fim;
Fim;
Fim;
Cria uma variável booleana que será verdadeira apenas quando a condição de compra ou venda acontecer, você irá armazenar os valor de max e min em um bloco separado da entrada.
Exemplo:
var bSinalC:Booleano; //Sinal de Entrada bGuardarValor : Booleano; //Variável que será acionada apenas quando o sinal ocorrer Stop, Alvo : Real; Begin {Buscar Sinal} bSinalC := (Maxima[1] > Maxima[0]) e (Minima[1] < Minima[0]); If bSinalC thne PaintBar(clVerde); {Buscar Entrada} If Not(IsBought) and ( bSinalC) then begin BuyAtMarket; bGuardarValor := verdadeiro; //Aqui aconteceu a condição então a variável é setada como verdadeiro. end; {bGuardarValor Valores de Saída} //É aqui que você pega e armazena o valor de max e min. If (IsBought) and (bGuardarValor) then begin Stop := Minima - (TicksStop * MinPriceIncrement); Alvo := Maxima + GanhoPontos; bGuardarValor := falso; //Muda a variável para falso e somente quando a condição se repetir novamente ela voltará a ser verdadeira. end; {Buscar Saída} SellToCoverLimit(Alvo); SellToCoverStop(Stop,Stop-OffsetStop); End;
Ainda sem (Travar o candle sinal de entrada, deixando que o take e stop variem candle a candle) eu fiz esses ajustes no seu setup... (no corpo, tem os resultados de BT)
Input
StopLoss(2); //161,8% DO TAMANHO DO CANDLE DE GATILHO
StopGain(4); //161,8% DO TAMANHO DO CANDLE DE GATILHO
HorarioInicio(0900); //====== HORÁRIO DE INICIO PARA O ENVIO DE ORDENS ======
HorarioFim(1630); //====== HORÁRIO FINAL PARA O ENVIO DE ORDENS E INICIO DO CANCELAMENTO DE ORDENS PENDENTES CASO AINDA NÃO ESTEJA POSICIONADO ======
HorarioFecharPosicoes(1700); //====== HORÁRIO ONDE DEVE SER REALIZADO O FECHAMENTO DAS POSIÇÕES ABERTAS ======
Var
TamCandle : Real;
BBS, BBI, aEst : Real;
PosC, PosV, PosGain, PosStop : Real;
HorarioNegociacao, HorarioZeragem : booleano;
BarraPosicionado, BarraNaoPosicionado : inteiro;
Inicio
BBs := BollingerBands(2, 20, 0)|0|;
BBi := BollingerBands (2, 20, 0)|1|;
//Plot(BBs);
//Plot2(BBi);
aEst := SlowStochastic(60);
Plot3(aEst);
TamCandle := maxima[1] - minima[1];
PosGain := TamCandle * StopGain;
PosStop := TamCandle * StopLoss;
//====== VERIFICA O HORÁRIO DE NEGOCIAÇÃO ======
Se (Time >= HorarioInicio) e (Time <= HorarioFim) então
HorarioNegociacao := verdadeiro
Senao
HorarioNegociacao := falso;
//====== VERIFICA O HORARIO DE ZERAGEM ======
Se (Time >= HorarioFecharPosicoes) então
HorarioZeragem := verdadeiro
Senao
HorarioZeragem := falso;
//====== VERIFICA SE ESTÁ COMPRADO ======
Se IsBought e (CurrentBar <> BarraNaoPosicionado) entao
Inicio
SellToCoverStop((buyprice - PosStop), (buyprice - PosStop));
SellToCoverLimit(buyprice + PosGain);
Fim
Senao
Inicio
//====== VERIFICA SE ESTÁ VENDIDO =====
Se IsSold e (CurrentBar <> BarraNaoPosicionado) entao
Inicio
BuyToCoverStop((sellprice + PosStop), (sellprice + PosStop)); //Stop
BuyToCoverLimit(sellprice - PosGain);
Fim
Senao
Inicio
//====== VERIFICA SE O HORARIO ATUAL PERMITE NOVAS ENTRADAS ======
Se HorarioNegociacao e (CurrentBar <> BarraPosicionado) entao
Inicio
//====== ENTRADAS COMPRAS =====
// --- testes Indice 5Min, com 1 slippage e 0,50 de custo por contrato (5 min)
// --- se eliminar a BB e usar Est40 = 1986trades(Lucro 74.4 / Fator 1,26 / DD tf9.8-tt4.8 / LR 1,18
// --- com uso de BB, melhor deixar Estoc em 020 / 080 = 755trades(lucro 27,2 / Fator 1,24 / DD tf28.8-tt17.1 / LR 1,07
// --- testes Indice 15Min, com 1 slippage e 0,50 de custo por contrato
// --- se eliminar a BB e usar Est40 = 983trades(Lucro 65.1 / Fator 1,27 / DD tf21-tt8 / LR 1,29
// --- ** com uso de BB, melhor deixar Estoc em 020 / 080 = 325trades (lucro 34,6 / Fator 1,39 / DD tf15-tt8.9 / LR 1,33
// --- com uso de BB, melhor deixar Estoc em 020 / 080 = 309trades (lucro 30,6 / Fator 1,30 / DD tf22-tt21.9 / LR 1,76
// --- ** MELHOR OPÇÃO M15 com BB
Se (aEst < 80)
e (fechamento < BBI)
entao BuyStop(Maxima,Maxima)
Senao
//====== ENTRADAS VENDAS =====
Se (aEst > 20)
e (fechamento > BBS)
entao SellShortStop(Maxima,Maxima);
BarraNaoPosicionado := CurrentBar;
Fim
Senao
//====== CANCELA AS ORDENS PENDENTES CASO NÃO ESTEJA COMPRADO E NÃO ESTEJA EM HORÁRIO DE NEGOCIAÇÃO ======
CancelPendingOrders;
Fim;
Fim;
Fim;
@m4tr1xbr , você poderia dar um exemplo de como armazenar o valor da entrada?
Por exemplo, se a condição de entrada for 2 ticks acima da máxima de um determinado candle, eu queria que a ordem ficasse aguardando ser acionada naquele valor esperando o pullback, e não ir mudando de candle em candle.
@paulo-ferreira , obrigado pela ajuda. Vou testar.
@MAHALO, vou postar um exemplo para você, nesse caso o sinal de entrada é uma Inside Bar, para efeitos didáticos coloquei o nome da variável (Sinal de Compra) como bInsideBar, então sempre que houver um inside bar iremos tentar fazer uma compra na máxima da barra sinal. Nesse caso eu define que se não houver a compra na barra seguinte irei aceitar uma compra até duas barras depois do sinal ocorrer, você também poderia usar o tempo, por exemplo até 5min após o sinal ocorrer, nesse caso é só usar a função Time, lugar de CurrentBar.
Em resumo iremos fazer o seguinte:
Definição das constantes e variáveis: No início do código, são definidas algumas constantes e variáveis que serão usadas ao longo do script. As constantes definem a quantidade de ticks para o stop loss, o offset para o stop loss e a quantidade de ticks para o take profit. As variáveis armazenam o sinal de entrada, se devemos guardar o valor de entrada ou não, os preços de stop e alvo, o preço de entrada, a barra atual, o número de barras desde o sinal de entrada e se podemos comprar ou não.
Buscar Sinal: Nesta seção, o script verifica se a barra atual é uma inside bar. Se for, ele pinta a barra de verde.
Buscar Entrada: Se não houver uma posição aberta e a barra atual for uma inside bar, o script define o preço de entrada como a alta da barra atual, ativa a possibilidade de compra, atualiza a barra atual e coloca uma ordem de compra stop no preço de entrada.
Executar Compra: Se a barra atual não for a barra onde o sinal de entrada ocorreu e a possibilidade de compra estiver ativa, o script atualiza o número de barras desde o sinal de entrada. Se ainda não houver uma posição aberta, ele coloca uma ordem de compra stop no preço de entrada. Se houver uma posição aberta ou se passaram duas barras desde o sinal de entrada, ele desativa a possibilidade de compra e reseta a barra onde o sinal ocorreu.
Definir Valores de Saída: Se a variável para guardar o valor for verdadeira, o script define o stop como a baixa da barra atual menos o TickStop e o alvo como a alta da barra atual mais o take profit em ticks. Em seguida, ele reseta a variável guardar valor.
Buscar Saída: Se houver uma posição aberta, o script coloca uma ordem de venda limitada no alvo e uma ordem de venda stop no stop, com offset. Depois, reseta a variável podeComprar.
// CONSTS ========================================================================================= const cTickStop=1; // Define a quantidade de ticks para o stop loss cOffsetStop =2; // Define o offset para o stop loss cTakeProfit=15; // Define a quantidade de ticks para o take profit (Alvo) // INPUTS ========================================================================================= // VARIÁVEIS ======================================================================================== var bInsideBar :Boolean; // Sinal de entrada bGuardarValor :Boolean; // Variável que será acionada apenas quando o sinal de entrada ocorrer fStop, fAlvo :float; // Valores de stop e alvo fPrecoEntrada :float; // Preço de entrada iCurrentBar :integer; // Recebe o valor da barra atual iNbarras :integer; // Número de barras desde o sinal de entrada bPodeComprar :boolean; // Autoriza a compra se a compra não for feita no sinal de entrada ocorrer Begin {Buscar Sinal} // Se a barra atual é uma inside bar (máxima e mínima dentro da barra anterior), pintar a barra de verde bInsideBar := (Maxima[1] > Maxima[0]) e (Minima[1] < Minima[0]); If bInsideBar then PaintBar(clVerde); {Buscar Entrada} // Se não houver uma posição aberta e ocorrer um sinal de entrada If Not(IsBought) and ( bInsideBar) then begin fPrecoEntrada := High; // Definir o preço de entrada como a máxima da barra atual bGuardarValor := true; // Definir a variável para guardar valor como verdadeira bPodeComprar:=true; // Ativar a possibilidade de comprar iCurrentBar:=CurrentBar; // Atualizar a barra atual BuyStop(fPrecoEntrada); // Colocar uma ordem de compra stop no preço de entrada end; // Se a barra atual não for a barra em que o sinal ocorreu e a possibilidade de compra estiver ativa if (iCurrentBar <> CurrentBar) and bPodeComprar then begin // Atualizar o número de barras desde o sinal de entrada iNbarras:=CurrentBar - iCurrentBar; // Se ainda não houver uma posição aberta, colocar uma ordem de compra stop no preço de entrada if Not(IsBought) then BuyStop(fPrecoEntrada); // Se houver uma posição aberta ou se passaram duas barras desde o sinal de entrada if (IsBought) or (iNbarras >=2) then begin // Desativar a possibilidade de compra bPodeComprar:=false; // Resetar a barra em que o sinal ocorreu iCurrentBar:=-1; end; end; {bGuardarValor Valores de Saída} // Se a variável para guardar valor for verdadeira irá fixar os valores para não atualizarem barra a barra If (bGuardarValor) then begin // Definir o stop como a mínima da barra atual menos o TickStop fStop := Low - (cTickStop * MinPriceIncrement); // Definir o alvo como a máxima da barra atual mais o take profit em ticks fAlvo := High + (cTakeProfit * MinPriceIncrement); // Resetar a variável guardar valor bGuardarValor := falso; end; {Buscar Saída} If (IsBought) then begin // Colocar uma ordem de venda limitada no alvo SellToCoverLimit(fAlvo); // Colocar uma ordem de venda stop no stop, com offset SellToCoverStop(fStop, fStop - cOffsetStop); //É preciso resetar a variável PodeComrar sempre que houver um fechamento de posição para evitar erros nos backtesting bPodeComprar:=false; end; End;