Successful Algorithmic Trading
Successful Algorithmic Trading
Conteúdo
II Sistemas de Negociação 13
1
Machine Translated by Google
Modelagem IV 79
6
Machine Translated by Google
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
1
Machine Translated by Google
Machine Translated by Google
Capítulo 1
Introdução ao livro
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].
"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.
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.
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.
• 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.
• 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.
• 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.
• 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.
• 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
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.
6
Machine Translated by Google
Capítulo 2
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.
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
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
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.
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.
• 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 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.
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!
É 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.
• 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.
• 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.
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.
• 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.
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.
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!
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".
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.
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.
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.
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
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.
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.
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.
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.
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.
• 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.
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
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.
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.
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.
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.
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.
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.
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.
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
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.
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.
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.
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
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.
• 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.
• 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:
• 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]
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:
• QuantNet - [Link]
33
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]
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:
• 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.
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.
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:
• Í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
• 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.
• 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.
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.
• 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
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.
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.
Para criar o ambiente de pesquisa, instalaremos as seguintes ferramentas de software, todas elas
são de código aberto e gratuitos para download:
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:
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:
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:
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:
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:
DataFrame vazio
Colunas: []
Índice: [] >>>
exit()
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.
45
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:
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:
O qtconsole tem alguns pacotes de dependências adicionais, nomeadamente as bibliotecas ZMQ e Pygments:
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.
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:
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
cd ~/ibapi git
clone [Link]
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
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
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.
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
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.
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:
É 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.
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.
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.
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.
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.
50
• 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).
• 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.
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!
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.
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:
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:
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 .
• 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.
• 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):
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:
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.
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.
Ao inserir todos os comandos SQL acima na linha de comando do MySQL, os quatro comandos necessários
tabelas serão criadas.
Agora estamos prontos para começar a interagir com nosso banco de dados MySQL via Python e pandas.
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
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():
"""
) 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:]
56
) símbolos de retorno
def insert_snp500_symbols(símbolos):
"""
[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
importar avisos de
importação de data e hora
def obter_lista_de_tiquetes_do_banco_de_dados():
"""
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]
[Link](yahoo_url).[Link]("\n")[1:-1] prices = []
Machine Translated by Google
58
para y em yf_data: p =
[Link]().split(',') preç[Link](
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.
"""
# 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
59
) 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.
#!/usr/bin/python # -*-
codificação: utf-8 -*-
# recuperando_dados.py
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
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.
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
Para os fins deste livro, vamos nos concentrar quase exclusivamente em ações e ETFs
para simplificar a implementação.
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].
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.
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
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.
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.
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.
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.
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.
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:
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.
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.
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:
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]())
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.
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]:
68
Agora clique no link "Novo: página de futuros..." para acessar a página inicial de futuros:
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
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
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
futuros = []
Machine Translated by Google
71
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:
"""
Agora, vinculamos as duas funções acima para baixar todos os contratos desejados:
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'
72
dl_dir = 'quandl/futuros/ES'
) es["Resolver"].plot()
[Link]()
A saída do gráfico é apresentada na Figura 8.4.2.
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.
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
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
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"]
74
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.
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.
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.
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.
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:
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
# 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
# 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'],
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.
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.
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 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.
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:
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.
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.
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.
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
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.
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:
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
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".
• 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:
89
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.
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.
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)
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:
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:
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)
# 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 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:
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.
É 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
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
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
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]
94
[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]
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]
df = [Link](index=[Link]) df["AREX"] =
arex["Fechamento Ajustável"] df["WLL"] =
wll["Fechamento Ajustável"]
# Plotar os resíduos
plot_residuals(df)
(-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
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.
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.
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.
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.
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.
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.
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
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.
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.
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.
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.
Á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.
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.
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.
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.
Á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á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.
#!/usr/bin/python # -*-
codificação: utf-8 -*-
# previsã[Link]
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:
104
"""
# 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)
# 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 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:
# 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)
106
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:
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
107
Machine Translated by Google
Machine Translated by Google
Capítulo 12
Medição de Desempenho
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 uma estratégia mantém esse desempenho positivo em uma implementação ativa ou se precisa ser descontinuada.
• 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.
• 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.
• 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.
• 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.
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:
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.
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]
115
importar data e
hora importar numpy
como np importar
pandas como pd importar [Link] como web
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.
# 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'])
>>> 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:
# 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)
Para o Google, o índice de Sharpe para a estratégia neutra de mercado longa/curta é de 0,832:
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.
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.
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
Capítulo 13
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.
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.
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.
121
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".
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.
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
É 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.
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.
124
Ou, mais genericamente, para perda L excedendo um valor V aR com um nível de confiança c, temos:
• 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.
• 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 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.
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:
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]
"""
alfa = [Link](1-c, mu, sigma)
retornar P - P*(alfa + 1)
Machine Translated by Google
126
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
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.
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.
• 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.
• 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.
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]
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):
"""
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):
"""
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
[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):
"""
133
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):
"""
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):
"""
134
"""
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
def calcular_ib_commissão(self):
"""
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
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.
#!/usr/bin/python # -*-
codificação: utf-8 -*-
# [Link]
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).
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):
"""
@abstractmethod
def get_latest_bars(self, símbolo, N=1):
"""
@abstractmethod
def get_latest_bar_datetime(self, símbolo):
"""
@abstractmethod
def get_latest_bar_value(self, símbolo, val_type):
"""
@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.
"""
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).
"""
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.
"""
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):
"""
Para este manipulador, será assumido que os dados são obtidos do Yahoo.
Portanto, seu formato será respeitado.
"""
] ).organizar()
comb_index.union(self.symbol_data[s].index)
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]
para b em self.symbol_data[símbolo]:
Machine Translated by Google
139
rendimento b
# [Link]
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]
"""
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":
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]
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)
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],
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.
"""
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]
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).
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):
"""
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]
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.
"""
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]
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):
"""
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
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):
"""
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):
"""
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.
"""
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]
data_hora_mais_última =
[Link].obter_data_hora_mais_última_barra(self.lista_de_símbolos[0]
)
# Atualizar posições #
================
# Atualizar participações #
=============== dh =
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
147
# portfó[Link]
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.
"""
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]
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.
"""
148
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]
"""
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]
Parâmetros: sinal
- A tupla contendo informações do sinal.
"""
ordem = Nenhum
quantidade_mkt = 100
quantidade_cur = [Link]ções_correntes[símbolo] tipo_pedido =
'MKT'
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]
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):
"""
"""
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):
"""
retorno_total = self.curva_de_equidade['curva_de_equidade']
[-1] retornos = self.curva_de_equidade['retornos']
pnl = self.curva_de_equidade['curva_de_equidade']
150
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.
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]
# execuçã[Link]
classe ExecutionHandler(objeto):
"""
"""
__metaclasse__ = ABCMeta
@abstractmethod def
execute_order(self, evento):
"""
151
Parâmetros:
evento - Contém um objeto Evento com informações do pedido.
"""
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):
"""
"""
Parâmetros:
eventos - A fila de objetos de eventos.
"""
[Link] = eventos
Parâmetros:
evento - Contém um objeto Evento com informações do pedido.
"""
se [Link] == 'ORDEM':
evento_preenchimento = EventoPreenchimento(
) [Link](evento_de_preenchimento)
Machine Translated by Google
152
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.
#!/usr/bin/python # -*-
codificação: utf-8 -*-
# [Link]
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):
"""
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):
"""
imprimir(
"Criando DataHandler, Estratégia, Portfólio e ExecutionHandler"
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)
[Link].update_signal(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):
"""
155
"""
[Link].create_equity_curve_dataframe()
# [Link]
def simular_negociação(self):
"""
self._run_backtest()
self._output_performance()
Isso conclui os objetos operacionais do backtester orientado 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
importar data e
hora importar hora
Machine Translated by Google
156
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"
):
"""
[Link] = eventos
self.order_routing = roteamento_de_pedidos
[Link] = moeda self.fill_dict =
{}
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
157
# 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):
"""
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):
"""
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):
"""
# ib_execution.py
"""
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
"""
pedido = Pedido()
pedido.m_orderType = tipo_pedido
pedido.m_totalQuantity = quantidade
Machine Translated by Google
159
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
"""
self.fill_dict[[Link]] = {
"símbolo": [Link].m_symbol, "troca":
[Link].m_exchange, "direção": [Link].m_action,
"preenchido": Falso
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]]
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
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]
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
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]
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
):
"""
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
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):
"""
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]
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
símbolo = s dt
= [Link]() sig_dir =
""
166
# [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)
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]
..
..
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
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
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
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 .
# snp_forecast.py
classe SPYDailyForecastStrategy(Estratégia):
"""
Machine Translated by Google
169
"""
self.long_market = Falso
self.short_market = Falso self.bar_index
=0
[Link] = self.criar_modelo_de_previsão_de_símbolo()
# snp_forecast.py
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
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
}
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)
171
..
..
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.
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
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).
"""
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
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:
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
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)
SignalEvent(1, p0, dt, 'SAÍDA', 1.0) x_signal = SignalEvent(1, p1, dt, 'SAÍDA', 1.0)
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):
"""
176
) x = [Link].obter_valores_das_barras_mais_recente(
[Link][1], "fechar", N=self.ols_window
)
[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
se [Link] == 'MERCADO':
self.calculate_signals_for_pairs()
# 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
) 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 é:
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:
178
sharpe_ratio = create_sharpe_ratio(retorna)
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!
É 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
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.
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.
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
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.
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.
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.
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
importar sklearn
Machine Translated by Google
184
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)
# train_test_split.py
# 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
)
185
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) )]
# 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):
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.
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
187
# k_fold_cross_val.py
X_trem = [Link][[Link][índice_trem]]
X_teste = [Link][[Link][índice_teste]] y_trem =
[Link][[Link][índice_trem]] y_teste =
[Link][[Link][índice_teste]]
188
É 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
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:
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
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
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".
# 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
)
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.
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.
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)
))
) 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):
"""
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):
"""
[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.
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
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)
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)
196
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
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)
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)
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.
[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.
[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