Python 低波动率投资组合构建

Python 低波动率投资组合构建,python,pandas,portfolio,Python,Pandas,Portfolio,我想测试股票以外的一些市场的低波动系数。与金融101相反,低波动率股票的表现优于高波动率股票(例如,见贝克、马尔科姆、布伦丹·布拉德利和杰弗里·沃尔格勒(2011),“作为套利限制的基准:理解低波动率异常”,《金融分析杂志》,第67卷,第1期,第40-54页。) 因此,我想做的是按照Jegadeesh和Titman(1993)的方法构建低波动系数,即根据之前的j历史波动率和前30%的短期波动率(波动最大)和后30%的长期波动率(波动最小)对股票进行拉宁,并在k个周期内持有该长短组合。因此,一个3

我想测试股票以外的一些市场的低波动系数。与金融101相反,低波动率股票的表现优于高波动率股票(例如,见贝克、马尔科姆、布伦丹·布拉德利和杰弗里·沃尔格勒(2011),“作为套利限制的基准:理解低波动率异常”,《金融分析杂志》,第67卷,第1期,第40-54页。)

因此,我想做的是按照Jegadeesh和Titman(1993)的方法构建低波动系数,即根据之前的j历史波动率和前30%的短期波动率(波动最大)和后30%的长期波动率(波动最小)对股票进行拉宁,并在k个周期内持有该长短组合。因此,一个3-3 j-k投资组合意味着,看看过去3个月的历史波动率(j),并在接下来的3个月内持有该投资组合(k)

我已经编写了一些代码,只需增加或减少滚动窗口vola计算的窗口,就可以轻松地管理j部分。我正在努力解决的部分是k部分,如何做到这一点。不幸的是,我在网上找不到很多例子

此外,我想知道我的代码是否正确,或者我是否犯了任何错误,因为不管我使用的是什么数据集,它都无法正常工作。我不确定这是否是一个合适的地方,但如果有人能看一看,这将是非常好的,可能会有助于其他计划实施类似战略的人

下面是一个只有10只股票的简单工作示例。正如我所说的,我想为其他一些资产实现它,但这段代码应该可以工作。您只需在第16行中使用自己的API密钥。非常感谢

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import quandl
import pickle
import scipy.optimize as sco
from scipy.ndimage.interpolation import shift
import matplotlib.pyplot as plt


##################
# Low volatility #
##################

quandl.ApiConfig.api_key = 'Your key here'
stocks = ['MSFT','AAPL','AMZN','FB','BRK.B','JPM','GOOG','JNJ','V','PG','XOM']
data = quandl.get_table('WIKI/PRICES', ticker = stocks,
                        qopts = { 'columns': ['date', 'ticker', 'adj_close'] },
                        date = { 'gte': '2016-1-1', 'lte': '2019-11-3' }, paginate=True)

# with open("data.pkl", "wb") as pickle_file:
#     pickle.dump(data, pickle_file)
# with open("data.pkl", "rb") as pickle_file:
#     data = pickle.load(pickle_file)

data = data.pivot_table(index='date', columns='ticker', values='adj_close')
data = data.groupby(pd.Grouper(freq="M")).mean() # convert from daily to monthly prices

returns = (np.log(data) - np.log(data.shift(1))).dropna()
stds = returns.rolling(12).std()
stds = stds.values # convert to numpy array

list = []
for x in range(0, stds.shape[0]): # for each row in std matrix, create decile buckets (dec -> breakpoint to the next bucket)
    for y in range(0,100,10):
        dec = np.percentile(stds[x], y)
        list.append(dec)

list = np.array(list) # convert list to numpy array
list = np.reshape(list, (stds.shape[0], -1)) # reshape the array such that it has the same format as returns (here: (26,10))

inds = []
for x in range(0, stds.shape[0]): # if the return is in the lower 30%, allocate a -1 to the asset. If it is in the upper 30%, allocate a 1. 0 otherwise.
    ind = np.digitize(stds[x], list[x])
    for x in range(0, ind.shape[0]):
        if ind[x] <= 3:
            ind[x] = 1
        elif ind[x] >= 8:
            ind[x] = -1
        else:
            ind[x] = 0
    inds.append(ind)

inds = np.array(inds)
inds = inds.astype(np.float32)

