क्या LLM अल्फा फैक्टर खोज सकता है?
LLM एक घंटे में सौ alpha factors लिख सकता है। जानिए, दस वर्षों के वास्तविक दामों पर 240 coin flip factors का प्रदर्शन कैसा रहा और सफल factors की जांच कैसे करें।
एक LLM पूरे दिन alpha factors प्रस्तावित कर सकता है। किसी सक्षम मॉडल को data dictionary और scoring harness दीजिए, और वह दोपहर के भोजन से पहले सौ संभावित factor expressions लिख देगा। कठिन प्रश्न इसके नीचे है: जब उन्हें खोजने वाली प्रक्रिया noise से winners बनाने वाली मशीन हो, तो कोई कभी कैसे जान पाएगा कि उनमें से कोई वास्तविक है?
alpha factor क्या है?
Factor एक ऐसा नियम है जो हर तारीख को हर stock के लिए market data को एक संख्या में बदलता है। बारह महीने का price change एक factor है। Debt-to-equity ratio भी factor है। जब आप किसी universe को इसके आधार पर rank करते हैं, शीर्ष हिस्से को खरीदते हैं, निचले हिस्से को बेचते हैं और तय schedule पर rebalance करते हैं, तब factor strategy बन जाता है। Alpha वह return है जो सामान्य market exposure से मिलने वाले return को घटाने के बाद बचता है।
Candidates को Sharpe ratio से score किया जाता है: average return को उस return के standard deviation से भाग देकर और वर्ष के आधार पर scale करके। यह उतार-चढ़ाव की प्रत्येक इकाई के बदले मिलने वाला return है। Live strategy में दीर्घकालिक Sharpe लगभग 1 सम्मानजनक माना जाता है। अगली बार कोई backtest 3 बताए, तो यह बात ध्यान में रखें।
LLM factor research वास्तव में कैसे काम करती है
इस क्षेत्र की हर project लगभग इसी loop का एक रूप चलाती है।
- मॉडल factor expressions को एक छोटी language में लिखता है, जिसे harness evaluate कर सकता है।
- Backtester prices और fundamentals के एक निश्चित history पर हर expression को score करता है।
- Score threshold से ऊपर वाले expressions रखे जाते हैं। बाकी हटा दिए जाते हैं।
- रखे गए expressions worked examples के रूप में मॉडल के context में लौटते हैं और loop फिर चलता है।
Multi-agent trading systems इन कामों को अलग-अलग roles में बाँटते हैं। एक role प्रस्ताव देता है और दूसरा testing करता है। यह plumbing वास्तव में उपयोगी है। market data skills an AI agent needs वही skills हैं जिनकी किसी व्यक्ति को भी आवश्यकता होती है।
इस loop में कुछ भी बेईमानी वाला नहीं है। Research इसी तरह search के माध्यम से की जाती है। समस्या arithmetic है। जैसे ही step 2 कुछ ही बार से अधिक चलता है, यह समस्या सामने आ जाती है।
LLM alpha factor search winners क्यों बनाती है
एक price history। हजारों सस्ते hypotheses। हर hypothesis को उसी सीमित sample पर score किया जाता है, और उस sample में काफी luck होता है। पर्याप्त rules का परीक्षण करें, तो कुछ rules उस luck से बहुत closely fit हो जाएंगे। Score यह नहीं बता सकता कि आपको किस प्रकार का fit मिला है। Noise से मेल खाने वाला rule और market से मेल खाने वाला rule एक ही संख्या दिखा सकते हैं।
यहाँ null hypothesis को 240 बार draw किया गया है। नीचे दिया गया प्रत्येक “factor” coin flip है। Ticker, month और trial number का hash हर महीने 40 बड़े US names को दो हिस्सों में बाँटता है। Strategy एक हिस्से को long और दूसरे को short रखती है। इसमें construction के अनुसार कोई information नहीं है। January 2016 से June 2021 तक के वास्तविक month-end returns पर score करने पर 240 trials इस प्रकार आते हैं।
हर आंकड़े के पीछे का पूरा SQL
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)यही spread मुख्य बात है। उस chart में कुछ भी किसी चीज की भविष्यवाणी नहीं करता। फिर भी 1 trials top band (1.2 and above) में पहुँचे, search के 0.4%, और 1 bottom band (below -1.2) में पहुँचे। कोई researcher एक lucky trial चलाकर रुक जाए, तो उसके पास chart और Sharpe ratio होंगे। लेकिन यह जानने का कोई तरीका नहीं होगा कि वे discovery हैं या नहीं। यहाँ returns month-end close से अगले month-end close तक के हैं। monthly returns कैसे मापे जाते हैं में यह arithmetic समझाया गया है।
महत्वपूर्ण संख्या यह है कि आपने कितने trials आजमाए
अकेले report किया गया backtest अपना denominator छिपा देता है। उन्हीं 240 trials को एक लगातार बढ़ती search की तरह पढ़िए। हर step पर board पर मौजूद best score को अब तक आजमाई गई सभी चीजों के average के साथ दिखाया गया है।
हर आंकड़े के पीछे का पूरा SQL
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_triedRunning maximum केवल बढ़ सकता है। यही trap है। पहले tested rule का score 0.44 था। 240 trials के बाद board पर best score 1.59 दिखता है, जबकि सभी trials का average 0.01 है। किसी एक rule में सुधार हुए बिना headline बेहतर हो गई। जो harness दस हजार expressions evaluate करता है, वह इस curve को यहाँ दिखाए गए दायरे से बहुत आगे तक चलाता है। वह जिस संख्या की report करता है, वह इसी curve का शीर्ष है।
Held-out period winners के साथ क्या करता है
सामान्य defense holdout है। एक period पर score करें और फिर survivors को उस बाद के period पर दोबारा score करें, जिसे search ने पहले नहीं देखा था। Training window के बारह सबसे अच्छे coin flips लें और July 2021 से June 2026 तक के अगले पाँच वर्षों में उन्हीं rules को चलाएँ।
हर आंकड़े के पीछे का पूरा SQL
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 12हर bar pair एक rule है। बायाँ bar report में स्थान दिलाने वाला score है। दायाँ bar अगले पाँच वर्षों में उसी rule का score है। Top-ranked trial ने training में 1.59 score किया और बाद में -0.51। Twelfth trial ने 0.74 score किया और फिर 0.49।
बारह rules स्वयं एक छोटा sample हैं। सभी 240 को training score के आधार पर fifths में बाँटकर प्रत्येक fifth के holdout score का average लेने से अधिक स्पष्ट view मिलता है।
हर आंकड़े के पीछे का पूरा SQL
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)Training में groups ऊपर 0.66 से नीचे -0.63 तक जाते हैं। यह चौड़ी और पूरी तरह क्रमबद्ध ladder है, क्योंकि groups उसी score के आधार पर बनाए गए थे। Holdout में इन्हीं दोनों सिरों का average 0.01 और 0.13 है। Ladder flat हो जाती है। Holdout pipeline का एकमात्र ऐसा हिस्सा है जिसे optimize नहीं किया गया है। इसी कारण इसे सावधानी से खर्च करना चाहिए।
वे defenses जो वास्तव में काम करते हैं
ऐसा holdout जिसे केवल एक बार खर्च किया जाए। हर बार उसे देखने से वह training data में बदल जाता है। Walk-forward testing इसका वह रूप है जो repeated use के बाद भी टिकता है। इसमें window आगे बढ़ती है और प्रत्येक score fit के बाद के data से आता है।
Multiple-testing adjustment। Bailey और López de Prado ने 2014 में deflated Sharpe ratio प्रस्तुत किया था। यह observed Sharpe को इस आधार पर discount करता है कि कितने trials चलाए गए, sample कितना लंबा है, returns कितने skewed हैं और उनकी tails कितनी fat हैं। ईमानदार trial count देने पर दस हजार expressions वाली search का headline Sharpe अक्सर शून्य के करीब deflate हो जाता है।
आजमाए गए प्रत्येक expression का audit trail, जिसमें हटाए गए expressions भी शामिल हों। यह सबसे महत्वपूर्ण हिस्सा है। इसी कारण factor-research project के वर्णन में “auditable” सबसे महत्वपूर्ण शब्द है। Deflation के लिए trial count चाहिए। केवल winners को log करने वाली pipeline अपने correction के input को ही नष्ट कर देती है। Discarded drafts, छोड़े गए parameter sweeps, researcher द्वारा की गई अपनी restarts और scoring code का हर पुराना version, सभी उस संख्या में गिने जाते हैं।
Score पर भरोसा करने से पहले cost और look-ahead bias checks। यदि factor को fundamentals field की उस तारीख पर rank किया गया है जिस दिन vendor ने उसे load किया था, न कि उस तारीख पर जब market उसे देख सकता था, तो backtest सुंदर दिखेगा और trading खराब होगी।
“auditable” शब्द को कैसे पढ़ें
इस क्षेत्र में नए repositories लगभग हर सप्ताह दिखाई देते हैं। कुछ दर्जन stars वाला project track record नहीं, बल्कि prototype होता है। Star counts भी code से तेज़ी से बदलते हैं। इसलिए यह page किसी एक project के बजाय pattern को score करता है। जो project आपके सामने आए, उसमें पहले ये चीजें देखें।
- क्या वह हर candidate को उसके expression और score के साथ timestamp सहित log करता है, या केवल रखे गए candidates को?
- क्या holdout harness द्वारा enforce किया जाता है, या researcher की self-discipline पर निर्भर है?
- क्या प्रत्येक reported score के साथ trial count दिया गया है?
- इसे किस market के लिए बनाया गया था? China A-shares के लिए tuned library daily price limits और उसी session में खरीदे गए shares को बेचने पर लगे restriction को inherit करती है। उन rules के तहत factor का behavior US equities पर लागू नहीं होता।
- क्या आप इसे फिर से चला सकते हैं और numbers को reproduce कर सकते हैं? जिस exact commit को आपने पढ़ा है, उसे pin करें। इस stage का project weekends के बीच अपना scoring code बदल सकता है।
इनमें से कोई भी बात LLM को factor research में बेकार नहीं बनाती। Hypotheses generate करना वास्तविक bottleneck है और models इसमें अच्छे हैं। बदलता केवल यह है कि जिम्मेदारी कहाँ आती है: कितनी hypotheses आजमाई गईं, इसका accounting रखना आवश्यक हो जाता है। इससे पहले कि इनमें से कोई live order book तक पहुँचे, paper trading वह जगह है जहाँ backtest और वास्तविक fill के बीच का अंतर दिखाई देता है।
LLM alpha factor FAQ
क्या LLM alpha factors खोज सकता है?
यह हजारों factors प्रस्तावित कर सकता है, लेकिन proposal finding नहीं है। दावा scoring step पर किया जाता है। व्यापक search से निकला score selection problem साथ लाता है, जिसे score स्वयं नहीं देख सकता। Expression से पहले holdout discipline और recorded trial count को परखें।
Deflated Sharpe ratio क्या है?
यह ऐसा correction है जो observed Sharpe ratio को इस संभावना में बदलता है कि इतनी बड़ी search ने वास्तविक edge के बिना भी यह परिणाम दे दिया होता। Bailey और López de Prado ने इसे 2014 में प्रकाशित किया था। इसका मुख्य input trials की संख्या है। यही वह संख्या है जिसे unaudited research loop उपलब्ध नहीं करा सकता।
कितने backtests बहुत अधिक हैं?
कोई निश्चित threshold नहीं है। केवल ऐसा adjustment है जिसे लागू करना आवश्यक है। 1.0 score वाला एक backtest और 1.0 का best score देने वाले दस हजार backtests, दुनिया के बारे में अलग-अलग claims हैं। ऊपर दिए गए coin flips ने data में किसी information के बिना 1.59 trials में 240 तक पहुँचा।
Publication के बाद published factors कमजोर क्यों पड़ते हैं?
Academic research ने publication के बाद के वर्षों में published anomalies के decay को track किया है। Crowding इसका एक संभावित mechanism है। अपने sample पर overfit हुआ original result दूसरा mechanism है। दोनों chart पर एक ही shape बना सकते हैं। efficient market hypothesis पहले mechanism को समझाती है। ऊपर दिए गए trials दूसरे को प्रदर्शित करते हैं।
यहाँ प्रत्येक panel वास्तविक month-end prices पर stored query है और उसके नीचे SQL खुला हुआ है। किसी एक को copy करें, trial count बढ़ाएँ और Strasmore terminal पर best number को बढ़ते हुए देखें।