Строительство портфеля с низкой волатильностью - PullRequest
0 голосов
/ 07 ноября 2019

Я хочу проверить коэффициент низкой волатильности для какого-то рынка, кроме акций. В отличие от финансов 101, было показано, что акции с низкой волатильностью превосходят акции с высокой волатильностью (см., Например, Baker, Malcolm, Brendan Bradley и Jeffrey Wurgler (2011), «Тесты как пределы арбитража: понимание аномалии низкой волатильности»)Журнал Financial Analyst Journal, том 67, № 1, с. 40–54.)

Итак, я хочу построить коэффициент низкой волы, следуя методологии Jegadeesh и Titman (1993),а именно: ранжирование акций в соответствии с их предыдущей j исторической волатильностью и короткой верхней 30% (наиболее волатильной) и длинной нижней 30% (наименее волатильной), и удержание этого длинного короткого портфеля в течение k периодов. Таким образом, Портфолио в 3-3 тысячи долларов будет означать, если посмотреть на последние 3 месяца исторической волатильности (j), и удерживать этот Портфель в течение следующих 3 месяцев (k).

Я написал некоторый Кодекс, иЧасть j может легко управляться простым увеличением или уменьшением окна расчета скользящего окна. Часть, с которой я борюсь - это часть k, как это можно сделать. К сожалению, я не смог найти много примеров в Интернете.

Кроме того, мне было интересно, правильный ли мой код или я допустил какую-либо ошибку, поскольку он неожиданно не работал, независимо от набора данных, который я использовал. Я не уверен, что это правильное место, чтобы спросить, но если бы кто-то мог взглянуть на это, это было бы здорово и могло бы быть полезным для других, планирующих реализовать такую ​​стратегию.

Ниже приведен простой рабочий пример с 10 акциями. Как я уже сказал, я хочу реализовать это для некоторых других активов, но этот Кодекс должен работать. Вы просто должны использовать свой собственный ключ API в строке 16. Большое спасибо!

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()
...