← Alle Insights

PCA-Faktor-Mining: Residuen-Strategien aus latenten Faktoren.

Pairs-Trading skaliert schlecht — irgendwann reichen Ihnen die paar Dutzend cointegrierten Pairs nicht mehr. Die natürliche Verallgemeinerung: ein statistisches Faktor-Modell auf das gesamte Universum, das systematisches Risiko entfernt und idiosynkratische Mean Reversion handelt. PCA liefert die Faktoren dafür.

Vom Pair zum Faktor-Modell.

In einem Pair erklären Sie y durch x. In einem Faktor-Modell erklären Sie jedes Asset durch eine kleine Zahl gemeinsamer Faktoren. Die Residuen — das, was die Faktoren nicht erklären — sind Ihr Spread. Wenn diese Residuen stationär und mean-reverting sind, haben Sie die Grundlage für eine Statistical-Arbitrage-Strategie mit hunderten gleichzeitigen Positionen.

Anders als bei Fama-French wählen wir die Faktoren nicht ökonomisch (Size, Value, Momentum), sondern lassen sie statistisch aus den Daten emergieren. PCA findet orthogonale Linearkombinationen der Returns, die maximal Varianz erklären. Die ersten 1–3 Komponenten korrelieren typischerweise stark mit dem Markt und großen Sektoren — der Rest ist verteilbar.

Datenaufbereitung: das oft Unterschätzte.

PCA reagiert empfindlich auf Vorverarbeitung. Drei Schritte, ohne die Sie nichts Sinnvolles bekommen:

  1. Returns statt Preise: PCA auf Preisen führt zu degenerierten Komponenten, weil Preise driften.
  2. Standardisierung: Jeden Return auf Mittelwert 0 und Standardabweichung 1 normieren. Sonst dominieren volatile Aktien das Bild.
  3. Outlier-Behandlung: Winsorisieren bei 3 Sigma oder Median-basierte Skalierung. Earnings-Tage und Crash-Tage können PCA komplett verzerren.
import numpy as np
import pandas as pd
from sklearn.decomposition import PCA

def prepare_returns(prices, window=252, winsor=3.0):
    """Returns ableiten, winsorisieren, standardisieren."""
    rets = np.log(prices / prices.shift(1)).dropna()
    # Pro Spalte standardisieren auf rollendem Fenster
    mu = rets.rolling(window).mean()
    sd = rets.rolling(window).std()
    z = (rets - mu) / sd
    # Winsorisieren
    z = z.clip(lower=-winsor, upper=winsor)
    return z.dropna()

def fit_pca_returns(z_returns, n_components=5):
    pca = PCA(n_components=n_components)
    factors = pca.fit_transform(z_returns.values)
    loadings = pd.DataFrame(
        pca.components_.T,
        index=z_returns.columns,
        columns=[f'F{i+1}' for i in range(n_components)]
    )
    factors_df = pd.DataFrame(
        factors,
        index=z_returns.index,
        columns=[f'F{i+1}' for i in range(n_components)]
    )
    explained = pca.explained_variance_ratio_
    return factors_df, loadings, explained

Wie viele Faktoren? Marchenko-Pastur als Leitlinie.

Die Frage „wie viele Komponenten?" wird oft naiv beantwortet — Scree-Plot, Elbow, 80-%-erklärte-Varianz. Im Quant-Kontext gibt es einen besseren Anker: die Marchenko-Pastur-Verteilung. Sie sagt Ihnen, welche Eigenwerte einer Zufalls- Korrelationsmatrix entsprechen würden — alles darüber ist „Signal", alles darunter ist „Rauschen".

Bei T = 1000 Handelstagen und N = 500 Aktien liegt die obere Grenze typischer Zufalls-Eigenwerte bei etwa (1 + √(N/T))² ≈ 2,9. Eigenwerte oberhalb dieser Grenze sind „echte" Faktoren — bei einem typischen US-Aktienuniversum sind das die ersten 5–15 Komponenten.

In der Praxis nehmen wir die ersten 5–8 Faktoren für die meisten Anwendungen. Marktkomponente plus Sektor-Strukturen stecken da fast immer drin.

Residuen-Konstruktion und Signal-Generierung.

Hat man Faktoren F und Loadings B, sind die Residuen für jedes Asset:

r_residual_i,t = r_i,t − B_i · F_t

