Strasmore Research
تفصیلی تجزیہ Matt Connorاز Matt Connor · اپ ڈیٹ شدہ 2026-08-08

Python میں Reproducible Backtest، API Key کے بغیر

Python میں reproducible backtest چلائیں: version 0.12.4 پر pinned install، deterministic sample data اور zero credentials کے ساتھ، پھر جانیں ایک equity curve کیا چھپاتی ہے۔

Python میں reproducible backtest وہ ہوتا ہے جسے کوئی اجنبی clean machine پر دوبارہ چلا سکے اور عین وہی numbers حاصل کر سکے، جبکہ راستے میں کسی account یا API key کی ضرورت نہ ہو۔ زیادہ تر tutorials پہلے ہی جملے میں اس معیار پر پورے نہیں اترتے، کیونکہ live download اگلے قاری کو price history کا قدرے مختلف ریکارڈ فراہم کرتا ہے جو مصنف کو ملا تھا۔ یہ walkthrough ایک open-source engine کو version 0.12.4 پر pin کرتا ہے، quantjourney-bt کے ساتھ اس کی bundled example کو zero credentials کے ذریعے چلاتا ہے، اور پھر real market data استعمال کر کے دکھاتا ہے کہ ایک single clean run اب بھی آپ کو کیا نہیں بتا سکتا۔

Reproducible backtest کی بنیادی خصوصیت کیا ہے؟

یہاں reproducibility کا مفہوم محدود اور قابلِ جانچ ہے: دوسرا شخص ایک صاف machine پر ایک command چلا کر آپ کے نتائج decimal تک دوبارہ حاصل کر سکے۔ دو عام عوامل اس عمل کو ناکام بنا دیتے ہیں۔

پہلا عامل code ہے۔ version 0.x والی library معمولی releases کے درمیان compatibility کی کوئی ضمانت نہیں دیتی۔ کسی نام کی تبدیلی، default setting میں ردوبدل یا column کی ترتیب بدلنے سے script چلتا رہ سکتا ہے، لیکن خاموشی سے مختلف نتیجہ رپورٹ کر سکتا ہے۔

دوسرا عامل data ہے۔ جس tutorial کی پہلی سطر live download ہو، وہ حقیقتاً reproducible نہیں ہوتا۔ Vendors تاریخی data میں نظرثانی کرتے ہیں، stock splits کے مطابق adjustment کرتے ہیں اور خالی حصے بعد میں پُر کرتے ہیں۔ اس لیے ایک ہی script ایک ماہ بعد مختلف numbers دے سکتی ہے۔ بعد میں یہ الگ کرنا ممکن نہیں رہتا کہ تبدیلی code میں ہوئی یا data میں۔

اس project سے اپنانے کے قابل طریقہ یہ ہے کہ fixed engine version کو ایک چھوٹے bundled dataset کے ساتھ جوڑا جائے، جو خود package کے اندر شامل ہو۔

تنصیب کا ورژن مقرر کریں: pip install quantjourney-bt==0.12.4

quantjourney-bt QuantJourney backtester ہے، جو Apache License 2.0 کے تحت جاری کیا گیا ہے اور اس کے لیے Python 3.11 یا اس کے بعد کا ورژن درکار ہے۔ Version 0.12.4، 21 July 2026 کو شائع ہوا تھا، اور August 2026 میں دستاویزی شکل دیے جانے کے مطابق، ذیل کی ہر command اسی version کے لیے ہے۔

Isolated environment میں کام کریں۔ python3 -m venv .venv اسے بناتا ہے، source .venv/bin/activate اس میں داخل ہوتا ہے، اور python -m pip install -U pip اسی environment کے اندر installer کو update کرتا ہے۔ اس کے بعد عین مطلوبہ version انسٹال کریں: pip install quantjourney-bt==0.12.4۔

Project میں unpinned form، pip install quantjourney-bt، درج ہے۔ ==0.12.4 والا حصہ آپ کی ذمہ داری ہے، اور 0.x version میں اس کی اہمیت خاصی زیادہ ہے۔ Pin کو ایسی جگہ لکھ کر محفوظ کریں جہاں اگلا صارف اسے آسانی سے تلاش کر سکے: pip freeze > requirements.txt ہر resolved dependency کو capture کرتا ہے، ان dependencies کو بھی جنہیں آپ نے خود نامزد نہیں کیا۔

