← Makalelere geri dön
March 19, 2026
5 dakikalık okuma

Kripto Piyasalarında İstatistiksel Arbitraj ve Çift Ticareti: Kointegrasyon'dan Kalman Filtresine

Kripto Piyasalarında İstatistiksel Arbitraj ve Çift Ticareti: Kointegrasyon'dan Kalman Filtresine
#istatistiksel arb
#çift ticareti
#kointegrasyon
#kalman
#arbitraj
#algo trading
#kripto

1987 yılında Morgan Stanley'deki bir grup fizikçi, hiçbirinin bankanın yönetimine tam olarak açıklayamadığı bir algoritmayla hisse senedi çiftleri ticareti yaparak tek bir yılda 50 milyon dolar kazandı. Yönetim itiraz etmedi. 2026'da aynı fikri kripto borsalarında hayata geçirebilirsiniz — sürekli vadeli işlemler, 7/24 piyasa ve Nunzio Tartaglia'nın imreneceği likidite ile. Ancak bir sorun var: Ford ve GM hisselerinde internet öncesi dönemde işe yarayan şey, BTC'nin bir gecede %20 düşebildiği ve fonlama oranının tek bir blok içinde tersine dönebildiği bir dünya için ciddi bir uyarlama gerektiriyor.

Bu makale, kripto piyasaları için istatistiksel arbitraj ve çift ticaretinin tam bir analizidir. Matematiksel teoriden (kointegrasyon, Ornstein-Uhlenbeck süreci, Kalman filtresi) gerçek veriler üzerinde çalıştırabileceğiniz Python koduna kadar. Üslup mühendislik odaklıdır: formülleri açıklıyoruz, kodu gösteriyoruz ve tuzakları gizlemiyoruz.

1. Kısa Bir Tarih: Bir Cizvit Rahipten Quant'lara

Modern anlamıyla istatistiksel arbitraj, 1980'lerin ortasında Morgan Stanley'deki işlem masasında doğdu. Nunzio Tartaglia — fizik doktorasına sahip eski bir Cizvit rahibi — matematikçilerden, fizikçilerden ve bilgisayar bilimcilerinden oluşan bir ekip kurdu. Amaç: geleneksel yatırımcıların göremediği hisse senedi fiyatlarındaki kalıpları bulmak.

Fikir çarpıcı biçimde basitti. Coca-Cola ve Pepsi hisseleri tarihsel olarak birlikte hareket ediyorsa (ki bu mantıklı — farklı renkteki şişelerde aynı şekerli suyu satıyorlar), fiyatlarındaki bir sapma geçici bir anomalidir. Geri kalanı alın, öndekini satın, yakınsamayı bekleyin, kârı kilitleyin. Piyasadan bağımsız bir strateji: piyasanın yönü bizi ilgilendirmez.

Tartaglia'nın ekibinde daha sonra tüm Wall Street'i dönüştürecek isimler vardı:

  • David Shaw — daha sonra en büyük niceliksel hedge fonlarından biri olan D.E. Shaw & Co.'yu kurdu
  • Peter Muller — Morgan Stanley içindeki efsanevi istatistiksel arb grubu PDT Partners'ı kurdu
  • Robert Frey — daha sonra Jim Simons'ın altında Renaissance Technologies'e katıldı

Grup bir yatırım bankası içinde araştırma laboratuvarı olarak faaliyet gösterdi. Otomasyon o dönemin en iyisiydi: VAX kümeleri sinyal üretiyor, uygulama terminaller üzerinden gerçekleşiyordu. En iyi yıllarda (1987-1988), strateji on milyonlarca dolar kazandı. Ardından iki üst üste zarar yılı geldi ve 1989'da Morgan Stanley masayı kapattı.

