Machine Learning financeiro: divisão de conjunto de dados e modelo de linha de base

O 115º artigo original enfoca "crescimento pessoal e liberdade de riqueza, a lógica da operação mundial, investimento quantitativo em IA".

O overfitting é um fenômeno muito comum durante o treinamento do modelo. O chamado overfitting significa que o desempenho no conjunto de treinamento é muito bom, mas o desempenho no conjunto de teste é muito ruim. A fim de reduzir o overfitting e melhorar a capacidade de generalização do modelo, existem muitas medidas para aliviar o problema de overfitting na prática. Um dos métodos comuns é reter uma parte dos dados no conjunto de dados existente como o conjunto de teste, ou seja, dividir os dados originais em X_train, X_test, X_train é usado para treinar o modelo e X_test é usado para verificar e ajustar o modelo.

A estratégia quantitativa orientada pelo aprendizado de máquina é diferente da quantificação tradicional, pois seus sinais de negociação são dados pelo modelo, portanto, precisa seguir o processo de aprendizado de máquina. O aprendizado de máquina precisa dividir o conjunto de dados: conjunto de treinamento, conjunto de teste. O modelo é "aprendido" no conjunto de treinamento e as transações treinadas são avaliadas no conjunto de teste.

A diferença da amostragem aleatória de aprendizado de máquina tradicional é que, como precisamos fazer backtesting "contínuo" no conjunto de teste, não podemos embaralhar aleatoriamente a população geral e fazer parte dela, mas dividir o período em dois segmentos proporcionalmente. Uma seção é usada como conjunto de treinamento e uma seção é usada como conjunto de teste. Portanto, em vez de usar a função train_test_split do sklearn, implementamos uma função de segmentação de conjunto de dados para séries temporais financeiras.

O mais comumente usado é selecionar por data. Por exemplo, os dados anteriores ao dia "2017-01-01" serão usados ​​como conjunto de treinamento para treinar o modelo e os dados posteriores a esse dia serão usados ​​como conjunto de teste , ou seja, para backtesting futuro. intervalo de tempo.

O código principal é a seguinte função:

x_cols são todas as colunas de feição e y_col é a coluna de rótulo.Se a data dividida estiver vazia, a data será calculada de acordo com 80% do conjunto de treinamento.

O formato de dados retornado é semelhante ao train_test_split do sklearn.

import numpy as np 
import datetime as dt 
from engine.datafeed.dataloader import Dataloader 


class OneStepTimeSeriesSplit: 
    """Gera tuplas de pares train_idx, test_idx 
    Assume que o índice contém um nível rotulado como 'date'""" 

    def __init__(self, n_splits=3 , test_period_length=1, shuffle=False): 
        self.n_splits = n_splits 
        self.test_period_length = test_period_length 
        self.shuffle = shuffle 

    @staticmethod 
    def chunks(l, n): 
        for i in range(0, len(l), n): 
            print(l[i:i + n]) 
            yield l[i:i + n] 

    def split(self, X, y=None, groups=None): 
        unique_dates = (X.índice
                        # .get_level_values('date') 
                        .unique() 
                        .sort_values(ascending=False) 
        [:self.n_splits * self.test_period_length]) 

        datas = X.reset_index()[['date']] 
        for test_date in self.chunks (unique_dates, self.test_period_length): 
            train_idx = datas[dates.date < min(test_date)].index 
            test_idx = datas[dates.date.isin(test_date)].index 
            if self.shuffle: 
                np.random.shuffle(list (train_idx)) 
            yield train_idx, test_idx 

    def get_n_splits(self, X, y, groups=None): 
        return self.n_splits


def get_date_by_percent(start_date, end_date, percent): 
    days = (end_date - start_date).days 
    target_days = np.trunc(days * percent) 
    target_date = start_date + dt.timedelta(days=target_days) 
    # print days, target_days,target_date 
    return target_date 


def split_df(df, x_cols, y_col, split_date=None, split_ratio=0.8): 
    se não split_date: 
        split_date = get_date_by_percent(df.index[0], df.index[df.shape[0] - 1], split_ratio) 

    input_data = df[x_cols] 
    output_data = df[y_col] 

    # Criar conjuntos de treinamento e teste 
    X_train = input_data[input_data.index < split_date] 
    X_test = input_data[input_data.index >= split_date] 
    Y_train = output_data[output_data.index < split_date]
    Y_test = output_data[output_data.index >= split_date] 

    return X_train, X_test, Y_train, Y_test 


class Dataset: 
    def __init__(self, symbol, feature_names, feature_fields, split_date, label_name='label', label_field=None): 
        self.feature_names = feature_names 
        self.feature_fields = feature_fields 
        self.split_date = split_date 
        self.label_name = label_name 
        self.label_field = 'Sign(Ref($close,-1)/$close -1)' se label_field for None else label_field 

        names = self.feature_names + [self.label_name] 
    def get_split_dataset(self): 
        campos = self.feature_fields + [self.label_field]
        loader = Dataloader() 
        self.df = loader.load_one_df(símbolos, nomes, campos) 

        X_train, X_test, Y_train, Y_test = split_df(self.df, x_cols=self.feature_names, y_col=self.label_name, 
                                                    split_date=self.split_date ) 
        return X_train, X_test, Y_train, Y_test 

    def get_train_data(self): 
        X_train, X_test, Y_train, Y_test = split_df(self.df, x_cols=self.feature_names, y_col=self.label_name, 
                                                    split_date=self.split_date) 
        return X_train, Y_train 

    def get_test_data(self):
        X_train, X_test, Y_train, Y_test = split_df(self.df, x_cols=self.feature_names, y_col=self.label_name, 
                                                    split_date=self.split_date) 
        return X_test, Y_test

    def get_X_y_data(self): 
        X = self.df[self.feature_names] 
        y = self.df[self.label_name] 
        return X, y 




if __name__ == '__main__': 
    codes = ['000300.SH', 'SPX' ] 
    nomes = [] 
    campos = [] 
    campos += ["Corr($close/Ref($close,1), Log($volume/Ref($volume, 1)+1), 30)"] nomes + 
    = ["CORR30"] 

    dataset = Dataset(códigos, nomes, campos, split_date='2020-01-01') 
    X_train, Y_train = dataset.get_train_data() 
    print(X_train, Y_train)

Treinamento modelo:

codes = ['000300.SH', '399006.SZ'] 
nomes = [] 
campos = [] 

campos += ["Corr($close/Ref($close,1), Log($volume/Ref($volume , 1)+1), 30)"] 
nomes += ["CORR30"] 

campos += ["Corr($close/Ref($close,1), Log($volume/Ref($volume, 1)+ 1), 60)"] 
nomes += ["CORR60"] 

campos += ["Ref($close, 5)/$close"] 
nomes += ["ROC5"] 

campos += ["(2*$close -$high-$low)/$open"] 
nomes += ['KSFT'] 

campos += ["($close-Min($low, 5))/(Max($high, 5)-Min($ low, 5)+1e-12)"] 
nomes += ["RSV5"] 

campos += ["($high-$low)/$open"] 
nomes += ['KLEN'] 

campos += ["$ fechar"] 
nomes += ['fechar'] 

campos += ['KF(Slope($close,20))'] 
nomes += ['KF']

campos += ['$close/Ref($close,20)-1'] 
nomes += ['ROC_20'] 

campos += ['KF($ROC_20)'] 
nomes += ['KF_ROC_20'] 

conjunto de dados = Conjunto de dados (codes, names, fields, split_date='2020-01-01') 

from sklearn.ensemble import RandomForestClassifier 
from sklearn.svm import LinearSVC, SVC 
from sklearn.linear_model import LogisticRegression 
from engine.ml.boosting_models import gb_clf 

for model in [gb_clf , LogisticRegression(), RandomForestClassifier(), SVC()]: 
    m = ModelRunner(modelo, conjunto de dados) 
    m.fit() 
    m.predict()

O aprendizado conjunto é muito melhor do que o aprendizado de máquina tradicional para resultados de dados tabulares. O mesmo conjunto de dados, o melhor é Sensen aleatório, que na verdade é lightGBM (versão desafinada).

Continua amanhã.

Rotação ETF + tempo RSRS, mais filtro Kaman: anualizado 48,41%, índice de Sharpe 1,89

Rotação de momentum do ETF + timing de mercado: estratégia 30% anualizada

Meus projetos de código aberto e planeta do conhecimento

 

Acho que você gosta

Origin blog.csdn.net/weixin_38175458/article/details/128042440
Recomendado
Clasificación