Ricerca…


Analisi quintile: con dati casuali

L'analisi a quintile è una struttura comune per valutare l'efficacia dei fattori di sicurezza.

Qual è un fattore

Un fattore è un metodo per classificare / classificare insiemi di titoli. Per un particolare momento nel tempo e per un particolare insieme di titoli, un fattore può essere rappresentato come una serie di panda in cui l'indice è una matrice degli identificatori di sicurezza ei valori sono i punteggi o i gradi.

Se prendiamo i punteggi dei fattori nel tempo, possiamo, in ogni momento, dividere il set di titoli in 5 bucket uguali, o quintili, in base all'ordine dei punteggi dei fattori. Non c'è nulla di particolarmente sacro nel numero 5. Avremmo potuto usare 3 o 10. Ma usiamo 5 spesso. Infine, monitoriamo le prestazioni di ciascuno dei cinque bucket per determinare se esiste una differenza significativa nei rendimenti. Tendiamo a concentrarci più intensamente sulla differenza nei rendimenti del bucket con il rango più alto rispetto a quello del rango più basso.


Iniziamo impostando alcuni parametri e generando dati casuali.

Per facilitare la sperimentazione con la meccanica, forniamo un codice semplice per creare dati casuali per darci un'idea di come funzioni.

Include dati casuali

  • Resi : genera rendimenti casuali per il numero specificato di titoli e periodi.
  • Segnali : generano segnali casuali per il numero specificato di titoli e periodi e con il livello prescritto di correlazione con i rendimenti . Affinché un fattore sia utile, deve esserci qualche informazione o correlazione tra i punteggi / ranghi e i successivi rendimenti. Se non ci fosse una correlazione, lo vedremmo. Sarebbe un buon esercizio per il lettore, duplicare questa analisi con dati casuali generati con 0 correlazione.

Inizializzazione

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

Ora generiamo un indice delle serie temporali e un indice che rappresenta gli ID di sicurezza. Quindi usali per creare dataframes per ritorni e segnali

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] per 25 per ridimensionare a qualcosa che assomiglia a rendimenti azionari. Aggiungo anche 1e-7 per dare un modesto ritorno medio positivo.

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

pd.qcut - Crea pd.qcut

Usiamo pd.qcut per dividere i miei segnali in bucket quintili per ogni periodo.

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)

Usa questi tagli come indice dei nostri rendimenti

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

Analisi

Riporta il grafico

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

inserisci la descrizione dell'immagine qui

Visualizza la correlazione 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()

inserisci la descrizione dell'immagine qui

Calcola e visualizza Maximum 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)

Cosa sembra questo

max_dd_df(returns_cut)

inserisci la descrizione dell'immagine qui

Tracciamolo

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)

inserisci la descrizione dell'immagine qui

Calcola statistiche

Esistono molte statistiche potenziali che possiamo includere. Qui di seguito sono solo alcuni, ma dimostra come semplicemente possiamo incorporare nuove statistiche nel nostro sommario.

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

Finiremo per utilizzare solo la funzione describe mentre riunisce tutti gli altri.

describe(returns_cut)

inserisci la descrizione dell'immagine qui


Questo non è inteso per essere completo. È pensato per riunire molte delle caratteristiche dei panda e dimostrare come puoi usarlo per rispondere a domande importanti per te. Questo è un sottoinsieme dei tipi di metriche che uso per valutare l'efficacia dei fattori quantitativi.



Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow