← Kembali ke artikel
March 6, 2026
5 menit baca

Monte Carlo Bootstrap: Cara Mendapatkan Confidence Interval untuk Backtest dalam 10 Baris Kode

Monte Carlo Bootstrap: Cara Mendapatkan Confidence Interval untuk Backtest dalam 10 Baris Kode
#algotrading
#backtest
#Monte Carlo
#bootstrap
#confidence interval
#manajemen risiko
#statistik

Anda menjalankan strategi melalui backtest. Anda mendapatkan PnL +42%, Sharpe 1.8, MaxDD -12%. Hasilnya terlihat luar biasa. Anda meluncurkan bot ke produksi, dan sebulan kemudian Anda menemukan bahwa drawdown sudah -28% dan PnL menuju nol.

Apa yang salah? Ini bukan bug dan bukan "pasar yang berubah." Masalahnya adalah Anda membuat keputusan berdasarkan satu angka — estimasi titik tunggal. Anda mengetahui bahwa strategi menunjukkan +42%, tetapi Anda tidak mengetahui seberapa besar Anda dapat mempercayai angka tersebut.

Masalah dengan Estimasi Titik Tunggal

Estimasi titik tunggal versus distribusi probabilitas penuh Satu titik data (kiri) memberikan gambaran yang menyesatkan, sementara distribusi penuh (kanan) mengungkapkan rentang hasil yang mungkin sebenarnya.

Backtest pada data historis adalah satu kali jalan melalui satu urutan kejadian pasar yang spesifik. Hasilnya bergantung pada urutan perdagangan: strategi yang sama dengan perdagangan yang sama, tetapi dalam urutan berbeda, dapat menunjukkan drawdown maksimum yang sepenuhnya berbeda.

Bayangkan 491 perdagangan. Setiap perdagangan adalah kejadian acak dengan distribusi return tertentu. Backtest historis hanya menunjukkan satu realisasi dari proses ini. Ini seperti melempar dadu sekali dan menyimpulkan bahwa dadu selalu menghasilkan angka empat.

Yang sebenarnya kita butuhkan:

  • Bukan estimasi titik, tetapi sebuah interval: "dengan probabilitas 95%, PnL akhir akan berada antara X dan Y"
  • Bukan satu drawdown maksimum, tetapi sebuah distribusi: "dalam 5% skenario terburuk, drawdown melebihi Z%"
  • Bukan rata-rata, tetapi ekornya: apa yang terjadi jika keberuntungan tidak berpihak pada Anda?

Inilah tepatnya fungsi Monte Carlo bootstrap.

Apa Itu Monte Carlo Bootstrap

Resampling Monte Carlo bootstrap: ribuan jalur ekuitas alternatif yang dihasilkan dari data perdagangan Bootstrap menghasilkan ribuan trajektori ekuitas alternatif dengan melakukan resampling perdagangan dengan penggantian dari dataset asli.

Bootstrap adalah metode resampling yang diusulkan oleh Bradley Efron pada tahun 1979. Idenya elegan: jika kita memiliki sampel data, kita dapat menghasilkan ribuan sampel "baru" dengan memilih elemen secara acak dari aslinya dengan penggantian.

Dalam konteks backtest, cara kerjanya adalah sebagai berikut:

  1. Anda memiliki array return untuk setiap perdagangan — misalnya, 491 nilai
  2. Anda memilih secara acak 491 nilai dari array ini dengan penggantian — beberapa perdagangan akan muncul dua kali, beberapa tidak akan muncul sama sekali
  3. Anda membangun kurva ekuitas dari sampel baru ini
  4. Anda mengulanginya 10.000 kali
  5. Anda mendapatkan distribusi metrik akhir, bukan satu angka

Setiap iterasi adalah satu "skenario alternatif": apa yang bisa terjadi jika urutan dan kumpulan perdagangan sedikit berbeda.

Implementasi dalam 10 Baris

Berikut adalah implementasi lengkap yang berfungsi:

import numpy as np

def max_drawdown(equity_curve):
    """Menghitung drawdown maksimum dari kurva ekuitas."""
    peak = np.maximum.accumulate(equity_curve)
    drawdown = (equity_curve - peak) / peak
    return drawdown.min()

trade_returns = [...]  # 491 nilai, misal [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)
    })

Waktu eksekusi: ~2 detik pada laptop biasa. 10.000 sejarah alternatif dari strategi Anda.