دو optional extras دستیاب ہیں۔ pip install "quantjourney-bt[wf]" walk-forward اور optimization examples کے لیے Optuna شامل کرتا ہے۔ pip install "quantjourney-bt[data]" benchmarks کے لیے استعمال ہونے والا yfinance fallback شامل کرتا ہے۔

Apache-2.0 ایک permissive licence ہے۔ آپ code کو تجارتی طور پر استعمال اور modify کر سکتے ہیں۔ کسی بھی redistribution کے ساتھ licence اور notice files برقرار رکھنا لازم ہے، جبکہ contributors واضح طور پر patent rights بھی grant کرتے ہیں۔

بغیر API key کے bundled SMA example کیسے چلائیں

Repository میں launcher script کے ساتھ پچاس قابلِ اجرا example strategies شامل ہیں۔ یہ strategies weight-based path اور order-based path میں تقسیم ہیں۔ ان میں five walk-forward workflows بھی شامل ہیں۔ ./strategy.sh --list catalog دکھاتا ہے۔ ./strategy.sh example_weights_01_sma_daily --check ایک strategy import کرتا ہے اور کسی بھی data کو access نہیں کرتا۔ یہ تصدیق کرنے کا تیز ترین طریقہ ہے کہ installation درست ہے۔

Demo run کے لیے صرف ایک command درکار ہے: ./strategy.sh example_weights_01_sma_daily --sample-data --output /tmp/qj-sample

--sample-data flag ہی اس مثال کا بنیادی مقصد واضح کرتا ہے۔ Project اس کے dataset کو یوں بیان کرتا ہے:

Sample dataset دانستہ طور پر چھوٹا اور reproducible ہے۔ یہ installation checks، report generation، اور account بنائے بغیر engine flow سمجھنے کے لیے مفید ہے۔
ماخذ: quantjourney-bt README، version 0.12.4، 6 August 2026 کو پڑھا گیا۔

Run console پر verdict دینے کے بجائے ایک directory میں files لکھتا ہے: summary.txt اور summary.json، ایک metrics.csv، اس کے equity_curve.png کے ساتھ موجود equity_curve.csv، ایک dashboard.html، plots/ folder، اور run_metadata.json جس میں درج ہوتا ہے کہ run کو کیسے configure کیا گیا تھا۔ آخری file کو اکثر لوگ نظرانداز کر دیتے ہیں، حالانکہ یہی file ایک سال بعد result کو قابلِ audit بناتی ہے۔

حاصل ہونے والی metrics کو حقیقت پسندانہ انداز میں پڑھیں۔ Bundled dataset چھوٹا اور illustrative ہے، اس لیے summary.txt میں دکھایا گیا Sharpe ratio اور maximum drawdown صرف ایک sample file کی خصوصیات بیان کرتے ہیں۔ یہ کسی strategy کے بارے میں evidence نہیں ہیں۔ انہیں result سمجھ لینا پہلی غلطی ہے۔

یہ run جو بات ثابت کرتا ہے، وہ بہرحال مفید ہے: installation کام کرتی ہے، اور signal سے target weights اور پھر reconstructed portfolio value تک مکمل engine flow آپ کی machine پر کسی بھی credential کے بغیر اپنے artifacts تیار کرتا ہے۔ Real history پر backtests کے لیے project کی اپنی data service تک credentialed path بھی موجود ہے۔ اس path کی documentation دستیاب ہے، اور یہ walkthrough یہاں ختم ہوتا ہے، کیونکہ اس مرحلے پر کسی سے بھی کچھ درکار نہیں۔

ایک in-sample equity curve آپ کو کیا نہیں بتاتی

یہ sample run ایک equity curve دکھاتا ہے۔ حقیقی market data پر ناپنے سے معلوم ہوتا ہے کہ یہ curve کیا ثابت نہیں کر سکتی، نہ کہ صرف demo file کے ذریعے۔

ذیل کا panel وہی تصور استعمال کرتا ہے جو example strategy میں ہے: 20-session moving average کا 50-session moving average کو cross کرنا۔ اسے SPY پر لاگو کیا گیا ہے اور 2017 سے 2025 تک ہر calendar year کی رپورٹ الگ دی گئی ہے۔ ہر session میں position کا تعین پچھلے session کے close سے کیا جاتا ہے، اس لیے rule کبھی ایسے number پر trade نہیں کرتا جو اس وقت دستیاب نہ ہو۔

