Блог им. Riskplayer
df["Tomorrow"] = df["Close"].shift(-1) df["Target"] = (df["Tomorrow"] > df["Close"]).astype(int) # наша цельОчень важно, какие данные будут использоваться для прогнозирования. Здесь используется: показатель силы закрытия бара (т.е. (Close-Low)/(High-Low)) за текущий и предыдущий день, процентные соотношения между ценой закрытия и средними за периоды 2,10,15,25,50 дней по индексам IMOEX, RVI, RGBITR, и плюс цены закрытия индексов RVI, RGBITR.
train = df.loc['2013':'2022'] test = df.loc['2023':]Для создания модели используется <a href=«scikit-learn. org/stable/modules/generated/sklearn.ensemble.RandomForestClassifier.html»>RandomForestClassifier из библиотеки scikit-learn. Подгоняется только один параметр min_samples_split (минимальное число объектов, необходимое для того, чтобы узел дерева мог бы расщепиться), по умолчанию этот параметр равен 2, но для того, чтобы модель была не слишком переобученной, подгонка будет идти от 50 до 200 с шагом 10. Подгонка идет через кросс-валидацию TimeSeriesSplit с помощью процедуры GridSearchCV.
tscv = TimeSeriesSplit() model = RandomForestClassifier(random_state = 0) forest_params = {"min_samples_split": range(50,201,10)} forest_grid = GridSearchCV(model, forest_params, n_jobs=-1, verbose = 1, cv = tscv) forest_grid.fit(train[new_predictors], train['Target'])По итогу кросс-валидации получилось, что лучший параметр и лучший оценка:
best_params_ {'min_samples_split': 70} best_score_ = 0.4986149584487535
import pandas as pd ticker = 'imoex'.upper() filename = "..\\h5\\moex.h5" # здесь хранятся минутки with pd.HDFStore(filename) as store: store = pd.HDFStore(filename) data_1min = store[ticker] rgbitr_1min = store['RGBITR'] rvi_1min = store['RVI'] def convert_to_daily(intraday_data): return intraday_data.between_time('10:00', '18:40').resample('D').agg({'Open': 'first', 'High': 'max', 'Low': 'min', 'Close': 'last'}).dropna() df = convert_to_daily(data_1min) rgbitr = rgbitr_1min.between_time('10:00', '18:40').resample('D').agg({'Close': 'last'}).dropna() rgbitr = rgbitr.rename(columns = {'Close' : 'rgbitr'}) rvi = rvi_1min.between_time('10:00', '18:40').resample('D').agg({'Close': 'last'}).dropna() rvi = rvi.rename(columns = {'Close' : 'rvi'}) df = pd.concat([df, rgbitr, rvi], axis = 1) df["Tomorrow"] = df["Close"].shift(-1) df["Target"] = (df["Tomorrow"] > df["Close"]).astype(int) df['ibs'] = (df['Close'] - df['Low'])/(df['High'] - df['Low']) df['ibs1'] = df['ibs'].shift() df['pct_chg'] = df['Close'].pct_change() df = df.dropna() new_predictors = ['ibs','ibs1', 'rgbitr', 'rvi' ] horizons = [2,10,15,25,50] for col in ['Close', 'rgbitr', 'rvi']: for horizon in horizons: rolling_averages = df.rolling(horizon).mean() ratio_column = f"{col}_Ratio_{horizon}" df.loc[:, ratio_column] = df[col] / rolling_averages[col] - 1 new_predictors += [ratio_column] df = df.dropna() train = df.loc['2013':'2022'] test = df.loc['2023':] from sklearn.ensemble import RandomForestClassifier from sklearn.model_selection import GridSearchCV, cross_val_score from sklearn.model_selection import TimeSeriesSplit tscv = TimeSeriesSplit() model = RandomForestClassifier(random_state = 0) forest_params = {"min_samples_split": range(50,201,10)} forest_grid = GridSearchCV(model, forest_params, n_jobs=-1, verbose = 1, cv = tscv) forest_grid.fit(train[new_predictors], train['Target']) print(f"best_params_ {forest_grid.best_params_}") print(f"best_score_ = {forest_grid.best_score_}") test_preds = forest_grid.predict(test[new_predictors]) test = test.assign( preds = test_preds) test['pos'] = test['preds'] test['pos'] = test['pos'].replace(0, -1) test['ret'] = test['pos'].shift()*test['pct_chg'] import matplotlib.pyplot as plt fig, axs = plt.subplots(1,2,figsize=(12, 4)) axs[0].axis([0, 10, 0, 10]) axs[0].text(1, 9, f"{ticker}") axs[0].text(1, 7, f"min_samples_split = {forest_grid.best_params_['min_samples_split']}") test['ret'].cumsum().plot(ax=axs[1]) axs[1].grid() axs[1].set_title(f"test")
best_score — лучшая средняя оценка после кросс-валидации.
Хотя, как ни странно, на тестовом периоде оценка будет 0,617.
Не густо фичей).
А как выглядит график зависимости метрики качества от min_samples_split?
Выглядит он, скажем прямо, так себе.
Код, в основном, сам писал.