Kann ein LLM Alpha-Faktoren finden?
Ein LLM erstellt in einer Stunde hundert Alpha-Faktoren. Wir testen 240 Zufallsfaktoren mit zehn Jahren echter Kurse und zeigen, wie Sie robuste Kandidaten prüfen.
Ein LLM kann den ganzen Tag Alpha-Faktoren vorschlagen. Gibt man einem leistungsfähigen Modell ein Datenwörterbuch und ein Scoring-Framework, schreibt es vor dem Mittagessen hundert plausible Faktorausdrücke. Die schwierigere Frage liegt darunter: Wie soll man jemals wissen, ob einer davon tatsächlich funktioniert, wenn die Suche, die ihn hervorgebracht hat, eine Maschine zur Herstellung von Gewinnern aus Rauschen ist?
Was ist ein Alpha-Faktor?
Ein Faktor ist eine Regel, die Marktdaten in eine Zahl für jede Aktie an jedem Handelstag umwandelt. Die Kursveränderung über zwölf Monate ist ein Faktor. Das Verhältnis von Fremd- zu Eigenkapital ebenfalls. Ein Faktor wird zu einer Strategie, wenn Sie ein Anlageuniversum danach sortieren, den obersten Teil kaufen, den untersten verkaufen und nach einem festen Zeitplan rebalancieren. Alpha ist die Rendite, die übrig bleibt, nachdem Sie die Rendite abgezogen haben, die ein einfaches Marktengagement ohnehin erbracht hätte.
Kandidaten werden mit der Sharpe Ratio bewertet: der durchschnittlichen Rendite geteilt durch die Standardabweichung dieser Rendite und auf ein Jahr hochgerechnet. Sie misst die Rendite pro Einheit Schwankung. Eine langfristige Sharpe Ratio nahe 1 bei einer Live-Strategie ist solide. Das sollten Sie im Hinterkopf behalten, wenn ein Backtest das nächste Mal eine Sharpe Ratio von 3 ausweist.
Wie die Faktorforschung mit LLMs tatsächlich funktioniert
Jedes Projekt in diesem Bereich durchläuft eine Variante derselben Schleife.
- Das Modell schreibt Faktorausdrücke in einer kleinen Sprache, die das Framework auswerten kann.
- Ein Backtester bewertet jeden Ausdruck anhand einer festen historischen Zeitreihe von Kursen und Fundamentaldaten.
- Ausdrücke oberhalb eines Schwellenwerts werden behalten. Der Rest wird verworfen.
- Die behaltenen Ausdrücke gelangen als Beispiele in den Kontext des Modells, und die Schleife beginnt von vorn.
Multi-Agent-Handelssysteme verteilen diese Aufgaben auf getrennte Rollen: Eine Rolle schlägt Ausdrücke vor, eine andere testet sie. Die technische Infrastruktur ist tatsächlich nützlich. Die Marktdatenkenntnisse, die ein KI-Agent benötigt sind dieselben, die auch ein Mensch braucht.
Nichts an dieser Schleife ist unehrlich. So wird Forschung betrieben. Das Problem ist die Mathematik. Es entsteht in dem Moment, in dem Schritt 2 mehr als einige wenige Male ausgeführt wird.
Warum eine LLM-Suche nach Alpha-Faktoren Gewinner aus Rauschen erzeugt
Eine Kurszeitreihe. Tausende kostengünstige Hypothesen. Jede Hypothese wird anhand derselben begrenzten Stichprobe bewertet. Diese Stichprobe enthält viel Zufall. Testet man genügend Regeln, werden einige eng an diesen Zufall angepasst sein. Der Score kann nicht erkennen, welche Art von Anpassung vorliegt. Eine Regel, die auf Rauschen passt, und eine Regel, die auf den Markt passt, erzeugen denselben Zahlenwert.
Hier ist die Nullhypothese, 240-mal gezogen. Jeder der folgenden „Faktoren“ ist ein Münzwurf: Ein Hash aus Ticker, Monat und Versuchsnummer teilt jeweils 40 große US-Aktien in zwei Hälften. Die Strategie geht eine Hälfte long und die andere short. Konstruktionsbedingt enthält sie keinerlei Information. Bewertet anhand der tatsächlichen Monatsrenditen von Januar 2016 bis Juni 2021 verteilen sich die 240 Versuche wie folgt.
Das exakte SQL hinter jeder Zahl
WITH month_end AS (
SELECT ticker,
toStartOfMonth(toDate(toTimeZone(window_start, 'America/New_York'))) AS month_start,
argMax(toFloat64(close), window_start) AS close_px
FROM global_markets.delayed_stocks_minute_aggs
WHERE ticker IN ('AAPL','ADBE','AMZN','BA','CAT','COST','CRM','CSCO','CVX','DE',
'DUK','GE','GOOGL','HD','HON','IBM','INTC','JNJ','JPM','KO',
'LMT','MCD','MMM','MRK','MSFT','NKE','NVDA','ORCL','PEP','PFE',
'PG','QCOM','SO','T','TGT','TXN','UNP','VZ','WMT','XOM')
AND toDate(toTimeZone(window_start, 'America/New_York')) >= toDate('2015-12-01')
AND toDate(toTimeZone(window_start, 'America/New_York')) <= toDate('2021-06-30')
AND toDayOfMonth(toTimeZone(window_start, 'America/New_York')) >= 22
AND (toHour(toTimeZone(window_start, 'America/New_York')) * 60
+ toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959
GROUP BY ticker, month_start
),
lagged AS (
SELECT ticker,
month_start,
close_px,
lagInFrame(close_px) OVER (PARTITION BY ticker ORDER BY month_start
ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) AS prev_px
FROM month_end
),
monthly_return AS (
SELECT ticker, month_start, close_px / prev_px - 1 AS ret
FROM lagged
WHERE prev_px > 0
AND month_start >= toDate('2016-01-01')
),
trial AS (
SELECT arrayJoin(range(1, 241)) AS n
),
factor_month AS (
SELECT t.n AS trial_id,
m.month_start AS month_start,
avgIf(m.ret, bitAnd(cityHash64(m.ticker, toString(m.month_start), t.n), 1) = 1)
- avgIf(m.ret, bitAnd(cityHash64(m.ticker, toString(m.month_start), t.n), 1) = 0) AS long_short_ret
FROM monthly_return AS m
CROSS JOIN trial AS t
GROUP BY trial_id, month_start
HAVING countIf(bitAnd(cityHash64(m.ticker, toString(m.month_start), t.n), 1) = 1) > 0
AND countIf(bitAnd(cityHash64(m.ticker, toString(m.month_start), t.n), 1) = 0) > 0
),
scored AS (
SELECT trial_id,
avg(long_short_ret) / stddevSamp(long_short_ret) * sqrt(12) AS sharpe
FROM factor_month
GROUP BY trial_id
HAVING stddevSamp(long_short_ret) > 0
)
SELECT multiIf(sharpe < -1.2, 'below -1.2',
sharpe < -0.8, '-1.2 to -0.8',
sharpe < -0.4, '-0.8 to -0.4',
sharpe < 0.0, '-0.4 to 0.0',
sharpe < 0.4, '0.0 to 0.4',
sharpe < 0.8, '0.4 to 0.8',
sharpe < 1.2, '0.8 to 1.2',
'1.2 and above') AS sharpe_bucket,
count() AS factor_count,
round(100 * count() / 240, 1) AS share_pct
FROM scored
GROUP BY sharpe_bucket
ORDER BY min(sharpe)Die Streuung ist der entscheidende Punkt. Nichts in diesem Diagramm prognostiziert irgendetwas. Trotzdem landeten 1 Versuche im oberen Bereich (1.2 and above), 0.4% der Suche, und 1 im unteren Bereich (below -1.2). Ein Researcher, der einen glücklichen Versuch durchgeführt und danach aufgehört hätte, besäße ein Diagramm und eine Sharpe Ratio. Er hätte aber keine Möglichkeit festzustellen, ob es sich dabei um eine Entdeckung handelt. Die Renditen werden hier vom Monatsultimo zum Monatsultimo berechnet. Wie Monatsrenditen berechnet werden erläutert diese Mathematik.
Entscheidend ist die Zahl der getesteten Hypothesen
Ein isoliert ausgewiesener Backtest lässt seinen Nenner offen. Dieselben 240 Versuche, diesmal als fortlaufend erweiterte Suche betrachtet: Bei jedem Schritt steht der beste bisherige Score neben dem Durchschnitt aller bis dahin getesteten Versuche.
Das exakte SQL hinter jeder Zahl
WITH month_end AS (
SELECT ticker,
toStartOfMonth(toDate(toTimeZone(window_start, 'America/New_York'))) AS month_start,
argMax(toFloat64(close), window_start) AS close_px
FROM global_markets.delayed_stocks_minute_aggs
WHERE ticker IN ('AAPL','ADBE','AMZN','BA','CAT','COST','CRM','CSCO','CVX','DE',
'DUK','GE','GOOGL','HD','HON','IBM','INTC','JNJ','JPM','KO',
'LMT','MCD','MMM','MRK','MSFT','NKE','NVDA','ORCL','PEP','PFE',
'PG','QCOM','SO','T','TGT','TXN','UNP','VZ','WMT','XOM')
AND toDate(toTimeZone(window_start, 'America/New_York')) >= toDate('2015-12-01')
AND toDate(toTimeZone(window_start, 'America/New_York')) <= toDate('2021-06-30')
AND toDayOfMonth(toTimeZone(window_start, 'America/New_York')) >= 22
AND (toHour(toTimeZone(window_start, 'America/New_York')) * 60
+ toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959
GROUP BY ticker, month_start
),
lagged AS (
SELECT ticker,
month_start,
close_px,
lagInFrame(close_px) OVER (PARTITION BY ticker ORDER BY month_start
ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) AS prev_px
FROM month_end
),
monthly_return AS (
SELECT ticker, month_start, close_px / prev_px - 1 AS ret
FROM lagged
WHERE prev_px > 0
AND month_start >= toDate('2016-01-01')
),
trial AS (
SELECT arrayJoin(range(1, 241)) AS n
),
factor_month AS (
SELECT t.n AS trial_id,
m.month_start AS month_start,
avgIf(m.ret, bitAnd(cityHash64(m.ticker, toString(m.month_start), t.n), 1) = 1)
- avgIf(m.ret, bitAnd(cityHash64(m.ticker, toString(m.month_start), t.n), 1) = 0) AS long_short_ret
FROM monthly_return AS m
CROSS JOIN trial AS t
GROUP BY trial_id, month_start
HAVING countIf(bitAnd(cityHash64(m.ticker, toString(m.month_start), t.n), 1) = 1) > 0
AND countIf(bitAnd(cityHash64(m.ticker, toString(m.month_start), t.n), 1) = 0) > 0
),
scored AS (
SELECT trial_id,
avg(long_short_ret) / stddevSamp(long_short_ret) * sqrt(12) AS sharpe
FROM factor_month
GROUP BY trial_id
HAVING stddevSamp(long_short_ret) > 0
),
ladder AS (
SELECT arrayJoin([1, 2, 5, 10, 25, 50, 100, 160, 240]) AS n
)
SELECT l.n AS factors_tried,
round(max(s.sharpe), 2) AS best_sharpe,
round(avg(s.sharpe), 2) AS average_sharpe
FROM ladder AS l
CROSS JOIN scored AS s
WHERE s.trial_id <= l.n
GROUP BY factors_tried
ORDER BY factors_triedEin laufendes Maximum kann nur steigen. Genau darin liegt die Falle. Die erste getestete Regel erzielte 0.44. Nach 240 Versuchen lag der beste Wert bei 1.59, während der Durchschnitt aller Versuche 0.01 betrug. Die Schlagzeile wurde besser, ohne dass eine einzige Regel besser wurde. Ein Framework, das zehntausend Ausdrücke bewertet, führt diese Kurve weit über den hier dargestellten Bereich hinaus. Der gemeldete Wert ist ihr höchster Punkt.
Was eine zurückgehaltene Periode mit den Gewinnern macht
Die Standardmaßnahme ist ein Holdout: Man bewertet eine Periode und bewertet die verbleibenden Kandidaten anschließend in einer späteren Periode erneut, die die Suche zuvor nicht gesehen hat. Nehmen Sie die zwölf besten Münzwürfe aus dem Trainingsfenster und wenden Sie dieselben Regeln auf die fünf folgenden Jahre an, von Juli 2021 bis Juni 2026.
Das exakte SQL hinter jeder Zahl
WITH month_end AS (
SELECT ticker,
toStartOfMonth(toDate(toTimeZone(window_start, 'America/New_York'))) AS month_start,
argMax(toFloat64(close), window_start) AS close_px
FROM global_markets.delayed_stocks_minute_aggs
WHERE ticker IN ('AAPL','ADBE','AMZN','BA','CAT','COST','CRM','CSCO','CVX','DE',
'DUK','GE','GOOGL','HD','HON','IBM','INTC','JNJ','JPM','KO',
'LMT','MCD','MMM','MRK','MSFT','NKE','NVDA','ORCL','PEP','PFE',
'PG','QCOM','SO','T','TGT','TXN','UNP','VZ','WMT','XOM')
AND toDate(toTimeZone(window_start, 'America/New_York')) >= toDate('2015-12-01')
AND toDate(toTimeZone(window_start, 'America/New_York')) <= toDate('2026-06-30')
AND toDayOfMonth(toTimeZone(window_start, 'America/New_York')) >= 22
AND (toHour(toTimeZone(window_start, 'America/New_York')) * 60
+ toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959
GROUP BY ticker, month_start
),
lagged AS (
SELECT ticker,
month_start,
close_px,
lagInFrame(close_px) OVER (PARTITION BY ticker ORDER BY month_start
ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) AS prev_px
FROM month_end
),
monthly_return AS (
SELECT ticker, month_start, close_px / prev_px - 1 AS ret
FROM lagged
WHERE prev_px > 0
AND month_start >= toDate('2016-01-01')
),
trial AS (
SELECT arrayJoin(range(1, 241)) AS n
),
factor_month AS (
SELECT t.n AS trial_id,
m.month_start AS month_start,
avgIf(m.ret, bitAnd(cityHash64(m.ticker, toString(m.month_start), t.n), 1) = 1)
- avgIf(m.ret, bitAnd(cityHash64(m.ticker, toString(m.month_start), t.n), 1) = 0) AS long_short_ret
FROM monthly_return AS m
CROSS JOIN trial AS t
GROUP BY trial_id, month_start
HAVING countIf(bitAnd(cityHash64(m.ticker, toString(m.month_start), t.n), 1) = 1) > 0
AND countIf(bitAnd(cityHash64(m.ticker, toString(m.month_start), t.n), 1) = 0) > 0
),
scored AS (
SELECT trial_id,
avgIf(long_short_ret, month_start < toDate('2021-07-01'))
/ stddevSampIf(long_short_ret, month_start < toDate('2021-07-01')) * sqrt(12) AS in_sample_sharpe,
avgIf(long_short_ret, month_start >= toDate('2021-07-01'))
/ stddevSampIf(long_short_ret, month_start >= toDate('2021-07-01')) * sqrt(12) AS out_of_sample_sharpe
FROM factor_month
GROUP BY trial_id
HAVING countIf(month_start < toDate('2021-07-01')) >= 24
AND countIf(month_start >= toDate('2021-07-01')) >= 24
)
SELECT concat('trial ', toString(trial_id)) AS factor_label,
round(in_sample_sharpe, 2) AS in_sample_sharpe,
round(out_of_sample_sharpe, 2) AS out_of_sample_sharpe
FROM scored
ORDER BY in_sample_sharpe DESC
LIMIT 12Jedes Balkenpaar steht für eine Regel. Der linke Balken zeigt den Score, der ihr einen Platz im Bericht verschaffte. Der rechte Balken zeigt denselben Score für die folgenden fünf Jahre. Der bestplatzierte Versuch erzielte im Training 1.59 und anschließend -0.51. Der zwölftplatzierte erzielte 0.74 und danach 0.49.
Zwölf Regeln bilden selbst eine kleine Stichprobe. Sortiert man alle 240 Regeln nach ihrem Trainingsscore in fünf Gruppen und bildet für jede Gruppe den durchschnittlichen Holdout-Score, ergibt sich ein klareres Bild.
Das exakte SQL hinter jeder Zahl
WITH month_end AS (
SELECT ticker,
toStartOfMonth(toDate(toTimeZone(window_start, 'America/New_York'))) AS month_start,
argMax(toFloat64(close), window_start) AS close_px
FROM global_markets.delayed_stocks_minute_aggs
WHERE ticker IN ('AAPL','ADBE','AMZN','BA','CAT','COST','CRM','CSCO','CVX','DE',
'DUK','GE','GOOGL','HD','HON','IBM','INTC','JNJ','JPM','KO',
'LMT','MCD','MMM','MRK','MSFT','NKE','NVDA','ORCL','PEP','PFE',
'PG','QCOM','SO','T','TGT','TXN','UNP','VZ','WMT','XOM')
AND toDate(toTimeZone(window_start, 'America/New_York')) >= toDate('2015-12-01')
AND toDate(toTimeZone(window_start, 'America/New_York')) <= toDate('2026-06-30')
AND toDayOfMonth(toTimeZone(window_start, 'America/New_York')) >= 22
AND (toHour(toTimeZone(window_start, 'America/New_York')) * 60
+ toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959
GROUP BY ticker, month_start
),
lagged AS (
SELECT ticker,
month_start,
close_px,
lagInFrame(close_px) OVER (PARTITION BY ticker ORDER BY month_start
ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) AS prev_px
FROM month_end
),
monthly_return AS (
SELECT ticker, month_start, close_px / prev_px - 1 AS ret
FROM lagged
WHERE prev_px > 0
AND month_start >= toDate('2016-01-01')
),
trial AS (
SELECT arrayJoin(range(1, 241)) AS n
),
factor_month AS (
SELECT t.n AS trial_id,
m.month_start AS month_start,
avgIf(m.ret, bitAnd(cityHash64(m.ticker, toString(m.month_start), t.n), 1) = 1)
- avgIf(m.ret, bitAnd(cityHash64(m.ticker, toString(m.month_start), t.n), 1) = 0) AS long_short_ret
FROM monthly_return AS m
CROSS JOIN trial AS t
GROUP BY trial_id, month_start
HAVING countIf(bitAnd(cityHash64(m.ticker, toString(m.month_start), t.n), 1) = 1) > 0
AND countIf(bitAnd(cityHash64(m.ticker, toString(m.month_start), t.n), 1) = 0) > 0
),
scored AS (
SELECT trial_id,
avgIf(long_short_ret, month_start < toDate('2021-07-01'))
/ stddevSampIf(long_short_ret, month_start < toDate('2021-07-01')) * sqrt(12) AS in_sample_sharpe,
avgIf(long_short_ret, month_start >= toDate('2021-07-01'))
/ stddevSampIf(long_short_ret, month_start >= toDate('2021-07-01')) * sqrt(12) AS out_of_sample_sharpe
FROM factor_month
GROUP BY trial_id
HAVING countIf(month_start < toDate('2021-07-01')) >= 24
AND countIf(month_start >= toDate('2021-07-01')) >= 24
),
ranked AS (
SELECT trial_id,
in_sample_sharpe,
out_of_sample_sharpe,
row_number() OVER (ORDER BY in_sample_sharpe DESC) AS in_sample_rank
FROM scored
)
SELECT multiIf(in_sample_rank <= 48, 'best fifth in training',
in_sample_rank <= 96, 'second fifth',
in_sample_rank <= 144, 'middle fifth',
in_sample_rank <= 192, 'fourth fifth',
'worst fifth in training') AS training_group,
round(avg(in_sample_sharpe), 2) AS avg_in_sample_sharpe,
round(avg(out_of_sample_sharpe), 2) AS avg_out_of_sample_sharpe
FROM ranked
GROUP BY training_group
ORDER BY min(in_sample_rank)Im Training reichen die Gruppen von 0.66 an der Spitze bis -0.63 am unteren Ende. Das ergibt eine breite und vollkommen geordnete Rangfolge. Sie ist garantiert, weil die Gruppen genau nach diesem Score gebildet wurden. Im Holdout liegen die Durchschnittswerte derselben beiden Enden bei 0.01 und 0.13. Die Rangfolge flacht ab. Ein Holdout ist der einzige Teil der Pipeline, der nicht optimiert wurde. Genau das macht ihn wertvoll und erfordert einen sorgfältigen Umgang damit.
Maßnahmen, die tatsächlich funktionieren
Ein Holdout, den Sie nur einmal verwenden. Jede Betrachtung macht ihn zu Trainingsdaten. Walk-forward-Tests, bei denen sich das Fenster verschiebt und jeder Score aus Daten nach dem Fit stammt, sind die Variante, die bei wiederholter Nutzung Bestand hat.
Eine Anpassung für multiples Testen. Die 2014 von Bailey und López de Prado eingeführte deflationierte Sharpe Ratio reduziert eine beobachtete Sharpe Ratio abhängig von der Zahl der durchgeführten Versuche, der Länge der Stichprobe, der Schiefe der Renditen und der Stärke ihrer Extremwerte. Gibt man eine ehrliche Versuchszahl ein, schrumpft eine hohe Sharpe Ratio aus einer Suche über zehntausend Ausdrücke oft auf null.
Ein Prüfpfad für jeden getesteten Ausdruck, einschließlich der verworfenen. Das ist der entscheidende Punkt. Deshalb ist „auditierbar“ das interessante Wort in der Beschreibung eines Faktorforschungsprojekts. Die Deflation benötigt eine Versuchszahl. Eine Pipeline, die nur Gewinner protokolliert, hat die Grundlage ihrer eigenen Korrektur zerstört. Verworfenes Rohmaterial, abgebrochene Parametersuchen, die Neustarts des Researchers und jede frühere Version des Scoring-Codes zählen zu dieser Zahl.
Prüfungen auf Kosten und Look-ahead-Bias, bevor der Score als belastbar gilt. Ein Faktor, der nach einem Fundamentaldatenfeld sortiert wird, das mit dem Datum des Datenimports durch den Anbieter versehen ist statt mit dem Datum, an dem der Markt diese Information sehen konnte, sieht im Backtest hervorragend aus und handelt schlecht.
Das Wort „auditierbar“ richtig einordnen
Neue Repositories in diesem Bereich erscheinen fast jede Woche. Ein Projekt mit einigen Dutzend Sternen ist eher ein Prototyp als ein Leistungsnachweis. Die Zahl der Sterne entwickelt sich außerdem schneller als der Code. Deshalb bewertet diese Seite das Muster und nicht ein bestimmtes Projekt. Das sollten Sie zuerst prüfen, unabhängig davon, welches Projekt vor Ihnen liegt.
- Protokolliert es jeden Kandidaten mit seinem Ausdruck und Score einschließlich Zeitstempel oder nur die behaltenen Kandidaten?
- Wird der Holdout durch das Framework erzwungen oder durch die Selbstdisziplin des Researchers?
- Wird neben jedem gemeldeten Score eine Versuchszahl angegeben?
- Für welchen Markt wurde es entwickelt? Eine auf chinesische A-Aktien abgestimmte Bibliothek übernimmt tägliche Kurslimits sowie ein Verbot, am selben Handelstag gekaufte Aktien zu verkaufen. Das Verhalten eines Faktors unter diesen Regeln lässt sich nicht auf US-Aktien übertragen.
- Können Sie das Projekt erneut ausführen und die Zahlen reproduzieren? Fixieren Sie den exakten Commit, den Sie gelesen haben. Ein Projekt in diesem Entwicklungsstadium schreibt seinen Scoring-Code zwischen zwei Wochenenden möglicherweise um.
Nichts davon macht ein LLM in der Faktorforschung nutzlos. Das Erzeugen von Hypothesen ist ein echter Engpass, und Modelle sind darin gut. Die Verantwortung verschiebt sich lediglich: Entscheidend wird die Dokumentation darüber, wie viele Hypothesen verbraucht wurden. Bevor irgendeine davon auf ein Live-Orderbuch trifft, zeigt Paper-Trading, wie groß der Abstand zwischen einem Backtest und einer tatsächlichen Ausführung ist.
FAQ zu Alpha-Faktoren mit LLMs
Kann ein LLM Alpha-Faktoren finden?
Es kann tausende vorschlagen. Ein Vorschlag ist jedoch noch keine Entdeckung. Die Behauptung entsteht beim Scoring. Ein Score aus einer umfangreichen Suche enthält ein Selektionsproblem, das der Score selbst nicht erkennen kann. Bewerten Sie die Holdout-Disziplin und die dokumentierte Versuchszahl höher als den Ausdruck.
Was ist die deflationierte Sharpe Ratio?
Eine Korrektur, die eine beobachtete Sharpe Ratio in die Wahrscheinlichkeit übersetzt, dass eine Suche dieser Größe sie auch ohne einen tatsächlichen Vorteil hervorgebracht hätte. Bailey und López de Prado veröffentlichten sie 2014. Ihr zentraler Input ist die Zahl der Versuche. Genau diese Zahl kann eine nicht auditierte Forschungsschleife nicht liefern.
Wie viele Backtests sind zu viele?
Es gibt keinen Schwellenwert. Es gibt nur eine Anpassung, die angewendet werden muss. Ein Backtest mit einer Sharpe Ratio von 1,0 und zehntausend Backtests, deren bester Score 1,0 beträgt, sind unterschiedliche Aussagen über die Welt. Die Münzwürfe oben erreichten bei 240 Versuchen einen Wert von 1.59, obwohl die Daten keinerlei Information enthielten.
Warum schwächen sich veröffentlichte Faktoren nach ihrer Veröffentlichung ab?
Wissenschaftliche Arbeiten haben den Rückgang veröffentlichter Anomalien in den Jahren nach ihrer Veröffentlichung dokumentiert. Eine mögliche Erklärung ist die Überfüllung eines Trades. Eine andere ist die Überanpassung eines ursprünglichen Ergebnisses an seine eigene Stichprobe. Beide Mechanismen erzeugen auf einem Diagramm dasselbe Muster. Die Hypothese effizienter Märkte beschreibt den ersten Mechanismus. Die oben dargestellten Versuche zeigen den zweiten.
Jedes Diagramm hier ist eine gespeicherte Abfrage über echte Monatsendkurse. Der zugrunde liegende SQL-Code ist offen einsehbar. Kopieren Sie eine Abfrage, erhöhen Sie die Versuchszahl und beobachten Sie, wie der beste Wert auf dem Strasmore-Terminal steigt.