Buscar..


Análisis quintil: con datos aleatorios

El análisis quintil es un marco común para evaluar la eficacia de los factores de seguridad.

Que es un factor

Un factor es un método para calificar / clasificar conjuntos de valores. Para un punto particular en el tiempo y para un conjunto particular de valores, un factor puede representarse como una serie de pandas donde el índice es una matriz de los identificadores de seguridad y los valores son las puntuaciones o rangos.

Si tomamos las puntuaciones de los factores a lo largo del tiempo, podemos, en cada momento, dividir el conjunto de valores en 5 grupos o quintiles iguales, según el orden de las puntuaciones de los factores. No hay nada particularmente sagrado en el número 5. Podríamos haber usado 3 o 10. Pero usamos 5 a menudo. Finalmente, hacemos un seguimiento del rendimiento de cada uno de los cinco grupos para determinar si hay una diferencia significativa en las devoluciones. Tendemos a enfocarnos más intensamente en la diferencia en los rendimientos del grupo con el rango más alto en relación con el rango más bajo.


Comencemos estableciendo algunos parámetros y generando datos aleatorios.

Para facilitar la experimentación con los mecanismos, proporcionamos un código simple para crear datos aleatorios que nos dan una idea de cómo funciona esto.

Incluye datos aleatorios

  • Devoluciones : generar devoluciones aleatorias para un número específico de valores y periodos.
  • Señales : genere señales aleatorias para un número específico de valores y períodos y con el nivel prescrito de correlación con las devoluciones . Para que un factor sea útil, debe haber alguna información o correlación entre las puntuaciones / rangos y los rendimientos posteriores. Si no hubiera correlación, lo veríamos. Ese sería un buen ejercicio para el lector, duplique este análisis con datos aleatorios generados con 0 correlaciones.

Inicialización

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

Ahora generemos un índice de series de tiempo y un índice que represente los identificadores de seguridad. Luego úselos para crear marcos de datos para devoluciones y señales

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)

Divido m[0] por 25 para reducir a algo que se parece a los rendimientos de las acciones. También agrego 1e-7 para dar un rendimiento promedio positivo modesto.

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

pd.qcut - Crea cubos quintiles

pd.qcut para dividir mis señales en pd.qcut de quintiles para cada período.

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)

Utilice estos recortes como un índice en nuestras devoluciones

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

Análisis

Devoluciones de parcela

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

introduzca la descripción de la imagen aquí

Visualizar la correlación del scatter_matrix con scatter_matrix

from pandas.tools.plotting import scatter_matrix

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

introduzca la descripción de la imagen aquí

Calcula y visualiza Máximo 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)

A qué se parece esto

max_dd_df(returns_cut)

introduzca la descripción de la imagen aquí

Vamos a trazarlo

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)

introduzca la descripción de la imagen aquí

Calcular estadísticas

Hay muchas estadísticas potenciales que podemos incluir. A continuación se muestran solo algunas, pero demuestre cómo podemos incorporar nuevas estadísticas en nuestro resumen.

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')

Terminaremos usando solo la función de describe , ya que une a todos los demás.

describe(returns_cut)

introduzca la descripción de la imagen aquí


Esto no pretende ser exhaustivo. Está pensado para reunir muchas de las características de los pandas y demostrar cómo se puede usar para responder preguntas importantes para usted. Este es un subconjunto de los tipos de métricas que utilizo para evaluar la eficacia de los factores cuantitativos.



Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow