Suche…


Quintil-Analyse: mit zufälligen Daten

Die Quintilanalyse ist ein allgemeiner Rahmen für die Bewertung der Wirksamkeit von Sicherheitsfaktoren.

Was ist ein Faktor?

Ein Faktor ist eine Methode zum Scoring / Ranking von Wertpapieren. Für einen bestimmten Zeitpunkt und für einen bestimmten Satz von Wertpapieren kann ein Faktor als Pandaserie dargestellt werden, wobei der Index ein Array der Wertpapierkennungen ist und die Werte die Bewertungen oder Ränge sind.

Wenn wir die Faktorwerte über die Zeit nehmen, können wir zu jedem Zeitpunkt den Satz von Wertpapieren in fünf gleiche Buckets oder Quintile unterteilen, basierend auf der Reihenfolge der Faktorwerte. An der Zahl 5 ist nichts besonders Heiliges. Wir hätten 3 oder 10 verwenden können. Aber wir verwenden häufig 5. Schließlich verfolgen wir die Leistung jedes der fünf Buckets, um festzustellen, ob es einen signifikanten Unterschied bei den Renditen gibt. Wir tendieren dazu, uns intensiver auf die Ertragsunterschiede des Eimers mit dem höchsten Rang im Vergleich zum niedrigsten Rang zu konzentrieren.


Beginnen wir mit der Einstellung einiger Parameter und der Generierung von Zufallsdaten.

Um das Experimentieren mit den Mechanismen zu erleichtern, bieten wir einfachen Code zum Erstellen von Zufallsdaten, um eine Vorstellung davon zu erhalten, wie dies funktioniert.

Zufällige Daten enthalten

  • Rückkehr: erzeugt zufällige Renditen für festgelegte Anzahl von Wertpapieren und Perioden.
  • Signale : Generieren Sie Zufallssignale für eine bestimmte Anzahl von Wertpapieren und Perioden und mit einem vorgeschriebenen Korrelationsgrad mit den Renditen . Damit ein Faktor nützlich ist, müssen einige Informationen oder Korrelationen zwischen den Bewertungen / Rängen und den nachfolgenden Ergebnissen vorhanden sein. Wenn es keine Korrelation gäbe, würden wir es sehen. Das wäre eine gute Übung für den Leser, diese Analyse mit zufälligen Daten zu kopieren, die mit einer 0 Korrelation generiert wurden.

Initialisierung

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

Jetzt generieren wir einen Zeitreihenindex und einen Index, der die Sicherheits-IDs darstellt. Dann erstellen Sie damit Datenrahmen für Rückgaben und Signale

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)

Ich dividiere m[0] durch 25 , um etwas zu reduzieren, das wie Aktienrenditen aussieht. Ich füge auch 1e-7 , um eine bescheidene positive mittlere Rendite zu erzielen.

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

pd.qcut - Erstellen Sie Quintil-Buckets

Verwenden pd.qcut , um meine Signale für jede Periode in Quintil-Buckets zu unterteilen.

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)

Verwenden Sie diese Kürzungen als Index für unsere Renditen

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

Analyse

Plot kehrt zurück

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

Geben Sie hier die Bildbeschreibung ein

Visualisieren Sie die scatter_matrix mit scatter_matrix

from pandas.tools.plotting import scatter_matrix

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

Geben Sie hier die Bildbeschreibung ein

Berechnen und visualisieren Sie den maximalen 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)

Wie sieht das aus?

max_dd_df(returns_cut)

Geben Sie hier die Bildbeschreibung ein

Lass es uns plotten

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)

Geben Sie hier die Bildbeschreibung ein

Statistiken berechnen

Es gibt viele mögliche Statistiken, die wir aufnehmen können. Im Folgenden sind nur einige davon aufgeführt, zeigen Sie jedoch, wie einfach neue Statistiken in unsere Zusammenfassung aufgenommen werden können.

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

Am Ende verwenden wir nur die Funktion describe , da sie alle anderen zusammenbringt.

describe(returns_cut)

Geben Sie hier die Bildbeschreibung ein


Dies soll nicht umfassend sein. Es soll viele Funktionen von Pandas zusammenführen und demonstrieren, wie Sie es verwenden können, um die für Sie wichtigen Fragen zu beantworten. Dies ist eine Teilmenge der Arten von Metriken, die ich zur Bewertung der Wirksamkeit quantitativer Faktoren verwende.



Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow