Strasmore Research
Deep Dives · Matt ConnorBy Matt Connor ·

کیا LLM Alpha Factors تلاش کر سکتا ہے؟

LLM ایک گھنٹے میں سو alpha factors لکھ سکتا ہے۔ جانیں کہ دس سال کی حقیقی قیمتوں پر coin flip کے 240 factors نے کیا score کیا، اور کامیاب عوامل کو کیسے test کریں۔

ایک LLM سارا دن alpha factors تجویز کر سکتا ہے۔ کسی قابل ماڈل کو data dictionary اور scoring harness دیں، اور دوپہر کے کھانے سے پہلے وہ سو قابلِ قبول factor expressions لکھ دے گا۔ اصل مشکل اس کے نیچے ہے: آخر کسی کو کیسے معلوم ہوگا کہ ان میں سے کوئی واقعی مؤثر ہے، جب اسے تلاش کرنے والا عمل شور سے کامیاب نتائج تیار کرنے کی مشین بن چکا ہو؟

alpha factor کیا ہے؟

Factor ایک ایسا اصول ہے جو market data کو ہر تاریخ پر ہر stock کے لیے ایک عدد میں بدل دیتا ہے۔ بارہ ماہ کی price change ایک factor ہے۔ debt-to-equity ratio بھی factor ہے۔ Factor اس وقت strategy بنتا ہے جب آپ اس کی بنیاد پر ایک universe کی ranking کریں، اوپر والے حصے کو خریدیں، نچلے حصے کو فروخت کریں، اور مقررہ schedule کے مطابق portfolio کو rebalance کریں۔ Alpha وہ return ہے جو عام market exposure سے ملنے والے return کو منہا کرنے کے بعد باقی رہتا ہے۔

Candidates کو Sharpe ratio سے score کیا جاتا ہے: average return کو اس return کے standard deviation سے تقسیم کرکے سالانہ بنیاد پر scale کیا جاتا ہے۔ یہ اتار چڑھاؤ کی فی unit return ہے۔ Live strategy میں طویل مدت کا Sharpe تقریباً 1 ہو تو اسے قابلِ احترام سمجھا جاتا ہے۔ اگلی بار جب کوئی backtest 3 کا Sharpe بتائے تو یہ بات ذہن میں رکھیں۔

LLM factor research حقیقت میں کیسے کام کرتی ہے

اس شعبے کا ہر project اسی loop کی کسی نہ کسی شکل میں چلتا ہے۔

  1. Model ایک چھوٹی زبان میں factor expressions لکھتا ہے، جسے harness evaluate کر سکتا ہے۔
  2. Backtester قیمتوں اور fundamentals کی مقررہ تاریخ پر ہر expression کو score کرتا ہے۔
  3. مقررہ score threshold سے اوپر والے expressions رکھے جاتے ہیں۔ باقی خارج کر دیے جاتے ہیں۔
  4. رکھے گئے expressions worked examples کے طور پر model کے context میں واپس آتے ہیں، اور loop دوبارہ چلتا ہے۔

کثیر ایجنٹ trading systems ان کاموں کو الگ الگ roles میں تقسیم کرتے ہیں؛ ایک تجویز دیتا ہے اور دوسرا testing کرتا ہے۔ یہ infrastructure واقعی مفید ہے، اور وہ market data skills جن کی AI agent کو ضرورت ہوتی ہے وہی skills ہیں جن کی ایک انسان کو ضرورت ہوتی ہے۔

اس loop میں بذاتِ خود کوئی بددیانتی نہیں۔ Research اسی طرح کی جاتی ہے۔ مسئلہ arithmetic ہے، اور step 2 چند مرتبہ سے زیادہ چلتے ہی سامنے آ جاتا ہے۔

ایک price history۔ ہزاروں سستے hypotheses۔ ہر hypothesis کو اسی محدود sample کے خلاف score کیا جاتا ہے، اور اس sample میں اتفاق کا بڑا کردار ہوتا ہے۔ کافی rules test کریں تو کچھ rules اس اتفاق کے ساتھ بہت قریب سے fit ہو جائیں گے۔ Score یہ نہیں بتا سکتا کہ fit کس نوعیت کا ہے، کیونکہ noise سے ملنے والا rule اور market سے ملنے والا rule ایک ہی number ظاہر کرتے ہیں۔

یہ null hypothesis ہے، جسے 240 مرتبہ کھینچا گیا ہے۔ نیچے ہر "factor" ایک coin flip ہے: ticker، month اور trial number کا hash ہر ماہ 40 بڑے US names کو دو حصوں میں تقسیم کرتا ہے، اور strategy ایک حصے میں long اور دوسرے میں short رہتی ہے۔ اس میں، تعریف کے مطابق، کوئی information نہیں ہے۔ January 2016 سے June 2021 تک کے حقیقی month-end returns پر score کرنے کے بعد 240 trials کی صورتِ حال یہ ہے۔