Ancak fikir çoktan yayılmıştı. Grubun mezunları çift ticareti kavramını tüm Wall Street'e yaydı. Gatev, Goetzmann ve Rouwenhorst 2006'da klasik akademik makaleyi yayımladı — "Çift Ticareti: Göreceli Değer Arbitraj Kuralının Performansı" — basit bir çift ticareti stratejisinin 1962'den 2002'ye kadar ABD hisse senetlerinde tutarlı biçimde ~%11 yıllık getiri sağladığını gösterdi. Etkin piyasa hipotezine ikna edici bir yanıttı: piyasa bir bütün olarak etkin olabilir, ancak belirli varlık çiftleri sistematik olarak dengeden sapıyor.

Bugün istatistiksel arbitraj, yüzlerce milyar dolarlık AUM'a sahip bir sektördür ve kripto piyasaları özellikle verimli bir zemin sunmaktadır: parçalanmış likidite, olgunlaşmamış piyasa yapısı, günün 24 saati ticaret ve geleneksel piyasalarda hiç var olmayan bir araç olan fonlama oranlı sürekli vadeli işlemler.

2. Matematiksel Temel: Korelasyon Bir Tuzaktır

Korelasyon Neden İşe Yaramaz

Her ikinci başlangıç düzeyindeki quant'ın yaptığı hatayla başlayalım: "BTC ve ETH 0,85 katsayısıyla ilişkili, dolayısıyla çifti işlem yapabiliriz." Hayır. Yapamazsınız. Ya da yapabilirsiniz — ama para kaybedersiniz.

Korelasyon, iki varlığın getirilerindeki doğrusal ilişkiyi ölçer. İki varlık mükemmel biçimde ilişkili olabilir, ancak fiyatları sonsuza kadar birbirinden uzaklaşabilir. Klasik örnek: ilişkili artışlara sahip iki rastgele yürüyüş — yüksek korelasyona rağmen sonsuz biçimde ayrışırlar. Hiç gelmeyecek bir "yakınsama" bekleyerek pozisyon açacaksınız.

Kointegrasyon ile korelasyon

Kointegrasyon: Doğru Yaklaşım

Kointegrasyon, getiriler değil fiyat serileri özelliğidir. X(t) ve Y(t) iki durağan olmayan seri, eğer şu doğrusal kombinasyon varsa kointegre olarak adlandırılır:

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

durağan olması — yani bir ortalama değerine geri dönmesi anlamına gelir. β katsayısı riskten korunma oranı olarak adlandırılır ve S(t) ise spread'dir.

Sezgi: BTC ve ETH aya fırlatılabilir ya da uçuruma düşebilir, ancak eğer farkları (uygun şekilde ölçeklendirilmiş) sabit bir düzey etrafında salınıyorsa — bu kointegrasyon demektir. Ve ticaret için tam olarak ihtiyacımız olan bu.

Engle-Granger Testi (1987)

Robert Engle ve Clive Granger'ın 2003'te Nobel Ekonomi Ödülü'nü aldığı iki adımlı prosedür:

Adım 1. OLS regresyonu: Y(t) = α + β · X(t) + ε(t). β riskten korunma oranını ve ε(t) artıklarını elde ederiz.

Adım 2. ε(t) artıkları üzerinde ADF (Artırılmış Dickey-Fuller) testi. Boş hipotez: ε(t) birim köke sahiptir (durağan değil). p-değeri < 0,05 ise H₀'ı reddederiz — seriler kointegre edilmiştir.

Önemli: kointegrasyon testi için standart ADF kritik değerlerini kullanamazsınız. Engle-Granger kritik değerleri Monte Carlo simülasyonu yoluyla türetilmiş olup OLS regresyonundaki değişkenler arasındaki bağımlılığı hesaba katar. statsmodels'ta bu, coint() fonksiyonunda doğru biçimde uygulanmaktadır.

Johansen Testi

İkiden fazla değişkene sahip sistemler için (örneğin BTC, ETH ve SOL aynı anda), Johansen testi kullanılır. Sistemdeki tüm kointegrasyon ilişkilerini bulur ve birden fazla varlıktan oluşan portföyler oluşturmaya olanak tanır. Test, bir VAR (vektör otoregresyon) modeline dayanır ve iki kriter kullanır: iz istatistiği ve maksimum özdeğer istatistiği.

