O sinal prevê alguma coisa?
Antes de construir uma estratégia sobre um sinal, pergunte se ele aponta na direção certa. Todo dia um sinal dá uma pontuação a cada ação, e um dia depois cada ação tem um retorno. O coeficiente de informação (IC) é a correlação, entre ações, da pontuação e do retorno posterior. Vai de -1 a 1; zero significa nenhum poder preditivo. Sinais reais são fracos: um IC de 0,02 a 0,05 já é útil, porque se repete em muitas ações e muitos dias.
import numpy as np
signal = np.array([0.3, -1.2, 0.8, 0.1, 1.5]) # today's score for five stocks
returns = np.array([0.01, -0.02, 0.005, -0.004, 0.03]) # their returns over the next day
s = signal - signal.mean()
r = returns - returns.mean()
ic = np.sum(s * r) / np.sqrt(np.sum(s ** 2) * np.sum(r ** 2)) # the Pearson correlation
print(round(float(ic), 4))
print(round(float(np.corrcoef(signal, returns)[0, 1]), 4)) # the same numberIC de postos: robusto a valores extremos
Uma correlação de Pearson é dominada por valores extremos: uma ação com pontuação enorme ou um salto de 40% em um dia pode virá-la. O IC de postos substitui cada array pelo posto de seus valores (1 para o menor) e correlaciona os postos. Só pergunta se as ações que o sinal coloca mais alto terminam mais alto, seja qual for o tamanho dos números, então não muda com qualquer transformação crescente do sinal. É a versão que a maioria das equipes quantitativas cita, e é a correlação de Spearman.
# The rank of each value: argsort gives the order, assigning 1..n through it gives the ranks
import numpy as np
values = np.array([30.0, 10.0, 20.0, 5.0])
order = np.argsort(values) # positions from smallest to largest: [3 1 2 0]
ranks = np.empty(len(values))
ranks[order] = np.arange(1, len(values) + 1)
print(order, ranks) # [3 1 2 0] [4. 2. 3. 1.]# Ranks via argsort, and a rank IC that shrugs off an extreme value
import numpy as np
def ranks(values):
order = np.argsort(values)
out = np.empty(len(values))
out[order] = np.arange(1, len(values) + 1)
return out
def ic(a, b):
a, b = a - a.mean(), b - b.mean()
return float(np.sum(a * b) / np.sqrt(np.sum(a ** 2) * np.sum(b ** 2)))
signal = np.array([0.3, -1.2, 0.8, 0.1, 1.5])
returns = np.array([0.01, -0.02, 0.005, -0.004, 0.03])
print(ranks(signal)) # [3. 1. 4. 2. 5.]
print(round(ic(signal, returns), 4), round(ic(ranks(signal), ranks(returns)), 4))
signal_with_outlier = signal.copy()
signal_with_outlier[4] = 1000.0 # one absurd score
print(round(ic(signal_with_outlier, returns), 4), round(ic(ranks(signal_with_outlier), ranks(returns)), 4))Consistência no tempo: o índice de informação do IC
O IC de um dia é ruidoso. Calcule-o todo dia para obter uma série e olhe sua média e sua regularidade. O ICIR, o IC médio dividido por seu desvio-padrão, premia sinais que acertam um pouco todo dia em vez de acertar muito em poucos dias. É um índice tipo Sharpe para o próprio sinal. Uma série diária de IC com média 0,03 e desvio-padrão 0,06 tem ICIR 0,5.
# A daily rank-IC series for a weak signal, and its ICIR
import numpy as np
def ranks(values):
out = np.empty(len(values))
out[np.argsort(values)] = np.arange(1, len(values) + 1)
return out
def ic(a, b):
a, b = a - a.mean(), b - b.mean()
return float(np.sum(a * b) / np.sqrt(np.sum(a ** 2) * np.sum(b ** 2)))
rng = np.random.default_rng(8)
daily_ics = []
for day in range(250):
signal = rng.normal(size=300) # 300 stocks
returns = 0.01 * (0.05 * signal + rng.normal(size=300)) # the signal explains a sliver of the return
daily_ics.append(ic(ranks(signal), ranks(returns)))
daily_ics = np.array(daily_ics)
print(round(float(daily_ics.mean()), 4), round(float(daily_ics.std(ddof=1)), 4))
print(round(float(daily_ics.mean() / daily_ics.std(ddof=1)), 3))- O IC é a correlação entre o sinal de hoje nas ações e seus retornos posteriores; 0,02 a 0,05 é útil, acima de 0,1 é suspeito.
- O IC de postos correlaciona postos (
argsortduas vezes): robusto a valores extremos e inalterado por qualquer transformação crescente do sinal. - ICIR = IC médio / desvio-padrão do IC em muitos dias: premia a habilidade constante; avalie sempre fora da amostra.
A seguir: o VaR histórico, a perda que uma carteira não deve exceder exceto nos piores dias.
Lab: o que é avaliado
- Colocar os valores em ordem com
ranks25 pts - Correlacionar sinal e resultado com
ic25 pts - Ignorar valores extremos com
rank_ic25 pts - Avaliar a consistência com
icir25 pts