← Kembali ke artikel
March 19, 2026
Bacaan 5 minit

Arbitraj Statistik dan Dagangan Pasangan dalam Pasaran Kripto: Dari Kointegrasi hingga Penapis Kalman

Arbitraj Statistik dan Dagangan Pasangan dalam Pasaran Kripto: Dari Kointegrasi hingga Penapis Kalman
#stat arb
#dagangan pasangan
#kointegrasi
#kalman
#arbitraj
#dagangan algo
#kripto

Pada tahun 1987, sekumpulan ahli fizik di Morgan Stanley memperoleh $50 juta dalam masa setahun dengan memperdagangkan pasangan saham menggunakan algoritma yang tidak dapat dijelaskan sepenuhnya oleh mana-mana daripada mereka kepada pengurusan bank. Pengurusan tidak membantah. Pada tahun 2026, anda boleh melancarkan idea yang sama di bursa kripto — dengan niaga hadapan kekal, pasaran 24/7, dan kecairan yang akan dicemburui oleh Nunzio Tartaglia. Namun ada kelemahannya: apa yang berkesan dengan saham Ford dan GM pada era pra-internet memerlukan adaptasi serius untuk dunia di mana BTC boleh jatuh 20% dalam semalam dan kadar pendanaan boleh berbalik dalam satu blok sahaja.

Artikel ini adalah kupasan lengkap tentang arbitraj statistik dan dagangan pasangan untuk pasaran kripto. Dari teori matematik (kointegrasi, proses Ornstein-Uhlenbeck, penapis Kalman) hingga kod Python berfungsi yang boleh anda jalankan pada data sebenar. Gaya penulisan berorientasikan kejuruteraan: kami menerangkan formula, menunjukkan kod, dan tidak menyembunyikan perangkap.

1. Sejarah Ringkas: Dari Jesuit hingga Quants

Arbitraj statistik dalam bentuk moden lahir di meja dagangan Morgan Stanley pada pertengahan 1980-an. Nunzio Tartaglia — seorang bekas paderi Jesuit dengan PhD dalam fizik — mengumpulkan pasukan ahli matematik, ahli fizik, dan saintis komputer. Objektif: cari corak dalam harga saham yang tidak dapat dilihat oleh pedagang tradisional.

Ideanya sangat mudah. Jika saham Coca-Cola dan Pepsi secara historis bergerak bersama (yang masuk akal — mereka menjual air manis yang sama dalam botol berwarna berbeza), maka divergens dalam harga mereka adalah anomali sementara. Beli yang ketinggalan, jual yang terdepan, tunggu konvergens, kunci keuntungan. Strategi berkecuali pasaran: arah pasaran tidak menjadi perhatian kami.

Pasukan Tartaglia termasuk orang-orang yang kemudiannya akan mengubah seluruh Wall Street:

  • David Shaw — kemudiannya mendirikan D.E. Shaw & Co., salah satu dana lindung nilai kuantitatif terbesar
  • Peter Muller — mendirikan PDT Partners, kumpulan stat arb legenda dalam Morgan Stanley
  • Robert Frey — kemudiannya menyertai Renaissance Technologies di bawah Jim Simons

Kumpulan ini beroperasi sebagai makmal penyelidikan dalam bank pelaburan. Automasi adalah canggih: kluster VAX menjana isyarat, dan pelaksanaan melalui terminal. Pada tahun terbaik (1987-1988), strategi ini memperoleh puluhan juta. Kemudian datang dua tahun berturut-turut kerugian, dan pada tahun 1989 Morgan Stanley menutup meja dagangan itu.

Tetapi idea itu sudah tersebar. Alumni kumpulan itu menyebarkan konsep dagangan pasangan ke seluruh Wall Street. Gatev, Goetzmann, dan Rouwenhorst menerbitkan kertas akademik klasik pada tahun 2006 — "Pairs Trading: Performance of a Relative-Value Arbitrage Rule" — menunjukkan bahawa strategi dagangan pasangan yang mudah secara konsisten memberikan pulangan tahunan ~11% dari tahun 1962 hingga 2002 pada ekuiti AS. Ia merupakan jawapan yang meyakinkan kepada hipotesis pasaran cekap: pasaran secara keseluruhan mungkin cekap, tetapi pasangan aset tertentu secara sistematik menyimpang dari keseimbangan.

Hari ini, arbitraj statistik adalah industri dengan beratus-ratus bilion dolar dalam AUM, dan pasaran kripto menawarkan tanah yang sangat subur: kecairan berfragmen, struktur mikro yang belum matang, dagangan sepanjang masa, dan niaga hadapan kekal dengan kadar pendanaan — instrumen yang tidak wujud dalam pasaran tradisional.

2. Asas Matematik: Korelasi Adalah Perangkap

Mengapa Korelasi Tidak Berkesan

Mari mulakan dengan kesilapan yang dilakukan oleh hampir setiap quant pemula: "BTC dan ETH berkorelasi dengan pekali 0.85, jadi kita boleh memperdagangkan pasangan itu." Tidak. Anda tidak boleh. Boleh — tetapi anda akan rugi wang.

Korelasi mengukur hubungan linear antara pulangan dua aset. Dua aset boleh berkorelasi sempurna, namun harga mereka menyimpang selama-lamanya. Contoh klasik: dua jalan rawak dengan kenaikan berkorelasi — ia menyimpang tanpa had walaupun korelasi tinggi. Anda akan membuka kedudukan mengharapkan "konvergens" yang tidak akan pernah datang.

Kointegrasi vs korelasi

Kointegrasi: Pendekatan yang Betul

Kointegrasi adalah sifat siri harga, bukan pulangan. Dua siri tidak pegun X(t) dan Y(t) dikatakan terkointegrasi jika terdapat gabungan linear:

S(t) = Y(t) - β · X(t)

yang pegun — bermaksud ia kembali ke nilai min. Pekali β dipanggil nisbah lindung nilai, dan S(t) adalah spread.

Intuisi: BTC dan ETH boleh meroket ke bulan atau jatuh ke jurang, tetapi jika perbezaan mereka (diskalakan dengan betul) berayun di sekitar tahap tetap — itulah kointegrasi. Dan itulah tepat yang kita perlukan untuk dagangan.

Ujian Engle-Granger (1987)

Prosedur dua langkah yang Robert Engle dan Clive Granger menerima Hadiah Nobel dalam Ekonomi pada tahun 2003:

Langkah 1. Regresi OLS: Y(t) = α + β · X(t) + ε(t). Kita memperoleh nisbah lindung nilai β dan sisa ε(t).

Langkah 2. Ujian ADF (Augmented Dickey-Fuller) pada sisa ε(t). Hipotesis nol: ε(t) mempunyai akar unit (tidak pegun). Jika p-nilai < 0.05, kita menolak H₀ — siri adalah terkointegrasi.

Penting: untuk ujian kointegrasi, anda tidak boleh menggunakan nilai kritikal ADF standard. Nilai kritikal Engle-Granger diperoleh melalui simulasi Monte Carlo dan mengambil kira kebergantungan antara pemboleh ubah dalam regresi OLS. Dalam statsmodels, ini dilaksanakan dengan betul dalam fungsi coint().

Ujian Johansen

Untuk sistem dengan lebih daripada dua pemboleh ubah (cth., BTC, ETH, dan SOL serentak), ujian Johansen digunakan. Ia mencari semua hubungan kointegrasi dalam sistem dan membenarkan pembinaan portfolio berbilang aset. Ujian ini berdasarkan model VAR (regresi vektor autoregresif) dan menggunakan dua kriteria: statistik surih dan statistik nilai eigen maksimum.

Proses Ornstein-Uhlenbeck

Jika spread terkointegrasi, dinamiknya boleh dimodelkan sebagai proses Ornstein-Uhlenbeck (OU):

dS(t) = θ(μ - S(t))dt + σ dW(t)

di mana:

  • θ — kelajuan pembalikan min
  • μ — tahap min jangka panjang
  • σ — turun naik
  • W(t) — proses Wiener (gerakan Brown)

Dari parameter proses OU, separuh hayat pembalikan min dikira:

t½ = ln(2) / θ

Separuh hayat adalah metrik yang sangat penting. Jika t½ = 5 hari, spread kembali ke min dalam kira-kira 5 hari. Jika t½ = 200 hari, anda akan berada dalam kedudukan selama setengah tahun menunggu konvergens. Untuk strategi kripto, separuh hayat optimum adalah 1-30 hari. Lebih pendek — terlalu pantas, komisen memakan keuntungan. Lebih panjang — terlalu perlahan, risiko perubahan rejim struktur.

Dalam amalan, θ dianggarkan melalui regresi:

ΔS(t) = a + b · S(t-1) + ε(t)

di mana θ = -b, dan t½ = -ln(2) / b.

Penormalan Z-Skor

Untuk menjana isyarat dagangan, spread dinormalisasikan:

z(t) = (S(t) - μ̂) / σ̂

di mana μ̂ dan σ̂ adalah min bergulir dan sisihan piawai spread. Z-skor menunjukkan berapa banyak sisihan piawai spread telah menyimpang dari min. Ambang kemasukan tipikal: |z| > 2.0; ambang keluar: |z| < 0.5.

3. Memilih Pasangan dalam Pasaran Kripto

BTC-ETH: Klasik yang (Kadang-kadang) Berkesan

BTC dan ETH adalah pasangan yang paling jelas dan paling cair. Korelasi pulangan secara konsisten melebihi 0.7. Tetapi kointegrasi adalah perkara berbeza. Ia muncul dan hilang:

  • Semasa pasaran mendatar 2023, BTC/ETH terkointegrasi dengan boleh dipercayai (p-nilai < 0.01)
  • Semasa divergens 2024-2025 (BTC melonjak atas aliran ETF, ETH ketinggalan), kointegrasi rosak
  • Menjelang awal 2026, selepas pelancaran ETH ETF dan pemulihan nisbah ETH/BTC, kointegrasi stabil semula

Kesimpulan: kointegrasi mesti dipantau secara berterusan. Parameter regresi dikira semula pada tetingkap bergulir, dan strategi dimatikan secara automatik jika p-nilai ujian ADF melebihi ambang.

Pasangan Sektor

Pasaran kripto dibahagikan dengan mudah mengikut sektor, dan pasangan dalam sektor sering menunjukkan kointegrasi yang stabil:

Sektor Contoh Pasangan Ciri-ciri
Blockchain L1 SOL/AVAX, NEAR/APT Kecairan tinggi, separuh hayat 3-10 hari
Protokol DeFi AAVE/COMP, UNI/SUSHI Kecairan sederhana, separuh hayat 5-15 hari
Penyelesaian L2 ARB/OP, MATIC/MANTA Turun naik spread tinggi
Memekoin DOGE/SHIB Tidak dapat diramal tetapi menarik (tidak disyorkan)

Pasangan terbaik untuk stat arb mempunyai tiga sifat: (1) kointegrasi stabil sepanjang tetingkap sejarah >6 bulan, (2) kecairan mencukupi — volum harian >$10J setiap aset, (3) separuh hayat yang munasabah — dari 1 hingga 30 hari.

Spot vs Niaga Hadapan Kekal (Basis)

Kategori "pasangan" berasingan adalah aset yang sama di pasaran spot dan niaga hadapan. Perbezaan antara harga niaga hadapan kekal dan harga spot (basis) adalah pegun secara takrifan: mekanisme kadar pendanaan memampaskannya kembali ke sifar. Ini menjadikan dagangan basis salah satu bentuk stat arb yang paling boleh dipercayai dalam kripto.

4. Tiga Pendekatan Dagangan

A. Dagangan Basis: Spot-Niaga Hadapan dan Pembawa Kadar Pendanaan

Bentuk stat arb yang "paling tulen" dalam kripto. Mekanismenya:

  1. Beli aset di spot (cth., 1 BTC)
  2. Buka kedudukan pendek pada niaga hadapan kekal (1 BTC)
  3. Jika kadar pendanaan positif (kedudukan panjang membayar pendek) — anda menerima pendanaan setiap 8 jam

Pada kadar pendanaan purata 0.01% setiap 8 jam, itu adalah ~0.03% sehari atau ~11% setahun tanpa risiko arah. Semasa pasaran lembu, kadar pendanaan boleh naik ke 0.05-0.1% setiap 8 jam — itu sudah 55-110% setahun.

Risiko: pendanaan negatif (pasaran berbalik), likuidasi kedudukan pendek semasa kenaikan harga yang tajam (penampan margin diperlukan), dan yuran bursa.

Sehingga Mac 2026, kadar pendanaan BTC purata telah stabil pada kira-kira ~0.015% per 8 jam — kira-kira 50% lebih tinggi daripada tahap 2024.

B. Arbitraj Antara Bursa

Syiling yang sama, dua bursa, harga berbeza. Sebabnya — perbezaan dalam kecairan, komposisi pedagang, dan kelajuan kemaskini buku pesanan.

Contoh: BTC di Binance: 87,150.BTCdiBybit:87,150. BTC di Bybit: 87,175. Spread: $25 (0.029%).

Strategi: beli di Binance, jual di Bybit. Masalah: pada masa kedua-dua pesanan dilaksanakan, spread mungkin telah hilang. Penyelesaian: kekalkan baki di kedua-dua bursa dan laksanakan serentak.

Yuran tipikal:

  • Binance: ~0.075% pengambil (dengan diskaun ~0.05%)
  • Bybit: ~0.03% pengambil (VIP)
  • Jumlah: ~0.08%

Ini bermaksud spread mesti melebihi 0.08% agar strategi menguntungkan. Pada tahun 2026, spread sedemikian berlaku:

  • Pada pasangan kurang cair (altcoin) — secara kerap
  • Pada pasangan utama (BTC, ETH) — hanya semasa momen turun naik tinggi
  • Antara CEX dan DEX — lebih kerap, tetapi dengan risiko MEV dan gelinciran

Tanpa ko-lokasi, latensi API adalah 10-100 ms. Dengan rangkaian yang dioptimumkan — ~1 ms. Kebanyakan pedagang runcit beroperasi dalam julat 100-500 ms, yang mencukupi untuk banyak strategi arbitraj tetapi tidak mencukupi untuk bersaing dengan institusi.

C. Dagangan Pasangan dengan Leverage

Dagangan pasangan klasik pada dua aset berbeza menggunakan leverage. Ini adalah yang paling kompleks daripada tiga strategi — dan berpotensi paling menguntungkan.

Mekanisme menggunakan pasangan SOL/AVAX sebagai contoh:

  1. Kira nisbah lindung nilai β (cth., β = 1.3)
  2. Apabila z-skor > +2: pendek SOL, panjang AVAX × β
  3. Apabila z-skor < -2: panjang SOL, pendek AVAX × β
  4. Keluar: |z-skor| < 0.5 atau tamat masa (cth., 30 hari)

Dengan leverage 3x pada setiap bahagian dan purata pembalikan spread 2σ → 3σ:

  • Pulangan sasaran setiap dagangan: ~3-6%
  • Kekerapan purata: 2-4 dagangan sebulan setiap pasangan
  • Pulangan tahunan dijangka: 30-60% (sebelum komisen dan gelinciran)

Risiko utama: korelasi boleh rosak pada masa yang paling tidak menguntungkan (biasanya semasa kejatuhan pasaran). Lebih lanjut mengenai ini dalam bahagian 8.

5. Penapis Kalman untuk Nisbah Lindung Nilai Adaptif

Mengapa Nisbah Lindung Nilai Statik Adalah Masalah

Pendekatan klasik: anggarkan β melalui OLS pada tetingkap sejarah dan tetapkan. Masalah: β berubah dari masa ke masa. Pasaran kripto sangat tidak pegun — peralihan naratif (musim DeFi → hype NFT → token AI) mengubah hubungan asas antara aset.

Menggunakan OLS bergulir (regresi bergulir) adalah separuh langkah. Anda perlu memilih panjang tetingkap: terlalu pendek — hingar; terlalu panjang — lag. Penapis Kalman menyelesaikan masalah ini dengan elegan.

Penapis Kalman

Model Ruang Keadaan

Kami mewakili hubungan antara Y(t) dan X(t) sebagai model linear dengan pekali berubah-ubah masa:

Persamaan pemerhatian:

Y(t) = α(t) + β(t) · X(t) + ε(t),   ε(t) ~ N(0, R)

Persamaan keadaan:

[α(t+1), β(t+1)]ᵀ = [α(t), β(t)]ᵀ + w(t),   w(t) ~ N(0, Q)

Parameter α(t) dan β(t) dianggap sebagai keadaan tersembunyi yang melayang perlahan (jalan rawak). Penapis Kalman menganggarkan keadaan tersembunyi ini secara optimum daripada pemerhatian yang berhingar.

  • R (hingar pemerhatian) — varians hingar pemerhatian. Semakin besar R, semakin perlahan penapis bertindak balas terhadap data baharu.
  • Q (hingar keadaan) — matriks kovarians hingar keadaan. Semakin besar Q, semakin pantas penapis menyesuaikan diri.

Nisbah Q/R menentukan "kehalusan" penapis — analog dengan memilih panjang tetingkap dalam OLS bergulir, tetapi tanpa pemotongan data keras.

Kelebihan Berbanding OLS Bergulir

Spread yang dikira menggunakan penapis Kalman jauh lebih pegun dan lebih pembalikan min berbanding spread daripada regresi bergulir. Penapis Kalman menggunakan semua pemerhatian lalu dengan pemberat yang merosot secara eksponen, dan bukannya memotong data pada panjang tetingkap tetap. Selain itu, penapis Kalman tidak memerlukan penyetelan parameter "panjang tetingkap" — sebaliknya, ia secara automatik mengkalibrasi keseimbangan antara inersia dan keadaptifan melalui matriks Q dan R.

Pelaksanaan dengan filterpy

import numpy as np
from filterpy.kalman import KalmanFilter

def create_kalman_filter(
    delta: float = 1e-4,
    obs_noise: float = 1.0
) -> KalmanFilter:
    """
    Creates a Kalman filter for adaptive hedge ratio estimation.

    delta: state noise variance (Q = delta * I).
           Larger delta → faster adaptation, more noise.
    obs_noise: observation noise variance (R).
    """
    kf = KalmanFilter(dim_x=2, dim_z=1)

    kf.x = np.zeros((2, 1))

    kf.F = np.eye(2)

    kf.P = np.eye(2) * 1000

    kf.Q = np.eye(2) * delta

    kf.R = np.array([[obs_noise]])

    return kf

def estimate_hedge_ratio(
    prices_y: np.ndarray,
    prices_x: np.ndarray,
    delta: float = 1e-4,
    obs_noise: float = 1.0
) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
    """
    Estimates the adaptive hedge ratio using a Kalman filter.

    Returns:
        alphas: array of intercepts (α)
        betas: array of hedge ratios (β)
        spreads: array of spreads Y - α - β*X
    """
    n = len(prices_y)
    kf = create_kalman_filter(delta, obs_noise)

    alphas = np.zeros(n)
    betas = np.zeros(n)
    spreads = np.zeros(n)

    for t in range(n):
        kf.H = np.array([[1.0, prices_x[t]]])

        kf.predict()

        kf.update(np.array([[prices_y[t]]]))

        alphas[t] = kf.x[0, 0]
        betas[t] = kf.x[1, 0]
        spreads[t] = prices_y[t] - kf.x[0, 0] - kf.x[1, 0] * prices_x[t]

    return alphas, betas, spreads

Parameter delta adalah kunci. Untuk pasangan kripto dengan turun naik tinggi (memekoin, alt kecil-cap), gunakan delta = 1e-3. Untuk pasangan stabil (BTC/ETH, SOL/AVAX) — delta = 1e-5.

6. Isyarat Kemasukan dan Keluar

Ambang Z-Skor

Logik isyarat asas:

def generate_signals(
    spreads: np.ndarray,
    lookback: int = 60,
    entry_z: float = 2.0,
    exit_z: float = 0.5,
    stop_z: float = 4.0
) -> np.ndarray:
    """
    Generates trading signals based on spread z-score.

    Returns array: +1 (long spread), -1 (short spread), 0 (flat)
    """
    signals = np.zeros(len(spreads))
    position = 0

    for t in range(lookback, len(spreads)):
        window = spreads[t - lookback:t]
        mu = np.mean(window)
        sigma = np.std(window)

        if sigma < 1e-10:
            continue

        z = (spreads[t] - mu) / sigma

        if position == 0:
            if z > entry_z:
                position = -1  # Short spread (short Y, long X)
            elif z < -entry_z:
                position = 1   # Long spread (long Y, short X)
        else:
            if position == 1 and z > -exit_z:
                position = 0
            elif position == -1 and z < exit_z:
                position = 0
            elif abs(z) > stop_z:
                position = 0

        signals[t] = position

    return signals