Ornstein-Uhlenbeck Süreci

Spread kointegre ise, dinamikleri bir Ornstein-Uhlenbeck (OU) süreci olarak modellenebilir:

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

burada:

  • θ — ortalamaya dönüş hızı
  • μ — uzun vadeli ortalama düzey
  • σ — oynaklık
  • W(t) — bir Wiener süreci (Brownian hareketi)

OU süreci parametrelerinden, ortalamaya dönüşün yarı ömrü hesaplanır:

t½ = ln(2) / θ

Yarı ömür kritik öneme sahip bir metriktir. t½ = 5 gün ise, spread yaklaşık 5 günde ortalamasına geri döner. t½ = 200 gün ise, yakınsama beklerken yarım yıl boyunca bir pozisyonda oturursunuz. Kripto stratejileri için optimal yarı ömür 1-30 gün'dür. Daha kısa — çok hızlı, komisyonlar kârı yer bitirir. Daha uzun — çok yavaş, yapısal rejim değişikliği riski.

Pratikte θ, regresyon yoluyla tahmin edilir:

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

burada θ = -b ve t½ = -ln(2) / b.

Z-Skoru Normalleştirmesi

Ticaret sinyalleri üretmek için spread normalleştirilir:

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

burada μ̂ ve σ̂, spread'in hareketli ortalaması ve standart sapmasıdır. Z-skoru, spread'in ortalamadan kaç standart sapma uzaklaştığını gösterir. Tipik giriş eşikleri: |z| > 2,0; çıkış eşikleri: |z| < 0,5.

3. Kripto Piyasasında Çift Seçimi

BTC-ETH: (Bazen) İşe Yarayan Klasik

BTC ve ETH en açık ve en likit çifttir. Getiri korelasyonu tutarlı biçimde 0,7'nin üzerindedir. Ancak kointegrasyon farklı bir meseledir. Ortaya çıkar ve kaybolur:

  • 2023'teki yatay piyasalarda BTC/ETH güvenilir biçimde kointegre edildi (p-değeri < 0,01)
  • 2024-2025 ayrışmasında (BTC, ETF akışlarıyla yükseldi, ETH geride kaldı), kointegrasyon bozuldu
  • 2026 başında, ETH ETF lansmanı ve ETH/BTC oranının toparlanmasının ardından, kointegrasyon yeniden istikrar kazandı

Sonuç: kointegrasyon sürekli izlenmelidir. Regresyon parametreleri hareketli bir pencerede yeniden hesaplanır ve ADF testi p-değeri eşiği aşarsa strateji otomatik olarak kapanır.

Sektör Çiftleri

Kripto piyasası sektöre göre kullanışlı biçimde bölünmüştür ve sektör içi çiftler genellikle istikrarlı bir kointegrasyon sergiler:

Sektör Örnek Çiftler Özellikler
L1 blok zincirleri SOL/AVAX, NEAR/APT Yüksek likidite, yarı ömür 3-10 gün
DeFi protokolleri AAVE/COMP, UNI/SUSHI Orta likidite, yarı ömür 5-15 gün
L2 çözümleri ARB/OP, MATIC/MANTA Yüksek spread oynaklığı
Memecoin'ler DOGE/SHIB Öngörülemeyen ama eğlenceli (önerilmez)

İstatistiksel arb için en iyi çiftler üç özelliğe sahiptir: (1) 6 aydan uzun tarihsel pencere üzerinde istikrarlı kointegrasyon, (2) yeterli likidite — varlık başına günlük hacim >10 milyon dolar, (3) makul yarı ömür — 1'den 30 güne kadar.

Spot ile Sürekli Vadeli İşlemler (Basis)

Ayrı bir "çift" kategorisi, aynı varlığın spot ve vadeli piyasalardaki halidir. Sürekli vadeli işlem fiyatı ile spot fiyat arasındaki fark (basis), tanım gereği durağandır: fonlama oranı mekanizması onu sıfıra doğru geri iter. Bu, basis ticaretini kripto'daki en güvenilir istatistiksel arb biçimlerinden biri yapar.

