work.log

元エンジニアの備忘録的ブログ

Pythonで株式投資(個別銘柄)のリスク計算をする

投稿:2017-10-17 23:15  更新:

今流行の人工知能(AI)開発に挑戦したい!と言う事で、久しぶりに新しい言語 Python の勉強に取り組んでいます。

ただ、いきなり人工知能って言っても厳しいのでその手前の機械学習からと思っているのですが、機械学習用のデータにも使えそうな『株式投資のリスク値』を Python で計算してみたいと思います。

Python と人工知能の情報を調べているとチラホラと投資関連のコードが散見されますし、自分も興味がある分野なのでやってみようかと思った訳です。

株式投資で事前にリスクを計算するのは可能なのか?

Python のコードに入る前にそもそも投資のリスクを事前に計算するのは可能なのか?という事について触れたいと思います。

今回やろうとしている株式の個別銘柄についてリスク計算をする、具体的な情報は見つけられなかったのですが、株(多分インデックス)や債券等は『期待リターン・リスク』という数値が実際に計算され使われています。

例えば、皆さんの年金を運用している GPIF という機関は、国内株式の期待リターンは4.8%、リスクは22.48%という具体的な数値を公表しています。

統計学とか金融工学に明るくないといまいちピンと来ない資料だとは思いますが、GPIF は巨額の資金をこういう風に計算して運用し、これまでに58兆円もの利益を生み出しているというのだから、予測は可能という前提でいきたいと思います。

なので、投資でそんなオカルトを信じるやつは馬鹿だ!と思う方はここでページを閉じて貰えれば幸いです。

投資リスクの計算方法

上記資料にもある通り資産運用で言うリスクとは『過去のリターンの振れ幅』を指すそうです。

なので、30%値上がりした株のリスクは30%、-10%下落した株のリスクは10%となります。

何だかしっくり来ませんが、リターンは決してプラスだけの事を指す訳じゃないので、上がっても下がっても以降はリスクと見直すようです。

また、振れ幅は一定では無いため、散らばる振れ幅からリスクを推定するのに標準偏差が使われますが、標準偏差については下記ページが抜群にわかりやすいです。

Pythonでリスクの計算をする

前置きが長くなりましたが、Python を使って株(個別銘柄)のリスク計算をしてみます。

通常は年率リターンの値を使うみたいですが、今回は月次リターンの値を使います。

どちらのリターン値を使って計算するかは短期なのか長期なのかという投資の運用スタイルにより変わると思っているのですが、これは今後の機械学習用のデータにもなるかもしれないという事で、沢山データが取れる月次でやってみます。

まずは計算に必要な株価を取得するプログラムから。これはこちらの『jpstock.py』というサンプルを参考に、Python3 で動くようにしてみました。

## jpstock.py
# coding: utf-8

import sys
import datetime
import pandas as pd
import pandas_datareader.data as web
import pandas_datareader._utils as pdu

class JpStock:
	def base_url(self):
		return ('https://info.finance.yahoo.co.jp/history/?code={0}.T&{1}&{2}&tm={3}&p={4}')

	def get(self, code, start=None, end=None, interval='m'):
		base = self.base_url()
		start, end = pdu._sanitize_dates(start, end)
		start = 'sy={0}&sm={1}'.format(start.year, start.month)
		end = 'ey={0}&em={1}'.format(end.year, end.month - 1)
		p = 1
		results = []

		if interval not in ['d', 'w', 'm', 'v']:
			raise ValueError("Invalid interval: valid values are 'd', 'w', 'm' and 'v'")

		while True:
			url = base.format(code, start, end, interval, p)
			tables = pd.read_html(url, header=0)
			if len(tables) < 2 or len(tables[1]) == 0:
				break
			results.append(tables[1])
			p += 1
		result = pd.concat(results, ignore_index=True)

		result.columns = ['Date', 'Open', 'High', 'Low', 'Close', 'Volume', 'Adj Close']
		result['Date'] = pd.to_datetime(result['Date'], format='%Y年%m月')
		result = result.set_index('Date')
		result = result.sort_index()
		return result


if __name__ == '__main__':
	argsmin = 2
	version = (3, 0)
	if sys.version_info > (version):
		if len(sys.argv) > argsmin:
			try:
				stock = sys.argv[1]
				start = sys.argv[2]
				jpstock = JpStock()
				stock_tse = jpstock.get(int(stock), start=start)
				print (stock_tse)
				stock_tse.to_csv("".join(["stock_", stock, ".csv"]))
			except ValueError:
				print("Value Error occured in", stock)
		else:
			print("This program needs at least %(argsmin)s arguments" %
			locals())
	else:
		print("This program requires python > %(version)s" % locals())

オリジナルと違うのは日足データではなく、月足データを取ってくる点です。

今回はトヨタ自動車(7203)のデータを使うのでこのように実行します。

$ python jpstock.py 7203 2016-01-01

そうすると stock_7203.csv という CSV が作成されるのでこれをこんなコードで読み込んで計算します。

## risk.py
# coding: utf-8

import sys
import datetime
import numpy as np
import pandas as pd

if (len(sys.argv)) > 1:
	csv = sys.argv[1]
else:
	exit()

lm_close = 0
risk = []

dfs = pd.read_csv(csv)

for i,v in dfs.iterrows():

	if (lm_close == 0):
		lm_close = v['Adj Close']
		continue

	m_risk = 100 * (v['Adj Close'] - lm_close) / lm_close
	lm_close = v['Adj Close']

	risk.append(m_risk)
	print (i, v['Date'], m_risk)

print("Risk:" , round(np.std(risk), 2))

詳しい関数の使い方は省略しますが、numpy とか pandas とかのライブラリは便利すぎます。特に pandas はデータを読み込めば欲しいデータに直接アクセスできるのが楽すぎます。

振れ幅の求め方については『(今月の終値 – 先月の終値) / 先月の終値 * 100』で求めます。『Adj Close』という値は調整後終値と言って、株式分割があった際等にそれらの要因を調整された値となります。

このスクリプトで、約15年分のデータでリスク計算をしてみるとこのようになりました。

15年分のトヨタ自動車の株価(月足)
stock_7203.csv
$ python risk.py stock_7203.csv
.
. ~省略~
.
170 2016-11-01 9.376542194439875
171 2016-12-01 3.444126936381411
172 2017-01-01 -4.274498400697877
173 2017-02-01 -3.3262454434993924
174 2017-03-01 -5.074626865671642
175 2017-04-01 -0.11585567692816948
176 2017-05-01 -1.7067108533554267
177 2017-06-01 -0.6574511126095752
178 2017-07-01 5.786526387239097
179 2017-08-01 -0.8180943214629451
180 2017-09-01 8.523370532104156
Risk: 7.03

トヨタ自動車の月間リスクは大体7%を見ておけばいいようです。もしAIトレードを考えた時に8%は急変と見なし利食い or 損切りするとかそういう使い方でしょうか。

当然ですが、7%以上動くかもしれないし全く動かないかもしれませんのであくまでも予測データの一つとして捉えます。

リスク計算の精度を上げる

追記です。標準偏差はデータが平均値の周辺にどれ位バラついているかを持っているかを表す方法ですが、この範囲には全体の約68.3%のデータが収まっている事となります。

そうすると、上記で算出したリスクは半分以上はその数値内に収まると言えますが、もっと精度を上げるために投資では2標準偏差(2SD)を使うのが良いみたいです。

2標準偏差の範囲内に収まるデータは約95.5%となり飛躍的に上昇します。ちなみに3標準偏差となると約99.7%になります。

上記のコードを2標準偏差に対応させるには、最後の行をこのようにするだけで良いはず。

print("Risk:" , round(np.std(risk) * 2, 2))

これで再度リスク計算を行うとこのようになります。

$ python risk.py stock_7203.csv
.
. ~省略~
.
170 2016-11-01 9.376542194439875
171 2016-12-01 3.444126936381411
172 2017-01-01 -4.274498400697877
173 2017-02-01 -3.3262454434993924
174 2017-03-01 -5.074626865671642
175 2017-04-01 -0.11585567692816948
176 2017-05-01 -1.7067108533554267
177 2017-06-01 -0.6574511126095752
178 2017-07-01 5.786526387239097
179 2017-08-01 -0.8180943214629451
180 2017-09-01 8.523370532104156
Risk: 14.06

テクニカル分析をした事がある人はピンと来たかもしれないですがこれってボリンジャーバンドの計算方法なんですね。

ここまでやってようやくその事実に気づきましたが、このリスク計算の方法は幅広く使われているようです。

まとめ

標準偏差を使ってそれっぽいリスク計算が出来ましたが、このリスクは計算期間が長ければ長くなるほど増加しやすい、つまり過去の振れ幅が大きかった銘柄程リスクも高く出やすいです事になります。

なので、計算期間に答えが無いのがまた難しい所ですが、一つの指標には十分使えそうだなという印象です。(と言うかボリンジャーバンドを使ったトレード…)

ちなみに、本当は期待リターンも計算したかったのですがこちらはリスク計算と違ってかなり複雑になるみたいです。

まだ、そこまで理解が足りてないのでまずは計算方法を勉強したいと思いますが、Python で株式投資のリスク計算をする方法は以上です。

最後にお約束ですが、投資は完全に自己責任となりますのでこの計算方法の信憑性について一切保証できませんのでご了承ください。

関連記事

コメント

コメントを残す

おすすめのVPSサーバ

  • OSが選べる
  • VPS同士でLANが組める
  • 複数台構成向き

このブログで使っています。

  • 転送量が多いサービスに
  • 借りてるのは3年間一度もdown無し!

よく見られている記事

  • 本日
  • 週間
  • 月間