0% acharam este documento útil (0 voto)
50 visualizações208 páginas

Successful Algorithmic Trading

Direitos autorais
© © All Rights Reserved
Levamos muito a sério os direitos de conteúdo. Se você suspeita que este conteúdo é seu, reivindique-o aqui.
Formatos disponíveis
Baixe no formato PDF, TXT ou leia on-line no Scribd
0% acharam este documento útil (0 voto)
50 visualizações208 páginas

Successful Algorithmic Trading

Direitos autorais
© © All Rights Reserved
Levamos muito a sério os direitos de conteúdo. Se você suspeita que este conteúdo é seu, reivindique-o aqui.
Formatos disponíveis
Baixe no formato PDF, TXT ou leia on-line no Scribd

Machine Translated by Google

Machine Translated by Google

Conteúdo

I Introdução à Negociação Algorítmica 1

1 Introdução ao livro . . . . ....... ...... . ..... . ...... .3


1.1 Introdução ao QuantStart. . .
. . . . . ... . . . . . . . . . . . . . . . . . . . 3
1.2 O que é este livro? . . . . . . . .
. . . . . . . . . ... . . . . . . . . . . . . . . . 3
1.3 Para quem é este livro? . . . . . .
. . . . . . . . . ... . . . . . . . . . . . . . . . 3
1.4 Quais são os pré-requisitos? . . .
. . . . . . . . . ... . . . . . . . . . . . . . . . 3
1.5 Requisitos de software/hardware. . . . . ... . ... . . . . . . . . . . . . . . . 4
1.6 Estrutura do livro. . . . . . . . . . . . . . . . . . . ... . . . . . . . . . . . . . . . 4
1.7 O que o livro não cobre. . . . . . . . . . ... . . . . . . . . . . . . . . . 5
1.8 Onde obter ajuda. . . . . . . . . . . . . . . . . ... . ... . . . . . . . . . . . 5

2 O que é negociação algorítmica? . ....... . ..... . ..... . ...... .7


2.1 Visão geral. . . . . . . . . . . . .
. . . . . . . . . ... . ... . . . . . . . . . . . 7
2.1.1 Vantagens . . . . . . . .
. . . . . . . . . ... . ... . . . . . . . . . . . 7
2.1.2 Desvantagens . . . . . . .
. . . . . . . . . ... . . . . . . . . . . . . . . . 8
2.2 Método científico. . . . . . . . .
. . . . . . . . . ... . . . . . . . . . . . . . . . 9
2.3 Por que Python? . . . . . . . . . . .
. . . . . . . . . ... . ... . . . . . . . . . . . 9
2.4 Os comerciantes varejistas ainda podem competir? . . . . . . . ... . . . . . . . . . . . . . . . 10
2.4.1 Vantagens de negociação. . . . . . . . . . . . ... . . . . . . . . . . . . . . . 10
2.4.2 Gestão de Riscos. . . . . . . . . . . . . ... . ... . . . . . . . . . . . 11
2.4.3 Relações com Investidores. . . . . . . . . . . . . ... . . . . . . . . . . . . . . . 11
2.4.4 Tecnologia . . . . . . . . . . . . . . . . . ... . ... . . . . . . . . . . . 11

II Sistemas de Negociação 13

3 Backtesting bem-sucedido. ..... . ...... ...... . ..... . ..... . . 15


3.1 Por que estratégias de backtest? . . . . . . . . ... . ... . . . . . . . . . . . . . . . 15
3.2 Vieses de backtesting. . . . . . . . . . . . . . . . . ... . . . . . . . . . . . . . . . 16
3.2.1 Viés de otimização. . . . . . . . . . . . . ... . . . . . . . . . . . . . . . 16
3.2.2 Viés de previsão. . . . . . . . . . . . . . ... . ... . . . . . . . . . . . 16
3.2.3 Viés de sobrevivência. . . . . . . . . . . . . . ... . . . . . . . . . . . . . . . 17
3.2.4 Viés cognitivo. . . . . . . . . . . . . . . ... . ... . . . . . . . . . . . 17
3.3 Problemas de câmbio. . . . . . . . . . . . . . . . . . . ... . ... . . . . . . . . . . . 18
3.3.1 Tipos de pedidos. . . . . . . . . . . . . . . . . . . . ... . . . . . . . . . . . 18
3.3.2 Consolidação de preços. . . . . . . . . . . . ... . . . . . . . . . . . . . . . 18
3.3.3 Negociação Forex e ECNs. . . . . . . . . ... . ... . . . . . . . . . . . 19
3.3.4 Restrições de curto-circuito. . . . . . . . ... . ... . . . . . . . . . . . . . . . 19
3.4 Custos de transação. . . . . . . . . . . . . . . . . ... . . . . . . . . . . . . . . . 19
3.4.1 Comissão . . . . . . . . . . . . . . . . . . . . . ... . . . . . . . . . . . 19
3.4.2 Deslizamento. . . . . . . . . . . . . . . . . . . ... . ... . . . . . . . . . . . 19
3.4.3 Impacto no mercado. . . . . . . . . . . . . . . ... . ... . . . . . . . . . . . 20
3.5 Backtesting vs Realidade. . . . . . . . . . . ... . ... . . . . . . . . . . . . . . . 20

4 Execução automatizada. ..... . ..... . ...... . ..... . ..... . . 21


4.1 Plataformas de Backtesting. . . . . . . . . . . ... . ... . . . . . . . . . . . . . . . 21

1
Machine Translated by Google

4.1.1 Programação . . . . . . . . . . . ... . . ... . . . . . . . . . . . . . . . 22


4.1.2 Ferramentas de Pesquisa. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
4.1.3 Backtesting orientado a eventos. . . . . . . . . . . . . . . . . . . . . . . . . . . 23
4.1.4 Latência. . . . . . . . . . . . . . ... . ... . . . . . . . . . . . . . . . . 23
4.1.5 Opções de idioma. . . . . . . . ... . ... . . . . . . . . . . . . . . . . 23
4.1.6 Ambientes de Desenvolvimento Integrados. ... . . . . . . . . . . . . . . . . 24
4.2 Colocation. . . . . . . . . . . . . . . . . ... . . . . . . . . . . . . . . . . . . . . 26
4.2.1 Área de trabalho doméstica. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
4.2.2 VPS . . . . . . . . . . . . . . . . . . . . ... . . . . . . . . . . . . . . . . 27
4.2.3 Troca . . . . . . . . . . . . . . . . . ... . . . . . . . . . . . . . . . . 27

5 ideias de estratégias de sourcing. . ..... . ..... . ...... . ..... . . . . . . 29


5.1 Identificando suas preferências pessoais para negociação. . . . . . . . . . . . . . . 29
5.2 Obtendo ideias de negociação algorítmica. . ... . . . . . . . . . . . . . . . . . . . . 30
5.2.1 Livros didáticos. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
5.2.2 A Internet. . . . . . . . . . . . . . . ... . . . . . . . . . . . . . . . . 31
5.2.3 Literatura do periódico. . . . . . . . ... . . . . . . . . . . . . . . . . . . . . 33
5.2.4 Pesquisa Independente. . . . . . . . . . ... . . . . . . . . . . . . . . . . 33
5.3 Avaliação de estratégias de negociação. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
5.4 Obtenção de dados históricos. . . . ... . ... . . . . . . . . . . . . . . . . . . . . 35

III Desenvolvimento de Plataforma de Dados 39

6 Instalação de software . . . . ..... ..... .


. ...... . ..... . . . . . . 41
6.1 Escolha do sistema operacional. . . . . .
. ... . ... . . . . . . . . . . . . . . . . . 41
6.1.1 Microsoft Windows . . . . . .
. ... . ... . . . . . . . . . . . . . . . . . 41
6.1.2 Mac OSX . . . . . . . . . . .
. . . . . ... . .. . . . . . . . . . . . . . . 41
6.1.3 Linux . . . . . . . . . . . . .
. . . . . ... . .. . . . . . . . . . . . . . . 42
6.2 Instalando um ambiente Python no Ubuntu Linux. . . . . . . . . . . . . . . . . 42
6.2.1 Python . . . . . . . . . . . . . . . . . . ... . . . . . . . . . . . . . . . . 43
6.2.2 NumPy, SciPy e Pandas. . . . . . . ... . . . . . . . . . . . . . . . . 43
6.2.3 Statsmodels e Scikit-Learn. . ... . ... . . . . . . . . . . . . . . . . 44
6.2.4 PyQt, IPython e Matplotlib. ... . ... . . . . . . . . . . . . . . . . 44
6.2.5 IbPy e estação de trabalho do Trader. . . . . . . . . . . . . . . . . . . . . . . . . 45

7 Armazenamento de dados financeiros . . . . . . . . . .....


. ...... . ..... . . . . . . 47
.
7.1 Bancos de Dados Mestres de Valores Mobiliários.. . . . . ...
. . . . . . . . . . . . . . . . . . . . 47
7.2 Conjuntos de dados financeiros. . . . . . . . . . . . ...
. . . . . . . . . . . . . . . . . . . . 48
7.3 Formatos de armazenamento. . . . . . . . . . . . . ...
. . . . . . . . . . . . . . . . . . . . 48
7.3.1 Armazenamento de arquivo simples. . . .
. . . . . ...
. . . . . . . . . . . . . . . . . . . . 48
7.3.2 Armazenamentos de documentos/NoSQL. . . . ...
... . . . . . . . . . . . . . . . . . 49
7.3.3 Sistemas de gerenciamento de banco de dados relacional. . . . . . . . . . . . . . . . . . 49
7.4 Estrutura de dados históricos. . . . ... . ... . . . . . . . . . . . . . . . . . . . . 49
7.5 Avaliação da precisão dos dados. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
7.6 Automação . . . . . . . . . . . . . . . . ... . . . . . . . . . . . . . . . . . . . . 51
7.7 Disponibilidade de dados. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
7.8 MySQL para Mestres de Valores Mobiliários. . . . . . . . . . ... . . . . . . . . . . . . . . . . 51
7.8.1 Instalando o MySQL . . . . . . . . . . . . ... . . . . . . . . . . . . . . . . 51
7.8.2 Configurando o MySQL . . . . . . . . . . . ... . . . . . . . . . . . . . . . . 51
7.8.3 Projeto de esquema para ações EOD. . . . ... . . . . . . . . . . . . . . . . 52
7.8.4 Conectando ao Banco de Dados . . . . . . . ... . . . . . . . . . . . . . . . . 54
7.8.5 Usando um mapeador objeto-relacional. . . . . . . . . . . . . . . . . . . . . 54
7.9 Recuperando Dados do Mestre de Valores Mobiliários. . . . . . . . . . . . . . . . . . . . . 59

8 Processamento de dados financeiros. . . . . . . ...... ...... . ..... . . . . . . 61


8.1 Classificação de Mercado e Instrumentos. . ... . . . . . . . . . . . . . . . . . . . . 61
Machine Translated by Google

8.1.1 Mercados . . . . . . . . .. . . . . . . . . . ... . ... . . . . . . . . . . . 61


8.1.2 Instrumentos . . . . . . .. . . . . . . . . . . . . . ... . . . . . . . . . . . 61
8.1.3 Dados Fundamentais. . .. . . . . . . . . . ... . ... . . . . . . . . . . . 62
8.1.4 Dados não estruturados. . .. . . . . . . . . . ... . . . . . . . . . . . . . . . 62
8.2 Frequência de Dados. . . . . . .. . . . . . . . . . ... . . . . . . . . . . . . . . . 63
8.2.1 Dados semanais e mensais. . . . . . . . . ... . . . . . . . . . . . . . . . 63
8.2.2 Dados diários. . . . . . . . . . . . . . . . . ... . . . . . . . . . . . . . . . 63
8.2.3 Barras intradiárias. . . . . . . . . . . . . . . . ... . ... . . . . . . . . . . . 63
8.2.4 Dados do livro de ordens e ticks. . . . . . . . ... . ... . . . . . . . . . . . 63
8.3 Fontes de Dados. . . . . . . . . . . . . . . ... . ... . . . . . . . . . . . . . . . 64
8.3.1 Fontes gratuitas. . . . . . . . . . . . . . . . . . . . ... . . . . . . . . . . . 64
8.3.2 Fontes comerciais. . . . . . . . . . . . . . . . ... . . . . . . . . . . . 65
8.4 Obtenção de Dados. . . . . . . . . . . . . . . ... . ... . . . . . . . . . . . . . . . 66
8.4.1 Yahoo Finanças e Pandas. . . . . . . . ... . ... . . . . . . . . . . . 66
8.4.2 Quandl e Pandas. . . . . . . . . . . . ... . ... . . . . . . . . . . . 67
8.4.3 DTN IQFeed. . . . . . . . . . . . . . . . . . . . ... . . . . . . . . . . . 72
8.5 Limpeza de dados financeiros. . . . . . . . . . ... . ... . . . . . . . . . . . . . . . 74
8.5.1 Qualidade dos dados. . . . . . . . . . . . . . . . ... . . . . . . . . . . . . . . . 74
8.5.2 Contratos Futuros Contínuos. . . . . . . ... . . . . . . . . . . . . . . . 74

Modelagem IV 79

9 Aprendizagem Estatística . . . ..... . ...... ...... . ..... . ..... . . 81


9.1 O que é Aprendizagem Estatística? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
9.1.1 Predição e Inferência. . . . . . . . . . ... . ... . . . . . . . . . . . 81
9.1.2 Modelos paramétricos e não paramétricos. ... . . . . . . . . . . . . . . . 82
9.1.3 Aprendizagem supervisionada e não supervisionada. . ... . ... . . . . . . . . . . . 83
9.2 Técnicas . . . . . . . . . . . . . . . . . . . . . ... . ... . . . . . . . . . . . 83
9.2.1 Regressão . . . . . . . . . . . . . . . . . . . .. . ... . . . . . . . . . . . 83
9.2.2 Classificação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
9.2.3 Modelos de séries temporais. . . . . . . . . . . . . . . . ... . . . . . . . . . . . 84

10 Análise de Séries Temporais. . . . . . . . ...... ...... . ..... . ..... . . 87


10.1 Teste de Reversão à Média. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
10.1.1 Teste Dickey-Fuller Aumentado (ADF). . . . . . . . . . . . . . . . . . . . 88
10.2 Teste de estacionariedade. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
10.2.1 Expoente de Hurst. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
10.3 Cointegração. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
10.3.1 Teste Dickey-Fuller Aumentado Cointegrado . . . . . . . . . . . . . . . . . 91
10.4 Por que testes estatísticos? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96

11 Previsão. . ..... . ..... . ...... ...... . ..... . ..... . . 97


11.1 Medindo a precisão da previsão. . . . . ... . . . . . . . . . . . . . . . . . . . 97
11.1.1 Taxa de acerto. . . . . . . . . . . . . . . . . . . ... . . . . . . . . . . . . . . . 97
11.1.2 Matriz de confusão. . . . . . . . . . . . . . ... . . . . . . . . . . . . . . . 98
11.2 Escolha do fator. . . . . . . . . . . . . . . . ... . ... . . . . . . . . . . . . . . . 98
11.2.1 Fatores de preço defasados e volume. ... . ... . . . . . . . . . . . . . . . 98
11.2.2 Fatores externos. . . . . . . . . . ... . ... . . . . . . . . . . . . . . . 99
11.3 Modelos de Classificação. . . . . . . . . . . . ... . ... . . . . . . . . . . . . . . . 99
11.3.1 Regressão Logística. . . . . . . . . ... . ... . . . . . . . . . . . . . . . 99
11.3.2 Análise Discriminante. . . . . . . ... . ... . . . . . . . . . . . . . . . 100
11.3.3 Máquinas de Vetores de Suporte. . . . . . . . . ... . . . . . . . . . . . . . . . 100
11.3.4 Árvores de decisão e florestas aleatórias. . . ... . ... . . . . . . . . . . . 101
11.3.5 Análise de Componentes Principais. . ... . ... . . . . . . . . . . . . . . . 101
11.3.6 Qual previsor? . . . . . . . . . . . . . ... . ... . . . . . . . . . . . 101
Machine Translated by Google

11.4 Previsão do movimento do índice de ações. . . . . . . . . . . . . . . . . . . . . . . . . . 103


11.4.1 Implementações em Python. . . . . ... . . . . . . . . . . . . . . . . . . . . 103
11.4.2 Resultados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106

V Gestão de Desempenho e Riscos 107

12 Medição de desempenho . . . . . . ..... . ...... ...... . . . . . . 109


12.1 Análise de Comércio. . . . . . . . . . . . . . ... . . . . . . . . . . . . . . . . . . . . 110
12.1.1 Estatísticas resumidas. . . . . . . . ... . ... . . . . . . . . . . . . . . . . 110
12.2 Estratégia e Análise de Portfólio. ... . . . . . . . . . . . . . . . . . . . . . . . . 111
12.2.1 Análise de Devoluções. . . . . . . . . ... . ... . . . . . . . . . . . . . . . . 111
12.2.2 Análise de Risco/Recompensa. . . . . . ... . . . . . . . . . . . . . . . . . . . . 112
12.2.3 Análise de rebaixamento. . . . . . . ... . ... . . . . . . . . . . . . . . . . 117

13 Gestão de Riscos e Dinheiro . . . . ..... . ...... . ..... . . . . . . 119


13.1 Fontes de Risco. . . . . . . . . . . . . . ... . . . . . . . . . . . . . . . . . . . . 119
13.1.1 Risco de estratégia. . . . . . . . . . . ... . . . . . . . . . . . . . . . . . . . . 119
13.1.2 Risco de portfólio. . . . . . . . . . . ... . . . . . . . . . . . . . . . . . . . . 120
13.1.3 Risco de contraparte. . . . . . . . ... . ... . . . . . . . . . . . . . . . . 120
13.1.4 Risco Operacional. . . . . . . . . ... . . . . . . . . . . . . . . . . . . . . 120
13.2 Gestão de dinheiro. . . . . . . . . . . ... . ... . . . . . . . . . . . . . . . . 121
13.2.1 Critério de Kelly. . . . . . . . . . ... . ... . . . . . . . . . . . . . . . . 121
13.3 Gestão de Riscos. . . . . . . . . . . . ... . ... . . . . . . . . . . . . . . . . 123
13.3.1 Valor em Risco. . . . . . . . . . . ... . . . . . . . . . . . . . . . . . . . . 123
13.4 Vantagens e Desvantagens. ... . ... . . . . . . . . . . . . . . . . . . . . 124

VI Negociação Automatizada 127

14 Implementação de mecanismo de negociação orientado a eventos . . . . . . ..... . . . . . . 129


14.1 Software orientado a eventos. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129
14.1.1 Por que um Backtester orientado a eventos? . . . . . . . . . . . . . . . . . . . . . . 130
14.2 Objetos Componentes. . . . . . . . . . . ... . ... . . . . . . . . . . . . . . . . 130
14.2.1 Eventos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131
14.2.2 Manipulador de dados. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134
14.2.3 Estratégia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140
14.2.4 Portfólio . . . . . . . . . . . . . . ... . . . . . . . . . . . . . . . . . . . . 142
14.2.5 Manipulador de execução. . . . . . . . ... . ... . . . . . . . . . . . . . . . . 150
14.2.6 Backtest . . . . . . . . . . . . . . ... . ... . . . . . . . . . . . . . . . . 152
14.3 Execução orientada a eventos. . . . . . . . . ... . . . . . . . . . . . . . . . . . . . . 155

15 Implementação da estratégia de negociação. . ...... ...... . ..... . . . . . . 163


15.1 Estratégia de Crossover de Média Móvel . . . ... . . . . . . . . . . . . . . . . . . . . 163
15.2 Previsão de negociação do S&P500. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 168
15.3 Negociação de pares de ações com reversão à média. . . . . . . . . . . . . . . . . . . . . . . . . 172
15.4 Desempenho de plotagem. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 179

16 Otimização de Estratégia . . . ..... . ..... . ...... . ..... . . . . . . 181


16.1 Otimização de parâmetros. . . . . . . . . ... . . . . . . . . . . . . . . . . . . . . 181
16.1.1 Quais parâmetros otimizar? . . . . ... . . . . . . . . . . . . . . . . 181
16.1.2 A otimização é cara. . . . ... . . . . . . . . . . . . . . . . . . . . 182
16.1.3 Sobreajuste. . . . . . . . . . . . ... . ... . . . . . . . . . . . . . . . . 182
16.2 Seleção de modelo. . . . . . . . . . . . . . ... . . . . . . . . . . . . . . . . . . . . 183
16.2.1 Validação cruzada. . . . . . . . . ... . . . . . . . . . . . . . . . . . . . . 183
16.2.2 Pesquisa em grade. . . . . . . . . . . . ... . ... . . . . . . . . . . . . . . . . 189
16.3 Otimizando Estratégias. . . . . . . . . . ... . . . . . . . . . . . . . . . . . . . . 191
Machine Translated by Google

16.3.1 Pares de reversão da média intradiária. . . . . . . . . . . . . . . . . . . . . . . . 191


16.3.2 Ajuste de parâmetros. . . . . . . . . . . ... . . . . . . . . . . . . . . . 191
16.3.3 Visualização . . . . . . . . . . . . ... . ... . . . . . . . . . . . . . . . 194
Machine Translated by Google

6
Machine Translated by Google

Limite de responsabilidade/isenção de responsabilidade de

Garantia

Embora o autor tenha feito o máximo de esforços na preparação deste livro, ele não faz nenhuma representação ou garantia quanto à
precisão ou integridade do conteúdo deste livro e especificamente se isenta de quaisquer garantias implícitas de comercialização ou
adequação a uma finalidade específica.
A obra é vendida sob a condição de que o autor não se dedique à prestação de serviços profissionais e não seja responsabilizado por danos
decorrentes da mesma. Caso necessite de aconselhamento profissional ou de outra assistência especializada, deverá procurar os serviços
de um profissional competente.

eu
Machine Translated by Google

ii
Machine Translated by Google

Parte I

Apresentando a Negociação Algorítmica

1
Machine Translated by Google
Machine Translated by Google

Capítulo 1

Introdução ao livro

1.1 Introdução ao QuantStart

A QuantStart foi fundada por Michael Halls-Moore em 2010 para ajudar analistas quantitativos juniores (QAs) a encontrar
emprego em meio ao cenário econômico difícil. Desde então, o site evoluiu e se tornou um recurso substancial para finanças
quantitativas. O site agora se concentra em negociação algorítmica, mas também discute desenvolvimento quantitativo, tanto
em Python quanto em C++.
Desde março de 2010, a QuantStart ajudou mais de 200.000 visitantes a aprimorar suas habilidades em
finanças quantitativas. Você pode entrar em contato com a QuantStart enviando um e-mail para mike@[Link].

1.2 O que é este livro?

"Sucesso em Negociação Algorítmica" foi escrito para ensinar traders discricionários de varejo e profissionais de negociação,
com habilidades básicas de programação, a criar sistemas de negociação algorítmica totalmente automatizados, lucrativos e
robustos usando a linguagem de programação Python. O livro descreve a natureza de um sistema de negociação algorítmica,
como obter e organizar dados financeiros, o conceito de backtesting e como implementar um sistema de execução. O livro foi
projetado para ser extremamente prático, com exemplos abundantes de código Python ao longo do livro para demonstrar os
princípios e a prática da negociação algorítmica.

1.3 Para quem é este livro?

Este livro foi escrito tanto para traders de varejo quanto para quants profissionais que têm alguma experiência básica em
programação e desejam aprender a aplicar linguagens e bibliotecas modernas à negociação algorítmica. Ele foi elaborado para
aqueles que gostam de autoestudo e podem aprender por meio de exemplos. O livro é destinado a pessoas interessadas em
programação e implementação, pois acredito que o verdadeiro sucesso na negociação algorítmica advém da compreensão
completa dos detalhes da implementação.

Traders quantitativos profissionais também acharão o conteúdo útil. A exposição a novas bibliotecas e métodos de
implementação pode levar a uma execução mais otimizada ou a backtests mais precisos.

1.4 Quais são os pré-requisitos?

O livro é relativamente autocontido, mas pressupõe familiaridade com os fundamentos da negociação em um ambiente
discricionário. Não requer amplo conhecimento de programação, mas pressupõe-se familiaridade básica com uma linguagem
de programação. Você deve estar ciente de conceitos elementares de programação, como declaração de variáveis, controle de
fluxo (if-else) e looping (for/while).
Algumas das estratégias de negociação utilizam técnicas estatísticas de aprendizado de máquina. Além disso, as seções
de otimização de portfólio/estratégia fazem uso extensivo de mecanismos de busca e otimização.

3
Machine Translated by Google

Algoritmos. Embora um conhecimento profundo de matemática não seja absolutamente necessário, será fácil entender
como esses algoritmos funcionam em um nível conceitual.
Se você está enferrujado neste material, ou se ele é novo para você, dê uma olhada na lista de leitura do QuantStart.

1.5 Requisitos de software/hardware


Aplicações de negociação quantitativa em Python podem ser desenvolvidas em Windows, Mac OSX ou Linux.
Este livro é independente de sistema operacional, portanto, é melhor usar qualquer sistema com o qual você se sinta
confortável. No entanto, recomendo Mac OSX ou Linux (eu uso Ubuntu), pois descobri que a instalação e o gerenciamento
de dados são muito mais simples.
Para escrever programas em Python, você só precisa de acesso a um editor de texto (de preferência com destaque
de sintaxe). No Windows, costumo usar o Notepad++. No Mac OSX, uso o SublimeText. No Ubuntu, costumo usar o Emacs,
mas, claro, você pode usar o Vim.
O código neste livro será executado no Python versão 2.7.x (especificamente 2.7.6 no meu
máquina Ubuntu 14.04) e Python 3.4.x (especificamente 3.4.0 na minha máquina Ubuntu 14.04).
Em termos de hardware, você provavelmente vai querer pelo menos 1 GB de RAM, mas mais é sempre melhor.
Você também vai querer usar uma CPU relativamente nova e bastante espaço em disco rígido para dados históricos,
dependendo da frequência com que pretende negociar. Um disco rígido de 200 GB deve ser suficiente para dados menores,
enquanto 1 TB é útil para um amplo universo de símbolos de dados de ticks.

1.6 Estrutura do livro


O livro foi elaborado para criar um conjunto de estratégias de negociação algorítmica, desde a ideia até a execução
automatizada. O processo seguido é descrito abaixo.

• Por que Negociação Algorítmica? - Os benefícios de usar uma abordagem sistemática/algorítmica para negociação
são discutidos em oposição a uma metodologia discricionária. Além disso, são apresentadas as diferentes
abordagens adotadas para a negociação algorítmica.

• Desenvolvimento de Sistema de Negociação - O processo de desenvolvimento de um sistema de negociação


algorítmica é abordado, desde a hipótese até a negociação ao vivo e avaliação contínua.

• Design de Sistema de Negociação - Os componentes que compõem um sistema de negociação algorítmica são
abordados, em particular, geração de sinais, gestão de risco, mensuração de desempenho, dimensionamento/
alavancagem de posições, otimização e execução de portfólio.

• Ambiente do Sistema de Negociação - O procedimento de instalação de todo o software Python é realizado e os


dados históricos são obtidos, limpos e armazenados em um sistema de banco de dados local.

• Análise de Séries Temporais - Diversos métodos de séries temporais são utilizados para previsão, reversão à média,
identificação de momentum e volatilidade. Esses métodos estatísticos, posteriormente, formam a base das
estratégias de negociação.

• Otimização - Algoritmos de otimização/busca são discutidos e exemplos de como eles são


aplicam-se à otimização de estratégia são consideradas.

• Medição de Desempenho - Implementações para várias medidas de risco/recompensa e


outras métricas de desempenho são descritas em detalhes.

• Gestão de Riscos - São descritas diversas fontes de risco que afetam um sistema de negociação algorítmica e são
fornecidos métodos para mitigar esse risco.

• Implementação de estratégias de negociação - São fornecidos exemplos de estratégias de negociação baseadas em


medidas estatísticas e indicadores técnicos, juntamente com detalhes de como otimizar um portfólio dessas
estratégias.

• Execução - Conexão com uma corretora, criação de uma negociação automatizada baseada em eventos
infraestrutura e ferramentas de monitoramento/resiliência são todas discutidas.
Machine Translated by Google

1.7 O que o livro não cobre

Este não é um livro para iniciantes em negociação discricionária, nem um livro repleto de estratégias de
negociação de "análise técnica". Se você nunca realizou nenhuma negociação (discricionária ou não), sugiro a
leitura de alguns dos livros da lista de leitura da QuantStart.
Também não é um livro tutorial de Python, embora, mais uma vez, a lista de leitura do QuantStart possa ser
consultada. Embora todos os esforços tenham sido feitos para introduzir o código Python conforme cada exemplo
justifica, uma certa familiaridade com Python será extremamente útil.

1.8 Onde obter ajuda


O melhor lugar para procurar ajuda é a lista de artigos no [Link], disponível em [Link]/articles ou
entrando em contato comigo pelo e-mail mike@[Link]. Escrevi mais de 140 artigos sobre finanças
quantitativas (e negociação algorítmica em particular), então você pode se atualizar lendo alguns deles.
Também quero agradecer por comprar o livro e me ajudar a escrever mais conteúdo — é muito importante.
Boa sorte com suas estratégias algorítmicas! Agora, vamos às negociações...
Machine Translated by Google

6
Machine Translated by Google

Capítulo 2

O que é negociação algorítmica?

Negociação algorítmica, conforme definida aqui, é o uso de um sistema automatizado para a realização de negociações, que
são executadas de maneira predeterminada por meio de um algoritmo específico, sem qualquer intervenção humana. Esta
última ênfase é importante. Estratégias algorítmicas são projetadas antes do início da negociação e são executadas sem a
intervenção discricionária de traders humanos.

Neste livro, "negociação algorítmica" refere-se à prática de negociação automatizada, sistemática e


quantitativa, que serão tratadas como sinônimos para os fins deste texto. No setor financeiro, "negociação
algorítmica" geralmente se refere a uma classe de algoritmos de execução (como o Preço Médio Ponderado por
Volume, VWAP) usados para otimizar os custos de ordens de negociação maiores.

2.1 Visão geral

A negociação algorítmica difere substancialmente da negociação discricionária. Nesta seção, serão delineadas
as vantagens e desvantagens de uma abordagem sistemática.

2.1.1 Vantagens
A negociação algorítmica possui inúmeras vantagens sobre métodos discricionários.

Avaliação Histórica

A vantagem mais importante na criação de uma estratégia automatizada é que seu desempenho pode ser
apurado com base em dados históricos de mercado, que (espera-se) sejam representativos de dados futuros de
mercado. Esse processo é conhecido como backtesting e será discutido em detalhes neste livro. O backtesting
permite determinar as propriedades estatísticas (anteriores) da estratégia, fornecendo insights sobre a
probabilidade de uma estratégia ser lucrativa no futuro.

Eficiência

A negociação algorítmica é substancialmente mais eficiente do que uma abordagem discricionária. Com um
sistema totalmente automatizado, não há necessidade de um indivíduo ou equipe monitorar constantemente os
mercados em busca de movimentos de preços ou notícias. Isso libera tempo para que o(s) desenvolvedor(es) da
estratégia de negociação realize(em) mais pesquisas e, assim, dependendo das restrições de capital,
implemente(m) mais estratégias em um portfólio.
Além disso, ao automatizar o processo de gestão de risco e dimensionamento de posições, considerando
uma série de estratégias sistemáticas, é necessário ajustar automaticamente a alavancagem e os fatores de risco
de forma dinâmica, respondendo diretamente à dinâmica do mercado em tempo real. Isso não é possível em um
mundo discricionário, pois o trader não consegue calcular o risco continuamente e precisa fazer pausas ocasionais
no monitoramento do mercado.

7
Machine Translated by Google

Nenhuma entrada discricionária

Uma das principais vantagens de um sistema de negociação automatizado é que (teoricamente) não há entrada
discricionária subsequente. Isso se refere à modificação das negociações no momento da execução ou enquanto
o investidor está em uma posição. Medo e ganância podem ser motivadores avassaladores ao realizar
negociações discricionárias. No contexto da negociação sistemática, é raro que a entrada discricionária melhore
o desempenho de uma estratégia.
Dito isso, é certamente possível que estratégias sistemáticas deixem de ser lucrativas devido a mudanças de regime ou
outros fatores externos. Nesse caso, é necessário discernimento para modificar os parâmetros da estratégia ou descontinuá-la.
Observe que esse processo ainda não interfere nas operações individuais.

Comparação

Estratégias sistemáticas fornecem informações estatísticas sobre o desempenho histórico e atual.


Em particular, é possível determinar o crescimento do patrimônio, o risco (em diversas formas), a frequência de negociação e
uma miríade de outras métricas. Isso permite uma comparação direta entre diversas estratégias, permitindo a alocação ideal do
capital. Isso contrasta com o caso em que apenas as informações de lucros e perdas (P&L) são monitoradas em um ambiente
discricionário, uma vez que mascaram o risco potencial de rebaixamento.

Frequências mais altas

Este é um corolário da vantagem de eficiência discutida acima. Estratégias que operam em frequências mais
altas em diversos mercados tornam-se possíveis em um ambiente automatizado. De fato, algumas das estratégias
de negociação mais lucrativas operam no domínio de frequência ultra-alta em dados de ordens limitadas. Essas
estratégias são simplesmente impossíveis de serem executadas por um ser humano.

2.1.2 Desvantagens
Embora as vantagens da negociação algorítmica sejam inúmeras, há algumas desvantagens.

Requisitos de capital

A negociação algorítmica geralmente exige uma base de capital muito maior do que a utilizada para negociação discricionária no
varejo. Isso se deve simplesmente ao fato de que existem poucas corretoras que oferecem execução automatizada de negociações
e que não exigem valores mínimos elevados para contas. A corretora mais prolífica no setor de automação de varejo é a Interactive
Brokers, que exige um saldo de conta de US$ 10.000. A situação está mudando lentamente, especialmente porque outras corretoras
estão permitindo a conexão direta via protocolo FIX. Além disso, os requisitos do Pattern Day Trader, conforme definidos pela
Securities and Exchange Commission (SEC), exigem a manutenção de um mínimo de US$ 25.000 em capital próprio em conta o
tempo todo, em determinadas situações de margem. Essas questões serão discutidas detalhadamente na seção sobre Execução.

Além disso, obter feeds de dados para estratégias quantitativas intradiárias, especialmente se utilizar
contratos futuros, não é barato para o trader de varejo. Feeds intradiários comuns para o varejo costumam custar
entre US$ 300 e US$ 500 por mês, com feeds comerciais um pouco acima disso. Dependendo das suas
necessidades de latência, pode ser necessário co-localizar um servidor em uma bolsa, o que aumenta os custos
mensais. Para o trader de varejo interdiário, isso não é necessariamente um problema, mas vale a pena
considerar. Há também recursos auxiliares, como uma conexão de internet mais robusta e computadores desktop
potentes (e, portanto, caros) a serem adquiridos.

Programação/Expertise Científica

Embora existam certas plataformas de negociação sistemática, como Quantopian, QuantConnect e TradeSta-tion,
que aliviam a maior parte da dificuldade de programação, algumas ainda não oferecem (até o momento da
redação deste texto) suporte à execução em tempo real. A TradeStation é claramente uma exceção neste caso.
Portanto, é um requisito para o trader algorítmico ser relativamente proficiente tanto em programação quanto em
modelagem científica.
Machine Translated by Google

Tentei demonstrar uma ampla variedade de estratégias, cujas bases são quase sempre fundamentadas de forma
simples e compreensível. No entanto, se você possui habilidades em modelagem numérica, provavelmente achará mais
fácil utilizar os métodos estatísticos de séries temporais apresentados na seção "Modelagem". A maioria das técnicas
demonstradas já foi implementada em bibliotecas Python externas, o que nos economiza uma quantidade substancial
de trabalho de desenvolvimento. Assim, somos "reduzidos" a unir nossas bibliotecas de análise e execução de dados
para produzir um sistema de negociação algorítmica.

2.2 Método Científico


A concepção das estratégias de negociação neste livro baseia-se exclusivamente nos princípios do método científico. O
processo do método científico começa com a formulação de uma pergunta, baseada em observações. No contexto de
negociação, um exemplo seria "Existe uma relação entre o SPDR Gold Shares ETF (GLD) e o Market Vectors Gold
Miners ETF (GDX)?". Isso permite formular uma hipótese que possa explicar o comportamento observado. Neste caso,
uma hipótese pode ser "O spread entre GLD e GDX apresenta comportamento de reversão à média?".

A hipótese nula é que não há comportamento de reversão à média, ou seja, o spread de preços é um passeio aleatório.

Após a formulação de uma hipótese, cabe ao cientista refutar a hipótese nula e demonstrar que, de fato, há um
comportamento de reversão à média. Para isso, é necessário definir uma previsão. Voltando ao exemplo do GLD-GDX,
a previsão é de que a série temporal que representa o spread dos dois ETFs seja estacionária. Para comprovar ou
refutar a hipótese, a previsão está sujeita a testes. No caso do GLD-GDX, isso significa aplicar testes de estacionariedade
estatística, como os testes Dickey-Fuller Aumentado, Expoente de Hurst e Razão de Variância (descritos em detalhes
nos capítulos subsequentes).

Os resultados do procedimento de teste fornecerão uma resposta estatística sobre se a hipótese nula pode ser
rejeitada a um determinado nível de confiança. Se a hipótese nula não puder ser rejeitada, o que implica que não houve
relação discernível entre os dois ETFs, ainda é possível que a hipótese seja (parcialmente) verdadeira. Um conjunto
maior de dados e a incorporação de informações adicionais (como um terceiro ETF afetando o preço) também podem
ser testados. Este é o processo de análise. Frequentemente, isso leva à rejeição da hipótese nula após o refinamento.

A principal vantagem de usar o método científico para o design de estratégias de negociação é que, se a estratégia
"quebrar" após um período anterior de lucratividade, é possível revisitar a hipótese inicial e reavaliá-la, o que pode levar
a uma nova hipótese que leve à recuperação da lucratividade de uma estratégia.

Isso contrasta diretamente com a abordagem de mineração de dados ou caixa-preta, na qual uma grande quantidade
de parâmetros ou "indicadores" é aplicada a uma série temporal. Se tal "estratégia" for inicialmente lucrativa e, em
seguida, o desempenho piorar, é difícil (se não impossível) determinar o motivo. Isso frequentemente leva à aplicação
arbitrária de novas informações, indicadores ou parâmetros que podem levar temporariamente à lucratividade, mas
posteriormente levar a uma degradação ainda maior do desempenho. Nesse caso, a estratégia geralmente é descartada
e o processo de "pesquisa" continua.
Neste livro, todas as estratégias de negociação serão desenvolvidas com uma abordagem de observação-hipótese.

2.3 Por que Python?


As seções acima descreveram os benefícios da negociação algorítmica e do método científico. Agora é hora de nos
concentrarmos na linguagem de implementação dos nossos sistemas de negociação. Para este livro, escolhi Python.
Python é uma linguagem de alto nível projetada para velocidade de desenvolvimento.
Possui uma ampla gama de bibliotecas para praticamente qualquer tarefa computacional imaginável. Também está
ganhando ampla adoção nas comunidades de gestão de ativos e bancos de investimento.
Aqui estão as razões pelas quais escolhi Python como linguagem para pesquisa de sistemas de negociação
e implementação:

• Aprendizado - Python é extremamente fácil de aprender em comparação com outras linguagens como C++.
Você pode ser extremamente produtivo em Python depois de apenas algumas semanas (alguns dizem dias!) de
Machine Translated by Google

10

uso.

• Bibliotecas - O principal motivo para usar Python é que ele vem com uma variedade impressionante de bibliotecas,
o que reduz significativamente o tempo de implementação e a chance de introduzir bugs em nosso código. Em
particular, usaremos NumPy (operações vetorizadas), SciPy (algoritmos de otimização), pandas (análise de séries
temporais), statsmodel (modelagem estatística), scikit-learn (estatística/aprendizado de máquina), IPython
(desenvolvimento interativo) e matplotlib (visualização).

• Velocidade de Desenvolvimento - Python se destaca em velocidade de desenvolvimento a ponto de alguns


comentarem que é como escrever em "pseudocódigo". A natureza interativa de ferramentas como o IPython torna
a pesquisa estratégica extremamente rápida, sem sacrificar a robustez.

• Velocidade de Execução - Embora não seja tão rápida quanto C++, Python fornece componentes de computação
científica altamente otimizados (via vetorização). Se a velocidade de execução se tornar um problema, pode-se
utilizar Cython e obter velocidades de execução semelhantes às de C, com um pequeno aumento na complexidade
do código.

• Execução de Negociações - Existem plugins Python para corretoras maiores, como a Interactive Brokers (IBypy).
Além disso, o Python pode facilmente utilizar o protocolo FIX quando necessário.

• Custo/Licença - Python é gratuito, de código aberto e multiplataforma. Ele rodará perfeitamente em


Windows, Mac OSX ou Linux.

Embora Python seja extremamente aplicável a quase todas as formas de negociação algorítmica, ele não pode
competir com C (ou linguagens de nível inferior) no campo de negociação de ultra-alta frequência (UHFT).
No entanto, esses tipos de estratégias estão bem fora do escopo deste livro!

2.4 Os comerciantes varejistas ainda podem competir?

É comum, para um trader algorítmico iniciante atuando no varejo, questionar se ainda é possível competir com os grandes
fundos quantitativos institucionais. Nesta seção, argumentaremos que, devido à natureza do ambiente regulatório
institucional, à estrutura organizacional e à necessidade de manter relações com investidores, os fundos sofrem de certas
desvantagens que não afetam os traders algorítmicos de varejo.

As restrições de capital e regulatórias impostas aos fundos levam a certos comportamentos previsíveis, que podem
ser explorados por um trader de varejo. "Big money" movimenta os mercados e, como tal, é possível conceber diversas
estratégias para tirar proveito desses movimentos. Algumas dessas estratégias serão discutidas em capítulos posteriores.
As vantagens comparativas desfrutadas pelo trader algorítmico em relação a muitos fundos maiores serão agora descritas.

2.4.1 Vantagens de negociação


Há muitas maneiras pelas quais um trader de algoritmos de varejo pode competir com um fundo apenas com base em
seu processo de negociação, mas também há algumas desvantagens:

• Capacidade - Um trader de varejo tem maior liberdade para atuar em mercados menores. Ele pode gerar retornos
significativos nesses espaços, mesmo que fundos institucionais não consigam.

• Aglomeração da negociação - Os fundos sofrem com a "transferência de tecnologia", já que a rotatividade de


pessoal pode ser alta. Acordos de Confidencialidade e de Não Concorrência atenuam o problema, mas ainda
levam muitos fundos quantitativos a "perseguir a mesma negociação". O sentimento caprichoso dos investidores
e a "próxima moda" agravam o problema. Os traders de varejo não são obrigados a seguir as mesmas estratégias
e, portanto, podem permanecer sem correlação com os fundos maiores.

• Impacto no mercado - Ao jogar em mercados não OTC de alta liquidez, a baixa base de capital
de contas de varejo reduz substancialmente o impacto no mercado.
Machine Translated by Google

11

• Alavancagem - Um trader de varejo, dependendo de sua estrutura legal, é limitado por regulamentações de margem/
alavancagem. Fundos de investimento privados não sofrem da mesma desvantagem, embora sejam igualmente
limitados do ponto de vista da gestão de risco.

• Liquidez - Ter acesso a uma corretora de primeira linha está fora do alcance do trader de algoritmos de varejo médio.
Eles precisam se "consertar" com uma corretora de varejo como a Interactive Brokers. Consequentemente, há um
acesso reduzido à liquidez em certos instrumentos. O roteamento de ordens de negociação também é menos claro e é
uma maneira pela qual o desempenho da estratégia pode divergir dos backtests.

• Fluxo de notícias do cliente - A desvantagem potencialmente mais importante para o trader de varejo é a falta de acesso
ao fluxo de notícias do cliente de sua corretora principal ou instituição fornecedora de crédito.
Os comerciantes de varejo precisam fazer uso de fontes não tradicionais, como grupos de encontro, blogs, fóruns e
periódicos financeiros de acesso aberto.

2.4.2 Gestão de Riscos


Os traders de algoritmos de varejo costumam adotar uma abordagem diferente à gestão de risco em comparação com os
grandes fundos quantitativos. Muitas vezes, é vantajoso ser "pequeno e ágil" no contexto de risco.
Fundamentalmente, não há um orçamento de gerenciamento de risco imposto ao trader além daquele que ele próprio
impõe, nem há um departamento de conformidade ou gerenciamento de risco que imponha a supervisão.
Isso permite que o comerciante varejista implante metodologias de modelagem de risco personalizadas ou preferenciais, sem
a necessidade de seguir "padrões do setor" (um requisito implícito do investidor).
No entanto, o argumento alternativo é que essa flexibilidade pode levar os traders de varejo a se tornarem "desleixados"
com a gestão de risco. Preocupações com risco podem estar incorporadas ao processo de backtest e execução, sem que se
considere externamente o risco da carteira como um todo. Embora "reflexão profunda" possa ser aplicada ao modelo alfa
(estratégia), a gestão de risco pode não atingir um nível semelhante de consideração.

2.4.3 Relações com Investidores

Investidores externos são a principal diferença entre lojas de varejo e grandes fundos. Isso gera todos os tipos de incentivos
para o fundo maior — questões com as quais o varejista não precisa se preocupar:

• Estrutura de remuneração - No ambiente de varejo, o trader se preocupa apenas com o retorno absoluto. Não há metas
máximas a serem atingidas nem regras de alocação de capital a serem seguidas. Os traders de varejo também podem
sofrer com curvas de patrimônio mais voláteis, já que ninguém está acompanhando seu desempenho e que possa
resgatar capital de seus fundos.

• Regulamentações e relatórios - Além da tributação, há poucas restrições regulatórias em termos de relatórios para o
trader de varejo. Além disso, não há necessidade de fornecer relatórios mensais de desempenho ou "enfeitar" uma
carteira antes do envio de um boletim informativo ao cliente. Isso economiza muito tempo.

• Comparação de benchmarks - Os fundos não são comparados apenas com seus pares, mas também com "benchmarks"
do setor. Para um fundo de ações americano long-only, os investidores desejarão retornos superiores ao S&P 500, por
exemplo. Os traders de varejo não são obrigados da mesma forma a comparar suas estratégias com um benchmark.

• Taxas de performance - A desvantagem de administrar seu próprio portfólio como trader de varejo é a ausência de taxas
de gestão e performance, comuns aos fundos quantitativos de sucesso. Não existe "2 e 20" no varejo!

2.4.4 Tecnologia
Uma área em que o trader varejista tem uma vantagem significativa é na escolha da pilha tecnológica para o sistema de
negociação. O trader não só pode escolher as "melhores ferramentas para o trabalho" conforme achar adequado, como
também não há preocupações com a integração de sistemas legados ou políticas de TI para toda a empresa. Mais recente
Machine Translated by Google

12

Linguagens como Python ou R agora possuem pacotes para construir um sistema de backtesting, execução, risco e
gerenciamento de portfólio de ponta a ponta com muito menos linhas de código (LOC) do que seria necessário em uma
linguagem mais detalhada como C++.
No entanto, essa flexibilidade tem um preço. É preciso construir o stack por conta própria ou terceirizar todo ou parte
dele para fornecedores. Isso é caro em termos de tempo, capital ou ambos. Além disso, o trader precisa depurar todos os
aspectos do sistema de negociação — um processo longo e potencialmente trabalhoso.
Todas as máquinas de pesquisa de desktop e quaisquer servidores colocalizados devem ser pagos diretamente com os
lucros comerciais, pois não há taxas de administração para cobrir despesas.
Concluindo, observa-se que os traders de varejo possuem vantagens comparativas significativas em relação aos
fundos quantitativos de maior porte. Potencialmente, há muitas maneiras de explorar essas vantagens. Capítulos
posteriores discutirão algumas estratégias que utilizam essas diferenças.
Machine Translated by Google

Parte II

Sistemas de negociação

13
Machine Translated by Google
Machine Translated by Google

Capítulo 3

Backtesting bem-sucedido

O backtesting algorítmico exige conhecimento de diversas áreas, incluindo psicologia, matemática, estatística,
desenvolvimento de software e microestrutura de mercado/bolsa. Não conseguiria cobrir todos esses tópicos
em um capítulo, então vou dividi-los em duas ou três partes menores.
O que discutiremos nesta seção? Começarei definindo o backtesting e, em seguida, descreverei os princípios
básicos de sua implementação. Em seguida, elucidarei os vieses que abordamos nos capítulos anteriores.

Nos capítulos subsequentes, examinaremos os detalhes da implementação de estratégias que muitas vezes
são pouco mencionadas ou ignoradas em outros lugares. Também consideraremos como tornar o processo de
backtesting mais realista, incluindo as idiossincrasias de uma bolsa de valores. Em seguida, discutiremos os
custos de transação e como modelá-los corretamente em um cenário de backtesting. Encerraremos com uma
discussão sobre o desempenho de nossos backtests e, por fim, forneceremos exemplos detalhados de
estratégias quantitativas comuns.
Vamos começar discutindo o que é backtesting e por que devemos realizá-lo em nossa negociação
algorítmica.

3.1 Por que estratégias de backtest?


A negociação algorítmica se diferencia de outros tipos de investimento porque, graças à ampla disponibilidade
de dados, podemos prever expectativas sobre o desempenho futuro com mais confiabilidade a partir de
resultados passados. O processo pelo qual isso é realizado é conhecido como backtesting.
Em termos simples, o backtesting é realizado expondo o algoritmo da sua estratégia específica a um fluxo
de dados financeiros históricos, o que leva a um conjunto de sinais de negociação. Cada operação (que aqui
queremos dizer como uma "viagem de ida e volta" de dois sinais) terá um lucro ou prejuízo associado. O
acúmulo desse lucro/prejuízo ao longo da duração do backtest da sua estratégia levará ao lucro e prejuízo totais
(também conhecido como "P&L" ou "PnL"). Essa é a essência da ideia, embora, é claro, "o diabo esteja sempre
nos detalhes"!
Quais são os principais motivos para testar uma estratégia algorítmica?

• Filtragem - Se você se lembra do capítulo anterior sobre Identificação de Estratégias, nosso objetivo na
fase inicial da pesquisa era montar um pipeline de estratégias e, em seguida, filtrar qualquer estratégia
que não atendesse a determinados critérios. O backtesting nos fornece outro mecanismo de filtragem,
pois podemos eliminar estratégias que não atendem às nossas necessidades de desempenho.

• Modelagem - O backtesting nos permite testar (com segurança!) novos modelos de certos fenômenos de mercado, como
custos de transação, roteamento de ordens, latência, liquidez ou outros problemas de microestrutura de mercado.

• Otimização - Embora a otimização de estratégias seja repleta de vieses, o backtesting nos permite
aumentar o desempenho de uma estratégia modificando a quantidade ou os valores dos parâmetros
associados a essa estratégia e recalculando seu desempenho.

15
Machine Translated by Google

16

• Verificação - Nossas estratégias são frequentemente obtidas externamente, por meio do nosso pipeline de estratégias.
O back-testing de uma estratégia garante que ela não tenha sido implementada incorretamente. Embora raramente
tenhamos acesso aos sinais gerados por estratégias externas, frequentemente temos acesso às métricas de
desempenho, como o Índice de Sharpe e as características de Drawdown. Assim, podemos compará-las com a
nossa própria implementação.

O backtesting oferece uma série de vantagens para a negociação algorítmica. No entanto, nem sempre é possível
realizar o backtest de uma estratégia de forma direta. Em geral, à medida que a frequência da estratégia aumenta, torna-se
mais difícil modelar corretamente os efeitos da microestrutura do mercado e das bolsas. Isso leva a backtests menos
confiáveis e, portanto, a uma avaliação mais complexa da estratégia escolhida.
Este é um problema específico em que o sistema de execução é a chave para o desempenho da estratégia, como acontece
com algoritmos de ultra-alta frequência.
Infelizmente, o backtesting é repleto de vieses de todos os tipos e agora os discutiremos em profundidade.

3.2 Vieses de Backtesting


Existem muitos vieses que podem afetar o desempenho de uma estratégia testada em backtest. Infelizmente, esses vieses
tendem a inflar o desempenho em vez de prejudicá-lo. Portanto, você deve sempre considerar um backtest como um limite
superior idealizado para o desempenho real da estratégia. É quase impossível eliminar vieses da negociação algorítmica,
portanto, é nosso trabalho minimizá-los da melhor forma possível para tomar decisões informadas sobre nossas estratégias
algorítmicas.

Existem quatro vieses principais que desejo discutir: Viés de otimização, Viés de previsão,
Viés de sobrevivência e viés cognitivo.

3.2.1 Viés de otimização

Este é provavelmente o viés mais insidioso de todos os vieses de backtest. Envolve o ajuste ou a introdução de parâmetros
de negociação adicionais até que o desempenho da estratégia no conjunto de dados de backtest seja muito atraente. No
entanto, uma vez em vigor, o desempenho da estratégia pode ser significativamente diferente. Outro nome para esse viés é
"ajuste de curva" ou "viés de espionagem de dados".
O viés de otimização é difícil de eliminar, pois estratégias algorítmicas frequentemente envolvem muitos parâmetros.
"Parâmetros", neste caso, podem ser os critérios de entrada/saída, períodos de análise retrospectiva, períodos de média
(ou seja, o parâmetro de suavização da média móvel) ou a frequência de medição da volatilidade.
O viés de otimização pode ser minimizado mantendo o número de parâmetros no mínimo e aumentando a quantidade de
pontos de dados no conjunto de treinamento. Aliás, é preciso ter cuidado com este último, pois pontos de treinamento mais
antigos podem estar sujeitos a um regime anterior (como um ambiente regulatório) e, portanto, podem não ser relevantes
para sua estratégia atual.
Um método para ajudar a mitigar esse viés é realizar uma análise de sensibilidade. Isso significa variar os parâmetros
incrementalmente e traçar uma "superfície" de desempenho. Um raciocínio sólido e fundamental para a escolha dos
parâmetros deve, com todos os outros fatores considerados, levar a uma superfície de parâmetros mais suave. Se você tiver
uma superfície de desempenho muito instável, isso geralmente significa que um parâmetro não está refletindo um fenômeno
e é um artefato dos dados de teste. Há uma vasta literatura sobre algoritmos de otimização multidimensional e esta é uma
área de pesquisa altamente ativa. Não vou me aprofundar nisso aqui, mas tenha isso em mente quando encontrar uma
estratégia com um backtest fantástico!

3.2.2 Viés de previsão

O viés de previsão é introduzido em um sistema de backtesting quando dados futuros são acidentalmente incluídos em um
ponto da simulação onde esses dados não estariam realmente disponíveis. Se estivermos executando o backtest
cronologicamente e atingirmos o ponto de tempo N, o viés de previsão ocorre se os dados forem incluídos para qualquer
ponto N + k, onde k > 0. Erros de viés de previsão podem ser incrivelmente sutis. Aqui estão três exemplos de como o viés
de previsão pode ser introduzido:
Machine Translated by Google

17

• Erros técnicos - Matrizes/vetores em código frequentemente possuem iteradores ou variáveis de índice. Deslocamentos incorretos
desses índices podem levar a um viés de previsão ao incorporar dados em N + k para k diferente de zero.

• Cálculo de Parâmetros - Outro exemplo comum de viés de previsão ocorre ao calcular parâmetros estratégicos ótimos, como em
regressões lineares entre duas séries temporais. Se todo o conjunto de dados (incluindo dados futuros) for usado para calcular
os coeficientes de regressão e, portanto, aplicado retroativamente a uma estratégia de negociação para fins de otimização,
então dados futuros estão sendo incorporados e existe um viés de previsão.

• Máximas/Mínimas - Certas estratégias de negociação utilizam valores extremos em qualquer período de tempo, como a
incorporação de preços máximos ou mínimos em dados OHLC. No entanto, como esses valores máximos/mínimos só podem
ser calculados ao final de um período, um viés de previsão é introduzido se esses valores forem utilizados durante o período
atual. É sempre necessário defasar os valores máximos/mínimos em pelo menos um período em qualquer estratégia de
negociação que os utilize.

Assim como acontece com o viés de otimização, é preciso ter extremo cuidado para evitar sua introdução. Muitas vezes, é o
principal motivo pelo qual as estratégias de negociação apresentam desempenho significativamente inferior aos seus backtests em
"negociações ao vivo".

3.2.3 Viés de sobrevivência


O viés de sobrevivência é um fenômeno particularmente perigoso e pode levar a um desempenho significativamente inflado para certos
tipos de estratégias. Ele ocorre quando as estratégias são testadas em conjuntos de dados que não incluem todo o universo de ativos
anteriores que podem ter sido escolhidos em um determinado momento, mas consideram apenas aqueles que "sobreviveram" até o
momento atual.
Por exemplo, considere testar uma estratégia com uma seleção aleatória de ações antes e depois da quebra do mercado de 2001.
Algumas ações de tecnologia faliram, enquanto outras conseguiram se manter à tona e até prosperaram. Se tivéssemos restringido
essa estratégia apenas às ações que sobreviveram ao período de queda do mercado, estaríamos introduzindo um viés de sobrevivência,
pois elas já demonstraram seu sucesso para nós. Na verdade, este é apenas mais um caso específico de viés de previsão, visto que
informações futuras estão sendo incorporadas à análise passada.

Existem duas maneiras principais de mitigar o viés de sobrevivência em seus backtests de estratégia:

• Conjuntos de Dados Livres de Viés de Sobrevivência - No caso de dados de ações, é possível adquirir conjuntos de dados que
incluem entidades deslistadas, embora não sejam baratos e tendam a ser utilizados apenas por empresas institucionais. Em
particular, os dados do Yahoo Finance NÃO são livres de viés de sobrevivência, e isso é comumente usado por muitos traders
de algoritmos de varejo. Também é possível negociar em classes de ativos que não são propensas ao viés de sobrevivência,
como certas commodities (e seus derivativos futuros).

• Use dados mais recentes - No caso de ações, utilizar um conjunto de dados mais recente reduz a possibilidade de a seleção de
ações escolhida ser ponderada para "sobreviventes", assim como há menos probabilidade de fechamento de capital em
períodos mais curtos. Também é possível começar a construir um conjunto de dados pessoal livre de viés de sobrevivência,
coletando dados do ponto atual em diante.
Após 3 a 4 anos, você terá um conjunto sólido de dados de ações, livres de viés de sobrevivência, com os quais poderá testar
outras estratégias.

Vamos agora considerar certos fenômenos psicológicos que podem influenciar seu desempenho de negociação.
desempenho.

3.2.4 Viés cognitivo


Este fenômeno específico não é frequentemente discutido no contexto de negociação quantitativa. No entanto, é amplamente discutido
em relação a métodos de negociação mais discricionários. Ao criar backtests para um período de 5 anos ou mais, é fácil observar uma
curva de ações com tendência de alta, calcular o retorno anual composto, o índice de Sharpe e até mesmo as características de
rebaixamento e ficar satisfeito com os resultados. Por exemplo, a estratégia pode possuir um máximo relativo
Machine Translated by Google

18

drawdown de 25% e duração máxima de drawdown de 4 meses. Isso não seria atípico para uma estratégia de momentum. É
fácil se convencer de que é fácil tolerar tais períodos de perdas porque o panorama geral é positivo. No entanto, na prática, é
muito mais difícil!
Se ocorrerem quedas históricas de 25% ou mais nos backtests, é muito provável que você veja períodos de queda
semelhantes em negociações reais. Esses períodos de queda são psicologicamente difíceis de suportar. Observei em
primeira mão como pode ser uma queda prolongada em um ambiente institucional, e não é nada agradável – mesmo que os
backtests sugiram que tais períodos ocorrerão.
A razão pela qual chamei isso de "viés" é que, muitas vezes, uma estratégia que, de outra forma, seria bem-sucedida é
impedida de ser negociada durante períodos de drawdown prolongado, o que leva a um desempenho significativamente
inferior ao de um backtest. Assim, mesmo que a estratégia seja algorítmica por natureza, fatores psicológicos ainda podem
ter uma forte influência na lucratividade. A conclusão é garantir que, se você observar drawdowns de uma determinada
porcentagem e duração nos backtests, espere que eles ocorram em ambientes de negociação reais e precisará perseverar
para alcançar a lucratividade novamente.

3.3 Problemas de troca


3.3.1 Tipos de pedidos

Uma escolha que um trader algorítmico deve fazer é como e quando utilizar as diferentes ordens de negociação disponíveis.
Essa escolha geralmente se enquadra no sistema de execução, mas a consideraremos aqui, pois pode afetar significativamente
o desempenho do backtest da estratégia. Existem dois tipos de ordens que podem ser executadas: ordens de mercado e
ordens limitadas.
Uma ordem de mercado executa uma operação imediatamente, independentemente dos preços disponíveis. Portanto,
grandes operações executadas como ordens de mercado frequentemente receberão uma mistura de preços à medida que
cada ordem limite subsequente no lado oposto for executada. Ordens de mercado são consideradas ordens agressivas, pois
quase certamente serão executadas, embora com um custo potencialmente desconhecido.
Ordens limitadas fornecem um mecanismo para a estratégia determinar o pior preço pelo qual a operação será executada,
com a ressalva de que a operação pode não ser executada parcial ou totalmente. Ordens limitadas são consideradas ordens
passivas, pois frequentemente não são executadas, mas quando o são, o preço é garantido. O conjunto de ordens limitadas
de uma bolsa individual é conhecido como livro de ordens limitadas, que é essencialmente uma fila de ordens de compra e
venda em determinados tamanhos e preços.
Ao realizar backtests, é essencial modelar corretamente os efeitos do uso de ordens de mercado ou limitadas.
Especialmente para estratégias de alta frequência, os backtests podem superar significativamente as negociações ao vivo se
os efeitos do impacto do mercado e do livro de ordens limite não forem modelados com precisão.

3.3.2 Consolidação de preços

Existem questões específicas relacionadas às estratégias de backtesting ao utilizar dados diários na forma de números de
Abertura-Máxima-Mínima-Fechamento (OHLC), especialmente para ações. Observe que este é exatamente o formato de
dados fornecido pelo Yahoo Finanças, que é uma fonte de dados muito comum para traders algorítmicos de varejo!

Conjuntos de dados baratos ou gratuitos, embora sofram de viés de sobrevivência (que já discutimos acima), também
são frequentemente feeds de preços compostos de várias bolsas. Isso significa que os pontos extremos (ou seja, abertura,
fechamento, máxima e mínima) dos dados são muito suscetíveis a valores "diferentes" devido a ordens pequenas em bolsas
regionais. Além disso, esses valores às vezes também são mais propensos a serem erros de tick que ainda não foram
removidos do conjunto de dados.
Isso significa que, se sua estratégia de negociação fizer uso extensivo de qualquer um dos pontos OHLC especificamente,
o desempenho do backtest pode diferir do desempenho ao vivo, pois as ordens podem ser roteadas para diferentes corretoras,
dependendo da sua corretora e do seu acesso à liquidez. A única maneira de resolver esses problemas é utilizar dados de
frequência mais alta ou obter dados diretamente de uma corretora específica, em vez de um feed composto mais barato.
Machine Translated by Google

19

3.3.3 Negociação Forex e ECNs


O backtest de estratégias de câmbio é um pouco mais complexo de implementar do que o de estratégias de ações. A negociação
em Forex ocorre em múltiplos locais e Redes de Comunicação Eletrônica (ECN). Os preços de compra/venda alcançados em
um local podem diferir substancialmente daqueles em outro local. É preciso ter extremo cuidado ao utilizar informações de
preços do local específico em que você negociará no backtest, em vez de um feed consolidado de vários locais, pois isso será
significativamente mais indicativo dos preços que você provavelmente alcançará no futuro.

Outra idiossincrasia dos mercados de câmbio é que as próprias corretoras não são obrigadas a compartilhar preços/
tamanhos de negociação com todos os participantes, visto que essas informações são de sua propriedade[6]. Portanto, é mais
apropriado usar cotações de compra e venda em seus backtests e ser extremamente cuidadoso com a variação dos custos de
transação entre corretoras/locais.

3.3.4 Restrições de curto-circuito


Ao realizar operações de venda a descoberto no backtest, é necessário estar ciente de que algumas ações podem não estar
disponíveis (devido à falta de disponibilidade daquela ação para empréstimo) ou devido a uma restrição de mercado, como a
proibição da SEC dos EUA de vender a descoberto ações financeiras durante a crise do mercado de 2008.

Isso pode inflar severamente os retornos de backtesting, portanto, tenha cuidado ao incluir essas restrições de venda a
descoberto em seus backtests ou evite qualquer venda a descoberto se você acredita que provavelmente há restrições de
liquidez nos instrumentos que você negocia.

3.4 Custos de transação

Um dos erros mais comuns de iniciantes na implementação de modelos de negociação é negligenciar (ou subestimar
grosseiramente) os efeitos dos custos de transação em uma estratégia. Embora muitas vezes se presuma que os custos de
transação refletem apenas as comissões dos corretores, existem, na verdade, muitas outras maneiras pelas quais os custos
podem ser acumulados em um modelo de negociação. Os três principais tipos de custos que devem ser considerados incluem:

3.4.1 Comissão

A forma mais direta de custos de transação incorridos por uma estratégia de negociação algorítmica são comissões e taxas.
Todas as estratégias exigem alguma forma de acesso a uma corretora, seja diretamente ou por meio de um intermediário ("o
corretor"). Esses serviços incorrem em um custo incremental a cada negociação, conhecido como comissão.

As corretoras geralmente oferecem muitos serviços, embora algoritmos quantitativos utilizem apenas a infraestrutura da
bolsa. Portanto, as comissões de corretagem costumam ser pequenas por operação. As corretoras também cobram taxas, que
são os custos incorridos para compensar e liquidar operações. Além disso, existem impostos cobrados por governos regionais
ou nacionais. Por exemplo, no Reino Unido, há um imposto de selo a ser pago sobre transações com ações. Como comissões,
taxas e impostos são geralmente fixos, eles são relativamente simples de implementar em um mecanismo de backtest (veja
abaixo).

3.4.2 Deslizamento
Slippage é a diferença de preço alcançada entre o momento em que um sistema de negociação decide realizar uma transação
e o momento em que a transação é efetivamente realizada em uma bolsa. Slippage é um componente considerável dos custos
de transação e pode fazer a diferença entre uma estratégia muito lucrativa e uma com desempenho ruim. Slippage é uma função
da volatilidade do ativo subjacente, da latência entre o sistema de negociação e a bolsa e do tipo de estratégia que está sendo
executada.

Um instrumento com maior volatilidade tem maior probabilidade de estar em movimento e, portanto, os preços entre o sinal
e a execução podem diferir substancialmente. A latência é definida como a diferença de tempo entre a geração do sinal e o
ponto de execução. Estratégias de frequência mais alta são mais sensíveis à latência.
Machine Translated by Google

20

Problemas e melhorias de milissegundos nessa latência podem fazer toda a diferença para a lucratividade. O
tipo de estratégia também é importante. Sistemas de momentum sofrem mais com slippage, em média, porque
estão tentando comprar instrumentos que já estão se movendo na direção prevista. O oposto é verdadeiro para
estratégias de reversão à média, pois essas estratégias estão se movendo em uma direção oposta à negociação.

3.4.3 Impacto no Mercado O

impacto no mercado é o custo incorrido pelos traders devido à dinâmica de oferta/demanda da bolsa (e do
ativo) por meio da qual estão tentando negociar. Uma ordem grande em um ativo relativamente ilíquido
provavelmente movimentará o mercado substancialmente, pois a negociação precisará acessar uma grande
parte da oferta atual. Para combater isso, grandes negociações em bloco são divididas em "blocos" menores,
que são transacionados periodicamente, à medida que nova liquidez chega à bolsa. Por outro lado, para
instrumentos de alta liquidez, como o contrato futuro do índice S&P500 E-Mini, negociações de baixo volume
dificilmente ajustarão o "preço atual" em grande escala.
Ativos mais ilíquidos são caracterizados por um spread maior, que é a diferença entre os preços de compra
e venda atuais no livro de ordens limitadas. Esse spread representa um custo de transação adicional associado
a qualquer negociação. O spread é um componente muito importante do custo total de transação – como
evidenciado pela miríade de empresas de apostas de spread do Reino Unido cujas campanhas publicitárias
expressam a "rigidez" de seus spreads para instrumentos altamente negociados.

3.5 Backtesting vs Realidade


Em resumo, há uma gama impressionante de fatores que podem ser simulados para gerar um backtest realista.
Os perigos de overfitting, limpeza de dados inadequada, tratamento incorreto dos custos de transação,
mudanças no regime de mercado e restrições de negociação frequentemente levam a um desempenho de
backtest substancialmente diferente de uma implementação de estratégia real.
Portanto, é preciso estar ciente de que o desempenho futuro dificilmente corresponderá diretamente ao
desempenho histórico. Discutiremos essas questões com mais detalhes quando implementarmos um mecanismo
de backtesting orientado a eventos, próximo ao final do livro.
Machine Translated by Google

Capítulo 4

Execução automatizada

Execução automatizada é o processo que permite que a estratégia gere automaticamente sinais de execução que são
enviados à corretora sem qualquer intervenção humana. Esta é a forma mais pura de estratégia de negociação algorítmica,
pois minimiza problemas devido à intervenção humana. É o tipo de sistema que consideraremos com mais frequência neste
livro.

4.1 Plataformas de Backtesting


O cenário de software para backtesting estratégico é vasto. As soluções variam de softwares sofisticados de nível institucional
totalmente integrados a linguagens de programação como C++, Python e R, onde quase tudo precisa ser escrito do zero (ou
"plugins" adequados devem ser obtidos).
Como traders quantitativos, estamos interessados no equilíbrio entre a capacidade de "possuir" nossa tecnologia de
negociação e a velocidade e confiabilidade de nossa metodologia de desenvolvimento. Aqui estão as principais considerações
para a escolha do software:

• Habilidade de Programação - A escolha do ambiente dependerá, em grande parte, da sua capacidade de programar
software. Eu diria que ter controle sobre toda a pilha de software terá um efeito maior no seu PnL a longo prazo do que
terceirizar o máximo possível para o software do fornecedor. Isso se deve ao risco de ter bugs externos ou
idiossincrasias que você não consegue corrigir no software do fornecedor, que de outra forma seriam facilmente
remediados se você tivesse mais controle sobre sua "pilha de tecnologia". Você também precisa de um ambiente que
atinja o equilíbrio certo entre produtividade, disponibilidade da biblioteca e velocidade de execução. Faço minha própria
recomendação pessoal abaixo.

• Capacidade de Execução/Interação com a Corretora - Certos softwares de backtesting, como o Tradestation, estão
diretamente vinculados a uma corretora. Não sou fã dessa abordagem, pois a redução dos custos de transação
costuma ser um componente importante para obter um Índice de Sharpe mais alto. Se você estiver vinculado a uma
corretora específica (e o Tradestation o "forçar" a fazer isso), terá mais dificuldade em fazer a transição para um novo
software (ou uma nova corretora) se necessário.
A Interactive Brokers fornece uma API robusta, embora com uma interface um pouco obtusa.

• Personalização - Um ambiente como MATLAB ou Python oferece uma grande flexibilidade ao criar estratégias de
algoritmos, pois eles fornecem bibliotecas fantásticas para quase qualquer operação matemática imaginável, mas
também permitem ampla personalização quando necessário.
sário.

• Complexidade da Estratégia - Certos softwares simplesmente não são adequados para cálculos complexos ou
matemática complexa. O Excel é um desses softwares. Embora seja bom para estratégias mais simples, ele não
consegue lidar com inúmeros ativos ou algoritmos mais complexos em alta velocidade.

• Minimização de Vieses - Um determinado software ou dado se presta mais a vieses de negociação? Você precisa ter
certeza de que, se quiser criar todas as funcionalidades sozinho,

21
Machine Translated by Google

22

Que você não introduza bugs que possam levar a vieses. Um exemplo aqui é o viés de previsão, que o Excel minimiza,
enquanto um backtester de pesquisa vetorizado pode se prestar a isso acidentalmente.

• Velocidade de Desenvolvimento - Não é necessário passar meses e meses implementando um mecanismo de backtest.
A prototipagem deve levar apenas algumas semanas. Certifique-se de que seu software não esteja atrapalhando seu
progresso de forma significativa, apenas para ganhar alguns pontos percentuais extras de velocidade de execução.

• Velocidade de Execução - Se a sua estratégia depender totalmente da rapidez da execução (como em HFT/UHFT), uma
linguagem como C ou C++ será necessária. No entanto, você estará se aproximando da otimização do kernel Linux e
do uso de FPGA para esses domínios, o que está fora do escopo deste livro.

• Custo - Muitos dos ambientes de software com os quais você pode programar estratégias de negociação algorítmica são
totalmente gratuitos e de código aberto. De fato, muitos fundos de hedge utilizam software de código aberto para todas
as suas plataformas de negociação algorítmica. Além disso, Excel e MATLAB são relativamente baratos e existem até
alternativas gratuitas para cada um.

Estratégias diferentes exigirão pacotes de software diferentes. Estratégias HFT e UHFT serão escritas em C/C++.
Atualmente, tais estratégias são frequentemente executadas em GPUs e FPGAs.
Por outro lado, estratégias de ações direcionais de baixa frequência são fáceis de implementar no TradeStation, devido à
natureza "tudo em um" do software/corretagem.

4.1.1 Programação
O desenvolvimento personalizado de uma linguagem de backtesting dentro de uma linguagem de programação de primeira
linha oferece a maior flexibilidade ao testar uma estratégia. Por outro lado, uma plataforma de backtesting integrada desenvolvida
pelo fornecedor sempre terá que fazer suposições sobre como os backtests são realizados.
A escolha de linguagens de programação disponíveis é ampla e diversificada. Não fica claro, antes do desenvolvimento, qual
linguagem seria adequada.
Uma vez que uma estratégia tenha sido codificada em regras sistemáticas, é necessário testá-la de forma que o trader
quantitativo tenha certeza de que seu desempenho futuro refletirá seu desempenho passado. Geralmente, existem duas formas
de sistema de backtesting utilizadas para testar essa hipótese. Em termos gerais, eles são categorizados como backtesters de
pesquisa e backtesters orientados a eventos.

4.1.2 Ferramentas de Pesquisa

A forma mais simples de uma ferramenta de backtesting, a ferramenta de pesquisa, geralmente é considerada primeiro. A
ferramenta de pesquisa é usada para verificar rapidamente a probabilidade de uma estratégia ter algum desempenho. Essas
ferramentas frequentemente fazem suposições irrealistas sobre custos de transação, prováveis preços de preenchimento,
restrições de venda a descoberto, dependência de local, gestão de risco e uma série de outras questões que foram descritas
no capítulo anterior. Ferramentas comuns para pesquisa incluem MATLAB, R, Python e Excel.
A fase de pesquisa é útil porque os pacotes de software oferecem capacidade significativa de vetorização, o que resulta em
boa velocidade de execução e implementação direta (menos linhas de código). Assim, é possível testar múltiplas estratégias,
combinações e variantes de forma rápida e iterativa.

Embora essas ferramentas sejam frequentemente utilizadas tanto para backtesting quanto para execução, tais ambientes
de pesquisa geralmente não são adequados para estratégias que abordam a negociação intradiária em frequências mais altas
(subminutos). Isso ocorre porque esses ambientes geralmente não possuem as bibliotecas necessárias para se conectar aos
servidores de fornecedores de dados de mercado em tempo real ou para interagir com APIs de corretoras de forma limpa.

Apesar dessas deficiências de execução, os ambientes de pesquisa são amplamente utilizados no ambiente quantitativo
profissional. Eles são o "primeiro teste" para todas as ideias de estratégia antes de passá-las por uma verificação mais rigorosa
em um ambiente de backtesting realista.
Machine Translated by Google

23

4.1.3 Backtesting orientado a eventos


Uma vez que uma estratégia tenha sido considerada adequada com base em pesquisa, ela deve ser testada de forma mais
realista. Esse realismo tenta levar em conta a maioria (senão todas) das questões descritas no capítulo anterior. O ideal é poder
usar o mesmo código de geração de negociações tanto para o backtesting histórico quanto para a execução ao vivo. Isso pode
ser alcançado usando um backtester orientado a eventos.

Sistemas orientados a eventos são amplamente utilizados em engenharia de software, comumente para lidar com entradas
de interface gráfica do usuário (GUI) em sistemas operacionais baseados em janelas. Eles também são ideais para negociação
algorítmica. Esses sistemas são frequentemente escritos em linguagens de alto desempenho, como C++, C# e Java.

Considere uma situação em que uma estratégia de negociação automatizada está conectada a um feed de mercado em
tempo real e a uma corretora (podem ser a mesma pessoa). Novas informações de mercado serão enviadas ao sistema, que
aciona um evento para gerar um novo sinal de negociação e, portanto, um evento de execução. Assim, esse sistema está em
um loop contínuo, aguardando para receber eventos e tratá-los adequadamente.

É possível gerar subcomponentes, como um manipulador de dados históricos e um simulador de corretagem, que podem
imitar suas contrapartes reais. Isso permite o backtesting de estratégias de maneira extremamente semelhante à execução ao
vivo.
A desvantagem desses sistemas é que eles são muito mais complexos de projetar e implementar do que uma ferramenta
de pesquisa mais simples. Consequentemente, o "tempo de lançamento no mercado" é maior. São mais propensos a bugs e
exigem um conhecimento razoável de programação e, até certo ponto, de metodologia de desenvolvimento de software.

4.1.4 Latência
Em termos de engenharia, latência é definida como o intervalo de tempo entre uma simulação e uma resposta.
Para nossos propósitos, geralmente se referirá ao tempo de atraso de ida e volta entre a geração de um sinal de execução e o
recebimento das informações de preenchimento de um corretor que está realizando a execução.

Essa latência raramente é um problema em estratégias interdiárias de baixa frequência, visto que a provável movimentação
de preços durante o período de latência não afetará significativamente a estratégia. Infelizmente, o mesmo não se aplica a
estratégias de frequência mais alta. Nessas frequências, a latência torna-se importante. O objetivo final é reduzir a latência o
máximo possível para minimizar o slippage, conforme discutido no capítulo anterior.

Reduzir a latência envolve minimizar a "distância" entre o sistema de negociação algorítmica e a bolsa final na qual uma
ordem está sendo executada. Isso pode envolver encurtar a distância geográfica entre os sistemas (e, portanto, reduzir o
deslocamento pelo cabeamento da rede), reduzir o processamento realizado em hardware de rede (importante em estratégias
de HFT) ou escolher uma corretora com infraestrutura mais sofisticada.

A redução da latência torna-se exponencialmente mais cara em função da "distância da internet" (ou seja, a distância da
rede entre dois servidores). Assim, para um operador de alta frequência, é preciso encontrar um meio-termo entre o gasto com
a redução da latência e o ganho com a minimização do slippage.
Essas questões serão discutidas na seção sobre Colocation abaixo.

4.1.5 Opções de idioma


Algumas questões que influenciam a escolha da linguagem já foram delineadas. Agora, consideraremos as vantagens e
desvantagens de cada linguagem de programação. Classifiquei as linguagens de forma geral em: alto desempenho/
desenvolvimento mais complexo versus baixo desempenho/desenvolvimento mais fácil.
Esses são termos subjetivos e alguns discordarão dependendo de sua formação.
Um dos aspectos mais importantes da programação de um ambiente de backtesting personalizado é que o programador
esteja familiarizado com as ferramentas utilizadas. Esse é provavelmente um critério mais importante do que a velocidade de
desenvolvimento. No entanto, para aqueles que são novos no cenário de linguagens de programação, o seguinte deve esclarecer
o que tende a ser utilizado na negociação algorítmica.
Machine Translated by Google

24

C++, C# e Java

C++, C# e Java são exemplos de linguagens de programação orientadas a objetos de propósito geral.
Ou seja, eles podem ser usados sem um IDE correspondente, são todos multiplataforma (podem ser executados no Windows,
Mac OSX ou Linux), têm uma ampla variedade de bibliotecas para quase qualquer tarefa imaginável e possuem alta velocidade
de execução.
Se você busca velocidade máxima de execução, C++ (ou C) provavelmente é a melhor escolha. Ela oferece a maior
flexibilidade para gerenciar memória e otimizar a velocidade de execução. Essa flexibilidade tem um preço. C++ é notoriamente
difícil de aprender bem e pode frequentemente levar a bugs sutis.
O tempo de desenvolvimento pode levar muito mais tempo do que em outras linguagens.
C# e Java são semelhantes, pois ambas exigem que todos os componentes sejam objetos, com exceção de tipos de dados
primitivos, como floats e inteiros. Elas diferem de C++, pois ambas realizam coleta de lixo automática. Isso significa que a
memória não precisa ser desalocada manualmente após a destruição de um objeto. A coleta de lixo adiciona uma sobrecarga
de desempenho, mas torna o desenvolvimento substancialmente mais rápido. Ambas as linguagens são boas escolhas para o
desenvolvimento de um backtester, pois possuem recursos nativos de interface gráfica (GUI), bibliotecas de análise numérica e
alta velocidade de execução.

Pessoalmente, eu usaria C++ para criar um backtester orientado a eventos que exija velocidade de execução extremamente
rápida, como para HFT. Isso somente se eu achar que um sistema orientado a eventos em Python está se tornando um gargalo,
já que esta última linguagem seria minha primeira escolha para tal sistema.

MATLAB, R e Python

O MATLAB é um ambiente de desenvolvimento integrado (IDE) comercial para computação numérica. Ele conquistou ampla aceitação nos setores
acadêmico, de engenharia e financeiro. Possui uma vasta gama de bibliotecas numéricas para diversas tarefas de computação científica e possui alta
velocidade de execução, considerando que qualquer algoritmo em desenvolvimento esteja sujeito a vetorização ou paralelização. É caro, o que às vezes
o torna menos atraente para investidores de varejo com orçamento limitado. O MATLAB às vezes é usado para execução direta por meio de uma corretora
como a Interactive Brokers.

R é menos uma linguagem de programação de propósito geral e mais um ambiente de script estatístico. É gratuito, de
código aberto, multiplataforma e contém uma enorme variedade de pacotes estatísticos disponíveis gratuitamente para a
realização de análises extremamente avançadas. R é amplamente utilizado no setor de fundos de hedge quantitativos para
pesquisas estatísticas/estratégicas. Embora seja possível conectar R a uma corretora, ele não é adequado para essa tarefa e
deve ser considerado mais como uma ferramenta de pesquisa.
Além disso, falta velocidade de execução, a menos que as operações sejam vetorizadas.
Agrupei Python sob este título, embora se situe em algum lugar entre MATLAB, R e as linguagens de uso geral mencionadas
anteriormente. É gratuito, de código aberto e multiplataforma. É interpretado em vez de compilado, o que o torna nativamente
mais lento que C++. Apesar disso, contém uma biblioteca para executar praticamente qualquer tarefa imaginável, desde
computação científica até o design de servidores web de baixo nível. Em particular, contém NumPy, SciPy, pandas, matplotlib e
scikit-learn, que fornecem um ambiente robusto de pesquisa numérica que, quando vetorizado, é comparável à velocidade de
execução de uma linguagem compilada.

Além disso, possui bibliotecas maduras para conexão com corretoras. Isso o torna um "balcão único" para a criação de um
ambiente de backtesting e execução ao vivo orientado a eventos, sem a necessidade de recorrer a outras linguagens. A
velocidade de execução é mais do que suficiente para traders intradiários que operam em minutos. Por fim, é muito simples de
aprender, em comparação com linguagens de nível inferior, como C++. Por esses motivos, utilizamos amplamente Python neste
livro.

4.1.6 Ambientes de Desenvolvimento Integrados


O termo IDE tem múltiplos significados em negociação algorítmica. Desenvolvedores de software o utilizam para se referir a
uma interface gráfica de usuário (GUI) que permite a programação com destaque de sintaxe, navegação em arquivos, depuração
e recursos de execução de código. Traders algorítmicos o utilizam para se referir a um ambiente de backtesting/negociação
totalmente integrado, incluindo download de dados históricos ou em tempo real, gráficos, avaliação estatística e
Machine Translated by Google

25

Execução ao vivo. Para nossos propósitos, uso o termo para significar qualquer ambiente (geralmente baseado em interface
gráfica de usuário) que não seja uma linguagem de programação de uso geral, como C++ ou Python. O MATLAB é considerado
um IDE, por exemplo.

Excel

Embora alguns analistas quantitativos mais puros possam menosprezar o Excel, descobri que ele é extremamente útil para a
"verificação da sanidade" dos resultados. O fato de todos os dados estarem diretamente disponíveis, em vez de ocultos atrás
de objetos, facilita a implementação de estratégias de sinal/filtro muito básicas.
Corretoras, como a Interactive Brokers, também permitem plugins DDE que permitem ao Excel receber dados de mercado em
tempo real e executar ordens de negociação.
Apesar da facilidade de uso, o Excel é extremamente lento para qualquer escala razoável de dados ou nível de computação
numérica. Eu o utilizo apenas para verificar erros ao desenvolver com base em outras estratégias e para garantir que evitei o
viés de previsão, que é fácil de observar no Excel devido à natureza de planilha do software.

Se você não se sente confortável com linguagens de programação e está realizando uma tarefa interdiária
estratégia, então o Excel pode ser a escolha perfeita.

Software de backtesting comercial/de varejo

O mercado de softwares de gráficos de varejo, "análise técnica" e backtesting é extremamente competitivo. Os recursos
oferecidos por esses softwares incluem gráficos de preços em tempo real, uma variedade de indicadores técnicos, linguagens
de backtesting personalizadas e execução automatizada.
Alguns fornecedores oferecem uma solução completa, como a TradeStation. A TradeStation é uma corretora online que
produz software de negociação (também conhecido como TradeStation) que permite a execução eletrônica de ordens em
diversas classes de ativos. Atualmente, não acredito que eles ofereçam uma API direta para execução automatizada; em vez
disso, as ordens devem ser colocadas por meio do software. Isso contrasta com a Interactive Brokers, que possui uma
interface de gráficos/negociação mais simplificada (Trader WorkStation), mas oferece suas próprias APIs de execução de
ordens/mercado em tempo real e uma interface FIX.

Outra plataforma extremamente popular é a MetaTrader, usada em operações de câmbio para a criação de "Expert
Advisors". Trata-se de scripts personalizados, escritos em uma linguagem proprietária, que podem ser usados para operações
automatizadas. Não tenho muita experiência com a TradeStation ou a MetaTrader, então não vou me aprofundar muito em
seus méritos.
Essas ferramentas são úteis se você não se sente confortável com desenvolvimento de software aprofundado e deseja
que muitos detalhes sejam resolvidos. No entanto, com esses sistemas, muita flexibilidade é sacrificada e você frequentemente
fica preso a uma única corretora.

Ferramentas baseadas na Web

Os dois sistemas de backtesting baseados na web mais populares atualmente são o Quantopian ([Link] e o
QuantConnect ([Link] O primeiro utiliza Python (e ZipLine, veja abaixo), enquanto o segundo utiliza
C#. Ambos fornecem uma riqueza de dados históricos. O Quan-topian atualmente oferece suporte para negociação ao vivo
com a Interactive Brokers, enquanto o QuantConnect está trabalhando para implementar a negociação ao vivo.

Software de backtesting de código aberto

Além das ofertas comerciais, existem alternativas de código aberto para backtesting de software
mercadorias.

A Algo-Trader é uma empresa com sede na Suíça que oferece licenças de código aberto e comercial para seu sistema.
Pelo que pude perceber, a oferta parece bastante madura e eles têm muitos clientes institucionais. O sistema permite
backtesting histórico completo e processamento de eventos complexos, e eles se integram à Interactive Brokers. A edição
Enterprise oferece recursos de alto desempenho consideravelmente maiores.

A Marketcetera oferece um sistema de backtesting que pode ser integrado a muitas outras linguagens, como Python e R,
para aproveitar o código que você já escreveu. A 'Estratégia
Machine Translated by Google

26

O Studio' oferece a capacidade de escrever código de backtesting, bem como algoritmos de execução otimizados e,
posteriormente, fazer a transição de um backtest histórico para negociações em papel ao vivo.
ZipLine é a biblioteca Python que alimenta o serviço Quantopian mencionado acima. É um ambiente de backtest
totalmente orientado a eventos e atualmente suporta ações dos EUA com base em barras de minuto a minuto. Não
utilizei muito o ZipLine, mas conheço outras pessoas que o consideram uma boa ferramenta.
Ainda há muitas áreas a serem melhoradas, mas a equipe está constantemente trabalhando no projeto, por isso ele é
mantido de forma muito ativa.
Há também alguns projetos hospedados no Github/Google Code que você pode querer analisar.
Não dediquei muito tempo a investigá-los. Projetos como OpenQuant ([Link] TradeLink
([Link] e PyAlgoTrade ([Link] são exemplos disso.

Software de Backtesting Institucional

Sistemas de backtesting de nível institucional, como Deltix e QuantHouse, não são frequentemente utilizados por
traders algorítmicos de varejo. As licenças de software geralmente estão bem acima do orçamento de infraestrutura.
Dito isso, esse tipo de software é amplamente utilizado por fundos quantitativos, casas de negociação proprietárias,
family offices e similares.
Os benefícios desses sistemas são claros. Eles oferecem uma solução completa para coleta de dados,
desenvolvimento de estratégias, backtesting histórico e execução ao vivo em instrumentos ou portfólios individuais, até
o nível de frequência ultra-alta. Essas plataformas passaram por testes extensivos e bastante uso "em campo" e,
portanto, são consideradas robustas.
Os sistemas são orientados a eventos e, como tal, o ambiente de backtesting pode frequentemente simular bem o ambiente
real. Os sistemas também suportam algoritmos de execução otimizados, que tentam minimizar os custos de transação.

Devo admitir que não tenho muita experiência com Deltix ou QuantHouse além de algumas visões gerais
superficiais. Dito isso, o orçamento por si só os coloca fora do alcance da maioria dos traders de varejo, então não vou
me alongar sobre esses sistemas.

4.2 Colocação

O cenário de software para negociação algorítmica já foi analisado. Agora é hora de focar a atenção na implementação
do hardware que executará nossas estratégias.
Um trader de varejo provavelmente executará sua estratégia de casa durante o horário de mercado, ligando seu
PC, conectando-se à corretora, atualizando seu software de mercado e, em seguida, permitindo que o algoritmo seja
executado automaticamente durante o dia. Por outro lado, um fundo quantitativo profissional com ativos sob gestão
(AUM) significativos terá uma infraestrutura de servidor dedicada, localizada na bolsa, a fim de reduzir a latência ao
máximo para executar suas estratégias de alta velocidade.

4.2.1 Área de trabalho doméstica

A abordagem mais simples para implantação de hardware é simplesmente executar uma estratégia algorítmica com um computador
desktop doméstico conectado à corretora por meio de uma conexão de banda larga (ou similar).

Embora essa abordagem seja simples de começar, ela apresenta muitas desvantagens.
Basicamente, o computador desktop está sujeito a falhas de energia, a menos que seja alimentado por uma UPS. Além
disso, a conexão de internet residencial também depende do provedor de internet. A falta de energia ou a falha na
conexão com a internet podem ocorrer em um momento crucial da negociação, deixando o trader algorítmico com
posições abertas que não podem ser fechadas.
Em segundo lugar, um computador desktop precisa ser reiniciado ocasionalmente, muitas vezes devido à
confiabilidade do sistema operacional. Isso significa que a estratégia sofre com um certo grau de intervenção manual
indireta. Se isso ocorrer fora do horário de negociação, o problema é atenuado. No entanto, se um computador precisar
ser reiniciado durante o horário de negociação, o problema é semelhante a uma queda de energia. Posições não
fechadas ainda podem estar sujeitas a risco.
A falha de componentes também leva ao mesmo conjunto de problemas de "tempo de inatividade". Uma falha no
disco rígido, no monitor ou na placa-mãe geralmente ocorre exatamente no momento errado. Por todos esses motivos
Machine Translated by Google

27

Hesito em recomendar uma abordagem de negociação algorítmica em um desktop. Se você decidir adotar essa abordagem,
certifique-se de ter um computador reserva E uma conexão de internet reserva (por exemplo, um dongle 3G) que você possa
usar para fechar posições em uma situação de inatividade.

4.2.2 VPS
O próximo nível acima de um desktop doméstico é usar um servidor virtual privado (VPS). Um VPS é um sistema de servidor
remoto frequentemente comercializado como um serviço de "nuvem". Eles são muito mais baratos do que um servidor
dedicado correspondente, já que um VPS é, na verdade, uma partição de um servidor muito maior, com um ambiente de
sistema operacional virtual isolado disponível exclusivamente para você. A carga da CPU é compartilhada entre vários
servidores e uma parte da RAM do sistema é alocada ao VPS.
Provedores comuns de VPS incluem Amazon EC2 e Rackspace Cloud. Eles oferecem desde sistemas básicos com
baixa RAM e uso básico de CPU até servidores corporativos com alta RAM e alta CPU. Para a maioria dos traders de varejo
algorítmicos, os sistemas básicos são suficientes para estratégias intradiárias ou interdiárias de baixa frequência e bancos
de dados históricos menores.
As vantagens de um sistema baseado em VPS incluem disponibilidade 24 horas por dia, 7 dias por semana (com um
tempo de inatividade realista!), recursos de monitoramento mais robustos, "plugins" fáceis de usar para serviços adicionais,
como armazenamento de arquivos ou bancos de dados gerenciados, e uma arquitetura flexível. As desvantagens incluem o
custo à medida que o sistema cresce, já que o hardware dedicado se torna muito mais barato por desempenho, considerando
a colocação longe de uma exchange, bem como a necessidade de lidar com cenários de falha (por exemplo, criando um
segundo VPS idêntico).
Além disso, a latência nem sempre é melhorada pela escolha de um provedor de VPS/nuvem. Sua localização pode
estar mais próxima de uma determinada corretora financeira do que dos data centers do seu provedor de nuvem. Isso é
parcialmente atenuado pela escolha de uma empresa que ofereça VPS voltado especificamente para negociação algorítmica
e que esteja localizada em corretoras ou perto delas. No entanto, essas empresas provavelmente custarão mais do que um
provedor de VPS "tradicional", como Amazon ou Rackspace.

4.2.3 Troca
Para obter a melhor minimização de latência e sistemas mais rápidos, é necessário alocar um servidor dedicado (ou conjunto
de servidores) diretamente no data center da corretora. Esta é uma opção proibitivamente cara para quase todos os traders
algorítmicos de varejo (a menos que estejam muito bem capitalizados). Na verdade, é domínio do fundo quantitativo
profissional ou da corretora.
Como mencionei acima, uma opção mais realista é adquirir um sistema VPS de um provedor localizado perto de uma
exchange. Não vou me aprofundar muito na colocação de exchanges, pois o tópico está um pouco fora do escopo do livro.
Machine Translated by Google

28
Machine Translated by Google

Capítulo 5

Ideias de Estratégias de Sourcing

Neste capítulo, quero apresentar os métodos pelos quais eu mesmo identifico estratégias de negociação algorítmica
lucrativas. Discutiremos como encontrar, avaliar e selecionar tais sistemas. Explicarei como a identificação de estratégias
depende tanto da preferência pessoal quanto do desempenho da estratégia, como determinar o tipo e a quantidade de
dados históricos para teste, como avaliar imparcialmente uma estratégia de negociação e, finalmente, como prosseguir para
a fase de backtesting e implementação da estratégia.

5.1 Identificando suas preferências pessoais para negociação


Para ser um trader de sucesso – seja de forma discricionária ou algorítmica – é necessário se fazer algumas perguntas
honestas. O trading oferece a possibilidade de perder dinheiro a uma taxa alarmante, portanto, é necessário "conhecer a si
mesmo" tanto quanto entender a estratégia escolhida.

Eu diria que a consideração mais importante no trading é estar ciente da sua própria personalidade. O trading, e o
trading algorítmico em particular, exige um grau significativo de disciplina, paciência e distanciamento emocional. Como você
está permitindo que um algoritmo realize suas negociações por você, é necessário estar determinado a não interferir na
estratégia enquanto ela estiver sendo executada. Isso pode ser extremamente difícil, especialmente em períodos de
drawdown prolongado. No entanto, muitas estratégias que se mostraram altamente lucrativas em um backtest podem ser
arruinadas por uma simples interferência. Entenda que, se você deseja entrar no mundo do trading algorítmico, será testado
emocionalmente e que, para ter sucesso, é necessário superar essas dificuldades!

A próxima consideração é sobre tempo. Você tem um emprego em tempo integral? Trabalha meio período?
Você trabalha em casa ou tem um longo trajeto diário? Essas perguntas ajudarão a determinar a frequência da estratégia
que você deve buscar. Para aqueles que trabalham em tempo integral, uma estratégia de futuros intradiários pode não ser
apropriada (pelo menos até que seja totalmente automatizada!). Suas restrições de tempo também determinarão a
metodologia da estratégia. Se sua estratégia for negociada com frequência e depender de feeds de notícias caros (como um
terminal Bloomberg), você claramente terá que ser realista quanto à sua capacidade de executá-la com sucesso no escritório!
Para aqueles com bastante tempo ou habilidades para automatizar sua estratégia, talvez seja interessante considerar uma
estratégia de negociação de alta frequência (HFT) mais técnica.

Acredito que é necessário pesquisar continuamente suas estratégias de negociação para manter um portfólio
consistentemente lucrativo. Poucas estratégias permanecem "fora do radar" para sempre.
Portanto, uma parte significativa do tempo alocado à negociação será dedicada à realização de pesquisas contínuas.
Pergunte a si mesmo se você está preparado para fazer isso, pois pode ser a diferença entre uma forte lucratividade ou um
declínio gradual rumo às perdas.
Você também precisa considerar seu capital de negociação. O valor mínimo ideal geralmente aceito para uma estratégia
quantitativa é de 50.000 USD (aproximadamente £ 35.000 para nós no Reino Unido).
Se eu fosse recomeçar, começaria com um valor maior, provavelmente próximo a 100.000 USD (aproximadamente £
70.000). Isso ocorre porque os custos de transação podem ser extremamente altos para estratégias de média a alta
frequência e é necessário ter capital suficiente para absorvê-los em determinados momentos.

29
Machine Translated by Google

30

de drawdown. Se você está pensando em começar com menos de US$ 10.000, precisará se restringir a estratégias de
baixa frequência, negociando em um ou dois ativos, pois os custos de transação consumirão rapidamente seus
retornos. A Interactive Brokers, que é uma das corretoras mais amigáveis para quem tem habilidades de programação,
devido à sua API, tem um mínimo de US$ 10.000 para contas de varejo.
Habilidades de programação são um fator importante na criação de uma estratégia de negociação algorítmica
automatizada. Ter conhecimento em uma linguagem de programação como C++, Java, C#, Python ou R permitirá que
você crie o armazenamento de dados de ponta a ponta, o mecanismo de backtest e o sistema de execução por conta
própria. Isso tem uma série de vantagens, sendo a principal delas a capacidade de estar completamente ciente de
todos os aspectos da infraestrutura de negociação. Também permite que você explore estratégias de frequência mais
alta, pois terá controle total sobre sua "pilha de tecnologia". Embora isso signifique que você pode testar seu próprio
software e eliminar bugs, também significa mais tempo gasto codificando a infraestrutura e menos tempo na
implementação de estratégias, pelo menos no início de sua carreira em negociação algorítmica. Você pode se sentir
confortável negociando em Excel ou MATLAB e pode terceirizar o desenvolvimento de outros componentes. No
entanto, eu não recomendaria isso, especialmente para quem negocia em alta frequência.

Você precisa se perguntar o que espera alcançar com a negociação algorítmica. Você está interessado em uma
renda regular, com a qual espera obter ganhos da sua conta de negociação?
Ou você está interessado em um ganho de capital a longo prazo e pode se dar ao luxo de negociar sem a necessidade
de sacar fundos? A dependência da renda determinará a frequência da sua estratégia. Saques de renda mais regulares
exigirão uma estratégia de negociação com maior frequência e menor volatilidade (ou seja, um índice de Sharpe mais
alto). Traders de longo prazo podem se dar ao luxo de uma frequência de negociação mais moderada.
Por fim, não se iluda com a ideia de enriquecer muito em pouco tempo! O trading de algoritmos NÃO é um
esquema para enriquecer rapidamente – na verdade, pode ser um esquema para empobrecer rapidamente. É preciso
muita disciplina, pesquisa, diligência e paciência para ter sucesso no trading algorítmico. Pode levar meses, senão
anos, para gerar lucratividade consistente.

5.2 Obtendo Ideias de Negociação Algorítmica


Apesar da percepção comum em contrário, é bastante simples encontrar estratégias de negociação lucrativas em
domínio público. Nunca as ideias de negociação estiveram tão disponíveis como hoje. Revistas acadêmicas de
finanças, servidores de pré-impressão, blogs de negociação, fóruns de negociação, revistas semanais de negociação
e textos especializados oferecem milhares de estratégias de negociação para você basear suas ideias.

Nosso objetivo como pesquisadores de negociação quantitativa é estabelecer um pipeline de estratégias que nos
forneça um fluxo contínuo de ideias de negociação. Idealmente, queremos criar uma abordagem metódica para buscar,
avaliar e implementar as estratégias que encontramos. Os objetivos do pipeline são gerar uma quantidade consistente
de novas ideias e nos fornecer uma estrutura para rejeitar a maioria delas com o mínimo de consideração emocional.

Devemos ser extremamente cuidadosos para não deixar que vieses cognitivos influenciem nossa metodologia de
tomada de decisão. Isso pode ser tão simples quanto ter preferência por uma classe de ativos em detrimento de outra
(ouro e outros metais preciosos vêm à mente) por serem percebidos como mais exóticos. Nosso objetivo deve ser
sempre encontrar estratégias consistentemente lucrativas, com expectativa positiva. A escolha da classe de ativos
deve se basear em outras considerações, como restrições de capital de negociação, taxas de corretagem e capacidade
de alavancagem.

5.2.1 Livros didáticos

Se você não está familiarizado com o conceito de estratégia de negociação e com os mercados financeiros em geral,
o primeiro lugar a consultar são os livros didáticos consagrados. Os livros clássicos oferecem uma ampla gama de
ideias mais simples e diretas para se familiarizar com a negociação quantitativa. Aqui está uma seleção que recomendo
para quem é novo na negociação quantitativa, que se torna gradualmente mais sofisticada à medida que você avança
na lista.
Machine Translated by Google

31

Mercados Financeiros e Participantes

A lista a seguir detalha livros que descrevem como os mercados de capitais funcionam e a negociação eletrônica moderna.

• Guia do Financial Times para os Mercados Financeiros, de Glen Arnold[1] - Este livro foi elaborado para iniciantes nos
mercados financeiros e é extremamente útil para obter insights sobre todos os participantes do mercado. Para nossos
propósitos, ele nos fornece uma lista de mercados nos quais podemos posteriormente formular estratégias de negociação
algorítmica.

• Negociação e trocas: microestrutura de mercado para profissionais por Larry Harris[7]


- Este é um livro extremamente informativo sobre os participantes dos mercados financeiros e como eles operam. Ele
fornece detalhes significativos sobre como as negociações são realizadas, as diversas motivações dos participantes e
como os mercados são regulamentados. Embora alguns possam considerá-lo uma "leitura árida",
Acredito que é absolutamente essencial entender esses conceitos para ser um bom trader algorítmico.

• Algorithmic Trading and DMA: An introduction to direct access trading strate-gies, de Barry Johnson[10] - O livro de
Johnson é mais voltado para o lado tecnológico dos mercados. Ele discute os tipos de ordens, algoritmos de execução
ideal, os tipos de bolsas que aceitam negociação algorítmica, bem como estratégias mais sofisticadas. Assim como o
livro de Harris acima, ele explica em detalhes como funcionam os mercados de negociação eletrônica, cujo conhecimento
também acredito ser um pré-requisito essencial para a execução de estratégias sistemáticas.

Negociação Quantitativa

O próximo conjunto de livros aborda diretamente a negociação algorítmica/quantitativa. Eles descrevem alguns dos conceitos
básicos e estratégias específicas que podem ser implementadas.

• Quantitative Trading: How to Build Your Own Algorithmic Trading Business, de Ernest Chan[5] - O primeiro livro de Ernest
Chan é um "guia para iniciantes" em estratégias de negociação quantitativa. Embora não seja muito focado em ideias
estratégicas, apresenta uma estrutura para a criação de um negócio de negociação, com ideias de gestão de risco e
ferramentas de implementação. Este é um ótimo livro se você é completamente novo em negociação algorítmica. O livro
utiliza o MATLAB.

• Negociação Algorítmica: Estratégias Vencedoras e Sua Justificativa por Ernest Chan[6]


- O segundo livro de Chan é bastante focado em ideias estratégicas. Ele essencialmente começa onde o livro anterior
parou e é atualizado para refletir as "condições atuais do mercado". O livro discute estratégias de reversão à média e
baseadas em momentum nas frequências interdiárias e intradiárias. Também aborda brevemente a negociação de alta
frequência. Assim como o livro anterior, faz uso extensivo do código MATLAB.

• Inside The Black Box: The Simple Truth About Quantitative and High-Frequency Trading, 2ª edição, por Rishi Narang[12] - O
livro de Narang oferece uma visão geral dos componentes de um sistema de negociação empregado por um fundo de
hedge quantitativo, incluindo geradores de alfa, gestão de risco, otimização de portfólio e custos de transação. A
segunda edição aborda detalhes significativos sobre técnicas de negociação de alta frequência.

• Volatility Trading por Euan Sinclair[16] - O livro de Sinclair concentra-se exclusivamente na modelagem/previsão de
volatilidade e estratégias de opções projetadas para tirar vantagem desses modelos.
Se você planeja negociar opções de forma quantitativa, este livro fornecerá muitas ideias de pesquisa.

5.2.2 A Internet
Depois de adquirir uma base sólida no processo de negociação algorítmica por meio dos textos clássicos, ideias estratégicas
adicionais podem ser encontradas na internet. Blogs de finanças quantitativas, agregadores de links e fóruns de negociação
oferecem fontes valiosas de ideias para testar.
Machine Translated by Google

32

No entanto, um aviso: muitos recursos de negociação online baseiam-se no conceito de análise técnica.
A análise técnica envolve a utilização de indicadores básicos de análise de sinais e psicologia comportamental
para determinar tendências ou padrões de reversão nos preços dos ativos.
Apesar de ser extremamente popular no ambiente de negociação em geral, a análise técnica é
considerada um tanto controversa na comunidade de finanças quantitativas. Alguns sugeriram que ela não é
melhor do que ler um horóscopo ou estudar folhas de chá em termos de seu poder preditivo! Na realidade,
existem indivíduos bem-sucedidos que fazem uso extensivo da análise técnica em suas negociações.
Como quants, com um conjunto de ferramentas matemáticas e estatísticas mais sofisticadas à disposição,
podemos avaliar facilmente a eficácia dessas estratégias "baseadas em AT". Isso nos permite tomar decisões
baseadas em análise de dados e testes de hipóteses, em vez de basear tais decisões em considerações
emocionais ou preconceitos.

Blogs Quantitativos

Recomendo os seguintes blogs quantitativos para boas ideias e conceitos sobre negociação algorítmica em
geral:

• Negociação MATLAB - [Link]

• Negociação Quantitativa (Ernest Chan) - [Link]

• Quantidade - [Link]

• Quantopian - [Link]

• Quantpedia - [Link]

Agregadores

Tornou-se moda nos últimos anos agregar links temáticos e depois discuti-los. Eu leio os seguintes
agregadores:

• Quantocracy - [Link]

• Notícias quantitativas - [Link]

• Sub-Reddit de negociação de algoritmos - [Link]

Fóruns

O próximo lugar para encontrar ideias de estratégias adicionais são os fóruns de negociação. Não se deixe intimidar
por estratégias mais voltadas para a "análise técnica". Essas estratégias geralmente oferecem boas ideias que podem
ser testadas estatisticamente:

• Fóruns Elite Trader - [Link]

• Nuclear Phynance - [Link]

• QuantNet - [Link]

• Laboratório de Riqueza - [Link]

• Fóruns Wilmott - [Link]


Machine Translated by Google

33

5.2.3 Literatura do periódico

Depois de adquirir alguma experiência na avaliação de estratégias mais simples, é hora de analisar as ofertas
acadêmicas mais sofisticadas. Alguns periódicos acadêmicos serão difíceis de acessar, sem assinaturas altas
ou custos únicos. Se você for membro ou ex-aluno de uma universidade, deverá conseguir acesso a alguns
desses periódicos financeiros. Caso contrário, você pode consultar servidores de pré-impressão, que são
repositórios na internet de rascunhos recentes de artigos acadêmicos que estão sendo revisados por pares.
Como estamos interessados apenas em estratégias que podemos replicar, testar e obter lucratividade com
sucesso, uma revisão por pares é de menor importância para nós.
A principal desvantagem das estratégias acadêmicas é que elas podem frequentemente estar
desatualizadas, exigir dados históricos obscuros e caros, negociar em classes de ativos ilíquidas ou não levar
em conta taxas, slippage ou spread. Também pode não ser claro se a estratégia de negociação será executada
com ordens de mercado, ordens limitadas ou se contém stop loss, etc. Portanto, é absolutamente essencial
replicar a estratégia da melhor forma possível, testá-la e adicionar custos de transação realistas que incluam o
máximo de aspectos das classes de ativos que você deseja negociar.
Aqui está uma lista dos servidores de pré-impressão e periódicos financeiros mais populares dos quais você pode obter
ideias:

• arXiv - [Link]

• SSRN - [Link]

• Revista de Estratégias de Investimento - [Link]


estratégias de investimento

• Revista de Finanças Computacionais - [Link]


de finanças computacionais

• Finanças Matemáticas - [Link]


9965

5.2.4 Pesquisa Independente

Que tal elaborar suas próprias estratégias quantitativas? Isso geralmente requer (mas não se limita a) expertise
em uma ou mais das seguintes categorias:

• Microestrutura de mercado - Especialmente para estratégias de alta frequência, pode-se utilizar a


microestrutura de mercado, ou seja, a compreensão da dinâmica do livro de ordens para gerar
lucratividade. Diferentes mercados terão diferentes limitações tecnológicas, regulamentações,
participantes do mercado e restrições, todas abertas à exploração por meio de estratégias específicas.
Esta é uma área muito sofisticada e os profissionais de varejo terão dificuldade em ser competitivos
neste espaço, especialmente porque a concorrência inclui fundos de hedge quantitativos de grande
porte, bem capitalizados e com sólidas capacidades tecnológicas.

• Estrutura do fundo - Fundos de investimento coletivos, como fundos de pensão, parcerias de investimento
privado (hedge funds), consultores de negociação de commodities e fundos mútuos, são limitados tanto
por uma regulamentação rigorosa quanto por suas grandes reservas de capital. Assim, certos
comportamentos consistentes podem ser explorados por aqueles que são mais ágeis. Por exemplo,
grandes fundos estão sujeitos a restrições de capacidade devido ao seu tamanho. Portanto, se
precisarem vender rapidamente uma quantidade de títulos, terão que escalonar a venda para evitar "mover o mercado".
Algoritmos sofisticados podem tirar proveito disso e de outras idiossincrasias em um processo geral
conhecido como arbitragem de estrutura de fundos.

• Aprendizado de máquina/inteligência artificial - Algoritmos de aprendizado de máquina tornaram-se mais


prevalentes nos mercados financeiros nos últimos anos. Classificadores (como Naive-Bayes et al.),
correspondentes de funções não lineares (redes neurais) e rotinas de otimização (algoritmos genéticos)
têm sido usados para prever trajetórias de ativos ou otimizar estratégias de negociação. Se você tem
experiência nessa área, pode ter alguma ideia de como algoritmos específicos podem ser aplicados a
determinados mercados.
Machine Translated by Google

34

Ao continuar monitorando as fontes acima semanalmente, ou mesmo diariamente, você estará se preparando para
receber uma lista consistente de estratégias de uma gama diversificada de fontes. O próximo passo é determinar como
rejeitar um grande subconjunto dessas estratégias para minimizar o desperdício de tempo e recursos de backtesting em
estratégias que provavelmente não serão lucrativas.

5.3 Avaliação de estratégias de negociação


A primeira, e provavelmente a mais óbvia, consideração é se você realmente entende a estratégia. Você seria capaz de
explicar a estratégia de forma concisa ou ela requer uma série de ressalvas e listas intermináveis de parâmetros? Além
disso, a estratégia tem uma base sólida e sólida na realidade? Por exemplo, você poderia apontar alguma justificativa
comportamental ou restrição na estrutura do fundo que possa estar causando o(s) padrão(ões) que você está tentando
explorar? Essa restrição se manteria em uma mudança de regime, como uma ruptura drástica no ambiente regulatório?
A estratégia se baseia em regras estatísticas ou matemáticas complexas? Ela se aplica a alguma série temporal
financeira ou é específica para a classe de ativos na qual se alega ser lucrativa? Você deve considerar constantemente
esses fatores ao avaliar novos métodos de negociação, caso contrário, poderá perder uma quantidade significativa de
tempo tentando fazer backtests e otimizar estratégias não lucrativas.

Depois de determinar que você entende os princípios básicos da estratégia, precisa decidir se ela se encaixa no seu
perfil de personalidade mencionado. Esta não é uma consideração tão vaga quanto parece! As estratégias variam
substancialmente em suas características de desempenho.
Existem certos tipos de personalidade que conseguem lidar com períodos mais significativos de retração ou estão
dispostos a aceitar riscos maiores por um retorno maior. Apesar de nós, como quants, tentarmos eliminar o máximo
possível de vieses cognitivos e sermos capazes de avaliar uma estratégia com imparcialidade, vieses sempre surgirão.
Portanto, precisamos de um meio consistente e imparcial para avaliar o desempenho das estratégias. Aqui está a lista
de critérios pelos quais julgo uma potencial nova estratégia:

• Metodologia - A estratégia é baseada em momentum, reversível à média, neutra em relação ao mercado e


direcional? A estratégia se baseia em técnicas estatísticas ou de aprendizado de máquina sofisticadas (ou
complexas!) que são difíceis de entender e exigem um doutorado em estatística para entendê-las?
Essas técnicas introduzem uma quantidade significativa de parâmetros, o que pode levar a um viés de otimização?
A estratégia tem probabilidade de resistir a uma mudança de regime (ou seja, uma possível nova regulamentação
dos mercados financeiros)?

• Índice de Sharpe - O índice de Sharpe caracteriza heuristicamente a relação risco/retorno da estratégia. Ele
quantifica o retorno que você pode alcançar para o nível de volatilidade suportado pela curva de ações.
Naturalmente, precisamos determinar o período e a frequência com que esses retornos e volatilidade (ou seja,
desvio padrão) são medidos. Uma estratégia de frequência mais alta exigirá uma taxa de amostragem maior de
desvio padrão, mas um período geral de medição mais curto, por exemplo.

• Alavancagem - A estratégia exige alavancagem significativa para ser lucrativa? A estratégia exige o uso de
contratos de derivativos alavancados (futuros, opções, swaps) para obter retorno? Esses contratos alavancados
podem ter características de alta volatilidade e, portanto, podem facilmente levar a chamadas de margem. Você
tem o capital de negociação e a disposição para tal volatilidade?

• Frequência - A frequência da estratégia está intimamente ligada à sua pilha de tecnologia (e, portanto, à expertise
tecnológica), ao índice de Sharpe e ao nível geral de custos de transação.
Considerando todos os outros aspectos, estratégias de frequência mais alta exigem mais capital, são mais
sofisticadas e difíceis de implementar. No entanto, supondo que seu mecanismo de backtesting seja sofisticado
e livre de bugs, elas geralmente terão índices de Sharpe muito mais altos.

• Volatilidade - A volatilidade está fortemente relacionada ao "risco" da estratégia. O índice de Sharpe caracteriza
isso. Uma maior volatilidade das classes de ativos subjacentes, se não protegidas, frequentemente leva a uma
maior volatilidade na curva de ações e, portanto, a índices de Sharpe menores. É claro que estou assumindo que
a volatilidade positiva é aproximadamente igual à volatilidade negativa. Algumas estratégias podem apresentar
maior volatilidade negativa. Você precisa estar ciente desses atributos.
Machine Translated by Google

35

• Ganho/Perda, Lucro/Perda Médio - As estratégias diferem em suas características de ganho/perda e lucro/perda


médio. É possível ter uma estratégia muito lucrativa, mesmo que o número de operações com prejuízo exceda o
número de operações com lucro. As estratégias de momentum tendem a ter esse padrão, pois dependem de um
pequeno número de "grandes sucessos" para serem lucrativas. As estratégias de reversão à média tendem a
ter perfis opostos, em que a maioria das operações é "vencedora", mas as operações com prejuízo podem ser
bastante severas.

• Drawdown Máximo - O drawdown máximo é a maior queda percentual geral do pico ao fundo na curva de
patrimônio da estratégia. Estratégias de momentum são bem conhecidas por sofrerem com períodos de
drawdowns prolongados (devido a uma série de muitas operações com perdas incrementais). Muitos traders
desistem em períodos de drawdown prolongado, mesmo que testes históricos sugiram que isso é "normal" para
a estratégia. Você precisará determinar qual porcentagem de drawdown (e em qual período) você pode aceitar
antes de parar de operar sua estratégia. Esta é uma decisão altamente pessoal e, portanto, deve ser considerada
com cuidado.

• Capacidade/Liquidez - No varejo, a menos que você esteja negociando um instrumento altamente ilíquido (como
uma ação de pequena capitalização), você não precisará se preocupar muito com a capacidade da estratégia. A
capacidade determina a escalabilidade da estratégia para obter mais capital. Muitos dos maiores fundos de hedge
sofrem com problemas significativos de capacidade à medida que suas estratégias aumentam a alocação de
capital.

• Parâmetros - Certas estratégias (especialmente aquelas encontradas na comunidade de aprendizado de máquina)


exigem uma grande quantidade de parâmetros. Cada parâmetro extra que uma estratégia exige a torna mais
vulnerável ao viés de otimização (também conhecido como "ajuste de curva"). Você deve tentar direcionar
estratégias com o mínimo de parâmetros possível ou certificar-se de ter dados suficientes para testar suas
estratégias.

• Benchmark - Quase todas as estratégias (a menos que sejam caracterizadas como "retorno absoluto") são
medidas em relação a algum benchmark de desempenho. O benchmark geralmente é um índice que caracteriza
uma grande amostra da classe de ativos subjacente em que a estratégia opera. Se a estratégia opera ações
americanas de grande capitalização, o S&P 500 seria um benchmark natural para comparar sua estratégia. Você
ouvirá os termos "alfa" e "beta" aplicados a estratégias desse tipo.

Observe que não discutimos os retornos reais da estratégia. Por quê? Isoladamente, os retornos nos fornecem
informações limitadas sobre a eficácia da estratégia.
Eles não fornecem informações sobre alavancagem, volatilidade, benchmarks ou requisitos de capital.
Portanto, as estratégias raramente são julgadas apenas pelos seus retornos. Sempre considere os atributos de risco de
uma estratégia antes de analisar os retornos.
Nesta fase, muitas das estratégias encontradas em seu pipeline serão rejeitadas imediatamente, pois não atenderão
aos seus requisitos de capital, restrições de alavancagem, tolerância máxima de drawdown ou preferências de
volatilidade. As estratégias restantes podem agora ser consideradas para backtesting.
No entanto, antes que isso seja possível, é necessário considerar um critério final de rejeição: os dados históricos
disponíveis para testar essas estratégias.

5.4 Obtendo Dados Históricos


Atualmente, a amplitude dos requisitos técnicos para armazenamento de dados históricos em todas as classes de ativos
é substancial. Para se manterem competitivos, tanto o lado comprador (fundos, mesas de apoio) quanto o lado vendedor
(corretoras/dealers) investem pesadamente em sua infraestrutura técnica. É fundamental considerar sua importância.
Em particular, estamos interessados em pontualidade, precisão e requisitos de armazenamento. Discutiremos o
armazenamento de dados em capítulos posteriores do livro.
Na seção anterior, configuramos um pipeline de estratégias que nos permitiu rejeitar determinadas estratégias com
base em nossos próprios critérios de rejeição. Nesta seção, filtraremos mais estratégias com base em nossas
preferências para obter dados históricos. As principais considerações (especialmente no nível de profissionais de varejo)
são os custos dos dados, os requisitos de armazenamento e seu nível de
Machine Translated by Google

36

conhecimento técnico. Também precisamos discutir os diferentes tipos de dados disponíveis e as diferentes considerações
que cada tipo de dado nos impõe.
Vamos começar discutindo os tipos de dados disponíveis e as principais questões sobre as quais precisaremos pensar,
tendo em mente que exploraremos essas questões com bastante profundidade no restante do livro:

• Dados Fundamentais - Incluem dados sobre tendências macroeconômicas, como taxas de juros, inflação, eventos
corporativos (dividendos, desdobramentos de ações), registros na SEC, contas corporativas, resultados, relatórios de
safra, dados meteorológicos, etc. Esses dados são frequentemente usados para avaliar empresas ou outros ativos
com base em critérios fundamentais, ou seja, por meio de alguma previsão de fluxos de caixa futuros. Não incluem
séries de preços de ações. Alguns dados fundamentais estão disponíveis gratuitamente em sites governamentais.
Outros dados fundamentais históricos de longo prazo podem ser extremamente caros. Os requisitos de armazenamento
geralmente não são particularmente grandes, a menos que milhares de empresas estejam sendo estudadas
simultaneamente.

• Dados de Notícias - Os dados de notícias são frequentemente de natureza qualitativa. Consistem em artigos, postagens de blog,
postagens de microblog ("tweets") e editoriais. Técnicas de aprendizado de máquina, como classificadores, são frequentemente
utilizadas para interpretar sentimentos. Esses dados também costumam estar disponíveis gratuitamente ou a preços baixos,
por meio de assinaturas de veículos de comunicação. Os bancos de dados de armazenamento de documentos "NoSQL" mais
recentes são projetados para armazenar esse tipo de dados qualitativos não estruturados.

• Dados de Preços de Ativos - Este é o domínio de dados tradicional da análise quantitativa. Consiste em séries temporais
de preços de ativos. Ações, produtos de renda fixa (títulos), commodities e preços de câmbio se enquadram nessa
classe. Dados históricos diários costumam ser fáceis de obter para classes de ativos mais simples, como ações. No
entanto, uma vez que a precisão e a limpeza são incluídas e os vieses estatísticos são removidos, os dados podem
se tornar caros. Além disso, dados de séries temporais geralmente exigem armazenamento significativo, especialmente
quando se consideram dados intradiários.

• Instrumentos Financeiros - Ações, títulos, futuros e as opções de derivativos mais exóticas têm características e
parâmetros muito diferentes. Portanto, não existe uma estrutura de banco de dados "tamanho único" que possa
acomodá-los. Deve-se ter muito cuidado ao projetar e implementar estruturas de banco de dados para diversos
instrumentos financeiros.

• Frequência - Quanto maior a frequência dos dados, maiores os custos e os requisitos de armazenamento. Para
estratégias de baixa frequência, dados diários costumam ser suficientes. Para estratégias de alta frequência, pode
ser necessário obter dados de ticks e até mesmo cópias históricas de dados específicos do livro de ordens de uma
bolsa de valores. Implementar um mecanismo de armazenamento para esse tipo de dados exige muita tecnologia e é
adequado apenas para quem possui sólida formação em programação/técnica.

• Benchmarks - As estratégias descritas acima geralmente serão comparadas a um benchmark.


Isso geralmente se manifesta como uma série temporal financeira adicional. Para ações, geralmente é um benchmark
nacional de ações, como o índice S&P 500 (EUA) ou o FTSE100 (Reino Unido). Para um fundo de renda fixa, é útil
comparar com uma cesta de títulos ou produtos de renda fixa. A "taxa livre de risco" (ou seja, a taxa de juros
apropriada) também é outro benchmark amplamente aceito. Todas as categorias de classes de ativos possuem um
benchmark preferencial, portanto, será necessário pesquisá-lo com base em sua estratégia específica, se você deseja
gerar interesse externo em sua estratégia.

• Tecnologia - As pilhas de tecnologia por trás de um centro de armazenamento de dados financeiros são complexas.
No entanto, geralmente gira em torno de um mecanismo de cluster de banco de dados, como um Sistema de
Gerenciamento de Banco de Dados Relacional (RDBMS), como MySQL, SQL Server, Oracle ou um Mecanismo de
Armazenamento de Documentos (ou seja, "NoSQL"). O acesso é feito por meio de código de aplicativo de "lógica de
negócios" que consulta o banco de dados e fornece acesso a ferramentas externas, como MATLAB, R ou Excel.
Frequentemente, essa lógica de negócios é escrita em C++, Java ou Python. Você também precisará hospedar esses
dados em algum lugar, seja em seu próprio computador pessoal ou remotamente por meio de servidores de internet.
Produtos como a Amazon Web Services tornaram isso mais simples e barato em
Machine Translated by Google

37

nos últimos anos, mas ainda exigirá conhecimentos técnicos significativos para alcançar um resultado robusto
maneiras.

Como pode ser visto, uma vez que uma estratégia tenha sido identificada através do pipeline, será necessário
avaliar a disponibilidade, os custos, a complexidade e os detalhes de implementação de um conjunto específico de
dados históricos. Você pode achar necessário rejeitar uma estratégia baseada apenas em considerações sobre
dados históricos. Esta é uma área ampla, e equipes de doutores trabalham em grandes fundos para garantir que a
precificação seja precisa e pontual. Não subestime as dificuldades de criar um data center robusto para seus fins de
backtesting!
Gostaria de dizer, no entanto, que muitas plataformas de backtesting podem fornecer esses dados
automaticamente — a um custo. Assim, você se livra de grande parte da dor de cabeça da implementação e pode
se concentrar exclusivamente na implementação e otimização da estratégia. Ferramentas como a TradeStation
possuem essa capacidade. No entanto, minha visão pessoal é implementar o máximo possível internamente e evitar
terceirizar partes da pilha para fornecedores de software. Prefiro estratégias de frequência mais alta devido aos seus
índices de Sharpe mais atraentes, mas elas costumam estar fortemente acopladas à pilha de tecnologia, onde a
otimização avançada é crucial.
Machine Translated by Google

38
Machine Translated by Google

Parte III

Desenvolvimento de plataforma de dados

39
Machine Translated by Google
Machine Translated by Google

Capítulo 6

Instalação de software

Este capítulo discutirá em detalhes como instalar um ambiente de negociação algorítmica. A escolha do sistema operacional
é considerada um primeiro passo necessário, com as três principais opções descritas.
Posteriormente, o Linux é escolhido como o sistema de escolha (Ubuntu em particular) e o Python é instalado com todas
as bibliotecas necessárias.
A instalação de pacotes/bibliotecas é frequentemente abordada superficialmente em livros adicionais, mas pessoalmente
sinto que pode ser um obstáculo para muitos, por isso dediquei um capítulo inteiro a ela. Infelizmente, a realidade é que o
capítulo ficará obsoleto no momento em que for lançado. Novas versões de sistemas operacionais surgem e os pacotes são
constantemente atualizados. Portanto, é provável que haja detalhes específicos de implementação.

Se tiver problemas para instalar ou trabalhar com esses pacotes, verifique as versões instaladas e atualize-as, se
necessário. Se ainda tiver problemas, sinta-se à vontade para me enviar um e-mail para mike@[Link] e tentarei
ajudar.

6.1 Escolha do sistema operacional


A primeira grande escolha ao escolher uma plataforma de negociação algorítmica é o sistema operacional. Em certo
sentido, isso será ditado pela linguagem de programação primária ou pela forma de conexão com a corretora. Hoje em dia,
a maioria dos softwares, especialmente os de código aberto, é multiplataforma e, portanto, a escolha é menos restrita.

6.1.1 Microsoft Windows


O Windows é provavelmente a opção "padrão" de muitos traders algorítmicos. É extremamente familiar e, apesar das
críticas em contrário, em certas formas é bastante robusto. O Windows 8 não foi muito bem recebido, mas a versão anterior,
o Windows 7, é considerado um sistema operacional sólido.
Certas ferramentas no setor de negociação algorítmica funcionam apenas no Windows, em particular o servidor
IQFeed, necessário para baixar dados de ticks do DTN IQFeed. Além disso, o Windows é a plataforma nativa do
Microsoft .NET Framework, no qual uma grande quantidade de software financeiro é escrita, utilizando C++ e C#.

Se você não deseja usar o Windows, às vezes é possível executar o Windows


software em um sistema baseado em UNIX usando o emulador WINE ([Link]

6.1.2 Mac OSX


O Mac OSX combina a facilidade gráfica do Windows (alguns dizem que o melhora substancialmente!) com a robustez de
um sistema baseado em UNIX (FreeBSD). Embora eu use um MacBook Air para todo o meu trabalho "do dia a dia", como
web/e-mail e desenvolvimento do site QuantStart, descobri que é extremamente trabalhoso instalar uma pilha de pesquisa
algorítmica completa, baseada em Python, no Mac OSX.

O cenário de pacotes do Mac OSX é significativamente fragmentado, com Homebrew e Mac-Ports sendo os principais
concorrentes. A instalação a partir da fonte é complicada devido à compatibilidade proprietária.

41
Machine Translated by Google

42

processo de compilação (usando XCode). Ainda não consegui instalar o NumPy, o SciPy e o Pandas no meu MacBook
até o momento em que escrevo!
No entanto, se você conseguir navegar pelo campo minado que é a instalação do Python no Mac OSX, ele pode
fornecer um ótimo ambiente para pesquisa algorítmica. Como o Interactive Brokers Trader Workstation é baseado em
Java, ele roda sem problemas no Mac OSX.

6.1.3 Linux
"Linux" refere-se a um conjunto de distribuições UNIX gratuitas, como Cent OS, Debian e Ubuntu. Não pretendo entrar
em detalhes sobre as vantagens e desvantagens de cada distribuição; em vez disso, vou me concentrar nas distribuições
baseadas em Debian. Em particular, considerarei o Ubuntu Desktop como o ambiente de negociação algorítmica.

O gerenciamento de pacotes do aptitude facilita a instalação das bibliotecas subjacentes necessárias. Além disso,
é fácil criar um ambiente virtual para Python que pode isolar seu código de negociação algorítmica de outros aplicativos
Python. Nunca tive problemas (grandes) para instalar um ambiente Python em um sistema Ubuntu moderno e, por isso,
escolhi este como o ambiente principal para conduzir minhas negociações.

Se você quiser experimentar o Ubuntu antes de se comprometer totalmente, por exemplo, com dual-boot, é possível
usar o VirtualBox ([Link] para instalá-lo. Tenho um guia detalhado sobre o QuantStart (http://
[Link]/articles/Installing-a-Desktop-Algorithmic-Trading-Research-Environment-using-Ubuntu-Linux-and-
Python), que descreve o processo.

6.2 Instalando um ambiente Python no Ubuntu Linux


Nesta seção, discutiremos como configurar um ambiente de desenvolvimento robusto, eficiente e interativo para pesquisa
de estratégias de negociação algorítmica, utilizando o Ubuntu Desktop Linux e a linguagem de programação Python.
Utilizaremos esse ambiente para todas as implementações subsequentes de negociação algorítmica.

Para criar o ambiente de pesquisa, instalaremos as seguintes ferramentas de software, todas elas
são de código aberto e gratuitos para download:

• Ubuntu Desktop Linux - O sistema operacional

• Python - O ambiente de programação central

• NumPy/SciPy - Para cálculos rápidos e eficientes de matrizes/arrays vetorizados

• IPython - Para desenvolvimento visual interativo com Python

• matplotlib - Para visualização gráfica de dados

• pandas - Para "confusão" de dados e análise de séries temporais

• scikit-learn - Para algoritmos de aprendizado de máquina e inteligência artificial

• IbPy - Para realizar negociações com a API da Interactive Brokers

Essas ferramentas, juntamente com um banco de dados mestre de títulos MySQL adequado, nos permitirão criar um
ambiente rápido e interativo de pesquisa e backtesting de estratégias. O Pandas foi projetado para "conversão de dados"
e pode importar e limpar dados de séries temporais com muita eficiência. O NumPy/SciPy, em execução por baixo,
mantém o sistema extremamente otimizado. O IPython/matplotlib (e o qtconsole descrito abaixo) permitem a visualização
interativa dos resultados e a iteração rápida. O scikit-learn nos permite aplicar técnicas de aprendizado de máquina às
nossas estratégias para aprimorar ainda mais o desempenho.
Machine Translated by Google

43

6.2.1 Python
As versões mais recentes do Ubuntu, que no momento da escrita deste texto é a 13.10, ainda utilizam a família de
versões Python 2.7.x. Embora haja uma transição em andamento para a versão 3.3.x, a maioria das bibliotecas é
totalmente compatível com a versão 2.7.x. Por isso, optei por usá-la para negociação algorítmica.
É provável que as coisas evoluam rapidamente, então, em alguns anos, a versão 3.3.x poderá ser a versão
predominante. Começaremos agora com a instalação do ambiente Python.
A primeira coisa a fazer em qualquer sistema Ubuntu Linux novo é atualizar e atualizar os pacotes. O primeiro
informa o Ubuntu sobre novos pacotes disponíveis, enquanto o segundo realiza o processo de substituição de pacotes
mais antigos por versões mais recentes. Execute os seguintes comandos em uma sessão de terminal e suas senhas
serão solicitadas:

sudo apt-get -y update sudo


apt-get -y upgrade

Observe que o prefixo -y indica ao Ubuntu que você deseja aceitar "sim" para todas as perguntas de sim/não.
"sudo" é um comando do Ubuntu/Debian Linux que permite que outros comandos sejam executados com privilégios
de administrador. Como estamos instalando nossos pacotes em todo o site, precisamos de "acesso root" à máquina e,
portanto, precisamos usar "sudo".
Após a execução bem-sucedida de ambos os comandos de atualização, precisamos instalar os pacotes de
desenvolvimento Python e os compiladores necessários para compilar todo o software. Observe que estamos instalando
o build-essential , que contém os compiladores GCC e a biblioteca de álgebra linear LAPACK, bem como o pip, que é
o sistema de gerenciamento de pacotes Python:

sudo apt-get install python-pip python-dev python2.7-dev \ build-essential


liblapack-dev libblas-dev

O próximo passo é instalar as bibliotecas numéricas e de análise de dados do Python.

6.2.2 NumPy, SciPy e Pandas


Após a instalação dos pacotes necessários, podemos prosseguir com a instalação do NumPy via pip, o gerenciador de
pacotes Python. O Pip baixará um arquivo zip do pacote e o compilará a partir do código-fonte. Lembre-se de que a
compilação levará algum tempo, podendo levar 10 minutos ou mais, dependendo da sua CPU:

sudo pip install numpy

Após a instalação do NumPy, precisamos verificar se ele funciona antes de prosseguir. Se você olhar no terminal,
verá seu nome de usuário seguido pelo nome do seu computador. No meu caso, é mhallsmoore@algobox, seguido
pelo prompt. No prompt, digite python e tente importar o NumPy. Testaremos seu funcionamento calculando a média
aritmética de uma lista:

mhallsmoore@algobox:~$ python
Python 2.7.4 (padrão, 26 de setembro de 2013, [Link])
[GCC 4.7.3] no Linux2 Digite
"help", "copyright", "credits" ou "license" para mais informações. >>> import numpy >>> from
numpy import mean
>>> mean([1,2,3])

2.0
>>> saída()

Agora que o NumPy foi instalado com sucesso, queremos instalar a biblioteca científica Python conhecida como
SciPy. Ela possui algumas dependências de pacotes próprias, incluindo a biblioteca ATLAS e o compilador GNU
Fortran, que devem ser instalados primeiro:

sudo apt-get install libatlas-base-dev gfortran

Estamos prontos para instalar o SciPy agora, com o pip. A compilação levará bastante tempo, talvez de 10 a 20
minutos, dependendo da velocidade da CPU:

sudo pip install scipy


Machine Translated by Google

44

O SciPy já está instalado. Vamos testá-lo de forma semelhante ao NumPy ao calcular o desvio padrão de uma lista de
inteiros:

mhallsmoore@algobox:~$ python
Python 2.7.4 (padrão, 26 de setembro de 2013, [Link])
[GCC 4.7.3] no Linux2. Digite
"help", "copyright", "credits" ou "license" para obter mais informações. >>> import scipy >>> from scipy import
std >>> std([1,2,3])
0.81649658092772603

>>> saída()

A tarefa final desta seção é instalar a biblioteca de análise de dados do Pandas. Não precisamos
quaisquer dependências adicionais nesta fase, pois são cobertas pelo NumPy e SciPy:

sudo pip install pandas


Agora podemos testar a instalação do pandas, como antes:

>>> de pandas importar DataFrame >>>


pd = DataFrame() >>> pd

DataFrame vazio
Colunas: []
Índice: [] >>>
exit()

Agora que as bibliotecas numéricas e científicas básicas foram instaladas, instalaremos o


bibliotecas estatísticas e de aprendizado de máquina, statsmodels e scikit-learn.

6.2.3 Statsmodels e Scikit-Learn


A instalação prossegue como antes, utilizando pip para instalar os pacotes:

sudo pip install statsmodels sudo


pip install scikit-learn
Ambas as bibliotecas podem ser testadas:

mhallsmoore@algobox:~$ python
Python 2.7.4 (padrão, 26 de setembro de 2013, [Link])
[GCC 4.7.3] no Linux2 Digite
"help", "copyright", "credits" ou "license" para mais informações. >>> from sklearn import datasets >>> iris
= datasets.load_iris() >>> iris

..
..
'largura da pétala (cm)']}
>>>
Agora que as duas bibliotecas estatísticas estão instaladas, podemos instalar a visualização e o desenvolvimento
ferramentas de desenvolvimento, IPython e matplotlib.

6.2.4 PyQt, IPython e Matplotlib


A primeira tarefa é instalar os pacotes de dependência para matplotlib, a biblioteca de gráficos do Python.
Como matplotlib é um pacote Python, não podemos usar pip para instalar as bibliotecas subjacentes para trabalhar com PNGs,
JPEGs e fontes freetype, então precisamos que o Ubuntu as instale para nós:

sudo apt-get install libpng-dev libjpeg8-dev libfreetype6-dev


Machine Translated by Google

45

Agora podemos instalar o matplotlib:

sudo pip install matplotlib

A última tarefa desta seção é instalar o IPython. Este é um interpretador Python interativo que oferece um fluxo de trabalho
significativamente mais simplificado em comparação com o uso do console Python padrão. Nos capítulos seguintes,
enfatizaremos toda a utilidade do IPython para o desenvolvimento de negociação algorítmica:

sudo pip install ipython

Embora o IPython seja suficientemente útil por si só, ele pode se tornar ainda mais poderoso com a inclusão do qtconsole,
que permite a inclusão de visualizações em linha no matplotlib. No entanto, é preciso um pouco mais de trabalho para colocá-lo
em funcionamento.
Primeiro, precisamos instalar a biblioteca Qt:

sudo apt-get install libqt4-core libqt4-gui libqt4-dev

O qtconsole tem alguns pacotes de dependências adicionais, nomeadamente as bibliotecas ZMQ e Pygments:

sudo apt-get install libzmq-dev sudo


pip install pyzmq sudo pip
install pygments

É simples testar o IPython digitando o seguinte comando:

ipython qtconsole --pylab=inline

Para testar o IPython, um gráfico simples pode ser gerado digitando os seguintes comandos. Observe que
Incluí a entrada/saída numerada do IPython que você não precisa digitar:

Em [1]: x=[Link]([1,2,3])

Em [2]: plot(x)
Saída[2]: [<[Link].Line2D em 0x392a1d0>]

Isso deve exibir um gráfico matplotlib em linha. Fechar o IPython nos permite continuar com a instalação.

6.2.5 IbPy e estação de trabalho do Trader


A Interactive Brokers é uma das principais corretoras utilizadas por traders algorítmicos de varejo devido aos seus requisitos de
saldo mínimo de conta relativamente baixos (10.000 USD) e à sua API (relativamente) simples. Nesta seção, instalaremos o
IbPy e o Trader Workstation, que usaremos posteriormente para executar negociações automatizadas.

Quero enfatizar que não negociaremos nenhum capital real com este download!
Vamos simplesmente instalar um software que nos permitirá testar uma "conta demo", que fornece um simulador de mercado
com dados desatualizados em "tempo real".
Divulgação: Não tenho nenhuma afiliação com a Interactive Brokers. Já os utilizei antes em uma
contexto de fundos profissionais e, portanto, estou familiarizado com seu software.
O IbPy é um wrapper Python escrito em torno da API da Interactive Brokers baseada em Java. Ele torna o desenvolvimento
de sistemas de negociação algorítmica em Python um pouco menos problemático. Ele será usado como base para toda a
comunicação subsequente com a Interactive Brokers. Uma alternativa é usar o protocolo FIX, mas não consideraremos esse
método neste livro.
Como o IBPy é mantido no site de controle de versão do código-fonte do GitHub, como um repositório git, precisaremos
instalar o git. Isso é feito por:

sudo apt-get install git-core

Após a instalação do git, é necessário criar um subdiretório para armazenar o IBPy. Ele pode ser simplesmente colocado
abaixo do diretório inicial:

mkdir ~/ibapi
Machine Translated by Google

46

O próximo passo é baixar o IBPy por meio do comando 'git clone':

cd ~/ibapi git
clone [Link]

O passo final é entrar no diretório IbPy e instalar usando o Python setuptools:

cd ~/ibapi/IbPy python
[Link] instalar

Isso conclui a instalação do IBPy. O próximo passo é instalar o Trader Workstation. No momento da
redação deste texto, era necessário seguir este link (IB), que leva diretamente para a página de download
do Trader Workstation na Interactive Brokers. Selecione a plataforma que deseja utilizar. Neste caso,
escolhi o download para UNIX, que pode ser encontrado aqui (IB Unix Download).

Nesse link será descrito o restante do processo, mas vou replicá-lo aqui para
completude. O arquivo baixado será chamado unixmacosx_latest.jar. Abra o arquivo:

jar xf unixmacosx_latest.jar

Em seguida, vá para o diretório IBJts e carregue o TWS:


cd IBJts
java -cp [Link]:[Link] -Xmx512M -XX:MaxPermSize=128M [Link] .

Isso apresentará a tela de login do Trader Workstation. Se você escolher o nome de usuário
"edemo" e a senha "demo user" e você será logado no sistema.
Isso conclui a instalação de um ambiente de negociação algorítmica completo em Python e Ubuntu. A
próxima etapa é começar a coletar e armazenar dados históricos de preços para nossas estratégias.
Machine Translated by Google

Capítulo 7

Armazenamento de dados financeiros

Na negociação algorítmica, os holofotes geralmente se concentram no componente do modelo alfa do sistema de negociação
completo. Este componente gera os sinais de negociação, antes da filtragem por um sistema de gestão de risco e construção
de portfólio. Assim, os traders algorítmicos frequentemente dedicam uma parte significativa de seu tempo de pesquisa
refinando o modelo alfa para otimizar uma ou mais métricas antes da implementação da estratégia em produção.

No entanto, a qualidade de um modelo alfa depende dos dados que o alimentam. Este conceito é bem caracterizado
pelo velho ditado da ciência da computação: "lixo que entra, lixo que sai". É absolutamente crucial que dados precisos e
oportunos sejam usados para alimentar o modelo alfa. Caso contrário, os resultados serão, na melhor das hipóteses,
ruins ou, na pior, completamente incorretos. Isso levará a um desempenho significativamente inferior quando o sistema
for implantado em tempo real.
Neste capítulo, discutiremos questões relacionadas à aquisição e ao fornecimento de dados precisos e oportunos para um
sistema de backtesting de estratégias algorítmicas e, por fim, para um mecanismo de execução de negociações. Em particular,
estudaremos como obter dados financeiros e como armazená-los. Os capítulos subsequentes discutirão como limpá-los e
exportá-los. No setor financeiro, esse tipo de serviço de dados é conhecido como banco de dados mestre de títulos.

7.1 Bancos de Dados Mestres de Valores Mobiliários

Um banco de dados mestre de valores mobiliários é um banco de dados de toda a organização que armazena dados
fundamentais, de precificação e transacionais para uma variedade de instrumentos financeiros em diferentes classes de
ativos. Ele fornece acesso a essas informações de forma consistente para uso por outros departamentos, como gestão
de risco, compensação/liquidação e negociação proprietária.
Em grandes organizações, uma variedade de instrumentos e dados serão armazenados. Aqui estão alguns dos
instrumentos que podem ser de interesse para uma empresa:

• Ações

• Opções de ações

• Índices

• Câmbio

• Taxas de juros

• Futuros

• Mercadorias

• Títulos - Governo e Corporativo

• Derivativos - Caps, Floors, Swaps

47
Machine Translated by Google

48

Bancos de dados de títulos geralmente contam com equipes de desenvolvedores e especialistas em dados, garantindo
alta disponibilidade dentro de uma instituição financeira. Embora isso seja necessário em grandes empresas, no varejo ou em
um pequeno fundo, um banco de dados de títulos pode ser muito mais simples. De fato, embora grandes bancos de dados de
títulos utilizem sistemas de banco de dados e análise corporativos caros, é possível usar software de código aberto para
fornecer o mesmo nível de funcionalidade, desde que o sistema seja bem otimizado.

7.2 Conjuntos de dados financeiros

Para o trader algorítmico de varejo ou para pequenos fundos quantitativos, os conjuntos de dados mais comuns são os preços
históricos de fim de dia e intradiários para ações, índices, futuros (principalmente commodities ou renda fixa) e câmbio (forex).
Para simplificar esta discussão, nos concentraremos apenas nos dados de fim de dia (EOD) para ações, ETFs e índices de
ações. Seções posteriores discutirão a adição de dados de frequência mais alta, classes de ativos adicionais e dados de
derivativos, que possuem requisitos mais avançados.

Os dados de EOD para ações são fáceis de obter. Existem vários serviços que fornecem acesso para
grátis via APIs disponíveis na web:

• Yahoo Finanças - [Link]

• Google Finanças - [Link]

• QuantQuote - [Link] (apenas dados do S&P500 EOD)

• EODData - [Link] (requer registro)

É simples baixar manualmente dados históricos de títulos individuais, mas isso se torna demorado se muitas ações
precisarem ser baixadas diariamente. Portanto, um componente importante do nosso banco de dados de títulos será a
atualização automática do conjunto de dados.
Outro problema é o período de retrospectiva. Até que ponto no passado precisamos ir com nossos dados? Isso será
específico para os requisitos da sua estratégia de negociação, mas existem certos problemas que abrangem todas as
estratégias. O mais comum é a mudança de regime, que frequentemente é caracterizada por um novo ambiente regulatório,
períodos de maior/menor volatilidade ou mercados com tendências de longo prazo. Por exemplo, uma estratégia de longo
prazo de acompanhamento de tendências/momentum de direção curta provavelmente teria um desempenho muito bom de
2000 a 2003 ou de 2007 a 2009. No entanto, teria passado por um período difícil de 2003 a 2007 ou de 2009 até o presente.

Minha regra geral é obter o máximo de dados possível, especialmente para dados EOD, onde o armazenamento é barato.
Só porque os dados existem no seu mestre de segurança, não significa que eles devem ser utilizados.
Há ressalvas quanto ao desempenho, pois tabelas de banco de dados maiores significam tempos de consulta mais longos (veja
abaixo), mas os benefícios de ter mais pontos de amostra geralmente superam quaisquer problemas de desempenho.

Como acontece com todos os dados financeiros, é fundamental estar ciente de erros, como valores altos/baixos incorretos.
preços ou viés de sobrevivência, que discuti longamente em capítulos anteriores.

7.3 Formatos de armazenamento

Existem três maneiras principais de armazenar dados financeiros. Todas elas possuem diferentes graus de acesso, desempenho
e capacidades estruturais. Analisaremos cada uma delas separadamente.

7.3.1 Armazenamento de arquivo simples

O armazenamento de dados mais simples para dados financeiros, e a maneira pela qual você provavelmente receberá os
dados de qualquer fornecedor de dados, é o formato de arquivo simples. Arquivos simples geralmente utilizam o formato de
Variáveis Separadas por Vírgula (CSV), que armazena uma matriz bidimensional de dados como uma série de linhas, com os
dados das colunas separados por um delimitador (geralmente uma vírgula, mas pode ser um espaço em branco, como um
espaço ou tabulação). Para dados de preços de EOD, cada linha representa um dia de negociação pelo paradigma OHLC (ou
seja, os preços na abertura, máxima, mínima e fechamento do período de negociação).
Machine Translated by Google

49

A vantagem dos arquivos simples é a simplicidade e a possibilidade de serem compactados para arquivamento ou
download. As principais desvantagens residem na falta de capacidade de consulta e no baixo desempenho para iteração em
grandes conjuntos de dados. O SQLite e o Excel atenuam alguns desses problemas ao fornecer determinados recursos de
consulta.

7.3.2 Armazenamentos de documentos/NoSQL

Armazenamentos de documentos/bancos de dados NoSQL, embora certamente não sejam um conceito novo, ganharam
destaque significativo nos últimos anos devido ao seu uso em empresas "web-scale" como Google, Facebook e Twitter. Eles
diferem substancialmente dos sistemas RDBMS por não existir o conceito de esquemas de tabela. Em vez disso, existem
coleções e documentos, que são as analogias mais próximas de tabelas e registros, respectivamente. Existe uma ampla
taxonomia de armazenamentos de documentos, cuja discussão está bem além deste capítulo! No entanto, alguns dos
armazenamentos mais populares incluem MongoDB, Cassandra e CouchDB.

Os armazenamentos de documentos, em aplicações financeiras, são mais adequados para dados fundamentais ou metadados.
Dados fundamentais para ativos financeiros vêm em muitas formas, como eventos corporativos, demonstrações de resultados,
registros da SEC, etc. Portanto, a natureza sem esquema dos bancos de dados NoSQL é bem adequada. No entanto, os bancos
de dados NoSQL não são bem projetados para séries temporais, como dados de preços de alta resolução, e, portanto, não os
consideraremos mais detalhadamente neste capítulo.

7.3.3 Sistemas de Gerenciamento de Banco de Dados Relacionais

Um sistema de gerenciamento de banco de dados relacional (RDBMS) utiliza o modelo relacional para armazenar dados. Esses
bancos de dados são particularmente adequados para dados financeiros, pois diferentes "objetos" (como bolsas, fontes de
dados, preços) podem ser separados em tabelas com relacionamentos definidos entre eles.

Os RDBMS utilizam Linguagem de Consulta Estruturada (SQL) para realizar consultas complexas a dados financeiros.
Exemplos de RDBMS incluem Oracle, MySQL, SQLServer e PostgreSQL.

As principais vantagens dos SGBDRs são a simplicidade de instalação, a independência de plataforma, a facilidade de
consulta, a integração com os principais softwares de backtest e os recursos de alto desempenho em larga escala (embora
alguns argumentem que este último não é o caso!). Suas desvantagens geralmente se devem à complexidade da personalização
e às dificuldades de atingir esse desempenho sem o conhecimento prévio de como os dados dos SGBDRs são armazenados.
Além disso, eles possuem esquemas semirrígidos e, portanto, os dados frequentemente precisam ser modificados para se
adequarem a esses designs. Isso difere dos armazenamentos de dados NoSQL, onde não há esquema.

Para todo o código de implementação de preços históricos futuros neste livro, usaremos o MySQL RDBMS. Ele é gratuito,
de código aberto, multiplataforma, altamente robusto e seu comportamento em escala é bem documentado, o que o torna uma
escolha sensata para trabalho quantitativo.

7.4 Estrutura de Dados Históricos


Existe um conjunto significativo de teorias e pesquisas acadêmicas realizadas na área da ciência da computação para o design
ideal de armazenamentos de dados. No entanto, não entraremos em muitos detalhes, pois é fácil se perder em detalhes! Em
vez disso, apresentarei um padrão comum para a construção de um master de segurança de ações, que você pode modificar
conforme achar necessário para suas próprias aplicações.
A primeira tarefa é definir nossas entidades, que são elementos dos dados financeiros que eventualmente serão mapeados
para tabelas no banco de dados. Para um banco de dados mestre de ações, prevejo as seguintes entidades:

• Trocas - Qual é a fonte original final dos dados?

• Fornecedor - De onde um ponto de dados específico é obtido?

• Instrumento/Ticker - O ticker/símbolo do patrimônio ou índice, juntamente com informações corporativas da empresa ou


fundo subjacente.
Machine Translated by Google

50

• Preço - O preço real de um determinado título em um determinado dia.

• Ações Corporativas - A lista de todas as divisões de ações ou ajustes de dividendos (isso pode levar
para uma ou mais tabelas), necessário para ajustar os dados de preços.

• Feriados nacionais - Para evitar a classificação incorreta de feriados comerciais como erros de dados ausentes,
pode ser útil armazenar feriados nacionais e fazer referências cruzadas.

Existem problemas significativos com relação ao armazenamento de tickers canônicos. Posso atestar isso por
experiência própria em um fundo de hedge que lidou exatamente com esse problema! Diferentes fornecedores usam
métodos diferentes para resolver tickers e, portanto, combinam diversas fontes para garantir a precisão.
Além disso, empresas entram em falência, ficam expostas a fusões e aquisições (ou seja, são adquiridas e mudam de
nome/símbolo) e podem ter várias classes de ações negociadas em bolsa. Muitos de vocês não precisarão se preocupar
com isso, pois seu universo de tickers estará limitado aos componentes de índices maiores (como o S&P 500 ou o FTSE
350).

7.5 Avaliação da precisão dos dados


Dados históricos de preços de fornecedores estão sujeitos a muitas formas de erro:

• Ações Corporativas - Tratamento incorreto de desdobramentos de ações e ajustes de dividendos. Um


deve ter absoluta certeza de que as fórmulas foram implementadas corretamente.

• Picos - Pontos de precificação que excedem em muito certos níveis históricos de volatilidade. É preciso ter cuidado
aqui, pois esses picos ocorrem - veja o Flash Crash de maio como um exemplo assustador. Picos também podem
ser causados por desdobramentos de ações não considerados quando ocorrem. Scripts de filtro de picos são
usados para notificar os traders sobre tais situações.

• Agregação OHLC - Dados OHLC gratuitos, como os do Yahoo/Google, são particularmente propensos a situações
de "agregação de ticks ruins", em que bolsas menores processam pequenas negociações bem acima dos preços
"principais" da bolsa no dia, levando a máximas/mínimas superinflacionadas após a agregação. Isso não é tanto
um "erro" em si, mas sim um problema a ser observado.

• Dados Ausentes - Dados ausentes podem ser causados pela falta de negociações em um determinado período
(comum em dados de resolução de segundos/minutos de small-caps ilíquidas), por feriados de negociação ou
simplesmente por um erro no sistema de negociação. Dados ausentes podem ser preenchidos (ou seja,
preenchidos com o valor anterior), interpolados (linearmente ou não) ou ignorados, dependendo do sistema de
negociação.

Muitos desses erros dependem de julgamento manual para decidir como proceder. É possível automatizar a
notificação desses erros, mas é muito mais difícil automatizar sua solução. Por exemplo, é preciso definir o limite para
ser informado sobre picos — quantos desvios-padrão usar e em qual período de retrospectiva? Um desvio-padrão muito
alto deixará de detectar alguns picos, mas um desvio-padrão muito baixo e muitos anúncios de notícias incomuns levarão
a falsos positivos. Todas essas questões exigem julgamento antecipado do operador quantitativo.

Também é necessário decidir como corrigir os erros. Os erros devem ser corrigidos assim que forem identificados e,
em caso afirmativo, deve ser realizada uma trilha de auditoria? Isso exigirá uma tabela extra no banco de dados.
Isso nos leva ao tópico de preenchimento, que é uma questão particularmente insidiosa para backtesting.
Trata-se da correção automática de dados incorretos a montante. Se o seu fornecedor de dados corrigir um erro histórico,
mas uma estratégia de negociação testada e comprovada estiver em produção com base em pesquisas de dados
incorretos anteriores, decisões precisam ser tomadas em relação à eficácia da estratégia. Isso pode ser parcialmente
atenuado pelo pleno conhecimento das métricas de desempenho da sua estratégia (em particular, a variância nas suas
características de ganho/perda para cada operação). As estratégias devem ser escolhidas ou projetadas de forma que
um único ponto de dados não distorça significativamente o desempenho da estratégia.
Machine Translated by Google

51

7.6 Automação

A vantagem de escrever scripts de software para realizar o download, o armazenamento e a limpeza dos dados é que os scripts
podem ser automatizados por meio de ferramentas fornecidas pelo sistema operacional. Em sistemas baseados em UNIX (como
Mac OSX ou Linux), pode-se usar o crontab, um processo de execução contínua que permite a execução de scripts específicos
em horários personalizados ou em períodos regulares. Existe um processo equivalente no MS Windows conhecido como
Agendador de Tarefas.
Um processo de produção, por exemplo, pode automatizar o download de todos os preços de fim de dia do S&P500 assim
que forem publicados por meio de um fornecedor de dados. Em seguida, ele executará automaticamente os dados ausentes
mencionados e os scripts de filtragem de picos, alertando o trader por e-mail, SMS ou algum outro tipo de notificação. Nesse
ponto, qualquer ferramenta de backtesting terá acesso automático aos dados recentes, sem que o trader precise fazer nada!
Dependendo se o seu sistema de negociação está localizado em um desktop ou em um servidor remoto, você pode optar por
um processo semiautomático ou totalmente automatizado para essas tarefas.

7.7 Disponibilidade de dados

Assim que os dados forem atualizados automaticamente e estiverem armazenados no RDBMS, será necessário inseri-los no
software de backtesting. Esse processo dependerá muito de como seu banco de dados está instalado e se o seu sistema de
negociação é local (ou seja, em um computador desktop) ou remoto (como em um servidor de câmbio co-localizado).

Uma das considerações mais importantes é minimizar o excesso de Entrada/Saída (E/S), pois isso pode ser extremamente
custoso, tanto em termos de tempo quanto de dinheiro, considerando conexões remotas com alto custo de largura de banda. A
melhor maneira de abordar esse problema é mover apenas os dados necessários através de uma conexão de rede (por meio
de consulta seletiva) ou exportar e compactar os dados.

Muitos RDBMS suportam a tecnologia de replicação, que permite que um banco de dados seja clonado em outro sistema
remoto, geralmente com um certo grau de latência. Dependendo da sua configuração e da quantidade de dados, isso pode levar
apenas minutos ou segundos. Uma abordagem simples é replicar um banco de dados remoto em um desktop local. No entanto,
esteja ciente de que problemas de sincronização são comuns e demorados para serem corrigidos!

7.8 MySQL para Mestres em Valores Mobiliários

Agora que discutimos a ideia por trás de um banco de dados mestre de segurança, é hora de realmente construir um. Para isso,
utilizaremos duas tecnologias de código aberto: o banco de dados MySQL e a linguagem de programação Python. Ao final deste
capítulo, você terá um banco de dados mestre de segurança de ações completo para conduzir análises de dados adicionais
para sua pesquisa quantitativa de negociação.

7.8.1 Instalando o MySQL


A instalação do MySQL no Ubuntu é simples. Basta abrir um terminal e digitar o seguinte:

sudo apt-get install mysql-server


Por fim, será solicitada uma senha de root. Esta é a sua conta de administração principal.
senha, então não a esqueça! Digite a senha e a instalação prosseguirá e será concluída.

7.8.2 Configurando o MySQL


Agora que o MySQL está instalado em seu sistema, podemos criar um novo banco de dados e um usuário para interagir com
ele. Você será solicitado a fornecer uma senha de root durante a instalação. Para fazer login no MySQL pela linha de comando,
use a seguinte linha e, em seguida, digite sua senha:

mysql -u root -p
Machine Translated by Google

52

Depois de efetuar login no MySQL, você pode criar um novo banco de dados chamado securities_master e selecioná-lo:

mysql> CRIAR BANCO DE DADOS security_master; mysql>


USE security_master;

Depois de criar um banco de dados, é necessário adicionar um novo usuário para interagir com ele.
Embora você possa usar o usuário root , isso é considerado uma prática ruim do ponto de vista da segurança, pois concede
permissões em excesso e pode levar ao comprometimento do sistema. Em uma máquina local, isso é irrelevante, mas em um
ambiente de produção remoto, você certamente precisará criar um usuário com permissões reduzidas. Neste caso, nosso
usuário será chamado sec_user. Lembre-se de substituir a senha por uma senha segura:

mysql> CRIAR USUÁRIO 'sec_user'@'localhost' IDENTIFICADO POR 'password'; mysql>


CONCEDER TODOS OS PRIVILÉGIOS EM securities_master.* PARA 'sec_user'@'localhost'; mysql> LIBERAR
PRIVILÉGIOS;

As três linhas acima criam e autorizam o usuário a usar o security_master e aplicar esses privilégios. A partir de agora,
qualquer interação com o banco de dados utilizará o usuário sec_user .

7.8.3 Projeto de Esquema para Ações EOD


Agora instalamos o MySQL e configuramos um usuário para interagir com nosso banco de dados.
Nesta fase, estamos prontos para construir as tabelas necessárias para armazenar nossos dados financeiros. Para um modelo
de ações simples e direto, criaremos quatro tabelas:

• Bolsas de Valores - A tabela de bolsas lista as bolsas das quais desejamos obter informações sobre precificação de
ações. Neste caso, serão quase exclusivamente a Bolsa de Valores de Nova York (NYSE) e a Associação Nacional de
Corretores de Valores Mobiliários (NASDAQ).

• DataVendor - Esta tabela lista informações sobre fornecedores de dados históricos de preços. Usaremos o Yahoo Finance
para obter nossos dados de fim de dia (EOD). Com a introdução desta tabela, facilitamos a adição de mais fornecedores,
se necessário, como o Google Finance.

• Símbolo - A tabela de símbolos armazena a lista de símbolos de ação e informações da empresa.


Neste momento, evitaremos problemas como diferentes classes de ações e múltiplos símbolos
nomes.

• DailyPrice - Esta tabela armazena as informações diárias de preços de cada título. Ela pode se tornar muito grande se
muitos títulos forem adicionados. Portanto, é necessário otimizá-la para melhor desempenho.

O MySQL é um banco de dados extremamente flexível, pois permite personalizar a forma como os dados são armazenados
em um mecanismo de armazenamento subjacente. Os dois principais concorrentes do MySQL são o MyISAM e o InnoDB.
Embora eu não entre em detalhes sobre mecanismos de armazenamento (que são muitos!), direi que o MyISAM é mais útil
para leituras rápidas (como consultas em grandes quantidades de informações de preços), mas não suporta transações
(necessárias para reverter completamente uma operação de várias etapas que falha no meio do caminho). O InnoDB, embora
seguro para transações, é mais lento para leituras.
O InnoDB também permite bloqueio em nível de linha ao realizar gravações, enquanto o MyISAM bloqueia a tabela inteira
ao gravar nela. Isso pode causar problemas de desempenho ao gravar muitas informações em pontos arbitrários da tabela
(como com instruções UPDATE). Este é um tópico profundo, então deixarei a discussão para outro dia!

Usaremos o InnoDB, pois ele é nativamente seguro em termos de transações e fornece bloqueio em nível de linha.
Se percebermos que uma tabela está lenta para ser lida, podemos criar índices como primeiro passo e, em seguida, alterar o
mecanismo de armazenamento subjacente se o desempenho ainda for um problema. Todas as nossas tabelas usarão o
conjunto de caracteres UTF-8, pois queremos oferecer suporte a trocas internacionais.
Vamos começar com o esquema e o código SQL CREATE TABLE para a tabela de câmbio . Ele armazena a abreviatura
e o nome da bolsa (ou seja, NYSE - Bolsa de Valores de Nova York), bem como
Machine Translated by Google

53

a localização geográfica. Ele também suporta uma moeda e um deslocamento de fuso horário do UTC. Também
armazenamos uma data de criação e última atualização para nossos próprios propósitos internos. Finalmente, definimos
32
a chave de índice primária para ser um ID inteiro de incremento automático (o que é suficiente para registros):

lidar com 2 CREATE TABLE


'exchange' ( 'id' int NOT NULL
AUTO_INCREMENT, 'abrev' varchar(32)
NOT NULL, 'name' varchar(255) NOT
NULL, 'city' varchar(255) NULL,
'country' varchar(255) NULL, 'currency'
varchar(64) NULL, 'timezone_offset'
time NULL, 'created_date' datetime NOT
NULL, 'last_updated_date' datetime NOT NULL,
PRIMARY KEY ('id')

) MOTOR=InnoDB INCREMENTO AUTOMÁTICO=1 CONJUNTO DE CARACTERES PADRÃO=utf8;

Aqui está o esquema e o código SQL CREATE TABLE para a tabela data_vendor . Ela armazena o nome, o site e
o e-mail de suporte. Com o tempo, podemos adicionar mais informações úteis para o fornecedor, como a URL do ponto
de extremidade da API:

CRIAR TABELA 'data_vendor' (


'id' int NÃO NULO INCREMENTO_AUTO, 'nome'
varchar(64) NÃO NULO, 'url_do_site'
varchar(255) NULO, 'email_de_suporte' varchar(255)
NULO, 'data_de_criação' data_hora NÃO NULO,
'data_da_última_atualização' data_hora NÃO NULO,

CHAVE PRIMÁRIA ('id')


) MOTOR=InnoDB INCREMENTO AUTOMÁTICO=1 CONJUNTO DE CARACTERES PADRÃO=utf8;

Aqui está o esquema e o código SQL CREATE TABLE para a tabela de símbolos . Ele contém um link de chave
estrangeira para uma bolsa (por enquanto, ofereceremos suporte apenas a instrumentos negociados em bolsa), um
símbolo de ação (por exemplo, GOOG), um tipo de instrumento ('ação' ou 'índice'), o nome da ação ou índice do
mercado de ações, um setor de ações e uma moeda.

CRIAR TABELA 'símbolo' ( 'id' int


NÃO NULO INCREMENTO_AUTO, 'exchange_id' int
NULO, 'ticker' varchar(32) NÃO
NULO, 'instrumento' varchar(64) NÃO NULO,
'nome' varchar(255) NULO, 'setor' varchar(255) NULO,
'moeda' varchar(32) NULO,
'data_de_criação' data_hora NÃO NULO,
'data_da_última_atualização' data_hora
NÃO NULO,

CHAVE PRIMÁRIA ('id'),


CHAVE 'index_exchange_id' ('exchange_id')
) MOTOR=InnoDB INCREMENTO AUTOMÁTICO=1 CONJUNTO DE CARACTERES PADRÃO=utf8;

Aqui está o esquema e o código SQL CREATE TABLE para a tabela daily_price . Esta tabela é onde os dados
históricos de preços são armazenados. Prefixamos o nome da tabela com daily_ , pois podemos desejar criar dados
com resolução de minutos ou segundos em tabelas separadas posteriormente para estratégias de frequência mais alta.
A tabela contém duas chaves estrangeiras — uma para o fornecedor de dados e outra para um símbolo. Isso identifica
exclusivamente o ponto de dados e nos permite armazenar os mesmos dados de preço para vários fornecedores na
mesma tabela. Também armazenamos uma data de preço (ou seja, o período diário durante o qual os dados OHLC são
válidos) e as datas de criação e última atualização para nossos próprios dados.
propósitos.
Os campos restantes armazenam os preços de abertura-máxima-mínima-fechamento e os preços de fechamento
ajustados. O Yahoo Finanças nos fornece dividendos e desdobramentos de ações, cujo preço termina no adj_close_price.
Machine Translated by Google

54

coluna. Observe que o tipo de dado é decimal(19,4). Ao lidar com dados financeiros, é absolutamente necessário ser preciso.
Se tivéssemos usado o tipo de dado float , acabaríamos com erros de arredondamento devido à natureza de como os dados
float são armazenados internamente. O campo final armazena o volume de negociação do dia. Ele usa o tipo de dado bigint
para que não trunquemos acidentalmente dias com volumes extremamente altos.

CRIAR TABELA 'preço_diário' (


'id' int NÃO NULO INCREMENTO_AUTO,
'id_fornecedor_de_dados' int NÃO NULO,
'id_símbolo' int NÃO NULO, 'data_preço'
data/hora NÃO NULO, 'data_criação' data/hora
NÃO NULO, 'data_última_atualização' data/hora
NÃO NULO, 'preço_de_abertura' decimal(19,4) NULO,
'preço_alto' decimal(19,4) NULO, 'preço_baixo'
decimal(19,4) NULO, 'preço_de_fechamento'
decimal(19,4) NULO, 'preço_de_ajuste_fechamento'
decimal(19,4) NULO, 'preço_de_fechamento_ajustado'
decimal(19,4) NULO, 'volume' bigint NULO,

CHAVE PRIMÁRIA ('id'),


CHAVE 'index_data_vendor_id' ('data_vendor_id'),
CHAVE 'index_symbol_id' ('symbol_id')
) MOTOR=InnoDB INCREMENTO AUTOMÁTICO=1 CONJUNTO DE CARACTERES PADRÃO=utf8;

Ao inserir todos os comandos SQL acima na linha de comando do MySQL, os quatro comandos necessários
tabelas serão criadas.

7.8.4 Conectando ao Banco de Dados


Antes de usar o MySQL com Python, precisamos instalar a biblioteca mysqlclient . mysqlclient é, na verdade, uma bifurcação
de outra biblioteca, conhecida como Python-MySQL. Infelizmente, esta última biblioteca não é suportada em Python 3, portanto,
precisamos usar mysqlclient. Em máquinas com versões Mac OSX/UNIX, precisamos executar os seguintes comandos:

sudo apt-get install libmysqlclient-dev pip install mysqlclient

Agora estamos prontos para começar a interagir com nosso banco de dados MySQL via Python e pandas.

7.8.5 Usando um Mapeador Objeto-Relacional


Aqueles com experiência em administração e desenvolvimento de banco de dados podem estar se perguntando se é mais
sensato usar um Mapeador Objeto-Relacional (ORM).
Um ORM permite que objetos dentro de uma linguagem de programação sejam mapeados diretamente para tabelas em bancos
de dados, de forma que o código do programa não tenha conhecimento algum do mecanismo de armazenamento subjacente.
Isso não é isento de problemas, mas pode economizar bastante tempo. No entanto, essa economia de tempo geralmente
ocorre em detrimento do desempenho.
Um ORM popular para Python é o SQLAlchemy. Ele permite especificar o esquema do banco de dados dentro do próprio
Python e, assim, gerar automaticamente o código CREATE TABLE . Como escolhemos especificamente o MySQL e estamos
preocupados com o desempenho, optei por não usar um ORM neste capítulo.

Recuperação de Símbolos

Vamos começar obtendo todos os símbolos de ação associados à lista da Standard & Poor's com as 500 ações de grande
capitalização, ou seja, o S&P 500. Claro, este é apenas um exemplo. Se você estiver negociando no Reino Unido e quiser usar
índices nacionais do Reino Unido, também poderá obter a lista de empresas do FTSE100 negociadas na Bolsa de Valores de
Londres (LSE).
Machine Translated by Google

55

A Wikipédia lista convenientemente os componentes do S&P500. Observe que, na verdade, existem 502 componentes no
S&P500! Vamos analisar o site usando as solicitações Python e as bibliotecas BeautifulSoup e, em seguida, adicionar o conteúdo
diretamente ao MySQL. Primeiro, certifique-se de que as bibliotecas estejam instaladas:

solicitações de instalação do
pip pip install beautifulsoup4

O código a seguir usará as requisições e as bibliotecas BeautifulSoup para adicionar os símbolos diretamente ao banco de
dados MySQL que criamos anteriormente. Lembre-se de substituir "password" pela senha escolhida, conforme criado acima:

#!/usr/bin/python # -*-
codificação: utf-8 -*-

# insert_symbols.py

de __future__ importar função_de_impressão

importar data e
hora de matemática importar teto

importar bs4
importar MySQLdb como mdb
solicitações de importação

def obter_parse_wiki_snp500():
"""

Baixe e analise a lista da Wikipédia de constituintes do S&P500 usando


solicitações e BeautifulSoup.

Retorna uma lista de tuplas para adicionar ao MySQL.


"""

# Armazena a hora atual para o registro created_at now = [Link]()

# Use requests e BeautifulSoup para baixar a # lista de empresas do


S&P500 e obter a tabela de símbolos response = [Link]( "[Link]
wiki/List_of_S%26P_500_companies"

) sopa = [Link]([Link])

# Isso seleciona a primeira tabela, usando a sintaxe do seletor CSS # e então ignora a
linha de cabeçalho ([1:]) symbolslist = [Link]('table')
[0].select('tr')[1:]

# Obtenha as informações do símbolo para cada # linha na


tabela constituinte do S&P500 symbols = [] para i, símbolo
em
enumerate(symbolslist): tds = [Link]('td')
[Link]( (

tds[0].select('a')[0].text, # Ticker 'ação', tds[1].select('a')


[0].text, #
Nome
Machine Translated by Google

56

tds[3].text, # Setor 'USD', agora,


agora
)

) símbolos de retorno

def insert_snp500_symbols(símbolos):
"""

Insira os símbolos do S&P500 no banco de dados MySQL.


"""

# Conecte-se à instância do MySQL db_host =


'localhost' db_user = 'sec_user'
db_pass = 'password' db_name
= 'securities_master' con =
[Link]( host=db_host, user=db_user,
passwd=db_pass,
db=db_name
)

# Crie as strings de inserção column_str


= """ticker, instrumento, nome, setor,
moeda, data_de_criação, data_da_última_atualização
"""

insert_str = ("%s, " final_str = * 7)[:-2]


"INSERIR NO símbolo (%s) VALORES (%s)" % \
(str_coluna, str_inserção)

# Usando a conexão MySQL, execute # um INSERT INTO


para cada símbolo com con: cur = [Link]()

[Link](final_str,
symbols)

se __nome__ == "__principal__":
símbolos = obter_parse_wiki_snp500()
inserir_snp500_symbols(símbolos)
imprimir("%s símbolos foram adicionados com sucesso." % len(símbolos))

Nesta fase, teremos todos os 502 símbolos atuais constituintes do índice S&P500 no banco de dados. Nossa próxima tarefa é
obter os dados históricos de preços de fontes distintas e compará-los com os símbolos.

Recuperação de preços

Para obter os dados históricos dos atuais constituintes do S&P500, primeiro precisamos consultar o banco de dados para obter a
lista de todos os símbolos.
Depois que a lista de símbolos, juntamente com os IDs dos símbolos, forem retornados, é possível chamar
a API do Yahoo Finance e baixe os dados históricos de preços para cada símbolo.
Assim que tivermos cada símbolo, podemos inserir os dados no banco de dados um por um. Aqui está o
Código Python para executar isso:

#!/usr/bin/python # -*-
codificação: utf-8 -*-

# price_retrieval.py
Machine Translated by Google

57

de __future__ importar função_de_impressão

importar avisos de
importação de data e hora

importar MySQLdb como


solicitações de importação mdb

# Obter uma conexão de banco de dados com a instância MySQL db_host =


'localhost' db_user = 'sec_user'
db_pass = 'password' db_name
= 'securities_master' con =
[Link](db_host, db_user, db_pass,
db_name)

def obter_lista_de_tiquetes_do_banco_de_dados():
"""

Obtém uma lista dos símbolos de cotação no banco de dados.


"""

com con: cur


= [Link]()
[Link]("SELECIONE id, ticker DO símbolo") dados =
[Link]() retorne [(d[0], d[1])
para d em dados]

def obter_dados_históricos_diários_yahoo( ticker,


data_de_início=(2000,1,1),
data_de_término=data_hora.[Link]().timetuple()[0:3]
):
"""

Obtém dados dos retornos do Yahoo Finance e uma lista de tuplas.

ticker: símbolo do ticker do Yahoo Finance, por exemplo, "GOOG" para Google, Inc. start_date: data
de início no formato (AAAA, M, D) end_date: data de término no
formato (AAAA, M, D)
"""

# Construa a URL do Yahoo com os parâmetros de consulta inteiros corretos # para as datas de início
e término. Observe que alguns parâmetros são baseados em zero! ticker_tup = ( ticker, start_date[1]-1,
start_date[2],
start_date[0], end_date[1]-1, end_date[2], end_date[0]

) yahoo_url = "[Link] yahoo_url += "?


s=%s&a=%s&b=%s&c=%s&d=%s&e=%s&f=%s" yahoo_url = yahoo_url %
ticker_tup

# Tente conectar-se ao Yahoo Finance e obter os dados. # Em caso de falha, imprima


uma mensagem de erro. tente: yf_data =

[Link](yahoo_url).[Link]("\n")[1:-1] prices = []
Machine Translated by Google

58

para y em yf_data: p =
[Link]().split(',') preç[Link](

([Link](p[0], '%Y-%m-%d'), p[1], p[2], p[3], p[4],


p[5], p[6])
)
exceto Exceção como e: print("Não
foi possível baixar os dados do Yahoo: %s" % e) retornar preços

def insert_daily_data_into_db(id_fornecedor_de_dados,
id_símbolo, dados_diários
):
"""

Pega uma lista de tuplas de dados diários e a adiciona ao banco de dados MySQL.
Adiciona o ID do fornecedor e o ID do símbolo aos dados.

daily_data: Lista de tuplas dos dados OHLC (com adj_close e volume)

"""

# Crie o horário now now =


[Link]()

# Altere os dados para incluir o ID do fornecedor e o ID do símbolo daily_data = [

(data_vendor_id, symbol_id, d[0], agora, agora, d[1], d[2], d[3],


d[4], d[5], d[6]) para d em daily_data

# Crie as strings de inserção column_str


= """data_vendor_id, symbol_id, price_date, created_date,
última_data_de_atualização, preço_de_abertura, preço_alto, preço_baixo,
preço_de_fechamento, volume,
preço_de_fechamento_ajustável"""
insert_str =
("%s, " * 11)[:-2] final_str = "INSERIR EM preço_diário (%s) VALORES (%s)" % \
(str_coluna, str_inserção)

# Usando a conexão MySQL, execute um INSERT INTO para cada símbolo com con: cur = [Link]()

[Link](final_str,
daily_data)

se __nome__ == "__principal__":
# Isso ignora os avisos sobre truncamento de dados # dos avisos de precisão do
Yahoo para tipos de dados Decimal (19,4).filterwarnings('ignore')

# Faça um loop nos tickers e insira os dados históricos diários no banco de dados

tickers = obter_lista_de_tickers_do_db() lentickers =


len(tickers) para i, t em
enumerate(tickers): print(
Machine Translated by Google

59

"Adicionando dados para %s: %s de %s" % (t[1], i+1,


lentickers)

) yf_data = get_daily_historic_data_yahoo(t[1])
insert_daily_data_into_db('1', t[0], yf_data) print("Dados de preços do
Yahoo Finance adicionados com sucesso ao BD.")

Observe que certamente existem maneiras de otimizar esse procedimento. Se utilizarmos a biblioteca Python ScraPy, por
exemplo, obteremos alta simultaneidade nos downloads, já que o ScraPy é construído no framework Twisted, orientado a
eventos. No momento, cada download será realizado sequencialmente.

7.9 Recuperando Dados do Mestre de Valores Mobiliários


Agora que baixamos o histórico de preços de todos os componentes atuais do S&P500, queremos acessá-lo em Python. A
biblioteca pandas torna isso extremamente simples. Aqui está um script que obtém os dados de Abertura-Máxima-Mínima-
Fechamento (OHLC) para as ações do Google durante um determinado período de tempo do nosso banco de dados mestre
de títulos e gera a cauda do conjunto de dados:

#!/usr/bin/python # -*-
codificação: utf-8 -*-

# recuperando_dados.py

de __future__ importar função_de_impressão

importar pandas como pd


importar MySQLdb como mdb

se __name__ == "__main__": # Conecte-


se à instância do MySQL db_host = 'localhost'
db_user = 'sec_user' db_pass =
'password' db_name =
'securities_master' con =
[Link](db_host, db_user, db_pass,
db_name)

# Selecione todos os dados históricos de fechamento ajustados do Google sql =


"""SELECT dp.price_date, dp.adj_close_price
DE símbolo COMO sym
INNER JOIN preço_diário COMO dp EM
dp.symbol_id = [Link] ONDE
[Link] = 'GOOG' ORDEM POR
dp.price_date ASC;"""

# Crie um dataframe pandas a partir da consulta SQL goog =


pd.read_sql_query(sql, con=con, index_col='price_date')

# Saída do dataframe tail print([Link]())

A saída do script é a seguinte:

adj_close_price
preço_data
2015-06-09 526,69
Machine Translated by Google

60

2015-06-10 536,69
2015-06-11 534,61
2015-06-12 532,33
2015-06-15 527,20

Este é obviamente apenas um script simples, mas mostra o quão poderoso é ter um arquivo armazenado localmente
O mestre em títulos pode ser. É possível fazer backtest de certas estratégias extremamente rápido com este
abordagem, pois a velocidade de entrada/saída (E/S) do banco de dados será significativamente mais rápida do que
a de uma conexão de internet.
Machine Translated by Google

Capítulo 8

Processamento de dados financeiros

No capítulo anterior, descrevemos como construir um banco de dados mestre de títulos baseado em ações.
Este capítulo discutirá um tópico que não é frequentemente considerado em grande medida na maioria dos livros
de negociação: o processamento de dados do mercado financeiro antes do uso em um teste de estratégia.
A discussão começará com uma visão geral dos diferentes tipos de dados que serão de interesse para traders
algorítmicos. Em seguida, será considerada a frequência dos dados, desde dados trimestrais (como relatórios da
SEC) até dados de ticks e ordens na escala de milissegundos. As fontes desses dados (gratuitas e comerciais)
serão então descritas, juntamente com o código para obtenção dos dados. Por fim, serão discutidas a limpeza e
a preparação dos dados para uso em estratégias.

8.1 Classificação de Mercado e Instrumentos

Como traders algorítmicos, frequentemente nos interessamos por uma ampla gama de dados do mercado financeiro. Isso
pode variar desde preços de séries temporais de instrumentos subjacentes e derivativos, dados não estruturados baseados
em texto (como artigos de notícias) até informações sobre lucros corporativos. Este livro abordará predominantemente dados
de séries temporais financeiras.

8.1.1 Mercados
Ações americanas e internacionais, câmbio, commodities e renda fixa são as principais fontes de dados de
mercado que interessam a um trader algorítmico. No mercado de ações, ainda é extremamente comum comprar
o ativo subjacente diretamente, enquanto nos três últimos mercados, instrumentos derivativos de alta liquidez
(futuros, opções ou instrumentos mais exóticos) são mais comumente utilizados para fins de negociação.

Essa ampla categorização torna, essencialmente, relativamente simples lidar com os mercados de ações,
embora com problemas relacionados ao tratamento de dados de eventos corporativos (veja abaixo). Assim,
grande parte do cenário de negociação algorítmica no varejo será baseado em ações, como ações corporativas
diretas ou Fundos Negociados em Bolsa (ETFs). Os mercados de câmbio ("forex") também são muito populares,
já que as corretoras permitem a negociação com margem em movimentos de porcentagem em ponto (PIP).
Um pip é uma unidade da quarta casa decimal na taxa de câmbio. Para moedas denominadas em dólares
americanos, isso equivale a 1/100 de um centavo.
Os mercados de commodities e renda fixa são mais difíceis de negociar diretamente com o ativo subjacente.
Um trader algorítmico de varejo geralmente não está interessado em entregar barris de petróleo a um depósito!
Em vez disso, contratos futuros sobre o ativo subjacente são usados para fins especulativos. Mais uma vez, a
negociação de margem é utilizada, permitindo ampla alavancagem nesses contratos.

8.1.2 Instrumentos
Uma ampla gama de instrumentos subjacentes e derivativos estão disponíveis para o trader algorítmico.
A tabela a seguir descreve os casos de uso comuns de interesse.

61
Machine Translated by Google

62

Mercado Instrumentos

Ações/Índices Ações, ETFs, Futuros, Opções


Margem de câmbio/à vista, ETFs, futuros, opções
Commodities Futuros, Opções
Renda Fixa Futuros, Opções

Para os fins deste livro, vamos nos concentrar quase exclusivamente em ações e ETFs
para simplificar a implementação.

8.1.3 Dados Fundamentais


Embora os traders algorítmicos realizem principalmente análises de séries temporais de preços financeiros, dados
fundamentais (de frequências variadas) também são frequentemente adicionados à análise. As chamadas estratégias de Valor
Quantitativo (VQ) dependem fortemente da acumulação e análise de dados fundamentais, como informações macroeconômicas,
históricos de lucros corporativos, índices de inflação, relatórios de folha de pagamento, taxas de juros e registros da SEC.
Esses dados também costumam estar em formato temporal, embora em escalas de tempo muito maiores, como meses,
trimestres ou anos. As estratégias de VQ também operam nesses períodos.
Este livro não discutirá estratégias de QV ou estratégias fundamentais de grande escala em grande extensão, mas se
concentrará nas estratégias diárias ou mais frequentes derivadas principalmente da ação do preço.

8.1.4 Dados não estruturados


Dados não estruturados consistem em documentos como artigos de notícias, postagens de blog, artigos ou relatórios.
A análise desses dados pode ser complexa, pois depende de técnicas de Processamento de Linguagem Natural (PLN). Um
exemplo de uso da análise de dados não estruturados é a tentativa de determinar o contexto do sentimento. Isso pode ser útil
para conduzir uma estratégia de negociação. Por exemplo, ao classificar textos como "otimistas", "pessimistas" ou "neutros",
um conjunto de sinais de negociação pode ser gerado. O termo para esse processo é análise de sentimento.

Python fornece uma biblioteca extremamente abrangente para a análise de dados de texto, conhecida como Natural
Language Toolkit (NLTK). De fato, um livro da O'Reilly sobre NLTK pode ser baixado gratuitamente no site dos autores -
Natural Language Processing with Python [3].

Dados de texto completo

Existem inúmeras fontes de dados em texto completo que podem ser úteis para a geração de uma estratégia de negociação.
Fontes financeiras populares, como a Bloomberg e o Financial Times, bem como blogs de comentários financeiros, como o
Seeking Alpha e o ZeroHedge, fornecem fontes significativas de texto para análise. Além disso, feeds de notícias proprietários,
fornecidos por fornecedores de dados, também são boas fontes desses dados.

Para obter dados em maior escala, é necessário utilizar ferramentas de "web scraping",
projetadas para automatizar o download de sites em massa. Tenha cuidado, pois ferramentas
automatizadas de web scraping às vezes violam os Termos de Serviço desses sites. Certifique-se
de verificar antes de iniciar o download desse tipo de dados. Uma ferramenta particularmente útil
para web scraping, que torna o processo eficiente e estruturado, é a biblioteca ScraPy.

Dados de mídia social

Nos últimos anos, tem havido um interesse significativo na obtenção de informações sobre sentimentos a partir de dados de
mídias sociais, particularmente por meio do serviço de microblog Twitter. Em 2011, foi lançado um fundo de hedge em torno
do sentimento do Twitter, conhecido como Derwent Capital. De fato, estudos acadêmicos[4] demonstraram evidências de que
é possível gerar um grau de capacidade preditiva com base nessa análise de sentimentos.

Embora a análise de sentimentos esteja fora do escopo deste livro, caso você queira realizar pesquisas sobre sentimentos,
há dois livros[15, 14] de Matt Russell sobre como obter dados de mídia social por meio de APIs públicas fornecidas por esses
serviços da web.
Machine Translated by Google

63

8.2 Frequência de Dados


A frequência dos dados é uma das considerações mais importantes ao projetar um sistema de negociação algorítmica. Ela
impactará todas as decisões de projeto relacionadas ao armazenamento de dados, ao backtest de uma estratégia e à execução
de um algoritmo.
Estratégias de frequência mais alta tendem a levar a análises estatisticamente mais robustas, simplesmente devido ao maior
número de pontos de dados (e, portanto, de negociações) que serão utilizados. As estratégias de HFT geralmente exigem um
investimento significativo de tempo e capital para o desenvolvimento do software necessário para executá-las.

Estratégias de frequência mais baixa são mais fáceis de desenvolver e implantar, pois exigem menos automação.
No entanto, elas geralmente geram muito menos negociações do que uma estratégia de frequência mais alta, o que leva a uma
análise estatisticamente menos robusta.

8.2.1 Dados semanais e mensais


Dados fundamentais são frequentemente reportados semanalmente, mensalmente, trimestralmente ou até mesmo anualmente.
Esses dados incluem dados de folha de pagamento, relatórios de desempenho de fundos de hedge, registros da SEC, índices
baseados na inflação (como o Índice de Preços ao Consumidor, IPC), crescimento econômico e contas corporativas.
O armazenamento desses dados geralmente é adequado para bancos de dados não estruturados, como o MongoDB, que
pode manipular dados aninhados hierarquicamente, permitindo assim um grau razoável de capacidade de consulta.
A alternativa é armazenar texto de arquivo simples em um RDBMS, o que é menos apropriado, já que a consulta de texto
completo é mais complicada.

8.2.2 Dados Diários A maioria

dos traders algorítmicos de varejo utiliza dados diários de séries temporais financeiras ("fim do dia"/EOD), particularmente em
ações e câmbio. Esses dados estão disponíveis gratuitamente (veja abaixo), mas frequentemente são de qualidade questionável
e sujeitos a certos vieses. Os dados de fim do dia são frequentemente armazenados em RDBMS, uma vez que a natureza do
mapeamento de ticker/símbolo se aplica naturalmente ao modelo relacional.
Dados EOD não exigem grandes requisitos de armazenamento. Há 252 dias de negociação por ano nas bolsas americanas
e, portanto, durante uma década, haverá 2.520 barras por título. Mesmo com um universo de 10.000 símbolos, isso representa
25.200.000 barras, que podem ser facilmente gerenciadas em um ambiente de banco de dados relacional.

8.2.3 Barras Intradiárias


As estratégias intradiárias costumam utilizar barras OHLCV horárias, quinzenais, quinquenais, um minuto ou secundárias.
Provedores de feeds intradiários, como QuantQuote e DTN IQFeed, costumam fornecer barras minuciosas ou secundárias com
base em seus dados de ticks.
Dados nessas frequências apresentarão muitas barras "ausentes" simplesmente porque nenhuma negociação foi realizada
naquele período. Pandas podem ser usados para aumentar esses valores, embora com uma redução na precisão dos dados.
Além disso, Pandas também podem ser usados para criar dados em escalas de tempo menos granulares, se necessário.

Durante um período de dez anos, dados minuciosos gerarão quase um milhão de barras por título.
Da mesma forma, para os dados secundários, o número de pontos de dados no mesmo período totalizará quase sessenta
milhões por título. Portanto, armazenar mil desses títulos resultará em sessenta bilhões de barras de dados. Essa é uma grande
quantidade de dados a ser mantida em um RDBMS e, consequentemente, abordagens mais sofisticadas são necessárias.

O armazenamento e a recuperação de dados secundários desta magnitude estão um pouco fora do escopo de
este livro, então não vou discuti-lo mais.

8.2.4 Dados de ticks e de livros de ordens


Quando uma negociação é concluída em uma bolsa ou outro local, um tick é gerado. Os feeds de ticks consistem em todas
essas transações por bolsa. Os feeds de ticks de varejo são armazenados com cada dado contendo um registro de data e hora
com precisão de milissegundos. Os dados de ticks geralmente também incluem o melhor preço de compra/venda atualizado. O
armazenamento de dados de ticks está muito além do escopo deste livro, mas é desnecessário dizer...
Machine Translated by Google

64

Os volumes desses dados são substanciais. Os mecanismos de armazenamento comuns incluem HDF5, kdb e simplesmente
arquivos simples/CSV.
Múltiplas ordens limitadas em uma bolsa levam ao conceito de livro de ordens. Este é essencialmente a lista de todas as
ordens limitadas de compra e venda em determinados volumes para cada participante do mercado. Isso leva à definição do
spread de compra e venda (ou simplesmente "spread"), que é a menor diferença entre os preços de compra e venda para as
ordens "top of book". A criação de uma representação histórica, ou um simulador de mercado, de um livro de ordens limitadas
geralmente é necessária para a execução de estratégias de negociação de ultra-alta frequência (UHFT). O armazenamento
desses dados é complexo e, portanto, estará fora do escopo deste livro.

8.3 Fontes de Dados


Existem inúmeras fontes e fornecedores de dados financeiros. Eles variam substancialmente em abrangência, pontualidade,
qualidade e preço.
Em termos gerais, os dados do mercado financeiro fornecidos com uma frequência diária defasada ou mais longa estão
disponíveis gratuitamente, embora com qualidade geral duvidosa e potencial viés de sobrevivência. Para obter dados
intradiários, geralmente é necessário adquirir um feed de dados comercial. Os fornecedores desses feeds variam enormemente
em sua capacidade de atendimento ao cliente, qualidade geral do feed e abrangência de instrumentos.

8.3.1 Fontes gratuitas

Dados de barras gratuitos de fim de dia, que consistem em preços de abertura-máxima-mínima-fechamento-volume (OHLCV)
para instrumentos, estão disponíveis para uma ampla gama de ações e futuros dos EUA e internacionais no Yahoo Finance,
Google Finance e Quandl.

Yahoo Finanças

O Yahoo Finanças é o recurso ideal para a formação de um banco de dados de ações dos EUA para o final do dia. A
abrangência dos dados é extremamente abrangente, listando milhares de ações negociadas. Além disso, desdobramentos
de ações e dividendos são processados usando um método de ajuste retroativo, que surge como a coluna "Fechamento
Ajustado" na saída CSV da API (que discutiremos abaixo). Assim, os dados permitem que traders algorítmicos comecem
rapidamente e sem custo algum.
Tenho muita experiência pessoal na limpeza de dados do Yahoo. Devo observar que os dados podem estar bastante
incorretos. Em primeiro lugar, eles estão sujeitos a um problema conhecido como preenchimento retroativo. Esse problema
ocorre quando dados históricos passados são corrigidos em uma data futura, resultando em testes de baixa qualidade que
mudam conforme seu próprio banco de dados é atualizado. Para lidar com esse problema, um registro de log geralmente é
adicionado ao mestre de títulos (em uma tabela de log apropriada) sempre que um ponto de dados históricos é modificado.

Em segundo lugar, o feed do Yahoo agrega apenas preços de algumas fontes para formar os pontos OHLCV. Isso
significa que os valores em torno da abertura, máxima, mínima e fechamento podem ser enganosos, pois outras bolsas/fontes
de liquidez podem ter executado preços diferentes, acima dos valores.
Em terceiro lugar, notei que, ao obter dados financeiros em massa do Yahoo, erros podem ocorrer na API. Por exemplo,
várias chamadas à API com parâmetros de data/ticker idênticos ocasionalmente levam a conjuntos de resultados diferentes.
Este é claramente um problema substancial e deve ser verificado cuidadosamente.

Em resumo, esteja preparado para realizar uma limpeza de dados abrangente no Yahoo Finance,
se você optar por usá-lo para preencher um grande mestre de títulos e precisar de dados altamente precisos.

Quandl

Quandl é um serviço relativamente novo que se propõe a ser "A maneira mais fácil de encontrar e usar dados numéricos na
web". Acredito que eles estão a caminho de atingir esse objetivo! O serviço fornece um conjunto substancial de dados diários
sobre ações americanas e internacionais, taxas de juros, commodities/futuros, câmbio e outros dados econômicos. Além
disso, o banco de dados é continuamente expandido e o projeto é mantido de forma bastante ativa.
Machine Translated by Google

65

Todos os dados podem ser acessados por uma API HTTP bastante moderna (CSV, JSON, XML ou HTML), com plugins para
uma ampla variedade de linguagens de programação, incluindo R, Python, Matlab, Excel, Stata, Maple, C#, EViews, Java, C/C+
+, .NET, Clojure e Julia. Sem uma conta, são permitidas 50 chamadas à API por dia, mas esse número pode ser aumentado para
500 com o registro de uma conta.
De fato, as chamadas podem ser atualizadas para 5.000 por hora, se desejar, entrando em contato com a equipe.
Não tenho muita experiência com o Quandl "em escala" e, portanto, não posso comentar sobre o nível de erros no conjunto
de dados, mas acredito que quaisquer erros provavelmente serão constantemente relatados e corrigidos. Portanto, vale a pena
considerá-los como fonte primária de dados para um mestre de títulos no final do dia.

Mais adiante no capítulo, discutiremos como obter dados de futuros de commodities dos EUA do Quandl com Python e
pandas.

8.3.2 Fontes Comerciais


Para realizar negociações algorítmicas intradiárias, geralmente é necessário adquirir um feed comercial. Os preços podem variar de
US$ 30 por mês a cerca de US$ 500 por mês para feeds de "nível de varejo". Feeds de qualidade institucional costumam custar
entre quatro dígitos por mês e, portanto, não os discutirei aqui.

Dados EOD

Utilizei o EODData no contexto de fundos, embora apenas com dados diários e predominantemente para câmbio. Apesar do
nome, eles oferecem um certo grau de fontes intradiárias. O custo é de US$ 25 por mês para o pacote "platina".

O recurso é muito útil para encontrar uma lista completa de símbolos negociados em bolsas globais, mas lembre-se de que
isso estará sujeito a viés de sobrevivência, pois acredito que a lista representa entidades listadas atualmente.

Infelizmente (pelo menos em 2010), descobri que o feed de desdobramento de ações era um tanto impreciso (pelo menos
quando comparado às informações da Morningstar). Isso levou a alguns problemas de picos substanciais (veja abaixo) nos
dados, o que aumentou o atrito no processo de limpeza de dados.

DTN IQFeed

O DTN IQFeed é um dos feeds de dados mais populares para o trader algorítmico de varejo de alto nível.
Eles afirmam ter mais de 80.000 clientes e fornecem dados tick-by-tick em tempo real, sem filtros da corretora, além de uma
grande quantidade de dados históricos.
O preço começa em US$ 50 por mês, mas, na prática, ficará na faixa de US$ 150 a US$ 200 por mês, após a seleção de
serviços específicos e o cálculo das taxas de câmbio. Utilizo o DTN IQFeed para todas as minhas estratégias intradiárias de
ações e futuros. Em termos de dados históricos, o IQFeed fornece para ações, futuros e opções:

• 180 dias corridos de tick (cada negociação)

• Mais de 7 anos de bares históricos de 1 minuto

• Mais de 15 anos de bares históricos diários

A principal desvantagem é que o software DTN IQFeed (o minisservidor, não as ferramentas de gráficos) só funciona no
Windows. Isso pode não ser um problema se todas as suas negociações algorítmicas forem realizadas neste sistema operacional,
mas eu, pessoalmente, desenvolvo todas as minhas estratégias no Ubuntu Linux.
Entretanto, embora eu não tenha testado ativamente, ouvi dizer que é possível executar o DTN IQFeed no emulador WINE.

Abaixo discutiremos como obter dados do IQFeed usando Python no Windows.


Machine Translated by Google

66

QuantQuote

A QuantQuote fornece dados históricos de minutos, segundos e ticks a preços acessíveis para ações dos
EUA, desde 1998. Além disso, fornece feeds de ticks em tempo real em nível institucional, embora isso seja
menos interessante para traders algorítmicos de varejo. Um dos principais benefícios da QuantQuote é que
seus dados são fornecidos sem viés de sobrevivência, graças ao seu software de correspondência de
símbolos TickMap e à inclusão de todas as ações de um determinado índice ao longo do tempo.
Por exemplo, para comprar todo o histórico do S&P500 desde 1998 em barras minúsculas, incluindo
ações deslistadas, o custo no momento da redação deste texto era de US$ 895. O preço varia com o aumento
da frequência dos dados.
A QuantQuote é atualmente a principal fornecedora de dados de mercado para o serviço de backtesting online
QuantConnect. A QuantQuote se esforça ao máximo para garantir a minimização de erros, portanto, se você procura um feed
de ações dos EUA em alta resolução, considere usar o serviço deles.

8.4 Obtenção de Dados


Nesta seção, discutiremos como usar o Quandl, o Pandas e o DTN IQFeed para baixar dados do mercado
financeiro em uma variedade de mercados e períodos de tempo.

8.4.1 Yahoo Finanças e Pandas

A biblioteca pandas torna extremamente simples baixar dados de EOD do Yahoo Finance.
O Pandas vem com um componente DataReader que se conecta ao Yahoo Finance (entre outras fontes).
Especificar um símbolo com uma data de início e término é suficiente para baixar uma série EOD em um
DataFrame do pandas, o que permite que operações vetorizadas rápidas sejam realizadas:

de __future__ importar função_de_impressão

importar data e hora


importar [Link] como web

se __nome__ == "__principal__":
espião = [Link]( "SPY",
"yahoo", data e
[Link] e hora(2007,1,1), data e
[Link] e hora(2015,6,15)

) imprimir(espiã[Link]())

A saída é fornecida abaixo:

Abrir Alto Baixo Fechar Volume \


Data
2015-06-09 208.449997 209.100006 207.690002 208.449997 98148200 2015-06-10 209.369995
211.410004 209.300003 210.960007 129936200 2015-06-11 211.479996 212.089996 211.199997
211.649994 72672100 2015-06-12 210.639999 211.479996 209.679993 209.929993 127811900 2015-06-15
208.639999 209.449997 207.789993 209.100006 121425800

Ajuste Fechar
Data
2015-06-09 208.449997
2015-06-10 210.960007
2015-06-11 211.649994
2015-06-12 209.929993
2015-06-15 209.100006
Machine Translated by Google

67

Observe que no pandas 0.17.0, [Link] será substituído por um pandas-datareader separado
pacote. No entanto, por enquanto (ou seja, versões do pandas 0.16.x), a sintaxe para importar o
leitor de dados é import [Link] as web.
Na próxima seção, usaremos o Quandl para criar uma solução de download mais abrangente e permanente.

8.4.2 Quandl e Pandas


Até recentemente, era bastante difícil e caro obter dados de futuros consistentes em todas as bolsas de valores
com atualizações frequentes. No entanto, o lançamento do serviço Quandl mudou drasticamente a situação, com
dados financeiros, em alguns casos, remontando à década de 1950. Nesta seção, usaremos o Quandl para baixar
um conjunto de contratos futuros de fim de dia em várias datas de entrega.

Inscrevendo-se no Quandl

A primeira coisa a fazer é se cadastrar no Quandl. Isso aumentará a cota diária de chamadas para a API. A
inscrição concede 500 chamadas por dia, em vez das 50 padrão. Visite o site em [Link]:

Figura 8.1: A página inicial do Quandl

Clique no botão de inscrição no canto superior direito:

Figura 8.2: A página de inscrição do Quandl

Depois de fazer login, você retornará à página inicial:


Machine Translated by Google

68

Figura 8.3: Página inicial autorizada da Quandl

Dados Futuros Quandl

Agora clique no link "Novo: página de futuros..." para acessar a página inicial de futuros:

Figura 8.4: Página inicial dos contratos futuros da Quandl

Neste tutorial, consideraremos o contrato futuro de alta liquidez E-Mini S&P500, que possui o símbolo
ES. Para baixar outros contratos, o restante deste tutorial pode ser feito com símbolos adicionais substituindo
a referência a ES.
Clique no link E-Mini S&P500 (ou no símbolo de futuros de sua escolha) e você será levado à seguinte
tela: Rolando mais
para baixo, a tela exibe a lista de contratos históricos desde 1997: Clique em um dos contratos
individuais. Como exemplo, escolhi ESZ2014, que se refere
ao contrato de entrega de dezembro de 2014. Será exibido um gráfico com os dados:
Ao clicar no botão "Download", os dados podem ser obtidos em diversos formatos: HTML, CSV, JSON
ou XML. Além disso, podemos baixar os dados diretamente para um DataFrame do Pandas usando as
ligações Python. Embora esta última seja útil para a rápida "prototipagem" e exploração dos dados, nesta
seção estamos considerando o desenvolvimento de um armazenamento de dados de longo prazo. Clique no
botão de download, selecione "CSV" e copie e cole a chamada da API:
A chamada da API terá o seguinte formato:

[Link]
&auth_token=MY_AUTH_TOKEN&trim_start=2013-09-18
&trim_end=2013-12-04&sort_order=desc

O token de autorização foi redigido e substituído por MY_AUTH_TOKEN. Será necessário copiar a string
alfanumérica entre "auth_token=" e "&trim_start" para
Machine Translated by Google

69

Figura 8.5: Página do contrato E-Mini S&P500

Figura 8.6: Contratos históricos do E-Mini S&P500

Figura 8.7: Gráfico do ESZ2014 (entrega em dezembro de 2014)

Uso posterior no script Python abaixo. Não o compartilhe com ninguém, pois é seu token de autorização exclusivo para
downloads do Quandl e é usado para determinar sua taxa de downloads do dia.

Esta chamada de API formará a base de um script automatizado que escreveremos abaixo para download
um subconjunto de todo o contrato futuro histórico.
Machine Translated by Google

70

Figura 8.8: Modal de download para arquivo CSV ESZ2014

Baixando Quandl Futures para Python

Como estamos interessados em usar os dados de futuros a longo prazo como parte de uma estratégia mais
ampla de banco de dados mestre de títulos, queremos armazená-los em disco. Portanto, precisamos criar um
diretório para armazenar nossos arquivos CSV de contratos E-Mini. No Mac/Linux (dentro do terminal/console),
isso é feito com o seguinte comando:

cd /CAMINHO/PARA/SEUS/quandl_data.py
mkdir -p quandl/futures/ES
Observação: substitua /PATH/TO/YOUR acima pelo diretório onde seu arquivo quandl_data.py está localizado.

Isso cria um subdiretório chamado quandl, que contém mais dois subdiretórios para futuros e, em particular,
para os contratos ES. Isso nos ajudará a organizar nossos downloads de forma contínua.

Para realizar o download usando Python, precisaremos importar algumas bibliotecas. Em particular,
precisaremos de requisições para o download, além de pandas e matplotlib para plotagem e manipulação de
dados:

#!/usr/bin/python # -*-
codificação: utf-8 -*-

# quandl_data.py

de __future__ importar função_de_impressão

importar [Link] como plt importar


pandas como pd importar
solicitações
A primeira função no código gerará a lista de símbolos de futuros que desejamos baixar. Adicionei
parâmetros de palavras-chave para os anos de início e fim, definindo-os com valores razoáveis de 2010 e 2014.
Você pode, é claro, optar por usar outros períodos:

def construct_futures_symbols( símbolo,


ano_de_início=2010, ano_final=2014
):
"""

Cria uma lista de códigos de contratos futuros para um símbolo e


período de tempo específicos.
"""

futuros = []
Machine Translated by Google

71

# Códigos de entrega para março, junho,


setembro e # dezembro meses =
'HMUZ' para y no
intervalo (ano_início, ano_fim+1): para m em meses:
[Link]("%s%s%s"
% (símbolo, m, y))
retornos futuros

Agora precisamos percorrer cada símbolo, obter o arquivo CSV do Quandl para aquele contrato específico e,
posteriormente, gravá-lo no disco para que possamos acessá-lo mais tarde:

def download_contract_from_quandl(contrato, dl_dir):


"""

Baixe um contrato futuro individual do Quandl e armazene-o em disco no diretório "dl_dir". É


necessário um auth_token, obtido do Quandl no momento do cadastro.

"""

# Construa a chamada de API a partir do contrato e auth_token api_call = "http://


[Link]/api/v1/datasets/" api_call += "OFDP/FUTURE_%[Link]" %
contract # Se desejar adicionar um token de autenticação para
mais downloads, basta # comentar a linha a seguir e substituir MY_AUTH_TOKEN pelo # seu
token de autenticação na linha abaixo params = "?sort_order=asc" #params = "?
auth_token=MY_AUTH_TOKEN&sort_order=asc"
full_url = "%s%s" % (api_call, params)

# Baixe os dados do Quandl data =


[Link](full_url).text

# Armazene os dados no disco fc =


open('%s/%[Link]' % (dl_dir, contract), 'w') [Link](data) [Link]()

Agora, vinculamos as duas funções acima para baixar todos os contratos desejados:

def download_historical_contracts( símbolo, dl_dir,


ano_de_início=2010, ano_final=2014
):
"""

Baixa todos os contratos futuros para um símbolo especificado entre um start_year


e um end_year.
"""

contratos = construct_futures_symbols(
símbolo, ano_de_início, ano_de_fim

) para c em contratos:
print("Baixando contrato: %s" % c)
download_contract_from_quandl(c, dl_dir)

Por fim, podemos adicionar um dos preços futuros a um dataframe do pandas usando a função principal.
Podemos então usar o matplotlib para plotar o preço de liquidação:

se __nome__ == "__principal__":
símbolo = 'ES'

# Certifique-se de ter criado este # diretório


relativo com antecedência
Machine Translated by Google

72

dl_dir = 'quandl/futuros/ES'

# Crie os anos de início e fim start_year =


2010 end_year = 2014

# Baixe os contratos para o diretório


download_historical_contracts( símbolo,
dl_dir, start_year, end_year
)

# Abra um único contrato via read_csv # e plote o


preço de liquidação es =
[Link].read_csv( "%s/
[Link]" % dl_dir, index_col="Date"

) es["Resolver"].plot()
[Link]()
A saída do gráfico é apresentada na Figura 8.4.2.

Figura 8.9: Preço de liquidação ESH2010

O código acima pode ser modificado para coletar qualquer combinação de contratos futuros da Quandl, conforme
necessário. Lembre-se de que, a menos que uma solicitação de API maior seja feita, o código será limitado a 50
solicitações de API por dia.

8.4.3 DTN IQFeed


Para aqueles que possuem uma assinatura do DTN IQFeed, o serviço oferece um mecanismo cliente-servidor para
obtenção de dados intradiários. Para que isso funcione, é necessário baixar o servidor IQLink e executá-lo no Windows.
Infelizmente, é complicado executar este servidor em Mac ou Linux, a menos que se utilize o emulador WINE. No
entanto, uma vez que o servidor esteja em execução, ele pode ser conectado por meio de um soquete, a partir do qual
é possível consultar dados.
Nesta seção, obteremos dados de barras minuciosas para um par de ETFs dos EUA a partir de 1º de janeiro de
2007, usando uma interface de soquete Python. Como há aproximadamente 252 dias de negociação dentro
Machine Translated by Google

73

a cada ano para os mercados dos EUA, e cada dia de negociação tem 6,5 horas de negociação, isso equivalerá a pelo
menos 650.000 barras de dados, cada uma com sete pontos de dados: Carimbo de data/hora, Abertura, Mínimo, Máximo,
Fechamento, Volume e Juros em aberto.
Selecionei os ETFs SPY e IWM para baixar em CSV. Faça isso para iniciar o programa IQLink no Windows antes de
executar este script:

#!/usr/bin/python # -*-
codificação: utf-8 -*-

# [Link]

importar
sistema importar soquete

def read_historical_data_socket(meia, buffer de recebimento=4096):


"""

Lê as informações do soquete, de forma armazenada em buffer, recebendo


apenas 4096 bytes por vez.

Parâmetros:
sock - O objeto socket recv_buffer -
Quantidade em bytes a receber por leitura
"""
""
buffer =
""
dados =
enquanto Verdadeiro:
dados = [Link](recv_buffer) buffer +=
dados

# Verifique se a string de mensagem final chega se "!ENDMSG!"


no buffer:
quebrar

# Remove a string de mensagem final buffer =


buffer[:-12] return buffer

se __nome__ == "__principal__":
# Defina o host do servidor, a porta e os símbolos para download host =
"[Link]" # Porta do host local = 9100 # Porta
do soquete de dados históricos syms = ["SPY", "IWM"]

# Baixe cada símbolo para o disco para sym


em syms: print
"Baixando símbolo: %s..." % sym

# Construa a mensagem necessária para o IQFeed recuperar dados message = "HIT,


%s,60,20070101 075000,,,093000,160000,1\n" % sym

# Abra um soquete de streaming para o servidor IQFeed localmente sock =


[Link](socket.AF_INET, socket.SOCK_STREAM) [Link]((host, port))

# Envie a mensagem de solicitação de dados


históricos # e armazene os dados em buffer
[Link](message)
Machine Translated by Google

74

dados = leitura_de_dados_históricos_socket(meia) [Link]

# Remova todas as linhas finais e o delimitador de vírgula de


fim de linha de cada registro data =
"".join([Link]("\r")) data = [Link](",
\n","\n")[:-1]

# Grave o fluxo de dados no disco f =


open("%[Link]" % sym, "w") [Link](data)
[Link]()

Com opções adicionais de assinatura na conta DTN IQFeed, é possível baixar contratos futuros individuais (e contratos
contínuos retroajustados), opções e índices. O DTN IQFeed também oferece streaming de ticks em tempo real, mas esse tipo
de dado está fora do escopo do livro.

8.5 Limpeza de Dados Financeiros


Após a entrega dos dados financeiros dos fornecedores, é necessário realizar a limpeza de dados.
Infelizmente, este pode ser um processo trabalhoso, mas extremamente necessário. Há vários problemas que
precisam ser resolvidos: dados incorretos, consideração de agregação de dados e preenchimento de dados.
Os contratos de ações e futuros têm seus próprios desafios específicos que devem ser enfrentados antes da
pesquisa de estratégia, incluindo ajustes para frente/para trás, costura contínua de contratos e tratamento de
ações corporativas.

8.5.1 Qualidade dos Dados


A reputação de um fornecedor de dados frequentemente depende da qualidade (percebida) dos seus dados. Em
termos simples, dados ruins ou ausentes levam a sinais de negociação errôneos e, portanto, a potenciais perdas.
Apesar disso, muitos fornecedores ainda sofrem com dados de baixa qualidade ou inconsistentes. Portanto,
sempre é necessário realizar um processo de limpeza.
Os principais culpados pela má qualidade dos dados são dados conflitantes/incorretos e agregação opaca de
múltiplas fontes de dados e correção de erros ("backfilling").

Dados conflitantes e incorretos

Dados incorretos podem ocorrer em qualquer ponto do fluxo. Bugs no software de uma corretora podem levar a
preços incorretos ao comparar negociações. Isso chega ao vendedor e, consequentemente, ao trader. Vendedores
respeitáveis tentarão sinalizar "ticks incorretos" no início do fluxo e, muitas vezes, deixarão a "correção" desses
pontos para o trader.

8.5.2 Contratos Futuros Contínuos

Nesta seção, discutiremos as características dos contratos futuros que apresentam um desafio de dados do ponto
de vista de backtesting. Em particular, a noção de "contrato contínuo".
Descreveremos as principais dificuldades dos futuros e forneceremos uma implementação em Python com pandas
que pode aliviar parcialmente os problemas.

Breve Visão Geral dos Contratos Futuros

Futuros são uma forma de contrato firmado entre duas partes para a compra ou venda de uma quantidade de um
ativo subjacente em uma data futura específica. Essa data é conhecida como entrega ou vencimento. Quando
essa data é atingida, o comprador deve entregar o ativo subjacente físico (ou equivalente em dinheiro) ao
vendedor pelo preço acordado na data de formação do contrato.
Machine Translated by Google

75

Na prática, os contratos futuros são negociados em bolsas de valores (ao contrário do mercado de balcão - OTC) para
quantidades e qualidades padronizadas do ativo subjacente. Os preços são ajustados a mercado diariamente. Os contratos
futuros são incrivelmente líquidos e amplamente utilizados para fins especulativos. Embora os contratos futuros fossem
frequentemente utilizados para proteger os preços de produtos agrícolas ou industriais, um contrato futuro pode ser firmado
sobre qualquer ativo subjacente tangível ou intangível, como índices de ações, taxas de juros ou valores cambiais.

Uma lista detalhada de todos os códigos de símbolos usados para contratos futuros em várias bolsas pode ser
encontrada no site CSI Data: Futures Factsheet.
A principal diferença entre um contrato futuro e a propriedade de ações é o fato de que um contrato futuro tem uma janela
de disponibilidade limitada em virtude da data de vencimento. A qualquer momento, haverá uma variedade de contratos futuros
sobre o mesmo ativo subjacente, todos com datas de vencimento variadas. O contrato com a data de vencimento mais próxima
é conhecido como contrato próximo. O problema que enfrentamos como traders quantitativos é que, a qualquer momento,
temos a opção de vários contratos para negociar. Portanto, estamos lidando com um conjunto sobreposto de séries temporais,
em vez de um fluxo contínuo, como no caso de ações ou câmbio.

O objetivo desta seção é delinear várias abordagens para construir um fluxo contínuo de contratos a partir deste conjunto
de múltiplas séries e destacar as compensações associadas a cada técnica.

Formando um Contrato Futuro Contínuo

A principal dificuldade em tentar gerar um contrato contínuo a partir de contratos subjacentes com entregas variáveis é que os
contratos nem sempre são negociados aos mesmos preços. Assim, surgem situações em que eles não proporcionam uma
transição suave de um para o outro. Isso se deve aos efeitos de contágio e backwardation . Existem várias abordagens para
lidar com esse problema, que discutiremos a seguir.

Infelizmente, não existe um método "padrão" único para unir contratos futuros no setor financeiro. Em última análise, o
método escolhido dependerá fortemente da estratégia utilizada nos contratos e do método de execução. Apesar de não existir
um método único, existem algumas abordagens comuns:

O método de ajuste Back/Forward ("Panamá") alivia a "lacuna" entre múltiplos contratos, deslocando cada contrato de
forma que as entregas individuais se juntem suavemente aos contratos adjacentes. Assim, a abertura/fechamento dos contratos
anteriores no vencimento coincidem.
O principal problema com o método do Panamá é a introdução de um viés de tendência, que introduz uma grande variação
nos preços. Isso pode levar a dados negativos para contratos com histórico suficiente. Além disso, há uma perda das diferenças
relativas de preços devido a uma mudança absoluta nos valores. Isso significa que os retornos são complicados de calcular (ou
simplesmente incorretos).
A abordagem de Ajuste de Proporcionalidade é semelhante à metodologia de ajuste para lidar com desdobramentos de
ações em ações. Em vez de considerar uma variação absoluta nos contratos sucessivos, a razão entre o preço de liquidação
(fechamento) mais antigo e o preço de abertura mais recente é usada para ajustar proporcionalmente os preços dos contratos
históricos. Isso permite um fluxo contínuo sem interrupção do cálculo dos retornos percentuais.

O principal problema com o ajuste proporcional é que qualquer estratégia de negociação baseada em um nível de preço
absoluto também terá que ser ajustada de forma semelhante para executar o sinal correto.
Este é um processo problemático e sujeito a erros. Portanto, esse tipo de fluxo contínuo costuma ser útil apenas para análises
estatísticas resumidas, em oposição à pesquisa de backtesting direta.
O método Rollover/Série Perpétua cria um contrato contínuo de contratos sucessivos tomando uma proporção ponderada
linearmente de cada contrato ao longo de um número de dias para garantir uma transição mais suave entre cada um.

Por exemplo, considere cinco dias de suavização. O preço no dia 1, P1, é igual a 80% do preço do contrato distante (F1) e
20% do preço do contrato próximo (N1). Da mesma forma, no dia 2, o preço é P2 = 0,6 × F2 + 0,4 × N2. No dia 5, temos P5 =
0,0 × F5 + 1,0 × N5 = N5 e o contrato se torna apenas uma continuação do preço próximo. Assim, após cinco dias, o contrato
passa suavemente do preço distante para o próximo.

O problema com o método de rollover é que ele exige negociações em todos os cinco dias, o que pode aumentar os custos
de transação. Existem outras abordagens menos comuns para o problema, mas iremos
Machine Translated by Google

76

evite-os aqui.
O restante da seção se concentrará na implementação do método de séries perpétuas, pois é o mais apropriado para
backtesting. É uma maneira útil de realizar pesquisas de pipeline de estratégia.
Vamos costurar os contratos futuros "próximo" e "distante" do petróleo bruto WTI (símbolo CL) para gerar uma série de
preços contínua. No momento da redação deste texto (janeiro de 2014), o contrato próximo é CLF2014 (janeiro) e o contrato
distante é CLG2014 (fevereiro).
Para realizar o download dos dados de futuros, utilizei o plugin Quandl. Certifique-se de configurar o ambiente virtual
Python correto no seu sistema e instale o pacote Quandl digitando o seguinte no terminal:

pip install Quandl

Agora que o pacote Quandl está instalado, precisamos usar o NumPy e o Pandas para realizar a construção do rollover. Crie um novo
arquivo e insira as seguintes instruções de importação:

#!/usr/bin/python # -*-
codificação: utf-8 -*-

# cont_futures.py

de __future__ importar função_de_impressão

importar data e hora

importar numpy como


np importar pandas
como pd importar Quandl
O trabalho principal é realizado na função futures_rollover_weights . Ela requer uma data de início (a primeira data
do contrato mais próximo), um dicionário de datas de liquidação do contrato (expiry_dates), os símbolos dos contratos e o
número de dias para a renovação do contrato (o padrão é cinco). Os comentários abaixo explicam o código:

def futures_rollover_weights(data_de_início, datas_de_expiração,


contratos, rollover_days=5): """Isso
constrói um DataFrame pandas que contém pesos (entre 0,0 e 1,0) de posições contratuais
a serem mantidas para realizar um rollover de rollover_days antes do vencimento do primeiro
contrato. A matriz pode então ser 'multiplicada' por outro DataFrame contendo os preços de
liquidação de cada contrato para produzir um contrato futuro de série temporal contínua."""

# Construir uma sequência de datas começando # da data de


início do contrato mais antiga até a data final # do contrato final dates =
pd.date_range(start_date, expiry_dates[-1],
freq='B')

# Crie o DataFrame 'roll weights' que armazenará os multiplicadores para # cada contrato (entre 0,0 e 1,0)
roll_weights = [Link]([Link]((len(dates),
len(contracts))),
índice=datas, colunas=contratos)
prev_date = roll_weights.index[0]

# Percorra cada contrato e crie as ponderações específicas para # cada contrato dependendo da data de
liquidação e dos dias de rollover para i, (item, ex_date) em enumerate(expiry_dates.iteritems()):

se i < len(datas_de_expiração) - 1:
Machine Translated by Google

77

roll_weights.ix[data_anterior:data_ex - [Link](), item] = 1 roll_rng = pd.date_range(fim=data_ex


- [Link](), períodos=rollover_days + 1, freq='B')

# Crie uma sequência de pesos de rolagem (ou seja, [0,0,0,2,...,0,8,1,0] # e use-os para ajustar as
ponderações de cada futuro decay_weights = [Link](0, 1, rollover_days + 1)
roll_weights.ix[roll_rng, item] = 1 - decay_weights roll_weights.ix[roll_rng,
expiry_dates.index[i+1]] = decay_weights

caso
contrário: roll_weights.ix[data_anterior:, item] = 1
data_anterior = data_ex
retornar roll_weights

Agora que a matriz de ponderação foi produzida, é possível aplicá-la às séries temporais individuais.
A função principal baixa os contratos próximos e distantes, cria um único DataFrame para ambos, constrói
a matriz de ponderação de rollover e, por fim, produz uma série contínua de ambos os preços,
devidamente ponderados:
se __nome__ == "__principal__":
# Baixe os contratos futuros de frente e verso (próximo e distante) atuais # para o petróleo bruto WTI,
negociados na NYMEX, do [Link]. Você precisará # ajustar os contratos para refletir seus contratos
de perto/longe atuais # dependendo do momento em que ler isto! wti_near = [Link]("OFDP/
FUTURE_CLF2014") wti_far = [Link]("OFDP/FUTURE_CLG2014") wti =
[Link]({'CLF2014': wti_near['Settle'],

'CLG2014': wti_far['Acordo']}, index=wti_far.index)

# Crie o dicionário de datas de expiração para cada contrato expiry_dates = [Link](

{'CLF2014': [Link](2013, 12, 19),


'CLG2014': data e [Link] e hora(2014, 2, 21)}).pedido()

# Obtenha a matriz de ponderação de rollover/pesos do DataFrame =


futures_rollover_weights(wti_near.index[0],
datas de expiração, com colunas)

# Construir o futuro contínuo dos contratos WTI CL wti_cts = (wti * weights).sum(1).dropna()

# Produza a série mesclada de preços de liquidação de contratos


print(wti_cts.tail(60))

A saída é a seguinte:
2013-10-14 102.230
2013-10-15 101.240
2013-10-16 102.330
2013-10-17 100.620
2013-10-18 100.990
2013-10-21 99.760
2013-10-22 98.470
2013-10-23 97.000
2013-10-24 97.240
2013-10-25 97.950
..
Machine Translated by Google

78

..
2013-12-24 99.220
2013-12-26 99.550
2013-12-27 100.320
2013-12-30 99.290
2013-12-31 98.420
2014-01-02 95.440
2014-01-03 93.960
2014-01-06 93.430
2014-01-07 93.670
2014-01-08 92.330
Comprimento: 60, tipo de d: float64
Pode-se observar que a série agora é contínua nos dois contratos. Isso pode ser estendido
para lidar com múltiplas entregas ao longo de vários anos, dependendo de suas necessidades de backtesting.
Machine Translated by Google

Parte IV

Modelagem

79
Machine Translated by Google
Machine Translated by Google

Capítulo 9

Aprendizagem Estatística

O objetivo da seção Modelagem deste livro é fornecer uma estrutura quantitativa robusta para identificar
relações em dados do mercado financeiro que possam ser exploradas para gerar estratégias de negociação
lucrativas. A abordagem utilizada é a de Aprendizado Estatístico. Este capítulo descreve a filosofia do
aprendizado estatístico e as técnicas associadas que podem ser usadas para criar modelos quantitativos
para negociação financeira.

9.1 O que é Aprendizagem Estatística?


Antes de discutir os aspectos teóricos da aprendizagem estatística, é apropriado considerar um exemplo de
situação em finanças quantitativas onde tais técnicas são aplicáveis. Considere um fundo quantitativo que
deseja fazer previsões de longo prazo para o índice de ações S&P 500.
O fundo conseguiu coletar uma quantidade substancial de dados fundamentais associados às empresas
que compõem o índice. Os dados fundamentais incluem, por exemplo, a relação preço-lucro ou o valor
contábil. Como o fundo deve utilizar esses dados para fazer previsões do índice e, assim, criar uma
ferramenta de negociação? O aprendizado estatístico oferece uma abordagem para esse problema.

Em um sentido mais quantitativo, estamos tentando modelar o comportamento de um resultado ou


resposta com base em um conjunto de preditores ou características, pressupondo uma relação entre os
dois. No exemplo acima, o valor do índice do mercado de ações é a resposta e os dados fundamentais
associados às empresas constituintes são os preditores.
Isso pode ser formalizado considerando uma resposta Y com p características diferentes x1, x2, ..., xp.
Se utilizarmos a notação vetorial, podemos definir X = (x1, x2, ..., xp), que é um vetor de comprimento p.
Então o modelo do nosso relacionamento é dado por:

Y = f(X) + (9.1)

Onde f é uma função desconhecida dos preditores e representa um termo de erro ou ruído.
Importante: não depende dos preditores e tem média zero. Este termo é incluído para representar
informações que não são consideradas em f. Assim, podemos retornar ao exemplo do índice do mercado de
ações para dizer que Y representa o valor do S&P 500, enquanto os componentes xi representam os valores
de fatores fundamentais individuais.
O objetivo da aprendizagem estatística é estimar a forma de f com base nos dados observados e
para avaliar a precisão dessas estimativas.

9.1.1 Predição e Inferência

Há duas tarefas gerais que são de interesse na aprendizagem estatística: previsão e inferência.
A previsão se preocupa em prever uma resposta Y com base em um preditor recém-observado, X. Se a
relação do modelo foi determinada, então é simples prever a resposta usando uma estimativa para f para
produzir uma estimativa para a resposta:

81
Machine Translated by Google

82

Yˆ = ˆf(X) (9.2)

A forma funcional de f é frequentemente sem importância em um cenário de previsão, assumindo que as


respostas estimadas são próximas das respostas verdadeiras e, portanto, são precisas em suas previsões.
Diferentes estimativas de f produzirão várias precisões das estimativas de Y . O erro associado a ter uma
estimativa ruim ˆf de f é chamado de erro redutível. Observe que sempre há um grau de erro irredutível porque
nossa especificação original do problema incluiu o termo de erro. Este termo de erro encapsula os fatores não
medidos que podem afetar a resposta Y . A abordagem adotada é tentar minimizar o erro redutível com o
entendimento de que sempre haverá um limite superior de precisão com base no erro irredutível.

A inferência se preocupa com a situação em que é necessário compreender a relação entre X e Y e, portanto,
sua forma exata deve ser determinada. Pode-se desejar identificar preditores importantes ou determinar a
relação entre preditores individuais e a resposta.
Pode-se também questionar se a relação é linear ou não linear. A primeira significa que o modelo provavelmente
será mais interpretável, mas às custas de uma previsibilidade potencialmente pior. A segunda fornece modelos
que geralmente são mais preditivos, mas às vezes menos interpretáveis. Portanto, frequentemente existe um
trade-off entre previsibilidade e interpretabilidade.
Neste livro, estamos menos preocupados com modelos de inferência, visto que a forma real de f não é tão
importante quanto sua capacidade de fazer previsões precisas. Portanto, uma grande parte da seção de
Modelagem do livro será baseada em modelagem preditiva. A próxima seção trata de como construímos uma
estimativa ˆf para f.

9.1.2 Modelos Paramétricos e Não Paramétricos


Em uma situação de aprendizado estatístico, muitas vezes é possível construir um conjunto de tuplas de
preditores e respostas da forma {(X1, Y1),(X2, Y2), ...,(XN , YN )}, onde Xj se refere ao j-ésimo vetor preditor e
não ao j-ésimo componente de um vetor preditor particular (que é denotado por xj ). Um conjunto de dados dessa
forma é conhecido como dados de treinamento, pois será usado para treinar um método de aprendizado
estatístico particular sobre como gerar ˆf. Para realmente estimar f, precisamos encontrar um ˆf que forneça uma
aproximação razoável para um Y particular sob um preditor X particular. Existem duas categorias amplas de
modelos estatísticos que nos permitem alcançar isso. Eles são conhecidos como modelos paramétricos e não
paramétricos.

Modelos Paramétricos

A característica definidora dos métodos paramétricos é que eles exigem a especificação ou suposição da forma
de f. Esta é uma decisão de modelagem. A primeira escolha é se deve considerar um modelo linear ou não linear.
Vamos considerar o caso mais simples de um modelo linear. Tal modelo reduz o problema da estimativa de
alguma função desconhecida de dimensão p para a estimativa de um vetor de coeficientes ÿ = (ÿ0, ÿ1, ..., ÿp) de
comprimento p + 1.
Por que p + 1 e não p? Como modelos lineares podem ser afins, ou seja, podem não passar pela origem ao
criar uma "linha de melhor ajuste", um coeficiente é necessário para especificar a "interseção".
Em um modelo linear unidimensional (regressão), isso é frequentemente representado como ÿ. Para nosso
modelo linear multidimensional, onde há p preditores, precisamos de um valor adicional ÿ0 para representar
nossa interceptação e, portanto, há componentes p + 1 em nossa estimativa ÿˆ de ÿ.
Agora que especificamos uma forma funcional (linear) de f, precisamos treiná-la. "Treinar", neste caso,
significa encontrar uma estimativa para ÿ tal que:

Y ÿ ÿˆT X = ÿ0 + ÿ1x1 + ... + ÿpxp (9.3)

No cenário linear, podemos usar um algoritmo como os mínimos quadrados ordinários (MQO), mas outros
métodos também estão disponíveis. É muito mais simples estimar ÿ do que ajustar um f (potencialmente não linear).
No entanto, ao escolher uma abordagem linear paramétrica, é improvável que nossa estimativa ˆf replique a
forma verdadeira de f. Isso pode levar a estimativas ruins, pois o modelo não é flexível o suficiente.
Machine Translated by Google

83

Uma solução possível é considerar adicionar mais parâmetros, escolhendo formas alternativas para ˆf.
Infelizmente, se o modelo se tornar flexível demais, isso pode levar a uma situação muito perigosa conhecida como overfitting,
que discutiremos detalhadamente nos capítulos subsequentes. Em essência, o modelo acompanha o ruído muito de perto e
não o sinal.

Modelos não paramétricos

A abordagem alternativa consiste em considerar uma forma não paramétrica de ˆf. A vantagem é que ela pode potencialmente
se ajustar a uma gama mais ampla de formas possíveis para f e, portanto, é mais flexível. Infelizmente, modelos não
paramétricos sofrem com a necessidade de uma grande quantidade de pontos de dados observacionais, frequentemente
muito mais do que em um ambiente paramétrico. Além disso, métodos não paramétricos também são propensos a overfitting
se não forem tratados com cuidado, conforme descrito acima.
Modelos não paramétricos podem parecer uma escolha natural para modelos de negociação quantitativa, visto que há
aparentemente uma abundância de dados (históricos) nos quais aplicar os modelos. No entanto, os métodos nem sempre são
ideais. Embora a maior flexibilidade seja atraente para modelar as não linearidades em dados do mercado de ações, é muito
fácil sobreajustar os dados devido à baixa relação sinal/ruído encontrada em séries temporais financeiras.

Portanto, é preferível um "meio-termo" de consideração de modelos com algum grau de flexibilidade.


Discutiremos esses problemas no capítulo sobre Otimização mais adiante no livro.

9.1.3 Aprendizagem supervisionada e não supervisionada


Em aprendizado de máquina estatístico, costuma-se distinguir entre métodos supervisionados e não supervisionados. Neste
livro, nos interessaremos quase exclusivamente por técnicas supervisionadas, mas as técnicas não supervisionadas
certamente são aplicáveis aos mercados financeiros.
Um modelo supervisionado requer que, para cada vetor preditor Xj, haja uma resposta Yj associada . A "supervisão" do
procedimento ocorre quando o modelo para f é treinado ou ajustado a esses dados específicos. Por exemplo, ao ajustar um
modelo de regressão linear, o algoritmo MCO é usado para treiná-lo, produzindo, em última análise, uma estimativa ÿˆ para o
vetor de coeficientes de regressão, ÿ.
Em um modelo não supervisionado, não há resposta correspondente Yj para nenhum preditor Xj específico . Portanto,
não há nada para "supervisionar" o treinamento do modelo. Este é claramente um ambiente muito mais desafiador para um
algoritmo produzir resultados, pois não existe uma "função de aptidão" para avaliar a precisão. Apesar dessa desvantagem,
as técnicas não supervisionadas são extremamente poderosas. Elas são particularmente úteis no contexto de agrupamento.

Um modelo de agrupamento parametrizado, quando fornecido com um parâmetro que especifica o número de
agrupamentos a serem identificados, pode frequentemente discernir relações imprevistas nos dados que, de outra forma, não
seriam facilmente determinadas. Esses modelos geralmente se enquadram no domínio da análise de negócios e da otimização
de marketing para o consumidor, mas têm aplicações em finanças, particularmente no que diz respeito à avaliação de
agrupamentos em volatilidade, por exemplo.
Este livro se concentrará predominantemente em métodos de aprendizagem supervisionada, uma vez que existe uma
grande quantidade de dados históricos para treinar tais modelos.

9.2 Técnicas
O aprendizado de máquina estatístico é um vasto campo interdisciplinar, com muitas áreas de pesquisa distintas.
O restante deste capítulo considerará as técnicas mais relevantes para finanças quantitativas e negociação algorítmica em
particular.

9.2.1 Regressão
Regressão refere-se a um amplo grupo de técnicas de aprendizado de máquina supervisionado que fornecem recursos
preditivos e inferenciais. Uma parcela significativa das finanças quantitativas utiliza técnicas de regressão e, portanto, é
essencial estar familiarizado com o processo. A regressão tenta modelar a relação entre uma variável dependente (resposta)
e um conjunto de variáveis independentes (preditores). Em particular, o objetivo da regressão é determinar a mudança em
uma resposta, quando
Machine Translated by Google

84

uma das variáveis independentes muda, sob a suposição de que as variáveis independentes restantes são mantidas
fixas.
A técnica de regressão mais conhecida é a Regressão Linear, que pressupõe uma relação linear entre os preditores
e a resposta. Esse modelo leva a estimativas de parâmetros (geralmente denotadas pelo vetor ÿˆ) para a resposta
linear a cada preditor. Esses parâmetros são estimados por meio de um procedimento conhecido como mínimos
quadrados ordinários (MQO). A regressão linear pode ser usada tanto para previsão quanto para inferência.

No primeiro caso, um novo valor do preditor pode ser adicionado (sem uma resposta correspondente) para prever
um novo valor de resposta. Por exemplo, considere um modelo de regressão linear usado para prever o valor do
S&P500 no dia seguinte, a partir de dados de preços dos últimos cinco dias. O modelo pode ser ajustado usando
Mínimos Quadrados Ordinários (MQO) em dados históricos. Então, quando novos dados de mercado chegam para o
S&P500, eles podem ser inseridos no modelo (como um preditor) para gerar uma resposta prevista para o preço diário
de amanhã. Isso pode formar a base de uma estratégia de negociação simplista.
No último caso (inferência), a força da relação entre a resposta e cada preditor pode ser avaliada para determinar
o subconjunto de preditores que afetam a resposta. Isso é mais útil quando o objetivo é entender por que a resposta
varia, como em um estudo de marketing ou ensaio clínico. A inferência costuma ser menos útil para quem realiza
negociação algorítmica, pois a qualidade da previsão é fundamentalmente mais importante do que a relação subjacente.
Dito isso, não se deve confiar apenas na abordagem da "caixa preta" devido à prevalência de sobreajuste ao ruído nos
dados.

Outras técnicas incluem a Regressão Logística, projetada para prever uma resposta categórica (como "PARA
CIMA", "PARA BAIXO", "ESTÁVEL") em oposição a uma resposta contínua (como o preço de uma ação no mercado).
Tecnicamente, isso a torna uma ferramenta de classificação (veja abaixo), mas geralmente é agrupada sob o nome de
regressão. Um procedimento estatístico geral conhecido como Estimativa de Máxima Verossimilhança (MLE) é usado
para estimar os valores dos parâmetros de uma regressão logística.

9.2.2 Classificação

A classificação abrange técnicas de aprendizado de máquina supervisionado que visam classificar uma observação
(semelhante a um preditor) em um conjunto de categorias predefinidas, com base nas características associadas à
observação. Essas categorias podem ser desordenadas, por exemplo, "vermelho", "amarelo", "azul", ou ordenadas,
por exemplo, "baixo", "médio", "alto". Neste último caso, esses grupos categóricos são conhecidos como ordinais.
Algoritmos de classificação – classificadores – são amplamente utilizados em finanças quantitativas, especialmente
na área de previsão da direção do mercado. Neste livro, estudaremos os classificadores extensivamente.

Classificadores podem ser utilizados em negociações algorítmicas para prever se uma determinada série temporal
terá retornos positivos ou negativos em períodos subsequentes (desconhecidos). Isso é semelhante a uma configuração
de regressão, exceto que o valor real da série temporal não está sendo previsto, mas sim sua direção. Mais uma vez,
podemos usar preditores contínuos, como preços de mercado anteriores, como observações. Consideraremos
classificadores lineares e não lineares, incluindo Regressão Logística, Análise Discriminante Linear/Quadrática,
Máquinas de Vetores de Suporte (SVM) e Redes Neurais Artificiais (ANN). Observe que alguns dos métodos anteriores
também podem ser usados em uma configuração de regressão.

9.2.3 Modelos de Séries Temporais

Um componente essencial na negociação algorítmica é o tratamento e a previsão de séries temporais financeiras.


Nosso objetivo geral é prever valores futuros de séries temporais com base em valores anteriores ou fatores externos.
Assim, a modelagem de séries temporais pode ser vista como um subconjunto misto de regressão e classificação.
Os modelos de séries temporais diferem dos modelos não temporais porque fazem uso deliberado da ordem temporal
das séries. Assim, os preditores são frequentemente baseados em valores passados ou atuais, enquanto as respostas
são frequentemente valores futuros a serem previstos.
Existe uma vasta literatura sobre diferentes modelos de séries temporais. Existem duas grandes famílias de
modelos de séries temporais que nos interessam em negociação algorítmica. O primeiro conjunto é a família de
modelos de média móvel integrada autorregressiva linear (ARIMA), que são usados para modelar as variações no
valor absoluto de uma série temporal. A outra família de séries temporais é a autorregressiva.
Machine Translated by Google

85

Modelos de heterocedasticidade condicional (ARCH), que são usados para modelar a variância (ou seja, a
volatilidade) de séries temporais ao longo do tempo. Os modelos ARCH usam valores anteriores (volatilidades)
da série temporal para prever valores futuros (volatilidades). Isso contrasta com os modelos de volatilidade
estocástica, que utilizam mais de uma série temporal estocástica (ou seja, múltiplas equações diferenciais
estocásticas) para modelar a volatilidade.
Todas as séries temporais brutas de preços históricos são discretas, pois contêm valores finitos. No campo
das finanças quantitativas, é comum estudar modelos de séries temporais contínuas. Em particular, o famoso
Movimento Browniano Geométrico, o modelo de Volatilidade Estocástica de Heston e o modelo de Ornstein-
Uhlenbeck representam séries temporais contínuas com diferentes formas de comportamento estocástico.
Utilizaremos esses modelos de séries temporais nos capítulos subsequentes para tentar caracterizar o
comportamento das séries temporais financeiras, a fim de explorar suas propriedades e criar estratégias de
negociação viáveis.
Machine Translated by Google

86
Machine Translated by Google

Capítulo 10

Análise de Séries Temporais

Neste capítulo, consideraremos testes estatísticos que nos ajudarão a identificar séries de preços que apresentam
comportamento de tendência ou de reversão à média. Se pudermos identificar essas séries estatisticamente, poderemos
capitalizar esse comportamento, formulando estratégias de negociação de momentum ou de reversão à média.
Nos capítulos seguintes, usaremos esses testes estatísticos para nos ajudar a identificar séries temporais candidatas e,
então, criar estratégias algorítmicas em torno delas.

10.1 Teste de Reversão à Média


Um dos principais conceitos de negociação quantitativa é a reversão à média. Esse processo se refere a uma série temporal
que apresenta uma tendência a retornar a um valor médio histórico. Essa série temporal pode ser explorada para gerar
estratégias de negociação à medida que entramos no mercado quando uma série de preços está longe da média, sob a
expectativa de que a série retorne a um valor médio, o que nos leva a sair do mercado com lucro. Estratégias de reversão à
média constituem um grande componente da arbitragem estatística em fundos de hedge quantitativos. Em capítulos
posteriores, criaremos estratégias intradiárias e interdiárias que exploram o comportamento de reversão à média.

A ideia básica ao tentar determinar se uma série temporal é reversível à média é usar um teste estatístico para verificar
se ela difere do comportamento de um passeio aleatório. Um passeio aleatório é uma série temporal em que o próximo
movimento direcional é completamente independente de quaisquer movimentos passados – em essência, a série temporal
não tem "memória" de onde esteve. Uma série temporal com reversão à média, no entanto, é diferente. A variação no valor
da série temporal no próximo período é proporcional ao valor atual. Especificamente, é proporcional à diferença entre o preço
histórico médio e o preço atual.

Matematicamente, tal série temporal (contínua) é chamada de processo de Ornstein-Uhlenbeck. Se pudermos demonstrar,
estatisticamente, que uma série de preços se comporta como uma série de Ornstein-Uhlenbeck, podemos iniciar o processo
de formação de uma estratégia de negociação em torno dela. Portanto, o objetivo deste capítulo é delinear os testes
estatísticos necessários para identificar a reversão à média e, em seguida, utilizar bibliotecas Python (em particular,
statsmodels) para implementar esses testes. Em particular, estudaremos o conceito de estacionariedade e como testá-la.

Conforme mencionado acima, uma série temporal contínua de reversão à média pode ser representada por um modelo de Ornstein-
Equação diferencial estocástica de Uhlenbeck:

dxt = ÿ(µ ÿ xt)dt + ÿdWt (10.1)

Onde ÿ é a taxa de reversão à média, µ é o valor médio do processo, ÿ é a variância do processo e Wt é um Processo
de Wiener ou Movimento Browniano.
Esta equação afirma essencialmente que a mudança da série de preços no próximo período de tempo contínuo é
proporcional à diferença entre o preço médio e o preço atual, com a adição de ruído gaussiano.

Podemos usar esta equação para motivar a definição do Teste Dickey-Fuller Aumentado,
que descreveremos agora.

87
Machine Translated by Google

88

10.1.1 Teste Dickey-Fuller Aumentado (ADF)


O teste ADF utiliza o fato de que, se uma série de preços apresentar reversão à média, o próximo nível de preços será
proporcional ao nível de preços atual. Matematicamente, o ADF baseia-se na ideia de testar a presença de uma raiz
unitária em uma amostra de série temporal autorregressiva.
Podemos considerar um modelo para uma série temporal, conhecido como modelo de defasagem linear de ordem p.
Este modelo afirma que a variação no valor da série temporal é proporcional a uma constante, o próprio tempo e os
valores de p anteriores da série temporal, juntamente com um termo de erro:

ÿyt = ÿ + ÿt + ÿytÿ1 + ÿ1ÿytÿ1 + · · · + ÿpÿ1ÿytÿp+1 + t (10.2)

Onde ÿ é uma constante, ÿ representa o coeficiente de uma tendência temporal e ÿyt = y(t) ÿ y(t ÿ 1). O papel do teste
de hipótese ADF é determinar, estatisticamente, se ÿ = 0, o que indicaria (com ÿ = ÿ = 0) que o processo é uma caminhada
aleatória e, portanto, não reversível à média.
Portanto, estamos testando a hipótese nula de que ÿ = 0.
Se a hipótese de que ÿ = 0 puder ser rejeitada, então o movimento subsequente da série de preços é proporcional ao
preço atual e, portanto, é improvável que seja um passeio aleatório. É isso que queremos dizer com um "teste estatístico".

Então, como o teste ADF é realizado?

• Calcular a estatística de teste, DFÿ , que é usado na decisão de rejeitar a hipótese nula

• Utilizar a distribuição da estatística de teste (calculada por Dickey e Fuller), juntamente com a
valores críticos, a fim de decidir se deve rejeitar a hipótese nula

Vamos começar calculando a estatística de teste (DFÿ ). Esta é dada pela proporcionalidade da amostra
constante ÿˆ dividida pelo erro padrão da constante de proporcionalidade da amostra:

ÿˆ
DFÿ = (10.3)
SE(ˆÿ)

Agora que temos a estatística de teste, podemos usar a distribuição da estatística de teste calculada por Dickey e
Fuller para determinar a rejeição da hipótese nula para qualquer valor crítico percentual escolhido. A estatística de teste
é um número negativo e, portanto, para ser significativa além dos valores críticos, o número deve ser menor (ou seja,
mais negativo) do que esses valores.
Uma questão prática fundamental para os traders é que qualquer desvio constante de longo prazo em um preço é de
magnitude muito menor do que quaisquer flutuações de curto prazo e, portanto, o desvio é frequentemente considerado
zero (ÿ = 0) para o modelo de defasagem linear descrito acima.
Como estamos considerando um modelo de defasagem de ordem p, precisamos definir p como um valor específico.
Geralmente, para pesquisa de negociação, é suficiente definir p = 1 para nos permitir rejeitar a hipótese nula.
No entanto, observe que isso tecnicamente introduz um parâmetro em um modelo de negociação baseado no ADF.

Para calcular o teste Dickey-Fuller Aumentado, podemos utilizar as bibliotecas pandas e statsmodels. A primeira nos fornece
um método simples para obter dados de Volume de Abertura-Alto-Baixo-Fechamento (OHLCV) do Yahoo Finanças, enquanto a
segunda encapsula o teste ADF em uma função fácil de chamar. Isso nos impede de calcular a estatística de teste manualmente, o
que nos economiza tempo.

Realizaremos o teste ADF em uma série de preços de amostra de ações da Amazon, de 1º de janeiro de 2000 a 1º
de janeiro de 2015.
Aqui está o código Python para realizar o teste:

de __future__ importar função_de_impressão

# Importar a biblioteca de séries


temporais import [Link] como ts

# Importar Datetime e o Pandas DataReader


Machine Translated by Google

89

de data e hora importar data e hora


importar [Link] como web

# Baixe os dados do Amazon OHLCV de 01/01/2000 a 01/01/2015 amzn =


[Link]("AMZN", "yahoo", datetime(2000,1,1), datetime(2015,1,1))

# Produza os resultados do teste Dickey-Fuller Aumentado para a Amazon # com um valor de


ordem de atraso de 1 [Link](amzn['Adj
Close'], 1)

Aqui está o resultado do teste Dickey-Fuller Aumentado para a Amazon durante o período. O primeiro valor é a estatística
de teste calculada, enquanto o segundo valor é o valor-p. O quarto valor é o número de pontos de dados na amostra. O quinto
valor, o dicionário, contém os valores críticos da estatística de teste nos valores de 1, 5 e 10%, respectivamente.

(0,049177575166452235,
0,96241494632563063, 1,

3771,
{'1%': -3,4320852842548395,
'10%': -2,5671781529820348, '5%':
-2,8623067530084247},
19576,116041473877)

Como o valor calculado da estatística de teste é maior do que qualquer um dos valores críticos nos níveis de 1, 5 ou 10%,
não podemos rejeitar a hipótese nula de ÿ = 0 e, portanto, é improvável que tenhamos encontrado uma série temporal com
reversão à média. Isso está de acordo com nossa teoria, visto que a maioria das ações se comporta de forma semelhante ao
Movimento Browniano Geométrico (MBG), ou seja, um passeio aleatório.
Isso conclui como utilizamos o teste ADF. No entanto, existem métodos alternativos para detectar a reversão à média,
particularmente por meio do conceito de estacionariedade, que discutiremos agora.

10.2 Teste de estacionariedade


Uma série temporal (ou processo estocástico) é definida como fortemente estacionária se sua distribuição de probabilidade
conjunta for invariante sob translações no tempo ou no espaço. Em particular, e de fundamental importância para os traders,
a média e a variância do processo não mudam ao longo do tempo ou do espaço, e cada uma delas não segue uma tendência.

Uma característica crucial das séries de preços estacionárias é que os preços dentro da série se difundem a partir de seu
valor inicial a uma taxa mais lenta do que a de um GBM. Ao medir a taxa desse comportamento difusivo, podemos identificar a
natureza da série temporal e, assim, detectar se ela está revertendo à média.
Vamos agora delinear um cálculo, nomeadamente o Expoente de Hurst, que nos ajuda a caracterizar
a estacionariedade de uma série temporal.

10.2.1 Expoente de Hurst


O objetivo do Expoente de Hurst é nos fornecer um valor escalar que nos ajudará a identificar (dentro dos limites da estimativa
estatística) se uma série está revertendo à média, caminhando aleatoriamente ou apresentando tendência.

A ideia por trás do cálculo do Expoente de Hurst é que podemos usar a variância de um preço de logaritmo, a variância
série para avaliar a taxa de comportamento difusivo. Para um intervalo de tempo arbitrário ÿ dado , de ÿ é
por:

2
Var(ÿ ) = | log(t + ÿ ) ÿ log(t)| (10.4)

Onde os colchetes e referem-se à média de todos os valores de t.


A ideia é comparar a taxa de difusão com a de um GBM. No caso de um GBM, em tempos grandes (ou seja, quando ÿ é
grande), a variância de ÿ é proporcional a ÿ:
Machine Translated by Google

90

2 ÿÿ
| log(t + ÿ ) ÿ log(t)| (10.5)

Se encontrarmos um comportamento diferente dessa relação, identificamos uma série de tendência ou de reversão à média. A
principal conclusão é que, se quaisquer movimentos sequenciais de preços apresentarem correlação diferente de zero (conhecida
como autocorrelação), a relação acima não é válida. Em vez disso, ela pode ser modificada para incluir um valor de expoente "2H",
que nos dá o valor H do Expoente de Hurst:

2 2H
ÿÿ (10.6)
| log(t + ÿ ) ÿ log(t)|

Assim, pode-se observar que, se H = 0,5, temos um GBM, visto que ele simplesmente se torna a relação anterior. No entanto,
se H = 0,5, temos um comportamento de tendência ou de reversão à média. Em particular:

• H < 0,5 - A série temporal é reversível à média

• H = 0,5 - A série temporal é um Movimento Browniano Geométrico

• H > 0,5 - A série temporal está em tendência

Além da caracterização da série temporal, o Expoente de Hurst também descreve até que ponto uma série se comporta da
maneira categorizada. Por exemplo, um valor de H próximo a 0 é uma série com alta reversão à média, enquanto para H próximo a
1, a série apresenta forte tendência.
Para calcular o Expoente de Hurst para a série de preços da Amazon, conforme utilizado acima na explicação
nação do ADF, podemos usar o seguinte código Python:

de __future__ importar função_de_impressão

de numpy importar cumsum, log, polyfit, sqrt, std, subtrair de [Link]


importar randn

def hurst(ts):
"""Retorna o Expoente de Hurst do vetor de série temporal ts"""
# Crie o intervalo de valores de atraso lags
= range(2, 100)

# Calcular a matriz das variâncias das diferenças defasadas tau =


[sqrt(std(subtract(ts[lag:], ts[:-lag]))) para defasagem em defasagens]

# Use um ajuste linear para estimar o Expoente de Hurst poly =


polyfit(log(lags), log(tau), 1)

# Retorna o expoente de Hurst da saída polyfit return poly[0]*2.0

# Crie uma série de movimento browniano gométrico, reversão à média e tendência gbm =
log(cumsum(randn(100000))+1000) mr =
log(randn(100000)+1000) tr =
log(cumsum(randn(100000)+1)+1000)

# Produza o Expoente de Hurst para cada uma das séries acima # e o preço
da Amazon (o preço de fechamento ajustado) para # o teste ADF fornecido
acima no artigo print("Hurst(GBM): %s" % hurst(gbm))
print("Hurst(MR): %s" % hurst(mr)) print("Hurst(TR):
%s" % hurst(tr))
Machine Translated by Google

91

# Supondo que você executou o código acima para obter 'amzn'! print("Hurst(AMZN):
%s" % hurst(amzn['Adj Close']))

A saída do código Python do Hurst Exponent é fornecida abaixo:

Hurst(GBM): 0,502051910931 Hurst(MR):


0,000166110248967 Hurst(TR): 0,957701001252
Hurst(AMZN): 0,454337476553

A partir desta saída, podemos ver que o GBM possui um Expoente de Hurst, H, que é quase exatamente 0,5. A série de
reversão à média tem H quase igual a zero, enquanto a série de tendência tem H próximo de 1.

Curiosamente, a Amazon também tem H próximo de 0,5, indicando que é semelhante a um GBM, pelo menos
para o período de amostra que estamos usando!

10.3 Cointegração
Na verdade, é muito difícil encontrar um ativo negociável que possua comportamento de reversão à média. As ações
se comportam, em linhas gerais, como GBMs e, portanto, tornam as estratégias de negociação de reversão à média
relativamente inúteis. No entanto, nada nos impede de criar uma carteira de séries de preços estacionárias. Portanto,
podemos aplicar estratégias de negociação de reversão à média à carteira.
A forma mais simples de estratégias de negociação com reversão à média é a clássica "negociação em pares",
que geralmente envolve um par de ações long-short neutro em relação ao dólar. A teoria é que duas empresas do
mesmo setor provavelmente estarão expostas a fatores de mercado semelhantes, que afetam seus negócios.
Ocasionalmente, os preços relativos das ações divergem devido a certos eventos, mas retornam à média de longo
prazo.
Consideremos duas ações do setor de energia: a Approach Resources Inc., indicada pelo ticker AREX, e a Whiting
Petroleum Corp., indicada pelo ticker WLL. Ambas estão expostas a condições de mercado semelhantes e, portanto,
provavelmente terão uma relação de pares estacionários. Agora, criaremos alguns gráficos, usando o Pandas e as
bibliotecas Matplotlib para demonstrar a natureza cointegradora do AREX e do WLL. O primeiro gráfico (Figura 10.1)
exibe seus respectivos históricos de preços para o período de 1º de janeiro de 2012 a 1º de janeiro de 2013.

Se criarmos um gráfico de dispersão de seus preços, veremos que a relação é amplamente linear (veja Figura
10.2) para esse período.
A negociação de pares funciona essencialmente usando um modelo linear para uma relação entre os dois preços
de ações:

y(t) = ÿx(t) + (t) (10.7)

Onde y(t) é o preço das ações da AREX e x(t) é o preço das ações da WLL, ambos no dia t.
Se plotarmos os resíduos (t) = y(t) ÿ ÿx(t) (para um valor específico de ÿ que determinaremos a seguir), criamos
uma nova série temporal que, à primeira vista, parece relativamente estacionária. Isso é mostrado na Figura 10.3.

Descreveremos o código para cada um desses gráficos abaixo.

10.3.1 Teste Dickey-Fuller Aumentado Cointegrado


Para confirmar estatisticamente se esta série é reversível à média, poderíamos usar um dos testes descritos acima, ou
seja, o Teste Dickey-Fuller Aumentado ou o Expoente de Hurst. No entanto, nenhum desses testes nos ajudará a
determinar ÿ, o índice de hedge necessário para formar a combinação linear; eles apenas nos dirão se, para um ÿ
específico, a combinação linear é estacionária.

É aqui que entra o teste Dickey-Fuller Aumentado Cointegrado (CADF). Ele determina a taxa de hedge ideal
realizando uma regressão linear em relação às duas séries temporais e, em seguida, testa a estacionariedade na
combinação linear.
Machine Translated by Google

92

Figura 10.1: Gráficos de séries temporais de AREX e WLL

Figura 10.2: Gráfico de dispersão dos preços AREX e WLL

Implementação Python

Agora usaremos bibliotecas Python para testar uma relação de cointegração entre AREX e WLL no período de
1º de janeiro de 2012 a 1º de janeiro de 2013. Usaremos o Yahoo Finance como fonte de dados e o
Statsmodels para realizar o teste ADF, como acima.
A primeira tarefa é criar um novo arquivo, [Link], e importar as bibliotecas necessárias. O código utiliza
NumPy, Matplotlib, Pandas e Statsmodels. Para rotular corretamente os eixos
Machine Translated by Google

93

Figura 10.3: Diagrama de resíduos da combinação linear AREX e WLL

e baixar dados do Yahoo Finanças via pandas, importamos os módulos [Link] e [Link].
Também usamos a função Mínimos Quadrados Ordinários (MQO) do pandas:

#!/usr/bin/python #
-*- codificação: utf-8 -*-

# [Link]

importar data e
hora importar numpy
como np importar [Link]
como plt importar [Link] como
mdates importar pandas
como pd importar [Link]
como web
importar pprint importar [Link] como ts

de [Link] importar ols

A primeira função, plot_price_series, recebe um DataFrame do Pandas como entrada, com duas
colunas definidas pelas strings de espaço reservado "ts1" e "ts2". Essas serão nossas ações em pares. A
função simplesmente plota as duas séries de preços no mesmo gráfico. Isso nos permite inspecionar
visualmente se alguma cointegração é provável.
Utilizamos o módulo de datas do Matplotlib para obter os meses dos objetos datetime. Em seguida,
criamos uma figura e um conjunto de eixos nos quais aplicaremos a rotulagem/plotagem. Por fim, plotamos
a figura:

# [Link]

def plot_price_series(df, ts1, ts2): months =


[Link]() # todo mês fig, ax = [Link]()
[Link]([Link], df[ts1],
label=ts1)
Machine Translated by Google

94

[Link]([Link], df[ts2], rótulo=ts2)


[Link].set_major_locator(meses)
[Link].set_major_formatter([Link]('%b %Y'))
ax.set_xlim([Link](2012, 1, 1), [Link](2013, 1, 1)) [Link](Verdadeiro)
fig.autofmt_xdate()

[Link]('Mês/Ano')
[Link]('Preço ($)')
[Link]('Preços diários de %s e %s' % (ts1, ts2)) [Link]()
[Link]()

A segunda função, plot_scatter_series, cria um gráfico de dispersão dos dois preços. Isso nos permite inspecionar
visualmente se existe uma relação linear entre as duas séries e, portanto, se elas são boas candidatas ao procedimento
MQO e ao teste ADF subsequente:

# [Link]

def plot_scatter_series(df, ts1, ts2): [Link]('%s


Preço ($)' % ts1) [Link]('%s Preço ($)'
% ts2) [Link]('%s e %s Gráfico de
dispersão de preços' % (ts1, ts2)) [Link](df[ts1], df[ts2]) [Link]()

A terceira função, plot_residuals, foi projetada para plotar os valores residuais do modelo linear ajustado das duas
séries de preços. Esta função requer que o DataFrame do pandas tenha uma coluna "res", representando os preços
residuais:

# [Link]

def plot_residuals(df):
meses = [Link]() # todo mês fig, ax =
[Link]() [Link]([Link],
df["res"], label="Resíduos") [Link].set_major_locator(meses)

[Link].set_major_formatter([Link]('%b %Y'))
ax.set_xlim([Link](2012, 1, 1), [Link](2013, 1, 1)) [Link](True)
fig.autofmt_xdate()

[Link]('Mês/Ano')
[Link]('Preço ($)')
[Link]('Gráfico Residual')
[Link]()

[Link](df["res"])
[Link]()

Por fim, o procedimento é encapsulado em uma função __main__. A primeira tarefa é baixar os dados do OHLCV para
AREX e WLL do Yahoo Finance. Em seguida, criamos um DataFrame separado, df, usando o mesmo índice do quadro
AREX para armazenar os valores de fechamento ajustados. Em seguida, plotamos a série de preços e o gráfico de
dispersão.
Após a conclusão dos gráficos, os resíduos são calculados chamando a função pandas ols nas séries WLL e AREX.
Isso nos permite calcular o índice de hedge ÿ. O índice de hedge é então usado para criar uma coluna "res" por meio da
formação da combinação linear de WLL e AREX.
Machine Translated by Google

95

Por fim, os resíduos são plotados e o teste ADF é realizado nos resíduos calculados.
Em seguida, imprimimos os resultados do teste ADF:

# [Link]

se __nome__ == "__principal__": início


= data e [Link] e hora(2012, 1, 1) fim = data e [Link]
e hora(2013, 1, 1)

arex = [Link]("AREX", "yahoo", início, fim) wll =


[Link]("WLL", "yahoo", início, fim)

df = [Link](index=[Link]) df["AREX"] =
arex["Fechamento Ajustável"] df["WLL"] =
wll["Fechamento Ajustável"]

# Trace as duas séries temporais


plot_price_series(df, "AREX", "WLL")

# Exibe um gráfico de dispersão das duas séries temporais


plot_scatter_series(df, "AREX", "WLL")

# Calcular a taxa de hedge ideal "beta" res = ols(y=df['WLL'],


x=df["AREX"]) beta_hr = [Link].x

# Calcular os resíduos da combinação linear df["res"] = df["WLL"] -


beta_hr*df["AREX"]

# Plotar os resíduos
plot_residuals(df)

# Calcular e gerar o teste CADF sobre os resíduos cadf = [Link](df["res"])


[Link](cadf)

A saída do código (junto com os gráficos do Matplotlib) é a seguinte:

(-2,9607012342275936,
0,038730981052330332, 0, 249,

{'1%':
-3,4568881317725864,
'10%': -2,5729936189738876, '5%':
-2,8732185133016057}, 601,96849256295991)

Observa-se que a estatística de teste calculada de -2,96 é menor que o valor crítico de 5% de -2,87, o que significa que
podemos rejeitar a hipótese nula de que não há uma relação de cointegração no nível de 5%. Portanto, podemos concluir, com
um grau razoável de certeza, que AREX e WLL possuem uma relação de cointegração, pelo menos para a amostra de período
considerada.

Usaremos esse par nos capítulos subsequentes para criar uma estratégia de negociação real usando um
implementou um sistema de backtesting orientado a eventos.
Machine Translated by Google

96

10.4 Por que testes estatísticos?


Fundamentalmente, no que diz respeito à negociação algorítmica, os testes estatísticos descritos acima são tão úteis quanto
os lucros que geram quando aplicados a estratégias de negociação. Portanto, certamente faz sentido simplesmente avaliar o
desempenho no nível da estratégia, em vez do nível do preço/série temporal? Por que se dar ao trabalho de calcular todas as
métricas acima quando podemos simplesmente usar a análise do nível da negociação, medidas de risco/recompensa e
avaliações de drawdown?
Em primeiro lugar, qualquer estratégia de negociação implementada com base em uma medida estatística de série
temporal terá uma amostra muito maior para trabalhar. Isso ocorre simplesmente porque, ao calcular esses testes estatísticos,
estamos utilizando cada barra de informação, e não cada operação. Haverá muito menos operações de ida e volta do que
barras e, portanto, a significância estatística de qualquer métrica em nível de operação será muito menor.

Em segundo lugar, qualquer estratégia que implementarmos dependerá de certos parâmetros, como períodos de
retrospectiva para medidas contínuas ou medidas de pontuação z para entrada/saída de uma operação em um cenário de
reversão à média. Portanto, as métricas em nível de estratégia são apropriadas apenas para esses parâmetros, enquanto os
testes estatísticos são válidos para a amostra da série temporal subjacente.
Na prática, queremos calcular ambos os conjuntos de estatísticas. O Python, por meio das bibliotecas statsmodels e
pandas, torna isso extremamente simples. O esforço adicional é, na verdade, mínimo!
Machine Translated by Google

Capítulo 11

Previsão
Neste capítulo, criaremos um processo estatisticamente robusto para previsão de séries temporais financeiras.
Essas previsões formarão a base para futuras estratégias de negociação automatizada. Expandiremos o tópico
de Aprendizado Estatístico discutido nos capítulos anteriores e usaremos um conjunto de algoritmos de
classificação para nos ajudar a prever a direção do mercado de séries temporais financeiras.
Neste capítulo, utilizaremos o Scikit-Learn, uma biblioteca de aprendizado de máquina estatística para
Python. O Scikit-Learn contém implementações "prontas" de diversas técnicas de aprendizado de máquina. Isso
não só nos economiza bastante tempo na implementação de nossos algoritmos de negociação, como também
minimiza o risco de bugs introduzidos pelo nosso próprio código. Também permite verificação adicional em
bibliotecas de aprendizado de máquina escritas em outros pacotes, como R ou C++.
Isso nos dá muita confiança se precisarmos criar nossa própria implementação personalizada, por razões de
velocidade de execução, por exemplo.
Começaremos discutindo maneiras de mensurar o desempenho dos analistas para o caso específico das
técnicas de aprendizado de máquina utilizadas. Em seguida, consideraremos os fatores preditivos que podem
ser usados em técnicas de previsão e como escolher bons fatores. Em seguida, consideraremos diversos
algoritmos de classificação supervisionada. Por fim, tentaremos prever a direção diária do S&P 500, o que
posteriormente formará a base de uma estratégia de negociação algorítmica.

11.1 Medindo a precisão da previsão


Antes de discutirmos as escolhas de algoritmos preditores e de classificação específicos, precisamos discutir
suas características de desempenho e como avaliá-los. A classe específica de métodos em que estamos
interessados envolve a classificação supervisionada binária. Ou seja, tentaremos prever se o retorno percentual
para um determinado dia futuro será positivo ou negativo (ou seja, se o preço do nosso ativo financeiro subiu ou
caiu).
Em um previsor de produção, usando uma técnica do tipo regressão, estaríamos muito preocupados com a
magnitude dessa previsão e os desvios da previsão em relação ao valor real.
Para avaliar o desempenho desses classificadores, podemos usar as duas medidas a seguir: Taxa de Acerto
e Matriz de Confusão.

11.1.1 Taxa de acerto


A pergunta mais simples que poderíamos fazer ao nosso classificador supervisionado é "Quantas vezes previmos
a direção correta, como uma porcentagem de todas as previsões?". Isso motiva a definição da taxa de acerto do
treinamento, dada pela seguinte fórmula[9]:

n
1
Eu(yj = ˆyj ) (11.1)
n
j=1

Onde yˆj é a previsão (para cima ou para baixo) para o j-ésimo período de tempo (por exemplo, um dia) usando um determinado
classificador. I(yj = ˆyj ) é a função indicadora e é igual a 1 se yj = ˆyj e 0 se yj = ˆyj .

97
Machine Translated by Google

98

Portanto, a taxa de acerto fornece um valor percentual quanto ao número de vezes que um classificador previu
corretamente a direção para cima ou para baixo.
O Scikit-Learn fornece um método para calcular a taxa de acerto para nós como parte do processo de classificação/
treinamento.

11.1.2 Matriz de Confusão


A matriz de confusão (ou tabela de contingência) é o próximo passo lógico após calcular a taxa de acerto.
Ela é motivada pela pergunta "Quantas vezes previmos corretamente para cima e quantas vezes previmos corretamente para
baixo? Elas diferiram substancialmente?".
Por exemplo, pode acontecer que um algoritmo específico seja consistentemente mais preciso na previsão de "dias de
baixa". Isso motiva uma estratégia que enfatiza a venda a descoberto de um ativo financeiro para aumentar a lucratividade.

Uma matriz de confusão caracteriza essa ideia ao determinar a taxa de falsos positivos (conhecidos estatisticamente como erro
Tipo I) e a taxa de falsos negativos (conhecidos estatisticamente como erro Tipo II) para um classificador supervisionado. Para o
caso de classificação binária (para cima ou para baixo), teremos uma matriz 2x2:

UT UF
DF DT
Onde UT representa períodos de alta classificados corretamente, UF representa períodos de alta classificados
incorretamente (ou seja, classificados como de baixa), DF representa períodos de baixa classificados incorretamente (ou seja,
classificados como de alta) e DT representa períodos de baixa classificados corretamente.
Além da taxa de acerto, o Scikit-Learn fornece um método para calcular a matriz de confusão para nós como parte do
processo de classificação/treinamento.

11.2 Escolha de Fatores


Um dos aspectos mais cruciais da previsão de preços de ativos é escolher os fatores usados como preditores.
Há um número impressionante de fatores potenciais para escolher, o que pode parecer assustador para quem não está
familiarizado com previsões financeiras. No entanto, mesmo técnicas simples de aprendizado de máquina produzem resultados
relativamente bons quando usadas com fatores bem escolhidos. Observe que o inverso não costuma ser o caso. "Usar um
algoritmo para resolver um problema" geralmente leva a uma baixa precisão de previsão.

A escolha dos fatores é realizada tentando determinar os motivadores fundamentais do movimento dos ativos.
No caso do S&P500, fica claro que os 500 componentes, ponderados, serão, por definição, os impulsionadores fundamentais
do preço! É claro que saberíamos o preço exato da série S&P500 se soubéssemos o valor instantâneo de seus componentes,
mas existe algum poder preditivo em usar o histórico de retornos de cada componente para prever a própria série?

Alternativamente, poderíamos considerar as taxas de câmbio com países que realizam muito comércio com os EUA como
impulsionadores do preço? Poderíamos até considerar fatores econômicos e corporativos mais fundamentais, como taxas de
juros, inflação e lucros trimestrais.
A precisão do previsor será em grande parte devida à habilidade do modelador em determinar
minerando os fatores corretos antes de realizar o ajuste do modelo.

11.2.1 Fatores de preço defasados e volume


O primeiro tipo de fator frequentemente considerado na previsão de uma série temporal são os valores históricos anteriores da
própria série temporal. Assim, um conjunto de fatores p poderia ser facilmente obtido criando p-lags do preço de fechamento
da série temporal. Considere uma série temporal diária. Para cada dia atual específico k, os fatores seriam os valores históricos
diários nos períodos de tempo k - 1, k - 2, ..., k - p.
Além da própria série de preços, também podemos incorporar o volume negociado como indicador, uma vez que ele é
fornecido ao utilizar dados OHLCV (como os obtidos do Yahoo Finance, Google Finance ou Quandl, por exemplo). Assim,
podemos criar um vetor de características p + unidimensional para cada dia da série temporal, que incorpora os p defasagens
temporais e a série de volumes. Isso naturalmente leva
Machine Translated by Google

99

para um conjunto de pares (Xk, yk) representando o vetor de características unidimensional p + Xk no dia k e o preço de fechamento
atual no dia k, yk. Isso é tudo o que precisamos para iniciar um exercício de classificação supervisionada.

A seguir, consideraremos uma série temporal defasada para o S&P500 e aplicaremos múltiplas máquinas
técnicas de aprendizagem para ver se podemos prever sua direção.

11.2.2 Fatores externos


Embora séries temporais defasadas e informações de volume sejam um bom ponto de partida para a análise de séries temporais,
estamos longe de estar restritos a esses dados. Há uma vasta gama de séries temporais macroeconômicas e séries de preços de
ativos para as quais podemos considerar previsões. Por exemplo, podemos desejar fornecer uma previsão de longo prazo dos
preços das commodities com base em padrões climáticos ou determinar os movimentos da direção dos preços do câmbio por meio
de movimentos das taxas de juros internacionais.
Se tal relação entre séries puder ser determinada e demonstrada como estatisticamente significativa, então estamos aptos a considerar um modelo
de negociação robusto. Não nos deteremos muito nessas relações aqui, pois nosso objetivo é introduzir a ideia de modelagem e técnicas de aprendizado
de máquina. É bastante fácil formular hipóteses sobre relações econômicas e obter os dados das séries temporais de um repositório como o Quandl ou
diretamente de sites de estatísticas governamentais.

11.3 Modelos de Classificação

O campo do aprendizado de máquina é vasto e há muitos modelos para escolher, particularmente no âmbito da classificação
supervisionada. Novos modelos são introduzidos mensalmente pela literatura acadêmica. Seria impraticável fornecer uma lista
exaustiva de classificadores supervisionados neste capítulo; em vez disso, consideraremos algumas das técnicas mais populares da
área.

11.3.1 Regressão Logística


A primeira técnica que consideraremos é a regressão logística (RL). No nosso caso, usaremos a regressão logística para medir a
relação entre uma variável dependente categórica binária (ou seja, períodos de "alta" ou "baixa") e múltiplas variáveis contínuas
independentes, como os retornos percentuais defasados de um ativo financeiro.

O modelo de regressão logística fornece a probabilidade de que um determinado período subsequente seja categorizado como
"em alta" ou "em baixa". Assim, o modelo introduz um parâmetro, ou seja, o limite de probabilidade para classificar se um período
subsequente é "em alta" ou "em baixa". A seguir, consideraremos esse limite como 50% (ou seja, 0,5), mas ele certamente pode ser
modificado para produzir previsões alternativas.

A regressão logística é baseada na fórmula logística para modelar a probabilidade de obter um dia "positivo" (Y = U) com base
nos fatores contínuos.
Neste caso, considere a situação em que estamos interessados em prever o período subsequente a partir dos dois retornos
defasados anteriores, que denotaremos por (L1, L2). A fórmula abaixo fornece a probabilidade de haver um dia de alta, dado que
observamos os retornos nos períodos anteriores, L1 e L2:

ÿ0+ÿ1L1+ÿ2L2 e

p(Y = U|L1, L2) = ÿ0+ÿ1L1+ÿ2L2 1 + e (11.2)

A função logística é usada em vez de uma função linear (ou seja, em regressão linear) porque fornece uma probabilidade entre
[0, 1] para todos os valores de L1 e L2. Em uma regressão linear, é possível obter probabilidades negativas para essas variáveis
contínuas, portanto, precisamos de outra função.

Para ajustar o modelo (ou seja, estimar os coeficientes ÿi ), utiliza-se o método da máxima verossimilhança. Felizmente para
nós, a implementação do ajuste e da previsão da regressão logística
Machine Translated by Google

100

O modelo já é gerenciado pela biblioteca Scikit-Learn. A técnica será descrita abaixo quando tentarmos prever a direção
do S&P500.

11.3.2 Análise Discriminante


A análise discriminante é uma técnica estatística alternativa à regressão logística. Embora a regressão logística seja
menos restritiva em suas premissas do que a análise discriminante, ela pode oferecer melhor desempenho preditivo se
as premissas mais restritivas forem atendidas.
Agora consideraremos um método linear e um método não linear de análise discriminante.

Análise Discriminante Linear

Na regressão logística, modelamos a probabilidade de ver um período de tempo "de alta", dados os dois retornos
defasados anteriores (P(Y = U|L1, L2)) como uma distribuição condicional da resposta Y, dados os preditores Li , usando
uma função logística.
Na Análise Discriminante Linear (ADL), a distribuição das variáveis Li é modelada separadamente, dado Y.
Essencialmente, a ADL , e P(Y = U|L1, L2) é obtido pelo Teorema de Bayes.
resulta da suposição de que os preditores são extraídos de uma distribuição gaussiana multivariada. Após calcular
as estimativas para os parâmetros dessa distribuição, os parâmetros podem ser inseridos no Teorema de Bayes para
fazer previsões sobre a qual classe uma observação pertence.

Uma importante suposição matemática do LDA é que todas as classes (por exemplo, "para cima" e "para baixo")
compartilham a mesma matriz de covariância.
Não vou me alongar sobre as fórmulas para estimar a distribuição ou as probabilidades posteriores que
são necessários para fazer previsões, já que mais uma vez o scikit-learn faz isso para nós.

Análise Discriminante Quadrática

A Análise Discriminante Quadrática (ADQ) está intimamente ligada à ADL. A diferença significativa é que cada classe
agora pode ter sua própria matriz de covariância.
A QDA geralmente tem melhor desempenho quando os limites de decisão são não lineares. A LDA geralmente tem
melhor desempenho quando há menos observações de treinamento (ou seja, quando é necessário reduzir a variância).
O QDA, por outro lado, tem bom desempenho quando o conjunto de treinamento é grande (ou seja, a variância é menos
preocupante). O uso de um ou outro se resume, em última análise, à compensação entre viés e variância.
Assim como com LR e LDA, o Scikit-Learn cuida da implementação do QDA, então precisamos apenas
para fornecer dados de treinamento/teste para estimativa e previsão de parâmetros.

11.3.3 Máquinas de Vetores de Suporte


Para motivar Máquinas de Vetores de Suporte (MVS), precisamos considerar a ideia de um classificador que separa
diferentes classes por meio de uma fronteira de separação linear. Se tal separação direta existisse, poderíamos criar um
classificador supervisionado baseado unicamente na decisão de se novos recursos estão acima ou abaixo desse plano
de classificação linear. Na realidade, tais separações raramente existem em situações de negociação quantitativa e,
portanto, precisamos considerar classificadores de margem suave ou Classificadores de Vetores de Suporte (VCS).

Os SVCs funcionam tentando localizar um limite de separação linear no espaço de características que classifique
corretamente a maioria, mas não todas, das observações de treinamento, criando um limite de separação ótimo entre as
duas classes. Às vezes, esse limite é bastante eficaz se a separação de classes for predominantemente linear. No
entanto, outras vezes, tais separações não são possíveis e é necessário utilizar outras técnicas.

A motivação por trás da extensão de um SVC é permitir limites de decisão não lineares.
Este é o domínio da Máquina de Vetores de Suporte (SVM). A principal vantagem das SVMs é que elas permitem uma
ampliação não linear do espaço de recursos para incluir uma não linearidade significativa, mantendo ainda uma eficiência
computacional significativa, usando um processo conhecido como "truque do kernel".

As SVMs permitem limites de decisão não lineares por meio de muitas opções diferentes de "kernel". Em particular,
em vez de usar um limite de separação totalmente linear como no SVC, podemos usar limites quadráticos.
Machine Translated by Google

101

polinômios, polinômios de ordem superior ou mesmo kernals radiais para descrever limites não lineares.
Isso nos dá um grau significativo de flexibilidade, à custa sempre presente de viés em nossas estimativas.
Usaremos o SVM abaixo para tentar particionar o espaço de recursos (ou seja, os fatores de preço defasados e o volume)
por meio de um limite não linear que nos permite fazer previsões razoáveis sobre se o dia seguinte será um movimento de
alta ou de baixa.

11.3.4 Árvores de Decisão e Florestas Aleatórias

Árvores de decisão são uma técnica de classificação supervisionada que utiliza uma estrutura de árvore para particionar o
espaço de recursos em subconjuntos recursivos por meio de uma "decisão" em cada nó da árvore.
Por exemplo, pode-se perguntar se o preço de ontem estava acima ou abaixo de um determinado limite, o que
imediatamente divide o espaço de recursos em dois subconjuntos. Para cada um dos dois subconjuntos, pode-se então
perguntar se o volume estava acima ou abaixo de um limite, criando assim quatro subconjuntos separados.
Esse processo continua até que não haja mais poder preditivo a ser obtido pelo particionamento.
Uma árvore de decisão fornece um mecanismo de classificação naturalmente interpretável quando comparada às
abordagens mais opacas do tipo "caixa preta" do SVM ou analisadores discriminantes e, portanto, é uma técnica popular de
classificação supervisionada.
Com o aumento do poder computacional, surgiu um novo método para abordar o problema da classificação: o aprendizado
de conjunto. A ideia básica é simples: criar uma grande quantidade de classificadores a partir do mesmo modelo base e treiná-
los com parâmetros variados. Em seguida, combinar os resultados da previsão em uma média para, com sorte, obter uma
precisão de previsão maior do que a obtida por qualquer um dos componentes individuais.

Um dos métodos de conjunto mais difundidos é o de Floresta Aleatória, que utiliza múltiplos aprendizes de árvores de
decisão (geralmente dezenas de milhares ou mais) e combina as previsões. Esses conjuntos costumam ter um desempenho
extremamente bom. O Scikit-Learn vem com uma classe RandomForestClassifier (RFC) em seu módulo de conjunto .

Os dois principais parâmetros de interesse para o RFC são n_estimators, que descreve quantas árvores de decisão
criar, e n_jobs, que descreve quantos núcleos de processamento distribuir os cálculos. Discutiremos essas configurações na
seção de implementação abaixo.

11.3.5 Análise de Componentes Principais

Todas as técnicas descritas acima pertencem ao domínio da classificação supervisionada. Uma abordagem alternativa para
realizar a classificação é não supervisionar o procedimento de treinamento e, em vez disso, permitir que um algoritmo
determine "características" por conta própria. Esses métodos são conhecidos como técnicas de aprendizado não supervisionado.

Casos de uso comuns para técnicas não supervisionadas incluem reduzir o número de dimensões de um problema para
apenas aquelas consideradas importantes, descobrir tópicos entre grandes quantidades de documentos de texto ou descobrir
recursos que podem fornecer poder preditivo na análise de séries temporais.
Nesta seção, interessa-nos o conceito de redução de dimensionalidade, que visa identificar os componentes mais
importantes em um conjunto de fatores que proporcionam a maior previsibilidade. Em particular, utilizaremos uma técnica não
supervisionada conhecida como Análise de Componentes Principais (ACP) para reduzir o tamanho do espaço de características
antes do uso em nossos classificadores supervisionados.
A ideia básica de uma ACP é transformar um conjunto de variáveis possivelmente correlacionadas (como na autocorrelação
de séries temporais) em um conjunto de variáveis linearmente não correlacionadas, conhecidas como componentes principais.
Tais componentes principais são ordenados de acordo com a quantidade de variância que descrevem, de forma ortogonal.
Assim, se tivermos um espaço de características de altíssima dimensão (mais de 10 características), poderíamos reduzir o
espaço de características por meio da ACP para talvez 2 ou 3 componentes principais que fornecem quase toda a variabilidade
dos dados, resultando em um modelo de classificador supervisionado mais robusto quando usado neste conjunto de dados
reduzido.

11.3.6 Qual previsor?

Em situações financeiras quantitativas com abundância de dados de treinamento, deve-se considerar o uso de um modelo
como uma Máquina de Vetores de Suporte (SVM). No entanto, as SVMs sofrem com a falta de interpretabilidade. Este não é
o caso com Árvores de Decisão e conjuntos de Florestas Aleatórias.
Machine Translated by Google

102

Estes últimos são frequentemente usados para preservar a interpretabilidade, algo que classificadores de "caixa preta" como
o SVM não fornecem.
Em última análise, quando os dados são tão extensos (por exemplo, dados de ticks), importa muito pouco qual classificador
será usado. Nesse estágio, surgem outros fatores, como a eficiência computacional e a escalabilidade do algoritmo. A regra
geral é que a duplicação dos dados de treinamento proporcionará um aumento linear no desempenho, mas, à medida que o
tamanho dos dados se torna substancial, essa melhoria se reduz a um aumento sublinear no desempenho.

A teoria estatística e matemática subjacente aos classificadores supervisionados é bastante complexa, mas a intuição
básica sobre cada classificador é simples de entender. Observe também que cada um dos seguintes classificadores terá um
conjunto diferente de premissas sobre quando funcionarão melhor; portanto, se você encontrar um classificador com
desempenho ruim, pode ser porque o conjunto de dados utilizado viola uma das premissas usadas para gerar a teoria.

Classificador Naive Bayes

Embora não tenhamos considerado um Classificador Naive Bayes nos exemplos acima, eu queria incluir uma discussão sobre
ele para fins de completude. O Naive Bayes (especificamente o Multinomial Naive Bayes - MNB) é bom para uso quando
existe um conjunto de dados limitado. Isso ocorre porque é um classificador com alto viés. O principal pressuposto do
classificador MNB é a independência condicional. Essencialmente, isso significa que ele é incapaz de discernir interações
entre características individuais, a menos que sejam especificamente adicionadas como características extras.

Por exemplo, considere uma situação de classificação de documentos, que aparece em ambientes financeiros ao tentar
realizar uma análise de sentimento. O MNB poderia aprender que palavras individuais como "gato" e "cachorro" poderiam se
referir, respectivamente, a documentos referentes a gatos e cachorros, mas a expressão "gatos e cachorros" (gíria britânica
para chuva forte) não seria considerada meteorológica pelo classificador! A solução para isso seria tratar "gatos e cachorros"
como uma característica extra, especificamente, e então associá-la a uma categoria meteorológica.

Regressão Logística

A regressão logística oferece algumas vantagens em relação a um modelo Naive Bayes, pois há menos preocupação com a
correlação entre características e, pela natureza do modelo, há uma interpretação probabilística dos resultados. Isso é mais
adequado para um ambiente onde é necessário usar limites. Por exemplo, podemos querer estabelecer um limite de 80%
(digamos) em um resultado "para cima" ou "para baixo" para que ele seja selecionado corretamente, em vez de escolher a
categoria de maior probabilidade. Neste último caso, a previsão para "para cima" poderia ser de 51% e a previsão para "para
baixo" poderia ser de 49%. Definir a categoria como "para cima" não é uma previsão muito forte neste caso.

Árvore de decisão e florestas aleatórias

Árvores de decisão (DT) particionam um espaço em uma hierarquia de escolhas booleanas que levam a uma categorização
ou agrupamento com base nas respectivas decisões. Isso as torna altamente interpretáveis (assumindo um número "razoável"
de decisões/nós na árvore!). As DT têm muitas vantagens, incluindo a capacidade de lidar com interações entre recursos,
além de serem não paramétricas.
Elas também são úteis em casos em que não é simples (ou impossível) separar linearmente os dados em classes (o que
é uma condição necessária para máquinas de vetores de suporte). A desvantagem de usar árvores de decisão individuais é
que elas são propensas a overfitting (alta variância). Esse problema é resolvido usando uma floresta aleatória. Florestas
aleatórias são, na verdade, alguns dos "melhores" classificadores quando usadas em competições de aprendizado de
máquina, portanto, devem ser sempre consideradas.

Máquina de vetores de suporte

Máquinas de Vetores de Suporte (MVS), embora possuam um procedimento de ajuste complexo, são, na verdade,
relativamente simples de entender. As MVMs lineares essencialmente tentam particionar um espaço usando limites de
separação linear em vários grupos distintos. Para certos tipos de dados, isso pode funcionar extremamente bem e levar a
boas previsões. No entanto, muitos dados não são linearmente separáveis e, portanto, as MVMs lineares podem ter um
desempenho ruim nesse caso.
Machine Translated by Google

103

A solução é modificar o kernel utilizado pela SVM, o que permite limites de decisão não lineares. Portanto, são
modelos bastante flexíveis. No entanto, é necessário escolher o limite correto da SVM para obter os melhores
resultados. As SVMs são especialmente eficazes em problemas de classificação de texto com alta dimensionalidade.
São desvantajosas devido à sua complexidade computacional, à dificuldade de ajuste e à dificuldade de interpretação
do modelo ajustado.

11.4 Previsão do movimento do índice de ações


O S&P500 é um índice ponderado das 500 maiores empresas de capital aberto por capitalização de mercado no
mercado de ações dos EUA. É frequentemente utilizado como referência para ações. Existem muitos produtos
derivativos que permitem especulação ou hedge sobre o índice. Em particular, o Contrato Futuro do Índice S&P500 E-
Mini é um meio extremamente líquido de negociação do índice.
Nesta seção, usaremos um conjunto de classificadores para prever a direção do preço de fechamento no dia k com
base apenas nas informações de preço conhecidas no dia k - 1. Um movimento direcional ascendente significa que o
preço de fechamento em k é maior que o preço em k - 1, enquanto um movimento descendente implica um preço de
fechamento em k menor que em k - 1.
Se pudermos determinar a direção do movimento de uma maneira que exceda significativamente uma taxa de
acerto de 50%, com baixo erro e boa significância estatística, então estamos no caminho certo para formar uma
estratégia básica de negociação sistemática com base em nossas previsões.

11.4.1 Implementações Python


Para a implementação desses previsores, utilizaremos o NumPy, o Pandas e o Scikit-Learn, que foram instalados nos
capítulos anteriores.
O primeiro passo é importar os módulos e bibliotecas relevantes. Vamos importar os classificadores
LogisticRegression, LDA, QDA, LinearSVC (uma Máquina de Vetores de Suporte linear), SVC (uma Máquina de
Vetores de Suporte não linear) e RandomForest para esta previsão:

#!/usr/bin/python # -*-
codificação: utf-8 -*-

# previsã[Link]

de __future__ importar função_de_impressão

importar data e hora


importar numpy como np
importar pandas como pd
importar sklearn

de [Link] importar DataReader de


[Link] importar RandomForestClassifier de sklearn.linear_model
importar LogisticRegression de [Link] importar LDA de [Link]
importar confusion_matrix de [Link]
importar QDA de [Link] importar LinearSVC, SVC

Agora que as bibliotecas foram importadas, precisamos criar um DataFrame do Pandas que contenha os retornos
percentuais defasados de um número anterior de dias (o padrão é cinco). create_lagged_series pegará um símbolo
de ação (conforme reconhecido pelo Yahoo Finance) e criará um DataFrame defasado para o período especificado. O
código está bem comentado, então deve ser fácil entender o que está acontecendo:

def create_lagged_series(símbolo, data_de_início, data_de_término, atrasos=5):


"""

Isso cria um Pandas DataFrame que armazena o


Machine Translated by Google

104

Retornos percentuais do valor de fechamento ajustado de uma ação obtidos do


Yahoo Finanças, juntamente com uma série de retornos defasados dos dias
de negociação anteriores (o padrão é 5 dias). O volume de negociação, bem
como a direção do dia anterior, também estão incluídos.

"""

# Obter informações de ações do Yahoo Finance ts =


DataReader( símbolo,
"yahoo", start_date-
[Link](days=365), end_date

# Crie o novo DataFrame com atraso tslag =


[Link](index=[Link]) tslag["Hoje"] =
ts["Fechamento Ajustável"] tslag["Volume"] =
ts["Volume"]

# Crie a série de defasagem deslocada dos valores de fechamento do período de negociação anterior para
i em range(0, lags): tslag["Lag%s" %
str(i+1)] = ts["Adj Close"].shift(i+1)

# Crie o DataFrame de retornos tsret =


[Link](index=[Link]) tsret["Volume"] =
tslag["Volume"] tsret["Hoje"] =
tslag["Hoje"].pct_change()*100.0

# Se algum dos valores de retornos percentuais for igual a zero, defina-os como # um número pequeno
(evita problemas com o modelo QDA no Scikit-Learn) for i,x in enumerate(tsret["Today"]): if
(abs(x) < 0.0001): tsret["Today"][i] = 0.0001

# Crie as colunas de retornos percentuais defasados para i em range(0,


lags): tsret["Lag%s" % str(i+1)] = \
tslag["Lag%s" % str(i+1)].pct_change()*100.0

# Crie a coluna "Direção" (+1 ou -1) indicando um dia de alta/baixa tsret["Direção"] = [Link](tsret["Hoje"])
tsret = tsret[[Link] >= start_date]

retornar tsret

Conectamos o procedimento de classificação com uma função __main__ . Neste caso, tentaremos prever a direção do
mercado de ações dos EUA em 2005, usando dados de retorno de 2001 a 2004.

Primeiramente, criamos uma série defasada do S&P500 usando cinco defasagens. A série também inclui o volume de
negociação. No entanto, vamos restringir o conjunto de preditores para usar apenas as duas primeiras defasagens. Assim,
estamos implicitamente afirmando ao classificador que as defasagens subsequentes têm menor valor preditivo. A propósito, esse
efeito é estudado mais concretamente sob o conceito estatístico de autocorrelação, embora isso esteja além do escopo do livro.

Após criar a matriz preditiva X e o vetor de resposta y, podemos particionar as matrizes em um conjunto de treinamento e
um conjunto de teste. O primeiro subconjunto é usado para treinar o classificador, enquanto o segundo é usado para testar o
desempenho. Dividiremos o conjunto de treinamento e teste em 1º de janeiro de 2005, deixando um ano completo de dados
(aproximadamente 250 dias) para
Machine Translated by Google

105

o conjunto de testes.
Após criarmos a divisão treinamento/teste, precisamos criar uma matriz de modelos de classificação, cada um em uma tupla
com um nome abreviado anexado. Embora não tenhamos definido nenhum parâmetro para os modelos de Regressão Logística,
Analisadores Discriminantes Lineares/Quadráticos ou Classificador de Vetores de Suporte Linear, usamos um conjunto de parâmetros
padrão para a Máquina de Vetores de Suporte Radial (RSVM) e a Floresta Aleatória (RF).

Por fim, iteramos sobre os modelos. Treinamos (ajustamos) cada modelo com base nos dados de treinamento e, em seguida,
fazemos previsões com base no conjunto de teste. Por fim, geramos a taxa de acerto e a matriz de confusão para cada modelo:

if __name__ == "__main__": # Crie


uma série defasada do índice de mercado de ações dos EUA S&P500 snpret =
create_lagged_series( "^GSPC",
[Link](2001,1,10), [Link](2005,12,31),
lags=5
)

# Use os dois dias anteriores de retornos como valores preditores,


com a direção como resposta
X = snpret[["Lag1","Lag2"]] y =
snpret["Direção"]

# Os dados de teste são divididos em duas partes: antes e depois de 1º de janeiro de 2005. start_test =
[Link](2005,1,1)

# Crie conjuntos de treinamento e teste


X_train = X[[Link] < start_test]
X_teste = X[[Link] >= iniciar_teste] y_train =
y[[Link] < iniciar_teste] y_teste = y[[Link] >=
iniciar_teste]

# Crie os modelos (parametrizados) print("Taxas


de acertos/Matrizes de confusão:\n") models = [("LR",
LogisticRegression()), ("LDA", LDA()), ("QDA", QDA()),
("LSVC", LinearSVC()),
("RSVM",
SVC( C=1000000.0,
cache_size=200,
class_weight=None, coef0=0.0, degree=3, gamma=0.0001, kernel='rbf',
max_iter=-1, probability=False, random_state=None, shrinking=True,
tol=0.001, verbose=False) ), ("RF", RandomForestClassifier(

n_estimators=1000, critério='gini', max_depth=Nenhum,


min_samples_split=2, min_samples_leaf=1,
max_features='auto', bootstrap=True, oob_score=False,
n_jobs=1, random_state=Nenhum, verbose=0) )]

# Iterar pelos modelos para m em


modelos:

# Treine cada um dos modelos no conjunto de treinamento


m[1].fit(X_train, y_train)
Machine Translated by Google

106

# Crie uma matriz de previsões no conjunto de teste pred =


m[1].predict(X_test)

# Produza a taxa de acerto e a matriz de confusão para cada modelo print("%s:\n%0.3f" %


(m[0], m[1].score(X_test, y_test))) print("%s\n" % confusion_matrix(pred, y_test))

11.4.2 Resultados
A saída de todos os modelos de classificação é a seguinte. Você provavelmente verá valores diferentes na saída
RF (Floresta Aleatória), pois sua construção é inerentemente estocástica:

Taxas de acerto/matrizes de confusão:

LR:
0,560
[[ 35 35] [ 76
106]]

LDA:
0,560
[[ 35 35]
[ 76 106]]

QDA:
0,599
[[ 30 20]
[ 81 121]]

LSVC:
0,560
[[ 35 35]
[ 76 106]]

RSVM:
0,563
[[ 9 8] [102
133]]

RF:
0,504
[[48 62] [63
79]]

Observe que todas as taxas de acerto estão entre 50% e 60%. Assim, podemos ver que as variáveis
defasadas não são muito indicativas da direção futura. No entanto, se observarmos o analisador discriminante
quadrático, podemos ver que seu desempenho preditivo geral no conjunto de teste é pouco inferior a 60%.

A matriz de confusão para este modelo (e os outros em geral) também afirma que a taxa de verdadeiros
positivos para os dias de "baixa" é muito maior do que para os dias de "alta". Portanto, se quisermos criar uma
estratégia de negociação com base nessas informações, poderíamos considerar restringir as negociações a
posições vendidas no S&P 500 como um meio potencial de aumentar a lucratividade.
Nos capítulos seguintes, usaremos esses modelos como base para uma estratégia de negociação,
incorporando-os diretamente à estrutura de backtesting orientada por eventos e usando um instrumento direto,
como um fundo negociado em bolsa (ETF), para nos dar acesso à negociação do S&P500.
Machine Translated by Google

Parte V

Gestão de Desempenho e Risco

107
Machine Translated by Google
Machine Translated by Google

Capítulo 12

Medição de Desempenho

A mensuração do desempenho é um componente absolutamente crucial da negociação algorítmica. Sem a avaliação do


desempenho, juntamente com a manutenção de registros sólidos, é difícil, senão impossível, determinar se os retornos da nossa
estratégia se devem à sorte ou a alguma vantagem real sobre o mercado.

Para ter sucesso na negociação algorítmica, é necessário estar ciente de todos os fatores que podem afetar a lucratividade
das operações e, em última análise, das estratégias. Devemos buscar constantemente melhorias em todos os aspectos da
negociação algorítmica. Em particular, devemos sempre tentar minimizar nossos custos de transação (taxas, comissões e
slippage), aprimorar nosso software e hardware, aprimorar a limpeza de nossos feeds de dados e buscar continuamente novas
estratégias para adicionar a um portfólio. A mensuração de desempenho em todas essas áreas fornece um parâmetro para avaliar
alternativas.

Em última análise, a negociação algorítmica visa gerar lucros. Portanto, é fundamental que medimos o desempenho, em
vários níveis de granularidade, de como e por que nosso sistema está gerando esses lucros. Isso motiva a avaliação de
desempenho no nível de negociações, estratégias e portfólios. Em particular, buscamos:

• Se as regras sistemáticas codificadas pela estratégia realmente produzem um retorno consistente


e se a estratégia possui desempenho positivo nos backtests.

• Se uma estratégia mantém esse desempenho positivo em uma implementação ativa ou se precisa ser descontinuada.

• A capacidade de comparar múltiplas estratégias/portfólios de modo a reduzir a oportunidade


custo de comunidade associado à alocação de uma quantidade limitada de capital de negociação.

Os itens específicos da análise quantitativa do desempenho em que estaremos interessados são


do seguinte modo:

• Retornos - O aspecto mais visível de uma estratégia de negociação diz respeito ao ganho percentual desde o início, seja em
um backtest ou em um ambiente de negociação real. As duas principais medidas de desempenho aqui são o Retorno Total
e a Taxa de Crescimento Anual Composta (CAGR).

• Drawdowns - Um drawdown é um período de desempenho negativo, definido a partir de uma máxima anterior, definida como
o pico mais alto anterior na curva de patrimônio de uma estratégia ou portfólio. Definiremos isso mais concretamente
abaixo, mas você pode pensar nisso por enquanto como uma descida (um tanto dolorosa!) no seu gráfico de desempenho.

• Risco - O risco abrange muitas áreas, e dedicaremos um tempo considerável a elas no próximo capítulo, mas, em geral,
refere-se tanto ao risco de perda de capital, como em rebaixamentos, quanto à volatilidade dos retornos. Este último
geralmente é calculado como um desvio padrão anualizado dos retornos.

• Relação Risco/Recompensa - Os investidores institucionais estão principalmente interessados em retornos ajustados ao risco.
Uma vez que uma maior volatilidade pode frequentemente levar a retornos mais elevados à custa de uma maior volatilidade,

109
Machine Translated by Google

110

Em drawdowns, eles sempre se preocupam com o nível de risco assumido por unidade de retorno. Consequentemente, uma
série de medidas de desempenho foram inventadas para quantificar esse aspecto do desempenho da estratégia, como o
Índice de Sharpe, o Índice de Sortino e o Índice de CALMAR, entre outros. O Índice de Sharpe fora da amostra costuma ser
a primeira métrica a ser discutida por investidores institucionais ao discutir o desempenho da estratégia.

• Análise de Operações - As medidas de desempenho anteriores são todas aplicáveis a estratégias e portfólios. Também é
instrutivo analisar o desempenho de operações individuais, pois existem muitas medidas para caracterizá-las. Em particular,
quantificaremos o número de operações com lucro/perda, o lucro médio por operação e a relação lucro/perda, entre outros.

As negociações são o aspecto mais granular de uma estratégia algorítmica e, portanto, começaremos por
discutindo análise comercial.

12.1 Análise de Comércio


O primeiro passo na análise de qualquer estratégia é considerar o desempenho das operações reais. Essas métricas podem variar
drasticamente entre as estratégias. Um exemplo clássico seria a diferença nas métricas de desempenho de uma estratégia de
acompanhamento de tendências em comparação com uma estratégia de reversão à média.
Estratégias de acompanhamento de tendências geralmente consistem em muitas operações com prejuízo, cada uma com uma
provável pequena perda. A menor quantidade de operações lucrativas ocorre quando uma tendência é estabelecida e o desempenho
dessas operações positivas pode exceder significativamente as perdas da maior quantidade de operações com prejuízo. As
estratégias de reversão à média de pair-trading apresentam o caráter oposto. Geralmente consistem em muitas operações com
pequeno lucro. No entanto, se uma série não reverte à média da maneira esperada, a natureza longa/curta da estratégia pode levar
a perdas substanciais.
Isso poderia potencialmente eliminar a grande quantidade de pequenos ganhos.
É essencial estar ciente da natureza do perfil de negociação da estratégia e do seu próprio perfil psicológico, pois ambos
precisam estar alinhados. Caso contrário, você poderá não conseguir perseverar durante um período de forte rebaixamento.

Agora, revisaremos as estatísticas que nos interessam em nível comercial.

12.1.1 Estatísticas resumidas


Ao considerar nossas negociações, estamos interessados no seguinte conjunto de estatísticas. Aqui, "período" refere-se ao período
coberto pela barra de negociação que contém dados OHLCV. Para estratégias de longo prazo, frequentemente são utilizadas barras
diárias. Para estratégias de frequência mais alta, podemos estar interessados em barras horárias ou minuciosas.

• Lucro/Perda Total (PnL) - O PnL total indica diretamente se um determinado


o comércio foi lucrativo ou não.

• PnL médio do período - O PnL médio do período indica se uma barra, em média, gera
um lucro ou prejuízo.

• Lucro Máximo por Período - O maior lucro por período obtido por esta operação até o momento.

• Perda Máxima do Período - A maior perda por período de barra registrada por esta operação até o momento. Observe que
isso não diz nada sobre a perda máxima futura do período! Uma perda futura pode ser muito maior do que isso.

• Lucro médio do período - A média ao longo da vida útil da operação de todos os períodos lucrativos.

• Perda média do período - A média ao longo da vida útil da operação de todos os períodos não lucrativos.

• Períodos vencedores - A contagem de todos os períodos vencedores.

• Períodos de perdas - A contagem de todos os períodos de perdas.

• Porcentagem de períodos de vitórias/derrotas - A porcentagem de todos os períodos de vitórias em relação aos períodos de derrotas.
Será significativamente diferente para estratégias de acompanhamento de tendências e de reversão à média.
Machine Translated by Google

111

Felizmente, é simples gerar essas informações a partir do resultado do nosso portfólio, eliminando completamente a
necessidade de registros manuais. No entanto, isso corre o risco de nunca pararmos para analisar os dados!

É fundamental que as operações sejam avaliadas pelo menos uma ou duas vezes por mês. Isso é um sistema útil
de detecção de alertas antecipados que pode ajudar a identificar quando o desempenho da estratégia começa a se
deteriorar. Muitas vezes, é muito melhor do que simplesmente considerar apenas o PnL acumulado.

12.2 Estratégia e Análise de Portfólio


A análise em nível de operação é extremamente útil em estratégias de longo prazo, particularmente com estratégias que
empregam operações complexas, como as que envolvem derivativos. Para estratégias de frequência mais alta,
estaremos menos interessados em cada operação individual e, em vez disso, consideraremos as medidas de
desempenho da estratégia. Obviamente, para estratégias de longo prazo, estamos igualmente interessados no
desempenho geral da estratégia. Nosso principal interesse está nas três áreas principais a seguir:

• Análise de Retornos - Os retornos de uma estratégia encapsulam o conceito de rentabilidade. Em ambientes


institucionais, eles geralmente são cotados líquidos de taxas e, portanto, fornecem uma imagem real de quanto
dinheiro foi ganho com o dinheiro investido. Os retornos podem ser difíceis de calcular, especialmente com
entradas/saídas de caixa.

• Análise de Risco/Recompensa - Geralmente, a primeira consideração que os investidores externos terão em uma
estratégia é o seu Índice de Sharpe fora da amostra (que descrevemos abaixo). Esta é uma métrica padrão do
setor que tenta caracterizar quanto retorno foi obtido por unidade de risco.

• Análise de Drawdown - Em um ambiente institucional, este é provavelmente o mais importante dos três aspectos.
O perfil e a extensão dos drawdowns de uma estratégia, portfólio ou fundo constituem um componente-chave na
gestão de risco. Definiremos drawdowns a seguir.

Apesar de eu ter enfatizado seu desempenho institucional, como um trader de varejo essas ainda são métricas muito
importantes e, com uma gestão de risco adequada (veja o próximo capítulo), formarão a base de um procedimento
contínuo de avaliação de estratégia.

12.2.1 Análise de Devoluções


Os números mais citados ao discutir o desempenho de estratégias, tanto em ambientes institucionais quanto de varejo,
costumam ser o retorno total, o retorno anual e o retorno mensal. É extremamente comum ver um boletim informativo
sobre o desempenho de fundos de hedge com uma "grade" de retorno mensal. Além disso, todos vão querer saber qual
é o "retorno" da estratégia.
O retorno total é relativamente simples de calcular, pelo menos em um ambiente de varejo sem
investidores externos ou entradas/saídas de caixa. Em termos percentuais, é calculado simplesmente como:

rt = (Pf ÿ Pi)/Pi × 100 (12.1)

Onde rt é o retorno total, Pf é o valor final do portfólio em dólares e Pi é o valor inicial do portfólio. Estamos
principalmente interessados no retorno total líquido, ou seja, o valor do portfólio/fundo após a dedução de todos os
custos de negociação/negócios.
Observe que esta fórmula se aplica apenas a carteiras long-only sem alavancagem. Se desejarmos adicionar vendas
a descoberto ou alavancagem, precisamos modificar a forma como calculamos os retornos, pois, tecnicamente, estamos
negociando com uma carteira de empréstimos maior do que a utilizada aqui. Isso é conhecido como carteira de margem.

Por exemplo, considere o caso em que uma estratégia de negociação opera comprando US$ 1.000 de um ativo e,
em seguida, vendendo US$ 1.000 de outro ativo. Esta é uma carteira neutra em relação ao dólar e o valor nominal total
negociado é de US$ 2.000. Se US$ 200 foram gerados com essa estratégia, o retorno bruto sobre esse valor nominal é
de 10%. A situação se torna mais complexa quando se consideram os custos de empréstimos e as taxas de juros para
financiar a margem. A contabilização desses custos leva ao retorno total líquido, que é o valor frequentemente citado
como "retorno total".
Machine Translated by Google

112

Curva de Equidade

A curva de patrimônio líquido costuma ser uma das visualizações mais enfatizadas em um relatório de desempenho de fundos
de hedge — supondo que o fundo esteja indo bem! É um gráfico do valor da carteira do fundo ao longo do tempo.
Em essência, é usado para mostrar como a conta cresceu desde a criação do fundo. Da mesma forma, em um ambiente de
varejo, é usado para mostrar o crescimento do patrimônio da conta ao longo do tempo. Veja a Figura 12.2.1 para um gráfico
típico da curva de patrimônio:

Figura 12.1: Curva típica de patrimônio da estratégia intradiária

Qual é a vantagem de tal gráfico? Em essência, ele fornece uma "pista" da volatilidade passada da estratégia, bem como
uma indicação visual de se a estratégia sofreu períodos prolongados de estagnação ou mesmo de retração. Essencialmente,
ele fornece respostas sobre como o valor do retorno total calculado ao final do período de negociação da estratégia foi obtido.

Em uma curva de patrimônio, buscamos determinar como eventos históricos incomuns moldaram a estratégia. Por exemplo,
uma pergunta comum é se houve excesso de volatilidade na estratégia por volta de 2008. Outra pergunta pode ser sobre a
consistência dos retornos.
É preciso ter extremo cuidado com a interpretação das curvas de ações, pois, quando comercializadas, geralmente são
apresentadas como "linhas com inclinação ascendente". Informações interessantes podem ser obtidas por meio do truncamento
dessas curvas, que pode enfatizar períodos de intensa volatilidade ou rebaixamento prolongado que, de outra forma, não
pareceriam tão severos quando se considera todo o período. Portanto, uma curva de ações precisa ser considerada em
contexto com outras análises, em particular a análise de risco/retorno e a análise de rebaixamento.

12.2.2 Análise de Risco/Recompensa


Como mencionamos acima, o conceito de análise de risco-retorno é extremamente importante em um ambiente institucional.
Isso não significa que, como investidores de varejo, possamos ignorá-lo.
Você deve prestar bastante atenção às métricas de risco/recompensa da sua estratégia, pois elas terão um impacto significativo
nos seus drawdowns, alavancagem e taxa de crescimento composta geral.
Machine Translated by Google

113

Esses conceitos serão aprofundados no próximo capítulo sobre Gestão de Riscos e de Recursos. Por ora,
discutiremos os índices comuns, em particular o Índice de Sharpe, que é onipresente como medida comparativa em
finanças quantitativas. Como ele é tão valorizado na negociação quantitativa institucionalizada, entraremos em detalhes.

Índice de Sharpe

Considere a situação em que nos são apresentadas duas estratégias com retornos idênticos.
Como sabemos qual deles contém mais risco? Além disso, o que queremos dizer com "mais risco"?
Em finanças, frequentemente nos preocupamos com a volatilidade dos retornos e os períodos de retração. Portanto, se
uma dessas estratégias apresentar uma volatilidade significativamente maior, provavelmente a consideraremos menos
atraente, apesar de seus retornos históricos poderem ser semelhantes, se não idênticos. Esses problemas de
comparação de estratégias e avaliação de risco motivam o uso do Índice de Sharpe.
William Forsyth Sharpe é um economista ganhador do Prêmio Nobel, que ajudou a criar o Modelo de Precificação
de Ativos de Capital (CAPM) e desenvolveu o Índice de Sharpe em 1966 (posteriormente atualizado em 1994).
O Índice de Sharpe S é definido pela seguinte relação:

E(Ra ÿ Rb)
S= (12.2)
Var(Ra ÿ Rb)

Onde Ra é o retorno do período do ativo ou estratégia e Rb é o retorno do período de uma estratégia adequada
referência, como uma taxa de juros livre de risco.
O índice compara a média dos retornos excedentes do ativo ou estratégia com o desvio padrão desses retornos
excedentes. Assim, uma menor volatilidade dos retornos levará a um índice de Sharpe maior, assumindo retornos
médios idênticos.
O "Índice de Sharpe", frequentemente citado por quem realiza estratégias de negociação, é o Índice de Sharpe anualizado, cujo cálculo depende
do período de negociação em que os retornos são medidos. Supondo que haja N períodos de negociação em um ano, o Índice de Sharpe anualizado é
calculado da seguinte forma:

E(Ra ÿ Rb)
SA = ÿ N
Var(Ra ÿ Rb)

Observe que o próprio índice de Sharpe DEVE ser calculado com base no Sharpe daquele tipo específico de período
de tempo. Para uma estratégia baseada em período de negociação de dias, N = 252 (já que há 252 dias de negociação
em um ano, não 365), e Ra, Rb devem ser os retornos diários. Da mesma forma, para horas, N = 252 × 6,5 = 1638, não
N = 252 × 24 = 6048, visto que há apenas 6,5 horas em um dia de negociação (pelo menos para a maioria dos mercados
de ações dos EUA!).
A fórmula para o índice de Sharpe acima faz alusão ao uso de um benchmark. Um benchmark é usado como um
"parâmetro" ou um "obstáculo" que uma estratégia específica deve superar para que valha a pena ser considerada. Por
exemplo, uma estratégia simples de compra (long-only) usando ações de grande capitalização dos EUA deve ter a
expectativa de superar o índice S&P 500 em média, ou igualá-lo em termos de menor volatilidade. Caso contrário, o que
se ganha em não simplesmente investir no índice com taxas de administração/desempenho muito mais baixas?
A escolha do benchmark pode, por vezes, ser pouco clara. Por exemplo, um Fundo de Índice de Mercado Acionário
(ETF) setorial deve ser utilizado como benchmark de desempenho para ações individuais ou para o próprio S&P 500?
Por que não para o Russell 3000? Da mesma forma, uma estratégia de fundo de hedge deve ser comparada a um
índice de mercado ou a um índice de outros fundos de hedge?
Há também a complicação da "taxa livre de risco". Devem ser utilizados títulos do governo nacional? Uma cesta de
títulos internacionais? Letras de curto ou longo prazo? Uma mistura? É claro que há muitas maneiras de escolher um
benchmark. O índice de Sharpe geralmente utiliza a taxa livre de risco e, frequentemente, para estratégias de ações dos
EUA, esta se baseia em letras do Tesouro do governo de 10 anos.
Em um caso específico, para estratégias neutras em relação ao mercado, há uma complicação específica quanto à
utilização da taxa livre de risco ou zero como referência. O índice de mercado em si não deve ser utilizado, visto que a
estratégia é, por natureza, neutra em relação ao mercado. A escolha correta para uma carteira neutra em relação ao
mercado é não subtrair a taxa livre de risco, pois ela é autofinanciável.
Como você ganha um interesse de crédito, Rf , ao manter uma margem, o cálculo real dos retornos
Machine Translated by Google

114

é: (Ra + Rf ) ÿ Rf = Ra. Portanto, não há subtração real da taxa livre de risco para estratégias neutras em dólar.

Apesar da prevalência do índice de Sharpe nas finanças quantitativas, ele apresenta algumas limitações. O índice de Sharpe é
retrospectivo. Ele considera apenas a distribuição e a volatilidade dos retornos históricos, não aqueles que ocorrem no futuro. Ao fazer
julgamentos com base no índice de Sharpe, há uma suposição implícita de que o passado será semelhante ao futuro.

Evidentemente, isso nem sempre acontece, principalmente em situações de mudanças no regime de mercado.
O cálculo do rácio de Sharpe pressupõe que os retornos utilizados são distribuídos normalmente (ou seja,
Gaussiana). Infelizmente, os mercados frequentemente sofrem de curtose acima daquela de uma distribuição normal.
Essencialmente, a distribuição de retornos tem "caudas mais grossas" e, portanto, eventos extremos têm maior probabilidade de
ocorrer do que uma distribuição gaussiana nos levaria a crer. Portanto, o índice de Sharpe é ruim para caracterizar o risco de cauda.

Isso pode ser visto claramente em estratégias altamente propensas a tais riscos. Por exemplo, a venda de opções de compra,
também conhecida como "moedas sob um rolo compressor". Um fluxo constante de prêmios de opções é gerado pela venda de opções
de compra ao longo do tempo, levando a uma baixa volatilidade dos retornos, com um forte excesso de retornos acima de um
benchmark. Nesse caso, a estratégia teria um alto índice de Sharpe com base em dados históricos. No entanto, ela não leva em
consideração que tais opções podem ser chamadas, levando a quedas significativas ou até mesmo à queda da curva de ações.
Portanto, como acontece com qualquer medida de desempenho de estratégia de negociação algorítmica, o índice de Sharpe não pode
ser usado isoladamente.

Embora este ponto possa parecer óbvio para alguns, os custos de transação DEVEM ser incluídos no cálculo do índice de Sharpe
para que este seja realista. Existem inúmeros exemplos de estratégias de negociação com índices de Sharpe altos e, portanto, com
grande probabilidade de alta lucratividade, que se reduzem a estratégias com índices de Sharpe baixos e baixa lucratividade, uma vez
considerados os custos realistas. Isso significa utilizar os retornos líquidos ao calcular acima do índice de referência. Portanto, os
custos de transação devem ser considerados antes do cálculo do índice de Sharpe.

Uma pergunta óbvia que permanece sem resposta até agora é "Qual é um bom Índice de Sharpe para uma estratégia?". Esta é,
na verdade, uma pergunta bastante difícil de responder, pois cada investidor tem um perfil de risco diferente. A regra geral é que
estratégias quantitativas com Índice de Sharpe anualizado S < 1 não devem ser consideradas com frequência. No entanto, há
exceções, particularmente no mercado de futuros que acompanham tendências.

Fundos quantitativos tendem a ignorar quaisquer estratégias que possuam índices de Sharpe S < 2. Um importante fundo de
hedge quantitativo com o qual estou familiarizado nem sequer consideraria estratégias com índices de Sharpe S < 3 durante sua
pesquisa. Como um trader algorítmico de varejo, se você conseguir obter um índice de Sharpe S > 2 fora da amostra (ou seja, em
negociações ao vivo!), estará se saindo muito bem.
O índice de Sharpe frequentemente aumenta com a frequência de negociação. Algumas estratégias de alta frequência apresentam
índices de Sharpe de um dígito (e, às vezes, de dois dígitos), pois podem ser lucrativas quase todos os dias e, certamente, todos os
meses. Essas estratégias raramente sofrem de risco catastrófico (no sentido de grandes perdas) e, portanto, minimizam a volatilidade
dos retornos, o que leva a índices de Sharpe tão altos. Esteja ciente, porém, de que estratégias de alta frequência como essas podem
simplesmente parar de funcionar repentinamente, o que é outro aspecto do risco não totalmente refletido no índice de Sharpe.

Vamos agora considerar alguns exemplos reais de Sharpe. Começaremos de forma simples, considerando uma estratégia de
compra e manutenção de uma ação específica, e depois consideraremos uma estratégia neutra em relação ao mercado. Ambos os
exemplos foram realizados com Pandas.
A primeira tarefa é obter os dados e inseri-los em um objeto DataFrame do Pandas. No capítulo anterior, sobre a implementação do
Securities Master com Python e MySQL, criamos um sistema para isso. Como alternativa, podemos usar este código mais simples para obter
dados do Google Finance diretamente e inseri-los em um DataFrame. No final deste script, criei uma função para calcular o índice de Sharpe
anualizado com base em um fluxo de retornos por período:

#!/usr/bin/python #
-*- codificação: utf-8 -*-

# [Link]

de __future__ importar função_de_impressão


Machine Translated by Google

115

importar data e
hora importar numpy
como np importar
pandas como pd importar [Link] como web

def annualised_sharpe(retorna, N=252):


"""

Calcule o índice de Sharpe anualizado de um fluxo de retornos com base em um número


de períodos de negociação, N. O padrão é 252, o que então assume um fluxo de retornos
diários.

A função assume que os retornos são o excesso daqueles comparados a um


benchmark.
"""

retornar [Link](N) * [Link]() / [Link]()

Agora que temos a capacidade de obter dados do Google Finance e calcular diretamente o índice de Sharpe
anualizado, podemos testar uma estratégia de comprar e manter para duas ações.
Usaremos o Google (GOOG) de 1º de janeiro de 2000 a 1º de janeiro de 2013.
Podemos criar uma função auxiliar adicional que nos permite ver rapidamente a compra e retenção de Sharpe
em várias ações para o mesmo período (codificado):

def equity_sharpe(ticker):
"""

Calcula o índice de Sharpe anualizado com base nos retornos diários de um símbolo de
ação listado no Google Finance.

As datas foram codificadas aqui para maior brevidade.


"""

início = data e [Link] e hora(2000,1,1) fim = data e


[Link] e hora(2013,1,1)

# Obtenha os dados históricos diários de ações para o período de tempo desejado # e adicione a um
DataFrame do pandas pdf = [Link](ticker,
'google', start, end)

# Use o método de variação percentual para calcular facilmente os retornos diários pdf['daily_ret'] =
pdf['Close'].pct_change()

# Assuma uma taxa livre de risco anual média durante o período de 5% pdf['excess_daily_ret'] =
pdf['daily_ret'] - 0,05/252

# Retorna o índice de Sharpe anualizado com base nos retornos diários excedentes return
annualised_sharpe(pdf['excess_daily_ret'])

Para o Google, o índice de Sharpe para compra e retenção é de 0,703:

>>> equity_sharpe('GOOG')
0,70265563285799615

Agora podemos tentar o mesmo cálculo para uma estratégia neutra em relação ao mercado. O objetivo dessa
estratégia é isolar completamente o desempenho de uma ação específica do mercado em geral. A maneira mais
simples de conseguir isso é operar vendido em igual montante (em dólares) em um Fundo Negociado em Bolsa
(ETF) projetado para acompanhar esse mercado. A escolha mais óbvia para o mercado de ações de grande
capitalização dos EUA é o índice S&P 500, que é monitorado pelo ETF SPDR, com o ticker SPY.
Para calcular o índice de Sharpe anualizado de tal estratégia, obteremos os preços históricos do SPY e
calcularemos os retornos percentuais de maneira semelhante às ações anteriores, com
Machine Translated by Google

116

A exceção é que não usaremos o benchmark livre de risco. Calcularemos os retornos líquidos diários, o que
requer a subtração da diferença entre os retornos longos e curtos e a divisão por 2, já que agora temos o dobro
do capital de negociação. Aqui está o código Python/Pandas para realizar isso:

def market_neutral_sharpe(ticker, benchmark):


"""

Calcula o índice de Sharpe anualizado de uma estratégia longa/curta neutra


de mercado, envolvendo a posição longa do 'ticker' com uma posição curta correspondente
do 'benchmark'.
"""

início = data e [Link] e hora(2000, 1, 1) fim = data e


[Link] e hora(2013, 1, 1)

# Obtenha dados históricos para um símbolo/ticker e um ticker de referência # As datas foram codificadas,
mas você pode modificá-las como achar melhor! tick = [Link](ticker, 'google', start, end) bench =
[Link](benchmark, 'google', start, end)

# Calcular os retornos percentuais em cada uma das séries temporais tick['daily_ret'] =


tick['Close'].pct_change() bench['daily_ret'] = bench['Close'].pct_change()

# Crie um novo DataFrame para armazenar as informações da estratégia # Os retornos


líquidos são (longo - curto)/2, já que há o dobro # do capital de negociação para esta estratégia
strat = [Link](index=[Link]) strat['net_ret'] =
(tick['daily_ret'] - bench['daily_ret'])/2.0

# Retorna o índice de Sharpe anualizado para esta estratégia return


annualised_sharpe(strat['net_ret'])

Para o Google, o índice de Sharpe para a estratégia neutra de mercado longa/curta é de 0,832:

>>> market_neutral_sharpe('GOOG', 'SPY') 0,83197496084314604

Agora, consideraremos brevemente outras relações risco/recompensa.

Razão de Sortino

O índice de Sortino é motivado pelo fato de que o índice de Sharpe captura tanto a volatilidade para cima
quanto para baixo em seu denominador. No entanto, investidores (e gestores de fundos de hedge) geralmente
não se incomodam muito quando temos uma volatilidade para cima significativa! O que realmente interessa, de
uma perspectiva de gestão de risco, é a volatilidade para baixo e os períodos de drawdown.
Assim, o índice de Sortino é definido como o excesso médio de retorno dividido pela desvantagem média
desvio:

E(Ra ÿ Rb)
Sortino = (12.3)
Var(Ra ÿ Rb)d

O Sortino às vezes é citado em um ambiente institucional, mas certamente não é tão prevalente quanto o
índice de Sharpe.

Razão CALMAR

Pode-se também argumentar que os investidores/traders estão preocupados apenas com a extensão máxima
do drawdown, e não com o drawdown médio. Isso motiva o CALMAR (California)
Machine Translated by Google

117

Índice de Relatórios de Contas Gerenciadas), também conhecido como índice de Drawdown, que fornece uma relação entre o retorno médio excedente e
o drawdown máximo:

E(Ra ÿ Rb)
CALMAR = (12.4)
redução máxima

Mais uma vez, o CALMAR não é tão amplamente utilizado quanto o índice de Sharpe.

12.2.3 Análise de Rebaixamento


Na minha opinião, o conceito de drawdown é o aspecto mais importante da mensuração de desempenho de um sistema de
negociação algorítmica. Simplificando, se o patrimônio da sua conta for eliminado, nenhuma das outras métricas de desempenho
importa! A análise de drawdown se refere à mensuração da queda no patrimônio da conta em relação a pontos altos anteriores.
Um ponto alto é definido como o último pico de patrimônio da conta atingido na curva de patrimônio.

Em um ambiente institucional, o conceito de drawdown é especialmente importante, pois a maioria dos fundos de hedge é
remunerada apenas quando o patrimônio líquido da conta está continuamente atingindo novos patamares. Ou seja, o gestor do
fundo não recebe taxa de performance enquanto o fundo permanece "submerso", ou seja, o patrimônio líquido da conta está em
um período de drawdown.
A maioria dos investidores ficaria preocupada com uma perda de 10% em um fundo e provavelmente resgataria seu
investimento quando a perda ultrapassasse 30%. No varejo, a situação é bem diferente.
Os indivíduos provavelmente sofrerão reduções maiores na esperança de obter retornos maiores.

Drawdown e duração máximos

As duas principais métricas de drawdown são o drawdown máximo e a duração do drawdown. A primeira descreve a maior
queda percentual de um pico anterior para a mínima atual ou anterior no patrimônio líquido da conta. É frequentemente citada
em um ambiente institucional ao tentar comercializar um fundo.
Os traders de varejo também devem prestar bastante atenção a este número. O segundo descreve a duração real do drawdown.
Este número geralmente é cotado em dias, mas estratégias de frequência mais alta podem usar um período de tempo mais
granular.
Em backtests, essas medidas fornecem uma ideia de como uma estratégia pode se comportar no futuro.
A curva de patrimônio líquido geral da conta pode parecer bastante atraente após um backtest calculado. No entanto, uma curva
de patrimônio líquido ascendente pode facilmente mascarar o quão difíceis podem ter sido os períodos anteriores de retração.

Quando uma estratégia começa a cair abaixo de 10% do patrimônio líquido da conta, ou mesmo abaixo de 20%, é
necessária uma força de vontade significativa para continuar com a estratégia, apesar de historicamente, pelo menos nos
backtests, a estratégia ter passado por períodos semelhantes. Este é um problema recorrente na negociação algorítmica e na
negociação sistemática em geral. Naturalmente, isso motiva a necessidade de definir limites de drawdown prévios e regras
específicas, como um "stop loss" para toda a conta, que será aplicado caso um drawdown ultrapasse esses níveis.

Curva de Rebaixamento

Embora seja importante estar ciente do rebaixamento máximo e da duração do rebaixamento, é significativamente mais instrutivo
ver um gráfico de série temporal do rebaixamento da estratégia ao longo da duração da negociação.

A Figura 12.2.3 mostra claramente que esta estratégia em particular sofreu um período relativamente prolongado de
rebaixamento, começando no terceiro trimestre de 2010 e terminando no segundo trimestre de 2011, atingindo um rebaixamento
máximo de 14,8%. Embora a estratégia em si tenha continuado a ser significativamente lucrativa a longo prazo, este período
específico teria sido muito difícil de suportar. Além disso, este é o rebaixamento histórico máximo já ocorrido. A estratégia pode
estar sujeita a um rebaixamento ainda maior no futuro. Portanto, é necessário considerar as curvas de rebaixamento, assim
como outras medidas de desempenho com análise histórica, no contexto em que foram geradas, ou seja, por meio de dados
históricos, e não futuros.
Machine Translated by Google

118

Figura 12.2: Curva de rebaixamento típica da estratégia intradiária

No capítulo a seguir, consideraremos o conceito de gestão quantitativa de risco e descreveremos técnicas


que podem nos ajudar a minimizar perdas e maximizar retornos, mantendo sempre um grau razoável de risco.
Machine Translated by Google

Capítulo 13

Gestão de Riscos e Dinheiro


Este capítulo aborda a gestão de risco aplicada a estratégias de negociação quantitativa. Isso geralmente se apresenta de duas
maneiras: primeiro, identificando e mitigando fatores internos e externos que podem afetar o desempenho ou a operação de uma
estratégia de negociação algorítmica e, segundo, como gerenciar de forma otimizada o portfólio da estratégia para maximizar a
taxa de crescimento e minimizar perdas de conta.

Na primeira seção, consideraremos diferentes fontes de risco (tanto intrínsecas quanto extrínsecas) que podem afetar o
desempenho de longo prazo de um negócio de negociação algorítmica, seja de varejo ou institucional.

Na segunda seção, veremos técnicas de gestão de dinheiro que podem simultaneamente proteger nosso portfólio da ruína e
também tentar maximizar a taxa de crescimento do patrimônio a longo prazo.
Na seção final, consideramos técnicas de gestão de risco em nível institucional que podem facilmente
ser aplicado em um ambiente de varejo para ajudar a proteger o capital comercial.

13.1 Fontes de Risco


Existem inúmeras fontes de risco que podem impactar o funcionamento correto de uma estratégia de negociação algorítmica.
"Risco" é geralmente definido neste contexto como a possibilidade de perdas na conta. No entanto, vou defini-lo em um contexto
muito mais amplo, significando qualquer fator que ofereça um grau de incerteza e possa afetar o desempenho de nossas
estratégias ou portfólio.
As amplas áreas de risco que consideraremos incluem Risco de Estratégia, Risco de Portfólio,
Risco de Mercado, Risco de Contraparte e Risco Operacional.

13.1.1 Risco de Estratégia


O risco de estratégia, ou risco de modelo, abrange a classe de riscos que surgem da concepção e implementação de uma
estratégia de negociação baseada em um modelo estatístico. Inclui todos os problemas anteriores que discutimos no capítulo
"Backtesting bem-sucedido", como ajuste de curva, viés de sobrevivência e viés de previsão. Inclui também outros tópicos
diretamente relacionados à análise estatística do modelo de estratégia.

Qualquer modelo estatístico é baseado em suposições. Essas suposições às vezes não são consideradas com a devida
profundidade ou são completamente ignoradas. Isso significa que o modelo estatístico baseado nessas suposições pode ser
inadequado e, portanto, levar a uma capacidade preditiva ou inferencial deficiente. Um exemplo geral ocorre no contexto da
regressão linear. A regressão linear pressupõe que os dados de resposta são homoscedásticos (ou seja, as respostas têm uma
variância constante em seus erros). Se esse não for o caso, a regressão linear fornece menos precisão nas estimativas dos
parâmetros.

Muitas estratégias quantitativas utilizam estatísticas descritivas de dados históricos de preços. Em particular, elas
frequentemente utilizam momentos dos dados, como a média, a variância, a assimetria e a curtose dos retornos da estratégia.
Tais modelos (incluindo o Critério de Kelly descrito abaixo) geralmente se baseiam na constância desses momentos ao longo do
tempo. Sob uma mudança no regime de mercado, esses momentos podem ser

119
Machine Translated by Google

120

drasticamente alterados e, portanto, levar à degradação do modelo. Modelos com "parâmetros móveis" são geralmente
utilizados para mitigar esse problema.

13.1.2 Risco de Portfólio


Um Portfólio contém uma ou mais estratégias. Portanto, está indiretamente sujeito ao Risco de Estratégia, conforme
descrito acima. Além disso, existem riscos específicos que ocorrem no nível do portfólio. Estes geralmente são
considerados apenas em um ambiente institucional ou em um ambiente de varejo de alto padrão, onde o
acompanhamento do portfólio é realizado com base em um conjunto de estratégias de negociação.
Ao regredir os retornos de uma carteira para um conjunto de fatores, como setores da indústria, classes de ativos
ou grupos de entidades financeiras, é possível verificar se a carteira está fortemente "carregada" em um fator específico.
Por exemplo, uma carteira de ações pode ser extremamente pesada em ações de tecnologia e, portanto, extremamente
exposta a quaisquer problemas que afetem o setor de tecnologia como um todo. Portanto, muitas vezes é necessário –
no nível da carteira – substituir estratégias específicas para contabilizar o risco de sobrecarga de fatores. Essa é
frequentemente uma preocupação mais significativa em um ambiente institucional, onde há mais capital a ser alocado
e a preservação do capital tem precedência sobre a taxa de crescimento de longo prazo do capital. No entanto,
certamente deve ser considerado mesmo como um investidor de varejo em negociação algorítmica.

Outra questão, em grande parte institucional (a menos que se negocie ativos mais ilíquidos), são os limites ao
volume diário de negociação. Para traders de varejo, que executam estratégias nos mercados futuros de large-caps
ou commodities, não há preocupação real quanto ao impacto no mercado. No entanto, em instrumentos menos líquidos,
é preciso ter cuidado para não negociar uma porcentagem significativa do volume diário negociado, devido ao potencial
impacto no mercado e, portanto, à invalidação de um modelo de negociação previamente testado (que muitas vezes
não leva em consideração o impacto no mercado). Para evitar isso, é necessário calcular o volume médio diário (usando
uma média ao longo de um período de loopback, por exemplo) e permanecer dentro de pequenos limites percentuais
desse valor.
A gestão de um portfólio de estratégias levanta a questão da correlação entre estratégias. As correlações podem
ser estimadas por meio de técnicas estatísticas, como o Coeficiente de Correlação Produto-Momento de Pearson. No
entanto, a correlação em si não é uma entidade estática e também está sujeita a mudanças rápidas, especialmente
sob restrições de liquidez em todo o mercado, frequentemente conhecidas como contágio financeiro. Em geral, as
estratégias devem ser projetadas para evitar correlações entre si em virtude de diferentes classes de ativos ou
horizontes temporais. Correlações contínuas podem ser estimadas ao longo de um longo período de tempo e devem
ser parte padrão do seu backtest, se estiver considerando uma abordagem de portfólio.

13.1.3 Risco de contraparte


O risco de contraparte é geralmente considerado uma forma de risco de crédito. É o risco de uma contraparte não
pagar uma obrigação sobre um ativo financeiro pelo qual é responsável. Existe todo um subconjunto de finanças
quantitativas relacionado à precificação de instrumentos de hedge de risco de contraparte, mas isso não é de nosso
interesse principal como traders algorítmicos de varejo. Estamos mais preocupados com o risco de inadimplência de
fornecedores, como uma bolsa de valores ou corretora.
Embora isso possa parecer acadêmico, posso garantir pessoalmente que essas questões são bastante reais! Em
um ambiente institucional, vivenciei pessoalmente a falência de uma corretora em condições que fizeram com que nem
todo o capital de negociação fosse devolvido. Portanto, agora considero esses riscos em uma carteira. A maneira
sugerida para mitigar esse problema é utilizar várias corretoras, embora, ao negociar com margem, isso possa tornar a
logística de negociação um tanto complicada.
O risco de contraparte é geralmente mais preocupante num ambiente institucional, por isso não vou insistir
muito nisso aqui!

13.1.4 Risco Operacional


O risco operacional abrange fontes de risco de dentro de um fundo ou infraestrutura operacional de negociação,
incluindo risco empresarial/de negócios, risco de TI e mudanças regulatórias ou legais externas.
Esses tópicos não costumam ser discutidos com muita profundidade, o que acredito ser um tanto míope, pois têm o
potencial de interromper completamente uma operação comercial de forma permanente.
Machine Translated by Google

121

O risco de infraestrutura é frequentemente associado a sistemas de tecnologia da informação e outras infraestruturas


comerciais relacionadas. Isso também inclui riscos para funcionários (como fraudes e desligamentos repentinos). À
medida que a escala de uma infraestrutura aumenta, também aumenta a probabilidade de um "ponto único de falha" (SPOF).
Este é um componente crítico na infraestrutura de negociação que, em caso de mau funcionamento, pode levar a uma
paralisação catastrófica de toda a operação. No contexto de TI, isso geralmente é consequência de uma arquitetura mal
planejada. Em um contexto não relacionado à TI, isso pode ser consequência de um organograma mal projetado.

Essas questões ainda são totalmente relevantes para o trader de varejo. Muitas vezes, uma infraestrutura de TI/
negociação pode acabar sendo "irregular" e "complicada". Além disso, a manutenção inadequada de registros e outras
falhas administrativas podem levar a enormes encargos tributários. Felizmente, a arquitetura "em nuvem" oferece
redundância em sistemas, e a automação de processos pode levar a hábitos administrativos sólidos. Esse tipo de
comportamento, ou seja, a consideração de riscos de outras fontes além do mercado e da estratégia, pode frequentemente
fazer a diferença entre um trader algorítmico bem-sucedido a longo prazo e aquele que desiste devido a um colapso
operacional catastrófico.
Uma questão que afeta o mundo dos fundos de hedge é a de relatórios e conformidade. A legislação pós-2008 impôs
um pesado ônus às empresas de gestão de ativos, o que pode ter um grande impacto em seu fluxo de caixa e despesas
operacionais. Para quem pensa em constituir uma empresa desse tipo, a fim de expandir uma estratégia ou operar com
fundos externos, é prudente manter-se atualizado sobre a legislação e o ambiente regulatório, visto que se trata de um
"alvo em movimento".

13.2 Gestão de dinheiro


Esta seção aborda um dos conceitos mais fundamentais em negociações – tanto discricionárias quanto algorítmicas – a
saber, a gestão financeira. Um investidor/trader ingênuo pode acreditar que o único objetivo importante de investimento
é simplesmente ganhar o máximo de dinheiro possível. No entanto, a realidade das negociações de longo prazo é mais
complexa. Como os participantes do mercado têm diferentes preferências e restrições de risco, os investidores podem
ter muitos objetivos.
Muitos traders de varejo consideram que o único objetivo é o aumento contínuo do patrimônio líquido da conta, com
pouca ou nenhuma consideração ao "risco" de uma estratégia que alcance esse crescimento. Investidores de varejo
mais sofisticados medem as perdas de capital da conta e, dependendo de suas preferências de risco, podem ser capazes
de lidar com uma queda substancial no patrimônio líquido da conta (digamos, 50%). A razão pela qual conseguem lidar
com uma perda de capital dessa magnitude é que percebem, quantitativamente, que esse comportamento pode ser
ótimo para a taxa de crescimento de longo prazo da carteira, por meio do uso de alavancagem.
Um investidor institucional provavelmente considerará o risco sob uma perspectiva diferente. Muitas vezes,
investidores institucionais impõem drawdowns máximos (digamos, 20%), com consideração significativa à alocação
setorial e aos limites de volume médio diário. Esses seriam restrições adicionais ao "problema de otimização" da alocação
de capital para estratégias. Esses fatores podem até ser mais importantes do que maximizar a taxa de crescimento de
longo prazo da carteira.
Portanto, estamos em uma situação em que podemos encontrar um equilíbrio entre maximizar a taxa de crescimento
a longo prazo por meio da alavancagem e minimizar nosso "risco" tentando limitar a duração e a extensão do drawdown.
A principal ferramenta que nos ajudará a alcançar isso é o Critério de Kelly.

13.2.1 Critério de Kelly


Nesta seção, o Critério de Kelly será nossa ferramenta para controlar a alavancagem e a alocação de um conjunto de
estratégias de negociação algorítmica que compõem um portfólio multiestratégia.
Definiremos alavancagem como a razão entre o tamanho de uma carteira e o patrimônio líquido real dessa carteira.
Para deixar isso claro, podemos usar a analogia da compra de uma casa com hipoteca. Sua entrada (ou "depósito" para
nós no Reino Unido!) constitui o patrimônio líquido da sua conta, enquanto a entrada mais o valor da hipoteca constituem
o equivalente ao tamanho de uma carteira. Assim, uma entrada de 50.000 dólares para uma casa de 200.000 dólares
(com uma hipoteca de 150.000 dólares) constitui uma alavancagem de (150.000 + 50.000) / 50.000 = 4. Portanto, neste
caso, você estaria 4x alavancado na casa. Uma carteira de conta com margem se comporta de forma semelhante. Há
um componente de "dinheiro" e, então, mais ações podem ser emprestadas com margem, para fornecer a alavancagem.

Antes de declararmos o Critério de Kelly especificamente, quero delinear as premissas que influenciam sua
derivação, as quais têm vários graus de precisão:
Machine Translated by Google

122

• Assumiremos que cada estratégia de negociação algorítmica possui um fluxo de retornos normalmente distribuído (ou
seja, gaussiano). Além disso, cada estratégia tem sua própria média fixa e desvio padrão de retornos. A fórmula
pressupõe que esses valores de média e desvio padrão não mudam, ou seja, que são os mesmos no passado e no
futuro. Este claramente não é o caso da maioria das estratégias, portanto, esteja ciente dessa suposição.

• Os retornos considerados aqui são retornos excedentes, o que significa que são líquidos de todos os custos de
financiamento, como juros pagos sobre margem e custos de transação. Se a estratégia for executada em um
ambiente institucional, isso também significa que os retornos são líquidos de taxas de administração e de
desempenho.

• Todos os lucros da negociação são reinvestidos e não são realizados saques de capital. Isso claramente não se aplica
em um ambiente institucional, onde as taxas de administração mencionadas acima são descontadas e os investidores
frequentemente fazem saques.

• Todas as estratégias são estatisticamente independentes (não há correlação entre as estratégias)


e, portanto, a matriz de covariância entre os retornos da estratégia é diagonal.

Agora chegamos ao Critério de Kelly! Imaginemos que temos um conjunto de N estratégias de negociação algorítmica
e queremos determinar como aplicar a alavancagem ideal para cada estratégia, a fim de maximizar a taxa de crescimento
(mas minimizar as perdas), e como alocar capital entre cada estratégia. Se denotarmos a alocação entre cada estratégia
i como um vetor f de comprimento N, st f = (f1, ..., fN ), então o Critério de Kelly para alocação ideal para cada estratégia
fi é dado por:

fi = µi/ÿ2 eu (13.1)

Onde µi são os retornos excedentes médios e ÿi são os desvios-padrão dos retornos excedentes para uma estratégia
i. Esta fórmula descreve essencialmente a alavancagem ideal que deve ser aplicada a cada estratégia.

Embora o Critério de Kelly fi nos forneça a alavancagem ideal e a alocação estratégica, ainda precisamos calcular a
taxa de crescimento composta esperada de longo prazo da carteira, que denotamos por g. A fórmula para isso é dada
por:

2
g=r+S /2 (13.2)

Onde r é a taxa de juros livre de risco, que é a taxa pela qual você pode tomar emprestado da corretora, e S é o
Índice de Sharpe anualizado da estratégia. Este último é calculado pela média anualizada dos retornos excedentes
divididos pelos desvios-padrão anualizados dos retornos excedentes.
Veja o capítulo anterior sobre Medição de Desempenho para obter detalhes sobre o Índice de Sharpe.

Um exemplo realista

Consideremos um exemplo no caso de estratégia única (i = 1). Suponhamos que operemos uma ação mítica XYZ com
retorno médio anual de m = 10,7% e desvio padrão anual de ÿ = 12,4%. Além disso, suponhamos que possamos tomar
um empréstimo a uma taxa de juros livre de risco de r = 3,0%.
Isso implica que os retornos excedentes médios são µ = m ÿ r = 10,7 ÿ 3,0 = 7,7%. Isso nos dá um Índice de Sharpe de
S = 0,077/0,124 = 0,62.
Com isso podemos calcular a alavancagem ótima de Kelly via f = µ/ÿ2 = 0,077/0,1242 = 5,01.
Assim, a alavancagem de Kelly indica que, para uma carteira de 100.000 USD, deveríamos tomar emprestados mais
401.000 USD para atingir um valor total de carteira de 501.000 USD. Na prática, é improvável que nossa corretora nos
permita negociar com uma margem tão substancial e, portanto, o Critério de Kelly precisaria ser ajustado.

Podemos então usar o índice de Sharpe S e a taxa de juros r para calcular g, a taxa de crescimento composta
esperada a longo prazo. g = r + S 2/2 = 0,03 + 0,622/2 = 0,22, ou seja, 22%. Portanto, devemos esperar um retorno de
22% ao ano com essa estratégia.
Machine Translated by Google

123

Critério de Kelly na prática

É importante estar ciente de que o Critério de Kelly exige um rebalanceamento contínuo da alocação de capital para
permanecer válido. Claramente, isso não é possível no cenário discreto de negociação real e, portanto, uma aproximação
deve ser feita. A "regra geral" padrão aqui é atualizar a alocação de Kelly uma vez por dia. Além disso, o próprio Critério
de Kelly deve ser recalculado periodicamente, usando uma média móvel e um desvio padrão com uma janela de
lookback. Novamente, para uma estratégia que negocia aproximadamente uma vez por dia, esse lookback deve ser
definido como algo da ordem de 3 a 6 meses de retornos diários.

Aqui está um exemplo de rebalanceamento de uma carteira sob o Critério de Kelly, que pode levar a um
comportamento contraintuitivo. Suponhamos que temos a estratégia descrita acima. Usamos o Critério de Kelly para
tomar dinheiro emprestado e dimensionar nossa carteira para US$ 501.000. Suponhamos que tenhamos um retorno
saudável de 5% no dia seguinte, o que aumenta o tamanho da nossa conta para US$ 526.050.
O Critério de Kelly nos diz que devemos tomar mais empréstimos para manter o mesmo fator de alavancagem de 5,01.
Em particular, o patrimônio líquido da nossa conta é de US$ 126.050 em uma carteira de US$ 526.050, o que significa
que o fator de alavancagem atual é de 4,17. Para aumentá-lo para 5,01, precisamos tomar emprestado US$ 105.460
adicionais para aumentar o tamanho da nossa conta para US$ 631.510,5 (ou seja, 5,01 × 126050).
Agora, considere que no dia seguinte perdemos 10% em nossa carteira (ai!). Isso significa que o tamanho total da
carteira agora é de 568.359,45 USD (631510,5 × 0,9). Nosso patrimônio líquido total agora é de 62.898,95 USD (126050
ÿ 631510,45 × 0,1). Isso significa que nosso fator de alavancagem atual é 568359,45/62898,95 = 9,03. Portanto,
precisamos reduzir nossa conta vendendo 253.235,71 USD em ações para reduzir o valor total da nossa carteira para
315.123,73 USD, de modo que tenhamos uma alavancagem de 5,01 novamente (315123,73/62898,95 = 5,01).

Portanto, compramos com lucro e vendemos com prejuízo. Esse processo de vender com prejuízo pode ser
extremamente difícil emocionalmente, mas é matematicamente a coisa "correta" a se fazer, supondo que as premissas
de Kelly tenham sido atendidas! É a abordagem a ser seguida para maximizar a taxa de crescimento composta de longo
prazo.
Você deve ter notado que os valores absolutos do dinheiro realocado entre os dias foram bastante severos. Isso é
consequência tanto da natureza artificial do exemplo quanto da ampla alavancagem empregada. Perdas de 10% em um
dia não são particularmente comuns em negociações algorítmicas de alta frequência, mas servem para mostrar o quão
ampla a alavancagem pode ser em termos absolutos.
Como a estimativa de médias e desvios-padrão está sempre sujeita a incertezas, na prática, muitos traders tendem
a usar um regime de alavancagem mais conservador, como o Critério de Kelly dividido por dois, carinhosamente
conhecido como "meio-Kelly". O Critério de Kelly deve ser considerado um limite superior de alavancagem a ser usado,
em vez de uma especificação direta. Se essa recomendação não for seguida, o uso do valor de Kelly direto pode levar
à ruína (ou seja, o patrimônio líquido da conta desaparecendo a zero) devido à natureza não gaussiana dos retornos da
estratégia.

Você deve usar o critério de Kelly?

Cada trader algorítmico é diferente, e o mesmo se aplica às preferências de risco. Ao optar por empregar uma estratégia
de alavancagem (da qual o Critério de Kelly é um exemplo), você deve considerar os mandatos de risco sob os quais
precisa trabalhar. Em um ambiente de varejo, você pode definir seus próprios limites máximos de drawdown e, assim,
sua alavancagem pode ser aumentada. Em um ambiente institucional, você precisará considerar o risco de uma
perspectiva muito diferente, e o fator de alavancagem será um componente de uma estrutura muito maior, geralmente
sob muitas outras restrições.

13.3 Gestão de Riscos


13.3.1 Valor em Risco
Estimar o risco de perda de uma estratégia de negociação algorítmica, ou portfólio de estratégias, é de extrema
importância para o crescimento de capital a longo prazo. Muitas técnicas de gestão de risco foram desenvolvidas para
uso em ambientes institucionais. Uma técnica em particular, conhecida como Valor em Risco ou VaR, será o tema desta
seção.
Machine Translated by Google

124

Aplicaremos o conceito de VaR a uma única estratégia ou a um conjunto de estratégias para


Ajude-nos a quantificar o risco em nossa carteira de negociação. A definição de VaR é a seguinte:
O VaR fornece uma estimativa, sob um determinado grau de confiança, do tamanho de uma perda de um portfólio ao
longo de um determinado período de tempo.
Neste caso, "portfólio" pode se referir a uma única estratégia, a um grupo de estratégias, a um livro de um trader, a
uma mesa de operações, a um fundo de hedge ou a um banco de investimento inteiro. O "grau de confiança dado" será um
valor de, digamos, 95% ou 99%. O "período de tempo dado" será escolhido para refletir um que levaria a um impacto
mínimo no mercado caso um portfólio fosse liquidado.
Por exemplo, um VaR igual a 500.000 USD com um nível de confiança de 95% para o período de um dia indicaria
simplesmente que há uma probabilidade de 95% de perder no máximo 500.000 USD no dia seguinte. Matematicamente,
isso seria expresso como:

P(L ÿ ÿ5,0 × 105 ) = 0,05 (13.3)

Ou, mais genericamente, para perda L excedendo um valor V aR com um nível de confiança c, temos:

P(L ÿ ÿV aR) = 1 ÿ c (13.4)

O cálculo "padrão" do VaR faz as seguintes suposições:

• Condições Padrão de Mercado - O VaR não deve considerar eventos extremos ou "risco de cauda", mas sim fornecer
a expectativa de uma perda em operações normais do "dia a dia".

• Volatilidades e Correlações - O VaR requer as volatilidades dos ativos em consideração, bem como suas respectivas
correlações. Essas duas grandezas são difíceis de estimar e estão sujeitas a mudanças constantes.

• Normalidade dos Retornos - O VaR, em sua forma padrão, pressupõe que os retornos do ativo ou portfólio sejam
distribuídos normalmente. Isso leva a um cálculo analítico mais direto, mas é bastante irrealista para a maioria dos
ativos.

13.4 Vantagens e Desvantagens


O VaR é difundido no setor financeiro, portanto, você deve estar familiarizado com as vantagens e desvantagens da técnica.
Algumas das vantagens do VaR são as seguintes:

• O VaR é muito simples de calcular para ativos individuais, estratégias algorítmicas, portfólios quantitativos, fundos
de hedge ou até mesmo mesas de apoio de bancos.

• O período de tempo associado ao VaR pode ser modificado para múltiplas estratégias de negociação
que têm horizontes temporais diferentes.

• Diferentes valores de VaR podem ser associados a diferentes formas de risco, por exemplo, desagregados por classe
de ativos ou tipo de instrumento. Isso facilita a interpretação de onde a maior parte do risco da carteira pode estar
concentrada, por exemplo.

• As estratégias individuais podem ser limitadas, assim como carteiras inteiras com base em suas características individuais.
VaR.

• O VaR é fácil de interpretar por investidores externos (potencialmente) não técnicos e


gestores de fundos.

No entanto, o VaR não está isento de desvantagens:

• O VaR não discute a magnitude da perda esperada além do valor do VaR, ou seja, ele nos dirá que provavelmente veremos
uma perda excedendo um valor, mas não o quanto ela o excede.
Machine Translated by Google

125

• Não leva em consideração eventos extremos, mas apenas condições típicas de mercado.

• Como utiliza dados históricos (é retrospectivo), não levará em consideração dados futuros
mudanças no regime de mercado que podem alterar volatilidades e correlações de ativos.

O VaR não deve ser usado isoladamente. Deve sempre ser usado em conjunto com um conjunto de estratégias de gestão de risco.
técnicas, como diversificação, alocação ideal de portfólio e uso prudente de alavancagem.

Métodos de Cálculo

Até o momento, não discutimos o cálculo real do VaR, nem no caso geral nem em um exemplo concreto de negociação. Há três
técnicas que nos interessarão. A primeira é o método de variância-covariância (que utiliza premissas de normalidade), a segunda
é um método de Monte Carlo (baseado em uma distribuição subjacente, potencialmente não normal) e a terceira é conhecida
como bootstrapping histórico, que utiliza informações históricas de retorno dos ativos em consideração.

Nesta seção, vamos nos concentrar no Método de Variância-Covariância.

Método de variância-covariância

Considere uma carteira de P dólares, com um nível de confiança c. Estamos considerando retornos diários, com desvio
padrão histórico do ativo (ou estratégia) ÿ e média µ. Então, o VaR diário, pelo método de variância-covariância para um
único ativo (ou estratégia), é calculado como:

P ÿ (P(ÿ(1 ÿ c) + 1)) (13.5)

Onde ÿ é o inverso da função de distribuição cumulativa de uma distribuição normal com média µ e desvio padrão ÿ.

Podemos usar as bibliotecas SciPy e Pandas para calcular esses valores. Se definirmos P = 106 e c = 0,99, podemos
usar o método ppf do SciPy para gerar os valores da função de distribuição cumulativa inversa para uma distribuição normal
com µ e ÿ obtidos a partir de alguns dados financeiros reais, neste caso, os retornos diários históricos do CitiGroup
(poderíamos facilmente substituir os retornos de uma estratégia algorítmica aqui):

#!/usr/bin/python #
-*- codificação: utf-8 -*-

# [Link]

de __future__ importar função_de_impressão

importar data e hora

importar numpy como


np importar [Link] como
web de [Link] importar norm

def var_cov_var(P, c, mu, sigma):


"""
Cálculo de variância-covariância do valor em risco diário usando nível de confiança
c, com média dos retornos mu e desvio padrão dos retornos sigma, em um
portfólio de valor P.

"""
alfa = [Link](1-c, mu, sigma)
retornar P - P*(alfa + 1)
Machine Translated by Google

126

se __nome__ == "__principal__": início


= data e [Link] e hora(2010, 1, 1) fim = data e [Link]
e hora(2014, 1, 1)

citi = [Link]("C", 'yahoo', início, fim) citi["rets"] =


citi["Fechamento de ajuste"].pct_change()

P = 1e6 # 1.000.000 USD c = 0,99 #


Intervalo de confiança de 99% mu = [Link](citi["rets"])
sigma = [Link](citi["rets"])

var = var_cov_var(P, c, mu, sigma) print("Valor em


risco: $%0,2f" % var)

O valor calculado do VaR é dado por:

Valor em risco: $ 56.503,12

O VaR é uma técnica extremamente útil e difundida em todas as áreas da gestão financeira, mas não está isenta
de falhas. David Einhorn, o renomado gestor de fundos de hedge, descreveu o VaR como "um airbag que funciona o
tempo todo, exceto quando você sofre um acidente de carro".
De fato, você deve sempre usar o VaR como um complemento à sua sobreposição de gerenciamento de risco, não
como um indicador único!
Machine Translated by Google

Parte VI

Negociação automatizada

127
Machine Translated by Google
Machine Translated by Google

Capítulo 14

Mecanismo de negociação orientado a eventos


Implementação

Este capítulo fornece uma implementação para um sistema de backtest totalmente independente, orientado a
eventos, escrito em Python. Em particular, este capítulo foi escrito para expandir os detalhes que geralmente
são omitidos em outros textos e artigos sobre negociação algorítmica. O código a seguir permitirá simular
estratégias de alta frequência (minuto a segundo) nos domínios de previsão, momentum e reversão à média
nos mercados de ações, câmbio e futuros.
No entanto, com muitos detalhes, vem a complexidade. O sistema de backtesting fornecido aqui requer
muitos componentes, cada um dos quais é uma entidade abrangente por si só. O primeiro passo é, portanto,
delinear o que é um software orientado a eventos e, em seguida, descrever os componentes do backtester e
como todo o sistema se integra.

14.1 Software orientado a eventos


Antes de nos aprofundarmos no desenvolvimento de um backtester, precisamos entender o conceito de
sistemas orientados a eventos. Os videogames oferecem um caso de uso natural para software orientado a
eventos e um exemplo simples para explorar. Um videogame possui múltiplos componentes que interagem
entre si em tempo real e com altas taxas de quadros. Isso é feito executando todo o conjunto de cálculos
dentro de um loop "infinito" conhecido como loop de eventos ou loop de jogo.
A cada tique do loop do jogo, uma função é chamada para receber o evento mais recente, que terá sido gerado por alguma
ação anterior correspondente dentro do jogo. Dependendo da natureza do evento, que pode incluir um pressionamento de tecla ou
um clique do mouse, alguma ação subsequente é executada, encerrando o loop ou gerando eventos adicionais. O processo então
continua.

Aqui está um exemplo de pseudocódigo:

while True: # Executa o loop para sempre


new_event = get_new_event() # Obtém o evento mais recente

# Com base no tipo de evento, execute uma ação if


new_event.type == "LEFT_MOUSE_CLICK":
open_menu()
elif new_event.type == "ESCAPE_KEY_PRESS":
quit_game()
elif new_event.type == "UP_KEY_PRESS":
move_player_north() # ...
e muitos outros eventos

redraw_screen() # Atualiza a tela para fornecer animação tick(50) # Espera 50


milissegundos

129
Machine Translated by Google

130

O código verifica continuamente novos eventos e, em seguida, executa ações com base nesses eventos. Em particular,
permite a ilusão de tratamento de resposta em tempo real, pois o código é continuamente repetido e os eventos são verificados.
Como ficará claro, isso é exatamente o que precisamos para realizar a simulação de negociação de alta frequência.

14.1.1 Por que um Backtester orientado a eventos?


Sistemas orientados a eventos oferecem muitas vantagens em relação a uma abordagem vetorizada:

• Reutilização de Código - Um backtester orientado a eventos, por design, pode ser usado tanto para backtesting histórico
quanto para negociação ao vivo, com troca mínima de componentes. Isso não se aplica a backtesters vetorizados, onde
todos os dados devem estar disponíveis simultaneamente para realizar análises estatísticas.

• Viés de previsão - Com um backtester orientado a eventos, não há viés de previsão, pois o recebimento de dados de
mercado é tratado como um "evento" que deve ser considerado. Assim, é possível "alimentar" um backtester orientado a
eventos com dados de mercado, replicando o comportamento de um sistema de gerenciamento de ordens e portfólio.

• Realismo - Os backtesters orientados a eventos permitem uma personalização significativa sobre como as ordens são
executadas e os custos de transação são incorridos. É simples lidar com ordens básicas de mercado e limitadas, bem
como com ordens de mercado na abertura (MOO) e de mercado no fechamento (MOC), já que um manipulador de bolsa
personalizado pode ser construído.

Embora os sistemas orientados a eventos apresentem muitas vantagens, eles apresentam duas grandes desvantagens
em relação aos sistemas vetorizados mais simples. Em primeiro lugar, são significativamente mais complexos de implementar e
testar. Há mais "partes móveis", o que aumenta a chance de introduzir bugs. Para mitigar isso, uma metodologia adequada de
teste de software, como o desenvolvimento orientado a testes, pode ser empregada.

Em segundo lugar, eles são mais lentos de executar em comparação com um sistema vetorizado. Vetorizado ideal
as operações não podem ser utilizadas ao realizar cálculos matemáticos.

14.2 Objetos Componentes


Para aplicar uma abordagem orientada a eventos a um sistema de backtesting, é necessário definir nossos componentes (ou
objetos) que lidarão com tarefas específicas:

• Evento - O Evento é a unidade de classe fundamental do sistema orientado a eventos. Ele contém um tipo (como
"MERCADO", "SINAL", "ORDEM" ou "PREENCHIMENTO") que determina como será tratado dentro do loop de eventos.

• Fila de Eventos - A Fila de Eventos é um objeto Python Queue na memória que armazena todos
dos objetos da subclasse Event que são gerados pelo restante do software.

• DataHandler - O DataHandler é uma classe base abstrata (ABC) que apresenta uma interface para lidar com dados de
mercado históricos ou em tempo real. Isso proporciona flexibilidade significativa, pois os módulos Estratégia e Portfólio
podem ser reutilizados entre ambas as abordagens. O DataHandler gera um novo MarketEvent a cada pulsação do
sistema (veja abaixo).

• Estratégia - A Estratégia também é um ABC que apresenta uma interface para coletar dados de mercado e gerar os
SignalEvents correspondentes, que são utilizados pelo objeto Portfólio. Um SignalEvent contém um símbolo de ticker,
uma direção (LONG ou SHORT) e um registro de data e hora.

• Portfólio - Esta é uma hierarquia de classes que gerencia as ordens associadas às posições atuais e subsequentes de
uma estratégia. Ela também realiza a gestão de risco em todo o portfólio, incluindo a exposição setorial e o
dimensionamento das posições. Em uma implementação mais sofisticada, isso poderia ser delegado a uma classe
RiskManagement. O Portfólio recebe SignalEvents da Fila e gera OrderEvents que são adicionados à Fila.
Machine Translated by Google

131

• ExecutionHandler - O ExecutionHandler simula uma conexão com uma corretora. A função do manipulador é pegar
OrderEvents da Fila e executá-los, seja por meio de uma abordagem simulada ou de uma conexão real com uma
corretora. Assim que as ordens são executadas, o manipulador cria FillEvents, que descrevem o que foi realmente
transacionado, incluindo taxas, comissão e slippage (se modelado).

• Backtest - Todos esses componentes são encapsulados em um loop de eventos que manipula corretamente
todos os tipos de eventos, encaminhando-os para o componente apropriado.

Apesar da quantidade de componentes, este é um modelo bastante básico de um mecanismo de negociação. Há um


amplo espaço para expansão, principalmente no que diz respeito à forma como o Portfólio é utilizado. Além disso, diferentes
modelos de custos de transação também podem ser abstraídos em sua própria hierarquia de classes.

14.2.1 Eventos
O primeiro componente a ser discutido é a hierarquia de classes Event. Nessa infraestrutura, existem quatro tipos de
eventos que permitem a comunicação entre os componentes acima por meio de uma fila de eventos. São eles: MarketEvent,
SignalEvent, OrderEvent e FillEvent.

Evento

A classe pai na hierarquia é chamada Event. É uma classe base e não fornece nenhuma funcionalidade ou interface
específica. Como em muitas implementações os objetos Event provavelmente desenvolverão maior complexidade, ela está
sendo "preparada para o futuro" com a criação de uma hierarquia de classes.

#!/usr/bin/python # -*-
codificação: utf-8 -*-

# [Link]

de __future__ importar função_de_impressão

classe Evento(objeto):
"""

Evento é uma classe base que fornece uma interface para todos os eventos
subsequentes (herdados), que acionarão outros eventos na infraestrutura de
negociação.
"""

passar

Evento de Mercado

MarketEvents são acionados quando o loop while externo do sistema de backtesting inicia uma nova "pulsação". Isso ocorre
quando o objeto DataHandler recebe uma nova atualização de dados de mercado para quaisquer símbolos que estejam
sendo rastreados no momento. É usado para acionar o objeto Strategy, gerando novos sinais de negociação. O objeto event
contém apenas uma identificação de que se trata de um evento de mercado, sem nenhuma outra estrutura.

# [Link]

classe MarketEvent(Evento):
"""

Lida com o evento de recebimento de uma nova atualização de mercado


com barras correspondentes.
"""

def __init__(self):
Machine Translated by Google

132

"""

Inicializa o MarketEvent.
"""

[Link] = 'MERCADO'

Evento de Sinal

O objeto Estratégia utiliza dados de mercado para criar novos Eventos de Sinal. O Evento de Sinal contém um ID de
estratégia, um símbolo de ticker, um registro de data e hora de quando foi gerado, uma direção (longa ou curta) e um
indicador de "força" (útil para estratégias de reversão à média). Os Eventos de Sinal são utilizados pelo objeto Portfólio como
orientação sobre como negociar.

# [Link]

classe SignalEvent(Evento):
"""

Manipula o evento de envio de um Sinal de um objeto Estratégia.


Isso é recebido por um objeto Portfólio e agido.
"""

def __init__(self, strategy_id, símbolo, data e hora, tipo de sinal, intensidade):


"""

Inicializa o SignalEvent.

Parâmetros:
strategy_id - O identificador exclusivo da estratégia que gerou o sinal. symbol - O símbolo
do ticker, por exemplo, 'GOOG'.
datetime - O registro de data e hora em que o sinal foi gerado.
signal_type - 'LONG' ou 'SHORT'. strength - Um fator de ajuste "sugestão" usado para
dimensionar

quantidade no nível do portfólio. Útil para estratégias de pares.


"""

[Link] = 'SINAL'
self.strategy_id = strategy_id [Link] =
símbolo [Link] = data e
hora self.signal_type = tipo de sinal
[Link] = força

Evento de pedido

Quando um objeto Portfólio recebe SignalEvents, ele os avalia no contexto mais amplo do portfólio, em termos de risco e
dimensionamento de posição. Isso, por fim, leva a OrderEvents que serão enviados a um ExecutionHandler.

O OrderEvent é um pouco mais complexo que um SignalEvent, pois contém um campo de quantidade, além das
propriedades mencionadas anteriormente. A quantidade é determinada pelas restrições do Portfólio. Além disso, o OrderEvent
possui um método print_order(), usado para enviar as informações para o console, se necessário.

# [Link]

classe OrderEvent(Evento):
"""

Lida com o evento de envio de uma Ordem para um sistema de execução.


A ordem contém um símbolo (por exemplo, GOOG), um tipo (mercado ou limite),
Machine Translated by Google

133

quantidade e uma direção.


"""

def __init__(self, símbolo, tipo_de_ordem, quantidade, direção):


"""

Inicializa o tipo de ordem, definindo se é uma ordem de mercado


('MKT') ou uma ordem de limite ('LMT'), tem uma quantidade
(integral) e sua direção ('COMPRA' ou 'VENDA').

Parâmetros:
símbolo - O instrumento a ser negociado.
tipo_de_ordem - 'MKT' ou 'LMT' para Mercado ou Limite.
quantidade - Inteiro não negativo para quantidade. direção -
'COMPRAR' ou 'VENDER' para longo ou curto.
"""

[Link] = 'ORDER'
[Link] = símbolo
self.order_type = tipo_de_ordem
[Link] = quantidade
[Link] = direção

def print_order(self):
"""

Gera os valores dentro do Pedido.


"""

print( "Ordem: Símbolo=%s, Tipo=%s, Quantidade=%s, Direção=%s" %


([Link], self.order_type, [Link], [Link])
)

PreencherEvento

Quando um ExecutionHandler recebe um OrderEvent, ele deve transacionar a ordem. Uma vez transacionada a ordem, ele
gera um FillEvent, que descreve o custo de compra ou venda, bem como os custos de transação, como taxas ou slippage.

O FillEvent é o evento com maior complexidade. Ele contém um registro de data e hora de quando uma ordem foi
executada, o símbolo da ordem e a bolsa em que foi executada, a quantidade de ações negociadas, o preço real da compra
e a comissão incorrida.
A comissão é calculada com base nas comissões da Interactive Brokers. Para ordens da API dos EUA, essa comissão
é de no mínimo US$ 1,30 por ordem, com uma taxa fixa de US$ 0,013 ou US$ 0,08 por ação, dependendo se o volume da
negociação for inferior ou superior a 500 unidades de ações.

# [Link]

classe FillEvent(Evento):
"""

Encapsula a noção de uma Ordem Preenchida, conforme retornada por


uma corretora. Armazena a quantidade de um instrumento efetivamente
preenchida e a que preço. Além disso, armazena a comissão da
negociação da corretora.
"""

def __init__(self, timeindex, símbolo, troca, quantidade, direção,


custo_de_preenchimento, comissão=None):
Machine Translated by Google

134

"""

Inicializa o objeto FillEvent. Define o símbolo, a troca, a quantidade, a direção, o custo de


preenchimento e uma comissão opcional.

Se a comissão não for fornecida, o objeto Preenchimento a calculará com base


no tamanho da negociação e nas taxas da Interactive Brokers.

Parâmetros:
timeindex - A resolução da barra quando a ordem foi preenchida. symbol - O instrumento
que foi preenchido. exchange - A bolsa onde a ordem foi
preenchida. quantity - A quantidade preenchida. direction - A direção do
preenchimento ('BUY' ou 'SELL') fill_cost - O
valor dos ativos em dólares. commission - Uma comissão opcional enviada do
IB.

"""

[Link] = 'FILL'
[Link] = timeindex [Link] =
símbolo [Link] = troca
[Link] = quantidade
[Link] = direção self.fill_cost
= custo_de_preenchimento

# Calcular comissão se a comissão


for Nenhuma: [Link] =
self.calculate_ib_commission() senão: [Link] = commission

def calcular_ib_commissão(self):
"""

Calcula as taxas de negociação com base na estrutura de taxas da Interactive


Brokers para API, em USD.

Isso não inclui taxas de câmbio ou ECN.

Com base em "Ordens Direcionadas pela API dos


EUA": [Link]
f=commission&p=stocks2
"""

custo_total = 1,3 se
[Link] <= 500:
custo_total = máx.(1,3, 0,013 * [Link])
else: # Maior que 500
custo_total = máx.(1,3, 0,008 * [Link]) retornar custo_total

14.2.2 Manipulador de Dados

Um dos objetivos de um sistema de negociação orientado a eventos é minimizar a duplicação de código


entre o elemento de backtesting e o elemento de execução ao vivo. O ideal seria utilizar a mesma
metodologia de geração de sinais e componentes de gestão de portfólio para ambos os indicadores históricos.
Machine Translated by Google

135

Testes e negociações ao vivo. Para que isso funcione, o objeto Estratégia, que gera os Sinais, e o objeto Portfólio,
que fornece Ordens com base neles, devem utilizar uma interface idêntica a um feed de mercado, tanto para a
execução histórica quanto para a execução ao vivo.
Isso motiva o conceito de uma hierarquia de classes baseada em um objeto DataHandler, que fornece a todas as
subclasses uma interface para fornecer dados de mercado aos demais componentes do sistema. Dessa forma,
qualquer manipulador de dados de subclasse pode ser "trocado" sem afetar a estratégia ou o cálculo do portfólio.

Exemplos específicos de subclasses podem incluir HistoricCSVDataHandler, QuandlDataHandler, Se-


curitiesMasterDataHandler, InteractiveBrokersMarketFeedDataHandler, etc. Neste capítulo, consideraremos apenas
a criação de um manipulador de dados CSV histórico, que carregará dados CSV intradiários para ações em um
conjunto de barras: Abertura-Mínima-Máxima-Fechamento-Volume-Juros Abertos. Isso pode ser usado para alimentar
os dados barra a barra nas classes Estratégia e Portfólio a cada pulsação do sistema, evitando assim o viés de
previsão.
A primeira tarefa é importar as bibliotecas necessárias. Especificamente, será necessário importar o pandas e a
classe base abstrata tools. Como o DataHandler gera MarketEvents, [Link] também é necessário, conforme
descrito acima.

#!/usr/bin/python # -*-
codificação: utf-8 -*-

# [Link]

de __future__ importar função_de_impressão

de abc importar ABCMeta, método abstrato


importar data e
hora importar os, [Link]

importar numpy como


np importar pandas como pd

do evento importar MarketEvent

O DataHandler é uma classe base abstrata (ABC), o que significa que é impossível instanciar uma instância
diretamente. Somente subclasses podem ser instanciadas. A justificativa para isso é que o ABC fornece uma interface
à qual todas as subclasses subsequentes do DataHandler devem aderir, garantindo assim a compatibilidade com
outras classes que se comunicam com elas.
Utilizamos a propriedade __metaclass__ para informar ao Python que se trata de um ABC. Além disso, usamos o
decorador @abstractmethod para informar ao Python que o método será sobrescrito em subclasses (o que é idêntico
a um método virtual puro em C++).
Há seis métodos listados para a classe. Os dois primeiros métodos, get_latest_bar e get_latest_bars, são usados
para recuperar um subconjunto recente das barras de negociação históricas de uma lista armazenada dessas barras.
Esses métodos são úteis nas classes Estratégia e Portfólio, devido à necessidade de estar constantemente ciente
dos preços e volumes de mercado atuais.
O método a seguir, get_latest_bar_datetime, simplesmente retorna um objeto datetime do Python que representa
o registro de data e hora da barra (por exemplo, uma data para barras diárias ou um objeto de resolução de minuto
para barras minuciosas).
Os dois métodos a seguir, get_latest_bar_value e get_latest_bar_values, são métodos convenientes usados
para recuperar valores individuais de uma barra específica ou de uma lista de barras. Por exemplo, muitas vezes,
uma estratégia está interessada apenas nos preços de fechamento. Nesse caso, podemos usar esses métodos para
retornar uma lista de valores de ponto flutuante representando os preços de fechamento de barras anteriores, em vez
de precisar obtê-los da lista de objetos de barra. Isso geralmente aumenta a eficiência de estratégias que utilizam
uma "janela de lookback", como aquelas que envolvem regressões.
O método final, update_bars, fornece um mecanismo de "alimentação por gotejamento" para inserir informações
de barras em uma nova estrutura de dados que proíbe estritamente o viés de previsão. Esta é uma das principais
diferenças entre um sistema de backtesting orientado a eventos e um baseado em vetorização. Observe que exceções
serão geradas se ocorrer uma tentativa de instanciação da classe:
Machine Translated by Google

136

# [Link]

classe DataHandler(objeto):
"""

DataHandler é uma classe base abstrata que fornece uma interface para todos os
manipuladores de dados subsequentes (herdados) (ativos e históricos).

O objetivo de um objeto DataHandler (derivado) é gerar um conjunto de barras (OHLCVI) para


cada símbolo solicitado.

Isso replicará o funcionamento de uma estratégia ativa, já que os dados atuais do mercado
seriam enviados "pelo canal". Assim, um sistema histórico e ativo serão tratados de forma
idêntica pelo restante do conjunto de backtesting.
"""

__metaclasse__ = ABCMeta

@abstractmethod
def get_latest_bar(self, símbolo):
"""

Retorna a última barra atualizada.


"""

raise NotImplementedError("Deve implementar get_latest_bar()")

@abstractmethod
def get_latest_bars(self, símbolo, N=1):
"""

Retorna as últimas N barras atualizadas.


"""

raise NotImplementedError("Deve implementar get_latest_bars()")

@abstractmethod
def get_latest_bar_datetime(self, símbolo):
"""

Retorna um objeto datetime Python para a última barra.


"""

raise NotImplementedError("Deve implementar


get_latest_bar_datetime()")

@abstractmethod
def get_latest_bar_value(self, símbolo, val_type):
"""

Retorna um dos valores de Abertura, Máximo, Mínimo, Fechamento, Volume


ou OI da última barra.
"""

raise NotImplementedError("Deve implementar


get_latest_bar_value()")

@abstractmethod
def get_latest_bars_values(self, símbolo, val_type, N=1):
"""
Retorna os últimos N valores de barra da lista
latest_symbol, ou Nk se menos disponíveis.
"""

raise NotImplementedError("Deve implementar


get_latest_bars_values()")
Machine Translated by Google

137

@abstractmethod def
update_bars(self):
"""

Envia as últimas barras para bars_queue para cada símbolo em um formato de tupla
OHLCVI: (data/hora, abertura, alta, baixa, fechamento, volume, juros em aberto).

"""

raise NotImplementedError("Deve implementar update_bars()")

Para criar um sistema de backtesting baseado em dados históricos, precisamos considerar um mecanismo para importar
dados por meio de fontes comuns. Já discutimos os benefícios de um Banco de Dados Mestre de Valores Mobiliários em
capítulos anteriores. Portanto, uma boa opção para criar uma classe DataHandler seria acoplá-la a esse banco de dados.

No entanto, para maior clareza neste capítulo, quero discutir um mecanismo mais simples: importar arquivos de variáveis
separadas por vírgula (CSV) (potencialmente grandes). Isso nos permitirá focar na mecânica de criação do DataHandler, em
vez de nos preocuparmos com o código padrão de conexão a um banco de dados e uso de consultas SQL para obter dados.

Portanto, vamos definir a subclasse HistoricCSVDataHandler, que foi projetada para processar vários arquivos CSV, um
para cada símbolo negociado, e convertê-los em um dicionário de DataFrames do Pandas que podem ser acessados pelos
métodos de barra mencionados anteriormente.
O manipulador de dados requer alguns parâmetros, a saber, uma Fila de Eventos para a qual enviar as informações do
MarketEvent, o caminho absoluto dos arquivos CSV e uma lista de símbolos. Aqui está a inicialização da classe:

# [Link]

classe HistóricoCSVDataHandler(DataHandler):
"""

O HistoricCSVDataHandler foi projetado para ler arquivos CSV para cada símbolo
solicitado do disco e fornecer uma interface para obter a barra "mais recente" de
maneira idêntica a uma interface de negociação ao vivo.

"""

def __init__(self, eventos, csv_dir, lista_de_símbolos):


"""

Inicializa o manipulador de dados históricos solicitando a localização dos


arquivos CSV e uma lista de símbolos.

Será assumido que todos os arquivos estão no formato '[Link]', onde


symbol é uma string na lista.

Parâmetros:
events - A fila de eventos. csv_dir -
Caminho absoluto do diretório para os arquivos CSV. symbol_list - Uma lista
de strings de símbolos.
"""

[Link] = eventos
self.csv_dir = csv_dir
self.symbol_list = lista_de_símbolos

self.symbol_data = {}
self.latest_symbol_data = {}
self.continue_backtest = Verdadeiro

self._open_convert_csv_files()
Machine Translated by Google

138

O manipulador procurará arquivos no diretório absoluto csv_dir e tentará abri-los com o formato
"[Link]", onde SYMBOL é o símbolo de ação (como GOOG ou AAPL).
O formato dos arquivos corresponde ao fornecido pelo Yahoo Finanças, mas pode ser facilmente
modificado para lidar com formatos de dados adicionais, como os fornecidos pelo Quandl ou DTN IQFeed.
A abertura dos arquivos é feita pelo método _open_convert_csv_files abaixo.
Um dos benefícios de usar o Pandas como um armazenamento de dados interno no HistoricCSVData-
Handler é que os índices de todos os símbolos rastreados podem ser mesclados. Isso permite que os
pontos de dados ausentes sejam preenchidos para frente, para trás ou interpolados dentro dessas
lacunas, de modo que os tickers possam ser comparados barra a barra. Isso é necessário para estratégias
de reversão à média, por exemplo. Observe o uso dos métodos union e reindex ao combinar os índices
de todos os símbolos:

# [Link]

def _abrir_converter_arquivos_csv(self):
"""

Abre os arquivos CSV do diretório de dados, convertendo-os em DataFrames do


Pandas dentro de um dicionário de símbolos.

Para este manipulador, será assumido que os dados são obtidos do Yahoo.
Portanto, seu formato será respeitado.
"""

comb_index = None para


s em self.symbol_list: # Carregue o
arquivo CSV sem informações de cabeçalho, indexado na data self.symbol_data[s] =
[Link].read_csv( [Link](self.csv_dir, '%[Link]' % s),
header=0, index_col=0, parse_dates=True, names=[ 'datetime',
'open', 'high', 'low', 'close', 'volume', 'adj_close'

] ).organizar()

# Combine o índice para preencher os valores seguintes se


comb_index for Nenhum:
comb_index = self.symbol_data[s].index senão:

comb_index.union(self.symbol_data[s].index)

# Defina os últimos dados do símbolo como Nenhum


self.latest_symbol_data[s] = []

# Reindexe os dataframes para s em


self.symbol_list: self.symbol_data[s] =
self.symbol_data[s].\
reindex(índice=comb_index, método='pad').iterrows()

O método _get_new_bar cria um gerador para fornecer uma nova barra. Isso significa que chamadas
subsequentes ao método produzirão uma nova barra até que o fim dos dados do símbolo seja atingido:

# [Link]

def _get_new_bar(self, símbolo):


"""

Retorna a barra mais recente do feed de dados.


"""

para b em self.symbol_data[símbolo]:
Machine Translated by Google

139

rendimento b

Os primeiros métodos abstratos do DataHandler a serem implementados são get_latest_bar e get_latest_bars.


Esses métodos simplesmente fornecem uma barra ou uma lista das últimas N barras da estrutura latest_symbol_data:

# [Link]

def get_latest_bar(self, símbolo):


"""

Retorna a última barra da lista latest_symbol.


"""

tente:
bars_list = self.latest_symbol_data[símbolo] exceto KeyError:
print("Esse símbolo não
está disponível no conjunto de dados históricos.") levante

outro:
retornar bars_list[-1]

def get_latest_bars(self, símbolo, N=1):


"""

Retorna as últimas N barras da lista latest_symbol, ou Nk se menos disponíveis.

"""

tente:
bars_list = self.latest_symbol_data[símbolo] exceto KeyError:
print("Esse símbolo não
está disponível no conjunto de dados históricos.") levante senão: retorne bars_list[-N:]

O próximo método, get_latest_bar_datetime, consulta a barra mais recente em busca de um objeto datetime
que representa o "último preço de mercado":

def get_latest_bar_datetime(self, símbolo):


"""

Retorna um objeto datetime Python para a última barra.


"""

tente:
bars_list = self.latest_symbol_data[símbolo] exceto KeyError:
print("Esse símbolo não
está disponível no conjunto de dados históricos.") levante senão: retorne bars_list[-1][0]

Os próximos dois métodos implementados são get_latest_bar_value e get_latest_bar_values.


Ambos os métodos utilizam a função getattr do Python, que consulta um objeto para verificar se um atributo
específico existe nele. Assim, podemos passar uma string como "open" ou "close" para getattr e obter o valor
diretamente da barra, tornando o método mais flexível. Isso nos livra da necessidade de escrever métodos do tipo
get_latest_bar_close, por exemplo:

def get_latest_bar_value(self, símbolo, val_type):


"""

Retorna um dos valores Aberto, Alto, Baixo, Fechamento, Volume ou OI do objeto


da série pandas Bar.
"""
Machine Translated by Google

140

tente:
bars_list = self.latest_symbol_data[símbolo] exceto KeyError:
print("Esse símbolo não
está disponível no conjunto de dados históricos.") levante senão: retorne getattr(bars_list[-1][1],
val_type)

def obter_valores_das_barras_mais_latas(self, símbolo, tipo_de_valor, N=1):


"""

Retorna os últimos N valores de barra da lista latest_symbol,


ou Nk se menos disponíveis.
"""

tente:
bars_list = self.get_latest_bars(symbol, N) exceto KeyError:
print("Esse símbolo não
está disponível no conjunto de dados históricos.") levante senão: retorne [Link]([getattr(b[1],

val_type) para b em bars_list])

O método final, update_bars, é o segundo método abstrato do DataHandler. Ele simplesmente gera um MarketEvent que é
adicionado à fila à medida que anexa as barras mais recentes ao dicionário latest_symbol_data:

# [Link]

def update_bars(self):
"""

Envia a barra mais recente para a estrutura latest_symbol_data para todos os símbolos
na lista de símbolos.
"""

para s em self.symbol_list: tente: bar =

next(self._get_new_bar(s)) exceto StopIteration:

self.continue_backtest = False senão:

se bar não for Nenhum:


self.latest_symbol_data[s].append(barra)
[Link](MarketEvent())

Assim, temos um objeto derivado de DataHandler, que é usado pelos componentes restantes para monitorar os dados de
mercado. Os objetos Strategy, Portfolio e ExecutionHandler requerem os dados de mercado atuais, portanto, faz sentido
centralizá-los para evitar duplicação de armazenamento entre essas classes.

14.2.3 Estratégia
Um objeto Strategy encapsula todos os cálculos sobre dados de mercado que geram sinais de recomendação para um objeto
Portfolio . Portanto, toda a "lógica da estratégia" reside nessa classe. Optei por separar os objetos Strategy e Portfolio para este
backtester, pois acredito que isso seja mais adequado à situação de múltiplas estratégias alimentando "ideias" para um Portfolio
maior, que então pode lidar com seu próprio risco (como alocação de setor, alavancagem). Em negociações de alta frequência,
os conceitos de estratégia e portfólio estarão fortemente acoplados e extremamente dependentes de hardware. No entanto, isso
está muito além do escopo deste capítulo!

Nesta fase do desenvolvimento do backtester orientado a eventos, não existe o conceito de indicador ou filtro, como os
encontrados na negociação técnica. Estes também são bons candidatos para a criação
Machine Translated by Google

141

uma hierarquia de classes, mas estão além do escopo deste capítulo. Portanto, tais mecanismos serão usados
diretamente em objetos Strategy derivados.
A hierarquia de estratégias é relativamente simples, pois consiste em uma classe base abstrata com um único
método virtual puro para gerar objetos SignalEvent . Para criar a hierarquia de estratégias, é necessário importar o
NumPy, o pandas, o objeto Queue (que se tornou queue no Python 3), ferramentas da classe base abstrata e o
SignalEvent:

#!/usr/bin/python # -*-
codificação: utf-8 -*-

# estraté[Link]

de __future__ importar função_de_impressão

de abc importar ABCMeta, método abstrato importar


data e hora tentar:

importar fila como fila exceto


ImportError: importar fila

importar numpy como


np importar pandas como pd

do evento importar SignalEvent

A classe base abstrata Strategy simplesmente define um método calculate_signals virtual puro .
Em classes derivadas, isso é usado para manipular a geração de objetos SignalEvent com base em atualizações de
dados de mercado:

# estraté[Link]

classe Estratégia(objeto):
"""

Estratégia é uma classe base abstrata que fornece uma interface para todos os
objetos de manipulação de estratégia subsequentes (herdados).

O objetivo de um objeto Strategy (derivado) é gerar objetos Signal para símbolos


específicos com base nas entradas de Bars (OHLCV) geradas por um objeto
DataHandler.

Isso foi projetado para funcionar tanto com dados históricos quanto ao vivo, já que
o objeto Strategy é independente da origem dos dados, pois ele obtém as tuplas
de barras de um objeto de fila.
"""

__metaclasse__ = ABCMeta

@abstractmethod
def calculate_signals(self):
"""

Fornece os mecanismos para calcular a lista de sinais.


"""

raise NotImplementedError("Deve implementar calculate_signals()")


Machine Translated by Google

142

14.2.4 Portfólio
Esta seção descreve um objeto Portfólio que monitora as posições dentro de um portfólio e gera ordens de uma quantidade
fixa de ações com base em sinais. Objetos de portfólio mais sofisticados podem incluir ferramentas de gerenciamento de risco
e dimensionamento de posições (como o Critério de Kelly).
De fato, nos próximos capítulos, adicionaremos essas ferramentas a algumas de nossas estratégias de negociação para ver
como elas se comparam a uma abordagem de portfólio mais "ingênua".
O sistema de gerenciamento de ordens de portfólio é possivelmente o componente mais complexo de um backtester
orientado a eventos. Sua função é monitorar todas as posições de mercado atuais, bem como o valor de mercado das
posições (conhecido como "holdings"). Isso é simplesmente uma estimativa do valor de liquidação da posição e é derivado,
em parte, da funcionalidade de processamento de dados do backtester.
Além da gestão de posições e participações, o portfólio também deve estar ciente dos fatores de risco e técnicas de
dimensionamento de posições para otimizar ordens enviadas a uma corretora ou outra forma de acesso ao mercado.

Infelizmente, os Sistemas de Gestão de Portfólio e Ordens (OMS) podem se tornar bastante complexos!
Portanto, tomei a decisão de manter o objeto Portfólio relativamente simples, para que você possa entender as ideias-chave
e como elas são implementadas. A natureza de um design orientado a objetos é que ele permite, de forma natural, a extensão
para situações mais complexas posteriormente.
Seguindo a hierarquia de classes Event , um objeto Portfolio deve ser capaz de manipular objetos SignalEvent , gerar
objetos OrderEvent e interpretar objetos FillEvent para atualizar posições. Portanto, não é surpresa que os objetos Portfolio
sejam frequentemente o maior componente de sistemas orientados a eventos, em termos de linhas de código (LOC).

Criamos um novo arquivo [Link] e importamos as bibliotecas necessárias. Elas são as mesmas da maioria das outras implementações de
classe, com a exceção de que Portfolio NÃO será uma classe base abstrata. Em vez disso, será uma classe base normal. Isso significa que ela pode
ser instanciada e, portanto, é útil como um objeto Portfolio "inicial" ao testar novas estratégias.

Outros Portfólios podem ser derivados dele e substituir seções para adicionar mais complexidade.
Para completar, aqui está o arquivo [Link]:

#!/usr/bin/python #
-*- codificação: utf-8 -*-

# [Link]

de __future__ importar função_de_impressão

importar numpy como


np importar pandas como pd

def create_sharpe_ratio(retorna, períodos=252):


"""
Crie o índice de Sharpe para a estratégia, com base em um parâmetro
de referência de zero (ou seja, sem informações de taxa livre de risco).

Parâmetros:
retornos - Uma série pandas representando retornos percentuais do período. Períodos - Diário (252),
Horário (252*6,5), Minucioso (252*6,5*60) etc.
"""
retornar [Link](períodos) * ([Link](retorna)) / [Link](retorna)

def create_drawdowns(pnl):
"""
Calcula o maior rebaixamento do pico ao vale da curva PnL, bem como a
duração do rebaixamento. Requer que pnl_returns seja uma série pandas.
Machine Translated by Google

143

Parâmetros: pnl
- Uma série pandas representando retornos percentuais do período.

Retornos:
rebaixamento, duração - Maior rebaixamento do pico ao vale e duração.
"""

# Calcule a curva de retornos cumulativos # e configure o High


Water Mark hwm = [0]

# Crie a série de rebaixamento e duração idx = [Link] drawdown


= [Link](index = idx)
duration = [Link](index = idx)

# Loop sobre o intervalo de índice para t


em intervalo(1, len(idx)):
[Link](max(hwm[t-1], pnl[t])) drawdown[t]=
(hwm[t]-pnl[t]) duration[t]= (0 se drawdown[t]
== 0 senão duração[t-1]+1) retornar drawdown, [Link](), [Link]()

Aqui está a listagem de importação para o arquivo [Link]. Precisamos importar a função floor da biblioteca
matemática para gerar tamanhos de pedidos com valores inteiros. Também precisamos dos objetos FillEvent e OrderEvent,
já que o Portfolio lida com ambos. Observe também que estamos adicionando duas funções adicionais, create_sharpe_ratio
e create_drawdowns, ambas do arquivo [Link] descrito acima.

#!/usr/bin/python # -*-
codificação: utf-8 -*-

# portfó[Link]

de __future__ importar função_de_impressão

importar data e hora de


matemática importar andar tentar:

importar fila como fila exceto


ImportError: importar fila

importar numpy como


np importar pandas como pd

do evento importar FillEvent, OrderEvent do desempenho


importar create_sharpe_ratio, create_drawdowns

A inicialização do objeto Portfólio requer acesso ao DataHandler de barras, à Fila de Eventos de eventos, a um registro
de data e hora de início e a um valor de capital inicial (padrão de 100.000 USD).
O Portfólio foi projetado para lidar com o dimensionamento de posições e as posições atuais, mas executará ordens de
negociação de forma "simplificada", simplesmente enviando-as diretamente para a corretora com um valor fixo predeterminado,
independentemente do caixa disponível. Todas essas são suposições irrealistas, mas ajudam a delinear como um sistema de
gerenciamento de ordens de portfólio (OMS) funciona de forma orientada a eventos.
Machine Translated by Google

144

O portfólio contém os membros all_positions e current_positions . O primeiro armazena uma lista de todas
as posições anteriores registradas no registro de data e hora de um evento de dados de mercado. Uma posição é
simplesmente a quantidade do ativo mantida. Posições negativas significam que o ativo foi vendido a descoberto.
O último dicionário current_positions armazena as posições atuais para a última atualização da barra de mercado,
para cada símbolo.
Além dos dados de posições, o portfólio armazena as posições, que descrevem o valor de mercado atual das
posições mantidas. "Valor de mercado atual", neste caso, significa o preço de fechamento obtido a partir da barra
de mercado atual, o que é claramente uma aproximação, mas razoável o suficiente por enquanto. all_holdings
armazena a lista histórica de todas as posições em ativos, enquanto current_holdings armazena o dicionário
mais atualizado de todos os valores de posições em ativos:

# portfó[Link]

classe Portfólio(objeto):
"""

A classe Portfólio lida com as posições e o valor de mercado de


todos os instrumentos com uma resolução de "barra", ou seja, a
cada segundo, a cada minuto, a cada 5 minutos, a cada 30 minutos, a cada 60 minutos ou EOD.

O DataFrame de posições armazena um índice de tempo da


quantidade de posições mantidas.

O DataFrame de participações armazena o valor em dinheiro e o


valor total das participações de mercado de cada símbolo
para um índice de tempo específico, bem como a variação
percentual no total do portfólio entre as barras.
"""

def __init__(self, barras, eventos, data_início, capital_inicial=100000.0):


"""

Inicializa o portfólio com barras e uma fila de eventos.


Inclui também um índice de data e hora inicial e capital inicial (USD, salvo indicação em
contrário).

Parâmetros: bars
- O objeto DataHandler com dados de mercado atuais. events - O objeto Event Queue.
start_date - A data de início (bar) do portfólio.
initial_capital - O capital inicial em USD.

"""

[Link] = barras
[Link] = eventos
self.symbol_list = [Link].symbol_list self.start_date =
data_de_início self.initial_capital =
capital_inicial

self.all_positions = self.construct_all_positions() self.current_positions =


dict( (k,v) para k, v em \ [(s, 0) para s em self.symbol_list] )

self.all_holdings = self.construct_all_holdings() self.current_holdings =


self.construct_current_holdings()

O método a seguir, construct_all_positions, simplesmente cria um dicionário para cada símbolo, define o
valor como zero para cada um e, em seguida, adiciona uma chave de data e hora, adicionando-o finalmente a uma lista.
Ele usa uma compreensão de dicionário, que é semelhante em espírito a uma compreensão de lista:

# portfó[Link]
Machine Translated by Google

145

def construct_all_positions(self):
"""

Constrói a lista de posições usando a start_date para determinar quando o


índice de tempo começará.
"""

d = dict( (k,v) para k, v em [(s, 0) para s em self.symbol_list] ) d['datetime'] = self.start_date retornar


[d]

O método construct_all_holdings é semelhante ao anterior, mas adiciona chaves extras para caixa, comissão e total, que
representam, respectivamente, o caixa disponível na conta após quaisquer compras, a comissão acumulada e o patrimônio
líquido total da conta, incluindo caixa e quaisquer posições em aberto. Posições vendidas são tratadas como negativas. O caixa
inicial e o patrimônio líquido total da conta são definidos como o capital inicial.

Dessa forma, há "contas" separadas para cada símbolo, o "dinheiro em caixa", a "comissão" paga (taxas da Interactive
Broker) e um valor "total" da carteira. Obviamente, isso não leva em consideração os requisitos de margem ou as restrições de
venda a descoberto, mas é suficiente para dar uma ideia de como um OMS é criado:

# portfó[Link]

def construct_all_holdings(self):
"""

Constrói a lista de acervos usando a start_date para determinar quando o


índice de tempo começará.
"""

d = dict( (k,v) para k, v em [(s, 0.0) para s em self.symbol_list] ) d['datetime'] = self.start_date d['cash']
= self.initial_capital d['commission'] = 0.0
d['total'] = self.initial_capital return [d]

O método a seguir, construct_current_holdings, é quase idêntico ao método acima, exceto que ele não encapsula o
dicionário em uma lista, porque ele cria apenas uma única entrada:

# portfó[Link]

def construct_current_holdings(self):
"""

Isso constrói o dicionário que conterá o valor instantâneo do portfólio em todos os símbolos.

"""

d = dict( (k,v) para k, v em [(s, 0.0) para s em self.symbol_list] ) d['dinheiro'] = self.initial_capital


d['comissão'] = 0.0 d['total'] = self.initial_capital
retornar d

A cada pulsação, ou seja, sempre que novos dados de mercado são solicitados ao objeto DataHandler , a carteira deve
atualizar o valor de mercado atual de todas as posições mantidas. Em um cenário de negociação ao vivo, essas informações
podem ser baixadas e analisadas diretamente da corretora, mas para uma implementação de backtesting, é necessário calcular
esses valores manualmente a partir do DataHandler das barras.

Infelizmente, não existe um "valor de mercado atual" devido aos spreads de compra/venda e a problemas de liquidez.
Portanto, é necessário estimá-lo multiplicando a quantidade do ativo detido por um "preço" aproximado específico. A abordagem
que adotei aqui é usar o fechamento
Machine Translated by Google

146

preço da última barra recebida. Para uma estratégia intradiária, isso é relativamente realista. Para uma estratégia
diária, isso é menos realista, pois o preço de abertura pode diferir substancialmente do preço de fechamento.
O método update_timeindex gerencia o rastreamento de novas posições. Primeiramente, ele obtém os
preços mais recentes do manipulador de dados de mercado e cria um novo dicionário de símbolos para
representar as posições atuais, definindo as "novas" posições como iguais às posições "atuais".
As posições atuais só são modificadas quando um FillEvent é obtido, o que é tratado posteriormente no
código do portfólio. O método então anexa esse conjunto de posições atuais à lista all_positions .

As participações são então atualizadas de maneira semelhante, com a exceção de que o valor de mercado
é recalculado multiplicando a contagem de posições atuais pelo preço de fechamento da barra mais recente.
Por fim, os novos acervos são anexados a all_holdings:

# portfó[Link]

def update_timeindex(self, evento):


"""

Adiciona um novo registro à matriz de posições para a barra de dados de


mercado atual. Isso reflete a barra ANTERIOR, ou seja, todos os dados de
mercado atuais neste estágio são conhecidos (OHLCV).

Faz uso de um MarketEvent da fila de eventos.


"""

data_hora_mais_última =
[Link].obter_data_hora_mais_última_barra(self.lista_de_símbolos[0]
)

# Atualizar posições #
================

dp = dict( (k,v) para k, v em [(s, 0) para s em self.symbol_list] ) dp['datetime'] =


latest_datetime

para s em self.symbol_list: dp[s]


= self.current_positions[s]

# Acrescente as posições atuais


self.all_positions.append(dp)

# Atualizar participações #
=============== dh =

dict( (k,v) para k, v em [(s, 0) para s em self.symbol_list] ) dh['datetime'] = latest_datetime dh['cash'] =


self.current_holdings['cash'] dh['commission'] =
self.current_holdings['commission'] dh['total'] =
self.current_holdings['cash']

para s em self.symbol_list: #
Aproximação ao valor real market_value =
self.current_positions[s] * \
[Link].get_latest_bar_value(s, "adj_close") dh[s] =
valor_de_mercado
dh['total'] += valor_de_mercado

# Adicionar as participações atuais


self.all_holdings.append(dh)

O método update_positions_from_fill determina se um FillEvent é uma Compra ou uma Venda e então


atualiza o dicionário current_positions de acordo adicionando/subtraindo
Machine Translated by Google

147

a quantidade correta de ações:

# portfó[Link]

def update_positions_from_fill(self, preencher):


"""

Pega um objeto Fill e atualiza a matriz de posição para refletir a nova posição.

Parâmetros: fill -
O objeto Fill para atualizar as posições.
"""

# Verifique se o preenchimento é uma compra ou venda fill_dir


= 0 if [Link]
== 'BUY': fill_dir = 1 if [Link] ==
'SELL': fill_dir = -1

# Atualizar lista de posições com novas quantidades


self.current_positions[[Link]] += fill_dir*[Link]

O update_holdings_from_fill correspondente é semelhante ao método acima, mas atualiza os valores dos ativos.
Para simular o custo de um preenchimento, o método a seguir não utiliza o custo associado ao FillEvent. Por quê?
Simplificando, em um ambiente de backtesting, o custo de preenchimento é, na verdade, desconhecido (o impacto no
mercado e a profundidade do livro são desconhecidos) e, portanto, deve ser estimado.

Assim, o custo de preenchimento é definido como o "preço de mercado atual", que é o preço de fechamento da última
barra. As posições em um determinado símbolo são então definidas como iguais ao custo de preenchimento multiplicado
pela quantidade transacionada. Para a maioria das estratégias de negociação de baixa frequência em mercados líquidos,
esta é uma aproximação razoável, mas em alta frequência, essas questões precisarão ser consideradas em um backtest
de produção e em um mecanismo de negociação ao vivo.
Uma vez conhecido o custo de preenchimento, os ativos atuais, o dinheiro e os valores totais podem ser atualizados.
A comissão cumulativa também é atualizada:

# portfó[Link]

def update_holdings_from_fill(self, preencher):


"""

Pega um objeto Fill e atualiza a matriz de participações para refletir o valor das
participações.

Parâmetros:
fill - O objeto Fill para atualizar os acervos.
"""

# Verifique se o preenchimento é uma compra ou venda fill_dir


= 0 if [Link]
== 'BUY': fill_dir = 1 if [Link] ==
'SELL': fill_dir = -1

# Atualizar lista de participações com novas quantidades


fill_cost = [Link].get_latest_bar_value([Link], "adj_close") cost = fill_dir * fill_cost * [Link]
self.current_holdings[[Link]] += cost
self.current_holdings['commission'] += [Link]
self.current_holdings['cash'] -= (cost + [Link])
Machine Translated by Google

148

self.current_holdings['total'] -= (custo + [Link])

O método virtual puro update_fill da classe Portfolio é implementado aqui. Ele simplesmente executa os dois
métodos anteriores, update_positions_from_fill e update_holdings_from_fill, ao receber um evento de preenchimento:

# portfó[Link]

def update_fill(self, evento):


"""

Atualiza as posições e participações atuais do portfólio a partir de um FillEvent.

"""

se [Link] == 'PREENCHER':
self.update_positions_from_fill(evento)
self.update_holdings_from_fill(evento)

Embora o objeto Portfolio deva manipular FillEvents, ele também deve cuidar da geração
OrderEvents após o recebimento de um ou mais SignalEvents.
O método generate_naive_order simplesmente recebe um sinal para operar comprado ou vendido em um
ativo, enviando uma ordem para 100 ações desse ativo. Claramente, 100 é um valor arbitrário e dependerá
claramente do patrimônio líquido total da carteira em uma simulação de produção.
Em uma implementação realista, esse valor será determinado por uma sobreposição de gerenciamento de risco
ou dimensionamento de posição. No entanto, este é um Portfólio simplista e, portanto, envia "ingenuamente" todas
as ordens diretamente dos sinais, sem um sistema de risco.
O método lida com a compra, venda a descoberto e saída de uma posição, com base na quantidade atual
e um símbolo específico. Objetos OrderEvent correspondentes são então gerados:

# portfó[Link]

def generate_naive_order(self, sinal):


"""

Simplesmente arquiva um objeto Order como um dimensionamento


de quantidade constante do objeto de sinal, sem considerações de
gerenciamento de risco ou dimensionamento de posição.

Parâmetros: sinal
- A tupla contendo informações do sinal.
"""

ordem = Nenhum

símbolo = sinal.símbolo direção


= sinal.tipo_de_sinal força = [Link]ça

quantidade_mkt = 100
quantidade_cur = [Link]ções_correntes[símbolo] tipo_pedido =
'MKT'

se direção == 'LONGO' e quantidade atual == 0: ordem =


OrderEvent(símbolo, tipo_ordem, quantidade_mercado, 'COMPRAR') se direção == 'CURTO'
e quantidade atual == 0: ordem = OrderEvent(símbolo, tipo_ordem,
quantidade_mercado, 'VENDER')

se direção == 'SAÍDA' e cur_quantity > 0:


pedido = OrderEvent(símbolo, tipo_de_pedido, abs(quantidade_atual), 'VENDER')
se direção == 'SAÍDA' e cur_quantity < 0:
pedido = OrderEvent(símbolo, tipo_de_pedido, abs(quantidade_atual), 'COMPRAR')
Machine Translated by Google

149

pedido de devolução

O método update_signal simplesmente chama o método acima e adiciona o pedido gerado à fila de eventos:

# portfó[Link]

def update_signal(self, evento):


"""

Atua em um SignalEvent para gerar novas ordens com base na


lógica do portfólio.
"""

se [Link] == 'SINAL':
order_event = self.generate_naive_order(evento)
[Link](order_event)
O penúltimo método no Portfólio é a geração de uma curva de patrimônio líquido. Isso simplesmente cria um fluxo de
retorno, útil para cálculos de desempenho, e então normaliza a curva de patrimônio líquido para ser baseada em porcentagem.
Assim, o tamanho inicial da conta é igual a 1,0, em oposição ao valor absoluto em dólares:

# portfó[Link]

def create_equity_curve_dataframe(self):
"""

Cria um DataFrame pandas a partir da lista de dicionários all_holdings.

"""

curva = [Link](self.all_holdings) curva.set_index('data/


hora', inplace=True) curva['retornos'] = curva['total'].pct_change()
curva['curva_de_equidade'] = (1,0+curva['retornos']).cumprod()
auto.curva_de_equidade = curva

O método final no Portfólio é a saída da curva de patrimônio líquido e diversas estatísticas de desempenho relacionadas à
estratégia. A linha final gera um arquivo, [Link], no mesmo diretório do código, que pode ser carregado em um script Python
Matplotlib (ou em uma planilha como MS Excel ou LibreOffice Calc) para análise subsequente.

Observe que a duração do drawdown é dada em termos do número absoluto de "barras" que o
o saque continuou por um período específico, e não por um período específico.

def output_summary_stats(self):
"""

Cria uma lista de estatísticas resumidas para o portfólio.


"""

retorno_total = self.curva_de_equidade['curva_de_equidade']
[-1] retornos = self.curva_de_equidade['retornos']
pnl = self.curva_de_equidade['curva_de_equidade']

sharpe_ratio = create_sharpe_ratio(retornos, períodos=252*60*6,5) drawdown, max_dd,


dd_duration = create_drawdowns(pnl) self.equity_curve['drawdown'] = drawdown

stats = [("Retorno total", "%0,2f%%" % \ ((retorno_total - 1,0) *


100,0)), ("Índice de Sharpe", "%0,2f" %
índice_de_sharpe), ("Redução máxima", "%0,2f%%" % (dd_máx.
* 100,0)), ("Duração da redução", "%d" % dd_duração)]
Machine Translated by Google

150

self.equity_curve.to_csv('[Link]') retorna estatísticas

O objeto Portfólio é o aspecto mais complexo de todo o sistema de backtest orientado a eventos.
A implementação aqui, embora complexa, é relativamente elementar no tratamento de posições.

14.2.5 Manipulador de Execução

Nesta seção, estudaremos a execução de ordens de negociação criando uma hierarquia de classes que
representará um mecanismo simulado de tratamento de ordens e, por fim, vinculará-se a uma corretora ou
outro meio de conectividade de mercado.
O ExecutionHandler descrito aqui é extremamente simples, pois atende a todas as ordens ao preço de
mercado atual. Isso é altamente irreal, mas serve como uma boa base para melhorias.
Assim como nas hierarquias de classes base abstratas anteriores, precisamos importar as propriedades e decoradores
necessários da biblioteca abc . Além disso, precisamos importar o FillEvent e o OrderEvent:

#!/usr/bin/python # -*-
codificação: utf-8 -*-

# execuçã[Link]

de __future__ importar função_de_impressão

de abc importar ABCMeta, método abstrato importar data e


hora tentar: importar
fila
como fila exceto ImportError:
importar fila

do evento importar FillEvent, OrderEvent

O ExecutionHandler é semelhante às classes base abstratas anteriores e tem apenas um puro


método virtual, execute_order:

# execuçã[Link]

classe ExecutionHandler(objeto):
"""

A classe abstrata ExecutionHandler manipula a interação entre um conjunto de objetos de


ordem gerados por um Portfólio e o conjunto final de objetos Fill que realmente ocorrem no
mercado.

Os manipuladores podem ser usados para subclassificar corretoras simuladas ou


corretoras reais, com interfaces idênticas. Isso permite que as estratégias sejam testadas
de forma muito semelhante ao mecanismo de negociação real.

"""

__metaclasse__ = ABCMeta

@abstractmethod def
execute_order(self, evento):
"""

Pega um evento Order e o executa, produzindo um evento Fill que é


colocado na fila de Eventos.
Machine Translated by Google

151

Parâmetros:
evento - Contém um objeto Evento com informações do pedido.
"""

raise NotImplementedError("Deve implementar execute_order()")

Para realizar o backtest de estratégias, precisamos simular como uma operação será realizada. A implementação mais
simples possível é assumir que todas as ordens são executadas ao preço de mercado atual para todas as quantidades. Isso
é claramente extremamente irrealista, e grande parte da melhoria do realismo do backtest virá da concepção de modelos mais
sofisticados de slippage e impacto no mercado.
Observe que o FillEvent recebe o valor None para o fill_cost (veja a penúltima linha em execute_order) , pois já
definimos o custo de preenchimento no objeto Portfolio descrito acima. Em uma implementação mais realista, usaríamos o
valor de dados de mercado "atual" para obter um custo de preenchimento realista.

Eu simplesmente utilizei o ARCA como exchange, embora, para fins de backtesting, este seja apenas um espaço
reservado para strings. Em um ambiente de execução ao vivo, essa dependência do local seria muito mais importante:

# execuçã[Link]

classe SimulatedExecutionHandler(ExecutionHandler):
"""

O manipulador de execução simulada simplesmente converte todos os


objetos de pedido em seus objetos de preenchimento equivalentes
automaticamente, sem problemas de latência, deslizamento ou taxa de preenchimento.

Isso permite um teste direto de "primeira tentativa" de qualquer estratégia, antes da


implementação com um manipulador de execução mais sofisticado.

"""

def __init__(self, eventos):


"""

Inicializa o manipulador, configurando as filas de eventos internamente.

Parâmetros:
eventos - A fila de objetos de eventos.
"""

[Link] = eventos

def execute_order(self, evento):


"""

Simplesmente converte objetos Order em objetos Fill de forma ingênua,


ou seja, sem qualquer problema de latência, deslizamento ou taxa de preenchimento.

Parâmetros:
evento - Contém um objeto Evento com informações do pedido.
"""

se [Link] == 'ORDEM':
evento_preenchimento = EventoPreenchimento(

[Link](), [Link], 'ARCA',


[Link], [Link], Nenhum

) [Link](evento_de_preenchimento)
Machine Translated by Google

152

14.2.6 Teste retrospectivo

Agora estamos em condições de criar a hierarquia de classes Backtest. O objeto Backtest encapsula a lógica de tratamento de
eventos e essencialmente conecta todas as outras classes que discutimos acima.

O objeto Backtest foi projetado para executar um sistema aninhado de loop while, orientado a eventos, para manipular os
eventos colocados no objeto Event Queue. O loop while externo é conhecido como "loop de pulsação" e determina a resolução
temporal do sistema de backtesting. Em um ambiente real, esse valor será um número positivo, como 600 segundos (a cada
dez minutos). Portanto, os dados de mercado e as posições serão atualizados apenas nesse período.

Para o backtester descrito aqui, o "heartbeat" pode ser definido como zero, independentemente do
frequência da estratégia, uma vez que os dados já estão disponíveis em virtude do fato de serem históricos!
Podemos executar o backtest na velocidade que quisermos, já que o sistema orientado a eventos é independente de
quando os dados se tornam disponíveis, desde que tenha um registro de data e hora associado. Portanto, incluí-o apenas para
demonstrar como um mecanismo de negociação em tempo real funcionaria. O loop externo termina assim que o DataHandler
informa o objeto Backtest, usando um atributo booleano continue_backtest.

O loop while interno processa os sinais e os envia ao componente correto, dependendo do tipo de evento. Assim, a Fila de
Eventos é continuamente preenchida e despovoada com eventos. É isso que significa que um sistema seja orientado a eventos.

A primeira tarefa é importar as bibliotecas necessárias. Importamos pprint ("pretty-print"), porque


queremos exibir as estatísticas de uma maneira amigável à saída:

#!/usr/bin/python # -*-
codificação: utf-8 -*-

# [Link]

de __future__ importar função_de_impressão

importar data e hora


importar pprint

tentar: importar fila como fila exceto


ImportError: importar fila
importar hora

A inicialização do objeto Backtest requer o diretório CSV, a lista completa de símbolos negociados, o capital inicial, o tempo
de pulsação em milissegundos, o registro de data e hora de início do backtest, bem como os objetos DataHandler,
ExecutionHandler, Portfolio e Strategy.
Uma fila é usada para armazenar os eventos. Os sinais, ordens e preenchimentos são contabilizados:

# [Link]

classe Backtest(objeto):
"""

Encapsula as configurações e os componentes para executar um backtest orientado a


eventos.
"""

def __init__(
self, csv_dir, lista_de_símbolos, capital_inicial, pulsação,
data_de_início, manipulador_de_dados,
manipulador_de_execução, portfólio, estratégia
):
"""

Inicializa o backtest.
Machine Translated by Google

153

Parâmetros:
csv_dir - A raiz rígida para o diretório de dados CSV. symbol_list - A lista de
sequências de símbolos. intial_capital - O capital inicial do
portfólio. heartbeat - "heartbeat" do backtest em segundos start_date - A data e hora de
início da estratégia. data_handler - (Classe) Manipula o feed de dados
de mercado. execution_handler - (Classe) Manipula os pedidos/preenchimentos
para negociações. portfolio - (Classe) Mantém o controle das posições atuais e
anteriores do portfólio. strategy - (Classe) Gera sinais com base em dados de mercado.

"""

self.csv_dir = csv_dir
self.symbol_list = lista_de_símbolos
self.initial_capital = capital_inicial [Link] = pulsação
self.start_date = data_de_início

self.data_handler_cls = manipulador_de_dados
self.execution_handler_cls = manipulador_de_execução self.portfolio_cls
= portfólio self.strategy_cls = estratégia

[Link] = [Link]()

[Link] = 0
[Link] = 0 [Link]
= 0 self.num_strats =
1

self._generate_trading_instances()
O primeiro método, _generate_trading_instances, anexa todos os objetos de negociação (Data-
Handler, Strategy, Portfolio e ExecutionHandler) para vários membros internos:

# [Link]

def _generate_trading_instances(self):
"""

Gera os objetos de instância de negociação a partir de seus tipos


de classe.
"""

imprimir(
"Criando DataHandler, Estratégia, Portfólio e ExecutionHandler"

) self.data_handler = self.data_handler_cls([Link], self.csv_dir, self.symbol_list) [Link] =

self.strategy_cls(self.data_handler, [Link]) [Link] = self.portfolio_cls(self.data_handler,


[Link],
self.data_de_início,
self.capital_inicial)
self.execution_handler = self.execution_handler_cls([Link])
O método _run_backtest é onde o processamento de sinais do mecanismo de Backtest é realizado.
Conforme descrito acima, existem dois laços while, um aninhado dentro do outro. O externo monitora a
pulsação do sistema, enquanto o interno verifica se há um evento no objeto Queue e age sobre ele
chamando o método apropriado no objeto necessário.
Machine Translated by Google

154

Para um MarketEvent, o objeto Strategy é instruído a recalcular novos sinais, enquanto o objeto
Portfolio é instruído a reindexar o tempo. Se um objeto SignalEvent for recebido, o Portfolio é instruído a
manipular o novo sinal e convertê-lo em um conjunto de OrderEvents, se apropriado. Se um OrderEvent
for recebido, o ExecutionHandler recebe a ordem para ser transmitida à corretora (se estiver em um
ambiente de negociação real). Por fim, se um FillEvent for recebido, o Portfolio se atualizará para estar
ciente das novas posições:
# [Link]

def _run_backtest(self):
"""

Executa o backtest.
"""

i=0
enquanto Verdadeiro:
eu += 1
print i #
Atualiza as barras de mercado if
self.data_handler.continue_backtest == True: self.data_handler.update_bars()
else:

quebrar

# Manipule os eventos
enquanto True:
tente:
evento = [Link](False) exceto
[Link]: interrompa senão:
se o
evento
não for Nenhum: se [Link]
== 'MARKET':
[Link].calculate_signals(evento)
[Link].update_timeindex(evento)

elif [Link] == 'SINAL': [Link] += 1

[Link].update_signal(evento)

elif [Link] == 'ORDEM': [Link]


+= 1
self.execution_handler.execute_order(evento)

elif [Link] == 'PREENCHER':


[Link] += 1
[Link].update_fill(evento)

[Link]([Link] cardíaco)
Após a conclusão da simulação do backtest, o desempenho da estratégia pode ser exibido no
terminal/console. O DataFrame da curva de patrimônio do Pandas é criado e as estatísticas resumidas
são exibidas, bem como a contagem de Sinais, Ordens e Preenchimentos:
# [Link]

def _output_performance(self):
"""

Gera o desempenho da estratégia a partir do backtest.


Machine Translated by Google

155

"""

[Link].create_equity_curve_dataframe()

print("Criando estatísticas de resumo...")


stats = [Link].output_summary_stats()

print("Criando curva de patrimônio...")


print([Link].equity_curve.tail(10)) [Link](stats)

print("Sinais: %s" % [Link])


print("Pedidos: %s" % [Link])
print("Preenchimentos: %s" % [Link])
O último método a ser implementado é o simulate_trading. Ele simplesmente chama os dois métodos anteriores
métodos descritos, em ordem:

# [Link]

def simular_negociação(self):
"""

Simula o backtest e gera o desempenho do portfólio.


"""

self._run_backtest()
self._output_performance()
Isso conclui os objetos operacionais do backtester orientado a eventos.

14.3 Execução orientada a eventos

Acima, descrevemos uma classe ExecutionHandler básica que simplesmente cria uma instância FillEvent
correspondente para cada OrderEvent. Isso é exatamente o que precisamos para um backtest de "primeira
passagem", mas quando queremos realmente conectar o sistema a uma corretora, precisamos de um tratamento
mais sofisticado. Nesta seção, definimos o IBExecutionHandler, uma classe que nos permite interagir com a
popular API Interactive Brokers e, assim, automatizar nossa execução.
A ideia essencial da classe IBExecutionHandler é receber instâncias de OrderEvent da fila de eventos e executá-las
diretamente na API de ordens da Interactive Brokers usando a biblioteca de código aberto IbPy. A classe também manipulará
as mensagens de "Resposta do Servidor" enviadas de volta pela API. Nesta etapa, a única ação tomada será criar instâncias
de FillEvent correspondentes , que serão então enviadas de volta à fila de eventos.

A classe em si poderia se tornar bastante complexa, com lógica de otimização de execução e tratamento de
erros sofisticado. No entanto, optei por mantê-la relativamente simples aqui para que você possa entender as ideias
principais e expandi-la na direção que melhor se adapta ao seu estilo de negociação.

Como sempre, a primeira tarefa é criar o arquivo Python e importar as bibliotecas necessárias.
O arquivo é chamado ib_execution.py e fica no mesmo diretório que os outros arquivos controlados por eventos.
Importamos as bibliotecas de manipulação de data/hora necessárias, os objetos IbPy e os objetos Event
específicos que são manipulados pelo IBExecutionHandler:

#!/usr/bin/python # -*-
codificação: utf-8 -*-

# ib_execution.py

de __future__ importar função_de_impressão

importar data e
hora importar hora
Machine Translated by Google

156

de [Link] importar Contrato de [Link]


importar Pedido de [Link] importar
ibConnection, mensagem

do evento importar FillEvent, OrderEvent da execução


importar ExecutionHandler

Agora definimos a classe IBExecutionHandler . O construtor __init__ requer, primeiramente, conhecimento da fila de
eventos . Ele também requer a especificação de order_routing, que eu configurei como "SMART" por padrão. Se você tiver
requisitos específicos de troca, pode especificá-los aqui.
A moeda padrão também foi definida como dólar americano.
Dentro do método, criamos um dicionário fill_dict , necessário posteriormente para uso na geração de instâncias de
FillEvent . Também criamos um objeto de conexão tws_conn para armazenar nossas informações de conexão com a API da
Interactive Brokers. Também precisamos criar um order_id padrão inicial, que rastreia todos os pedidos subsequentes para
evitar duplicatas. Por fim, registramos os manipuladores de mensagens (que definiremos com mais detalhes a seguir):

# ib_execution.py

classe IBExecutionHandler(ExecutionHandler):
"""

Lida com a execução de ordens por meio da API da Interactive Brokers, para
uso em contas ao negociar diretamente ao vivo.

"""

def __init__(
self, eventos, roteamento de pedidos="SMART", moeda="USD"
):
"""

Inicializa a instância IBExecutionHandler.


"""

[Link] = eventos
self.order_routing = roteamento_de_pedidos
[Link] = moeda self.fill_dict =
{}

self.tws_conn = self.create_tws_connection() self.order_id =


self.create_initial_order_id() self.register_handlers()

A API do IB utiliza um sistema de eventos baseado em mensagens que permite que nossa classe responda de maneiras
específicas a determinadas mensagens, de maneira semelhante ao próprio backtester orientado a eventos. Não incluí nenhum
tratamento de erro real (para fins de brevidade), além da saída para o terminal, por meio do método _error_handler .

O método _reply_handler , por outro lado, é usado para determinar se uma instância de FillEvent precisa ser criada. O
método pergunta se uma mensagem "openOrder" foi recebida e verifica se uma entrada em nosso fill_dict para este orderId
específico já foi definida.
Se não, então um é criado.
Se ele vir uma mensagem "orderStatus" e essa mensagem específica indicar que um pedido foi atendido, ele chama
create_fill para criar um FillEvent. Ele também envia a mensagem para o terminal para fins de registro/depuração:

# ib_execution.py

def _error_handler(self, msg):


"""
Machine Translated by Google

157

Lida com a captura de mensagens de erro


"""

# Atualmente sem tratamento de erros.


print("Erro do servidor: %s" % msg)

def _reply_handler(self, msg):


"""

Alças de respostas do servidor


"""

# Manipular o processamento do orderId do


pedido aberto se [Link] == "openOrder" e \
[Link] == self.order_id e \ não
self.fill_dict.has_key([Link]): self.create_fill_dict_entry(msg)

# Lidar com
preenchimentos se [Link] == "orderStatus" e \
[Link] == "Preenchido" e \
self.fill_dict[[Link]]["preenchido"] == Falso: self.create_fill(msg)
print("Resposta do servidor:
%s, %s\n" % ([Link], msg))

O método a seguir, create_tws_connection, cria uma conexão com a API do IB usando o objeto ibConnection do
IbPy . Ele usa uma porta padrão de 7496 e um clientId padrão de 10. Após a criação do objeto, o método connect é
chamado para realizar a conexão:

# ib_execution.py

def create_tws_connection(self):
"""

Conecte-se à Trader Workstation (TWS) em execução na porta usual 7496, com um


clientId de 10.
O clientId é escolhido por nós e precisaremos de IDs separados para
a conexão de execução e a conexão de dados de mercado, se esta última for
usada em outro lugar.
"""

tws_conn = ibConnection()
tws_conn.connect() retornar
tws_conn

Para acompanhar ordens separadas (para fins de acompanhamento de preenchimentos), utilizamos o seguinte método:
create_initial_order_id . Defini como padrão "1", mas uma abordagem mais sofisticada seria consultar o IB para obter o ID
mais recente disponível e usá-lo. Você sempre pode redefinir o ID da ordem da API atual no painel Estação de Trabalho do
Trader > Configuração Global > Configurações da API:

# ib_execution.py

def create_initial_order_id(self):
"""

Cria o ID do pedido inicial usado pela Interactive Brokers para rastrear os


pedidos enviados.
"""

# Há espaço para mais lógica aqui, mas # usaremos "1" como


padrão por enquanto.
retornar 1

O método a seguir, register_handlers, simplesmente registra os métodos de tratamento de erros e respostas definidos
acima com a conexão TWS:

# ib_execution.py
Machine Translated by Google

158

def register_handlers(self):
"""

Registre as funções de tratamento de


mensagens de erro e de resposta do servidor.
"""

# Atribua a função de tratamento de erros definida acima # à conexão TWS


self.tws_conn.register(self._error_handler,
'Error')

# Atribua todas as mensagens de resposta do servidor à função #


reply_handler definida acima
self.tws_conn.registerAll(self._reply_handler)
Para efetivamente realizar uma transação, é necessário criar uma instância de Contrato IbPy e, em seguida, emparelhá-la
com uma instância de Ordem IbPy , que será enviada à API do IB. O método a seguir, create_contract, gera o primeiro
componente deste par. Ele espera um símbolo de ticker, um tipo de ativo (por exemplo, ação ou futuro), uma bolsa/bolsa
primária e uma moeda. Ele retorna a instância de Contrato :

# ib_execution.py

def create_contract(self, símbolo, tipo_sec, troca, troca_primitiva, corrente):


"""

Crie um objeto Contrato definindo o que será comprado, em qual


bolsa e em qual moeda.

símbolo - O símbolo de ação do contrato sec_type - O tipo de


segurança do contrato ('STK' é 'stock') exch - A bolsa para executar o contrato prim_exch - A bolsa
principal para executar o contrato curr - A moeda na qual comprar o
contrato

"""

contrato = Contrato()
contrato.m_symbol = símbolo
contrato.m_secType = tipo_sec
contrato.m_exchange = troca
contrato.m_primaryExch = troca_primária
contrato.m_currency = retorno corrente
contrato

O método a seguir, create_order, gera o segundo componente do par, a saber, a instância Order . Ele espera um tipo de
ordem (por exemplo, mercado ou limite), uma quantidade do ativo a ser negociada e uma "ação" (comprar ou vender). Ele
retorna a instância Order :

# ib_execution.py

def create_order(self, tipo_de_pedido, quantidade, ação):


"""

Crie um objeto Ordem (Mercado/Limite) para operar comprado/vendido.

order_type - 'MKT', 'LMT' para ordens de mercado ou limite quantity - Número


integral de ativos para ação da ordem - 'COMPRAR' ou 'VENDER'

"""

pedido = Pedido()
pedido.m_orderType = tipo_pedido
pedido.m_totalQuantity = quantidade
Machine Translated by Google

159

order.m_action = ação retornar


ordem

Para evitar a duplicação de instâncias de FillEvent para um ID de pedido específico, utilizamos um dicionário chamado
fill_dict para armazenar chaves que correspondem a IDs de pedido específicos. Quando um preenchimento é gerado, a chave
"filled" de uma entrada para um ID de pedido específico é definida como True. Se uma mensagem "Server Response"
subsequente for recebida do IB informando que um pedido foi preenchido (e for uma mensagem duplicada), isso não resultará
em um novo preenchimento. O método create_fill_dict_entry a seguir realiza isso:

# ib_execution.py

def create_fill_dict_entry(self, msg):


"""

Cria uma entrada no Dicionário de Preenchimento que lista os IDs de pedidos


e fornece informações de segurança. Isso é necessário para o comportamento
orientado a eventos das mensagens do servidor IB.

"""

self.fill_dict[[Link]] = {
"símbolo": [Link].m_symbol, "troca":
[Link].m_exchange, "direção": [Link].m_action,
"preenchido": Falso

O método a seguir, create_fill, na verdade cria a instância FillEvent e a coloca


na fila de eventos:
# ib_execution.py

def create_fill(self, msg):


"""

Lida com a criação do FillEvent que será colocado na fila de eventos após o
atendimento de um pedido.

"""

fd = self.fill_dict[[Link]]

# Prepare os dados de preenchimento


symbol = fd["symbol"] exchange
= fd["exchange"] filled = [Link]
direction = fd["direction"]
fill_cost = [Link]

# Crie um objeto de evento de preenchimento


fill =
FillEvent( [Link](), symbol,
exchange, filled, direction, fill_cost
)

# Certifique-se de que várias mensagens não criem # preenchimentos


adicionais.
self.fill_dict[[Link]]["filled"] = True

# Coloque o evento de preenchimento na fila de eventos


[Link](fill_event)
Machine Translated by Google

160

Agora que todos os métodos anteriores foram implementados, resta substituir o método execute_order da classe base
abstrata ExecutionHandler . Este método, na verdade, realiza a colocação do pedido com a API do IB.

Primeiro, verificamos se o evento recebido por este método é realmente um OrderEvent e, em seguida, preparamos os
objetos Contract e Order com seus respectivos parâmetros. Após a criação de ambos, o método IbPy placeOrder do objeto de
conexão é chamado com um order_id associado.

É extremamente importante chamar o método [Link](1) para garantir que o pedido realmente chegue ao IB. A remoção
desta linha leva a um comportamento inconsistente da API, pelo menos no meu sistema!

Por fim, incrementamos o ID do pedido para garantir que não duplicaremos pedidos:

# ib_execution.py

def execute_order(self, evento):


"""

Cria o objeto de pedido InteractiveBrokers necessário e o envia ao IB por meio de sua


API.

Os resultados são então consultados para gerar um objeto Fill correspondente,


que é colocado de volta na fila de eventos.

Parâmetros:
evento - Contém um objeto Evento com informações do pedido.
"""

se [Link] == 'ORDEM':
# Prepare os parâmetros para o pedido de ativo asset = [Link]
asset_type = "STK" order_type
= event.order_type quantity
= [Link] direction = [Link]

# Crie o contrato da Interactive Brokers por meio do # evento Order passado


ib_contract = self.create_contract(

ativo, tipo_de_ativo, roteamento_de_ordem_self,


roteamento_de_ordem_self, moeda_self
)

# Crie o pedido da Interactive Brokers por meio do # evento Order passado


ib_order = self.create_order(

tipo_de_pedido, quantidade, direção


)

# Use a conexão para enviar o pedido para IB


self.tws_conn.placeOrder( self.order_id,
ib_contract, ib_order
)

# NOTA: A linha a seguir é crucial.


# Garante que o pedido seja processado! [Link](1)

# Incrementa o ID do pedido para esta sessão


Machine Translated by Google

161

self.order_id += 1

Esta classe forma a base de um manipulador de execução da Interactive Brokers e pode ser usada no lugar do
manipulador de execução simulada, que é adequado apenas para backtesting. Antes que o manipulador IB possa ser utilizado,
no entanto, é necessário criar um manipulador de feed de mercado ativo para substituir o manipulador de feed de dados
históricos do sistema de backtester.
Desta forma estamos reutilizando o máximo possível dos sistemas de backtest e ao vivo para garantir
que a "troca" de código é minimizada e, portanto, o comportamento em ambos é semelhante, se não idêntico.
Machine Translated by Google

162
Machine Translated by Google

Capítulo 15

Implementação da Estratégia de Negociação

Neste capítulo, consideraremos a implementação completa de estratégias de negociação utilizando o sistema de


backtesting orientado a eventos mencionado anteriormente. Em particular, geraremos curvas de patrimônio líquido para
todas as estratégias de negociação utilizando valores nocionais de portfólio, simulando assim os conceitos de margem/
alavancagem, o que representa uma abordagem muito mais realista em comparação com abordagens vetorizadas/
baseadas em retornos.
O primeiro conjunto de estratégias pode ser executado com dados disponíveis gratuitamente, seja do Yahoo
Finance, Google Finance ou Quandl. Essas estratégias são adequadas para traders algorítmicos de longo prazo que
desejam estudar apenas o aspecto de geração de sinais de negociação da estratégia ou até mesmo o sistema completo
de ponta a ponta. Essas estratégias geralmente possuem índices de Sharpe menores, mas são muito mais fáceis de
implementar e executar.
A última estratégia é executada utilizando dados intradiários de ações. Esses dados geralmente não estão
disponíveis gratuitamente, e um fornecedor de dados comercial geralmente é necessário para fornecer dados em
qualidade e quantidade suficientes. Eu mesmo uso o DTN IQFeed para barras intradiárias. Essas estratégias geralmente
possuem índices de Sharpe muito maiores, mas exigem uma implementação mais sofisticada, pois a alta frequência
exige ampla automação.
Veremos que nossas duas primeiras tentativas de criar uma estratégia de negociação com base em dados
interdiários não foram totalmente bem-sucedidas. Pode ser desafiador elaborar uma estratégia de negociação lucrativa
com base em dados interdiários, uma vez considerados os custos de transação. Este último aspecto é algo que muitos
textos sobre negociação algorítmica tendem a omitir. No entanto, acredito que o maior número possível de fatores deve
ser adicionado ao backtest para minimizar surpresas futuras.
Além disso, este livro aborda principalmente como criar efetivamente um sistema de backtesting intradiário ou
interdiário realista (bem como uma plataforma de execução ao vivo) e menos estratégias individuais específicas. É
muito mais difícil criar um backtester robusto e realista do que encontrar estratégias de negociação na internet! Embora
as duas primeiras estratégias apresentadas não sejam particularmente atraentes, a última estratégia (com dados
intradiários) tem um bom desempenho e nos dá confiança no uso de dados de frequência mais alta.

15.1 Estratégia de Crossover de Média Móvel


Gosto bastante do sistema técnico de Crossover de Médias Móveis porque é a primeira estratégia não trivial
extremamente útil para testar uma nova implementação de backtesting. Em um período diário, ao longo de vários anos,
com longos períodos de análise retrospectiva, poucos sinais são gerados em uma única ação e, portanto, é fácil
verificar manualmente se o sistema está se comportando conforme o esperado.

Para realmente gerar tal simulação com base no código de backtesting anterior, precisamos subclassificar o objeto
Strategy , conforme descrito no capítulo anterior, para criar o objeto MovingAverageCrossStrategy , que conterá a lógica das médias
móveis simples e a geração de sinais de negociação.

Além disso, precisamos criar a função __main__ que carregará o objeto Backtest e encapsulará a execução do
programa. O arquivo a seguir, [Link], contém ambos os objetos.

163
Machine Translated by Google

164

A primeira tarefa, como sempre, é importar corretamente os componentes necessários. Estamos importando
quase todos os objetos descritos no capítulo anterior:

#!/usr/bin/python # -*-
codificação: utf-8 -*-

# [Link]

de __future__ importar função_de_impressão

importar data e hora

importar numpy como


np importar pandas
como pd importar [Link] como sm

da estratégia importar Estratégia da


importação de evento SignalEvent da
importação de backtest Backtest da
importação de dados HistoricCSVDataHandler da importação
de execução SimulatedExecutionHandler da importação de portfólio
Portfólio
Agora, vamos criar a estratégia MovingAverageCrossStrategy. A estratégia requer o DataHandler de barras, a
Fila de Eventos de eventos e os períodos de lookback para as médias móveis simples que serão empregadas na
estratégia. Escolhi 100 e 400 como os períodos de lookback "curto" e "longo" para esta estratégia.

O atributo final, " comprado", é usado para informar à estratégia quando o backtest está realmente "no mercado".
Sinais de entrada são gerados apenas se for "SAÍDA" e sinais de saída são gerados apenas se for "COMPRA" ou
"CURTA":

# [Link]

classe MovingAverageCrossStrategy(Estratégia):
"""

Executa uma estratégia básica de Crossover de Média Móvel com uma média móvel
ponderada simples de curto/longo prazo. As janelas padrão de curto/longo prazo são de
100/400 períodos, respectivamente.
"""

def __init__(
self, barras, eventos, janela_curta=100, janela_longa=400
):
"""

Inicializa a estratégia de cruzamento de média móvel.

Parâmetros: bars
- O objeto DataHandler que fornece informações sobre barras events - O objeto Event Queue.
short_window - O lookback da média móvel curta.
long_window - O lookback da média móvel longa.

"""

[Link] = barras
self.symbol_list = [Link].symbol_list [Link] = eventos
self.short_window =
janela_curta self.long_window = janela_longa
Machine Translated by Google

165

# Defina como Verdadeiro se um símbolo estiver no mercado


[Link] = self._calculate_initial_bought()

Como a estratégia começa fora do mercado, definimos o valor inicial "comprado" como "OUT", para cada símbolo:

# [Link]

def _calculate_initial_bought(self):
"""

Adiciona chaves ao dicionário comprado para todos os símbolos e os define


como 'OUT'.
"""

comprou = {}
para s em self.symbol_list: comprou[s]
= 'OUT' retornar comprado

O núcleo da estratégia é o método calculate_signals . Ele reage a um objeto MarketEvent e, para cada símbolo
negociado, obtém os últimos N preços de fechamento de barras, onde N é igual ao maior período de lookback.

Em seguida, calcula as médias móveis simples de curto e longo prazo. A regra da estratégia é entrar no mercado (operar
comprado em uma ação) quando o valor da média móvel de curto prazo excede o valor da média móvel de longo prazo. Por
outro lado, se o valor da média móvel de longo prazo exceder o valor da média móvel de curto prazo, a estratégia é instruída
a sair do mercado.
Essa lógica é tratada colocando um objeto SignalEvent na Fila de Eventos em cada uma das respectivas situações e,
em seguida, atualizando o atributo "comprado" (por símbolo) para "LONGO" ou "SAÍDA", respectivamente. Como esta é uma
estratégia somente de compra, não consideraremos posições "VENDIDAS":

# [Link]

def calculate_signals(self, evento):


"""

Gera um novo conjunto de sinais com base no MAC SMA com a janela
curta cruzando a janela longa, o que significa uma entrada longa e vice-versa
para uma entrada curta.

Parâmetros
evento - Um objeto MarketEvent.
"""

se [Link] == 'MERCADO':
para s em self.symbol_list:
barras =
[Link].obter_valores_das_barras_mais_últimas(s, "adj_close", N=self.long_window

) bar_date = [Link].get_latest_bar_datetime(s) se bars não for


Nenhum e bars != []:
short_sma = [Link](barras[-self.janela_curta:]) long_sma =
[Link](barras[-self.janela_longa:])

símbolo = s dt
= [Link]() sig_dir =
""

se short_sma > long_sma e [Link][s] == "OUT": print("LONG: %s" %


bar_date) sig_dir = 'LONG'
Machine Translated by Google

166

sinal = SignalEvent(1, símbolo, dt, sig_dir, 1.0)


[Link](sinal)
[Link][s] = 'LONGO'
elif short_sma < long_sma e [Link][s] == "LONGO":
print("RESUMIDO: %s" % bar_date)
sig_dir = 'SAÍDA'
sinal = SignalEvent(1, símbolo, dt, sig_dir, 1.0)
[Link](sinal)
[Link][s] = 'FORA'

Isso conclui a implementação do objeto MovingAverageCrossStrategy . A tarefa final


de todo o sistema de backtesting é preencher um método __main__ em [Link] para realmente executar
o backtest.
Primeiro, certifique-se de alterar o valor de csv_dir para o caminho absoluto do seu arquivo CSV
diretório para os dados financeiros. Você também precisará baixar o arquivo CSV das ações da AAPL
(do Yahoo Finance), que é fornecido pelo seguinte link (de 1º de janeiro de 1990 a 1º de janeiro de 2002),
já que este é o estoque em que testaremos a estratégia:
[Link]
&f=2002&g=d&ignore=.csv
Certifique-se de colocar este arquivo no caminho apontado pela função principal em csv_dir.
A função __main__ simplesmente instancia um novo objeto de backtest e então chama a simulação
método late_trading para executá-lo:

# [Link]

se __nome__ == "__principal__":
csv_dir = '/caminho/para/seu/arquivo/csv' # MUDE ISTO!
lista_de_símbolos = ['AAPL']
capital_inicial = 100000,0
batimento cardíaco = 0,0
data_de_início = data/[Link]/hora(1990, 1, 1, 0, 0, 0)

backtest = Teste retrospectivo(


csv_dir, lista_de_símbolos, capital_inicial, pulsação,
data_de_início, HistoricCSVDataHandler, SimulatedExecutionHandler,
Portfólio, Estratégia de Média Móvel Cruzada
)
backtest.simulate_trading()

Para executar o código, certifique-se de já ter configurado um ambiente Python (conforme descrito em
capítulos anteriores) e então navegue até o diretório onde seu código está armazenado. Você deve
simplesmente ser capaz de executar:

python [Link]

Você verá a seguinte listagem (truncada devido à impressão da contagem de barras!):

..
..
3029
3030
Criando estatísticas resumidas...
Criando curva de patrimônio...
Comissão em dinheiro da AAPL, retornos totais, curva de patrimônio, redução
data e hora
2001-12-18 0 99211 0 13 99211 13 0 0,99211 0,025383
2001-12-19 99211 0 99211 13 0 0,99211 0,025383
2001-12-20 99211 0 99211 13 0 0,99211 0,025383
2001-12-21 99211 99211 0 0,99211 0,025383
Machine Translated by Google

167

2001-12-24 0 99211 2001-12-26 0 13 99211 13 0 0,99211 0,025383


99211 2001-12-27 0 99211 2001-12-28 99211 13 0 0,99211 0,025383
0 99211 2001-12-31 0 99211 99211 13 0 0,99211 0,025383
2001-12-31 0 99211 [('Retorno Total', 99211 13 0 0,99211 0,025383
'-0,79%'), 99211 13 0 0,99211 0,025383
99211 0 0,99211 0,025383

('Índice de Sharpe', '-0,09'),


('Redução Máxima', '2,56%'),
('Duração do saque', '2312')]
Sinais: 10
Pedidos: 10
Preenchimentos: 10

O desempenho desta estratégia pode ser visto na Fig 15.1:

Figura 15.1: Curva de patrimônio, retornos diários e reduções para o cruzamento da média móvel
estratégia

Evidentemente, os retornos e o Índice de Sharpe não são estelares para as ações da AAPL neste conjunto específico
de indicadores técnicos! É claro que temos algum trabalho a fazer no próximo conjunto de estratégias para encontrar um
sistema que pode gerar desempenho positivo.
Machine Translated by Google

168

15.2 Previsão de negociação do S&P500


Nesta seção, consideraremos uma estratégia de negociação construída com base no mecanismo de previsão discutido
nos capítulos anteriores. Tentaremos operar com base nas previsões feitas por um analista do mercado de ações.
Tentaremos prever o SPY, o ETF que acompanha o valor do S&P 500. Em última análise, queremos responder à
pergunta se um algoritmo básico de previsão usando dados de preços defasados, com desempenho preditivo leve, nos
oferece alguma vantagem em relação a uma estratégia de comprar e manter.

As regras para esta estratégia são as seguintes:

1. Ajuste um modelo de previsão a um subconjunto de dados do S&P 500. Pode ser uma Regressão Logística, um
Analisador Discriminante (Linear ou Quadrático), uma Máquina de Vetores de Suporte ou uma Floresta Aleatória.
O procedimento para isso foi descrito no capítulo sobre Previsão.

2. Use dois dados de retornos de fechamento ajustados de defasagem anteriores como preditor para os retornos
de amanhã. Se os retornos forem previstos como positivos, opere comprado. Se os retornos forem previstos
como negativos, opere fora. Não consideraremos a venda a descoberto para esta estratégia específica.

Implementação

Para esta estratégia, vamos criar o arquivo snp_forecast.py e importar as seguintes bibliotecas necessárias:

#!/usr/bin/python #
-*- codificação: utf-8 -*-

# snp_forecast.py

de __future__ importar função_de_impressão

importar data e hora

importar pandas como


pd de [Link] importar QDA

da estratégia importar Estratégia da


importação de evento SignalEvent
da importação de backtest Backtest
da importação de dados HistoricCSVDataHandler
da importação de execução SimulatedExecutionHandler da
importação de portfólio Portfólio da
importação de create_lagged_series create_lagged_series
Importamos o Pandas e o Scikit-Learn para realizar o procedimento de ajuste do modelo de classificador
supervisionado. Também importamos as classes necessárias do backtester orientado a eventos. Por fim, importamos
a função create_lagged_series , que usamos no capítulo sobre Previsão.

O próximo passo é criar a SPYDailyForecastStrategy como uma subclasse da classe base abstrata Strategy .
Como "codificaremos" os parâmetros da estratégia diretamente na classe, para simplificar, os únicos parâmetros
necessários para o construtor __init__ são o manipulador de dados bars e a fila de eventos .

Definimos as datas de início/término/teste self.model_*** como objetos datetime e, em seguida, informamos à


classe que estamos fora do mercado (self.long_market = False). Por fim, definimos [Link] como o modelo
treinado do create_symbol_forecast_model abaixo:

# snp_forecast.py

classe SPYDailyForecastStrategy(Estratégia):
"""
Machine Translated by Google

169

Estratégia de previsão do S&P500. Ela utiliza um Analisador Discriminante Quadrático


para prever os retornos de um período subsequente e, em seguida, gera sinais
de compra/saída com base na previsão.

"""

def __init__(self, barras, eventos): [Link] =


barras
self.lista_de_símbolos = [Link].lista_de_símbolos
[Link] = eventos
self.datetime_now = [Link]()

self.model_start_date = data/[Link]/hora(2001,1,10) self.model_end_date =


data/[Link]/hora(2005,12,31) self.model_start_test_date = data/[Link]/
hora(2005,1,1)

self.long_market = Falso
self.short_market = Falso self.bar_index
=0

[Link] = self.criar_modelo_de_previsão_de_símbolo()

Aqui definimos o create_symbol_forecast_model. Ele essencialmente chama o create_lagged_series


função, que produz um Pandas DataFrame com cinco defasagens de retornos diários para cada preditor atual.
Em seguida, consideramos apenas as duas defasagens mais recentes. Isso ocorre porque estamos tomando a
decisão de modelagem de que o poder preditivo de defasagens anteriores provavelmente será mínimo.
Nesta etapa, criamos os dados de treinamento e teste, sendo que estes últimos podem ser usados para
testar nosso modelo, se desejarmos. Optei por não gerar os dados de teste, pois já havíamos treinado o modelo
anteriormente no capítulo sobre Previsão. Por fim, ajustamos os dados de treinamento ao Analisador
Discriminante Quadrático e, em seguida, retornamos o modelo.
Observe que poderíamos facilmente substituir o modelo por uma Floresta Aleatória, Máquina de Vetores de
Suporte ou Regressão Logística, por exemplo. Tudo o que precisamos fazer é importar a biblioteca correta do
Scikit-Learn e simplesmente substituir a linha model = QDA() :

# snp_forecast.py

def create_symbol_forecast_model(self): # Cria uma série


defasada do índice de mercado de ações dos EUA S&P500 snpret =
create_lagged_series( self.symbol_list[0],
self.model_start_date, self.model_end_date, lags=5

# Use os dois dias anteriores de retornos como valores preditores, com a


direção como resposta
X = snpret[["Lag1","Lag2"]] y =
snpret["Direção"]

# Crie conjuntos de treinamento e teste


start_test = self.model_start_test_date X_train = X[[Link]
< start_test]
X_teste = X[[Link] >= iniciar_teste] y_train =
y[[Link] < iniciar_teste] y_teste = y[[Link] >=
iniciar_teste]

modelo = QDA()
[Link](X_train, y_train) retornar
modelo
Machine Translated by Google

170

Nesta etapa, estamos prontos para substituir o método calculate_signals da classe base Strategy . Primeiro,
calculamos alguns parâmetros de conveniência que entram em nosso objeto SignalEvent e, em seguida, geramos um
conjunto de sinais somente se recebermos um objeto MarketEvent (uma verificação básica de integridade).

Aguardamos cinco barras (ou seja, cinco dias nesta estratégia!) e, em seguida, obtemos os valores dos retornos
defasados. Em seguida, encapsulamos esses valores em uma Série Pandas para que o método de previsão do modelo
funcione corretamente. Em seguida, calculamos uma previsão, que se manifesta como +1 ou -1.

Se a previsão for +1 e ainda não estivermos comprados no mercado, criamos um SignalEvent para operar
comprado e informar à turma que estamos no mercado. Se a previsão for -1 e estivermos comprados no mercado,
simplesmente saímos do mercado:

# snp_forecast.py

def calculate_signals(self, evento):


"""

Calcule os SignalEvents com base em dados de mercado.


"""

sym = self.symbol_list[0] dt =
self.datetime_now

se [Link] == 'MERCADO':
self.bar_index += 1 se
self.bar_index > 5:
lags = [Link].get_latest_bars_values(self.symbol_list[0],
"retornos", N=3

) pred_series = [Link](
{
'Lag1': atrasos[1]*100,0, 'Lag2':
atrasos[2]*100,0
}

) pred = [Link](pred_series) se pred > 0 e não


self.long_market: self.long_market = True signal =
SignalEvent(1, sym, dt, 'LONG', 1.0)
[Link](signal)

se pred < 0 e self.long_market: self.long_market =


Falso sinal = SignalEvent(1, sym, dt,
'EXIT', 1.0) [Link](sinal)

Para executar a estratégia, você precisará baixar um arquivo CSV do Yahoo Finance para SPY e colocá-lo em um
diretório adequado (observe que você precisará alterar o caminho abaixo!). Em seguida, encerramos o backtest por
meio da classe Backtest e realizamos o teste chamando simulate_trading:

# snp_forecast.py

se __nome__ == "__principal__":
csv_dir = '/caminho/para/seu/arquivo/csv' # ALTERE ISTO! symbol_list =
['SPY'] initial_capital = 100000.0
heartbeat = 0.0 start_date =
[Link](2006,1,3)

backtest = Teste retrospectivo(


Machine Translated by Google

171

csv_dir, lista_de_símbolos, capital_inicial, pulsação,


data_de_início, HistoricCSVDataHandler, SimulatedExecutionHandler,
Portfólio, SPYDailyForecastStrategy
)
backtest.simulate_trading()

O resultado da estratégia é o seguinte e é líquido de custos de transação:

..
..
2209
2210
Criando estatísticas resumidas...
Criando curva de patrimônio...
ESPIÃO comissão em dinheiro curva_de_patrimônio_de_retornos_totais \
data e hora
2014-09-29 19754 90563.3 2014-09-30 349,7 110317,3 -0,000326 349,7 1.103173
19702 90563.3 2014-10-01 19435 90563.3 110265,3 -0,000471 349,7 109998,3 1.102653
2014-10-02 19438 90563.3 2014-10-03 -0,002421 349,7 110001,3 0,000027 1.099983
19652 90563.3 2014-10-06 19629 90563.3 349,7 110215,3 0,001945 349,7 1.100013
2014-10-07 19326 90563.3 2014-10-08 110192,3 -0,000209 349,7 109889,3 1.102153
19664 90563.3 2014-10-09 19274 90563.3 -0,002750 349,7 110227,3 0,003076 1.101923
2014-10-09 0 109836.0 349,7 109837,3 -0,003538 351,0 1.098893
109836,0 -0,000012 1.102273
1.098373
1.098360

rebaixamento
data e hora
29/09/2014 0,003340
2014-09-30 0,003860
2014-10-01 0,006530
2014-10-02 0,006500
2014-10-03 0,004360
2014-10-06 0,004590
2014-10-07 0,007620
2014-10-08 0,004240
2014-10-09 0,008140
2014-10-09 0,008153
[('Retorno Total', '9,84%'),
('Índice de Sharpe', '0,54'),
('Redução Máxima', '5,99%'),
('Duração do saque', '811')]
Sinais: 270
Pedidos: 270
Preenchimentos: 270

A visualização a seguir na Fig. 15.2 mostra a Curva de Patrimônio Líquido, os Retornos Diários e o
Redução da estratégia em função do tempo:
Note imediatamente que o desempenho não é ótimo! Temos um Índice de Sharpe < 1, mas um
redução razoável de pouco menos de 6%. Acontece que se tivéssemos simplesmente comprado e mantido SPY
nesse período teríamos tido um desempenho semelhante, ainda que um pouco pior.
Portanto, na verdade, não ganhamos muito com nossa estratégia preditiva uma vez que a transação
Os custos estão incluídos. Eu queria incluir este exemplo especificamente porque ele usa um "fim para
fim da implementação realista de uma estratégia que leve em conta as políticas conservadoras e realistas
custos de transação. Como se pode ver, não é fácil fazer uma previsão com base em dados diários
que produz um bom desempenho!
Machine Translated by Google

172

Figura 15.2: Curva de patrimônio, retornos diários e reduções para a estratégia de previsão SPY

Nossa estratégia final fará uso de outras séries temporais e de uma frequência mais alta. Veremos que
o desempenho pode ser melhorado drasticamente após modificar certos aspectos do sistema.

15.3 Negociação de pares de ações com reversão à média

Para buscar índices de Sharpe mais altos em nossas negociações, precisamos considerar estratégias intradiárias de
maior frequência.
O primeiro grande problema é que a obtenção de dados é significativamente mais difícil, pois dados intradiários de
alta qualidade geralmente não são gratuitos. Como mencionado acima, eu uso o DTN IQFeed para barras intradiárias
minuciosas e, portanto, você precisará de sua própria conta DTN para obter os dados necessários para esta estratégia.
O segundo problema é que as simulações de backtesting levam consideravelmente mais tempo, especialmente com
o modelo orientado a eventos que construímos aqui. Assim que começamos a considerar um backtest de um portfólio
diversificado de dados minuciosos abrangendo anos e, em seguida, realizamos qualquer otimização de parâmetros,
rapidamente percebemos que as simulações podem levar horas ou até dias para serem calculadas em um PC desktop
moderno. Isso precisará ser levado em consideração em seu processo de pesquisa.
O terceiro problema é que a execução ao vivo agora precisará ser totalmente automatizada, visto que estamos nos
aproximando de negociações de frequência mais alta. Isso significa que tais ambientes de execução e código devem ser
altamente confiáveis e livres de bugs, caso contrário, podem ocorrer perdas significativas.
Esta estratégia expande a estratégia interdiária anterior para fazer uso de dados intradiários.
Em particular, usaremos barras de OHLCV minuciosas, em vez de barras de OHLCV diárias.
As regras da estratégia são simples:

1. Identifique um par de ações que possuam uma série temporal de resíduos estatisticamente identificada como
revertida à média. Neste caso, encontrei duas ações do setor de energia dos EUA com os códigos AREX e WLL.
Machine Translated by Google

173

2. Crie a série temporal de resíduos do par realizando uma regressão linear contínua, para uma janela de lookback
específica, por meio do algoritmo de mínimos quadrados ordinários (MQO). Este período de lookback é um parâmetro
a ser otimizado.

3. Crie um z-score contínuo das séries temporais de resíduos do mesmo período de retrospectiva e use isso para
determinar limites de entrada/saída para sinais de negociação.

4. Se o limite superior for excedido quando não estiver no mercado, entre no mercado (comprado ou vendido, dependendo
da direção do excesso do limite). Se o limite inferior for excedido quando estiver no mercado, saia do mercado.
Novamente, os limites superior e inferior são parâmetros a serem otimizados.

De fato, poderíamos ter usado o teste Dickey-Fuller Aumentado Cointegrado (CADF) para identificar um parâmetro de
hedge ainda mais preciso. Isso seria uma extensão interessante da estratégia.

Implementação

O primeiro passo, como sempre, é importar as bibliotecas necessárias. Precisamos do pandas para o método rolling_apply ,
que é usado para aplicar o cálculo do z-score com uma janela de lookback de forma contínua. Importamos o statsmodels
porque ele fornece um meio de calcular o algoritmo de mínimos quadrados ordinários (MQO) para a regressão linear,
necessário para obter a taxa de hedge para a construção dos resíduos.

Também precisamos de um DataHandler e um Portfolio ligeiramente modificados para realizar negociações de barras
minuciosas com os dados do DTN IQFeed. Para criar esses arquivos, você pode simplesmente copiar todo o código em
[Link] e [Link] para os novos arquivos hft_portfolio.py e hft_data.py, respectivamente, e então modificar as seções
necessárias, que descreverei a seguir.
Aqui está a listagem de importação para intraday_mr.py:

#!/usr/bin/python #
-*- codificação: utf-8 -*-

# intraday_mr.py

de __future__ importar função_de_impressão

importar data e hora

importar numpy como


np importar pandas
como pd importar [Link] como sm

da estratégia importar Estratégia do


evento importar SignalEvent do
backtest importar Backtest de
hft_data importar HistoricCSVDataHandlerHFT do
hft_portfolio importar PortfolioHFT da execução
importar SimulatedExecutionHandler

No snippet a seguir, criamos a classe IntradayOLSMRStrategy derivada da classe base abstrata Strategy . O método
construtor __init__ requer acesso ao provedor de dados históricos de barras , à fila de eventos e a um limite zscore_low e
um limite zscore_high , usados para determinar quando a série residual entre os dois pares está revertendo à média.

Além disso, especificamos a janela de lookback do MCO (definida como 100 aqui), que é um parâmetro sujeito a potencial
otimização. No início da simulação, não estamos nem comprados nem vendidos no mercado, então definimos self.long_market
e self.short_market como False:

# intraday_mr.py
Machine Translated by Google

174

classe IntradayOLSMRStrategy(Estratégia):
"""

Utiliza mínimos quadrados ordinários (MQO) para executar uma regressão linear contínua para
determinar a taxa de hedge entre um par de ações.
O z-score da série temporal de resíduos é então calculado de forma contínua e, se exceder um
intervalo de limites (padrão [0,5, 3,0]), um par de sinais longo/curto é gerado (para o limite
alto) ou um par de sinais de saída é gerado (para o limite baixo).

"""

def __init__( self,


barras, eventos, ols_window=100, zscore_low=0,5,
zscore_high=3,0
):
"""

Inicializa a estratégia stat arb.

Parâmetros:
barras - O objeto DataHandler que fornece informações sobre barras eventos - O objeto Fila de
Eventos.
"""

[Link] = barras
self.symbol_list = [Link].symbol_list [Link] = eventos
self.ols_window = ols_window
self.zscore_low = zscore_low
self.zscore_high = zscore_high

[Link] = ('AREX', 'WLL') [Link] =


[Link]()

self.long_market = Falso
self.short_market = Falso
O método a seguir, calculate_xy_signals, pega o zscore atual (do cálculo contínuo realizado abaixo) e
determina se novos sinais de negociação precisam ser gerados.
Esses sinais são então retornados.
Há quatro estados potenciais nos quais podemos estar interessados. Eles são:

1. Longo do mercado e abaixo do limite superior do zscore negativo

2. Longo do mercado e entre o valor absoluto do limite inferior do zscore

3. Venda a descoberto no mercado e acima do limite superior do zscore positivo

4. Venda a descoberto no mercado e entre o valor absoluto do limite inferior do zscore

Em ambos os casos, é necessário gerar dois sinais, um para o primeiro componente do par (AREX) e outro
para o segundo componente do par (WLL). Se nenhuma dessas condições for atendida, um par de valores
"None" será retornado:

# intraday_mr.py

def calcular_sinais_xy(self, zscore_last):


"""

Calcula os pares de sinais x, y reais a serem enviados ao gerador


de sinais.
Machine Translated by Google

175

Parâmetros
zscore_last - O zscore atual para testar
"""

y_signal = Nenhum
x_signal = Nenhum p0
= [Link][0] p1 =
[Link][1] dt = [Link]
hr = abs(self.hedge_ratio)

# Se estivermos comprados no mercado e abaixo do # negativo


do limite alto do zscore se zscore_last <= -self.zscore_high e
não self.long_market: self.long_market = True y_signal = SignalEvent(1, p0, dt, 'LONG', 1.0)
x_signal = SignalEvent(1, p1, dt,
'SHORT', hr)

# Se estivermos comprados no mercado e entre o # valor absoluto


do limite inferior do zscore se abs(zscore_last) <= self.zscore_low e
self.long_market: self.long_market = False y_signal = SignalEvent(1, p0, dt, 'EXIT', 1.0) x_signal
= SignalEvent(1, p1, dt, 'EXIT', 1.0)

# Se estivermos vendidos no mercado e acima # do limite


alto do zscore se zscore_last >=
self.zscore_high e não self.short_market:
self.short_market = True y_signal =
SignalEvent(1, p0, dt, 'SHORT', 1.0) x_signal = SignalEvent(1, p1, dt,
'LONG', hr)

# Se estivermos vendidos no mercado e entre o # valor absoluto do


limite inferior do zscore se abs(zscore_last) <= self.zscore_low e
self.short_market:
self.short_market = Falso y_signal =

SignalEvent(1, p0, dt, 'SAÍDA', 1.0) x_signal = SignalEvent(1, p1, dt, 'SAÍDA', 1.0)

retornar sinal y, sinal x

O método a seguir, calculate_signals_for_pairs, obtém o conjunto mais recente de barras para cada componente do
par (neste caso, 100 barras) e as utiliza para construir uma regressão linear baseada em mínimos quadrados ordinários. Isso
permite a identificação da taxa de hedge, necessária para a construção da série temporal dos resíduos.

Uma vez construída a taxa de hedge, uma série de resíduos é construída. O próximo passo é calcular o z-score mais
recente da série de resíduos, subtraindo sua média e dividindo pelo seu desvio padrão ao longo do período de lookback.

Finalmente, o sinal y e o sinal x são calculados com base neste zscore. Se os sinais
não forem ambos None, então as instâncias SignalEvent são enviadas de volta para a fila de eventos :

# intraday_mr.py

def calcular_sinais_para_pares(self):
"""

Gera um novo conjunto de sinais com base na estratégia de reversão à média.


Machine Translated by Google

176

Calcula a taxa de hedge entre o par de tickers.


Usamos OLS para isso, embora o ideal fosse usar CADF.
"""

# Obtenha a última janela de valores para cada # componente do par


de tickers y = [Link].get_latest_bars_values(

[Link][0], "fechar", N=self.ols_window

) x = [Link].obter_valores_das_barras_mais_recente(
[Link][1], "fechar", N=self.ols_window
)

se y não for Nenhum e x não for Nenhum: # Verifique


se todos os períodos de janela estão disponíveis se len(y) >=
self.ols_window e len(x) >= self.ols_window: # Calcule a taxa de hedge atual usando OLS
self.hedge_ratio = [Link](y, x).fit().params[0]

# Calcular o z-score atual do spread residual = y - self.hedge_ratio * x


zscore_last = ((spread - [Link]())/[Link]())
[-1]

# Calcular sinais e adicionar à fila de eventos y_signal, x_signal =


self.calculate_xy_signals(zscore_last) se y_signal não for None e x_signal não for None:

[Link](sinal_y)
[Link](sinal_x)

O método final, calculate_signals , é substituído pela classe base e é usado para verificar se um evento recebido
da fila é realmente um MarketEvent, caso em que o cálculo dos novos sinais é realizado:

# intraday_mr.py

def calculate_signals(self, evento):


"""

Calcule os SignalEvents com base em dados de mercado.


"""

se [Link] == 'MERCADO':
self.calculate_signals_for_pairs()

A seção __main__ reúne os componentes para produzir um backtest para a estratégia.


Informamos à simulação onde os dados minuciosos do ticker são armazenados. Estou usando o formato DTN IQFeed.
Trunquei ambos os arquivos para que começassem e terminassem no mesmo minuto. Para este par específico de
AREX e WLL, a data de início comum é 8 de novembro de 2007, às 10h41.
Por fim, construímos o objeto de backtest e começamos a simular a negociação:

# intraday_mr.py

se __nome__ == "__principal__":
csv_dir = '/caminho/para/seu/arquivo/csv' # ALTERE ISTO! symbol_list =
['AREX', 'WLL'] initial_capital = 100000.0
heartbeat = 0.0 start_date =
[Link](2007,
11, 8, 10, 41, 0)

backtest = Backtest(csv_dir,
lista_de_símbolos, capital_inicial, pulsação,
Machine Translated by Google

177

data_de_início, HistoricCSVDataHandlerHFT, SimulatedExecutionHandler,


PortfólioHFT, IntradayOLSMRStratégia

) backtest.simulate_trading()

No entanto, antes de podermos executar este arquivo, precisamos fazer algumas modificações nos dados
objetos manipuladores e de portfólio.
Em particular, é necessário criar novos arquivos hft_data.py e hft_portfolio.py , que são cópias de [Link] e
[Link], respectivamente.
Em hft_data.py, precisamos renomear HistoricCSVDataHandler para HistoricCSVDataHandlerHFT e substituir a lista
de nomes no método _open_convert_csv_files .
A linha antiga é:

nomes=[ 'data e hora', 'aberto', 'alto',


'baixo', 'fechar', 'volume', 'ajuste_fechamento'
]

Isto deve ser substituído por:

nomes=[ 'data e hora', 'aberto', 'baixo',


'alto', 'fechar', 'volume', 'oi'
]

Isso é para garantir que o novo formato do DTN IQFeed funcione com o backtester.
A outra alteração é renomear Portfolio para PortfolioHFT em hft_portfolio.py. Precisamos então modificar
algumas linhas para levar em conta a frequência minuciosa dos dados DTN.
Em particular, dentro do método update_timeindex , devemos alterar o seguinte código:

para s em self.symbol_list: #
Aproximação ao valor real market_value =
self.current_positions[s] * \
[Link].get_latest_bar_value(s, "adj_close") dh[s] =
valor_de_mercado
dh['total'] += valor_de_mercado
Para:

para s em self.symbol_list: #
Aproximação ao valor real market_value =
self.current_positions[s] * \
[Link].get_latest_bar_value(s, "fechar") dh[s] =
valor_de_mercado
dh['total'] += valor_de_mercado

Isso garante que obtenhamos o preço de fechamento , em vez do preço adj_close . Este último é para o Yahoo
Finanças, enquanto o primeiro é para o DTN IQFeed.
Também precisamos fazer um ajuste semelhante em update_holdings_from_fill. Precisamos alterar o seguinte
código:

# Atualizar lista de acervos com novas quantidades


fill_cost = [Link].get_latest_bar_value( [Link],
"adj_close"
)
Para:

# Atualizar lista de acervos com novas quantidades


fill_cost = [Link].get_latest_bar_value(
símbolo de preenchimento, "fechar"
)
Machine Translated by Google

178

A alteração final ocorre no método output_summary_stats na parte inferior do arquivo.


Precisamos modificar a forma como o Índice de Sharpe é calculado para levar em conta negociações minuciosas.
A linha seguinte:

sharpe_ratio = create_sharpe_ratio(retorna)

Deve ser alterado para:

sharpe_ratio = create_sharpe_ratio(retorna, períodos=252*6,5*60)

Isso conclui as alterações necessárias. Ao executar intraday_mr.py , obtemos o seguinte:


saída seguinte (truncada) da simulação de backtest:
..
..
375072
375073
Criando estatísticas resumidas...
Criando curva de patrimônio...
AREX WLL comissão em dinheiro retornos totais \
data e hora
2014-03-11 [Link] 2098 -6802 120604.3 9721,4 115900,3 -0,000052
2014-03-11 [Link] 2101 -6799 120604.3 9721,4 115906,3 0,000052
2014-03-11 [Link] 2100 -6802 120604.3 9721,4 115902,3 -0,000035
2014-03-11 [Link] 2097 -6810 120604.3 9721,4 115891,3 -0,000095
2014-03-11 [Link] 2098 -6801 120604.3 9721,4 115901,3 0,000086
2014-03-11 [Link] 2098 -6800 120604.3 9721,4 115902,3 0,000009
2014-03-11 [Link] 2099 -6800 120604.3 2014-03-11 [Link] 9721,4 115903,3 0,000009
2100 -6801 120604.3 2014-03-11 [Link] 2100 -6801 120604.3 9721,4 115903,3 0,000000
2014-03-11 [Link] 2100 -6801 120604.3 9721,4 115903,3 0,000000
9721,4 115903,3 0,000000

redução da curva de equidade


data e hora
2014-03-11 [Link] 2014-03-11 1,159003 0,003933
[Link] 2014-03-11 [Link] 1,159063 0,003873
2014-03-11 [Link] 2014-03-11 1,159023 0,003913
[Link] 2014-03-11 [Link] 1,158913 0,004023
2014-03-11 [Link] 2014-03-11 1,159013 0,003923
[Link] 2014-03-11 [Link] 1,159023 0,003913
2014-03-11 [Link] [('Retorno 1,159033 0,003903
Total', '15,90%'), 1,159033 0,003903
1,159033 0,003903
1,159033 0,003903

('Índice de Sharpe', '1,89'),


('Redução Máxima', '3,03%'),
('Duração do saque', '120718')]
Sinais: 7594
Pedidos: 7478
Preenchimentos: 7478

Você pode ver que a estratégia tem um desempenho adequado durante este período. Ela tem um total
retorno de pouco menos de 16%. O índice de Sharpe é razoável (quando comparado a um índice diário típico
estratégia), mas dada a natureza de alta frequência da estratégia, deveríamos esperar mais.
a principal atração dessa estratégia é que o drawdown máximo é baixo (aproximadamente 3%).
Isso sugere que poderíamos aplicar mais alavancagem para obter mais retorno.
O desempenho desta estratégia pode ser visto na Fig 15.3:
Observe que esses números são baseados na negociação de um total de 100 ações. Você pode ajustar a alavancagem
simplesmente ajustando o método generate_naive_order da classe Portfolio . Procure o
Machine Translated by Google

179

Figura 15.3: Curva de patrimônio, retornos diários e reduções para estratégia de reversão à média intradiária

atributo conhecido como mkt_quantity. Ele será definido como 100. Alterar para 2000, por exemplo, fornece estes resultados:

..
..
[('Retorno Total', '392,85%'), ('Índice de
Sharpe', '2,29'), ('Redução Máxima',
'45,69%'), ('Duração da Redução', '102150')]

..
..

É claro que o Índice de Sharpe e o Retorno Total são muito mais atrativos, mas também temos que suportar uma queda
máxima de 45% nesse período!

15.4 Desempenho de plotagem


As três figuras exibidas acima foram criadas usando o script plot_performance.py . Para completar, incluí o código para que
você possa usá-lo como base para criar seus próprios gráficos de desempenho.

É necessário executar isso no mesmo diretório do arquivo de saída do backtest, ou seja, onde se encontra [Link] . A
listagem é a seguinte:

#!/usr/bin/python #
-*- codificação: utf-8 -*-

# plot_performance.py

importar [Link]
Machine Translated by Google

180

importar numpy como np


importar [Link] como plt importar
pandas como pd

se __nome__ == "__principal__": dados


= [Link].read_csv( "[Link]",
cabeçalho=0,
datas_de_análise=Verdadeiro,
coluna_de_índice=0 ).sort()

# Trace três gráficos: curva de patrimônio, #


retornos do período, reduções fig =
[Link]()
# Defina a cor externa como branco
[Link].set_facecolor('white')

# Trace a curva de patrimônio ax1


= fig.add_subplot(311, ylabel='Valor do portfólio, %') data['equity_curve'].plot(ax=ax1,
color="blue", lw=2.) [Link](True)

# Plotar os retornos ax2 =


fig.add_subplot(312, ylabel='Retornos de período, %') data['retornos'].plot(ax=ax2,
color="black", lw=2.) [Link](True)

# Plotar os retornos ax3 =


fig.add_subplot(313, ylabel='Drawdowns, %') data['drawdown'].plot(ax=ax3,
color="red", lw=2.) [Link](True)

# Trace a figura [Link]()


Machine Translated by Google

Capítulo 16

Otimização de Estratégia

Em capítulos anteriores, consideramos como criar tanto um modelo preditivo subjacente (como com a Máquina de Vetores de
Suporte e o Classificador de Floresta Aleatória), quanto uma estratégia de negociação baseada nele. Ao longo do caminho,
vimos que existem muitos parâmetros para tais modelos. No caso de um SVM, temos os parâmetros de "ajuste" ÿ e C. Em uma
estratégia de negociação de Crossover de Média Móvel, temos os parâmetros para as duas janelas de lookback dos filtros de
média móvel.

Neste capítulo, descreveremos métodos de otimização para melhorar o desempenho de nossas estratégias de negociação,
ajustando os parâmetros de forma sistemática. Para isso, utilizaremos mecanismos da área estatística de Seleção de Modelos,
como validação cruzada e busca em grade.
A literatura sobre seleção de modelos e otimização de parâmetros é vasta, e a maioria dos métodos está um pouco além do
escopo deste livro. Quero apresentar o assunto aqui para que você possa explorar técnicas mais sofisticadas no seu próprio
ritmo.

16.1 Otimização de Parâmetros


Nesta fase, quase todas as estratégias de negociação e modelos estatísticos subjacentes exigiram um ou mais parâmetros para
serem utilizados. Em estratégias de momentum que utilizam indicadores técnicos, como médias móveis (simples ou
exponenciais), é necessário especificar uma janela de lookback. O mesmo se aplica a muitas estratégias de reversão à média,
que exigem uma janela de lookback (móvel) para calcular uma regressão entre duas séries temporais. Modelos estatísticos
específicos de aprendizado de máquina, como regressão logística, SVM ou Random Forest, também exigem parâmetros para
serem calculados.

O maior perigo ao considerar a otimização de parâmetros é o sobreajuste de um modelo ou estratégia de negociação. Esse
problema ocorre quando um modelo é treinado em uma parcela retida de dados de treinamento dentro da amostra e é otimizado
para ter um bom desempenho (pela medida de desempenho apropriada), mas o desempenho se degrada substancialmente
quando aplicado a dados fora da amostra. Por exemplo, uma estratégia de negociação pode ter um desempenho extremamente
bom no backtest (os dados dentro da amostra), mas quando implementada para negociação real, pode ser completamente não
lucrativa.
Uma preocupação adicional com a otimização de parâmetros é que ela pode se tornar muito custosa computacionalmente.
Com os sistemas computacionais modernos, isso é menos problemático do que antes, devido à paralelização e às CPUs
rápidas. No entanto, a otimização de múltiplos parâmetros pode aumentar a complexidade computacional em ordens de
magnitude. É preciso estar ciente disso como parte do processo de pesquisa e desenvolvimento.

16.1.1 Quais parâmetros otimizar?


Um modelo de negociação algorítmica baseado em estatística frequentemente terá muitos parâmetros e diferentes medidas de
desempenho. Um algoritmo de aprendizado estatístico subjacente terá seu próprio conjunto de parâmetros. No caso de uma
regressão linear múltipla ou logística, estes seriam os coeficientes ÿi .
No caso de uma floresta aleatória, um desses parâmetros seria o número de árvores de decisão subjacentes a serem usadas
no conjunto. Uma vez aplicado a um modelo de negociação, outros parâmetros podem ser de entrada.

181
Machine Translated by Google

182

e limites de saída, como um z-score de uma série temporal específica. O próprio z-score pode ter uma janela de lookback contínua
implícita. Como pode ser visto, o número de parâmetros pode ser bastante extenso.

Além dos parâmetros, existem inúmeras maneiras de avaliar o desempenho de um modelo estatístico e a estratégia de
negociação nele baseada. Definimos conceitos como taxa de acerto e matriz de confusão. Além disso, existem outras medidas
estatísticas, como o Erro Quadrático Médio (MSE). Essas são medidas de desempenho que seriam otimizadas no nível do modelo

estatístico, por meio de parâmetros relevantes para seu domínio.

A estratégia de negociação real é avaliada com base em diferentes critérios, como a taxa de crescimento anual composta
(CAGR) e o drawdown máximo. Seria necessário variar os critérios de entrada e saída, bem como outros limites que não estão
diretamente relacionados ao modelo estatístico. Isso motiva a questão de qual conjunto de parâmetros otimizar e quando.

Nas seções seguintes, otimizaremos os parâmetros do modelo estatístico, no estágio inicial de pesquisa e desenvolvimento,
bem como os parâmetros associados a uma estratégia de negociação usando um modelo estatístico otimizado subjacente, em
cada um de seus respectivos desempenhos.
medidas.

16.1.2 A otimização é cara


Com múltiplos parâmetros de valor real, a otimização pode rapidamente se tornar extremamente cara, pois cada novo parâmetro
adiciona uma dimensão espacial adicional. Se considerarmos o exemplo de uma busca em grade (a ser discutido em detalhes
abaixo) e tivermos um único parâmetro ÿ, podemos desejar variar ÿ dentro do conjunto {0,1, 0,2, 0,3, 0,4, 0,5}. Isso requer 5
simulações.
Se considerarmos agora um parâmetro adicional ÿ, que pode variar no intervalo {0,2, 0,4, 0,6, 0,8, 1,0},
separados para serem testados, isso resultaria em 5 2 = 25 simulações. Outro parâmetro, ÿ, com 5 variações, resulta em 5 3 = 125 simulações. Se cada parâmetro tivesse 10 valores

simulações iguais a 103 = 1000 simulações. Como pode ser visto, o espaço de busca de parâmetros pode rapidamente tornar essas simulações extremamente caras.

É evidente que existe um compromisso entre realizar uma busca exaustiva de parâmetros e manter um tempo total de
simulação razoável. Embora o paralelismo, incluindo CPUs e unidades de processamento gráfico (GPUs) de múltiplos núcleos,
tenha atenuado um pouco o problema, ainda precisamos ser cautelosos ao introduzir parâmetros. Essa noção de redução de
parâmetros também é uma questão de eficácia do modelo, como veremos a seguir.

16.1.3 Sobreajuste
Overfitting é o processo de otimizar um parâmetro, ou conjunto de parâmetros, em relação a um conjunto de dados específico, de
forma que uma medida de desempenho apropriada (ou medida de erro) seja maximizada (ou minimizada), mas, quando aplicada
a um conjunto de dados desconhecido, essa medida de desempenho se degrada substancialmente. O conceito está intimamente
relacionado à ideia do dilema viés-variância.
O dilema viés-variância diz respeito à situação em que um modelo estatístico tem um dilema entre ser um modelo de baixo
viés ou um modelo de baixa variância, ou um meio-termo entre os dois. Viés refere-se à diferença entre a estimativa de um
parâmetro feita pelo modelo e o verdadeiro valor "populacional" do parâmetro, ou a suposições errôneas no modelo estatístico.
Variância refere-se ao erro da sensibilidade do modelo a pequenas flutuações no conjunto de treinamento (em dados amostrais).

Em todos os modelos estatísticos, busca-se minimizar simultaneamente o erro de viés e o erro de variância para melhorar a
precisão do modelo. Tal situação pode levar ao sobreajuste nos modelos, visto que o erro de treinamento pode ser substancialmente
reduzido com a introdução de modelos com maior flexibilidade (variação). No entanto, tais modelos podem ter um desempenho
extremamente baixo em dados novos (fora da amostra), visto que foram essencialmente "ajustados" aos dados da amostra.

Um exemplo comum de modelo de alto viés e baixa variância é o da regressão linear aplicada a um conjunto de dados não
lineares. A adição de novos pontos não afeta drasticamente a inclinação da regressão (assumindo que não estejam muito distantes
dos dados restantes), mas, como o problema é inerentemente não linear, há um viés sistemático nos resultados ao usar um modelo
linear.
Um exemplo comum de modelo de baixo viés e alta variância é o de um ajuste de spline polinomial aplicado a um conjunto de
dados não lineares. O parâmetro do modelo (o grau do polinômio)
Machine Translated by Google

183

poderia ser ajustado para se ajustar a esse modelo com muita precisão (ou seja, com baixo viés nos dados de
treinamento), mas a adição de novos pontos quase certamente levaria o modelo a modificar seu grau de polinômio
para se ajustar aos novos dados. Isso o tornaria um modelo de altíssima variância nos dados da amostra. Tal
modelo provavelmente teria previsibilidade ou capacidade inferencial muito baixas em dados fora da amostra.

O overfitting também pode se manifestar na estratégia de negociação e não apenas no modelo estatístico.
Por exemplo, poderíamos otimizar o índice de Sharpe variando os parâmetros de limite de entrada e saída.
Embora isso possa melhorar a lucratividade no teste retrospectivo (ou minimizar substancialmente o risco),
provavelmente não seria um comportamento replicado quando a estratégia fosse implementada ao vivo, pois
poderíamos estar ajustando essas otimizações ao ruído nos dados históricos.
Discutiremos abaixo técnicas para minimizar o overfitting o máximo possível. No entanto, é preciso estar
ciente de que se trata de um perigo constante tanto na negociação algorítmica quanto na análise estatística em
geral.

16.2 Seleção de Modelo

Nesta seção, consideraremos como otimizar o modelo estatístico que fundamentará uma estratégia de negociação.
No campo da estatística e do aprendizado de máquina, isso é conhecido como Seleção de Modelos.
Embora eu não apresente uma discussão exaustiva sobre as várias técnicas de seleção de modelos, descreverei alguns
dos mecanismos básicos, como validação cruzada e pesquisa em grade, que funcionam bem para estratégias de negociação.

16.2.1 Validação Cruzada

A validação cruzada é uma técnica usada para avaliar como um modelo estatístico se generalizará para novos
dados aos quais não foi exposto anteriormente. Essa técnica é geralmente usada em modelos preditivos, como
os classificadores supervisionados mencionados anteriormente, usados para prever o sinal dos retornos diários
subsequentes de uma série de preços de ativos. Fundamentalmente, o objetivo da validação cruzada é minimizar
o erro em dados fora da amostra sem levar a um modelo com sobreajuste.
Nesta seção, descreveremos a divisão de treinamento/teste e a validação cruzada k-fold, bem como usaremos
técnicas dentro do Scikit-Learn para executar automaticamente esses procedimentos em modelos estatísticos
que já desenvolvemos.

Divisão de treinamento/teste

O exemplo mais simples de validação cruzada é conhecido como divisão treinamento/teste, ou validação cruzada
dupla. Uma vez que um conjunto de dados históricos anteriores é reunido (como uma série temporal diária de
preços de ativos), ele é dividido em dois componentes. A proporção da divisão geralmente varia entre 0,5 e 0,8.
Neste último caso, isso significa que 80% dos dados são usados para treinamento e 20% para testes.
Todas as estatísticas de interesse, como taxa de acerto, matriz de confusão ou erro quadrático médio, são
calculadas no conjunto de teste, que não foi usado no processo de treinamento.
Para realizar este processo em Python com Scikit-Learn podemos usar o sklearn cross_validation
Método train_test_split . Continuaremos com nosso modelo conforme discutido no capítulo sobre Previsão. Em
particular, modificaremos o [Link] e criaremos um novo arquivo chamado train_test_split.py. Precisaremos
adicionar a nova importação à lista de importações:

#!/usr/bin/python #
-*- codificação: utf-8 -*-

# train_test_split.py

de __future__ importar função_de_impressão

importar data e hora

importar sklearn
Machine Translated by Google

184

de sklearn.cross_validation importar train_test_split de [Link]


importar RandomForestClassifier de sklearn.linear_model importar
LogisticRegression de [Link] importar LDA de [Link]
importar confusion_matrix de
[Link] importar QDA de [Link] importar LinearSVC,
SVC

de create_lagged_series importar create_lagged_series

Em [Link], originalmente dividimos os dados com base em uma data específica dentro da série temporal:

# previsã[Link]

..

# Os dados de teste são divididos em duas partes: antes e depois de 1º de janeiro de 2005. start_test =
[Link](2005,1,1)

# Crie conjuntos de treinamento e teste


X_train = X[[Link] < start_test]
X_teste = X[[Link] >= iniciar_teste] y_train =
y[[Link] < iniciar_teste] y_teste = y[[Link] >=
iniciar_teste]
..

Isso pode ser substituído pelo método train_test_split do Scikit-Learn em train_test_split.py


arquivo. Para completar, o método __main__ completo é fornecido abaixo:

# train_test_split.py

if __name__ == "__main__": # Crie


uma série defasada do índice de mercado de ações dos EUA S&P500 snpret =
create_lagged_series( "^GSPC",
[Link](2001,1,10), [Link](2005,12,31),
lags=5
)

# Use os dois dias anteriores de retornos como valores preditores,


com a direção como resposta
X = snpret[["Lag1","Lag2"]] y =
snpret["Direção"]

# Divisão de treinamento/
teste X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.8,
random_state=42
)

# Crie os modelos (parametrizados) print("Taxas


de acerto/Matrizes de confusão:\n") models = [("LR",
LogisticRegression()),
("LDA", LDA()),
("QDA", QDA()),
("LSVC", LinearSVC()),
("RSVM", SVC(
C=1000000.0, tamanho_do_cache=200, peso_da_classe=Nenhum,
coef0=0.0, grau=3, gama=0.0001, kernel='rbf', max_iter=-1,
probabilidade=Falso, estado_aleatório=Nenhum,
Machine Translated by Google

185

encolhendo=Verdadeiro, tol=0,001, verbose=Falso) ), ("RF",

RandomForestClassifier(
n_estimators=1000, critério='gini', max_depth=Nenhum,
min_samples_split=2, min_samples_leaf=1,
max_features='auto', bootstrap=True, oob_score=False,
n_jobs=1, random_state=Nenhum, verbose=0) )]

# Iterar pelos modelos para m em modelos:

# Treine cada um dos modelos no conjunto de treinamento m[1].fit(X_train,


y_train)

# Crie uma matriz de previsões no conjunto de teste pred =


m[1].predict(X_test)

# Produza a taxa de acerto e a matriz de confusão para cada modelo print("%s:\n%0.3f" % (m[0],
m[1].score(X_test, y_test))) print("%s\n" % confusion_matrix(pred, y_test))

Observe que escolhemos a proporção do conjunto de treinamento como 80% dos dados, deixando os
dados de teste com apenas 20%. Além disso, especificamos um estado aleatório para randomizar a
amostragem dentro da seleção de dados. Isso significa que os dados não são divididos sequencialmente
em ordem cronológica, mas sim amostrados aleatoriamente.
Os resultados da validação cruzada no modelo são os seguintes (provavelmente parecerão ligeiramente
diferentes devido à natureza do procedimento de ajuste):

Taxas de acerto/matrizes de confusão:

LR:
0,511
[[ 70 70]
[419 441]]

LDA:
0,513
[[ 69 67]
[420 444]]

QDA:
0,503
[[ 83 91]
[406 420]]

LSVC:
0,513
[[ 69 67]
[420 444]]

RSVM:
0,506
[[ 8 13]
[481 498]]
Machine Translated by Google

186

RF:
0,490
[[200 221]
[289 290]]

Observa-se que as taxas de acerto são substancialmente menores do que as encontradas no capítulo sobre previsão
mencionado anteriormente. Consequentemente, podemos provavelmente concluir que a escolha específica da divisão
treinamento/teste levou a uma visão excessivamente otimista da capacidade preditiva do classificador.
O próximo passo é aumentar o número de vezes que uma validação cruzada é realizada para
minimizar qualquer potencial sobreajuste. Para isso, usaremos a validação cruzada k-fold.

Validação Cruzada K-Fold

Em vez de particionar o conjunto em um único conjunto de treinamento e teste, podemos usar a validação cruzada k-fold
para particionar aleatoriamente o conjunto em k subamostras de tamanhos iguais. Para cada iteração (das quais existem k),
uma das k subamostras é mantida como um conjunto de teste, enquanto as k-1 subamostras restantes formam, juntas, um
conjunto de treinamento. Um modelo estatístico é então treinado em cada uma das k dobras e seu desempenho é avaliado
em seu k-ésimo conjunto de teste específico.
O objetivo disso é combinar os resultados de cada modelo em um conjunto, calculando a média dos resultados da
previsão (ou de outras formas) para produzir uma única previsão. O principal benefício do uso da validação cruzada k-fold é
que cada preditor dentro do conjunto de dados original é usado para treinamento e teste apenas uma vez.

Isso levanta a questão de como escolher k, que agora é outro parâmetro! Geralmente,
k = 10 é usado, mas também é possível realizar outra análise para escolher um valor ótimo de k.
Agora, usaremos o módulo cross_validation do Scikit-Learn para obter o objeto de validação cruzada k-fold do KFold .
Criamos um novo arquivo chamado k_fold_cross_val.py, que é uma cópia de train_test_split.py , e modificamos as
importações adicionando a seguinte linha:

#!/usr/bin/python # -*-
codificação: utf-8 -*-

# k_fold_cross_val.py

de __future__ importar função_de_impressão

importar data e hora

importar pandas como


pd importar
sklearn de sklearn importar validação cruzada
de [Link] importar confusion_matrix de
[Link] importar SVC

de create_lagged_series importar create_lagged_series

Precisamos então fazer alterações na função __main__ removendo o train_test_split


método e substituindo-o por uma instância do KFold. Ele recebe cinco parâmetros.
O primeiro é o comprimento do conjunto de dados, que neste caso é 1250 dias. O segundo valor é K, representando o
número de dobras, que neste caso é 10. O terceiro valor é indices, que defini como False. Isso significa que os valores de
índice reais são usados para as matrizes retornadas pelo iterador. O quarto e o quinto são usados para randomizar a ordem
das amostras.
Como anteriormente em [Link] e train_test_split.py, obtemos a série defasada do S&P500. Em seguida, criamos
um conjunto de vetores de preditores (X) e respostas (y). Em seguida, utilizamos o objeto KFold e iteramos sobre ele.
Durante cada iteração, criamos os conjuntos de treinamento e teste para cada um dos vetores X e y. Estes são então
inseridos em uma máquina de vetores de suporte radial com parâmetros idênticos aos dos arquivos mencionados
anteriormente, e o modelo é ajustado.
Por fim, a taxa de acerto e a matriz de confusão para cada instância do SVM são geradas.
Machine Translated by Google

187

# k_fold_cross_val.py

if __name__ == "__main__": # Crie uma


série defasada do índice de mercado de ações dos EUA S&P500 snpret =
create_lagged_series( "^GSPC",
[Link](2001,1,10), [Link](2005,12,31),
lags=5
)

# Use os dois dias anteriores de retornos como valores preditores, com a


direção como resposta
X = snpret[["Lag1","Lag2"]] y =
snpret["Direção"]

# Crie um objeto de validação cruzada k-fold kf =


cross_validation.KFold( len(snpret),
n_folds=10, indices=False, shuffle=True, random_state=42

# Use o objeto kf para criar matrizes de índice que # indicam quais


elementos foram retidos para treinamento # e quais elementos foram retidos para
teste # para cada iteração de k-elementos para train_index, test_index em kf:

X_trem = [Link][[Link][índice_trem]]
X_teste = [Link][[Link][índice_teste]] y_trem =
[Link][[Link][índice_trem]] y_teste =
[Link][[Link][índice_teste]]

# Neste caso, use apenas o


# Máquina de vetores de suporte radial (SVM) print("Matriz
de taxa de acerto/confusão:") modelo = SVC(

C=1000000.0, tamanho_do_cache=200, peso_da_classe=Nenhum,


coef0=0.0, grau=3, gama=0.0001, kernel='rbf', max_iter=-1,
probabilidade=Falso, estado_aleatório=Nenhum, redução=Verdadeiro,
tol=0.001, verbose=Falso
)

# Treine o modelo nos dados de treinamento retidos [Link](X_train,


y_train)

# Crie uma matriz de previsões no conjunto de testes pred =


[Link](X_test)

# Produza a taxa de acerto e a matriz de confusão para cada modelo print("%0.3f" %


[Link](X_test, y_test)) print("%s\n" % confusion_matrix(pred,
y_test))

A saída do código é a seguinte:


Matriz de taxa de acerto/confusão:
0,528
[[11 10] [49
55]]
Machine Translated by Google

188

Matriz de taxa de acerto/confusão:


0,400
[[ 2 5] [70
48]]

Matriz de taxa de acerto/confusão:


0,528
[[ 8 8] [51
58]]

Matriz de taxa de acerto/confusão:


0,536
[[ 6 3] [55
61]]

Matriz de taxa de acerto/confusão:


0,512
[[ 7 5] [56
57]]

Matriz de taxa de acerto/confusão:


0,480
[[11 11] [54
49]]

Matriz de taxa de acerto/confusão:


0,608
[[12 13] [36
64]]

Matriz de taxa de acerto/confusão:


0,440
[[ 8 17] [53
47]]

Matriz de taxa de acerto/confusão:


0,560
[[10 9] [46
60]]

Matriz de taxa de acerto/confusão:


0,528
[[ 9 11] [48
57]]

É evidente que as matrizes de taxa de acerto e de confusão variam drasticamente entre as diversas dobras. Isso indica que o modelo é propenso a
overfitting neste conjunto de dados específico. Uma solução para isso é usar significativamente mais dados, seja com uma frequência maior ou por um
período mais longo.

Para utilizar esse modelo em uma estratégia de negociação, seria necessário combinar cada um desses
classificadores treinados individualmente (ou seja, cada um dos K objetos) em uma média de conjunto e
então usar esse modelo combinado para classificação dentro da estratégia.
Observe que, tecnicamente, não é apropriado usar técnicas simples de validação cruzada em dados
ordenados temporalmente (ou seja, séries temporais). Existem mecanismos mais sofisticados para lidar com
a autocorrelação dessa forma, mas eu queria destacar a abordagem, então usamos dados de séries
temporais para simplificar.
Machine Translated by Google

189

16.2.2 Pesquisa em grade

Vimos até agora que a validação cruzada k-fold nos ajuda a evitar o sobreajuste nos dados, realizando a validação em
cada elemento da amostra. Agora, voltamos nossa atenção para a otimização dos hiperparâmetros de um modelo
estatístico específico. Esses parâmetros são aqueles não aprendidos diretamente pelo procedimento de estimativa do
modelo. Por exemplo, C e ÿ para uma máquina de vetores de suporte.
Em essência, são os parâmetros que precisamos especificar ao chamar a inicialização de cada modelo estatístico. Para
este procedimento, usaremos um processo conhecido como busca em grade.
A ideia básica é considerar uma série de parâmetros e avaliar o desempenho do modelo estatístico em cada
elemento de parâmetro dentro da série. Para isso, no Scikit-Learn, podemos criar uma ParameterGrid. Esse objeto
produzirá uma lista de dicionários Python, cada um contendo uma combinação de parâmetros a ser inserida em um
modelo estatístico.
Um trecho de código de exemplo que produz uma grade de parâmetros, para parâmetros relacionados a um suporte
máquina vetorial, é dada abaixo:

>>> de sklearn.grid_search importar ParameterGrid >>>


param_grid = {'C': [1, 10, 100, 1000], 'gama': [0,001, 0,0001]} >>>
lista(ParameterGrid(param_grid))

[{'C': 1, 'gama': 0,001}, {'C': 1,


'gama': 0,0001}, {'C': 10, 'gama':
0,001}, {'C': 10, 'gama': 0,0001},
{'C': 100, 'gama': 0,001}, {'C': 100,
'gama': 0,0001}, {'C': 1000, 'gama':
0,001}, {'C': 1000, 'gama': 0,0001}]

Agora que temos um meio adequado de gerar um ParameterGrid , precisamos inseri-lo em um modelo estatístico
iterativamente para buscar uma pontuação de desempenho ideal. Neste caso, buscaremos maximizar a taxa de acerto
do classificador.
O mecanismo GridSearchCV do Scikit-Learn nos permite realizar a pesquisa na grade real.
Na verdade, ele nos permite executar não apenas uma pesquisa de grade padrão, mas também um esquema de validação
cruzada ao mesmo tempo.
Agora vamos criar um novo arquivo, grid_search.py, que mais uma vez usa create_lagged_series.py
e uma máquina de vetores de suporte para realizar uma busca em grade de hiperparâmetros com validação cruzada.
Para isso, precisamos importar as bibliotecas corretas:

#!/usr/bin/python #
-*- codificação: utf-8 -*-

# grid_search.py

de __future__ importar função_de_impressão

importar data e hora

importar sklearn
de sklearn importar validação cruzada de
sklearn.cross_validation importar train_test_split de
sklearn.grid_search importar GridSearchCV de
[Link] importar classification_report de [Link]
importar SVC

de create_lagged_series importar create_lagged_series

Como antes com k_fold_cross_val.py , criamos uma série defasada e, em seguida, usamos os retornos dos dois
dias anteriores como preditores. Inicialmente, criamos uma divisão de treinamento/teste de forma que 50% do
Machine Translated by Google

190

os dados podem ser usados para treinamento e validação cruzada, enquanto os dados restantes podem ser "mantidos"
para avaliação.
Em seguida, criamos a lista tuned_parameters , que contém um único dicionário que denota os parâmetros que
desejamos testar. Isso criará um produto cartesiano de todas as listas de parâmetros, ou seja, uma lista de pares de todas
as combinações possíveis de parâmetros.
Depois que a lista de parâmetros é criada, nós a passamos para a classe GridSearchCV , junto com o tipo de
classificador no qual estamos interessados (ou seja, uma máquina de vetor de suporte radial), com um valor k de validação
cruzada k-fold de 10.
Por fim, treinamos o modelo e geramos o melhor estimador e suas pontuações de taxa de acerto associadas.
Dessa forma, não apenas otimizamos os parâmetros do modelo por meio de validação cruzada, mas também otimizamos
os hiperparâmetros do modelo por meio de uma busca em grade parametrizada, tudo em uma única classe!
Essa concisão do código permite uma experimentação significativa sem que haja um empecilho para a "discussão excessiva
de dados".

if __name__ == "__main__": # Crie


uma série defasada do índice de mercado de ações dos EUA S&P500 snpret =
create_lagged_series( "^GSPC",
[Link](2001,1,10), [Link](2005,12,31),
lags=5
)

# Use os dois dias anteriores de retornos como valores preditores,


com a direção como resposta
X = snpret[["Lag1","Lag2"]] y =
snpret["Direção"]

# Divisão de treinamento/
teste X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.5,
random_state=42
)

# Defina os parâmetros por validação cruzada


tuned_parameters =
[ {'kernel': ['rbf'], 'gamma': [1e-3, 1e-4], 'C': [1, 10, 100, 1000]}
]

# Execute a pesquisa de grade no modelo de parâmetros ajustados =


GridSearchCV(SVC(C=1), tuned_parameters, cv=10) [Link](X_train, y_train)

print("Parâmetros otimizados encontrados no conjunto de treinamento:")


print(model.best_estimator_, "\n")

print("Pontuações da grade calculadas no conjunto de treinamento:")


para parâmetros, mean_score, pontuações em model.grid_scores_:
print("%0.3f para %r" % (mean_score, params))

A saída do procedimento de validação cruzada da pesquisa em grade é a seguinte:

Parâmetros otimizados encontrados no conjunto de


treinamento: SVC(C=1, cache_size=200, class_weight=None, coef0=0.0, degree=3, gamma=0.001,
kernel='rbf', max_iter=-1, probabilidade=Falso, estado_aleatório=Nenhum, redução=Verdadeiro,
tol=0,001, verbose=Falso)

Pontuações da grade calculadas no conjunto de


treinamento: 0,541 para {'kernel': 'rbf', 'C': 1, 'gamma': 0,001} 0,541 para
{'kernel': 'rbf', 'C': 1, 'gamma': 0,0001} 0,541 para {'kernel': 'rbf', 'C': 10,
'gamma': 0,001}
Machine Translated by Google

191

0,541 para {'kernel': 'rbf', 'C': 10, 'gama': 0,0001} 0,541 para
{'kernel': 'rbf', 'C': 100, 'gama': 0,001} 0,541 para {'kernel': 'rbf',
'C': 100, 'gama': 0,0001} 0,538 para {'kernel': 'rbf', 'C': 1000, 'gama':
0,001} 0,541 para {'kernel': 'rbf', 'C': 1000, 'gama': 0,0001}

Como podemos ver, ÿ = 0,001 e C = 1 fornecem a melhor taxa de acerto, no conjunto de validação, para esta
máquina de vetores de suporte de kernel radial específica. Este modelo pode agora formar a base de uma estratégia de
negociação baseada em previsão, como demonstramos anteriormente no capítulo anterior.

16.3 Estratégias de Otimização


Até este ponto, nos concentramos na seleção de modelos e na otimização do modelo estatístico subjacente que (pode)
formar a base de uma estratégia de negociação. No entanto, um modelo preditivo e uma estratégia algorítmica funcional
e lucrativa são duas entidades diferentes. Agora, voltamos nossa atenção para a otimização de parâmetros que têm um
efeito direto nas métricas de lucratividade e risco.
Para isso, utilizaremos o software de backtesting orientado a eventos descrito em um capítulo anterior.
Consideraremos uma estratégia específica com três parâmetros associados e buscaremos no espaço formado pelo
produto cartesiano dos parâmetros, utilizando um mecanismo de busca em grade. Em seguida, tentaremos maximizar
métricas específicas, como o Índice de Sharpe, ou minimizar outras, como o drawdown máximo.

16.3.1 Pares de reversão à média intradiária


A estratégia que nos interessa neste capítulo é a "Intraday Mean Reverting Equity Pairs Trade", utilizando as ações de energia
AREX e WLL. Ela contém três parâmetros que podemos otimizar: o período de lookback da regressão linear, o limite de entrada
do escore z dos resíduos e o limite de saída do escore z dos resíduos.

Consideraremos uma série de valores para cada parâmetro e, em seguida, calcularemos um backtest para a
estratégia em cada uma dessas séries, gerando o retorno total, o índice de Sharpe e as características de drawdown de
cada simulação em um arquivo CSV para cada conjunto de parâmetros. Isso nos permitirá determinar um Sharpe
otimizado ou um drawdown máximo minimizado para nossa estratégia de negociação.

16.3.2 Ajuste de Parâmetros


Como o software de backtesting orientado a eventos exige bastante da CPU, restringiremos o intervalo de parâmetros a
3
três valores por parâmetro. Isso nos dará um total de 3 = 27 simulações separadas para realizar. Os intervalos de
parâmetros estão listados abaixo:

• Janela de retrospectiva OLS - wl ÿ {50, 100, 200}

• Limiar de entrada do Z-Score - zh ÿ {2,0, 3,0, 4,0}

• Limiar de saída do Z-Score - zl ÿ {0,5, 1,0, 1,5}

Para realizar o conjunto de simulações será calculado um produto cartesiano de todos os três intervalos
e então a simulação será realizada para cada combinação de parâmetros.
A primeira tarefa é modificar o arquivo intraday_mr.py para incluir o método do produto da biblioteca itertools :

# intraday_mr.py

..
de itertools importar produto
..

Podemos então modificar o método __main__ para incluir a geração de uma lista de parâmetros para
todos os três parâmetros discutidos acima.
Machine Translated by Google

192

A primeira tarefa é criar os intervalos de parâmetros reais para a janela de lookback do MCO, o limite de
entrada do zscore e o limite de saída do zscore. Cada um deles possui três variações distintas, resultando em
um total de 27 simulações.
Depois que os intervalos são criados, o método [Link] é usado para criar um produto cartesiano
de todas as variações, que é então alimentado em uma lista de dicionários para garantir que os argumentos de
palavras-chave corretos sejam passados para o objeto Strategy .
Finalmente, o backtest é instanciado com o strat_params_list formando a palavra-chave final
argumento:
se __nome__ == "__principal__":
csv_dir = '/caminho/para/seu/arquivo/csv' # ALTERE ISTO! symbol_list =
['AREX', 'WLL'] initial_capital = 100000.0
heartbeat = 0.0 start_date =
[Link](2007,
11, 8, 10, 41, 0)

# Crie a grade de parâmetros de estratégia # usando o


gerador de produtos cartesianos itertools strat_lookback = [50, 100, 200]
strat_z_entry = [2.0, 3.0, 4.0] strat_z_exit = [0.5,
1.0, 1.5] strat_params_list =
list(product( strat_lookback, strat_z_entry,
strat_z_exit

))

# Crie uma lista de dicionários com os pares de palavras-chave/valor


corretos para os parâmetros de estratégia strat_params_dict_list =
[ dict(ols_window=sp[0],
zscore_high=sp[1], zscore_low=sp[2]) for sp in strat_params_list

# Execute o conjunto de backtests para todas as combinações de parâmetros backtest =


Backtest( csv_dir, symbol_list,
initial_capital, heartbeat, start_date, HistoricCSVDataHandlerHFT,
SimulatedExecutionHandler,
PortfólioHFT, IntradayOLSMRStratégia,
strat_params_list=strat_params_dict_list

) backtest.simulate_trading()

O próximo passo é modificar o objeto Backtest em [Link] para que ele possa lidar com múltiplos
conjuntos de parâmetros. Precisamos modificar o método _generate_trading_instances para ter um argumento
que represente o conjunto de parâmetros específico na criação de um novo objeto Strategy :

# [Link]

..
def _generate_trading_instances(self, strategy_params_dict):
"""

Gera os objetos de instância de negociação a partir de seus tipos


de classe.
"""

print("Criando DataHandler, Strategy, Portfolio e ExecutionHandler para") print("lista de parâmetros de estratégia:


%s..." % strategy_params_dict) self.data_handler = self.data_handler_cls(

[Link], self.csv_dir, self.lista_de_símbolos, self.strings_de_cabeçalho


)
Machine Translated by Google

193

[Link] = self.strategy_cls(self.data_handler,
[Link], **strategy_params_dict

) [Link]ólio = [Link]ólio_cls(
self.data_handler, [Link], self.start_date, self.num_strats,
[Link], self.initial_capital

) self.execution_handler = self.execution_handler_cls([Link])
..

Este método agora é chamado dentro de um loop de lista de parâmetros de estratégia, em vez de na
construção do objeto Backtest . Embora possa parecer um desperdício recriar todos os manipuladores de dados,
filas de eventos e objetos de portfólio para cada conjunto de parâmetros, isso garante que todos os iteradores
tenham sido redefinidos e que estejamos realmente começando do zero para cada simulação.
A próxima tarefa é modificar o método simulate_trading para executar um loop em todas as variantes dos
parâmetros da estratégia. O método cria um arquivo CSV de saída que é usado para armazenar combinações
de parâmetros e suas métricas de desempenho específicas. Isso nos permitirá posteriormente plotar as
características de desempenho entre os parâmetros.
O método percorre todos os parâmetros da estratégia e gera uma nova instância de negociação em cada
simulação. O backtest é então executado e as estatísticas são calculadas. Estas são armazenadas e geradas no
arquivo CSV. Após o término da simulação, o arquivo de saída é fechado:

# [Link]

..
def simular_negociação(self):
"""

Simula o backtest e gera o desempenho do portfólio.


"""

out = abrir("saída/[Link]", "w")

spl = len(self.strat_params_list) para i, sp


em enumerate(self.strat_params_list):
print("Estratégia %s de %s..." % (i+1, spl))
self._generate_trading_instances(sp)
self._run_backtest() stats
= self._output_performance() [Link](stats)

tot_ret = float(estatísticas[0][1].replace("%","")) cagr =


float(estatísticas[1][1].replace("%","")) sharpe =
float(estatísticas[2][1]) max_dd =
float(estatísticas[3][1].replace("%","")) dd_dur =
int(estatísticas[4][1])

[Link]( "%s,
%s,%s,%s,%s,%s,%s,%s\n" %
( sp["ols_window"], sp["zscore_high"], sp["zscore_low"], tot_ret, cagr,
sharpe, max_dd, dd_dur
)
)

[Link]()

No meu sistema desktop, esse processo leva algum tempo! 27 simulações de parâmetros em mais de
600.000 pontos de dados por simulação levam cerca de 3 horas. O backtester não foi paralelizado nesta fase,
portanto, a execução simultânea de tarefas de simulação tornaria o processo muito mais rápido. A saída do
estudo de parâmetros atual é apresentada abaixo. As colunas são fornecidas por
Machine Translated by Google

194

Retrospectiva OLS, ZScore alto, ZScore baixo, retorno total (%), CAGR (%), Sharpe, DD máximo (%),
Duração do DD (minutos):

50,2,0,0,5,213,96,20,19,1,63,42,55,255568
50,2,0,1,0,264,9,23,13,2,18,27,83,160319
50,2,0,1,5,167,71,17,15,1,63,60,52,293207
50,3,0,0,5,298,64,24,9,2,82,14,06,35127
50,3,0,1,0,324,0,26,14,3,61,9,81,33533
50,3,0,1,5,294,91,24,71,3,71,8,04,31231
50,4,0,0,5,212,46,20,1,2,93,8,49,23920
50,4,0,1,0,222,94,20,74,3,5,8,21,28167
50,4,0,1,5,215,08,20,26,3,66,8,25,22462
100,2,0,0,5,378,56,28,62,2,54,22,72,74027
100,2,0,1,0,374,23,28,43,3,0,15,71,89118
100,2,0,1,5,317,53,25,83,2,93,14,56,80624
100,3,0,0,5,320,1,25,95,3,06,13,35,66012
100,3,0,1,0,307,18,25,32,3,2,11,57,32185
100,3,0,1,5,306,13,25,27,3,52,7,63,33930
100,4,0,0,5,231,48,21,25,2,82,7,44,29160
100,4,0,1,0,227,54,21,01,3,11,7,7,15400
100,4,0,1,5,224,43,20,83,3,33,7,73,18584
200,2,0,0,5,461,5,31,97,2,98,19,25,31024
200,2,0,1,0,461,99,31,99,3,64,10,53,64793
200,2,0,1,5,399,75,29,52,3,57,10,74,33463
200,3,0,0,5,333,36,26,58,3,07,19,24,56569
200,3,0,1,0,325,96,26,23,3,29,10,78,35045
200,3,0,1,5,284,12,24,15,3,21,9,87,34294
200,4,0,0,5,245,61,22,06,2,9,12,52,51143
200,4,0,1,0,223,63,20,78,2,93,9,61,40075
200,4,0,1,5,203,6,19,55,2,96,7,16,40078
Podemos observar que, para este estudo específico, os valores dos parâmetros wl = 50, zh = 3,0 e zl = 1,5 fornecem o
melhor índice de Sharpe em S = 3,71. Para este índice de Sharpe, temos um retorno total de 294,91% e um rebaixamento
máximo de 8,04%. O melhor retorno total de 461,99%, embora com um rebaixamento máximo de 10,53%, é dado pelo conjunto
de parâmetros wl = 200, zh = 2,0 e zl = 1,0.

16.3.3 Visualização

Como tarefa final na otimização da estratégia, vamos agora visualizar as características de desempenho do backtester usando
Matplotlib, o que é uma etapa extremamente útil na realização da pesquisa estratégica inicial. Infelizmente, estamos em uma
situação em que temos um problema tridimensional e, portanto, a visualização do desempenho não é simples! No entanto,
existem algumas soluções parciais para a situação.

Em primeiro lugar, poderíamos fixar o valor de um parâmetro e aplicar uma "fatia de parâmetro" ao restante do "cubo de dados". Por exemplo,
poderíamos fixar a janela de lookback em 100 e, em seguida, ver como a variação nos limites de entrada e saída do z-score afeta o Índice de Sharpe ou

o rebaixamento máximo.

Para isso, usaremos o Matplotlib. Leremos o CSV de saída e remodelaremos os dados


para que possamos visualizar os resultados.

Mapa de calor de Sharpe/Drawdown

Fixaremos o período de lookback de wl = 100 e então geraremos uma grade 3 × 3 e um "mapa de calor" da razão de Sharpe e
redução máxima para a variação nos limites do z-score.
No código a seguir, importamos o arquivo CSV de saída. A primeira tarefa é filtrar os períodos de lookback que não são de
interesse (wl ÿ {50, 200}). Em seguida, remodelamos os dados de desempenho restantes em duas matrizes 3 × 3. A primeira
representa o índice de Sharpe para cada combinação de limiar de pontuação z, enquanto a segunda representa o rebaixamento
máximo.
Machine Translated by Google

195

Aqui está o código para criar o mapa de calor do Índice de Sharpe. Primeiro, importamos o Matplotlib e o NumPy. Em seguida, definimos uma função

chamada create_data_matrix , que remodela os dados do Índice de Sharpe em uma grade 3 × 3. Dentro da função __main__ , abrimos o arquivo CSV

(certifique-se de alterar o caminho no seu sistema!) e excluímos quaisquer linhas que não façam referência a um período de lookback de 100.

Em seguida, criamos um mapa de calor sombreado em azul e aplicamos os rótulos corretos de linha/coluna
usando os limites do escore z. Em seguida, colocamos o valor real do Índice de Sharpe no mapa de calor.
Por fim, definimos os ticks, rótulos, título e então plotamos o mapa de calor:
#!/usr/bin/python # -*-
codificação: utf-8 -*-

# plot_sharpe.py

importar [Link] como plt importar


numpy como np

def create_data_matrix(csv_ref, col_index): data = [Link]((3, 3))


para i em intervalo(0, 3): para j em
intervalo(0, 3): data[i][j] =
float(csv_ref[i*3+j][col_index])
retornar dados

se __nome__ == "__principal__":
# Abra o arquivo CSV e obtenha apenas as linhas # com um valor de
lookback de 100 csv_file = open("/path/to/
[Link]", "r").readlines() csv_ref = [

[Link]().split(",") para c em
csv_file se c[:3] == "100"

] dados = create_data_matrix(csv_ref, 5)

fig, ax = [Link]() mapa de calor


= [Link](dados, cmap=[Link]) rótulos_de_linha = [0,5, 1,0,
1,5] rótulos_de_coluna = [2,0, 3,0, 4,0]

para y no intervalo([Link][0]):
para x no intervalo (dados. forma [1]): plt.
texto (x + 0,5, y + 0,5, '%.2f' % dados [y, x], alinhamento horizontal =
'centro', alinhamento vertical = 'centro', )

[Link](mapa de calor)

ax.set_xticks([Link]([Link][0])+0,5, menor=False)
ax.set_yticks([Link]([Link][1])+0,5, menor=False)
ax.set_xticklabels(rótulos_de_linha, menor=False)
ax.set_yticklabels(rótulos_de_coluna, menor=False)

[Link]('Mapa de calor da taxa de Sharpe', tamanho da fonte=18)


[Link]('Limite de saída da pontuação Z', tamanho da fonte=14)
Machine Translated by Google

196

[Link]('Limite de entrada de pontuação Z', tamanho da fonte=14) [Link]()

O gráfico para o rebaixamento máximo é quase idêntico, com a exceção de que usamos um mapa de
calor sombreado em vermelho e alteramos o índice da coluna na função create_data_matrix para usar os
dados de porcentagem de rebaixamento máximo.

#!/usr/bin/python # -*-
codificação: utf-8 -*-

# plot_drawdown.py

importar [Link] como plt importar


numpy como np

def create_data_matrix(csv_ref, col_index): data = [Link]((3, 3))


para i em intervalo(0, 3): para j em
intervalo(0, 3): data[i][j] =
float(csv_ref[i*3+j][col_index])
retornar dados

se __nome__ == "__principal__":
# Abra o arquivo CSV e obtenha apenas as linhas # com um valor de
lookback de 100 csv_file = open("/path/to/
[Link]", "r").readlines() csv_ref = [

[Link]().split(",") para c em
csv_file se c[:3] == "100"

] dados = create_data_matrix(csv_ref, 6)

fig, ax = [Link]() mapa de calor


= [Link](dados, cmap=[Link]) rótulos_de_linha = [0,5,
1,0, 1,5] rótulos_de_coluna = [2,0, 3,0, 4,0]

para y no intervalo([Link][0]):
para x no intervalo([Link][1]):
[Link](x + 0,5, y + 0,5, '%.2f%%' % data[y, x], alinhamento horizontal='centro',
alinhamento vertical='centro', )

[Link](mapa de calor)

ax.set_xticks([Link]([Link][0])+0,5, menor=False)
ax.set_yticks([Link]([Link][1])+0,5, menor=False)
ax.set_xticklabels(rótulos_de_linha, menor=False)
ax.set_yticklabels(rótulos_de_coluna, menor=False)

[Link]('Mapa de calor de redução máxima', tamanho da fonte=18)


[Link]('Limite de saída do Z-Score', tamanho da fonte=14)
[Link]('Limite de entrada do Z-Score', tamanho da fonte=14) [Link]()
Machine Translated by Google

197

Os mapas de calor produzidos a partir dos trechos acima são fornecidos na Fig. 16.3.3 e Fig. 16.3.3:

Figura 16.1: Mapa de calor do índice de Sharpe para limites de entrada/saída do z-score

Figura 16.2: Mapa de calor de redução máxima para limites de entrada/saída de pontuação z

Em wl = 100, as diferenças entre os menores e maiores Índices de Sharpe, bem como os menores e maiores rebaixamentos máximos, são facilmente
perceptíveis. O Índice de Sharpe é otimizado para limites de entrada e saída maiores, enquanto o rebaixamento é minimizado na mesma região. O Índice
de Sharpe e o rebaixamento máximo atingem seu pior desempenho quando os limites de entrada e saída são baixos.
Machine Translated by Google

198

Isto motiva-nos claramente a considerar a utilização de limiares de entrada e saída relativamente elevados para este
estratégia quando implantada em negociações ao vivo.
Machine Translated by Google

Bibliografia

[1] Glen. Arnold. Guia do Financial Times para os Mercados Financeiros. Financial Times/Prentice
Salão, 2011.

[2] David. Barber. Raciocínio Bayesiano e Aprendizado de Máquina. Cambridge University Press,
2012.

[3] Klein E. Loper E. Bird, S. Processamento de linguagem natural com Python. O'Reilly Media, 2009.

[4] Mao H. Zeng X. Bollen, J. O humor do Twitter prevê o mercado de ações. CoRR, abs/1010.3003,
2010.

[5] Ernest P. Chan. Negociação quantitativa: como construir seu próprio negócio de negociação algorítmica
ness. John Wiley & Sons, 2009.

[6] Ernest P. Chan. Negociação Algorítmica: Estratégias Vencedoras e sua Justificativa. John Wiley
& Filhos, 2013.

[7] Larry. Harris. Negociação e trocas: microestrutura de mercado para praticantes. Oxford
Imprensa universitária, 2002.

[8] Tibshirani Robert. Friedman Jerome. Hastie, Trevor. Os elementos da aprendizagem estatística:
Mineração de Dados, Inferência e Predição, 2ª Ed. Springer, 2011.

[9] Witten Daniela. Hastie Trevor. Tibshirani Robert. James, Gareth. Uma introdução à aprendizagem estatística: com
aplicações em R. Springer, 2013.

[10] Barry. Johnson. Negociação Algorítmica e DMA: Uma introdução à negociação de acesso direto
estratégias. 4Myeloma Press, 2010.

[11] W. McKinney. Python para análise de dados. O'Reilly Media, 2012.

[12] Rishi K. Narang. Dentro da caixa preta: a verdade simples sobre quantitativa e alta
Negociação de Frequência, 2ª Ed. John Wiley & Sons, 2013.

[13] Robert. Pardo. A Avaliação e Otimização de Estratégias de Negociação, 2ª Ed. John Wiley
& Filhos, 2008.

[14] MA Russell. 21 receitas para minerar o Twitter. O'Reilly Media, 2011.

[15] MA Russell. Mineração da Web Social, 2ª Ed. O'Reilly Media, 2013.

[16] Euan. Sinclair. Negociação de volatilidade, 2ª ed. John Wiley & Sons, 2013.

[17] Paul. Wilmott. Paul Wilmott apresenta Finanças Quantitativas, 2ª edição. John Wiley & Sons,
2007.

199

Você também pode gostar