Uma janela que desliza
O preço de hoje diz pouco sozinho; os traders o comparam com o passado recente. Uma janela móvel guarda as últimas n observações e avança um passo de cada vez, de modo que cada data recebe uma estatística calculada com seu próprio passado recente: uma média móvel, uma volatilidade recente, uma máxima recente. No pandas, series.rolling(n) descreve a janela e uma estatística como .mean() a calcula em cada posição de uma vez.
import pandas as pd
prices = pd.Series(
[100.0, 102, 101, 105, 107, 106, 110],
index=pd.bdate_range("2026-03-02", periods=7),
name="close",
)
moving_average = prices.rolling(3).mean()
print(moving_average.round(2).tolist())
# [nan, nan, 101.0, 102.67, 104.33, 106.0, 107.67]
print(prices.rolling(3).max().tolist()) # [nan, nan, 102.0, 105.0, 107.0, 107.0, 110.0] the 3-day highO começo da série
As primeiras n - 1 posições ainda não têm janela completa, então o resultado é NaN. Isso é honesto: a média de dois preços não é uma média de 3 dias. Se você realmente quer um valor mais cedo, min_periods=1 calcula com o que há, e expanding() usa tudo desde o início em vez de uma janela fixa. Estatísticas que precisam de vários pontos, como o desvio padrão, são NaN até chegarem observações suficientes. Uma série que já começa com NaN, como os retornos simples, empurra a primeira janela válida um passo adiante.
# min_periods=1: use whatever is available at the start
print(prices.rolling(3, min_periods=1).mean().round(2).tolist()) # [100.0, 101.0, 101.0, 102.67, 104.33, 106.0, 107.67]
# expanding(): everything since the first day, not a fixed window
print(prices.expanding().mean().round(2).tolist()) # [100.0, 101.0, 101.0, 102.0, 103.0, 103.5, 104.43]
# Rolling volatility of the returns: the first return is NaN, so three returns exist only from the 4th day
returns = prices.pct_change()
print(returns.rolling(3).std().round(4).tolist()) # [nan, nan, nan, 0.0249, 0.0248, 0.0246, 0.0237]Das janelas aos sinais
As estatísticas móveis viram sinais de trading quando comparadas. Um z-score diz a que distância o preço de hoje está da sua própria média recente, em unidades do seu desvio padrão recente: (price - rolling mean) / rolling std. Construa o objeto móvel uma vez (r = prices.rolling(3)) e use-o duas vezes. Um cruzamento de médias móveis compara uma média rápida com uma lenta: enquanto a rápida está acima, a tendência recente é de alta. Uma comparação dá booleanos, e astype("int64") os transforma em um sinal 0/1. Comparações com NaN são False, então o sinal é 0 até que as duas médias existam.
# z-score of each price against its own 3-day window
rolling = prices.rolling(3)
zscore = (prices - rolling.mean()) / rolling.std()
print(zscore.round(2).tolist()) # [nan, nan, 0.0, 1.12, 0.87, 0.0, 1.12]# Moving-average crossover: 1 while the 2-day average is above the 4-day average
fast = prices.rolling(2).mean()
slow = prices.rolling(4).mean()
signal = (fast > slow).astype("int64")
print(signal.tolist()) # [0, 0, 0, 1, 1, 1, 1]
# Trade on the next day: the signal of today's close cannot be traded before tomorrow
print(signal.shift(1).fillna(0).astype("int64").tolist()) # [0, 0, 0, 0, 1, 1, 1]center=True põe a linha atual no meio da janela e usa preços futuros: fica ótimo no gráfico e não dá para negociar. Um sinal calculado com o fechamento de hoje deve ser aplicado amanhã, daí o shift(1).series.rolling(n).mean()(oustd(),max()...) calcula uma estatística sobre as últimasnobservações em cada posição.- As primeiras
n - 1entradas sãoNaN;min_periodseexpanding()mudam isso de propósito. - Z-scores e cruzamentos de médias móveis transformam janelas em sinais; desloque um sinal antes de negociá-lo e evite
center=True.
A seguir: o tratamento de dados ausentes, o que fazer com os valores NaN que as tabelas de preços reais sempre contêm.
Lab: o que é avaliado
- Suavizar os preços com
moving_average25 pts - Medir o risco recente com
rolling_volatility25 pts - Padronizar cada preço em relação à sua janela com
rolling_zscore25 pts - Transformar duas médias em um sinal com
ma_crossover25 pts