Aplicação da Tecnologia de Aprendizagem de Máquina no Comércio

Autora:Lydia., Criado: 2022-12-30 10:53:07, Atualizado: 2023-09-20 09:30:09

img

Aplicação da Tecnologia de Aprendizagem de Máquina no Comércio

A inspiração para este artigo vem da minha observação de alguns avisos e armadilhas comuns depois de tentar aplicar a tecnologia de aprendizado de máquina a problemas de transação durante a pesquisa de dados na plataforma FMZ Quant.

Se você não leu meus artigos anteriores, sugerimos que leia o guia de ambiente de pesquisa de dados automatizados e método sistemático para formular estratégias de negociação que estabeleci na plataforma FMZ Quant antes deste artigo.

Estes dois endereços de artigos estão aqui:https://www.fmz.com/digest-topic/9862ehttps://www.fmz.com/digest-topic/9863.

Sobre a criação do ambiente de investigação

Este tutorial é destinado a entusiastas, engenheiros e cientistas de dados de todos os níveis de habilidade.

  • Instale o docker FMZ Quant e configure o Anaconda

A plataforma FMZ QuantFMZ.COMEste conjunto de interfaces inclui ferramentas práticas, como consulta de informações de conta, consulta de alto, aberto, baixo, preço de recebimento, volume de negociação e vários indicadores de análise técnica comumente usados de várias trocas principais. Em particular, fornece forte suporte técnico para as interfaces públicas de API que conectam as principais trocas principais no processo de negociação real.

Todos os recursos acima mencionados estão encapsulados em um sistema Docker-like. O que precisamos fazer é comprar ou alugar nossos próprios serviços de computação em nuvem e implantar o sistema Docker.

No nome oficial da plataforma FMZ Quant, o sistema Docker é chamado de sistema Docker.

Por favor, consulte meu artigo anterior sobre como implantar um docker e robô:https://www.fmz.com/bbs-topic/9864.

Os leitores que desejam comprar seu próprio servidor de computação em nuvem para implantar dockers podem consultar este artigo:https://www.fmz.com/digest-topic/5711.

Depois de implantar o servidor de computação em nuvem e o sistema docker com sucesso, em seguida, vamos instalar o maior artefato atual do Python: Anaconda

Para realizar todos os ambientes de programa relevantes (bibliotecas de dependências, gerenciamento de versões, etc.) necessários neste artigo, a maneira mais simples é usar o Anaconda.

Uma vez que instalamos o Anaconda no serviço de nuvem, recomendamos que o servidor de nuvem instale o sistema Linux mais a versão da linha de comando do Anaconda.

Para o método de instalação do Anaconda, consulte o guia oficial do Anaconda:https://www.anaconda.com/distribution/.

Se você é um programador Python experiente e se sente que não precisa usar o Anaconda, não é problema nenhum. Assumo que você não precisa de ajuda ao instalar o ambiente dependente necessário. Você pode ignorar esta seção diretamente.

Desenvolver uma estratégia de negociação

O resultado final de uma estratégia de negociação deve responder às seguintes questões:

  • Orientação: Determinar se o ativo é barato, caro ou pelo justo valor.

  • Condições de abertura da posição: se o ativo for barato ou caro, deve ir longo ou curto.

  • Transação de posição de encerramento: se o ativo tiver um preço razoável e tivermos uma posição no ativo (compra ou venda anterior), deve fechar a posição?

  • O valor da posição é o valor da posição em que a posição foi aberta, ou o valor da posição em que a posição foi aberta.

  • O valor da transação é o valor da transação em que a transação ocorre.

A aprendizagem de máquina pode ser usada para responder a cada uma dessas perguntas, mas para o resto deste artigo, vamos nos concentrar na primeira pergunta, que é a direção do comércio.

Abordagem estratégica

Existem dois tipos de abordagens para a construção de estratégias: um é baseado em modelos; o outro é baseado em mineração de dados.

Na construção de estratégias baseadas em modelos, começamos com o modelo de ineficiência do mercado, construímos expressões matemáticas (como preço e lucro) e testamos sua eficácia em um longo período de tempo.

Por outro lado, procuramos padrões de preço primeiro e tentamos usar algoritmos em métodos de mineração de dados. As razões para esses padrões não são importantes, porque apenas os padrões identificados continuarão a se repetir no futuro. Este é um método de análise cego, e precisamos verificar estritamente para identificar padrões reais a partir de padrões aleatórios. Método de teste repetido, modelo de gráfico de linha k e regressão de massa de características pertencem a esta categoria.

Obviamente, o aprendizado de máquina é muito fácil de aplicar aos métodos de mineração de dados.

O exemplo de código usa uma ferramenta de backtesting baseada na plataforma FMZ Quant e uma interface de API de transação automatizada. Depois de implantar o docker e instalar o Anaconda na seção acima, você só precisa instalar a biblioteca de análise de ciência de dados que precisamos e o famoso modelo de aprendizado de máquina scikit-learn.

pip install -U scikit-learn

Usar aprendizado de máquina para criar sinais de estratégia de negociação

