[機械学習・進化計算による株式取引最適化] No.07-01 cythonでのトレードシミュレーション

このプログラムの目的

進化計算は機械学習と違って多点探索手法なため,計算効率が悪いです.そこで,シミュレーション関数をcythonで書くことで,計算速度を向上させます.

cythonは.pyxというファイルを作成し,型定義等をpythonの記法にプラスすることで,C(++)言語に変換してくれるライブラリです..pyxファイルをコンパイルすることで,別のpythonファイルからも呼び出すことができるようになります.

コンパイルにはsetup.pyが必要になります.

work_share
├07_evolutionary_algorithms
  ├Dockerfile
  ├docker-compose.yml
  └src
    └neuro_evolution_torch
      ├torch_model.py
      ├simulation_cython.pyx (これを作成)
      └setup.py

使用ライブラリ

cimport cython
import numpy as np
cimport numpy as np
ctypedef np.float64_t DOUBLE_t

シミュレーション関数

引数は

  • init_money : 初期資金
  • P : 資産比率を表す[銘柄数+1, 日数]の行列.ネットワークの出力
  • values : 各銘柄の株価を表す[銘柄数, 日数]の行列
cpdef Logging trade_simulate(
        double init_money,
        np.ndarray[DOUBLE_t, ndim=2] P,
        np.ndarray[DOUBLE_t, ndim=2] values):
    cdef int i, j
    cdef int t = 0
    cdef int size_X = P.shape[0]
    cdef int output_num = P.shape[1]
    cdef np.ndarray[DOUBLE_t, ndim=1] p = np.zeros(output_num, dtype=np.float64)
    cdef double money = init_money
    cdef double total_assets = init_money
    cdef double value
    cdef int new_hold_num, buy_num, sell_num
    cdef double buy_value, sell_value
    cdef np.ndarray[int, ndim=1] stock_holds = np.zeros(output_num-1, dtype=np.int32)

    cdef Logging logging = Logging(size_X, output_num)

    for t in range(size_X):
        p = P[t]
        for i in range(output_num-1):#index -1 is money rate (not stock!)
            value = values[t, i]
            if value != 0.0:
                new_hold_num = int((p[i]*total_assets)//value)
                if new_hold_num > stock_holds[i]:
                    buy_num = new_hold_num - stock_holds[i]
                    buy_value = buy_num * value
                    money -= buy_value
                    money -= trade_cost_func(buy_value)
                if new_hold_num < stock_holds[i]:
                    sell_num = stock_holds[i] - new_hold_num
                    sell_value = sell_num * value
                    money += sell_value
                    money -= trade_cost_func(sell_value)
                stock_holds[i] = new_hold_num

        total_assets = money + np.sum(stock_holds*values[t])
        logging.log_wite(t, p, total_assets, money)
    return logging

ログの保存

シミュレーションのログを記録するクラス.

cdef class Logging:
    cdef public logging_p
    cdef public logging_total_assets
    cdef public logging_money
    def __init__(self, log_size, output_num):
        self.logging_p = np.zeros((log_size, output_num), dtype=np.float64)
        self.logging_total_assets = np.zeros(log_size, dtype=np.float64)
        self.logging_money = np.zeros(log_size, dtype=np.float64)

    def log_wite(self, t, p, total_assets, money):
        self.logging_p[t] = p
        self.logging_total_assets[t] = total_assets
        self.logging_money[t] = money

コスト関数

取引価格に対して取引手数料を返す関数.楽天の手数料を参考にした.

cdef double trade_cost_func(double value):
    #reference to https://www.rakuten-sec.co.jp/web/commission/
    if value < 50_000:
        return 55
    if 50_000 <= value < 100_000:
        return 99
    if 100_000 <= value < 200_000:
        return 115
    if 200_000 <= value < 500_000:
        return 275
    if 500_000 <= value < 1_000_000:
        return 535
    if 1_000_000 <= value < 1_500_000:
        return 640
    if 1_500_000 <= value < 30_000_000:
        return 1013
    if 30_000_000 <= value:
        return 1070

現金率の調整

ネットワークの出力をそのまま使うと,現金率が0%になる恐る.現実では取引手数料を支払えなくなるため,現金率の最低割合を調整する関数.

cpdef np.ndarray[DOUBLE_t, ndim=2] adjust_money_rate(np.ndarray[DOUBLE_t, ndim=2] P, double min_money_rate):
    cdef int size_X = P.shape[0]
    cdef int size_col = P.shape[1]
    cdef double p_sum
    cdef double alpha
    for t in range(size_X):
        if P[t, size_col-1] < min_money_rate:
            p_sum = np.sum(P[t, 0:size_col-1])
            alpha = (p_sum+(P[t, size_col-1]-min_money_rate))/p_sum
            P[t, 0:size_col-1] *= alpha
            P[t, size_col-1] = min_money_rate
    return P

setup.py

cythonファイル.pyxpython setup.py build_ext --inplaceコマンドでコンパイル・ライブラリ化できます.

コンパイルエラーが出なければ同ディレクトリにsimulation_cython.cpython-***.soが生成されます(他にも.cpp, build等が生成される).

from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext
from Cython.Build import cythonize

import numpy as np

sourcefiles = ['simulation_cython.pyx']

ext_modules = cythonize(Extension(
    "simulation_cython",
    sources=sourcefiles,
    language="c++",
    include_dirs=[np.get_include()],
    #libraries=["m"],
    extra_compile_args=['--optimize', '-O3']))

setup(
    cmdclass = {'build_ext': build_ext},
    ext_modules = ext_modules
    )
タイトルとURLをコピーしました