Mengekstrak Confidence Interval

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

Sekarang kita tidak memiliki satu angka, tetapi sebuah distribusi. Berikut cara mengekstrak informasi berguna darinya:

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)    # ke-5 — kasus terburuk
dd_50 = np.percentile(df['max_dd'], 50)
dd_95 = np.percentile(df['max_dd'], 95)  # ke-95 — kasus terbaik

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 nyata:

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

Sekarang perbedaannya jelas:

  • Backtest menunjukkan PnL +42% — tetapi dalam 5% skenario terburuk, PnL hanya +18,3%
  • Backtest menunjukkan MaxDD -12% — tetapi dalam 5% skenario terburuk, drawdown adalah -23,4%
  • Sharpe 1,8 — tetapi batas bawahnya adalah 1,12

Persentil ke-5 adalah "kasus terburuk realistis" Anda. Jika strategi berhenti menguntungkan pada persentil ke-5, meluncurkannya ke produksi berisiko.

Visualisasi: Fan Chart

Monte Carlo bootstrap secara alami divisualisasikan sebagai fan chart — kipas kurva ekuitas:

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()

Fan chart memberikan pemahaman intuitif tentang penyebaran hasil yang mungkin. Kipas yang sempit berarti strategi stabil. Kipas yang lebar berarti hasilnya sangat bergantung pada "keberuntungan" dengan urutan perdagangan.

Visualisasi Monte Carlo: Fan Chart dan Histogram Distribusi Fan chart (kiri) menunjukkan penyebaran trajektori ekuitas yang mungkin, dan histogram (kanan) menunjukkan distribusi kepadatan return akhir dengan confidence interval yang disorot (5%, 50%, 95%).

Analisis Lanjutan: Probabilitas Kebangkrutan

Analisis probabilitas kebangkrutan: kurva ekuitas yang bertahan atau jatuh ke kebangkrutan Visualisasi probabilitas kebangkrutan: jalur ekuitas yang bertahan (cyan) melengkung ke atas sementara jalur yang bangkrut (merah) jatuh di bawah tepi jurang ekuitas nol.

Bootstrap memungkinkan Anda menjawab pertanyaan kritis: berapa probabilitas 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 tidak mungkin diperoleh dari satu kali backtest. Namun metrik ini sangat penting untuk membuat keputusan meluncurkan strategi.

Untuk lebih lanjut tentang mengapa drawdown dalam secara matematis berbahaya dan bagaimana asimetri return bekerja, baca artikel kami Asimetri Kerugian-Keuntungan.

Ketika Bootstrap Klasik Tidak Bekerja

Metode ini memiliki keterbatasan yang penting untuk diketahui.

Autokorelasi Return

Bootstrap klasik mengasumsikan bahwa perdagangan bersifat independen. Pada kenyataannya, hal ini sering tidak terjadi — strategi dapat memiliki rentetan kemenangan dan kekalahan. Jika autokorelasi signifikan, gunakan block bootstrap:

def block_bootstrap(returns, block_size=10, n_simulations=10000):
    """Bootstrap yang mempertahankan struktur ketergantungan lokal."""
    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 mempertahankan ketergantungan lokal antara perdagangan yang berurutan, memberikan confidence interval yang lebih realistis untuk MaxDD.

Resampling block bootstrap: blok perdagangan berurutan dikocok dan digabungkan kembali Block bootstrap mempertahankan autokorelasi dalam blok dengan mempartisi urutan perdagangan menjadi blok dan melakukan resampling dengan penggantian.

Non-Stasioneritas Pasar

Bootstrap bekerja dengan distribusi perdagangan asli. Jika pasar telah berubah secara struktural (misalnya, volatilitas turun atau likuiditas berubah), perdagangan historis mungkin tidak representatif. Untuk mengatasinya:

  • Gunakan rolling window: bootstrap hanya pada N perdagangan terakhir
  • Berikan bobot lebih besar pada perdagangan terbaru: weighted bootstrap
  • Pisahkan data berdasarkan rezim pasar dan lakukan bootstrap secara terpisah

Jumlah Perdagangan yang Sedikit

Bootstrap dapat diandalkan ketika n > 30 perdagangan. Jika Anda hanya memiliki 10 perdagangan — tidak ada jumlah resampling yang akan membantu. 491 perdagangan adalah sampel yang sangat baik; Anda dapat mempercayai hasilnya.

Perbandingan Pendekatan untuk Penilaian Ketangguhan Backtest