- Mineração de dados Antes de começarmos, um sistema padrão de problemas de aprendizagem de máquina é mostrado na seguinte figura:

img

Sistema de problemas de aprendizagem de máquina

O recurso que vamos criar deve ter alguma capacidade de previsão (X). Queremos prever a variável alvo (Y) e usar dados históricos para treinar o modelo ML que pode prever Y o mais próximo possível do valor real. Finalmente, usamos esse modelo para fazer previsões em novos dados onde Y é desconhecido. Isso nos leva ao primeiro passo:

Passo 1: Prepare sua pergunta

  • O que você quer prever? O que é uma boa previsão? Como você avalia os resultados da previsão?

Ou seja, em nossa estrutura acima, o que é Y?

img

O que queres prever?

Quer prever preços futuros, retornos futuros/Pnl, sinais de compra/venda, otimizar a alocação de carteiras e tentar executar transações de forma eficiente?

Suponha que tentemos prever preços no próximo timestamp. neste caso, Y (t) = preço (t + 1). agora podemos usar dados históricos para completar nossa estrutura.

Observe que Y (t) é conhecido apenas no backtest, mas quando usamos nosso modelo, não saberemos o preço (t + 1) do tempo t. Usamos nosso modelo para prever Y (previsto, t) e compará-lo com o valor real apenas no tempo t + 1. Isso significa que você não pode usar Y como uma característica no modelo de previsão.

Uma vez que conhecemos o alvo Y, também podemos decidir como avaliar nossas previsões. Isso é importante para diferenciar entre os diferentes modelos dos dados que vamos tentar. Selecione um indicador para medir a eficiência do nosso modelo de acordo com o problema que estamos resolvendo. Por exemplo, se prevermos preços, podemos usar erro quadrado da raiz média como indicador. Alguns indicadores comumente usados (EMA, MACD, pontuação de variância, etc.) foram pré-codificados na caixa de ferramentas FMZ Quant. Você pode chamar esses indicadores globalmente através da interface API.

img

Quadro ML para a previsão de preços futuros

Para efeitos de demonstração, criaremos um modelo de previsão para prever o valor de referência (base) futuro esperado de um objeto de investimento hipotético, onde:

basis = Price of Stock — Price of Future

basis(t)=S(t)−F(t)

Y(t) = future expected value of basis = Average(basis(t+1),basis(t+2),basis(t+3),basis(t+4),basis(t+5))

Uma vez que este é um problema de regressão, vamos avaliar o modelo em RMSE (erro médio quadrado da raiz).

Nota: Por favor, consulte a Enciclopédia Baidu para o conhecimento matemático relevante do RMSE.

  • O nosso objetivo: criar um modelo para fazer o valor previsto o mais próximo possível de Y.

Passo 2: recolha de dados fiáveis

Recolha e limpe dados que possam ajudá-lo a resolver o problema em questão.

Quais dados é necessário considerar para prever a variável-alvo Y? Se prevermos o preço, podemos utilizar os dados de preço do objecto de investimento, os dados de quantidade de negociação do objecto de investimento, os dados semelhantes do objecto de investimento relacionado, o nível do índice do objecto de investimento e outros indicadores globais de mercado e o preço de outros ativos relacionados.

Você precisa definir permissões de acesso a dados para esses dados e garantir que seus dados sejam precisos e resolver os dados perdidos (um problema muito comum). Ao mesmo tempo, certifique-se de que seus dados sejam imparciais e totalmente representativos de todas as condições de mercado (por exemplo, o mesmo número de cenários de lucro e perda) para evitar viés no modelo. Você também pode precisar limpar os dados para obter dividendos, metas de investimento divididas, continuações, etc.

Se você usar a plataforma FMZ Quant (FMZ. COM), podemos acessar dados globais gratuitos do Google, Yahoo, NSE e Quandl; Dados de profundidade de futuros de commodities domésticos, como CTP e Esunny; Dados de exchanges de moeda digital convencionais, como Binance, OKX, Huobi e BitMex. A plataforma FMZ Quant também limpa e filtra esses dados, como a divisão de metas de investimento e dados de mercado em profundidade, e os apresenta aos desenvolvedores de estratégias em um formato que é fácil de entender para profissionais quantitativos.

Para facilitar a demonstração deste artigo, usamos os seguintes dados como o MQK da meta de investimento virtual. Também usaremos uma ferramenta quantitativa muito conveniente chamada Auquans Toolbox. Para mais informações, consulte:https://github.com/Auquan/auquan-toolbox-python.

# Load the data
from backtester.dataSource.quant_quest_data_source import QuantQuestDataSource
cachedFolderName = '/Users/chandinijain/Auquan/qq2solver-data/historicalData/'
dataSetId = 'trainingData1'
instrumentIds = ['MQK']
ds = QuantQuestDataSource(cachedFolderName=cachedFolderName,
                                    dataSetId=dataSetId,
                                    instrumentIds=instrumentIds)
