fin-pyもくもく会 #8 で for文を使って累積リターンを算出して遅いという話をしていたら、2casa さんより、対数収益率を計算した後に合計したらよいのではないかとアドバイスいただきました。
確かに「Pythonでfor文を使ったら負け」という格言があることですし、色々試してみました。
下記のように最初の株価を100円とし、1000日分の騰落率をランダムに与えてみます。
import numpy as np
n = 1000 # データ数
s0 = 100 # 最初の株価
np.random.seed(10)
pct_change = np.random.randn(n - 1) * 0.01 # 騰落率
pct_change[:5]
上記の騰落率を使うと、2日目の株価は
100 + 100 * 1.33158650e-02 = 101.3315865
3日目の株価は
101.3315865 + 101.3315865 * 7.15278974e-03 = 102.05639003225512
となります。
まずは古典的なfor文を使ってみます。
%%time
stock_price1 = []
stock_price1.append(s0)
for x in pct_change:
stock_price1.append(stock_price1[-1] * (1 + x))
前述した計算とあっているか確認してみましょう。
stock_price1[:3]
あってそうですね。
リストの append()
を使う処理は重いので、numpy.ndarray
を使ってみます。
%%time
stock_price2 = np.empty(n)
stock_price2[0] = s0
for i, x in enumerate(pct_change):
stock_price2[i + 1] = stock_price2[i] * (1 + x)
速度に大きな改善はありませんでしたが、コードが少しスッキリしました。
cumprod()
メソッドを使って累積積から算出します。
for文を使わないのでさらにスッキリしました。処理速度も改善しています。
%%time
stock_price3 = np.ones(1000)
stock_price3[1:] = (pct_change + 1).cumprod()
stock_price3 = stock_price3 * s0
対数収益率を使うと、累積リターンは合計(sum)から算出できます。
なんと、1行で書くことができました!
%%time
stock_price4 = np.exp(np.log(pct_change + 1).cumsum()) * s0
対数収益率にする利点としては、最後の収益を出したいだけなら合計するだけで算出できることです。
%%time
np.exp(np.log(pct_change + 1).sum()) * s0
それぞれの累積リターンの算出が合っているか確認してみます。
print(stock_price1[-5:])
print(stock_price2[-5:])
print(stock_price3[-5:])
print(stock_price4[-5:])
PythonユーザのためのJupyter[実践]入門 を参考に、算出した株価を可視化してみます。
import matplotlib.pyplot as plt
from matplotlib import rcParams
plt.plot(stock_price1, label='for(append)')
plt.plot(stock_price2, label='for(numpy.ndarray)')
plt.plot(stock_price3, label='cumprod')
plt.plot(stock_price4, label='cumsum')
plt.legend()
plt.show()
4つの方法で算出した結果が全て重なっているので、問題なさそうですね。
Copyright © 2020 driller
Powered by miyadaiku