Penapis Momentum

Isyarat pembalikan min tulen boleh ditingkatkan dengan penapis:

  1. Penapis momentum: jangan buka kedudukan jika spread terus menyimpang. Tunggu spread berbalik sebelum masuk. Secara teknikal: z-skor telah melintasi ambang, tetapi perubahan spread semasa sudah diarahkan ke arah min.

  2. Penapis turun naik: tingkatkan ambang kemasukan semasa tempoh turun naik tinggi. Apabila pasaran panik, z-skor boleh kekal melebihi 3σ selama berminggu-minggu.

  3. Penapis kointegrasi: sebelum setiap dagangan, sahkan bahawa kointegrasi masih berlaku (ujian ADF bergulir). Jika p-nilai > 0.1 — hentikan dagangan.

Keluar Berasaskan Masa

Jika kedudukan telah dibuka lebih lama daripada 2× separuh hayat dan spread belum kembali — tutup secara paksa. Jika spread tidak kembali dalam masa 2× yang dijangkakan, kointegrasi berkemungkinan telah rosak, dan tidak ada yang perlu ditunggu.

7. Backtesting: Melakukannya dengan Betul

Analisis Walk-Forward

Backtest standard (latih pada semua data → uji pada semua data) tidak berguna untuk stat arb. Parameter regresi terlebih sesuai dengan data, dan hasilnya akan terlalu optimistik.

Pendekatan walk-forward:

  1. Bahagikan data kepada tempoh: [latih₁ → uji₁] → [latih₂ → uji₂] → ...
  2. Pada setiap tempoh latihan: anggarkan kointegrasi, kira nisbah lindung nilai, pilih ambang z-skor
  3. Pada tempoh ujian: dagangan dengan parameter tetap
  4. Gabungkan semua tempoh ujian untuk penilaian akhir

Konfigurasi tipikal untuk kripto: latih = 180 hari, uji = 30 hari, langkah = 30 hari.

Backtest strategi spread

Model Kos Transaksi

Untuk kripto, anda perlu mengambil kira:

Komponen Nilai Tipikal Komen
Yuran pembuat 0.02% Pesanan had
Yuran pengambil 0.05-0.075% Pesanan pasaran
Gelinciran 0.01-0.1% Bergantung pada kecairan
Kadar pendanaan ±0.01%/8j Untuk kedudukan niaga hadapan
Spread (bida-tanya) 0.01-0.05% Pada bursa utama

Memasuki dan keluar dari kedudukan pasangan melibatkan 4 dagangan (2 bahagian × masuk + keluar). Jumlah kos: ~0.3-0.5% setiap pusingan. Ini bermaksud keuntungan purata setiap dagangan mesti melebihi 0.5% untuk nilai jangkaan positif.

Model Gelinciran

Model linear: gelinciran = k × (saiz_pesanan / ADV), di mana ADV adalah volum harian purata. Untuk kripto, k ≈ 0.1 untuk syiling top-10 dan k ≈ 0.3-0.5 untuk altcoin.

Model yang lebih realistik adalah impak punca kuasa dua: gelinciran = k × sqrt(saiz_pesanan / ADV). Ia lebih baik mencerminkan struktur mikro pasaran sebenar.

Metrik

def calculate_metrics(returns: np.ndarray, rf: float = 0.04) -> dict:
    """
    Calculates key strategy metrics.
    rf: risk-free rate (annual)
    """
    daily_rf = rf / 365
    excess = returns - daily_rf

    ann_return = np.mean(returns) * 365
    ann_vol = np.std(returns) * np.sqrt(365)

    sharpe = (ann_return - rf) / ann_vol if ann_vol > 0 else 0

    cumulative = np.cumprod(1 + returns)
    running_max = np.maximum.accumulate(cumulative)
    drawdowns = (cumulative - running_max) / running_max
    max_dd = np.min(drawdowns)

    calmar = ann_return / abs(max_dd) if max_dd != 0 else 0

    win_rate = np.mean(returns > 0) if len(returns) > 0 else 0

    gains = returns[returns > 0].sum()
    losses = abs(returns[returns < 0].sum())
    profit_factor = gains / losses if losses > 0 else float('inf')

    return {
        'annual_return': f'{ann_return:.1%}',
        'annual_volatility': f'{ann_vol:.1%}',
        'sharpe_ratio': f'{sharpe:.2f}',
        'max_drawdown': f'{max_dd:.1%}',
        'calmar_ratio': f'{calmar:.2f}',
        'win_rate': f'{win_rate:.1%}',
        'profit_factor': f'{profit_factor:.2f}',
    }

Penanda aras untuk stat arb kripto:

  • Sharpe > 1.5 — strategi yang baik
  • Drawdown maks < 15% — risiko yang boleh diterima
  • Calmar > 2.0 — nisbah pulangan/drawdown yang cemerlang
  • Faktor keuntungan > 1.5 — kelebihan yang mampan

8. Masalah Dunia Sebenar

Gelinciran dan Kecairan

