[機械学習・進化計算による株式取引最適化] No.02-04 学習データセット作成

このプログラムの目的

これまでのプログラムで各銘柄についての学習データを生成できました.
このプログラムではすべての銘柄の学習データを縦方向に結合することです.
また,時系列を扱うにあたり,-n時間までの過去の特徴量も学習データとして追加します.

work_share
├02_get_stock_price
  ├Dockerfile
  ├docker-compose.yml
  └src
    ├dataset_2018-2023
    | ├clusterling_dataset_dist.dfpkl
    | ├clusterling_dataset_mean.dfpkl
    | ├original_dataset.dfpkl
    | └learning_dataset.dfpkl (自動生成)
    ├original_data_2018-01-01_2023-01-01
    | ├1301.csv
    | ├1305.csv
    | └...
    ├time_cluster_result
    ├get_stock_price.py
    ├make_dataset.py (これを作成)
    ├make_original_data.py
    ├make_time_cluster_dataset.py
    └stocks_code.xls

使用ライブラリ

import pandas as pd
import numpy as np
import tqdm

import glob
import os

from sklearn.preprocessing import StandardScaler

元データのロード

original_dataset.dfpklclusterling_dataset_mean.dfpklclusterling_dataset_dist.dfpklをロードします.
このときoriginal_dataset.dfpklはStandardScalerで正規化します.データの結合の前に正規化をしないと,銘柄による価格差が正規化の値に現れてしまいます.また,利益計算などに元の価格も必要なので,original_dfとして保持しておきます.
さらに,学習時に学習データとテストデータを分けやすいようにevalとしてtrainなのかtestなのかを記録しておきます.

def df_split(df, train_rate):
    train_num = int(len(df) * train_rate)
    train_df = df.iloc[:train_num, :].reset_index(drop=True)
    test_df = df.iloc[train_num:, :].reset_index(drop=True)
    return train_df, test_df

def make_dataset(args):
    original_df = pd.read_pickle(args['original_dataset_path'])
    class_mean_df = pd.read_pickle(args['class_mean_dataset_path'])
    class_dist_df = pd.read_pickle(args['class_dist_dataset_path'])

    print(args['drop_codes'])
    original_df = original_df.drop(args['drop_codes'], axis=1)

    scaler_df = original_df.copy()
    scaler_df = scaler_df.reset_index(drop=True)

    train_df, _ = df_split(scaler_df, args['train_rate'])
    scaler = StandardScaler()
    scaler.fit(train_df)
    scaler_df = pd.DataFrame(data=scaler.transform(scaler_df), columns=scaler_df.columns)
    scaler_df['eval'] = 'test'
    train_num = int(len(scaler_df) * args['train_rate'])
    scaler_df.loc[:train_num, 'eval'] = 'train'

    dfs = {
        'original_df':original_df,
        'scaler_df':scaler_df,
        'class_mean_df':class_mean_df,
        'class_dist_df':class_dist_df
    }
    save_dataset(dfs, args)

学習データセットの作成と保存

ロードしたデータを基に学習データセットを作成します.
また,各時系列データにおけるtime_lengthの長さまでのt0, t1, …, tNを特徴量として保存します.

def save_dataset(dfs, args):
    original_df = dfs['original_df']
    scaler_df = dfs['scaler_df']
    class_mean_df = dfs['class_mean_df']
    class_dist_df = dfs['class_dist_df']

    original_df = original_df.reset_index()
    next_original_df = original_df.shift(-1).reset_index(drop=True)

    date = pd.to_datetime(original_df['Date'])
    original_df = original_df.drop('Date', axis=1)

    df_list = []
    for stock_code in tqdm.tqdm(original_df.columns):
        df = pd.DataFrame({
            'date':date,
            'eval':scaler_df['eval'],
            'original_value':original_df[stock_code],
            'next_original_value':next_original_df[stock_code],
            #'value':original_df[stock_code],
        })
        Xt = pd.DataFrame({
            f'value_t{i}':scaler_df[stock_code].shift(i).values for i in range(args['time_length']+1)
        })
        df = pd.concat([df, Xt], axis=1)

        for col in class_mean_df.columns:
            temp_Xt = pd.DataFrame({
                f'{col}_t{i}':class_mean_df[col].shift(i).values for i in range(args['time_length']+1)
            })
            df = pd.concat([df, temp_Xt], axis=1)

        for class_name, class_dist in class_dist_df[stock_code].to_dict().items():
            df[f'{class_name} dist'] = class_dist
        df['code'] = stock_code

        df = df.loc[args['time_length']+1:, :].reset_index(drop=True)
        nan_mask = df['next_original_value'].isna()
        df = df.loc[nan_mask != True, :].reset_index(drop=True)
        df_list.append(df)

    out_df = pd.concat(df_list)
    out_df = out_df.sort_values('date')
    out_df.to_pickle(args['out_path'])
    print(out_df)

パラメータと実行

time_lengthは30としていますが,それに応じて生成されるデータセットのサイズが変動します.より多くの過去特徴を使用したい場合は増やし,データセットのサイズを減らしたい場合は減らしてください.

if __name__ == '__main__':
    dataset_dir = 'dataset_2018_2023'
    args = {
        'out_path':f'./{dataset_dir}/learning_dataset.dfpkl',
        'original_dataset_path': f'./{dataset_dir}/original_dataset.dfpkl',
        'class_mean_dataset_path': f'./{dataset_dir}/clusterling_dataset_mean.dfpkl',
        'class_dist_dataset_path': f'./{dataset_dir}/clusterling_dataset_dist.dfpkl',
        'time_length':30,
        'train_rate':0.7,
        'drop_codes':['2747', '3858', '6775', '9468']
    }
    make_dataset(args)
タイトルとURLをコピーしました