Markowitz投资组合之Python模拟

1952年,芝加哥大学的Markowitz提出现代资产组合理论(Modern Portfolio Theory,简称MPT),为现代西方证券投资理论奠定了基础。其基本思想是,证券投资的风险在于证券投资收益的不确定性。如果将收益率视为一个数学上的随机变量的话,证券的期望收益是该随机变量的数学期望(均值),而风险可以用该随机变量的方差来表示。

对于投资组合而言,如何分配各种证券上的投资比例,从而使风险最小而收益最大?

答案是将投资比例设定为变量,通过数学规划,对每一固定收益率求最小方差,对每一个固定的方差求最大收益率,这个多元方程的解可以决定一条曲线,这条曲线上的每一个点都对应着最优投资组合,即在给定风险水平下,收益率最大,这条曲线称作“有效前沿” (Efficient Frontier)。

对投资者而言,不存在比有效前沿更优的投资组合,只需要根据自己的风险偏好在有效前沿上寻找最优策略。

以包含两项风险资产的投资组合为例,其最小方差模型与有效前沿如下图(CFA Notes Book 4, Page 155):

《Markowitz投资组合之Python模拟》 Paste_Image.png

期望收益与标准差分别定义如下(CFA Notes Book 4, Page 156):

《Markowitz投资组合之Python模拟》 Paste_Image.png

Python实现(Python 2.7.13 |Anaconda 4.3.1 (64-bit)):

数据源:
2010年~2016年美股股价–微博、搜狐、网易、百度、阿里巴巴

有约束的最优化问题:

  1. 约束条件:a. 5支美股权重之和=1; b. 给定期望收益率
  2. 通过最小二乘法计算给定收益率下对应的最小标准差

程序文件:

  1. Get_Yahoo_Quote.py 从Yahoo! Finance获取美股数据
  2. MPT.py 以最优化方法求解并绘制投资组合有效前沿

Get_Yahoo_Quote.py

# -*- coding: utf-8 -*-
"""
Created on Mon May 22 17:41:16 2017

@author: qiaovin
"""

import requests
import time
import pandas as pd
import warnings as ws

ws.filterwarnings("ignore")


def datetime_timestamp(dt):
     time.strptime(dt, '%Y-%m-%d %H:%M:%S')
     s = time.mktime(time.strptime(dt, '%Y-%m-%d %H:%M:%S'))
     return str(int(s))


def get_yahoo_quote(ticker,begin_date,end_date):
        
    s = requests.Session()
   
    cookies = dict(B='6sji959chqf8n&b=3&s=gk')

    crumb = 'hJ5N2TwcJhN'

    begin = datetime_timestamp(begin_date)
    
    end = datetime_timestamp(end_date)

    r = s.get("https://query1.finance.yahoo.com/v7/finance/download/"+ticker+"?period1="+begin+"&period2="+end+"&interval=1d&events=history&crumb="+crumb,cookies=cookies,verify=False)
   
    filename=ticker+".csv"   
   
    f = open(filename, 'w')
    f.write(r.text)
    f.close()    
   
    es = pd.read_csv(filename, index_col=0,parse_dates=True, sep=",", dayfirst=True)    
  
    return es
    
    
def get_yahoo_multi_quotes(tickers,begin_date,end_date):
    
    result = pd.DataFrame()

    i = 0

    for ticker in tickers:
        es = get_yahoo_quote(ticker,begin_date,end_date)
        p = pd.DataFrame(es['Adj Close'])
        p.rename(columns={'Adj Close': ticker},inplace=True)
        #print(p.head())
        
        if i == 0:
            result = p
            i = i + 1
        else:
            result =pd.merge(result,p,left_index=True,right_index=True)
                
    return result

MPT.py

# -*- coding: utf-8 -*-
"""
Created on Sat May 27 16:39:54 2017

@author: qiaovin
"""

import numpy as np
import scipy.optimize as opt
from Get_Yahoo_Quote import get_yahoo_multi_quotes
import matplotlib.pyplot as plt

def port_return(weights):
    '''
    Input:
    weights: weights for different securities in portfolio
    Output:
    pret : expected portfolio return
    '''
    weights = np.array(weights)
    pret = np.sum(rets.mean() * weights) * 252
 
    return pret      

def port_volatility(weights):
    '''
    Input:    
    weights: weights for different securities in portfolio
    Output:
    pvol : expected portfolio volatility
    '''
    weights = np.array(weights)
    pvol = np.sqrt(np.dot(weights.T, np.dot(rets.cov() * 252, weights)))
    
    return pvol

begdate="2010-01-01 09:00:00" # beginning date
enddate="2016-12-31 09:00:00" # ending date

stocks=['WB', 'SOHU', 'NTES', 'BIDU','BABA']

data = get_yahoo_multi_quotes(stocks,begdate,enddate)

noa = len(stocks)

rets = np.log(data / data.shift(1))

'''
constraint: 
1. sum of weights = 1 ; 
2. fixed target return level

bound of weights: (0,1)
'''

bnds = tuple((0, 1) for x in range(noa))

target_returns = np.linspace(0.0, 0.25, 50)
target_vols = []
for target_return in target_returns:
    cons = ({'type': 'eq', 'fun': lambda x: port_return(x) - target_return},
             {'type': 'eq', 'fun': lambda x: np.sum(x) - 1})
    res = opt.minimize(port_volatility, noa * [1. / noa,], method='SLSQP',
                       bounds=bnds, constraints=cons)
    target_vols.append(res['fun'])
target_vols = np.array(target_vols)


plt.figure(figsize=(8, 4))

# efficient frontier
# target_returns/target_vols: Sharpe ratio (risk free rate=0)
plt.scatter(target_vols, target_returns, c=target_returns/target_vols, marker='x')

plt.grid(True)
plt.xlabel('expected volatility')
plt.ylabel('expected return')
plt.colorbar(label='Sharpe ratio')

plt.show()

执行结果:

《Markowitz投资组合之Python模拟》 Paste_Image.png

作者微信公众号

《Markowitz投资组合之Python模拟》 qrcode_small.jpg

参考:
**代码中cookie与crumb定义参见:Yahoo Finance财经数据PYTHON临时读取方法
http://www.jianshu.com/p/85d563d326a9
Yves_Hilpisch_Python_for_Finance
CFA_2017_Level_1_Schweser_Notes_Book_4
金钱永不眠:资本世界的暗流涌动和金融逻辑,香帅无花(唐涯),中信出版社
金融数学-金融工程引论,马雷克/托马斯,人大出版社

    原文作者:vincentqiao
    原文地址: https://www.jianshu.com/p/c451e33af5d5
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