pythonでファイナンス計算

先日会社で退職金の説明会がありました。それによると退職金の一部を確定拠出年金として自分で運用しなくてはいけないとのことで、ポートフォリオを設計する必要あります。今時は各種金融機関がポートフォリオのシミュレーターを公開していますし、Excelを使って計算するという方法もありますが、ここでは例によってpythonポートフォリオの設計をやってみます。

実は、pythonにはnumpyやscipyといった科学計算のライブラリやcvxoptという最適化計算のライブラリがあるので、それらを利用すれば簡単かつ高速に最適なポートフォリオを計算することができます。

ちなみに、僕はファイナンス理論の専門家ではないので、ここに書いてある記述が正確であるという保証はできません。あしからず。

分散共分散行列を計算する

各投資対象のリスクと投資対象同士の相関係数が分かっているならば、分散共分散行列は行列計算で計算できます。
各投資対象の相関係数行列をC、各投資対象の標準偏差の対角行列をSとすると、分散共分散行列Σは

で計算できます。

pythonのコード例

import cvxopt
correl_mat = cvxopt.matrix([[1.0, 0.16, -0.06, -0.05, -0.29],
                            [0.16, 1.0, -0.25, 0.27, 0.73],
                            [-0.06, -0.25, 1.0, 0.56, 0.11],
                            [-0.05, 0.27, 0.56, 1.0, 0.91],
                            [-0.29, 0.73, 0.11, 0.91, 1.0]])
stddev_mat = cvxopt.spdiag([5.40, 22.15, 13.25, 19.59, 26.25])*0.01
sigma_mat = stddev_mat*correl_mat*stddev_mat

株価や投資信託の基準価額の時系列データから分散共分散行列を計算する場合は、numpy.covを使用すると簡単です。

import numpy
data1 = numpy.array([投資対象1の時系列データ], dtype=float)
data2 = numpy.array([投資対象2の時系列データ], dtype=float)

# リターンの計算
return_mat1 = data1[0:-1]/data1[1:]
return_mat2 = data2[0:-1]/data2[1:]

# 分散共分散行列の計算
sigma_mat = numpy.cov(numpy.vstack(return_mat1, return_mat2)) # 単位が年率でないので注意

効率的フロンティア上のポートフォリオを計算する

最適なポートフォリオを設計する問題は、ポートフォリオのリターンがある値以上という制約下でリスクを最小化するという問題と考えることができます。
これは、各種投資対象の期待リターンのベクトルをr、配分割合のベクトルをx、ポートフォリオの目標リターンをr0とすると、

となります。

これは、スラック変数sを導入すると、次のような最適化問題として定式化できます。

この最適化問題はcvxopt.solvers.coneqpを使って解くことができます。

import math
import cvxopt
import cvxopt.solvers
def calc_portfolio(sigma_mat, r, r0):
  """
  sigma_mat : 分散共分散行列 (cvxopt.base.matrix)
  r  : 各投資対象の期待リターンのベクトル (cvxopt.base.matrix または list)
  r0 : 目標リターン (float)
  """
  n = sigma_mat.size[0]
  minus_r = [-x for x in r]

  P = sigma_mat
  q = cvxopt.matrix(0.0, (n, 1))
  
  G = cvxopt.matrix([cvxopt.matrix([[1.0]*n, minus_r]).T, cvxopt.spdiag([-1.0]*n)])
  h = cvxopt.matrix([1.0, -r0] + [0 for i in xrange(n)])
  
  x = cvxopt.solvers.coneqp(P, q, G, h)['x']

  # (配分割合のリスト、ポートフォリオのリターン, ポートフォリオのリスク(標準偏差))
  return list(x), (r.T*x)[0], math.sqrt((x.T*P*x)[0])

ポートフォリオのリスクを計算する

正規分布の確率分布関数やその逆関数はscipy.stats.normで計算することが出来るので、これを使ってショートフォール確率やバリューアットリスクを計算します。

from scipy.stats import norm
def calc_shortfall_risk(r_min, r, sigma):
  return norm.cdf(r_min, r, sigma)

def calc_value_at_risk(probability, r, sigma):
  return norm.ppf(1.0-probability, r, sigma)

def calc_sharp_ratio(r_norisk, r, sigma):
  return (r - r_norisk)/sigma

その他

cvxoptの最適化計算の途中経過を表示しないようにするには、次のコードを実行します。

cvxopt.solvers.options['show_progress'] = False

参考URL

各種数値や計算結果の検算に下記のサイトを利用させていただきました。
アセットアロケーション分析 〜 投資信託のガイド|ファンドの海