El artículo original número 115 se centra en "el crecimiento personal y la libertad de riqueza, la lógica de la operación mundial, la inversión cuantitativa de IA".
El sobreajuste es un fenómeno muy común durante el entrenamiento de modelos. El llamado sobreajuste significa que el rendimiento en el conjunto de entrenamiento es muy bueno, pero el rendimiento en el conjunto de prueba es muy bajo. Para reducir el sobreajuste y mejorar la capacidad de generalización del modelo, existen muchas medidas para aliviar el problema del sobreajuste en la práctica. Uno de los métodos comunes es retener una parte de los datos en el conjunto de datos existente como conjunto de prueba, es decir, dividir los datos originales en X_train, X_test, X_train se usa para entrenar el modelo y X_test se usa para verificar y ajustar el modelo.
La estrategia cuantitativa impulsada por el aprendizaje automático se diferencia de la cuantificación tradicional en que sus señales comerciales las proporciona el modelo, por lo que debe seguir el proceso de aprendizaje automático. El aprendizaje automático necesita dividir el conjunto de datos: conjunto de entrenamiento, conjunto de prueba. El modelo se "aprende" en el conjunto de entrenamiento y las transacciones entrenadas se evalúan en el conjunto de prueba.
La diferencia con el muestreo aleatorio de aprendizaje automático tradicional es que, dado que necesitamos realizar pruebas retrospectivas "continuas" en el conjunto de prueba, no podemos mezclar aleatoriamente la población general y formar parte de ella, sino dividir el período en dos segmentos proporcionalmente. Una sección se usa como conjunto de entrenamiento y una sección se usa como conjunto de prueba. Por lo tanto, en lugar de utilizar la función train_test_split de sklearn, implementamos una función de segmentación de conjuntos de datos para series temporales financieras.
La más utilizada es seleccionar por fecha. Por ejemplo, los datos anteriores al día "2017-01-01" se utilizarán como conjunto de entrenamiento para entrenar el modelo, y los datos posteriores a este día se utilizarán como conjunto de prueba. , es decir, para futuros backtesting intervalo de tiempo.
El código central es la siguiente función:
x_cols son todas las columnas de características e y_col es la columna de etiquetas.Si la fecha dividida está vacía, la fecha se calculará de acuerdo con el 80% del conjunto de entrenamiento.
El formato de datos devuelto es similar a train_test_split de sklearn.
import numpy as np import datetime as dt from engine.datafeed.dataloader import Dataloader class OneStepTimeSeriesSplit: """Genera tuplas de pares train_idx, test_idx Supone que el índice contiene un nivel etiquetado como 'fecha'""" 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 trozos(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=Ninguno, grupos=Ninguno): fechas_únicas = (X.índice # .get_level_values('date') .unique() .sort_values(ascending=False) [:self.n_splits * self.test_period_length]) fechas = X.reset_index()[['date']] para test_date en self.chunks (fechas_únicas, self.test_period_length): train_idx = fechas[fechas.fecha < min(fecha_prueba)].index test_idx = fechas[fechas.fecha.esin(fecha_prueba)].index si self.shuffle: np.random.shuffle(lista (train_idx)) yield train_idx, test_idx def get_n_splits(self, X, y, groups=Ninguno): 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=Ninguno, split_ratio=0.8): if not 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] # Crear conjuntos de entrenamiento y prueba 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_prueba = salida_datos[salida_datos.índice >= fecha_dividida] return X_tren, X_prueba, Y_tren, Y_prueba clase Conjunto de datos: def __init__(self, símbolos, nombres_características, campos_características, fecha_dividida, nombre_etiqueta='etiqueta' , label_field=Ninguno): 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)' if label_field es Ninguno más label_field nombres = self.feature_names + [self.label_name] campos = self.feature_fields + [self.label_field] loader = Dataloader() self.df = loader.load_one_df(símbolos, nombres, campos) def get_test_data(self): def get_split_dataset(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, 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 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) devuelve X_test, Y_test def get_X_y_data(self): X = self.df[self.feature_names] y = self.df[self.label_name] devuelve X, y si __name__ == '__main__': códigos = ['000300.SH', 'SPX'] nombres = [] campos = [] campos += ["Corr($cerrar /Ref($cerrar,1), Log($volumen/Ref($volumen, 1)+1), 30)"] nombres += ["CORR30"] conjunto de datos = Conjunto de datos(códigos, nombres, campos, split_date=' 2020-01-01') X_tren, Y_tren = conjunto de datos.get_train_data() print(X_tren, Y_tren)
Entrenamiento modelo:
códigos = ['000300.SH', '399006.SZ'] nombres = [] campos = [] campos += ["Corr($cerrar/Ref($cerrar,1), Log($volumen/Ref($volumen) , 1)+1), 30)"] nombres += ["CORR30"] campos += ["Corr($cerrar/Ref($cerrar,1), Log($volumen/Ref($volumen, 1)+ 1), 60)"] nombres += ["CORR60"] campos += ["Ref($cerrar, 5)/$cerrar"] nombres += ["ROC5"] campos += ["(2*$cerrar -$high-$low)/$open"] nombres += ['KSFT'] campos += ["($close-Min($low, 5))/(Max($high, 5)-Min($ bajo, 5)+1e-12)"] nombres += ["RSV5"] campos += ["($alto-$bajo)/$abierto"] nombres += ['KLEN'] campos += ["$ cerca"] nombres += ['cerrar'] campos += ['KF(Pendiente($cerrar,20))'] nombres += ['KF'] campos += ['$cerrar/Ref($cerrar,20)-1'] nombres += ['ROC_20'] campos += ['KF($ROC_20)'] nombres += ['KF_ROC_20'] conjunto de datos = Conjunto de datos (códigos, nombres, campos, split_date='2020-01-01') de sklearn.ensemble importar RandomForestClassifier de sklearn.svm importar LinearSVC, SVC de sklearn.linear_model importar LogisticRegression de engine.ml.boosting_models importar gb_clf para el modelo en [gb_clf , LogisticRegression(), RandomForestClassifier(), SVC()]: m = ModelRunner(modelo, conjunto de datos) m.fit() m.predict()
El aprendizaje de conjuntos es mucho mejor que el aprendizaje automático tradicional para los resultados de datos tabulares. El mismo conjunto de datos, el mejor es Sensen aleatorio, que en realidad es lightGBM (versión sin sintonizar).
Continuar mañana.
Rotación de ETF + tiempo RSRS, más filtro Kaman: 48,41 % anualizado, ratio de Sharpe 1,89
Rotación de impulso de ETF + sincronización del mercado: estrategia anualizada del 30 %
Mis proyectos de código abierto y Knowledge Planet