4. Üç Ticaret Yaklaşımı

A. Basis Ticareti: Spot-Vadeli ve Fonlama Oranı Taşıma

Kripto'da istatistiksel arb'ın "en saf" biçimi. Mekanik:

  1. Varlığı spot'ta satın alın (örneğin 1 BTC)
  2. Sürekli vadeli işlemde kısa pozisyon açın (1 BTC)
  3. Fonlama oranı pozitifse (longlar shortlara ödüyor) — her 8 saatte bir fonlama alırsınız

Her 8 saatte bir ortalama %0,01 fonlama oranıyla, bu günde ~%0,03 veya yönsel risk olmaksızın yıllık ~%11'dir. Boğa piyasalarında fonlama oranı her 8 saatte bir %0,05-0,1'e yükselebilir — bu, yıllık %55-110 demektir.

Riskler: negatif fonlama (piyasa tersine döner), keskin fiyat yükselişinde kısa pozisyonun tasfiyesi (marj tamponu gereklidir) ve borsa ücretleri.

Mart 2026 itibarıyla ortalama BTC fonlama oranı yaklaşık ~%0,015 her 8 saatte bir oranında istikrar kazanmıştır — 2024 seviyelerinin yaklaşık %50 üzerinde.

B. Borsa Çapraz Arbitraj

Aynı coin, iki borsa, farklı fiyatlar. Neden — likidite, yatırımcı kompozisyonu ve emir defteri güncelleme hızlarındaki farklılıklar.

Örnek: Binance'de BTC: 87.150 dolar. Bybit'te BTC: 87.175 dolar. Spread: 25 dolar (%0,029).

Strateji: Binance'de satın alın, Bybit'te satın. Sorun: her iki emir uygulanana kadar spread kaybolmuş olabilir. Çözüm: her iki borsada bakiyeleri koruyun ve eş zamanlı olarak uygulayın.

Tipik ücretler:

  • Binance: ~%0,075 taker (indirimle ~%0,05)
  • Bybit: ~%0,03 taker (VIP)
  • Toplam: ~%0,08

Bu, stratejinin karlı olması için spread'in %0,08'i aşması gerektiği anlamına gelir. 2026'da bu tür spread'ler ortaya çıkar:

  • Daha az likit çiftlerde (altcoin'ler) — düzenli olarak
  • Ana çiftlerde (BTC, ETH) — yalnızca yüksek oynaklık anlarında
  • CEX ile DEX arasında — daha sık, ancak MEV riski ve kayma ile

Co-location olmadan, API gecikmesi 10-100 ms'dir. Optimize edilmiş ağlarla — ~1 ms. Çoğu perakende yatırımcı 100-500 ms aralığında çalışır; bu, birçok arbitraj stratejisi için yeterli ancak kurumlarla rekabet etmek için yetersizdir.

C. Kaldıraçlı Çift Ticareti

İki farklı varlık üzerinde kaldıraç kullanarak klasik çift ticareti. Bu, üç stratejinin en karmaşığıdır — ve potansiyel olarak en karlısıdır.

SOL/AVAX çifti örneğini kullanan mekanik:

  1. Riskten korunma oranı β'yı hesaplayın (örneğin, β = 1,3)
  2. Z-skoru > +2 olduğunda: SOL'u açığa satın, AVAX × β'yı satın alın
  3. Z-skoru < -2 olduğunda: SOL'u satın alın, AVAX × β'yı açığa satın
  4. Çıkış: |z-skoru| < 0,5 veya zaman aşımı (örneğin, 30 gün)

Her leg'de 3x kaldıraç ve ortalama spread dönüşü 2σ → 3σ ile:

  • İşlem başına hedef getiri: ~%3-6
  • Ortalama sıklık: çift başına ayda 2-4 işlem
  • Beklenen yıllık getiri: %30-60 (komisyon ve kayma öncesi)

Ana risk: korelasyon en kötü anda bozulabilir (genellikle piyasa çöküşü sırasında). Bu konuda daha fazlası bölüm 8'de.

5. Uyarlanabilir Riskten Korunma Oranı için Kalman Filtresi

Statik Riskten Korunma Oranı Neden Sorun

Klasik yaklaşım: tarihsel bir pencere üzerinde OLS ile β'yı tahmin edin ve sabitleyin. Sorun: β zamanla değişir. Kripto piyasası özellikle durağan değildir — anlatı değişimleri (DeFi yazı → NFT çılgınlığı → AI token'lar) varlıklar arasındaki temel ilişkileri değiştirir.

