← Kembali ke artikel
March 6, 2026
Bacaan 5 minit

Monte Carlo Bootstrap: Cara Mendapatkan Selang Keyakinan untuk Backtest dalam 10 Baris Kod

Monte Carlo Bootstrap: Cara Mendapatkan Selang Keyakinan untuk Backtest dalam 10 Baris Kod
#algotrading
#backtest
#Monte Carlo
#bootstrap
#selang keyakinan
#pengurusan risiko
#statistik

Anda telah menjalankan strategi melalui backtest. Anda mendapat PnL +42%, Sharpe 1.8, MaxDD -12%. Hasilnya kelihatan hebat. Anda melancarkan bot ke pengeluaran, dan sebulan kemudian anda mendapati drawdown sudah -28% dan PnL menuju ke sifar.

Apa yang salah? Ini bukan bug dan bukan "pasaran yang berubah." Masalahnya ialah anda membuat keputusan berdasarkan satu nombor — anggaran titik tunggal. Anda mengetahui bahawa strategi itu menunjukkan +42%, tetapi anda tidak mengetahui sejauh mana anda boleh mempercayai nombor tersebut.

Masalah dengan Anggaran Titik Tunggal

Anggaran titik tunggal berbanding taburan kebarangkalian penuh Satu titik data (kiri) memberikan gambaran yang mengelirukan, manakala taburan penuh (kanan) mendedahkan julat sebenar hasil yang mungkin.

Backtest pada data sejarah adalah satu larian melalui satu urutan peristiwa pasaran yang spesifik. Hasilnya bergantung pada susunan dagangan: strategi yang sama dengan dagangan yang sama, tetapi dalam susunan yang berbeza, boleh menunjukkan drawdown maksimum yang sama sekali berbeza.

Bayangkan 491 dagangan. Setiap dagangan adalah peristiwa rawak dengan taburan pulangan tertentu. Backtest sejarah hanya menunjukkan satu realisasi proses ini. Ia seperti melempar dadu sekali dan membuat kesimpulan bahawa dadu sentiasa mendarat pada empat.

Apa yang sebenarnya kita perlukan:

  • Bukan anggaran titik, tetapi selang: "dengan kebarangkalian 95%, PnL akhir akan berada antara X dan Y"
  • Bukan satu drawdown maksimum, tetapi taburan: "dalam 5% senario terburuk, drawdown melebihi Z%"
  • Bukan min, tetapi ekor: apa yang berlaku jika nasib tidak menyebelahi anda?

Inilah tujuan Monte Carlo bootstrap.

Apa Itu Monte Carlo Bootstrap

Bootstrap semula sampel Monte Carlo: ribuan laluan ekuiti alternatif dijana daripada data dagangan Bootstrap menjana ribuan trajektori ekuiti alternatif dengan mensampling semula dagangan dengan penggantian daripada set data asal.

Bootstrap adalah kaedah pensampelan semula yang dicadangkan oleh Bradley Efron pada tahun 1979. Ideanya elegan: jika kita mempunyai sampel data, kita boleh menjana ribuan sampel "baru" dengan memilih elemen secara rawak daripada asal dengan penggantian.

Dalam konteks backtest, ia berfungsi seperti ini:

  1. Anda mempunyai tatasusunan pulangan untuk setiap dagangan — contohnya, 491 nilai
  2. Anda memilih secara rawak 491 nilai daripada tatasusunan ini dengan penggantian — sesetengah dagangan akan muncul dua kali, sesetengah tidak akan muncul langsung
  3. Anda membina lengkung ekuiti daripada sampel baru ini
  4. Anda mengulangi 10,000 kali
  5. Anda mendapat taburan metrik akhir, bukan satu nombor

Setiap iterasi adalah satu "senario alternatif": apa yang mungkin berlaku jika susunan dan set dagangan berbeza sedikit.

Pelaksanaan dalam 10 Baris