استفسار کریںSPY پر 20/50 moving-average crossover بمقابلہ holding، سال بہ سال
ہر عدد کے پیچھے موجود درست SQL
WITH daily AS
(
    SELECT
        toDate(toTimeZone(window_start, 'America/New_York')) AS d,
        toFloat64(argMax(close, window_start))               AS px
    FROM global_markets.delayed_stocks_minute_aggs
    WHERE ticker = 'SPY'
      AND window_start >= '2016-01-01 00:00:00'
      AND window_start <  '2026-01-01 05:00:00'
      AND (toHour(toTimeZone(window_start, 'America/New_York')) * 60
           + toMinute(toTimeZone(window_start, 'America/New_York'))) >= 570
      AND (toHour(toTimeZone(window_start, 'America/New_York')) * 60
           + toMinute(toTimeZone(window_start, 'America/New_York'))) < 960
    GROUP BY d
),
averaged AS
(
    SELECT
        d,
        px,
        avg(px) OVER (ORDER BY d ROWS BETWEEN 19 PRECEDING AND CURRENT ROW) AS fast_ma,
        avg(px) OVER (ORDER BY d ROWS BETWEEN 49 PRECEDING AND CURRENT ROW) AS slow_ma,
        row_number() OVER (ORDER BY d)                                      AS session_no
    FROM daily
),
positioned AS
(
    SELECT
        d,
        px,
        if(session_no >= 50 AND fast_ma > slow_ma, 1, 0) AS long_today,
        lagInFrame(if(session_no >= 50 AND fast_ma > slow_ma, 1, 0), 1)
            OVER (ORDER BY d ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) AS long_prior,
        lagInFrame(px, 1)
            OVER (ORDER BY d ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) AS px_prior
    FROM averaged
)
SELECT
    toYear(d)                                                                   AS year,
    round((exp(sum(log(if(long_prior = 1, px / px_prior, 1.0)))) - 1) * 100, 1) AS rule_pct,
    round((exp(sum(log(px / px_prior))) - 1) * 100, 1)                          AS hold_pct,
    countIf(long_today != long_prior)                                           AS crossover_count
FROM positioned
WHERE px_prior > 0
  AND toYear(d) >= 2017
GROUP BY year
ORDER BY year
Run this yourself

ایک ہی unchanged rule، 9 مرتبہ الگ الگ ناپا گیا۔ اس کے علاوہ کچھ پڑھنے سے پہلے دونوں percent columns کو اوپر سے نیچے تک دیکھیں۔ 2017 میں rule نے سال 16% پر ختم کیا، جبکہ اسی مدت میں SPY کو hold کرنے کا نتیجہ 19.4% تھا۔ 2025 میں یہی دونوں columns 10.4% اور 16.4% دکھاتے ہیں۔ دونوں rows میں code یکساں ہے۔ صرف window بدلی گئی ہے۔

crossover column ظاہر کرتا ہے کہ underlying evidence کتنا محدود ہے۔ 2025 کے دوران 4 position changes کا مطلب ہے کہ پورے سال کی equity curve صرف چند فیصلوں پر مبنی ہے۔ کسی نتیجے کے لیے یہ بہت چھوٹا sample ہے۔

کیا یہی rule دوسرے names پر بھی اسی طرح کام کرتا ہے؟

تاریخ کی window تبدیل کرنا ایک curve کا جائزہ لینے کا ایک طریقہ ہے۔ دوسرا طریقہ universe تبدیل کرنا ہے۔ ذیل کا panel parameters کو برقرار رکھتے ہوئے یہی rule پانچ liquid names پر 2021 سے 2025 تک کے پانچ calendar years میں چلاتا ہے۔

استفسار کریںپانچ liquid names پر یہی 20/50 rule، 2021 تا 2025
ہر عدد کے پیچھے موجود درست SQL
WITH daily AS
(
    SELECT
        ticker,
        toDate(toTimeZone(window_start, 'America/New_York')) AS d,
        toFloat64(argMax(close, window_start))               AS px
    FROM global_markets.delayed_stocks_minute_aggs
    WHERE ticker IN ('SPY', 'QQQ', 'AAPL', 'MSFT', 'KO')
      AND window_start >= '2020-07-01 00:00:00'
      AND window_start <  '2026-01-01 05:00:00'
      AND (toHour(toTimeZone(window_start, 'America/New_York')) * 60
           + toMinute(toTimeZone(window_start, 'America/New_York'))) >= 570
      AND (toHour(toTimeZone(window_start, 'America/New_York')) * 60
           + toMinute(toTimeZone(window_start, 'America/New_York'))) < 960
    GROUP BY ticker, d
),
averaged AS
(
    SELECT
        ticker,
        d,
        px,
        avg(px) OVER (PARTITION BY ticker ORDER BY d ROWS BETWEEN 19 PRECEDING AND CURRENT ROW) AS fast_ma,
        avg(px) OVER (PARTITION BY ticker ORDER BY d ROWS BETWEEN 49 PRECEDING AND CURRENT ROW) AS slow_ma,
        row_number() OVER (PARTITION BY ticker ORDER BY d)                                      AS session_no
    FROM daily
),
positioned AS
(
    SELECT
        ticker,
        d,
        px,
        lagInFrame(if(session_no >= 50 AND fast_ma > slow_ma, 1, 0), 1)
            OVER (PARTITION BY ticker ORDER BY d ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) AS long_prior,
        lagInFrame(px, 1)
            OVER (PARTITION BY ticker ORDER BY d ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) AS px_prior
    FROM averaged
)
SELECT
    ticker                                                                      AS symbol,
    round((exp(sum(log(if(long_prior = 1, px / px_prior, 1.0)))) - 1) * 100, 1) AS rule_pct,
    round((exp(sum(log(px / px_prior))) - 1) * 100, 1)                          AS hold_pct,
    round(avg(long_prior) * 100, 0)                                             AS days_long_pct
FROM positioned
WHERE px_prior > 0
  AND d >= toDate('2021-01-01')
GROUP BY symbol
ORDER BY rule_pct DESC
Run this yourself

QQQ کے نتائج panel میں سب سے اوپر 40.2% پر ہیں، جبکہ آخری row، KO، 3.8% پر آتی ہے۔ days_long_pct column بتاتا ہے کہ ہر version نے window کے کتنے حصے میں کوئی position رکھی؛ اوپر والی row کے لیے یہ شرح 67% ہے۔ ایک parameter set، پانچ universes، اور نتائج میں اتنا وسیع spread کہ بعد میں winner منتخب کرنا اس run کے بارے میں کچھ نہیں بتاتا جو ابھی کیا ہی نہیں گیا۔

یہ crossover پر trade کرنے کی کوئی recommendation نہیں ہے۔ Crossover backtest کے لیے ایک measuring stick ہے، اور ہم دراصل backtest کو measure کر رہے ہیں۔

Weights backtest میں look-ahead bias کہاں داخل ہوتا ہے

Weights-based engine signal کو target weights میں تبدیل کرتا ہے، پھر ان weights کے مقابل fills کو simulate کرتا ہے، اور حاصل ہونے والی positions سے portfolio value دوبارہ بناتا ہے۔ خرابی signal اور weight کے درمیان join میں چھپی ہوتی ہے۔ اگر آج کا weight آج کے close سے حاصل ہو اور پھر اسی دن کا return کمائے، تو backtest ایسی معلومات کی بنیاد پر trade کر چکا ہے جو order داخل کیے جانے کے وقت دستیاب نہیں تھیں۔ یہی look-ahead bias ہے، اور اس پر کوئی error ظاہر نہیں ہوتا۔ یہ صرف ہر چیز کو بہتر دکھاتا ہے۔

ذیل کا panel ایک ہی rule کے دونوں versions کو SPY کی اسی history پر چلاتا ہے۔