def loadData(ds):
    data = None
    for key in ds.getBookDataByFeature().keys():
        if data is None:
            data = pd.DataFrame(np.nan, index = ds.getBookDataByFeature()[key].index, columns=[])
        data[key] = ds.getBookDataByFeature()[key]
    data['Stock Price'] =  ds.getBookDataByFeature()['stockTopBidPrice'] + ds.getBookDataByFeature()['stockTopAskPrice'] / 2.0
    data['Future Price'] = ds.getBookDataByFeature()['futureTopBidPrice'] + ds.getBookDataByFeature()['futureTopAskPrice'] / 2.0
    data['Y(Target)'] = ds.getBookDataByFeature()['basis'].shift(-5)
    del data['benchmark_score']
    del data['FairValue']
    return data
data = loadData(ds)

Com o código acima, o Auquans Toolbox baixou e carregou os dados no dicionário de quadros de dados. Agora precisamos preparar os dados no formato que gostamos. A função ds.getBookDataByFeature() retorna o dicionário de quadros de dados, um para cada recurso. Criamos novos quadros de dados para estoques com todas as características.

Passo 3: Dividir os dados

  • Criar conjuntos de treinamento, verificação cruzada e testar esses conjuntos de dados a partir de dados.

Este é um passo muito importante!Antes de continuarmos, devemos dividir os dados em conjuntos de dados de treinamento para treinar seu modelo; conjuntos de dados de teste para avaliar o desempenho do modelo.

img

Dividir os dados em conjuntos de formação e conjuntos de ensaio

Como os dados de treinamento são usados para avaliar os parâmetros do modelo, seu modelo pode se encaixar demais nesses dados de treinamento e os dados de treinamento podem enganar o desempenho do modelo. Se você não reter dados de teste individuais e usar todos os dados para treinamento, você não saberá o quão bem ou mal seu modelo funciona nos novos dados invisíveis. Esta é uma das principais razões para o fracasso do modelo de ML treinado em dados em tempo real: as pessoas treinam todos os dados disponíveis e estão animadas com os indicadores dos dados de treinamento, mas o modelo não pode fazer nenhuma previsão significativa sobre os dados em tempo real não treinados.

img

Dividir os dados em conjuntos de formação, de verificação e de ensaio

Há problemas com este método. Se treinarmos os dados de treinamento repetidamente, avaliarmos o desempenho dos dados de teste e otimizarmos nosso modelo até estarmos satisfeitos com o desempenho, tomamos os dados de teste como parte dos dados de treinamento implicitamente. No final, nosso modelo pode ter um bom desempenho neste conjunto de dados de treinamento e teste, mas não pode garantir que possa prever bem novos dados.

Para resolver este problema, podemos criar um conjunto de dados de validação separado. Agora, você pode treinar os dados, avaliar o desempenho dos dados de validação, otimizar até estar satisfeito com o desempenho e, finalmente, testar os dados do teste. Dessa forma, os dados do teste não serão poluídos e não usaremos nenhuma informação nos dados do teste para melhorar nosso modelo.

Lembre-se, uma vez que você tenha verificado o desempenho de seus dados de teste, não volte e tente otimizar ainda mais seu modelo. Se você achar que seu modelo não dá bons resultados, descarte o modelo completamente e comece de novo. Sugere-se que 60% dos dados de treinamento, 20% dos dados de validação e 20% dos dados de teste possam ser divididos.

Para a nossa pergunta, temos três conjuntos de dados disponíveis. vamos usar um como o conjunto de treinamento, o segundo como o conjunto de verificação, e o terceiro como o nosso conjunto de teste.

# Training Data
dataSetId =  'trainingData1'
ds_training = QuantQuestDataSource(cachedFolderName=cachedFolderName,
                                    dataSetId=dataSetId,
                                    instrumentIds=instrumentIds)
training_data = loadData(ds_training)
# Validation Data
dataSetId =  'trainingData2'
ds_validation = QuantQuestDataSource(cachedFolderName=cachedFolderName,
                                    dataSetId=dataSetId,
                                    instrumentIds=instrumentIds)
validation_data = loadData(ds_validation)
# Test Data
dataSetId =  'trainingData3'
ds_test = QuantQuestDataSource(cachedFolderName=cachedFolderName,
                                    dataSetId=dataSetId,
                                    instrumentIds=instrumentIds)
out_of_sample_test_data = loadData(ds_test)

Para cada um destes, somamos a variável alvo Y, que é definida como a média dos próximos cinco valores de base.

def prepareData(data, period):
    data['Y(Target)'] = data['basis'].rolling(period).mean().shift(-period)
    if 'FairValue' in data.columns:
        del data['FairValue']
    data.dropna(inplace=True)
period = 5
prepareData(training_data, period)
prepareData(validation_data, period)
prepareData(out_of_sample_test_data, period)

Etapa 4: Engenharia de características

Analisar o comportamento dos dados e criar recursos preditivos