استفسار کریںحقیقی قیمتوں پر 240 coin flip factors کی اسکورنگ: annualized Sharpe، جنوری 2016 تا جون 2021
ہر عدد کے پیچھے موجود درست 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)
Run this yourself

اصل نکتہ spread ہے۔ اس chart میں کوئی چیز کسی پیش گوئی کی صلاحیت نہیں رکھتی، پھر بھی 1 trials top band (1.2 and above) میں پہنچے، جو search کا 0.4% ہے، جبکہ 1 bottom band (below -1.2) میں رہے۔ کوئی researcher ایک خوش قسمت trial چلا کر رک جائے تو اس کے پاس chart اور Sharpe ratio تو ہوگا، مگر یہ جاننے کا کوئی طریقہ نہیں ہوگا کہ یہ discovery ہے یا نہیں۔ یہاں returns month-end سے اگلے month-end close تک کے ہیں؛ ماہانہ returns کیسے ناپے جاتے ہیں میں اس arithmetic کی وضاحت ہے۔

اہم عدد یہ ہے کہ آپ نے کتنے trials آزمائے

اکیلا رپورٹ کیا گیا backtest اپنا denominator نہیں رکھتا۔ انہی 240 trials کو ایک مسلسل پھیلتی ہوئی search کے طور پر دیکھیں: ہر مرحلے پر board کے بہترین score کو اب تک آزمائے گئے تمام scores کی average کے ساتھ دکھایا جائے۔

استفسار کریںsearch کے حجم کے ساتھ بہترین اسکور میں اضافہ: run کیے گئے trials کے لحاظ سے best اور average Sharpe
ہر عدد کے پیچھے موجود درست 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_tried
Run this yourself

Running maximum صرف بڑھ سکتا ہے، اور یہی trap ہے۔ پہلے test کیے گئے rule کا score 0.44 تھا۔ 240 trials کے بعد board پر بہترین score 1.59 ہے، جبکہ ان سب کی average 0.01 ہے۔ Headline بہتر ہوئی، مگر کسی ایک rule میں بہتری نہیں آئی۔ جو harness دس ہزار expressions کو evaluate کرتا ہے، وہ اس curve کو یہاں دکھائی گئی حد سے بہت آگے تک لے جا رہا ہے، اور جو number وہ report کرتا ہے وہ اسی curve کا بلند ترین نقطہ ہے۔

Held-out period winners کے ساتھ کیا کرتا ہے

معیاری دفاع holdout ہے: ایک period پر score کریں، پھر survivors کو بعد کے ایسے period میں دوبارہ score کریں جسے search نے پہلے نہیں دیکھا تھا۔ Training window کے بارہ بہترین coin flips لیں اور انہی rules کو اس کے بعد کے پانچ سال، یعنی July 2021 سے June 2026 تک، چلائیں۔

استفسار کریںtraining میں بہترین بارہ trials، پانچ غیر استعمال شدہ برسوں پر دوبارہ اسکور کیے گئے (جولائی 2021 تا جون 2026)
ہر عدد کے پیچھے موجود درست 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
Run this yourself

ہر bar pair ایک rule ہے۔ بائیں bar وہ score ہے جس کی بنیاد پر وہ report میں شامل ہوا۔ دائیں bar اگلے پانچ سال میں اسی rule کا score ہے۔ سب سے اوپر ranked trial نے training میں 1.59 score کیا اور بعد میں -0.51؛ بارہویں trial نے 0.74 score کیا اور پھر 0.49۔

بارہ rules بھی اپنا ایک چھوٹا sample ہیں۔ Training score کے مطابق تمام 240 کو پانچویں حصوں میں تقسیم کریں، پھر ہر حصے کے holdout score کی average نکالیں۔ اس سے زیادہ واضح تصویر ملتی ہے۔

