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