Hareketli OLS (hareketli regresyon) kullanmak yarım bir önlemdir. Pencere uzunluğunu seçmek zorundasınız: çok kısa — gürültü; çok uzun — gecikme. Kalman filtresi bu sorunu zarif biçimde çözer.

Kalman filtresi

Durum-Uzay Modeli

Y(t) ve X(t) arasındaki ilişkiyi zamana göre değişen katsayılarla doğrusal bir model olarak temsil ederiz:

Gözlem denklemi:

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

Durum denklemi:

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

α(t) ve β(t) parametreleri yavaşça sürüklenen (rastgele yürüyüş) bir gizli durum olarak ele alınır. Kalman filtresi bu gizli durumu gürültülü gözlemlerden optimum biçimde tahmin eder.

  • R (gözlem gürültüsü) — gözlem gürültüsünün varyansı. R ne kadar büyük olursa, filtre yeni verilere o kadar yavaş tepki verir.
  • Q (durum gürültüsü) — durum gürültüsünün kovaryans matrisi. Q ne kadar büyük olursa, filtre o kadar hızlı uyum sağlar.

Q/R oranı filtrenin "pürüzsüzlüğünü" belirler — hareketli OLS'de pencere uzunluğunu seçmeye benzer, ancak sert veri kısaltması olmadan.

Hareketli OLS Üzerindeki Avantajları

Kalman filtresi kullanılarak hesaplanan spread'ler, hareketli regresyondan elde edilen spread'lerden önemli ölçüde daha durağan ve ortalamaya dönüşlüdür. Kalman filtresi, verileri sabit bir pencere uzunluğunda kesmek yerine üssel azalan ağırlıklarla tüm geçmiş gözlemleri kullanır. Ayrıca Kalman filtresi, bir "pencere uzunluğu" parametresini ayarlamayı gerektirmez — bunun yerine Q ve R matrisleri aracılığıyla eylemsizlik ile uyarlanabilirlik arasındaki dengeyi otomatik olarak kalibre eder.

filterpy ile Uygulama

import numpy as np
from filterpy.kalman import KalmanFilter

def create_kalman_filter(
    delta: float = 1e-4,
    obs_noise: float = 1.0
) -> KalmanFilter:
    """
    Uyarlanabilir riskten korunma oranı tahmini için Kalman filtresi oluşturur.

    delta: durum gürültüsü varyansı (Q = delta * I).
           Daha büyük delta → daha hızlı uyum, daha fazla gürültü.
    obs_noise: gözlem gürültüsü varyansı (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]:
    """
    Kalman filtresi kullanarak uyarlanabilir riskten korunma oranını tahmin eder.

    Döndürür:
        alphas: sabit terimleri dizisi (α)
        betas: riskten korunma oranları dizisi (β)
        spreads: spread dizisi 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

delta parametresi anahtardır. Yüksek oynaklığa sahip kripto çiftleri için (memecoin'ler, küçük cap alt coin'ler), delta = 1e-3 kullanın. İstikrarlı çiftler için (BTC/ETH, SOL/AVAX) — delta = 1e-5.

6. Giriş ve Çıkış Sinyalleri

Z-Skoru Eşikleri

Temel sinyal mantığı:

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:
    """
    Spread z-skoruna dayalı ticaret sinyalleri üretir.

    Dizi döndürür: +1 (uzun spread), -1 (kısa spread), 0 (düz)
    """
    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  # Kısa spread (Y'yi açığa sat, X'i al)
            elif z < -entry_z:
                position = 1   # Uzun spread (Y'yi al, X'i açığa sat)
        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

