• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

Python量化交易10——资产组合比例优化(CAMP,VAR,CVAR)

武飞扬头像
阡之尘埃
帮助1

案例背景 

本科的金融或者投资学都会学到CAMP模型,资本资产定价模型,可是怎么用代码实现却一直没人教。

本期用Python代码案例配置一个资产组合,并且做CAMP模型,计算VAR和CVAR等指标。

(本期案例中涉及到的公司仅用于案例研究,不构成任何建议。)


数据获取

用证券宝获取gu票数据,先导入要用的包:

  1.  
    import baostock as bs
  2.  
    import numpy as np
  3.  
    import pandas as pd
  4.  
    import matplotlib.pyplot as plt
  5.  
    import seaborn as sns
  6.  
    import scipy.optimize as optimization
  7.  
    import scipy.stats as stats
  8.  
    plt.rcParams ['font.sans-serif'] ='SimHei' #显示中文
  9.  
    plt.rcParams ['axes.unicode_minus']=False #显示负号

定义获取日K数据的函数:

  1.  
    def get_stocks_daily(stocks=['sh.601318'],start_date='2022-04-01',end_date='2023-03-29'):
  2.  
    lg = bs.login()
  3.  
    df_results=pd.DataFrame()
  4.  
    for stock in stocks:
  5.  
    rs_result = bs.query_history_k_data_plus(stock,fields="date,code,open,high,low,close,preclose,volume,amount,adjustflag,turn,tradestatus,pctChg,isST"\
  6.  
    , start_date=start_date,end_date=end_date, frequency="d", adjustflag="3")
  7.  
    df_result = rs_result.get_data()
  8.  
    #df_result=df_result.set_index('date')
  9.  
    df_results=pd.concat([df_results,df_result],ignore_index=True)
  10.  
    print(f"{stock}获取完成")
  11.  
    bs.logout()
  12.  
    cols_to_convert = [col for col in df_results.columns if col != 'code' and col!='date']
  13.  
    df_results['date']=pd.to_datetime(df_results['date'])
  14.  
    df_results[cols_to_convert] = df_results[cols_to_convert].astype('float64')
  15.  
    return df_results
学新通

定义获取gu票的代码列表:

stocks_lst=['sh.601318','sh.600519','sz.300750','sz.002714','sz.002603','sz.000681','sh.000300']  #中国平安,贵州茅台,宁德时代,牧原股份,以岭药业,视觉中国,

最后的沪深300作为市场基础回报率。

为什么选这个几公司呢?因为他们有的是金融权重股,有的是消费白酒,还有这两年疫情炒得很热的医药,新能源,人工智能等等代表性的gu票。

获取:

data=get_stocks_daily(stocks=stocks_lst,start_date='2021-04-01',end_date='2023-03-29')

学新通

 数据处理一下:

  1.  
    data=data[['code','date','close']].pivot(index='date',columns='code', values='close')[stocks_lst]\
  2.  
    .set_axis(['中国平安','贵州茅台','宁德时代','牧原股份','以岭药业','视觉中国','沪深300'],axis='columns')
  3.  
    data

学新通


描述性统计 

 画出他们的价格走势:

  1.  
    colors=['c', 'b', 'g', 'tomato', 'm', 'y', 'lime', 'k','orange','pink','grey','tan','purple']
  2.  
    fig, ax = plt.subplots(4,2,figsize=(8,7),dpi=256)
  3.  
    for i,col in enumerate(data.columns):
  4.  
    n=int(str('42') str(i 1))
  5.  
    plt.subplot(n)
  6.  
    df_col=data[col]
  7.  
    plt.plot(df_col,label=col,color=colors[i])
  8.  
    plt.xticks(rotation=20)
  9.  
    plt.title(f'{col}的价格走势')
  10.  
    plt.tight_layout()
  11.  
    plt.show()