Metode Yang disediakan Kompleksitas Waktu Kapan digunakan
Backtest tunggal Satu estimasi titik Minimal Detik Tidak pernah sebagai hasil akhir
Walk-forward Metrik out-of-sample Sedang Menit Untuk memeriksa overfitting
Monte Carlo bootstrap Confidence interval Minimal ~2 detik Selalu sebelum produksi
Monte Carlo path Jalur harga baru Tinggi Menit-jam Untuk stress testing
Cross-validation Metrik rata-rata lintas fold Sedang Menit Untuk penyetelan parameter

Monte Carlo bootstrap adalah satu-satunya metode yang dalam waktu minimal memberikan gambaran lengkap tentang risiko.

Checklist: Menginterpretasikan Hasil

Berikut cara kami merekomendasikan untuk menginterpretasikan hasil Monte Carlo bootstrap:

Luncurkan ke produksi jika:

  • PnL pada persentil ke-5 positif
  • MaxDD pada persentil ke-5 dapat diterima sesuai selera risiko Anda
  • Probabilitas kebangkrutan < 1%
  • Sharpe pada persentil ke-5 > 0,5

Perlu diperbaiki jika:

  • PnL pada persentil ke-5 mendekati nol
  • MaxDD pada persentil ke-5 jauh lebih buruk dari persentil ke-50
  • Penyebaran fan chart lebar — strategi tidak stabil

Jangan diluncurkan jika:

  • PnL pada persentil ke-5 negatif
  • Probabilitas kebangkrutan > 5%
  • Confidence interval untuk Sharpe mencakup 0

Pengalaman Kami di marketmaker.cc

Di marketmaker.cc, kami mengembangkan mesin backtest kami sendiri, dan Monte Carlo bootstrap adalah bagian integral dari pipeline kami. Setiap strategi melewati bootstrap secara otomatis sebelum disetujui untuk live trading.

Kami mengintegrasikan bootstrap langsung ke dalam mesin backtest: setelah satu kali jalan, Anda tidak hanya mendapatkan PnL akhir, tetapi laporan lengkap dengan confidence interval, fan chart, probabilitas kebangkrutan, dan perbandingan block vs. bootstrap standar. Ini memerlukan tambahan 2-3 detik — harga yang tidak berarti untuk memahami risiko nyata.

Dari pengalaman kami: sekitar 30% strategi yang terlihat menarik berdasarkan estimasi titik tunggal tersaring setelah Monte Carlo bootstrap. PnL persentil ke-5 mereka menjadi negatif atau MaxDD ternyata tidak dapat diterima. Tanpa bootstrap, strategi-strategi ini akan masuk ke produksi dan kemungkinan besar akan mengakibatkan kerugian.

Kesimpulan

Monte Carlo bootstrap adalah ~10 baris kode dan ~2 detik komputasi. Ini mengubah satu angka dari backtest menjadi distribusi penuh dengan confidence interval. Ini mungkin adalah ROI tertinggi dari alat analisis kuantitatif mana pun:

  • Biaya minimal: implementasi dalam 30 menit
  • Manfaat maksimal: pemahaman tentang risiko strategi yang sebenarnya
  • Tanpa dependensi: hanya NumPy

Jika Anda belum menggunakan bootstrap — tambahkan ke pipeline Anda hari ini. Ini adalah satu-satunya cara untuk mengetahui seberapa besar Anda dapat mempercayai hasil backtest Anda.


Referensi

  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

Kutipan

@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/id/blog/post/monte-carlo-bootstrap-backtest},
  version = {0.1.0},
  description = {Mengapa estimasi titik tunggal dari backtest adalah ilusi yang berbahaya. Bagaimana Monte Carlo bootstrap dalam 2 detik komputasi memberikan confidence interval 95% untuk PnL dan MaxDD, dan mengapa ini adalah langkah wajib sebelum meluncurkan strategi ke produksi.}
}
Penafian: Informasi yang disediakan dalam artikel ini hanya untuk tujuan edukasi dan informasi serta tidak merupakan nasihat keuangan, investasi, atau trading. Trading mata uang kripto mengandung risiko kerugian yang signifikan.

Penulis

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

Selangkah Lebih Maju dari Pasar

Berlangganan newsletter kami untuk wawasan AI trading eksklusif, analisis pasar, dan pembaruan platform.

Kami menghormati privasi Anda. Berhenti berlangganan kapan saja.