Dalam backtest, anda masuk sekelika pada harga pertengahan. Dalam realiti — tidak begitu. Pada altcoin dengan volum harian 5J,pesanan5J, pesanan 50K boleh menggerakkan harga sebanyak 0.2-0.5%. Untuk strategi pasangan, itu adalah gelinciran berganda (dua bahagian), dan boleh memakan semua keuntungan.

Penyelesaian: gunakan pesanan had (pembuat, bukan pengambil), bahagikan pesanan kepada bahagian (TWAP/VWAP), dan hadkan saiz kedudukan secara ketat berbanding ADV (maksimum 1-2% daripada volum harian).

Risiko Kadar Pendanaan

Dengan dagangan basis, anda menerima kadar pendanaan, tetapi ia boleh menjadi negatif. Dalam pasaran bearish Disember 2022, kadar pendanaan BTC adalah -0.02% setiap 8 jam — jika anda berada dalam kedudukan "panjang spot + pendek perp", anda membayar 60/hariper60/hari per 100K kedudukan.

Perlindungan: pantau kadar pendanaan dalam masa nyata dan tutup kedudukan apabila kadar berbalik. Pendekatan yang lebih maju adalah arbitraj kadar pendanaan antara bursa (panjang di bursa dengan pendanaan rendah, pendek di bursa dengan pendanaan tinggi).

Keruntuhan Korelasi dalam Krisis

Mac 2020, Mei 2021, November 2022, Ogos 2024 — dalam setiap kejatuhan kripto, korelasi rosak. Lebih tepat lagi, korelasi menguat (semua jatuh bersama), tetapi kointegrasi rosak — spread boleh terbang ke 10σ dan tidak pernah kembali.

Ini adalah tumit Achilles dagangan pasangan. Strategi memperoleh jumlah kecil secara konsisten, kemudian kehilangan jumlah besar dalam satu hari. Profil klasik "mengutip syiling di hadapan penggelek jalan."

Perlindungan:

  1. Stop-loss ketat: tutup kedudukan apabila z-skor > 4σ
  2. Had leverage: maksimum 2-3x pada setiap bahagian
  3. Penapis VIX/turun naik: kurangkan saiz kedudukan apabila turun naik tersirat tinggi
  4. Kepelbagaian: dagangkan 10-20 pasangan serentak, jangan letakkan semua pada satu

Keperluan Modal

Untuk stat arb kripto yang serius:

  • Dagangan basis: dari $50K (pada satu pasangan, satu bursa)
  • Arbitraj antara bursa: dari $100K (baki di dua bursa)
  • Portfolio dagangan pasangan (10 pasangan): dari $200K
  • Tahap institusi: dari $1J

Dengan jumlah yang lebih kecil, komisen dan saiz kedudukan minimum menjadikan strategi tidak berdaya maju.

9. Pelaksanaan Python Hujung ke Hujung

Pengambilan Data

import ccxt
import pandas as pd
import numpy as np
from datetime import datetime, timedelta

def fetch_ohlcv(
    exchange_id: str,
    symbol: str,
    timeframe: str = '1h',
    days: int = 365
) -> pd.DataFrame:
    """Fetch OHLCV data via ccxt."""
    exchange = getattr(ccxt, exchange_id)({
        'enableRateLimit': True,
    })

    since = int((datetime.now() - timedelta(days=days)).timestamp() * 1000)
    all_candles = []

    while True:
        candles = exchange.fetch_ohlcv(
            symbol, timeframe, since=since, limit=1000
        )
        if not candles:
            break
        all_candles.extend(candles)
        since = candles[-1][0] + 1
        if len(candles) < 1000:
            break

    df = pd.DataFrame(
        all_candles,
        columns=['timestamp', 'open', 'high', 'low', 'close', 'volume']
    )
    df['timestamp'] = pd.to_datetime(df['timestamp'], unit='ms')
    df.set_index('timestamp', inplace=True)
    return df

sol = fetch_ohlcv('binance', 'SOL/USDT', '1h', 365)
avax = fetch_ohlcv('binance', 'AVAX/USDT', '1h', 365)

prices = pd.DataFrame({
    'SOL': sol['close'],
    'AVAX': avax['close']
}).dropna()

Ujian Kointegrasi

from statsmodels.tsa.stattools import coint, adfuller
from statsmodels.regression.linear_model import OLS
from statsmodels.tools import add_constant

def test_cointegration(y: np.ndarray, x: np.ndarray) -> dict:
    """
    Full cointegration test with diagnostics.
    """
    score, pvalue, crit_values = coint(y, x)

    x_const = add_constant(x)
    model = OLS(y, x_const).fit()
    alpha, beta = model.params
    spread = y - alpha - beta * x

    adf_stat, adf_pvalue, _, _, adf_crit, _ = adfuller(spread, maxlag=20)

    spread_lag = spread[:-1]
    spread_diff = np.diff(spread)
    spread_lag_const = add_constant(spread_lag)
    hl_model = OLS(spread_diff, spread_lag_const).fit()
    theta = -hl_model.params[1]
    half_life = np.log(2) / theta if theta > 0 else np.inf

    return {
        'coint_pvalue': pvalue,
        'cointegrated': pvalue < 0.05,
        'hedge_ratio': beta,
        'intercept': alpha,
        'adf_statistic': adf_stat,
        'adf_pvalue': adf_pvalue,
        'half_life_hours': half_life,
        'half_life_days': half_life / 24,
        'spread_mean': np.mean(spread),
        'spread_std': np.std(spread),
    }