Momentum Filtreleri

Saf bir ortalamaya dönüş sinyali filtrelerle geliştirilebilir:

  1. Momentum filtresi: spread ayrışmaya devam ediyorsa pozisyon açmayın. Girmeden önce spread'in tersine dönmesini bekleyin. Teknik olarak: z-skoru eşiği geçti, ancak mevcut spread değişimi zaten ortalamanın yönünde.

  2. Oynaklık filtresi: yüksek oynaklık dönemlerinde giriş eşiğini artırın. Piyasa panik yaparken z-skoru haftalarca 3σ'nın üzerinde kalabilir.

  3. Kointegrasyon filtresi: her işlemden önce, kointegrasyon'un hâlâ geçerli olduğunu doğrulayın (hareketli ADF testi). p-değeri > 0,1 ise — ticareti duraklatın.

Zamana Dayalı Çıkışlar

Bir pozisyon yarı ömrünün 2 katından uzun süre açık kalmış ve spread geri dönmemişse — zorla kapatın. Spread beklenen sürenin 2 katı içinde geri dönmemişse, kointegrasyon büyük ihtimalle bozulmuştur ve beklenecek bir şey kalmamıştır.

7. Geriye Dönük Test: Doğru Yapmak

Walk-Forward Analizi

Standart bir geriye dönük test (tüm veriler üzerinde eğitin → tüm veriler üzerinde test edin) istatistiksel arb için işe yaramaz. Regresyon parametreleri verilere aşırı uyumludur ve sonuç iyimser olacaktır.

Walk-forward yaklaşımı:

  1. Verileri dönemlere bölün: [eğitim₁ → test₁] → [eğitim₂ → test₂] → ...
  2. Her eğitim döneminde: kointegrasyon tahmini yapın, riskten korunma oranı hesaplayın, z-skoru eşiklerini seçin
  3. Test döneminde: sabit parametrelerle işlem yapın
  4. Son değerlendirme için tüm test dönemlerini birleştirin

Kripto için tipik yapılandırma: eğitim = 180 gün, test = 30 gün, adım = 30 gün.

Spread stratejisi geriye dönük testi

İşlem Maliyeti Modeli

Kripto için şunları hesaba katmanız gerekir:

Bileşen Tipik Değer Yorum
Maker ücreti %0,02 Limit emirler
Taker ücreti %0,05-0,075 Piyasa emirleri
Kayma %0,01-0,1 Likiditeye bağlı
Fonlama oranı ±%0,01/8s Vadeli işlem pozisyonları için
Spread (alış-satış) %0,01-0,05 Ana borsalarda

Çift pozisyona girmek ve çıkmak 4 işlem içerir (2 leg × giriş + çıkış). Toplam maliyet: gidiş-dönüş başına ~%0,3-0,5. Bu, pozitif beklenen değer için işlem başına ortalama kârın %0,5'i aşması gerektiği anlamına gelir.

Kayma Modeli

Doğrusal model: kayma = k × (emir_büyüklüğü / ADV); burada ADV ortalama günlük hacimdir. Kripto için k ≈ ilk 10 coin'de 0,1 ve altcoin'lerde ≈ 0,3-0,5.

Daha gerçekçi bir model, kare kök etkisidir: kayma = k × sqrt(emir_büyüklüğü / ADV). Gerçek piyasa mikro yapısını daha iyi yansıtır.

Metrikler

def calculate_metrics(returns: np.ndarray, rf: float = 0.04) -> dict:
    """
    Temel strateji metriklerini hesaplar.
    rf: risksiz oran (yıllık)
    """
    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}',
    }

Kripto istatistiksel arb için kıyaslamalar:

  • Sharpe > 1,5 — iyi bir strateji
  • Maksimum düşüş < %15 — kabul edilebilir risk
  • Calmar > 2,0 — mükemmel getiri/düşüş oranı
  • Kâr faktörü > 1,5 — sürdürülebilir avantaj

8. Gerçek Dünya Sorunları

Kayma ve Likidite

Geriye dönük testte, orta fiyattan anında girersiniz. Gerçekte — girmezsiniz. Günlük hacmi 5 milyon dolar olan altcoin'lerde, 50.000 dolarlık bir emir fiyatı %0,2-0,5 oranında hareket ettirebilir. Çift stratejisi için bu iki katı kaymadır (iki leg) ve tüm kârı tüketebilir.

Çözüm: limit emirler kullanın (taker değil maker), emirleri parçalara bölün (TWAP/VWAP) ve ADV'ye göre pozisyon büyüklüğünü kesinlikle sınırlayın (günlük hacmin maksimum %1-2'si).

Fonlama Oranı Riski

Basis ticaretinde fonlama oranı alırsınız, ancak negatife dönebilir. Aralık 2022'nin ayı piyasasında, BTC fonlama oranı her 8 saatte bir -0,02% idi — "uzun spot + kısa sürekli vadeli" pozisyonunda oturuyorsanız, 100.000 dolarlık pozisyon başına günde 60 dolar ödüyordunuz.

Koruma: fonlama oranını gerçek zamanlı olarak izleyin ve oran tersine döndüğünde pozisyonu kapatın. Daha gelişmiş bir yaklaşım, borsalar arası fonlama oranı arbitrajıdır (düşük fonlama oranına sahip borsada uzun, yüksek fonlama oranına sahip borsada kısa).

Kriz Sırasında Korelasyon Bozulması

Mart 2020, Mayıs 2021, Kasım 2022, Ağustos 2024 — her kripto çöküşünde korelasyonlar bozulur. Daha doğrusu, korelasyonlar yoğunlaşır (her şey birlikte düşer), ancak kointegrasyon bozulur — spread 10σ'ya fırlatılabilir ve hiç geri dönmeyebilir.

Bu, çift ticaretinin Aşil topuğudur. Strateji küçük miktarları tutarlı biçimde kazanır, ardından tek bir günde büyük bir miktar kaybeder. Klasik "buharlı silindirin önünde kuruş toplama" profili.

Koruma:

  1. Katı stop-loss: z-skoru > 4σ olduğunda pozisyonu kapatın
  2. Kaldıraç sınırları: her leg'de maksimum 2-3x
  3. VIX/oynaklık filtresi: ima edilen oynaklık yüksek olduğunda pozisyon büyüklüğünü azaltın
  4. Çeşitlendirme: aynı anda 10-20 çift işlem yapın, her şeyi bir çifte koymayın

Sermaye Gereksinimleri

Ciddi kripto istatistiksel arb için:

  • Basis ticareti: 50.000 dolardan (bir çiftte, bir borsada)
  • Borsa çapraz arbitraj: 100.000 dolardan (iki borsada bakiyeler)
  • Çift ticaret portföyü (10 çift): 200.000 dolardan
  • Kurumsal düzey: 1 milyon dolardan

Daha küçük miktarlarda, komisyonlar ve minimum pozisyon boyutları stratejiyi uygulanamaz hale getirir.

9. Uçtan Uca Python Uygulaması

Veri Alımı

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:
    """ccxt aracılığıyla OHLCV verisi al."""
    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()

Kointegrasyon Testi

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:
    """
    Tanılamalarla tam kointegrasyon testi.
    """
    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"Kointegrasyon: {result['cointegrated']} "
      f"(p-değeri: {result['coint_pvalue']:.4f})")
print(f"Riskten korunma oranı: {result['hedge_ratio']:.4f}")
print(f"Yarı ömür: {result['half_life_days']:.1f} gün")

Kalman Filtresi + Geriye Dönük Test Aracı

from filterpy.kalman import KalmanFilter

