Todos os nossos backtests até hoje foram realizados como swing trades. Hoje, executaremos a primeira estratégia de day trade, ou seja, com a operação sendo iniciada e finalizada no mesmo dia.
A vantagem do day trade é proteger o seu capital de movimentos abruptos que aconteçam fora do período do pregão. Além disso, é uma forma de rentabilizar o seu caixa, ou um capital que você deixa separado para trades oportunísticos. Por fim, algumas corretoras vão permitir o uso da margem, que permite uma alavancagem que pode ser lucrativa se utilizada com responsabilidade.
Por outro lado, o day trade é extremamente volátil e a taxa de acerto naturalmente será menor que de swing trades. Finalmente, como o trade tem menos tempo pra evoluir (uma vez que ele precisa ser encerrado até o fim do dia), é difícil capturar altas expressivas.
Hoje nós abordaremos uma estratégia que é bastante eficiente no day trade: o Gap Trap de Compra ou Venda.
Entendendo o Gap Trap
O sinal para um Gap Trap de Compra acontece quando o primeiro candle do dia abrir abaixo da mínima do dia anterior e fechar positivo (fechamento maior que abertura).
Analogamente, no Gap Trap de Venda o primeiro candle do dia abre acima da máxima do dia anterior e fecha negativo (fechamento menor que a abertura).
O primeiro candle pode ser em qualquer timeframe, mas classicamente trabalhamos esse modelo no período de 15 minutos. A ideia é trabalhar contra os traders que imaginaram que o papel perderia a mínima e afundaria ou romperia a máxima e dispararia, e portanto ficaram trapped e terão que stopar suas posições, gerando uma pressão na ponta contrária.
A Estratégia
Uma vez que o sinal foi dado, a condição para a entrada é que o candle seguinte supere a máxima do candle anterior (no caso do Gap Trap de Compra) ou perca a mínima do candle anterior (no caso do Gap Trap de Venda). Caso a superação ou perda não aconteça no candle imediatamente posterior, o sinal está cancelado e a estratégia não é executada.
Se o sinal for ativado, existem algumas formas de conduzir a operação:
Carregar a operação até o final do dia;
Estabelecer um alvo de 2x o tamanho do candle inicial;
Definir uma % de ganho e carregar a operação até atingi-la.
Caso os pontos 2 e 3 não aconteçam dentro do dia, a operação é encerrada no fechamento.
Para o stop, também podemos pensar em 3 abordagens diferentes:
Sem stop (operação necessariamente se encerrará no fechamento do pregão);
Stop na mínima do candle sinal (Gap Trap de Compra) ou máxima do candle sinal (Gap Trap de Venda);
Stop percentual (encerrar a operação se ultrapassar x%x\%x% de loss).
Ao longo dessa série de backtests vamos experimentar algumas dessas abordagens. Hoje, trabalharemos a estratégia sem stop e com o alvo sendo o fechamento do dia.
Importando as bibliotecas e os dados necessários
Como de praxe, vamos importar as bibliotecas a serem utilizadas em nossa análise:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
Após, vamos carregar a nossa base de dados. Realizaremos o backtest no mini dólar (WDO), de 2020 até hoje. Caso você esteja interessado na base, ela estará disponível para download no nosso grupo do Telegram.
OBS: Mesmo que o valor da base difira do valor observado, como estamos interessados na diferença entre os preços no intraday, não há impacto no backtest.
Agora que temos nossa base carregada, o primeiro passo é identificar o primeiro candle do dia, pois ele é o responsável por caracterizar o sinal. Existem diversas formas de se fazer isso: nós vamos fazer identificando, para cada candle, se o candle seguinte é de um dia diferente.
# The first 10 chars are the date in the YYYY-MM-DD format
df["day"] = df.index.str[:10]
df["first of day"] = df["day"] != df["day"].shift(1)
df.head(5)
open
high
low
close
day
first of day
datetime
2020-01-02 09:00:00
4016.5
4017.5
4008.5
4017.5
2020-01-02
True
2020-01-02 09:15:00
4017.5
4024.0
4016.5
4019.0
2020-01-02
False
2020-01-02 09:30:00
4019.0
4027.0
4016.0
4019.0
2020-01-02
False
2020-01-02 09:45:00
4018.5
4020.0
4013.5
4014.5
2020-01-02
False
2020-01-02 10:00:00
4015.0
4015.5
4008.5
4012.5
2020-01-02
False
Para definirmos se o candle abriu em gap, precisamos calcular a máxima e a mínima do dia anterior. Vamos aproveitar e calcular também o fechamento de cada dia, uma vez que isso será utilizado na hora de aferirmos o nosso lucro.
Note, porém, que temos vários candles para um mesmo dia, uma vez que estamos trabalhando no gráfico intradiário. Resolveremos esse problema através da função groupby, onde agruparemos nossos dados pela coluna day:
High by day
2020-01-02 4046.0
2020-01-03 4075.5
2020-01-06 4080.5
2020-01-07 4098.5
2020-01-08 4083.5
Name: high, dtype: float64
Low by day
2020-01-02 4008.5
2020-01-03 4037.5
2020-01-06 4053.0
2020-01-07 4061.0
2020-01-08 4045.5
Name: low, dtype: float64
Close by day
2020-01-02 4030.5
2020-01-03 4062.5
2020-01-06 4067.5
2020-01-07 4074.5
2020-01-08 4069.0
Name: close, dtype: float64
Finalmente, com os dados relevantes calculados para cada dia, podemos inseri-los no nosso dataframe original. Dessa vez, utilizaremos a função merge, definindo a coluna day como a chave comum e adicionando o sufixo of day nas novas colunas:
# Save index beforehand so we can reset it after merging
index = df.index
df = pd.merge(df, close_by_day, on="day", suffixes=[None, " of day"])
df = pd.merge(df, high_by_day, on="day", suffixes=[None, " of day"])
df = pd.merge(df, low_by_day, on="day", suffixes=[None, " of day"])
# When merging is done, reset to original index
df.index = index
df
open
high
low
close
day
first of day
close of day
high of day
low of day
datetime
2020-01-02 09:00:00
4016.5
4017.5
4008.5
4017.5
2020-01-02
True
4030.5
4046.0
4008.5
2020-01-02 09:15:00
4017.5
4024.0
4016.5
4019.0
2020-01-02
False
4030.5
4046.0
4008.5
2020-01-02 09:30:00
4019.0
4027.0
4016.0
4019.0
2020-01-02
False
4030.5
4046.0
4008.5
2020-01-02 09:45:00
4018.5
4020.0
4013.5
4014.5
2020-01-02
False
4030.5
4046.0
4008.5
2020-01-02 10:00:00
4015.0
4015.5
4008.5
4012.5
2020-01-02
False
4030.5
4046.0
4008.5
...
...
...
...
...
...
...
...
...
...
2021-05-27 15:30:00
5249.0
5257.0
5246.5
5255.5
2021-05-27
False
5254.0
5313.5
5240.5
2021-05-27 15:45:00
5256.0
5258.0
5241.5
5242.0
2021-05-27
False
5254.0
5313.5
5240.5
2021-05-27 16:00:00
5242.0
5251.0
5241.5
5249.5
2021-05-27
False
5254.0
5313.5
5240.5
2021-05-27 16:15:00
5249.5
5255.0
5248.0
5254.0
2021-05-27
False
5254.0
5313.5
5240.5
2021-05-27 16:30:00
5254.5
5254.5
5254.0
5254.0
2021-05-27
False
5254.0
5313.5
5240.5
12559 rows × 9 columns
Nós já temos tudo o necessário para determinar os sinais de entrada. Recapitulando:
Gap Trap de Compra:
Primeiro candle do dia tem sua abertura abaixo da mínima do dia anterior;
Primeiro candle do dia tem seu fechamento maior que a abertura;
Segundo candle do dia supera a máxima do primeiro candle.
Gap Trap de Venda:
Primeiro candle do dia tem sua abertura acima da máxima do dia anterior;
Primeiro candle do dia tem seu fechamento menor que a abertura;
Segundo candle do dia perde a mínima do primeiro candle.
Assim, criaremos uma nova coluna signal, onde:
signal
descrição
1
representa um Gap Trap de Compra que foi ativado com a superação da máxima do primeiro candle no candle seguinte
-1
representa um Gap Trap de Venda que foi ativado com a perda da mínima do primeiro candle no candle seguinte
0
sem Gap Trap no dia
# A bullish gap trap has its open below yesterday's low and closes above its open
bullish_gap_trap = (df["first of day"] == True) & \
(df["open"] < df['low of day'].shift(1)) & \
(df["close"] > df["open"])
# A bearish gap trap has its open above yesterday's high and closes below its open
bearish_gap_trap = (df["first of day"] == True) & \
(df["open"] > df['high of day'].shift(1)) & \
(df["close"] < df["open"])
# Signal is 1 when the next candle exceeds the high of the bulish gap trap
# or signal is -1 when the next candle has its low below the low of the
# bearish gap trap. If none occurs, then signal is 0 and no trade is placed
df["signal"] = np.where(
(bullish_gap_trap == True) & (df["high"].shift(-1) > df["high"]),
1,
np.where(
(bearish_gap_trap == True) & (df["low"].shift(-1) < df["low"]),
-1,
0
)
)
long_trades = df[df["signal"] == 1]
short_trades = df[df["signal"] == -1]
print(f"Total Gap Trap Compras: {len(long_trades)}")
print(f"Total Gap Trap Vendas: {len(short_trades)}")
Total Gap Trap Compras: 21 Total Gap Trap Vendas: 16
Como podemos observar, de 2020-01-02 até 2021-05-27, houve 37 trades utilizando o modelo no mini dólar, dos quais 21 foram na ponta da compra e 16 na ponta da venda. Veja que temos em média 2 trades por mês utilizando o modelo, ou seja, é um sinal pouco frequente.
Calculando a Rentabilidade e Taxa de Acerto
Com os sinais calculados, já temos todas as informações necessárias pra calcular a taxa de acerto e a rentabilidade da estratégia. No post de hoje, vamos ver como ela teria se saído carregando a operação até o fechamento, sem stop.
Para isso, vamos definir como entrada (entry) 1 tick acima da máxima ou mínima do candle sinal. Note que num modelo mais fidedigno deveríamos contar com o slippage, ou a diferença entre o preço onde a ordem foi efetivamente executada e o preço que gostaríamos de executar. No entanto, vamos desconsiderar o efeito do slippage por simplicidade.
min_tick = 0.5 # That's the min variation for this asset
df["entry"] = np.where(
df["signal"] == 1,
df["high"] + min_tick,
np.where(
df["signal"] == -1,
df["low"] - min_tick,
np.nan
)
)
trades = df[~np.isnan(df["entry"])][["day", "high", "low", "signal", "entry", "close of day"]]
trades["target"] = trades["close of day"]
trades.set_index("day", inplace=True)
trades.head(10)
high
low
signal
entry
close of day
target
day
2020-01-27
4216.0
4207.0
-1
4206.5
4209.0
4209.0
2020-01-30
4248.5
4240.5
-1
4240.0
4244.5
4244.5
2020-02-03
4279.0
4273.0
-1
4272.5
4253.5
4253.5
2020-02-13
4385.5
4371.5
-1
4371.0
4353.5
4353.5
2020-02-28
4506.5
4497.5
-1
4497.0
4497.0
4497.0
2020-03-12
5034.0
4986.0
-1
4985.5
4801.0
4801.0
2020-03-23
5095.0
5021.0
-1
5020.5
5148.5
5148.5
2020-04-07
5216.0
5190.0
1
5216.5
5230.5
5230.5
2020-04-09
5157.0
5114.5
1
5157.5
5114.0
5114.0
2020-04-27
5582.5
5536.0
1
5583.0
5657.5
5657.5
Veja que a entrada está sendo feita sempre 0.50 pontos acima da máxima (Gap Trap de Compra) ou 0.50 pontos abaixo da mínima (Gap Trap de Venda). Nosso target nada mais é que o fechamento do dia.
Com isso, podemos calcular quantos pontos a estratégia capturou no período:
Ora, mas cada ponto do mini dólar equivale a R$ 10,00. Dessa forma, podemos calcular o resultado financeiro de um trader operando somente essa estratégia, de janeiro de 2020 até hoje, com 5 contratos:
Total longs: 21
Total shorts: 16
Successful trades: 22
Successful bullish gap traps: 14
Successful bearish gap traps: 8
Total Accuracy (%): 59.46%
Bullish Gap Trap Accuracy (%): 66.67%
Bearish Gap Trap Accuracy (%): 50.00%
De onde produzimos a seguinte tabela:
Tipo
Trades
Acertos
% Acerto
Gap Trap Compra
22
15
66.67%
Gap Trap Venda
16
8
50.00%
Total
38
23
59.46%
Por fim, vamos calcular estatísticas básicas da estratégia:
A estratégia do Gap Trap de Compra ou Venda é bastante poderosa por sua simplicidade e objetividade. Além disso, a taxa de acerto fica em tornos dos 60%, o que é bem interessante. A assertividade na ponta da compra é de 2/3 dos trades no período calculado.
Observe que na média um trader pode esperar aproximadamente 14 pontos/trade, ou R$ 140,00 ao se operar apenas um contrato. Repare, porém, que o desvio padrão é bem grande, o que nos traduz que o resultado oscila bastante.
Observe que algum percentil pode ser utilizado como stop. No caso, podemos usar os dados para considerar o stop toda vez que o loss ultrapassar 13 pontos. Note, porém, que a estratégia deu poucos sinais, e portanto um teste de confiabilidade estatística se faz necessário.
Idealmente, um trader mais rigoroso poderia separar a base em training e test sets e otimizar os parâmetros de saída no intervalo. No entanto, tal prática pode ocasionar em overfitting. Fique ligado nos próximos posts se quiser saber mais sobre o assunto!
Próximos Passos
Nos próximos posts dessa série vamos explorar a estratégia em diversos ativos, como mini índice e ações, além de testar estratégias diferentes de stop e alvo. Se você se interessa por esse tipo de conteúdo, não deixe de entrar no nosso canal do Telegram e se inscrever na nossa newsletter abaixo!