学新通

 计算他们的日度收益率:
 

  1.  
    data1=pd.DataFrame(columns=data.columns)
  2.  
    for col in data.columns:
  3.  
    data1[col]=data[col].pct_change()
  4.  
    data1=data1.dropna()
  5.  
    data1.head()

学新通

 同样画出他们的图:

  1.  
    fig, ax = plt.subplots(4,2,figsize=(8,7),dpi=256)
  2.  
    for i,col in enumerate(data1.columns):
  3.  
    n=int(str('42') str(i 1))
  4.  
    plt.subplot(n)
  5.  
    df_col=data1[col]
  6.  
    plt.plot(df_col,label=col,color=colors[i])
  7.  
    plt.xticks(rotation=20)
  8.  
    plt.title(f'{col}的日度收益率')
  9.  
    plt.tight_layout()
  10.  
    plt.show()

学新通


投资组合

我们都知道特征组合就是一样资产买一点....所以核心是 每种资产都买多少?这个比例是关键。

我们先随机给一个权重比例看看:

  1.  
    weights = np.random.random(len(stocks_lst)-1)
  2.  
    weights /= np.sum(weights)
  3.  
    weights

学新通

计算投资组合的价格和收益率:

  1.  
    data['portfolio']=(data.iloc[:,:-1]*weights).sum(axis=1)
  2.  
    data1['portfolio']=data['portfolio'].pct_change().dropna()

 计算他们所有资产这两年的收益率:

  1.  
    #这两年的收益率
  2.  
    (data.iloc[-1,:]-data.iloc[0,:])/data.iloc[0,:]

学新通

 当然也可以这样算,但是我感觉这样直接相加不准:

data1.sum()  #这样不准

学新通

 计算他们的波动性:

  1.  
    #这两年的波动性
  2.  
    data1.std()

学新通


优化投资组合比例

我们要选择一个最优的投资组合,无非是最大的收益率,或者最小的波动率。

有一个指标可以结合他们,叫夏普比率,表示每承担一单位风险所获得的回报。

  1.  
    # optimal portfolio sharpe ratio
  2.  
    noa=len(stocks_lst)-1
  3.  
    rf = 0.04
  4.  
    def statistics(weights,risk_free=0.04):
  5.  
    data['portfolio']=(data.iloc[:,:noa]*weights).sum(axis=1)
  6.  
    data1['portfolio']=data['portfolio'].pct_change().dropna()
  7.  
    port_returns = (data['portfolio'][-1]-data['portfolio'][0])/data['portfolio'][0]
  8.  
    port_variance = data1['portfolio'].std()
  9.  
    return np.array([port_returns, port_variance, (port_returns-risk_free*2)/port_variance])
  10.  
    #最小化夏普指数的负值
  11.  
    #Minimize the negative value of the Sharpe Index
  12.  
    def min_sharpe(weights):
  13.  
    return -statistics(weights)[2]

比如我等权给一个比例,然后计算上面的指标:

statistics(weights=[1/6]*6,risk_free=0.04)

学新通

选择开始优化,最大化夏普比率,也就是最小化夏普比率负数:

  1.  
    # constraints an bounds
  2.  
    cons = ({'type':'eq', 'fun':lambda x: np.sum(x)-1})
  3.  
    bnds = tuple((0,1) for x in range(noa))
  4.  
     
  5.  
    opts = optimization.minimize(min_sharpe, noa*[1./noa,],method = 'SLSQP',bounds = bnds, constraints = cons,options={'maxiter': 100000})
  6.  
    print("Optimal weights:", opts['x'].round(4))
  7.  
    print("Expected return, volatility and Sharpe ratio:", statistics(opts['x']))

 学新通

 好家伙,直接给我扔掉了4个公司......只有两个公司被选入了。

如果最小化方差会是一个怎么样的组合呢:

  1.  
    # Minimal variance
  2.  
    np.set_printoptions(suppress=True)
  3.  
    def min_variance(weights):
  4.  
    return statistics(weights)[1]
  5.  
    optv = optimization.minimize(min_variance,noa*[1./noa],method = 'SLSQP',
  6.  
    bounds = bnds, constraints = cons,options={'maxiter': 100000})
  7.  
    print("Optimal weights:", optv['x'].round(4))
  8.  
    print("Expected return, volatility and Sharpe ratio:", statistics(optv['x']))

学新通

emmm,收益率太低。我们还是选第一个组合吧。


资本资产定价模型CAMP

CAMP模型很简单,公式懒得去word里面写了,我直接用python用图画一个公式:

  1.  
    plt.figure(figsize=(2.7,0.7),dpi=200)
  2.  
    plt.text(0.1, 0.4,fr'$E(R_i)=R_f \beta_i[E(R_m)-R_f]$')
  3.  
    plt.xticks([]) ;plt.yticks([])
  4.  
    plt.show()

学新通

 所有资产都进行拟合:

  1.  
    df_all=pd.DataFrame(columns=['alpha','beta'])
  2.  
    plt.subplots(4,2,figsize=(12,12),dpi=256)
  3.  
    for i,col in enumerate(data1.columns):
  4.  
    if col!='沪深300':
  5.  
    n=int(str('42') str(i 1))
  6.  
    plt.subplot(n)
  7.  
    b,a=np.polyfit(data1['沪深300'], data1[col], deg=1)
  8.  
    #plt.figure(figsize=(4,2.5),dpi=128)
  9.  
    plt.scatter(data1['沪深300'], data1[col], label="Data points")
  10.  
    plt.plot(data1['沪深300'], a b*data1['沪深300'], color='green', label="CAPM Line")
  11.  
    plt.title(f'{col} Capital Asset Pricing Model', fontsize=14)
  12.  
    plt.xlabel('Market return $r_m$', fontsize=14)
  13.  
    plt.ylabel(f'{col} return $r_i$', fontsize=14)
  14.  
    plt.text(0, 0.05, fr'$r_i = {a.round(3)} {b.round(3)} r_m$', color='red', fontsize=14)
  15.  
    plt.legend()
  16.  
    plt.axvline(color="red",linestyle="dashed",linewidth=2.5)
  17.  
    plt.axhline(color="red",linestyle="dashed",linewidth=2.5)
  18.  
    plt.grid(True, axis='both')
  19.  
    df_all.loc[col,:]=[a,b]
  20.  
    plt.tight_layout()
  21.  
    plt.show()
学新通

学新通

 查看计算的阿尔法和贝塔值

df_all  #查看计算的阿尔法和贝塔值

学新通

 用公式计算期望收益率

  1.  
    rm = data1['沪深300'].mean() * 252
  2.  
    rf df_all.loc['中国平安','beta'] * (rm-rf)

学新通

rf df_all.loc['贵州茅台','beta'] * (rm-rf)

学新通 

rf df_all.loc['portfolio','beta'] * (rm-rf)

学新通 


计算VAR和CVAR

定义一个var函数,然后再定义一个一体化函数,var和cvar都算出来,然后画图:

  1.  
    def VaR_r(mu,sigma,q,n):
  2.  
    z=stats.norm.ppf(q)
  3.  
    return (mu*n) ((sigma*np.sqrt(n))*z)
  1.  
    def deal_stock(series=data['贵州茅台'],stock_name='贵州茅台'):
  2.  
    AMZN_returns = np.log(series) - np.log(pd.Series(series).shift(1))
  3.  
    mu = np.mean(AMZN_returns) ; sigma = np.std(AMZN_returns)
  4.  
    #var
  5.  
    AMZN_VaR_1day=VaR_r(mu,sigma,0.01,1) ; AMZN_VaR_60day=VaR_r(mu,sigma,0.01,60)
  6.  
    print(f"{stock_name} 1day var is {AMZN_VaR_1day}, 60days var is {AMZN_VaR_60day}")
  7.  
    #CVaR
  8.  
    AMZN_cvar_1day=AMZN_returns[AMZN_returns <= AMZN_VaR_1day].mean()
  9.  
    AMZN_returns60 = AMZN_returns*60
  10.  
    AMZN_cvar_60day=AMZN_returns60[AMZN_returns60 <= AMZN_VaR_60day].mean()
  11.  
    print(f"{stock_name} 1day cvar is {AMZN_cvar_1day}, 60days cvar is {AMZN_cvar_60day}")
  12.  
     
  13.  
    # VaR_1day & cvar 1day
  14.  
    plt.subplots(1,2,figsize=(14,5),dpi=200)
  15.  
    plt.subplot(121)
  16.  
    plt.hist(AMZN_returns[AMZN_returns < AMZN_VaR_1day], bins=20,label='In var')
  17.  
    plt.hist(AMZN_returns[AMZN_returns > AMZN_VaR_1day], bins=20,label='Out var')
  18.  
    plt.axvline(AMZN_VaR_1day, color='blue', linestyle='dashed', linewidth=1,label=f"{stock_name} Var 1day")
  19.  
    plt.axvline(AMZN_cvar_1day, color='k', linestyle='dashed', linewidth=1,label=f"{stock_name} CVaR 1day")
  20.  
    plt.text(AMZN_VaR_1day, 40, f'{stock_name}_VaR_1day={round(AMZN_VaR_1day,3)}', color='black', fontsize=15, horizontalalignment='center', verticalalignment='center')
  21.  
    plt.text(AMZN_cvar_1day, 20, f'{stock_name}_CVaR_1day={round(AMZN_cvar_1day,3)}', color='blue', fontsize=15, horizontalalignment='center', verticalalignment='center')
  22.  
    plt.title(f'1 day VaR & CVaR of {stock_name}(99%)')
  23.  
    plt.legend()
  24.  
    plt.xlabel('return')
  25.  
    plt.ylabel('frequency')
  26.  
     
  27.  
    # VaR_60day & CVaR_60day
  28.  
    plt.subplot(122)
  29.  
    plt.hist(AMZN_returns60[AMZN_returns60 < AMZN_VaR_60day], bins=20,label='In var')
  30.  
    plt.hist(AMZN_returns60[AMZN_returns60 > AMZN_VaR_60day], bins=20,label='Out var')
  31.  
    plt.axvline(AMZN_VaR_60day, color='b', linestyle='dashed', linewidth=1,label=f"{stock_name} Var 60day")
  32.  
    plt.axvline(AMZN_cvar_60day, color='k', linestyle='dashed', linewidth=1,label=f"{stock_name} CVar 60day")
  33.  
    plt.text(AMZN_VaR_60day, 30, f'{stock_name}_VaR_60day={round(AMZN_VaR_60day,3)}', color='black', fontsize=15, horizontalalignment='center', verticalalignment='center')
  34.  
    plt.text(AMZN_cvar_60day, 20, f'{stock_name}_CVaR_60day={round(AMZN_cvar_60day,3)}', color='blue', fontsize=15, horizontalalignment='center', verticalalignment='center')
  35.  
    plt.title(f'60 day VaR & CVaR of {stock_name}(99%)')
  36.  
    plt.legend()
  37.  
    plt.xlabel('return')
  38.  
    plt.ylabel('frequency')
  39.  
     
  40.  
    plt.tight_layout()
  41.  
    plt.show()
  42.  
     
  43.  
    #return AMZN_VaR_1day,AMZN_VaR_60day,AMZN_cvar_1day,AMZN_cvar_60day,AMZN_returns
学新通
deal_stock(series=data['中国平安'],stock_name='中国平安')

学新通

带进去就行。用起来很方便

deal_stock(series=data['贵州茅台'],stock_name='贵州茅台')

 学新通

 其他的不展示了,就展示一下投资组合的吧:

deal_stock(series=data['portfolio'],stock_name='portfolio')

学新通

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhibafci
系列文章
更多 icon
同类精品
更多 icon
继续加载