pandas
Analys: Förena allt och fatta beslut
Sök…
Kvintilanalys: med slumpmässiga data
Kvintilanalys är ett vanligt ramverk för att utvärdera säkerhetsfaktorernas effektivitet.
Vad är en faktor
En faktor är en metod för värdering / rangordning av värdepappersuppsättningar. För en viss tidpunkt och för en viss uppsättning värdepapper kan en faktor representeras som en pandaserie där indexet är en rad säkerhetsidentifierare och värdena är poängen eller rankningarna.
Om vi tar faktorpoäng över tid kan vi vid varje tidpunkt dela upp värdet av värdepapper i 5 lika hinkar eller kvintiler, baserat på faktorns poängs ordning. Det finns inget särskilt heligt med siffran 5. Vi kunde ha använt 3 eller 10. Men vi använder 5 ofta. Slutligen spårar vi prestandan för var och en av de fem skoporna för att avgöra om det finns en meningsfull skillnad i avkastningen. Vi tenderar att fokusera mer intensivt på skillnaden i avkastning på skopan med den högsta rankningen relativt den för den lägsta rankningen.
Låt oss börja med att ställa in några parametrar och generera slumpmässiga data.
För att underlätta experimentet med mekaniken tillhandahåller vi enkel kod för att skapa slumpmässiga data för att ge oss en uppfattning om hur detta fungerar.
Slumpmässiga data inkluderar
- Returer : genererar slumpmässig avkastning för angivet antal värdepapper och perioder.
- Signaler : generera slumpmässiga signaler för specificerat antal värdepapper och perioder och med föreskriven korrelationsnivå med Returer . För att en faktor ska vara användbar måste det finnas viss information eller korrelation mellan poängen / rankningarna och efterföljande avkastning. Om det inte fanns någon korrelation, skulle vi se det. Det skulle vara en bra övning för läsaren, duplicera denna analys med slumpmässiga data genererade med
0
korrelation.
initiering
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
Låt oss nu skapa ett tidsserieindex och ett index som representerar säkerhets-id. Använd dem sedan för att skapa dataframe för returer och signaler
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)
Jag delar m[0]
med 25
att skala ner till något som ser ut som avkastning. Jag lägger också till 1e-7
att ge en blygsam positiv medelavkastning.
security_returns = pd.DataFrame(m[0] / 25 + 1e-7, tidx, ids)
security_signals = pd.DataFrame(m[1], tidx, ids)
pd.qcut
- Skapa kvintilskopor
Låt oss använda pd.qcut
att dela upp mina signaler i kvintilskopor för varje period.
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)
Använd dessa nedskärningar som index på avkastningen
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()
Analys
Plot Returns
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()
Visualisera kvintilkorrelation med scatter_matrix
from pandas.tools.plotting import scatter_matrix
scatter_matrix(returns_cut, alpha=0.5, figsize=(8, 8), diagonal='hist')
plt.show()
Beräkna och visualisera maximalt drag ned
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)
Hur ser det här ut?
max_dd_df(returns_cut)
Låt oss planera det
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)
Beräkna statistik
Det finns många potentiella statistik som vi kan inkludera. Nedan är bara några, men visa hur enkelt vi kan införliva ny statistik i vår sammanfattning.
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')
Vi kommer bara att använda describe
eftersom den drar ihop alla andra.
describe(returns_cut)
Detta är inte avsett att vara omfattande. Det är tänkt att föra samman många av pandas funktioner och visa hur du kan använda den för att svara på frågor som är viktiga för dig. Detta är en delmängd av de typer av mätvärden jag använder för att utvärdera effektiviteten hos kvantitativa faktorer.