for x in inds: # divide -1, 1 and 0 by the respective total number of counts of -1, 1 and 0, such that they sum up to -1 and 1 (beta neutral long-short)
    ones = np.count_nonzero(x == 1) # count the number of 1
    minus_ones = np.count_nonzero(x == -1) # count the number of -1
    zeros = np.count_nonzero(x == 0) # count the number of 0
    for y in range(0, inds.shape[1]):
        if x[y] == 1:
            x[y] = x[y] / ones
        elif x[y] == -1:
            x[y] = x[y] / minus_ones
        else:
            x[y] = x[y] / zeros

returns = returns.shift(periods=-1).values # shift returns one period back, and create numpy array
pf_returns = np.sum((inds*returns), axis=1) # multiply returns with weights, and sum up
pf_returns = pd.DataFrame(pf_returns)

print("---")
print(pf_returns.describe())

# Plot
pf_returns_indexed = 100 * (1 + pf_returns).cumprod()
pf_returns_indexed = pf_returns_indexed.plot(linewidth=1.2) # change line width
plt.show()
将熊猫作为pd导入
将numpy作为np导入
将matplotlib.pyplot作为plt导入
导入seaborn作为sns
导入quandl
进口泡菜
将scipy.optimize导入为sco
从scipy.ndimage.interpolation导入移位
将matplotlib.pyplot作为plt导入
##################
#低波动性#
##################
quandl.ApiConfig.api_key='您的密钥在这里'
股票=['MSFT'、'AAPL'、'AMZN'、'FB'、'BRK.B'、'JPM'、'GOOG'、'JNJ'、'V'、'PG'、'XOM']
data=quandl.get_table('WIKI/PRICES',ticker=stocks,
qopts={'columns':['date','ticker','adj_close']},
日期={'gte':'2016-1-1','lte':'2019-11-3'},paginate=True)
#打开(“data.pkl”、“wb”)作为pickle_文件:
#pickle.dump(数据,pickle\u文件)
#打开(“data.pkl”、“rb”)作为pickle_文件:
#data=pickle.load(pickle\u文件)
data=data.pivot\u表(index='date',columns='ticker',values='adj\u close')
data=data.groupby(pd.Grouper(freq=“M”)).mean()#将每日价格转换为每月价格
返回=(np.log(data)-np.log(data.shift(1)).dropna()
stds=返回值.rolling(12).std()
stds=stds.values#转换为numpy数组
列表=[]
对于范围内的x(0,stds.shape[0]):#对于std矩阵中的每一行,创建十进制存储桶(dec->breakpoint到下一个存储桶)
对于范围(0100,10)内的y:
十二月=百分位数(性病[x],y)
列表。追加(12月)
list=np.array(list)#将list转换为numpy数组
list=np.reformate(list,(stds.shape[0],-1))#对数组进行整形,使其具有与返回相同的格式(此处:(26,10))
inds=[]
对于范围内的x(0,stds.shape[0]):#如果回报率在较低的30%,则将-1分配给资产。如果在上30%,则分配1。否则为0。
ind=np.数字化(STD[x],列表[x])
对于范围(0,ind.shape[0])内的x:
如果ind[x]=8:
ind[x]=-1
其他:
ind[x]=0
inds.append(ind)
inds=np.数组(inds)
inds=inds.astype(np.32)
对于IND中的x:#将-1、1和0除以-1、1和0的计数总数,使其总和为-1和1(β中性长-短)
一=np。计数非零(x==1)#计数1的数目
负1=np。计数非零(x==-1)#计数-1的个数
零=np。计数_非零(x==0)#计数0的数量
对于范围(0,独立形状[1])中的y:
如果x[y]==1:
x[y]=x[y]/个
elif x[y]==-1:
x[y]=x[y]/负1
其他:
x[y]=x[y]/零
returns=returns.shift(句点=-1)。value#shift返回一个句点,并创建numpy数组
pf_返回值=np.sum((inds*returns),axis=1)#将返回值与权重相乘,求和
pf_返回=pd.DataFrame(pf_返回)
打印(“--”)
打印(pf_returns.descripe())
#密谋
pf_返回值索引=100*(1+pf_返回值).cumprod()
pf_返回_索引=pf_返回_索引。绘图(线宽=1.2)#更改线宽
plt.show()