result = test_cointegration(
    prices['SOL'].values,
    prices['AVAX'].values
)
print(f"Cointegration: {result['cointegrated']} "
      f"(p-value: {result['coint_pvalue']:.4f})")
print(f"Hedge ratio: {result['hedge_ratio']:.4f}")
print(f"Half-life: {result['half_life_days']:.1f} days")

Penapis Kalman + Backtester

from filterpy.kalman import KalmanFilter

class PairsBacktester:
    """
    Walk-forward backtester for pairs trading
    with Kalman filter.
    """

    def __init__(
        self,
        prices_y: np.ndarray,
        prices_x: np.ndarray,
        kalman_delta: float = 1e-4,
        obs_noise: float = 1.0,
        entry_z: float = 2.0,
        exit_z: float = 0.5,
        stop_z: float = 4.0,
        lookback: int = 60,
        fee_rate: float = 0.001,    # 0.1% round trip per leg
        slippage_rate: float = 0.0005,  # 0.05% slippage per leg
    ):
        self.prices_y = prices_y
        self.prices_x = prices_x
        self.n = len(prices_y)
        self.kalman_delta = kalman_delta
        self.obs_noise = obs_noise
        self.entry_z = entry_z
        self.exit_z = exit_z
        self.stop_z = stop_z
        self.lookback = lookback
        self.fee_rate = fee_rate
        self.slippage_rate = slippage_rate

    def run(self) -> pd.DataFrame:
        """Run the backtest. Returns a DataFrame with results."""
        kf = KalmanFilter(dim_x=2, dim_z=1)
        kf.x = np.zeros((2, 1))
        kf.F = np.eye(2)
        kf.P = np.eye(2) * 1000
        kf.Q = np.eye(2) * self.kalman_delta
        kf.R = np.array([[self.obs_noise]])

        alphas = np.zeros(self.n)
        betas = np.zeros(self.n)
        spreads = np.zeros(self.n)

        for t in range(self.n):
            kf.H = np.array([[1.0, self.prices_x[t]]])
            kf.predict()
            kf.update(np.array([[self.prices_y[t]]]))
            alphas[t] = kf.x[0, 0]
            betas[t] = kf.x[1, 0]
            spreads[t] = (
                self.prices_y[t] - kf.x[0, 0]
                - kf.x[1, 0] * self.prices_x[t]
            )

        positions = np.zeros(self.n)
        z_scores = np.zeros(self.n)
        position = 0

        for t in range(self.lookback, self.n):
            window = spreads[t - self.lookback:t]
            mu = np.mean(window)
            sigma = np.std(window)
            if sigma < 1e-10:
                continue

            z = (spreads[t] - mu) / sigma
            z_scores[t] = z

            if position == 0:
                if z > self.entry_z:
                    position = -1
                elif z < -self.entry_z:
                    position = 1
            else:
                if position == 1 and z > -self.exit_z:
                    position = 0
                elif position == -1 and z < self.exit_z:
                    position = 0
                elif abs(z) > self.stop_z:
                    position = 0

            positions[t] = position

        spread_returns = np.diff(spreads) / np.abs(
            spreads[:-1] + 1e-10
        )
        pnl = np.zeros(self.n)

        for t in range(1, self.n):
            if positions[t - 1] != 0:
                raw_return = positions[t - 1] * spread_returns[t - 1]
                pnl[t] = raw_return

                if positions[t] != positions[t - 1]:
                    total_cost = 2 * (self.fee_rate + self.slippage_rate)
                    pnl[t] -= total_cost

        return pd.DataFrame({
            'price_y': self.prices_y,
            'price_x': self.prices_x,
            'alpha': alphas,
            'beta': betas,
            'spread': spreads,
            'z_score': z_scores,
            'position': positions,
            'pnl': pnl,
            'cumulative_pnl': np.cumsum(pnl),
        })

bt = PairsBacktester(
    prices_y=prices['SOL'].values,
    prices_x=prices['AVAX'].values,
    kalman_delta=1e-4,
    entry_z=2.0,
    exit_z=0.5,
    stop_z=4.0,
    lookback=60,
    fee_rate=0.001,
    slippage_rate=0.0005,
)
results = bt.run()

daily_pnl = results['pnl'].resample('D').sum() if hasattr(
    results.index, 'freq'
) else results['pnl']
metrics = calculate_metrics(daily_pnl.values)
for k, v in metrics.items():
    print(f'{k}: {v}')

Rangka Dagangan Langsung

import ccxt
import asyncio
import logging

logger = logging.getLogger(__name__)

