Uma linha suave que viu o futuro
Um filtro transforma uma sequência de entrada xₜ numa saída yₜ como soma ponderada de entradas e saídas recentes. A média móvel exponencial é o mais simples: yₜ=αxₜ+(1-α)yₜ₋₁. No scipy.signal um filtro é um par de listas de coeficientes, b para as entradas e a para as saídas, e aqui yₜ=b₀xₜ-a₁yₜ₋₁ com b = [alpha] e a = [1, -(1 - alpha)]. signal.lfilter(b, a, x) o executa para a frente no tempo, exatamente como um sistema ao vivo, então yₜ depende só de xₛ com s≤t. Por padrão parte do repouso (estado zero), o que falseia os primeiros valores para um preço perto de 100; passe zi = signal.lfilter_zi(b, a) * x[0] para partir do primeiro valor. O resultado concorda com pd.Series(x).ewm(alpha=alpha, adjust=False).mean() até a precisão de máquina.
signal.filtfilt(b, a, x) executa o mesmo filtro para a frente e depois para trás. O atraso se cancela, então a linha suave fica em cima dos dados, o que parece perfeito num gráfico, mas cada saída agora depende de entradas posteriores. Usado para construir um sinal de trading é um vazamento. Pegue um sinal de tendência, o log do preço menos sua média, e correlacione-o com o retorno do dia seguinte: para o SPY em 2.501 dias e α=0,1, o sinal causal tem correlação de -0,055 (uma fraca reversão à média), o de fase zero -0,33; para α=0,3 os números são -0,076 e -0,54. Nenhum trader poderia ganhar esses números de fase zero, porque o valor filtrado já contém o preço de amanhã. O teste de um sinal é simples: trunque os dados numa data e confira que os valores anteriores não mudam.
lfilteré ao vivo,filtfilté retrospectiva: só o primeiro roda numa mesa de trading.- Defina o estado inicial:
lfilter_zi(b, a) * x[0]elimina o transitório de partida. - Teste de truncamento: corte os dados numa data; um sinal válido não muda antes dela.
Lab: o que é avaliado
- Coeficientes do filtro10 pts
- A média causal20 pts
- A versão do pandas10 pts
- A média de fase zero15 pts
- Sinais e retorno de amanhã25 pts
- O estudo do SPY20 pts