このプログラムの目的
進化計算は機械学習と違って多点探索手法なため,計算効率が悪いです.そこで,シミュレーション関数を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ファイル.pyx
はpython 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
)