Aprendizaje automático financiero: división de conjuntos de datos y modelo de referencia

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

 

Supongo que te gusta

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