class LivePairsTrader:
    """
    Minimal skeleton for live pairs trading.
    For production: add retry logic, monitoring,
    alerts, balance reconciliation.
    """

    def __init__(
        self,
        exchange_id: str,
        symbol_y: str,
        symbol_x: str,
        api_key: str,
        secret: str,
        position_size_usd: float = 1000.0,
        entry_z: float = 2.0,
        exit_z: float = 0.5,
    ):
        self.exchange = getattr(ccxt, exchange_id)({
            'apiKey': api_key,
            'secret': secret,
            'enableRateLimit': True,
        })
        self.symbol_y = symbol_y
        self.symbol_x = symbol_x
        self.position_size = position_size_usd
        self.entry_z = entry_z
        self.exit_z = exit_z
        self.position = 0  # +1, -1, 0

        self.kf = create_kalman_filter(delta=1e-4)
        self.spread_history = []

    async def update(self):
        """One update cycle."""
        ticker_y = self.exchange.fetch_ticker(self.symbol_y)
        ticker_x = self.exchange.fetch_ticker(self.symbol_x)
        price_y = ticker_y['last']
        price_x = ticker_x['last']

        self.kf.H = np.array([[1.0, price_x]])
        self.kf.predict()
        self.kf.update(np.array([[price_y]]))

        alpha = self.kf.x[0, 0]
        beta = self.kf.x[1, 0]
        spread = price_y - alpha - beta * price_x
        self.spread_history.append(spread)

        if len(self.spread_history) < 60:
            logger.info(f"Warming up: {len(self.spread_history)}/60")
            return

        window = np.array(self.spread_history[-60:])
        z = (spread - np.mean(window)) / np.std(window)

        logger.info(
            f"β={beta:.4f} spread={spread:.4f} z={z:.2f} "
            f"pos={self.position}"
        )

        new_position = self.position

        if self.position == 0:
            if z > self.entry_z:
                new_position = -1
            elif z < -self.entry_z:
                new_position = 1
        else:
            if self.position == 1 and z > -self.exit_z:
                new_position = 0
            elif self.position == -1 and z < self.exit_z:
                new_position = 0

        if new_position != self.position:
            await self._execute_trade(
                new_position, price_y, price_x, beta
            )
            self.position = new_position

    async def _execute_trade(
        self, target: int, price_y: float, price_x: float,
        beta: float
    ):
        """Execute a pairs trade."""
        if target == 0:
            logger.info("Closing position")
        elif target == 1:
            size_y = self.position_size / price_y
            size_x = (self.position_size * beta) / price_x
            logger.info(
                f"Long spread: buy {size_y:.4f} {self.symbol_y}, "
                f"sell {size_x:.4f} {self.symbol_x}"
            )
        elif target == -1:
            size_y = self.position_size / price_y
            size_x = (self.position_size * beta) / price_x
            logger.info(
                f"Short spread: sell {size_y:.4f} {self.symbol_y}, "
                f"buy {size_x:.4f} {self.symbol_x}"
            )

    async def run_loop(self, interval_seconds: int = 60):
        """Main loop."""
        logger.info(
            f"Starting live trading: "
            f"{self.symbol_y}/{self.symbol_x}"
        )
        while True:
            try:
                await self.update()
            except Exception as e:
                logger.error(f"Error in update: {e}")
            await asyncio.sleep(interval_seconds)

Sebagai Penutup

Arbitraj statistik bukan cawan suci. Ia adalah kraf. Antara "Saya tahu apa itu kointegrasi" dan "Saya mempunyai strategi yang berfungsi secara konsisten" terdapat jurang kejuruteraan yang besar: pemprosesan data yang betul, backtesting walk-forward yang tepat, model gelinciran yang realistik, pemantauan masa nyata.

Pasaran mata wang kripto masih menawarkan lebih banyak peluang untuk stat arb berbanding pasaran tradisional — kecairan berfragmen, infrastruktur pasaran yang belum matang, dan instrumen unik seperti niaga hadapan kekal dengan kadar pendanaan mewujudkan ketidakcekapan yang telah lama diarbitrajkan ke sifar di NYSE.

Tetapi tingkap itu sedang menutup. Pemain institusi memasuki pasaran kripto, modal arbitraj semakin berkembang (mengikut anggaran, jumlah modal arbitraj di bursa kripto meningkat sebanyak 215% pada 2025), dan margin semakin berkurang. Jika anda ingin melakukan stat arb dalam kripto — sebaiknya mulakan sekarang.

Semua kod dalam artikel ini tersedia sebagai titik permulaan. Jangan jalankannya dalam persekitaran produksi tanpa ujian yang serius. Dan ingat: satu-satunya strategi yang dijamin berkesan adalah pengurusan risiko.


Karya akademik utama:

  • Engle, R.F. & Granger, C.W.J. (1987). "Co-Integration and Error Correction: Representation, Estimation, and Testing". Econometrica, 55(2), 251-276.
  • Gatev, E., Goetzmann, W.N. & Rouwenhorst, K.G. (2006). "Pairs Trading: Performance of a Relative-Value Arbitrage Rule". The Review of Financial Studies, 19(3), 797-827.
  • Vidyamurthy, G. (2004). Pairs Trading: Quantitative Methods and Analysis. Wiley.
  • Avellaneda, M. & Lee, J.H. (2010). "Statistical Arbitrage in the US Equities Market". Quantitative Finance, 10(7), 761-782.
  • Frontiers (2026). "Deep learning-based pairs trading: real-time forecasting of co-integrated cryptocurrency pairs". Frontiers in Applied Mathematics and Statistics.

Pustaka berguna:

  • statsmodels — kointegrasi, ADF, OLS
  • filterpy — penapis Kalman
  • ccxt — API bersatu untuk 100+ bursa
  • arbitragelab — pustaka khusus untuk dagangan pasangan (OU, Kalman, kopula)
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.