استفسار کریںوہی rule، prior-session signal بمقابلہ same-session signal، SPY میں سال بہ سال
ہر عدد کے پیچھے موجود درست SQL
WITH daily AS
(
    SELECT
        toDate(toTimeZone(window_start, 'America/New_York')) AS d,
        toFloat64(argMax(close, window_start))               AS px
    FROM global_markets.delayed_stocks_minute_aggs
    WHERE ticker = 'SPY'
      AND window_start >= '2016-01-01 00:00:00'
      AND window_start <  '2026-01-01 05:00:00'
      AND (toHour(toTimeZone(window_start, 'America/New_York')) * 60
           + toMinute(toTimeZone(window_start, 'America/New_York'))) >= 570
      AND (toHour(toTimeZone(window_start, 'America/New_York')) * 60
           + toMinute(toTimeZone(window_start, 'America/New_York'))) < 960
    GROUP BY d
),
averaged AS
(
    SELECT
        d,
        px,
        avg(px) OVER (ORDER BY d ROWS BETWEEN 19 PRECEDING AND CURRENT ROW) AS fast_ma,
        avg(px) OVER (ORDER BY d ROWS BETWEEN 49 PRECEDING AND CURRENT ROW) AS slow_ma,
        row_number() OVER (ORDER BY d)                                      AS session_no
    FROM daily
),
positioned AS
(
    SELECT
        d,
        px,
        if(session_no >= 50 AND fast_ma > slow_ma, 1, 0) AS long_today,
        lagInFrame(if(session_no >= 50 AND fast_ma > slow_ma, 1, 0), 1)
            OVER (ORDER BY d ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) AS long_prior,
        lagInFrame(px, 1)
            OVER (ORDER BY d ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) AS px_prior
    FROM averaged
)
SELECT
    toYear(d)                                                                   AS year,
    round((exp(sum(log(if(long_prior = 1, px / px_prior, 1.0)))) - 1) * 100, 1) AS next_bar_pct,
    round((exp(sum(log(if(long_today = 1, px / px_prior, 1.0)))) - 1) * 100, 1) AS same_bar_pct,
    round(abs(exp(sum(log(if(long_today = 1, px / px_prior, 1.0))))
              - exp(sum(log(if(long_prior = 1, px / px_prior, 1.0))))) * 100, 1) AS gap_pp
FROM positioned
WHERE px_prior > 0
  AND toYear(d) >= 2017
GROUP BY year
ORDER BY year
Run this yourself

2017 میں prior-session version نے 16% رپورٹ کیا، جبکہ same-session version نے 17.1% رپورٹ کیا؛ دونوں کے درمیان 1.1 percentage points کا فرق تھا۔ 2025 میں دونوں کے درمیان فاصلہ 1.6 percentage points ناپا گیا۔ ان columns میں سے صرف ایک ایسی machine تیار کر سکتی ہے جسے پہلے سے معلوم نہ ہو کہ session کہاں بند ہوا، اور دونوں کے درمیان فرق خالص accounting ہے؛ اس کے پیچھے نہ کوئی idea ہے، نہ skill، اور نہ trade۔

یہ engine timing کے سوال پر اپنا مؤقف خود بیان کرتا ہے، جو کسی وعدے سے زیادہ قابلِ قدر ہے:

Open پر fills کے لیے range-sensitive slippage صرف گزشتہ مکمل bar کو دیکھتا ہے، جبکہ volume capacity کی پیش گوئی lagged observations سے کی جاتی ہے؛ engine اس دن کے بعد میں آنے والے high، low، close یا full-day volume کو استعمال نہیں کرتا۔
ماخذ: quantjourney-bt README، version 0.12.4، 6 August 2026 کو مطالعہ کیا گیا۔

Documented assumption کو اس source کے ساتھ جانچا جا سکتا ہے جو آپ پہلے ہی install کر چکے ہیں۔ Undocumented assumption محض ایک اندازہ ہے۔

Walk-forward اضافی مثال کیوں موجود ہے

Walk-forward مثالیں، WF01 سے WF05 تک، [wf] اضافی پیکیج اور اس کی Optuna dependency کے ساتھ آتی ہیں۔ Walk-forward طریقۂ کار تاریخ کے ایک حصے پر parameters fit کرتا ہے، اس کے بعد آنے والے حصے پر ان کی پیمائش کرتا ہے، پھر اس جوڑے کو آگے roll کرکے عمل دہراتا ہے۔ Rolling اور expanding variants میں فرق یہ ہے کہ آگے بڑھتے وقت fitting window اپنا قدیم ترین data خارج کرتی ہے یا نہیں۔ ایک اور مثال ہر boundary پر purge اور embargo شامل کرتی ہے۔ اس میں seam کے قریب ترین observations خارج کر دیے جاتے ہیں، تاکہ fitted slice اس slice میں leak نہ ہو جس پر اس کی پیمائش کی جا رہی ہے۔

ان میں سے کوئی چیز کمزور خیال کو working strategy میں تبدیل نہیں کرتی۔ یہ ایک واحد number کی جگہ numbers کی ایسی distribution دیتی ہے جس پر بحث کی جا سکتی ہے، اور اصل بہتری یہی ہے۔ اس کے بعد کا مرحلہ اب بھی live money نہیں ہے: حقیقی رقم سے پہلے paper trading وہ چیزیں جانچتی ہے جو backtest ساختی طور پر نہیں دیکھ سکتا۔ اس کا آغاز اس سوال سے ہوتا ہے کہ آپ کا order simulator کی فرض کردہ price کے قریب کہیں fill بھی ہوتا ہے یا نہیں۔ trading bots کے لیے circuit breakers اس صورتِ حال کو cover کرتے ہیں جب کسی دن آپ کا code وہ کام نہ کرے جو اسے کرنا چاہیے۔ اس پورے عمل کے بنیادی statistics کے لیے open-source quant trading book پر ہمارے notes ایک درجہ زیادہ گہرائی میں جاتے ہیں۔

اکثر پوچھے جانے والے سوالات

کیا API key کے بغیر strategy کا backtest کیا جا سکتا ہے؟

ہاں۔ quantjourney-bt میں --sample-data flag کے تحت ایک sample dataset شامل ہے، اور اس کی example strategies کسی account یا credentials کے بغیر اسی dataset پر چلتی ہیں۔ یہ dataset چھوٹا اور illustrative ہے، اس لیے اس run کو strategy کے بارے میں ثبوت نہیں بلکہ installation اور pipeline check سمجھیں۔

Python backtesting package کا version pin کرنا کیوں ضروری ہے؟

0.x package میں minor releases کے درمیان compatibility کی کوئی ضمانت نہیں ہوتی، اور تبدیل شدہ default یا renamed metric خود اس تبدیلی کا اعلان نہیں کرتا۔ pip install quantjourney-bt==0.12.4 کے ذریعے version pin کرنے اور requirements file میں environment record کرنے سے آج حاصل کیا گیا result اگلے سال بھی اسی engine پر دوبارہ تیار کیا جا سکتا ہے جس نے اسے پیدا کیا تھا۔

quantjourney-bt کس licence کے تحت جاری کیا گیا ہے؟

Apache License 2.0۔ یہ commercial use اور modification کی اجازت دیتا ہے، redistribution کے ساتھ licence اور notice files برقرار رکھنے کا تقاضا کرتا ہے، اور contributors کی جانب سے واضح patent grant بھی شامل کرتا ہے۔ Version 0.12.4، 21 July 2026 کو شائع ہوا تھا اور اس کے لیے Python 3.11 یا اس کے بعد کا version درکار ہے۔

کیا backtest کا مضبوط result اس بات کا مطلب ہے کہ strategy کام کرتی ہے؟

نہیں۔ Backtest ایک measurement ہوتا ہے، جو ایک window اور ایک universe پر کیا جاتا ہے۔ اوپر موجود panels دکھاتے ہیں کہ ایک ہی unchanged rule، ایک ticker پر سالانہ figures اور پانچ مختلف names پر مجموعی figures میں بہت مختلف results دے سکتا ہے۔ Walk-forward validation اور out-of-sample testing اسی فرق کو سامنے لانے کے لیے استعمال ہوتے ہیں۔

اوپر موجود panels کا حساب کیسے کیا گیا

Daily closes ہر تاریخ کے لیے regular session کے آخری minute print ہیں۔ یہ New York clock time کے مطابق صبح 9:30 سے شام 4:00 بجے کے درمیان لیے گئے ہیں۔ اس طریقے سے early-close والے half days درست رہتے ہیں اور session length کو hardcode کرنے کی ضرورت نہیں پڑتی۔ Fast average میں 20 sessions اور slow average میں 50 sessions شامل ہیں، اور دونوں simple averages ہیں۔ ہر series کی پہلی 49 sessions warm-up period ہیں، جس میں کوئی position نہیں رکھی جاتی۔ Yearly figures میں ان sessions کی close-to-close move کو compound کیا گیا ہے جن میں rule long تھا۔ تقابل کے لیے hold column میں اسی سال کی ہر session کو compound کیا گیا ہے۔ Cross-section میں شامل پانچ names کا انتخاب مسلسل histories اور window کے اندر split نہ ہونے کی بنیاد پر کیا گیا، اس لیے close series کو adjustment کی ضرورت نہیں۔ Windows ماضی میں fixed ہیں، لہٰذا ہر regeneration پر یہ panels وہی numbers واپس کرتے ہیں۔


یہاں موجود ہر panel کے نیچے اس کا exact SQL دیا گیا ہے۔ اس سے اس صفحے کے numbers بھی pinned install کی طرح دوبارہ چلائے جا سکتے ہیں۔ Backtest code لکھنے سے پہلے اپنی window پر کسی rule کی پیمائش کرنے کے لیے Strasmore terminal پر سوال plain English میں لکھیں۔

#backtesting#python#open source#reproducibility#quantjourney