Berikut adalah pelaksanaan lengkap yang berfungsi:

import numpy as np

def max_drawdown(equity_curve):
    """Calculate the maximum drawdown of an equity curve."""
    peak = np.maximum.accumulate(equity_curve)
    drawdown = (equity_curve - peak) / peak
    return drawdown.min()

trade_returns = [...]  # 491 values, e.g. [0.012, -0.005, 0.008, ...]

n_simulations = 10000
results = []

for _ in range(n_simulations):
    sampled = np.random.choice(trade_returns, size=len(trade_returns), replace=True)
    equity = np.cumprod(1 + sampled)
    results.append({
        "final_pnl": equity[-1] - 1,
        "max_dd": max_drawdown(equity),
        "sharpe": np.mean(sampled) / np.std(sampled) * np.sqrt(252)
    })

Masa pelaksanaan: ~2 saat pada laptop biasa. 10,000 sejarah alternatif strategi anda.

Mengekstrak Selang Keyakinan

Selang keyakinan untuk PnL, MaxDD, dan Sharpe Ratio dengan persentil ke-5, ke-50, dan ke-95 Selang keyakinan untuk metrik strategi utama: PnL, MaxDD, dan Sharpe Ratio, menunjukkan jalur persentil ke-5 (terburuk), ke-50 (median), dan ke-95 (terbaik).

Sekarang kita mempunyai bukan satu nombor, tetapi taburan. Berikut cara mengekstrak maklumat berguna daripadanya:

import pandas as pd

df = pd.DataFrame(results)

pnl_5 = np.percentile(df['final_pnl'], 5)
pnl_50 = np.percentile(df['final_pnl'], 50)
pnl_95 = np.percentile(df['final_pnl'], 95)

dd_5 = np.percentile(df['max_dd'], 5)    # 5th — worst case
dd_50 = np.percentile(df['max_dd'], 50)
dd_95 = np.percentile(df['max_dd'], 95)  # 95th — best case

print(f"PnL:   {pnl_5:.1%} | {pnl_50:.1%} | {pnl_95:.1%}")
print(f"MaxDD: {dd_5:.1%} | {dd_50:.1%} | {dd_95:.1%}")
print(f"Sharpe: {np.percentile(df['sharpe'], 5):.2f}{np.percentile(df['sharpe'], 95):.2f}")

Contoh output untuk strategi sebenar:

Metrik Persentil ke-5 (terburuk) Median Persentil ke-95 (terbaik)
PnL +18.3% +41.7% +72.1%
MaxDD -23.4% -12.8% -5.1%
Sharpe 1.12 1.76 2.41

Kini perbezaannya jelas:

  • Backtest menunjukkan PnL +42% — tetapi dalam 5% senario terburuk, PnL hanya +18.3%
  • Backtest menunjukkan MaxDD -12% — tetapi dalam 5% senario terburuk, drawdown adalah -23.4%
  • Sharpe 1.8 — tetapi had bawah adalah 1.12

Persentil ke-5 adalah "kes terburuk realistik" anda. Jika strategi berhenti menguntungkan pada persentil ke-5, melancarkannya ke pengeluaran adalah berisiko.

Visualisasi: Carta Kipas

Monte Carlo bootstrap divisualisasikan secara semula jadi sebagai carta kipas — kipas lengkung ekuiti:

import matplotlib.pyplot as plt

fig, axes = plt.subplots(1, 2, figsize=(16, 6))

ax = axes[0]
for i in range(min(500, n_simulations)):
    sampled = np.random.choice(trade_returns, size=len(trade_returns), replace=True)
    equity = np.cumprod(1 + sampled)
    ax.plot(equity, alpha=0.02, color='#4FC3F7')

all_equities = []
for _ in range(n_simulations):
    sampled = np.random.choice(trade_returns, size=len(trade_returns), replace=True)
    equity = np.cumprod(1 + sampled)
    all_equities.append(equity)