class PairsBacktester:
    """
    Kalman filtresi ile çift ticareti için
    walk-forward geriye dönük test aracı.
    """

    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,    # leg başına %0,1 gidiş-dönüş
        slippage_rate: float = 0.0005,  # leg başına %0,05 kayma
    ):
        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:
        """Geriye dönük testi çalıştır. Sonuçlarla bir DataFrame döndürü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) * 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}')

Canlı Ticaret İskeleti

import ccxt
import asyncio
import logging

logger = logging.getLogger(__name__)

class LivePairsTrader:
    """
    Canlı çift ticareti için minimal iskelet.
    Üretim için: yeniden deneme mantığı, izleme,
    uyarılar, bakiye mutabakatı ekleyin.
    """

    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):
        """Bir güncelleme döngüsü."""
        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"Isınıyor: {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
    ):
        """Çift ticareti uygula."""
        if target == 0:
            logger.info("Pozisyon kapatılıyor")
        elif target == 1:
            size_y = self.position_size / price_y
            size_x = (self.position_size * beta) / price_x
            logger.info(
                f"Uzun spread: {size_y:.4f} {self.symbol_y} al, "
                f"{size_x:.4f} {self.symbol_x} sat"
            )
        elif target == -1:
            size_y = self.position_size / price_y
            size_x = (self.position_size * beta) / price_x
            logger.info(
                f"Kısa spread: {size_y:.4f} {self.symbol_y} sat, "
                f"{size_x:.4f} {self.symbol_x} al"
            )

    async def run_loop(self, interval_seconds: int = 60):
        """Ana döngü."""
        logger.info(
            f"Canlı ticaret başlıyor: "
            f"{self.symbol_y}/{self.symbol_x}"
        )
        while True:
            try:
                await self.update()
            except Exception as e:
                logger.error(f"Güncelleme hatası: {e}")
            await asyncio.sleep(interval_seconds)

Sonuç Yerine

İstatistiksel arbitraj Kutsal Kase değildir. Bir zanaattır. "Kointegrasyon'un ne olduğunu biliyorum" ile "tutarlı biçimde çalışan bir stratejim var" arasında mühendislik detaylarından oluşan derin bir uçurum bulunur: uygun veri işleme, doğru walk-forward geriye dönük test, gerçekçi kayma modeli, gerçek zamanlı izleme.

Kripto para piyasaları, geleneksel piyasalardan daha fazla istatistiksel arb fırsatı sunmaya devam etmektedir — parçalanmış likidite, olgunlaşmamış piyasa altyapısı ve fonlama oranlı sürekli vadeli işlemler gibi benzersiz araçlar, NYSE'de çoktan sıfıra arbitraj edilmiş olan verimsizlikler yaratmaktadır.

Ancak pencere kapanmaktadır. Kurumsal oyuncular kripto piyasalarına girmekte, arbitraj sermayesi büyümekte (tahminlere göre, kripto borsalarındaki arbitraj sermayesi hacmi 2025'te %215 oranında artmıştır) ve marjlar daralıyor. Kripto'da istatistiksel arb yapacaksanız — şimdi başlamak en iyisi.

Bu makaledeki tüm kod bir başlangıç noktası olarak mevcuttur. Ciddi testler yapmadan üretimde çalıştırmayın. Ve unutmayın: işe yaraması garantili tek strateji risk yönetimidir.


Temel akademik çalışmalar:

  • 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.

Faydalı kütüphaneler:

  • statsmodels — kointegrasyon, ADF, OLS
  • filterpy — Kalman filtresi
  • ccxt — 100'den fazla borsa için birleşik API
  • arbitragelab — çift ticareti için özel kütüphane (OU, Kalman, kopulalar)
Sorumluluk Reddi: Bu makalede sağlanan bilgiler yalnızca eğitim ve bilgilendirme amaçlıdır ve finansal, yatırım veya ticaret tavsiyesi niteliği taşımaz. Kripto para ticareti önemli bir kayıp riski içerir.

Yazarlar

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

Piyasanın Önünde Olun

Özel yapay zeka ticaret içgörüleri, piyasa analizi ve platform güncellemeleri için bültenimize abone olun.

Gizliliğinize saygı duyuyoruz. İstediğiniz zaman abonelikten çıkabilirsiniz.