استفسار کریںtraining rank بمقابلہ holdout result: 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('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)
Run this yourself

Training میں groups اوپر سے 0.66 سے نیچے -0.63 تک جاتے ہیں۔ یہ ایک وسیع اور بالکل منظم ladder ہے، جو لازمی ہے کیونکہ groups اسی score کی بنیاد پر بنائے گئے تھے۔ Holdout میں انہی دونوں سروں کی average بالترتیب 0.01 اور 0.13 ہے۔ Ladder ہموار ہو جاتی ہے۔ Holdout pipeline کا واحد حصہ ہے جس کے خلاف optimization نہیں کی گئی، اسی لیے اسے احتیاط سے استعمال کرنا ضروری ہے۔

وہ دفاع جو واقعی کام کرتے ہیں

Holdout جسے صرف ایک مرتبہ استعمال کیا جائے۔ ہر بار اسے دیکھنے سے وہ training data میں بدل جاتا ہے۔ Walk-forward testing، جس میں window آگے بڑھتی ہے اور ہر score fit کے بعد کے data سے آتا ہے، repeated use کے باوجود قابلِ عمل رہنے والا طریقہ ہے۔

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 اکثر تقریباً ختم ہو جاتا ہے۔

ہر آزمائے گئے expression کا audit trail، ان expressions سمیت جو خارج کر دیے گئے۔ یہ بنیادی حصہ ہے، اور اسی لیے factor-research project کی description میں "auditable" اہم لفظ ہے۔ Deflation کے لیے trial count درکار ہوتا ہے۔ جو pipeline صرف winners کا log رکھتی ہے، وہ اپنی correction کے لیے درکار input ختم کر چکی ہوتی ہے۔ Discarded drafts، abandoned parameter sweeps، researcher کے اپنے restarts، اور scoring code کا ہر سابقہ version، سب اس number میں شامل ہوتے ہیں۔

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 کرتا ہے، یا صرف keepers کو؟
  • کیا holdout harness کے ذریعے نافذ ہے، یا researcher کی self-discipline پر منحصر ہے؟
  • کیا ہر reported score کے ساتھ trial count بھی دیا گیا ہے؟
  • یہ کس market کے لیے بنایا گیا تھا؟ China A-shares کے لیے tuned library روزانہ کی price limits اور اسی session میں خریدے گئے shares فروخت کرنے کی پابندی کو inherit کرتی ہے، اور ان rules کے تحت factor کا behavior US equities میں منتقل نہیں ہوتا۔
  • کیا آپ اسے دوبارہ چلا کر numbers reproduce کر سکتے ہیں؟ جو exact commit آپ پڑھ رہے ہیں اسے pin کریں، کیونکہ اس مرحلے کا project ہفتہ ختم ہونے اور اگلے weekend کے درمیان اپنا scoring code بدل سکتا ہے۔

اس میں سے کوئی بات LLM کو factor research میں بے فائدہ نہیں بناتی۔ Hypotheses پیدا کرنا ایک حقیقی bottleneck ہے، اور models اس کام میں اچھے ہیں۔ فرق یہ ہے کہ بوجھ کہاں منتقل ہوتا ہے: کتنے hypotheses استعمال ہو چکے ہیں، اس کا حساب رکھنے پر۔ 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 کو اس probability میں بدلتی ہے کہ اتنی بڑی search نے حقیقی edge کے بغیر بھی یہ result پیدا کیا ہوتا۔ Bailey اور López de Prado نے اسے 2014 میں شائع کیا تھا۔ اس کا مرکزی input trials کی تعداد ہے، اور یہی وہ number ہے جو unaudited research loop فراہم نہیں کر سکتا۔

کتنے backtests بہت زیادہ ہیں؟

کوئی مقررہ threshold نہیں؛ صرف ایک adjustment ہے جسے لاگو کرنا ضروری ہے۔ 1.0 score دینے والا ایک backtest اور ایسے دس ہزار backtests جن میں بہترین score 1.0 ہو، دنیا کے بارے میں مختلف دعوے ہیں۔ اوپر دیے گئے coin flips نے 1.59 trials میں 240 تک پہنچا، حالانکہ data میں کوئی information نہیں تھی۔

Publication کے بعد published factors کمزور کیوں پڑتے ہیں؟

Academic research نے publication کے بعد کے برسوں میں published anomalies کے decay کو track کیا ہے۔ Crowding اس کی ایک پیش کردہ وجہ ہے، جبکہ اپنے ہی sample پر overfit کیا گیا original result دوسری وجہ ہے؛ دونوں chart پر ایک جیسی شکل بناتے ہیں۔ efficient market hypothesis پہلی وجہ کو framework فراہم کرتی ہے، اور اوپر کے trials دوسری وجہ کا عملی مظاہرہ کرتے ہیں۔


یہاں ہر panel حقیقی month-end prices پر مبنی stored query ہے، اور اس کے نیچے SQL کھلا ہوا ہے۔ کسی query کو copy کریں، trial count بڑھائیں، اور دیکھیں کہ Strasmore terminal پر بہترین number کیسے اوپر جاتا ہے۔

#llm#factor research#overfitting#multiple testing#quant