all_equities = np.array(all_equities)
p5 = np.percentile(all_equities, 5, axis=0)
p50 = np.percentile(all_equities, 50, axis=0)
p95 = np.percentile(all_equities, 95, axis=0)

ax.fill_between(range(len(p5)), p5, p95, alpha=0.3, color='#7C4DFF', label='90% CI')
ax.plot(p50, color='#E040FB', linewidth=2, label='Median')
ax.set_title('Monte Carlo Bootstrap: Equity Curves')
ax.legend()

ax = axes[1]
ax.hist(df['final_pnl'] * 100, bins=80, color='#4FC3F7', alpha=0.7, edgecolor='#1A237E')
ax.axvline(pnl_5 * 100, color='#FF5252', linestyle='--', label=f'5th: {pnl_5:.1%}')
ax.axvline(pnl_50 * 100, color='#E040FB', linestyle='--', label=f'Median: {pnl_50:.1%}')
ax.axvline(pnl_95 * 100, color='#69F0AE', linestyle='--', label=f'95th: {pnl_95:.1%}')
ax.set_title('Distribution of Final PnL')
ax.set_xlabel('PnL, %')
ax.legend()

plt.tight_layout()
plt.savefig('monte_carlo_fan_chart.png', dpi=150)
plt.show()

Carta kipas memberikan pemahaman intuitif tentang penyebaran hasil yang mungkin. Kipas sempit bermakna strategi adalah stabil. Kipas lebar bermakna hasilnya sangat bergantung pada "nasib" dengan urutan dagangan.

Visualisasi Monte Carlo: Carta Kipas dan Histogram Taburan Carta kipas (kiri) menunjukkan penyebaran trajektori ekuiti yang mungkin, dan histogram (kanan) menunjukkan taburan ketumpatan pulangan akhir dengan selang keyakinan yang ditonjolkan (5%, 50%, 95%).

Analisis Lanjutan: Kebarangkalian Kemusnahan

Analisis kebarangkalian kemusnahan: lengkung ekuiti sama ada bertahan atau jatuh ke dalam kemusnahan Visualisasi kebarangkalian kemusnahan: laluan ekuiti yang bertahan (sian) melengkung ke atas manakala laluan yang musnah (merah) jatuh di bawah tebing ekuiti sifar.

Bootstrap membolehkan anda menjawab soalan kritikal: apakah kebarangkalian bahawa strategi akan kehilangan X% modal?

ruin_threshold = -0.20
prob_ruin = (df['max_dd'] < ruin_threshold).mean()
print(f"P(MaxDD < -20%) = {prob_ruin:.1%}")

prob_loss = (df['final_pnl'] < 0).mean()
print(f"P(PnL < 0) = {prob_loss:.1%}")

worst_5pct = df['final_pnl'].quantile(0.05)
cvar = df[df['final_pnl'] <= worst_5pct]['final_pnl'].mean()
print(f"CVaR(5%) = {cvar:.1%}")

Metrik-metrik ini adalah mustahil untuk diperoleh daripada satu larian backtest. Namun ia adalah kritikal untuk membuat keputusan melancarkan strategi.

Untuk maklumat lanjut tentang mengapa drawdown yang dalam adalah berbahaya secara matematik dan bagaimana asimetri pulangan berfungsi, baca artikel kami Asimetri Rugi-Untung.

Bila Bootstrap Klasik Tidak Berfungsi

Kaedah ini mempunyai batasan yang penting untuk diketahui.

Autokorelasi Pulangan

Bootstrap klasik mengandaikan bahawa dagangan adalah bebas. Dalam realiti, ini sering tidak berlaku — strategi boleh mempunyai jujukan menang dan kalah. Jika autokorelasi adalah signifikan, gunakan block bootstrap:

def block_bootstrap(returns, block_size=10, n_simulations=10000):
    """Bootstrap preserving local dependency structure."""
    n = len(returns)
    results = []

    for _ in range(n_simulations):
        starts = np.random.randint(0, n - block_size + 1, size=n // block_size + 1)
        sampled = np.concatenate([returns[s:s+block_size] for s in starts])[:n]
        equity = np.cumprod(1 + sampled)
        results.append({
            "final_pnl": equity[-1] - 1,
            "max_dd": max_drawdown(equity),
        })

    return pd.DataFrame(results)

Block bootstrap mengekalkan kebergantungan tempatan antara dagangan berturutan, memberikan selang keyakinan yang lebih realistik untuk MaxDD.

Pensampelan semula block bootstrap: blok dagangan berurutan dikocok dan digabungkan semula Block bootstrap mengekalkan autokorelasi dalam blok dengan membahagikan urutan dagangan kepada blok dan mensampling semula mereka dengan penggantian.

Ketidakstationeran Pasaran

Bootstrap berfungsi dengan taburan dagangan asal. Jika pasaran telah berubah secara struktur (contohnya, volatiliti menurun atau kecairan berubah), dagangan sejarah mungkin tidak mewakili. Untuk mengambil kira ini:

  • Gunakan tetingkap bergerak: bootstrap hanya pada N dagangan terakhir
  • Beri berat lebih kepada dagangan terkini: weighted bootstrap
  • Bahagikan data mengikut rejim pasaran dan bootstrap secara berasingan

Bilangan Dagangan yang Sedikit

Bootstrap boleh dipercayai apabila n > 30 dagangan. Jika anda mempunyai 10 dagangan — tiada jumlah pensampelan semula yang akan membantu. 491 dagangan adalah sampel yang sangat baik; anda boleh mempercayai hasilnya.

Perbandingan Pendekatan untuk Penilaian Keteguhan Backtest

Kaedah Apa yang disediakan Kerumitan Masa Bila digunakan
Backtest tunggal Satu anggaran titik Minimum Saat Tidak pernah sebagai hasil akhir
Walk-forward Metrik luar sampel Sederhana Minit Untuk memeriksa overfitting
Monte Carlo bootstrap Selang keyakinan Minimum ~2 saat Sentiasa sebelum pengeluaran
Laluan Monte Carlo Laluan harga baru Tinggi Minit-jam Untuk ujian tekanan
Pengesahan silang Metrik purata merentasi lipatan Sederhana Minit Untuk penalaan parameter

Monte Carlo bootstrap adalah satu-satunya kaedah yang dalam masa minimum memberikan gambaran lengkap risiko.

Senarai Semak: Mentafsir Hasil

Berikut adalah cara kami mengesyorkan untuk mentafsir hasil Monte Carlo bootstrap:

Lancarkan ke pengeluaran jika:

  • PnL pada persentil ke-5 adalah positif
  • MaxDD pada persentil ke-5 adalah boleh diterima untuk selera risiko anda
  • Kebarangkalian kemusnahan < 1%
  • Sharpe pada persentil ke-5 > 0.5

Perlu diperbaiki jika:

  • PnL pada persentil ke-5 hampir sifar
  • MaxDD pada persentil ke-5 jauh lebih teruk daripada pada persentil ke-50
  • Penyebaran carta kipas yang lebar — strategi tidak stabil

Jangan lancarkan jika:

  • PnL pada persentil ke-5 adalah negatif
  • Kebarangkalian kemusnahan > 5%
  • Selang keyakinan untuk Sharpe merangkumi 0

Pengalaman Kami di marketmaker.cc

Di marketmaker.cc, kami membangunkan enjin backtest kami sendiri, dan Monte Carlo bootstrap adalah bahagian penting dalam saluran paip kami. Setiap strategi melalui bootstrap secara automatik sebelum diluluskan untuk dagangan langsung.

Kami mengintegrasikan bootstrap terus ke dalam enjin backtest: selepas larian, anda mendapat bukan sahaja PnL akhir, tetapi laporan lengkap dengan selang keyakinan, carta kipas, kebarangkalian kemusnahan, dan perbandingan block berbanding bootstrap standard. Ini mengambil masa tambahan 2-3 saat — harga yang tidak bererti untuk memahami risiko sebenar.

Daripada pengalaman kami: kira-kira 30% strategi yang kelihatan menarik berdasarkan anggaran titik tunggal ditapis selepas Monte Carlo bootstrap. PnL persentil ke-5 mereka menjadi negatif atau MaxDD ternyata tidak boleh diterima. Tanpa bootstrap, strategi-strategi ini akan pergi ke pengeluaran dan sangat berkemungkinan mengakibatkan kerugian.

Kesimpulan

Monte Carlo bootstrap adalah ~10 baris kod dan ~2 saat pengiraan. Ia mengubah satu nombor daripada backtest menjadi taburan penuh dengan selang keyakinan. Ini mungkin pulangan pelaburan tertinggi mana-mana alat analisis kuantitatif:

  • Kos minimum: pelaksanaan dalam 30 minit
  • Ganjaran maksimum: pemahaman risiko strategi sebenar
  • Tiada kebergantungan: hanya NumPy

Jika anda belum menggunakan bootstrap — tambahkannya ke dalam saluran paip anda hari ini. Ini adalah satu-satunya cara untuk mengetahui sejauh mana anda boleh mempercayai hasil backtest anda.


Rujukan

  1. Efron, B. — Bootstrap Methods: Another Look at the Jackknife (1979)
  2. Davison, A.C., Hinkley, D.V. — Bootstrap Methods and their Application (Cambridge)
  3. Aronson, D.R. — Evidence-Based Technical Analysis: Monte Carlo permutation
  4. QuantStart — Monte Carlo Simulation for Backtest Analysis
  5. Marcos Lopez de Prado — Advances in Financial Machine Learning, Chapter 12: Backtesting
  6. Kevin Davey — Building Winning Algorithmic Trading Systems: Monte Carlo Analysis
  7. NumPy — numpy.random.choice

Petikan

@software{soloviov2026montecarlobootstrap,
  author = {Soloviov, Eugen},
  title = {Monte Carlo Bootstrap: How to Get Confidence Intervals for a Backtest in 10 Lines of Code},
  year = {2026},
  url = {https://marketmaker.cc/ms/blog/post/monte-carlo-bootstrap-backtest},
  version = {0.1.0},
  description = {Mengapa anggaran titik tunggal daripada backtest adalah ilusi berbahaya. Bagaimana Monte Carlo bootstrap dalam masa 2 saat pengiraan memberi anda selang keyakinan 95% untuk PnL dan MaxDD, dan mengapa ini adalah langkah wajib sebelum melancarkan strategi ke pengeluaran.}
}
Penafian: Maklumat yang disediakan dalam artikel ini adalah untuk tujuan pendidikan dan maklumat sahaja dan bukan merupakan nasihat kewangan, pelaburan, atau dagangan. Dagangan mata wang kripto melibatkan risiko kerugian yang ketara.

Pengarang

Eugen Soloviov
Eugen Soloviov

Trading-systems engineer

Trading-systems engineer building bots since 2017: cross-exchange arbitrage (connected up to 30 venues), cointegration-based pairs arbitrage across spot and futures, scalping, news and sentiment-driven strategies, trend algorithms, and portfolio management and balancing algorithms. Also builds sub-millisecond order execution, big-data warehouses, backtesting engines, AI agents, and trading interfaces (incl. open-source profitmaker.cc). Stack: JS/TS, Python, Rust/Zig/Go, DevOps, backend, frontend, architecture.

Newsletter

Kekal Mendahului Pasaran

Langgan surat berita kami untuk pandangan dagangan AI eksklusif, analisis pasaran, dan kemas kini platform.

Kami menghormati privasi anda. Berhenti melanggan pada bila-bila masa.