Agora, a construção real do projeto começou. A regra de ouro da seleção de recursos é que a capacidade de previsão vem principalmente de recursos, não de modelos. Você descobrirá que a seleção de recursos tem um impacto muito maior no desempenho do que a seleção de modelos. Algumas considerações para a seleção de recursos:

  • Não selecione um grande conjunto de características aleatoriamente sem explorar a relação com a variável-alvo.

  • Pouca ou nenhuma relação com a variável-alvo pode conduzir a um sobreajuste.

  • As características selecionadas podem estar fortemente relacionadas entre si, caso em que um pequeno número de características também pode explicar o alvo.

  • Eu geralmente criei alguns recursos intuitivos, verifiquei a correlação entre a variável alvo e esses recursos, e a correlação entre eles para decidir qual usar.

  • Pode também tentar realizar a análise dos componentes principais (PCA) e outros métodos para classificar as características candidatas de acordo com o coeficiente máximo de informação (MIC).

Transformação/normalização das características:

Os modelos de ML tendem a ter um bom desempenho em termos de normalização. No entanto, a normalização é difícil quando se lida com dados de séries temporais, porque o intervalo de dados futuro é desconhecido. Seus dados podem estar fora do intervalo de normalização, levando a erros de modelo. Mas você ainda pode tentar forçar algum grau de estabilidade:

  • Escalagem: divisão de características por desvio padrão ou intervalo de quartil.

  • Centramento: subtrair o valor médio histórico do valor atual.

  • Normalização: dois períodos retrospectivos do anterior (x - média) /stdev.

  • Normalização regular: padronizar os dados na faixa de - 1 a +1 e redeterminar o centro dentro do período de retrocesso (x-min) / ((max min).

Observe que, uma vez que usamos o valor médio contínuo histórico, o desvio padrão, os valores máximos ou mínimos além do período de retrocesso, o valor de normalização normalizado do recurso representará diferentes valores reais em diferentes momentos. Por exemplo, se o valor atual do recurso for 5 e o valor médio para 30 períodos consecutivos for 4,5, ele será convertido para 0,5 após a centralização. Depois disso, se o valor médio de 30 períodos consecutivos for 3, o valor 3,5 se tornará 0,5. Isso pode ser a causa do modelo errado. Portanto, a normalização é complicada e você deve descobrir o que melhora o desempenho do modelo (se realmente houver).

Para a primeira iteração do nosso problema, criamos um grande número de características usando parâmetros mistos.

def difference(dataDf, period):
    return dataDf.sub(dataDf.shift(period), fill_value=0)
def ewm(dataDf, halflife):
    return dataDf.ewm(halflife=halflife, ignore_na=False,
                      min_periods=0, adjust=True).mean()
def rsi(data, period):
    data_upside = data.sub(data.shift(1), fill_value=0)
    data_downside = data_upside.copy()
    data_downside[data_upside > 0] = 0
    data_upside[data_upside < 0] = 0
    avg_upside = data_upside.rolling(period).mean()
    avg_downside = - data_downside.rolling(period).mean()
    rsi = 100 - (100 * avg_downside / (avg_downside + avg_upside))
    rsi[avg_downside == 0] = 100
    rsi[(avg_downside == 0) & (avg_upside == 0)] = 0
return rsi
def create_features(data):
    basis_X = pd.DataFrame(index = data.index, columns =  [])
    
    basis_X['mom3'] = difference(data['basis'],4)
    basis_X['mom5'] = difference(data['basis'],6)
    basis_X['mom10'] = difference(data['basis'],11)
    
    basis_X['rsi15'] = rsi(data['basis'],15)
    basis_X['rsi10'] = rsi(data['basis'],10)
    
    basis_X['emabasis3'] = ewm(data['basis'],3)
    basis_X['emabasis5'] = ewm(data['basis'],5)
    basis_X['emabasis7'] = ewm(data['basis'],7)
    basis_X['emabasis10'] = ewm(data['basis'],10)
    basis_X['basis'] = data['basis']
    basis_X['vwapbasis'] = data['stockVWAP']-data['futureVWAP']
    
    basis_X['swidth'] = data['stockTopAskPrice'] -
                        data['stockTopBidPrice']
    basis_X['fwidth'] = data['futureTopAskPrice'] -
                        data['futureTopBidPrice']
    
    basis_X['btopask'] = data['stockTopAskPrice'] -
                         data['futureTopAskPrice']
    basis_X['btopbid'] = data['stockTopBidPrice'] -
                         data['futureTopBidPrice']

    basis_X['totalaskvol'] = data['stockTotalAskVol'] -
                             data['futureTotalAskVol']
    basis_X['totalbidvol'] = data['stockTotalBidVol'] -
                             data['futureTotalBidVol']
    
    basis_X['emabasisdi7'] = basis_X['emabasis7'] -
                             basis_X['emabasis5'] + 
                             basis_X['emabasis3']
    
    basis_X = basis_X.fillna(0)
    
    basis_y = data['Y(Target)']
    basis_y.dropna(inplace=True)
    
    print("Any null data in y: %s, X: %s"
            %(basis_y.isnull().values.any(), 
             basis_X.isnull().values.any()))
    print("Length y: %s, X: %s"
            %(len(basis_y.index), len(basis_X.index)))
    
    return basis_X, basis_y
basis_X_train, basis_y_train = create_features(training_data)
basis_X_test, basis_y_test = create_features(validation_data)

Etapa 5: Selecção do modelo

Selecionar o modelo estatístico/ML adequado de acordo com as questões selecionadas

A escolha do modelo depende de como o problema é formado. Você está resolvendo supervisionado (cada ponto X na matriz de características é mapeado para a variável alvo Y) ou aprendizagem não supervisionada (sem um mapeamento dado, o modelo tenta aprender um padrão desconhecido)? Você está lidando com regressão (previsão do preço real no tempo futuro) ou classificação (apenas previsão da direção do preço no tempo futuro (aumento / diminuição))?

img

Aprendizagem supervisada ou não supervisada

img

Regressão ou classificação

Alguns algoritmos comuns de aprendizagem supervisionada podem ajudá-lo a começar:

  • Regressão linear (parâmetros, regressão)

  • Regressão logística (parâmetro, classificação)

  • Algoritmo K-Nearest Neighbor (KNN) (baseado em casos, regressão)

  • SVM, SVR (parâmetros, classificação e regressão)

  • Árvore de decisão

  • Floresta de decisão

Sugiro começar com um modelo simples, como a regressão linear ou logística, e construir modelos mais complexos a partir daí, conforme necessário. Também é recomendável que você leia a matemática por trás do modelo em vez de usá-lo como uma caixa preta cegamente.

Etapa 6: Treinamento, verificação e otimização (repetir as etapas 4-6)

img

Usar conjuntos de dados de treinamento e verificação para treinar e otimizar seu modelo

Agora você está pronto para finalmente construir o modelo. Nesta fase, você realmente apenas itera o modelo e os parâmetros do modelo. Treine seu modelo nos dados de treinamento, meça seu desempenho nos dados de verificação e, em seguida, retorne, otimize, treine novamente e avalie-o. Se você não estiver satisfeito com o desempenho do modelo, tente outro modelo. Você passa por esta fase muitas vezes até finalmente ter um modelo com o qual esteja satisfeito.

Só quando tiveres o teu modelo preferido, passa para o próximo passo.

Para o nosso problema de demonstração, vamos começar com uma regressão linear simples:

from sklearn import linear_model
from sklearn.metrics import mean_squared_error, r2_score
def linear_regression(basis_X_train, basis_y_train,
                      basis_X_test,basis_y_test):
    
    regr = linear_model.LinearRegression()
    # Train the model using the training sets
    regr.fit(basis_X_train, basis_y_train)
    # Make predictions using the testing set
    basis_y_pred = regr.predict(basis_X_test)
    # The coefficients
    print('Coefficients: \n', regr.coef_)
    
    # The mean squared error
    print("Mean squared error: %.2f"
          % mean_squared_error(basis_y_test, basis_y_pred))
    
    # Explained variance score: 1 is perfect prediction
    print('Variance score: %.2f' % r2_score(basis_y_test,
                                            basis_y_pred))
    # Plot outputs
    plt.scatter(basis_y_pred, basis_y_test,  color='black')
    plt.plot(basis_y_test, basis_y_test, color='blue', linewidth=3)
    plt.xlabel('Y(actual)')
    plt.ylabel('Y(Predicted)')
    plt.show()
    
    return regr, basis_y_pred
_, basis_y_pred = linear_regression(basis_X_train, basis_y_train, 
                                    basis_X_test,basis_y_test)

img

Regressão linear sem normalização

('Coefficients: \n', array([ -1.0929e+08, 4.1621e+07, 1.4755e+07, 5.6988e+06, -5.656e+01, -6.18e-04, -8.2541e-05,4.3606e-02, -3.0647e-02, 1.8826e+07, 8.3561e-02, 3.723e-03, -6.2637e-03, 1.8826e+07, 1.8826e+07, 6.4277e-02, 5.7254e-02, 3.3435e-03, 1.6376e-02, -7.3588e-03, -8.1531e-04, -3.9095e-02, 3.1418e-02, 3.3321e-03, -1.3262e-06, -1.3433e+07, 3.5821e+07, 2.6764e+07, -8.0394e+06, -2.2388e+06, -1.7096e+07]))
Mean squared error: 0.02
Variance score: 0.96

Vejamos os coeficientes do modelo. Nós não podemos realmente compará-los ou dizer qual deles é importante, porque todos eles pertencem a escalas diferentes. Vamos tentar a normalização para fazê-los conformar-se com a mesma proporção e também impor alguma suavidade.

def normalize(basis_X, basis_y, period):
    basis_X_norm = (basis_X - basis_X.rolling(period).mean())/
                    basis_X.rolling(period).std()
    basis_X_norm.dropna(inplace=True)
    basis_y_norm = (basis_y - 
                    basis_X['basis'].rolling(period).mean())/
                    basis_X['basis'].rolling(period).std()
    basis_y_norm = basis_y_norm[basis_X_norm.index]
    
    return basis_X_norm, basis_y_norm
norm_period = 375
basis_X_norm_test, basis_y_norm_test = normalize(basis_X_test,basis_y_test, norm_period)
basis_X_norm_train, basis_y_norm_train = normalize(basis_X_train, basis_y_train, norm_period)
regr_norm, basis_y_pred = linear_regression(basis_X_norm_train, basis_y_norm_train, basis_X_norm_test, basis_y_norm_test)
basis_y_pred = basis_y_pred * basis_X_test['basis'].rolling(period).std()[basis_y_norm_test.index] + basis_X_test['basis'].rolling(period).mean()[basis_y_norm_test.index]

img

Regressão linear com normalização

Mean squared error: 0.05
Variance score: 0.90

Este modelo não melhora o modelo anterior, mas não é pior. Agora podemos comparar os coeficientes para ver quais são realmente importantes.

Vejamos os coeficientes:

for i in range(len(basis_X_train.columns)):
    print('%.4f, %s'%(regr_norm.coef_[i], basis_X_train.columns[i]))

Os resultados são:

19.8727, emabasis4
-9.2015, emabasis5
8.8981, emabasis7
-5.5692, emabasis10
-0.0036, rsi15
-0.0146, rsi10
0.0196, mom10
-0.0035, mom5
-7.9138, basis
0.0062, swidth
0.0117, fwidth
2.0883, btopask
2.0311, btopbid
0.0974, bavgask
0.0611, bavgbid
0.0007, topaskvolratio
0.0113, topbidvolratio
-0.0220, totalaskvolratio
0.0231, totalbidvolratio

Podemos ver claramente que algumas características têm coeficientes mais elevados do que outras, e podem ter uma capacidade de previsão mais forte.

Vejamos a correlação entre diferentes características.

import seaborn

c = basis_X_train.corr()
plt.figure(figsize=(10,10))
seaborn.heatmap(c, cmap='RdYlGn_r', mask = (np.abs(c) <= 0.8))
plt.show()

img

Correlação entre características

As áreas vermelhas escuras representam variáveis altamente correlacionadas. Vamos criar/modificar algumas características novamente e tentar melhorar nosso modelo.

Por exemplo, posso descartar características como emabasisdi7 facilmente, que são apenas combinações lineares de outras características.

def create_features_again(data):
    basis_X = pd.DataFrame(index = data.index, columns =  [])
    basis_X['mom10'] = difference(data['basis'],11)
    basis_X['emabasis2'] = ewm(data['basis'],2)
    basis_X['emabasis5'] = ewm(data['basis'],5)
    basis_X['emabasis10'] = ewm(data['basis'],10)
    basis_X['basis'] = data['basis']
    basis_X['totalaskvolratio'] = (data['stockTotalAskVol']
                                 - data['futureTotalAskVol'])/
                                   100000
    basis_X['totalbidvolratio'] = (data['stockTotalBidVol']
                                 - data['futureTotalBidVol'])/
                                   100000
    basis_X = basis_X.fillna(0)
    
    basis_y = data['Y(Target)']
    basis_y.dropna(inplace=True)
    return basis_X, basis_y
basis_X_test, basis_y_test = create_features_again(validation_data)
basis_X_train, basis_y_train = create_features_again(training_data)
_, basis_y_pred = linear_regression(basis_X_train, basis_y_train, basis_X_test,basis_y_test)
basis_y_regr = basis_y_pred.copy()

img

('Coefficients: ', array([ 0.03246139,
0.49780982, -0.22367172,  0.20275786,  0.50758852,
-0.21510795, 0.17153884]))
Mean squared error: 0.02
Variance score: 0.96

Olhe, o desempenho do nosso modelo não mudou. Só precisamos de algumas características para explicar as nossas variáveis-alvo. Sugiro que tente mais das características acima, tente novas combinações, etc., para ver o que pode melhorar o nosso modelo.

Também podemos tentar modelos mais complexos para ver se as alterações nos modelos podem melhorar o desempenho.

  • Algoritmo K-Nearest Neighbor (KNN)
from sklearn import neighbors
n_neighbors = 5
model = neighbors.KNeighborsRegressor(n_neighbors, weights='distance')
model.fit(basis_X_train, basis_y_train)
basis_y_pred = model.predict(basis_X_test)
basis_y_knn = basis_y_pred.copy()

img

  • SVR
from sklearn.svm import SVR
model = SVR(kernel='rbf', C=1e3, gamma=0.1)
model.fit(basis_X_train, basis_y_train)
basis_y_pred = model.predict(basis_X_test)
basis_y_svr = basis_y_pred.copy()

img

  • Árvore de decisão
model=ensemble.ExtraTreesRegressor()
model.fit(basis_X_train, basis_y_train)
basis_y_pred = model.predict(basis_X_test)
basis_y_trees = basis_y_pred.copy()

img

Etapa 7: Reexame dos dados do ensaio

Verificar o desempenho dos dados reais da amostra

img

Desempenho dos testes de regresso em conjuntos de dados de ensaio (não tocados)

Este é um momento crítico. Nós executamos o nosso modelo de otimização final a partir do último passo dos dados de teste, nós deixá-lo de lado no início e não tocou os dados até agora.

Isso lhe dá uma expectativa realista de como seu modelo irá executar em dados novos e invisíveis quando você começar a negociar em tempo real.

Se você não gosta dos resultados do backtest dos dados do teste, por favor, descarte o modelo e comece de novo. Nunca volte ou re-otimize seu modelo, o que levará ao over-fitting! (Também é recomendável criar um novo conjunto de dados do teste, porque este conjunto de dados está agora poluído; ao descartar o modelo, já sabemos o conteúdo do conjunto de dados implicitamente).

Aqui ainda vamos usar a caixa de ferramentas da Auquan:

import backtester
from backtester.features.feature import Feature
from backtester.trading_system import TradingSystem
from backtester.sample_scripts.fair_value_params import FairValueTradingParams
class Problem1Solver():
def getTrainingDataSet(self):
        return "trainingData1"
def getSymbolsToTrade(self):
        return ['MQK']
def getCustomFeatures(self):
        return {'my_custom_feature': MyCustomFeature}
def getFeatureConfigDicts(self):
                            
        expma5dic = {'featureKey': 'emabasis5',
                 'featureId': 'exponential_moving_average',
                 'params': {'period': 5,
                              'featureName': 'basis'}}
        expma10dic = {'featureKey': 'emabasis10',
                 'featureId': 'exponential_moving_average',
                 'params': {'period': 10,
                              'featureName': 'basis'}}                     
        expma2dic = {'featureKey': 'emabasis3',
                 'featureId': 'exponential_moving_average',
                 'params': {'period': 3,
                              'featureName': 'basis'}}
        mom10dic = {'featureKey': 'mom10',
                 'featureId': 'difference',
                 'params': {'period': 11,
                              'featureName': 'basis'}}
        
        return [expma5dic,expma2dic,expma10dic,mom10dic]    
    
    def getFairValue(self, updateNum, time, instrumentManager):
        # holder for all the instrument features
        lbInstF = instrumentManager.getlookbackInstrumentFeatures()
        mom10 = lbInstF.getFeatureDf('mom10').iloc[-1]
        emabasis2 = lbInstF.getFeatureDf('emabasis2').iloc[-1]
        emabasis5 = lbInstF.getFeatureDf('emabasis5').iloc[-1]
        emabasis10 = lbInstF.getFeatureDf('emabasis10').iloc[-1] 
        basis = lbInstF.getFeatureDf('basis').iloc[-1]
        totalaskvol = lbInstF.getFeatureDf('stockTotalAskVol').iloc[-1] - lbInstF.getFeatureDf('futureTotalAskVol').iloc[-1]
        totalbidvol = lbInstF.getFeatureDf('stockTotalBidVol').iloc[-1] - lbInstF.getFeatureDf('futureTotalBidVol').iloc[-1]
        
        coeff = [ 0.03249183, 0.49675487, -0.22289464, 0.2025182, 0.5080227, -0.21557005, 0.17128488]
        newdf['MQK'] = coeff[0] * mom10['MQK'] + coeff[1] * emabasis2['MQK'] +\
                      coeff[2] * emabasis5['MQK'] + coeff[3] * emabasis10['MQK'] +\
                      coeff[4] * basis['MQK'] + coeff[5] * totalaskvol['MQK']+\
                      coeff[6] * totalbidvol['MQK']
                    
        newdf.fillna(emabasis5,inplace=True)
        return newdf
problem1Solver = Problem1Solver()
tsParams = FairValueTradingParams(problem1Solver)
tradingSystem = TradingSystem(tsParams)
tradingSystem.startTrading(onlyAnalyze=False, 
                           shouldPlot=True,
                           makeInstrumentCsvs=False)

img

Resultados dos backtesting, Pnl é calculado em USD (Pnl não está incluído nos custos de transacção e outras taxas)

Etapa 8: Outros métodos para melhorar o modelo

Verificação de rolamento, aprendizagem de conjuntos, ensacamento e impulso

Além de coletar mais dados, criar melhores recursos ou experimentar mais modelos, há mais alguns pontos que você pode tentar melhorar.

1. Verificação em rolamento

img

Verificação em rolamento

As condições do mercado raramente permanecem as mesmas. Suponha que você tenha dados de um ano, e use os dados de janeiro a agosto para treinamento, e use os dados de setembro a dezembro para testar seu modelo. Você pode treinar para um conjunto muito específico de condições do mercado eventualmente. Talvez não tenha havido flutuação do mercado no primeiro semestre do ano, e algumas notícias extremas levaram a uma alta acentuada no mercado em setembro. Seu modelo não será capaz de aprender esse modelo, e ele lhe trará resultados de previsão de lixo.

Pode ser melhor tentar uma verificação contínua, como a formação de Janeiro a Fevereiro, a verificação em Março, a reciclagem de Abril a Maio, a verificação em Junho, etc.

2. Aprendizagem por conjuntos

img

Aprendizagem de conjuntos

Alguns modelos podem ser muito eficazes na previsão de certos cenários, enquanto os modelos podem ser extremamente sobreajustados na previsão de outros cenários ou sob certas circunstâncias. Uma maneira de reduzir erros e sobreajuste é usar um conjunto de modelos diferentes. Sua previsão será a média das previsões feitas por muitos modelos, e os erros de diferentes modelos podem ser compensados ou reduzidos.

img

Embalagem

img

Reforço

Por razões de brevidade, deixarei de lado estes métodos, mas pode encontrar mais informações online.

Vamos tentar um método conjunto para o nosso problema:

basis_y_pred_ensemble = (basis_y_trees + basis_y_svr +
                         basis_y_knn + basis_y_regr)/4

img

Mean squared error: 0.02
Variance score: 0.95

Até agora, acumulamos muito conhecimento e informação.

  • Resolva o seu problema;

  • Recolher dados fiáveis e limpar os dados;

  • Dividir os dados em conjuntos de formação, verificação e ensaios;

  • Criar características e analisar seus comportamentos;

  • Selecionar o modelo de formação adequado de acordo com o comportamento;

  • Usar dados de treinamento para treinar seu modelo e fazer previsões;

  • Verificar o desempenho do conjunto de verificação e re-otimizar;

  • Verificar o desempenho final do conjunto de ensaio.

Mas ainda não terminou. Você só tem um modelo de previsão confiável. Lembre-se do que realmente queríamos em nossa estratégia?

  • Desenvolver sinais baseados em modelos preditivos para identificar as direcções de negociação;

  • Desenvolver estratégias específicas para identificar posições abertas e fechadas;

  • Execução do sistema de identificação de posições e preços.

A empresa utilizará a plataforma FMZ Quant (FMZ.COMNa plataforma FMZ Quant, existem interfaces API altamente encapsuladas e perfeitas, bem como funções de ordenação e negociação que podem ser chamadas globalmente. Você não precisa conectar e adicionar interfaces API de diferentes exchanges uma a uma. Na quadrada de Estratégia da plataforma FMZ Quant, existem muitas estratégias alternativas maduras e perfeitas que combinam com o método de aprendizado de máquina neste artigo, o que tornará sua estratégia específica mais poderosa.https://www.fmz.com/square.

** Nota importante sobre os custos de transação: ** Seu modelo informará quando o ativo selecionado está indo longo ou indo curto. No entanto, ele não considera taxas / custos de transação / quantidade de negociação disponível / stop loss, etc. Os custos de transação geralmente transformam transações lucrativas em perdas. Por exemplo, um ativo com um aumento de preço esperado de US $ 0,05 é uma compra, mas se você tiver que pagar US $ 0,10 por essa transação, você obterá uma perda líquida de US $ 0,05 eventualmente. Depois de levar em conta a comissão, a taxa de câmbio e a diferença de pontos do corretor, nosso grande gráfico de lucro acima parece assim:

img

O resultado do backtest após taxas de negociação e diferença de pontos, Pnl é USD.

As taxas de transacção e as diferenças de preços representam mais de 90% do nosso PNL!

Por fim, vamos analisar algumas armadilhas comuns.

O que fazer e o que não

  • Evite exagerar com todas as suas forças!

  • Não treine novamente após cada ponto de dados: este é um erro comum que as pessoas cometem no desenvolvimento de aprendizado de máquina. Se o seu modelo precisa ser treinado após cada ponto de dados, pode não ser um modelo muito bom. Ou seja, ele precisa ser treinado regularmente e só precisa ser treinado com uma frequência razoável (por exemplo, se a previsão intra-diária for feita, ele precisa ser treinado no final de cada semana).

  • Evite viés, especialmente viés prospectivo: esta é outra razão pela qual o modelo não funciona, e certifique-se de que você não use nenhuma informação futura. Na maioria dos casos, isso significa que a variável alvo Y não é usada como um recurso no modelo. Você pode usá-la durante o backtesting, mas não estará disponível quando você executar o modelo, o que tornará seu modelo inutilizável.

  • Cuidado com o viés de mineração de dados: como estamos tentando realizar uma série de modelagem em nossos dados para determinar se é apropriado, se não houver uma razão especial, certifique-se de executar testes rigorosos para separar o modo aleatório do modo real que pode ocorrer. Por exemplo, a regressão linear explica bem o padrão de tendência ascendente, mas é provável que se torne uma fração das maiores vagas aleatórias!

Evitar o excesso de ajuste

É muito importante e penso que é necessário relembrá-lo.

  • O excesso de ajustamento é a armadilha mais perigosa nas estratégias de negociação;

  • Um algoritmo complexo pode ter um desempenho muito bom no backtest, mas falha miseravelmente nos novos dados invisíveis.

  • Mantenha o seu sistema o mais simples possível. Se você achar que precisa de muitas funções complexas para interpretar dados, você pode se adaptar demais;

  • Divida os dados disponíveis em dados de formação e de teste e verifique sempre o desempenho dos dados reais da amostra antes de utilizar o modelo para transacções em tempo real.


Relacionados

Mais.