Transações de acoplamento baseadas em tecnologia baseada em dados

Autora:Bem-estar, Criado: 2019-08-21 13:50:23, Atualizado: 2023-10-19 21:01:31

img

Os pairs de negociação são um bom exemplo de estratégias de negociação baseadas em análise matemática. Neste artigo, vamos demonstrar como usar dados para criar e automatizar estratégias de negociação de pairs.

Princípios Básicos

假设你有一对投资标的X和Y具有一些潜在的关联,例如两家公司生产相同的产品,如百事可乐和可口可乐。你希望这两者的价格比率或基差(也称为差价)随时间的变化而保持不变。然而,由于临时供需变化,如一个投资标的的大买/卖订单,对其中一家公司的重要新闻的反应等,这两对之间的价差可能会不时出现分歧。在这种情况下,一只投资标的向上移动而另一只投资标的相对于彼此向下移动。如果你希望这种分歧随着时间的推移恢复正常,你就可以发现交易机会(或套利机会)。此种套利机会在数字货币市场或者国内商品期货市场比比皆是,比如BTC与避险资产的关系;期货中豆粕,豆油与大豆品种之间的关系.

Quando há uma diferença de preço temporária, o negócio vai vender o indicador de investimento de excelente desempenho e comprar o indicador de investimento de baixo desempenho. Você pode ter certeza de que a diferença de lucro entre os dois investimentos acabará por ser causada por uma queda do indicador de investimento de excelente desempenho ou uma recuperação do indicador de investimento de baixo desempenho ou ambos.

Assim, a negociação de pares é uma estratégia de negociação neutra no mercado que permite aos traders lucrar com quase qualquer condição do mercado: tendência ascendente, tendência descendente ou liquidação horizontal.

Explique o conceito: dois indicadores de investimento

  • Construímos o nosso ambiente de pesquisa na plataforma de quantificação de inventores

Primeiro, para que o trabalho prossiga bem, precisamos construir o nosso ambiente de pesquisa, que neste artigo usamos a plataforma de quantificação dos inventores.FMZ.COMO projeto foi desenvolvido para criar um ambiente de pesquisa, principalmente para uma interface API rápida e fácil de usar e um sistema Docker completo para o futuro.

No nome oficial da plataforma de quantificação dos inventores, o sistema Docker é conhecido como sistema administrador.

Para mais informações sobre como implantar administradores e robôs, consulte meu artigo anterior:https://www.fmz.com/bbs-topic/4140

Os leitores que desejam comprar um administrador de instalação de servidores de nuvem podem consultar este artigo:https://www.fmz.com/bbs-topic/2848

Depois de implementarmos com sucesso um bom sistema de serviços e administradores de nuvem, vamos instalar o maior templo do Python até agora: o Anaconda.

Para implementar todos os ambientes de programação necessários para este artigo (dependências, gerenciamento de versões, etc.), o método mais simples é usar o Anaconda. É um ecossistema de ciência de dados Python e um gerenciador de dependências.

Para saber como instalar o Anaconda, consulte o guia oficial do Anaconda:https://www.anaconda.com/distribution/

本文还将用到numpy和pandas这两个目前在Python科学计算方面十分流行且重要的库.

O trabalho básico acima também pode ser referenciado em meu artigo anterior, onde eu expliquei como configurar o ambiente Anaconda e as bibliotecas numpy e pandas.https://www.fmz.com/digest-topic/4169

Em seguida, vamos usar o código para implementar uma meta de investimento de duas hipóteses.

import numpy as np
import pandas as pd

import statsmodels
from statsmodels.tsa.stattools import coint
# just set the seed for the random number generator
np.random.seed(107)

import matplotlib.pyplot as plt

Sim, nós vamos usar o matplotlib, o muito famoso catálogo de gráficos em Python.

Vamos gerar X de um indicador de investimento hipotético e simular seu retorno diário através de uma distribuição normal.

# Generate daily returns
Xreturns = np.random.normal(0, 1, 100) 
# sum them and shift all the prices up
X = pd.Series(np.cumsum(
    Xreturns), name='X') 
    + 50
X.plot(figsize=(15,7))
plt.show()

img

X do indicador de investimento, simulado para mapear seu retorno diário através de uma distribuição normal

Agora, o Y que nós geramos e X estão fortemente inter-relacionados, então o preço de Y deve ser muito semelhante à mudança de X. Nós modelamos isso tomando X, movendo-o para cima e adicionando algum ruído aleatório extraído da distribuição normal.

noise = np.random.normal(0, 1, 100)
Y = X + 5 + noise
Y.name = 'Y'
pd.concat([X, Y], axis=1).plot(figsize=(15,7))
plt.show()

img

X e Y dos indicadores de investimento co-integrados

Coordenação

协整非常类似于相关性,意味着两个数据系列之间的比率将在平均值附近变化.Y和X这两个系列遵循以下内容:

Y = X + e

A e é o ruído e o r é a taxa constante.

Para os pares de transações entre as duas sequências de tempo, essa proporção com o tempo deve convergir para o valor esperado, ou seja, elas devem ser co-integradas. A sequência de tempo que construímos acima é co-integrada. Agora vamos traçar a proporção entre as duas para que possamos ver como ela se parece.

(Y/X).plot(figsize=(15,7)) 
plt.axhline((Y/X).mean(), color='red', linestyle='--') 
plt.xlabel('Time')
plt.legend(['Price Ratio', 'Mean'])
plt.show()

img

Proporção e média entre os preços de dois indicadores de investimento coordenados

Teste de integração

Há uma maneira conveniente de testar isso, usando o statsmodels.tsa.stattools. Nós devemos ver um p-valor muito baixo, porque nós criamos duas séries de dados que são o mais coordenadas possível.

# compute the p-value of the cointegration test
# will inform us as to whether the ratio between the 2 timeseries is stationary
# around its mean
score, pvalue, _ = coint(X,Y)
print pvalue

Os resultados são: 1.81864477307e-17

Atenção: relevância e integração

Relacionados e conjugados, embora teoricamente semelhantes, não são o mesmo. Vejamos exemplos de séries de dados relacionadas, mas não conjugadas, e vice-versa. Primeiro, vamos examinar a relação das séries que acabamos de gerar.

X.corr(Y)

O resultado foi: 0.951.

Como esperávamos, isso é muito alto. Mas o que acontece com duas séries relacionadas, mas não coerentes? Um exemplo simples é duas séries de dados desviadas.

ret1 = np.random.normal(1, 1, 100)
ret2 = np.random.normal(2, 1, 100)

s1 = pd.Series( np.cumsum(ret1), name='X')
s2 = pd.Series( np.cumsum(ret2), name='Y')

pd.concat([s1, s2], axis=1 ).plot(figsize=(15,7))
plt.show()
print 'Correlation: ' + str(X_diverging.corr(Y_diverging))
score, pvalue, _ = coint(X_diverging,Y_diverging)
print 'Cointegration test p-value: ' + str(pvalue)

img

Duas séries relacionadas (não integradas)

Coeficiente de correlação: 0.998 P-valor do teste de integração: 0.258

Exemplos simples de integração sem correlação são a sequência de distribuição normal e a onda quadrada.

Y2 = pd.Series(np.random.normal(0, 1, 800), name='Y2') + 20
Y3 = Y2.copy()
Y3[0:100] = 30
Y3[100:200] = 10
Y3[200:300] = 30
Y3[300:400] = 10
Y3[400:500] = 30
Y3[500:600] = 10
Y3[600:700] = 30
Y3[700:800] = 10
Y2.plot(figsize=(15,7))
Y3.plot()
plt.ylim([0, 40])
plt.show()
# correlation is nearly zero
print 'Correlation: ' + str(Y2.corr(Y3))
score, pvalue, _ = coint(Y2,Y3)
print 'Cointegration test p-value: ' + str(pvalue)

img

Relacionado: 0.007546 P-valor do teste de integração: 0.0

A correlação é muito baixa, mas os p-valores mostram uma coesão perfeita!

Como é que se faz uma troca de par?

Como as duas sequências de tempo de convergência (por exemplo, X e Y acima) estão alinhadas e desviadas umas das outras, ocorre às vezes uma situação de alto e baixo limite. Nós fazemos uma transação de pares comprando um indicador e vendendo outro. Assim, se os dois indicadores caírem ou subirem juntos, não ganhamos nem perdemos dinheiro, ou seja, somos neutros no mercado.

Voltando para Y = X + e X e Y no topo, fazendo com que a proporção ((Y / X) se mova em torno de seu valor médio, nós ganhamos dinheiro com a proporção de retorno do valor médio. Para fazer isso, vamos observar quando X e Y estão muito distantes, ou seja, o limiar é muito alto ou muito baixo:

  • Fazer mais proporções: é quando a proporção é muito pequena e nós esperamos que ela seja maior. No exemplo acima, nós abrimos a posição fazendo mais Y e fazendo X vazio.

  • Taxa de vazio: é quando a taxa de vazio é grande e esperamos que ela mude por hora. No exemplo acima, abrimos a posição fazendo vazio Y e fazendo mais X.

Observe que sempre temos uma posição de hedge: se o valor da compra do indicador de negociação for perdido, a posição em branco ganha dinheiro, e vice-versa, portanto, somos imunes ao movimento do mercado em geral.

Se os indicadores X e Y se moverem em relação um ao outro, ganharemos ou perderemos dinheiro.

Usando dados para encontrar comportamentos semelhantes a indicadores de transações

A melhor maneira de fazer isso é começar com o indicador de negociação que você suspeita que pode ser coerente e executar um teste estatístico.Desvio de comparação múltiplaO que você está fazendo?

Desvio de comparação múltiplaRefere-se a uma situação em que aumenta a chance de erroneamente gerar p-valores importantes ao executar vários testes, porque precisamos executar muitos testes. Se 100 testes forem feitos com dados aleatórios, devemos ver 5 p-valores abaixo de 0.05. Se você comparar n indicadores de transações para fazer uma colaboração integral, você verá muitos p-valores incorretos, o que aumentará com o aumento da sua amostra de testes. Para evitar isso, escolha um pequeno número de pares de transações para os quais você tem razões para determinar que podem ser colaborações e teste-os individualmente.Desvio de comparação múltipla

Por isso, vamos tentar encontrar alguns indicadores que demonstrem coesão. Vejamos como exemplo uma cesta de grandes ações de tecnologia dos EUA no S&P 500, que operam em segmentos de mercado semelhantes e têm preços coesões.

Retorna a matriz de divisão de verificação de integração, a matriz de p-valores e todos os pares de p-valores menores que 0.05.O método é mais propenso a múltiplos desvios de comparação, então eles precisam de uma segunda verificação.Neste artigo, para facilitar nossa explicação, escolhemos ignorar isso nos exemplos.

def find_cointegrated_pairs(data):
    n = data.shape[1]
    score_matrix = np.zeros((n, n))
    pvalue_matrix = np.ones((n, n))
    keys = data.keys()
    pairs = []
    for i in range(n):
        for j in range(i+1, n):
            S1 = data[keys[i]]
            S2 = data[keys[j]]
            result = coint(S1, S2)
            score = result[0]
            pvalue = result[1]
            score_matrix[i, j] = score
            pvalue_matrix[i, j] = pvalue
            if pvalue < 0.02:
                pairs.append((keys[i], keys[j]))
    return score_matrix, pvalue_matrix, pairs

Observação: Nós incluímos os indicadores de mercado (SPX) nos dados - o mercado impulsiona o fluxo de muitos indicadores de negociação, e geralmente você pode encontrar dois indicadores de negociação que parecem coexistir; mas na verdade eles não coexistem entre si, mas coexistem com o mercado. Isso é chamado de variável de mistura.

from backtester.dataSource.yahoo_data_source import YahooStockDataSource
from datetime import datetime
startDateStr = '2007/12/01'
endDateStr = '2017/12/01'
cachedFolderName = 'yahooData/'
dataSetId = 'testPairsTrading'
instrumentIds = ['SPY','AAPL','ADBE','SYMC','EBAY','MSFT','QCOM',
                 'HPQ','JNPR','AMD','IBM']
ds = YahooStockDataSource(cachedFolderName=cachedFolderName,
                            dataSetId=dataSetId,
                            instrumentIds=instrumentIds,
                            startDateStr=startDateStr,
                            endDateStr=endDateStr,
                            event='history')
data = ds.getBookDataByFeature()['Adj Close']
data.head(3)

img

Agora vamos tentar usar o nosso método para encontrar uma combinação de transações compatíveis.

# Heatmap to show the p-values of the cointegration test
# between each pair of stocks
scores, pvalues, pairs = find_cointegrated_pairs(data)
import seaborn
m = [0,0.2,0.4,0.6,0.8,1]
seaborn.heatmap(pvalues, xticklabels=instrumentIds, 
                yticklabels=instrumentIds, cmap=’RdYlGn_r’, 
                mask = (pvalues >= 0.98))
plt.show()
print pairs
[('ADBE', 'MSFT')]

img

Parece que o alumínio ADBE e o alumínio MSFT funcionam em conjunto. Vejamos o preço para ter certeza de que realmente faz sentido.

S1 = data['ADBE']
S2 = data['MSFT']
score, pvalue, _ = coint(S1, S2)
print(pvalue)
ratios = S1 / S2
ratios.plot()
plt.axhline(ratios.mean())
plt.legend([' Ratio'])
plt.show()

img

Gráfico do rácio de preços entre MSFT e ADBE de 2008 a 2017

O índice realmente parece ser uma média estável. O índice absoluto não é muito útil estatisticamente. É mais útil padronizar nosso sinal por considerá-lo como uma pontuação z. A pontuação z é definida como:

Ponto Z (valor) = (valor média) / Desvio-padrão

Alerta

Na verdade, geralmente tentamos fazer alguma ampliação dos dados, mas assumindo que eles são distribuídos de forma normal. No entanto, muitos dados financeiros não são distribuídos de forma normal, então temos que ser muito cuidadosos para não simplesmente assumir a normalidade ou qualquer distribuição específica ao gerar estatísticas. A distribuição verdadeira das proporções pode ter um efeito de engorda, e aqueles dados que tendem para extremos podem confundir nossos modelos e causar grandes perdas.

def zscore(series):
    return (series - series.mean()) / np.std(series)
zscore(ratios).plot()
plt.axhline(zscore(ratios).mean())
plt.axhline(1.0, color=’red’)
plt.axhline(-1.0, color=’green’)
plt.show()

img

Relatório de preços Z entre MSFT e ADBE entre 2008 e 2017

Agora é mais fácil observar os índices se movendo perto da média, mas às vezes é fácil ter grandes diferenças com a média, e podemos aproveitar isso.

Agora que discutimos os fundamentos da estratégia de negociação de par e definimos um indicador de negociação compatível com base nos preços históricos, vamos tentar desenvolver um sinal de negociação.

  • Coletar dados confiáveis e limpar dados

  • Criar funções a partir de dados para identificar sinais/lógica de transação

  • A função pode ser uma média móvel ou dados de preço, correlação ou proporção de sinais mais complexos - combinando-os para criar novas funções

  • Usar essas funções para gerar sinais de negociação, ou seja, quais sinais são compras, vendas ou posições vazias

Felizmente, temos uma plataforma de quantificação de inventores.fmz.comO trabalho feito por nós nos quatro aspectos acima é uma grande benção para os desenvolvedores de estratégias, que podem dedicar energia e tempo ao design e expansão de funções da lógica estratégica.

Na plataforma de inventores de quantidade, existem interfaces de todas as principais exchanges bem embaladas, tudo o que precisamos fazer é chamar essas interfaces de APIs, e o restante é implementar a lógica no fundo, que já tem uma equipe de profissionais para refiná-la.

Para a completude da lógica e para a explicação do princípio, aqui vamos fazer uma representação de quebra-cabeça dessas lógicas subjacentes, mas em operações práticas, os leitores podem fazer os quatro aspectos diretamente através da interface API quantificada pelo inventor.

Vamos começar:

Primeiro passo: definir o seu problema

Aqui, nós tentamos criar um sinal que nos diga se o índice vai ser comprado ou vendido no próximo momento, ou seja, nossa variável de previsão Y:

Y = Relação é compra (1) ou venda (-1)

Y(t)= Sign(Ratio(t+1) Ratio(t))

Observe que não precisamos de prever o preço do indicador de negociação real, nem mesmo o valor real do índice (embora possamos), apenas a direção do próximo índice.

Segundo passo: coletar dados confiáveis e precisos

O inventor de quantificação é seu amigo! Basta especificar o indicador a ser negociado e a fonte de dados a ser usada, e ele extrai os dados necessários e os limpa para fazer o dividendo e o desmembramento do indicador.

Nós usamos os seguintes dados obtidos com o Yahoo Finance para os últimos 10 dias de negociação (cerca de 2500 pontos de dados): preço de abertura, preço de fechamento, preço mais alto, preço mais baixo e volume de negociação

Passo 3: Segmentação dos dados

Não se esqueça deste passo muito importante para testar a precisão do modelo.

  • Formação 7 anos ~ 70%

  • Teste ~ 3 anos 30%

ratios = data['ADBE'] / data['MSFT']
print(len(ratios))
train = ratios[:1762]
test = ratios[1762:]

Idealmente, também deveríamos criar conjuntos de verificação, mas não o faremos por enquanto.

Passo 4: Projetos de Características

O que pode ser uma função relacionada? Queremos prever a direção da mudança do índice. Vimos que nossos dois indicadores de negociação são conjugados, então o índice geralmente se desloca e retorna ao valor médio. Parece que nossa característica deve ser alguma medida do valor médio do índice, e a diferença entre o valor atual e o valor médio pode produzir nossos sinais de negociação.

O que é que a gente tem a fazer?

  • 60 dias média móvel: medição da média de rolagem

  • 5 dias de média móvel: medição do valor atual da média

  • Diferença padrão de 60 dias

  • Ponto z: ((5d MA - 60d MA) / 60d SD

ratios_mavg5 = train.rolling(window=5,
                               center=False).mean()
ratios_mavg60 = train.rolling(window=60,
                               center=False).mean()
std_60 = train.rolling(window=60,
                        center=False).std()
zscore_60_5 = (ratios_mavg5 - ratios_mavg60)/std_60
plt.figure(figsize=(15,7))
plt.plot(train.index, train.values)
plt.plot(ratios_mavg5.index, ratios_mavg5.values)
plt.plot(ratios_mavg60.index, ratios_mavg60.values)
plt.legend(['Ratio','5d Ratio MA', '60d Ratio MA'])
plt.ylabel('Ratio')
plt.show()

img

60d e 5d MA preço

plt.figure(figsize=(15,7))
zscore_60_5.plot()
plt.axhline(0, color='black')
plt.axhline(1.0, color='red', linestyle='--')
plt.axhline(-1.0, color='green', linestyle='--')
plt.legend(['Rolling Ratio z-Score', 'Mean', '+1', '-1'])
plt.show()

img

Proporção de preços de 60-5 Z

A pontuação Z da média de rolagem realmente traz a natureza de regressão da média da taxa!

Passo 5: Seleção do modelo

Vamos começar com um modelo muito simples. Vejamos o gráfico do ponto z, e podemos ver que ele retorna sempre que o ponto z é muito alto ou muito baixo. Vamos usar + 1 / -1 como nosso limite para definir muito alto e muito baixo, e então podemos usar o seguinte modelo para gerar sinais de negociação:

  • Quando z é inferior a -1.0, a proporção é comprar (-1), porque nós esperamos que z volte para 0, então a proporção aumenta.

  • Quando z é superior a 1.0, a taxa é vendida (−1) porque esperamos que z volte para 0, então a taxa diminui.

Passo 6: treinar, verificar e otimizar

Finalmente, vamos ver o impacto real do nosso modelo nos dados reais.

# Plot the ratios and buy and sell signals from z score
plt.figure(figsize=(15,7))
train[60:].plot()
buy = train.copy()
sell = train.copy()
buy[zscore_60_5>-1] = 0
sell[zscore_60_5<1] = 0
buy[60:].plot(color=’g’, linestyle=’None’, marker=’^’)
sell[60:].plot(color=’r’, linestyle=’None’, marker=’^’)
x1,x2,y1,y2 = plt.axis()
plt.axis((x1,x2,ratios.min(),ratios.max()))
plt.legend([‘Ratio’, ‘Buy Signal’, ‘Sell Signal’])
plt.show()

img

Signais de preço de compra e venda

Este sinal parece ser razoável, nós parecemos vender o índice quando ele é alto ou aumenta e comprar de volta quando ele é baixo e diminui.

# Plot the prices and buy and sell signals from z score
plt.figure(figsize=(18,9))
S1 = data['ADBE'].iloc[:1762]
S2 = data['MSFT'].iloc[:1762]
S1[60:].plot(color='b')
S2[60:].plot(color='c')
buyR = 0*S1.copy()
sellR = 0*S1.copy()
# When buying the ratio, buy S1 and sell S2
buyR[buy!=0] = S1[buy!=0]
sellR[buy!=0] = S2[buy!=0]
# When selling the ratio, sell S1 and buy S2 
buyR[sell!=0] = S2[sell!=0]
sellR[sell!=0] = S1[sell!=0]
buyR[60:].plot(color='g', linestyle='None', marker='^')
sellR[60:].plot(color='r', linestyle='None', marker='^')
x1,x2,y1,y2 = plt.axis()
plt.axis((x1,x2,min(S1.min(),S2.min()),max(S1.max(),S2.max())))
plt.legend(['ADBE','MSFT', 'Buy Signal', 'Sell Signal'])
plt.show()

img

Sinais de compra e venda de ações da MSFT e da ADBE

Observe como às vezes ganhamos dinheiro com as pernas curtas, às vezes ganhamos com as pernas compridas e às vezes com ambas.

Estamos satisfeitos com os sinais dos dados de treinamento. Vejamos como esse sinal pode gerar lucros. Quando o índice é baixo, podemos fazer um simples retrômetro, comprar uma proporção (compre 1 ação ADBE e venda proporção x ação MSFT) e vender uma proporção quando ela for alta (compre 1 ação ADBE e compra proporção x ação MSFT) e calcular o PnL dessas transações.

# Trade using a simple strategy
def trade(S1, S2, window1, window2):
    
    # If window length is 0, algorithm doesn't make sense, so exit
    if (window1 == 0) or (window2 == 0):
        return 0
    
    # Compute rolling mean and rolling standard deviation
    ratios = S1/S2
    ma1 = ratios.rolling(window=window1,
                               center=False).mean()
    ma2 = ratios.rolling(window=window2,
                               center=False).mean()
    std = ratios.rolling(window=window2,
                        center=False).std()
    zscore = (ma1 - ma2)/std
    
    # Simulate trading
    # Start with no money and no positions
    money = 0
    countS1 = 0
    countS2 = 0
    for i in range(len(ratios)):
        # Sell short if the z-score is > 1
        if zscore[i] > 1:
            money += S1[i] - S2[i] * ratios[i]
            countS1 -= 1
            countS2 += ratios[i]
            print('Selling Ratio %s %s %s %s'%(money, ratios[i], countS1,countS2))
        # Buy long if the z-score is < 1
        elif zscore[i] < -1:
            money -= S1[i] - S2[i] * ratios[i]
            countS1 += 1
            countS2 -= ratios[i]
            print('Buying Ratio %s %s %s %s'%(money,ratios[i], countS1,countS2))
        # Clear positions if the z-score between -.5 and .5
        elif abs(zscore[i]) < 0.75:
            money += S1[i] * countS1 + S2[i] * countS2
            countS1 = 0
            countS2 = 0
            print('Exit pos %s %s %s %s'%(money,ratios[i], countS1,countS2))
            
            
    return money
trade(data['ADBE'].iloc[:1763], data['MSFT'].iloc[:1763], 60, 5)

O resultado foi: 1783.375.

Agora, podemos otimizar ainda mais o desempenho dos dados de verificação, alterando a janela de tempo de média móvel, alterando o limiar de compra/venda de posições de paz, etc.

Também podemos tentar modelos mais complexos, como Logistic Regression, SVM, etc., para fazer previsões de 1/1.

Agora, vamos avançar com este modelo, e isso nos leva a

Passo 7: Reavaliação dos dados do teste

A plataforma de quantificação do inventor, que usa um motor de retrospecção QPS/TPS de alto desempenho, reprodução real do ambiente histórico, elimina as armadilhas comuns de retrospecção quantitativa e detecta as falhas de estratégia em tempo hábil, ajudando assim a melhorar os investimentos em disco real.

Este artigo explica o princípio, ou escolhe mostrar a lógica subjacente, em aplicação prática, ou recomenda que os leitores usem a plataforma de quantificação do inventor, além de economizar tempo, é importante aumentar a taxa de erro.

O teste é muito simples e podemos usar a função acima para ver o PnL dos dados do teste.

trade(data[‘ADBE’].iloc[1762:], data[‘MSFT’].iloc[1762:], 60, 5)

O resultado foi: 5262.868.

O modelo é muito bom! Torna-se o nosso primeiro modelo simples de partilha de transações.

Evitar a adaptação excessiva

Antes de terminar a discussão, gostaria de discutir em particular sobre a superajuste. A superajuste é a armadilha mais perigosa da estratégia de negociação. Um algoritmo de superajuste pode ter um excelente desempenho em retrospectivas, mas falhar em novos dados invisíveis - o que significa que ele não revela realmente nenhuma tendência nos dados e não tem uma capacidade de previsão real.

Em nosso modelo, usamos a previsão de parâmetros de rolagem e esperamos otimizar o comprimento da janela de tempo por ela. Podemos decidir simplesmente repetir todas as possibilidades, o comprimento da janela de tempo razoável e escolher o comprimento de tempo que é melhor de acordo com o desempenho do nosso modelo. Abaixo, escrevemos um simples ciclo para avaliar o pnnl do comprimento da janela de tempo com base nos dados de treinamento e encontrar o melhor ciclo.

# Find the window length 0-254 
# that gives the highest returns using this strategy
length_scores = [trade(data['ADBE'].iloc[:1762], 
                data['MSFT'].iloc[:1762], l, 5) 
                for l in range(255)]
best_length = np.argmax(length_scores)
print ('Best window length:', best_length)
('Best window length:', 40)

Agora, quando verificamos o desempenho do modelo em dados de teste, descobrimos que o comprimento da janela de tempo não é o ideal!

# Find the returns for test data
# using what we think is the best window length
length_scores2 = [trade(data['ADBE'].iloc[1762:], 
                  data['MSFT'].iloc[1762:],l,5) 
                  for l in range(255)]
print (best_length, 'day window:', length_scores2[best_length])
# Find the best window length based on this dataset, 
# and the returns using this window length
best_length2 = np.argmax(length_scores2)
print (best_length2, 'day window:', length_scores2[best_length2])
(40, 'day window:', 1252233.1395)
(15, 'day window:', 1449116.4522)

Obviamente, os dados de nossa amostra não sempre produzem bons resultados no futuro. Apenas para testar, vamos traçar uma fração de comprimento calculada a partir de dois conjuntos de dados.

plt.figure(figsize=(15,7))
plt.plot(length_scores)
plt.plot(length_scores2)
plt.xlabel('Window length')
plt.ylabel('Score')
plt.legend(['Training', 'Test'])
plt.show()

img

Podemos ver que qualquer coisa entre 20 e 50 é uma boa opção para a janela de tempo.

Para evitar a adaptação excessiva, podemos usar a natureza de raciocínio econômico ou de algoritmos para escolher o comprimento de uma janela de tempo. Também podemos usar o filtro de Karman, que não requer que nós especificemos o comprimento; esse método será apresentado em outro artigo mais tarde.

Próximo passo

Neste artigo, apresentamos algumas abordagens simples para demonstrar o processo de desenvolvimento de uma estratégia de negociação. Na prática, com estatísticas mais complexas, podemos considerar as seguintes opções:

  • Índice Hurst

  • Período de semi-desgaste da regressão do valor médio deduzido do processo Ornstein-Uhlenbeck

  • Filtros Karman


Relacionados

Mais.

bk_fundNão encontrei o pacote.

bk_fundOnde instalar o backtester.dataSource.yahoo_data_source