Das ist der idiosynkratische Anteil — was die Aktie an dem Tag „falsch" gemacht hat gegenüber dem, was die gemeinsamen Faktoren impliziert hätten. Die Annahme: diese Abweichung kehrt zurück. Aufgesetzt auf die kumulative Residual-Reihe haben Sie wieder einen Spread, den Sie mit Z-Score handeln.

def residual_signals(returns, factors, loadings, lookback=60, entry_z=1.5):
    """Pro Asset: kumulative Residuen, Z-Score, Long/Short-Signal."""
    # Residuen = tatsächliche Returns - vorhergesagte Returns
    predicted = factors.values @ loadings.values.T
    residuals = returns.values - predicted
    residuals = pd.DataFrame(residuals,
                             index=returns.index,
                             columns=returns.columns)
    # Kumulieren zur Spread-Reihe
    cumres = residuals.cumsum()
    # Z-Score über rollendes Fenster
    mu = cumres.rolling(lookback).mean()
    sd = cumres.rolling(lookback).std()
    z = (cumres - mu) / sd
    # Signal: Mean-Reversion auf den Z-Score
    pos = -z.clip(lower=-entry_z, upper=entry_z) / entry_z
    pos = pos.where(z.abs() > entry_z, 0.0)
    return pos, z

Das Ergebnis ist ein dollar-neutrales Portfolio: Sie sind long in den unterperformenden Residuen und short in den outperformenden. Über Tausende Aktien hinweg hebt sich das Marktrisiko fast vollständig heraus.

Risiko-Allokation: Equal-Weight vs. Risk-Parity.

Naives Equal-Weight pro Aktie funktioniert, ist aber suboptimal. Aktien mit niedriger Residual-Volatilität bekommen die gleiche Gewichtung wie hochvolatile — das verzerrt die effektive Risiko-Exposure. Besser: Inverse-Volatility-Weighting, bei dem die Position skaliert wird mit der inversen Residual-Volatilität.

In großem Maßstab (200+ Namen) lohnt sich Mean-Variance-Optimization mit Constraints: Sektor-Neutralität, Beta-Neutralität gegen die Faktoren selbst, maximale Single-Name- Konzentration. Das ist der Punkt, an dem das ganze Setup von „Hobby-Quant" zu „institutioneller Stat-Arb" wird.

Rolling-PCA vs. statische PCA.

Faktor-Strukturen ändern sich. 2008 sah anders aus als 2019, 2022 anders als 2024. Wer statische PCA über den gesamten Backtest-Zeitraum schätzt, baut Look-Ahead direkt ins Modell ein.

Rolling-PCA mit Fenstern von 252 Handelstagen (1 Jahr) liefert realistische Out-of- Sample-Performance, ist aber numerisch teurer. Wenn der Backtest 20 Jahre und 1.000 Aktien umfasst, müssen Sie sich Gedanken machen — Sparse-PCA, Online-PCA oder randomized SVD sind die Stichworte.

Pragmatischer Mittelweg: Faktoren alle 20–40 Handelstage neu schätzen statt täglich. Faktor-Strukturen ändern sich nicht jeden Tag substanziell, und Sie sparen 90 % der Rechenzeit.

Was uns die letzten Jahre gelehrt haben.

PCA-basierte Statistical Arbitrage war früher der heilige Gral. Avellaneda und Lee haben 2010 mit dem klassischen Paper gezeigt, dass Residuen-Strategien Sharpe-Ratios von 1,5+ erzielten. Heute sind die einfachsten Versionen weitgehend ausgespielt — zu viele Player handeln dieselben Signale.

Was funktioniert noch:

Mein Pragmatismus zum Faktor-Mining.

PCA bleibt das mit Abstand robusteste Faktor-Modell für Stat-Arb. Es ist interpretierbar (Loadings zeigen klar, welche Aktien zusammenhängen), schnell zu schätzen und liefert orthogonale Faktoren — ein riesiger Vorteil gegenüber ökonomischen Modellen mit ihren korrelierten Faktoren.

In unseren Setups läuft PCA-Faktor-Mining als „Risiko-Modell" parallel zum Signal- Generator. Selbst wenn die Strategie eigentlich nichts mit PCA zu tun hat — wir nutzen die Faktoren, um sicherzustellen, dass das resultierende Portfolio keine ungewollten Risiko-Exposures aufbaut. Das ist oft der größere Hebel als die Signal- Generierung selbst.

Sie wollen ein Faktor-Modell oder eine PCA-basierte Stat-Arb-Strategie aufbauen? Erstgespräch buchen — wir setzen das Modell, das Risiko-Framework und die Live-Engine auf.