Поиск…


Анализ Quintile: со случайными данными

Анализ Quintile является общей основой для оценки эффективности факторов безопасности.

Что такое фактор

Фактор - это метод оценки / оценки наборов ценных бумаг. В определенный момент времени и для определенного набора ценных бумаг фактор может быть представлен как серия панд, где индекс представляет собой массив идентификаторов безопасности, а значения - это оценки или ранги.

Если мы будем оценивать множители с течением времени, мы можем в каждый момент времени разбить набор ценных бумаг на 5 равных ковшей или квинтилей на основе порядка коэффициентов. Нет ничего особенного в отношении числа 5. Мы могли бы использовать 3 или 10. Но мы часто используем 5. Наконец, мы отслеживаем производительность каждого из пяти ведер, чтобы определить, есть ли значительная разница в доходах. Мы склонны более пристально фокусироваться на различиях в доходности ведра с самым высоким рангом относительно наименьшего ранга.


Начнем с установки некоторых параметров и создания случайных данных.

Чтобы облегчить эксперименты с механикой, мы предоставляем простой код для создания случайных данных, чтобы дать нам представление о том, как это работает.

Случайные данные включают

  • Возвращает : генерирует случайные доходности для указанного количества ценных бумаг и периодов.
  • Сигналы : генерируют случайные сигналы для определенного количества ценных бумаг и периодов и с заданным уровнем корреляции с Returns . Для того чтобы фактор был полезным, должна быть какая-то информация или корреляция между баллами / рангами и последующими доходами. Если бы не было корреляции, мы бы это увидели. Это было бы хорошим упражнением для читателя, дублируйте этот анализ со случайными данными, созданными с корреляцией 0 .

инициализация

import pandas as pd
import numpy as np

num_securities = 1000
num_periods = 1000
period_frequency = 'W'
start_date = '2000-12-31'

np.random.seed([3,1415])

means = [0, 0]
covariance = [[  1., 5e-3],
              [5e-3,   1.]]

# generates to sets of data m[0] and m[1] with ~0.005 correlation
m = np.random.multivariate_normal(means, covariance,
                                  (num_periods, num_securities)).T

Теперь создадим индекс временных рядов и индекс, представляющий идентификаторы безопасности. Затем используйте их для создания dataframes для возвратов и сигналов

ids = pd.Index(['s{:05d}'.format(s) for s in range(num_securities)], 'ID')
tidx = pd.date_range(start=start_date, periods=num_periods, freq=period_frequency)

Я делю m[0] на 25 чтобы уменьшить масштаб до того, что выглядит как возврат акций. Я также добавляю 1e-7 чтобы дать умеренный положительный средний доход.

security_returns = pd.DataFrame(m[0] / 25 + 1e-7, tidx, ids)
security_signals = pd.DataFrame(m[1], tidx, ids)

pd.qcut - Создайте pd.qcut Quintile

Давайте используем pd.qcut чтобы разделить мои сигналы на квинтильные ведра за каждый период.

def qcut(s, q=5):
    labels = ['q{}'.format(i) for i in range(1, 6)]
    return pd.qcut(s, q, labels=labels)
    
cut = security_signals.stack().groupby(level=0).apply(qcut)

Используйте эти сокращения как индекс для наших возвратов

returns_cut = security_returns.stack().rename('returns') \
    .to_frame().set_index(cut, append=True) \
    .swaplevel(2, 1).sort_index().squeeze() \
    .groupby(level=[0, 1]).mean().unstack()

Анализ

Возврат к списку

import matplotlib.pyplot as plt

fig = plt.figure(figsize=(15, 5))
ax1 = plt.subplot2grid((1,3), (0,0))
ax2 = plt.subplot2grid((1,3), (0,1))
ax3 = plt.subplot2grid((1,3), (0,2))

# Cumulative Returns
returns_cut.add(1).cumprod() \
    .plot(colormap='jet', ax=ax1, title="Cumulative Returns")
leg1 = ax1.legend(loc='upper left', ncol=2, prop={'size': 10}, fancybox=True)
leg1.get_frame().set_alpha(.8)

# Rolling 50 Week Return
returns_cut.add(1).rolling(50).apply(lambda x: x.prod()) \
    .plot(colormap='jet', ax=ax2, title="Rolling 50 Week Return")
leg2 = ax2.legend(loc='upper left', ncol=2, prop={'size': 10}, fancybox=True)
leg2.get_frame().set_alpha(.8)

# Return Distribution
returns_cut.plot.box(vert=False, ax=ax3, title="Return Distribution")

fig.autofmt_xdate()

plt.show()

введите описание изображения здесь

Визуализируйте корреляцию Quintile с помощью scatter_matrix

from pandas.tools.plotting import scatter_matrix

scatter_matrix(returns_cut, alpha=0.5, figsize=(8, 8), diagonal='hist')
plt.show()

введите описание изображения здесь

Вычислить и визуализировать максимальный Draw Down

def max_dd(returns):
    """returns is a series"""
    r = returns.add(1).cumprod()
    dd = r.div(r.cummax()).sub(1)
    mdd = dd.min()
    end = dd.argmin()
    start = r.loc[:end].argmax()
    return mdd, start, end

def max_dd_df(returns):
    """returns is a dataframe"""
    series = lambda x: pd.Series(x, ['Draw Down', 'Start', 'End'])
    return returns.apply(max_dd).apply(series)

Как это выглядит

max_dd_df(returns_cut)

введите описание изображения здесь

Давайте заговорим

draw_downs = max_dd_df(returns_cut)

fig, axes = plt.subplots(5, 1, figsize=(10, 8))
for i, ax in enumerate(axes[::-1]):
    returns_cut.iloc[:, i].add(1).cumprod().plot(ax=ax)
    sd, ed = draw_downs[['Start', 'End']].iloc[i]
    ax.axvspan(sd, ed, alpha=0.1, color='r')
    ax.set_ylabel(returns_cut.columns[i])

fig.suptitle('Maximum Draw Down', fontsize=18)
fig.tight_layout()
plt.subplots_adjust(top=.95)

введите описание изображения здесь

Рассчитать статистику

Есть много потенциальных статистических данных, которые мы можем включить. Ниже всего несколько, но продемонстрируйте, как просто мы можем включить новую статистику в наше резюме.

def frequency_of_time_series(df):
    start, end = df.index.min(), df.index.max()
    delta = end - start
    return round((len(df) - 1.) * 365.25 / delta.days, 2)

def annualized_return(df):
    freq = frequency_of_time_series(df)
    return df.add(1).prod() ** (1 / freq) - 1
    
def annualized_volatility(df):
    freq = frequency_of_time_series(df)
    return df.std().mul(freq ** .5)

def sharpe_ratio(df):
    return annualized_return(df) / annualized_volatility(df)

def describe(df):
    r = annualized_return(df).rename('Return')
    v = annualized_volatility(df).rename('Volatility')
    s = sharpe_ratio(df).rename('Sharpe')
    skew = df.skew().rename('Skew')
    kurt = df.kurt().rename('Kurtosis')
    desc = df.describe().T
    
    return pd.concat([r, v, s, skew, kurt, desc], axis=1).T.drop('count')

Мы закончим тем, что будем использовать только функцию describe как она объединяет всех остальных.

describe(returns_cut)

введите описание изображения здесь


Это не должно быть всеобъемлющим. Он призван объединить многие функции панд и продемонстрировать, как вы можете использовать его, чтобы помочь ответить на важные для вас вопросы. Это подмножество типов показателей, которые я использую для оценки эффективности количественных факторов.



Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow