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

Kademeli Stratejiler: Öncelikli Yürütme ve Yedek Doldurma

Kademeli Stratejiler: Öncelikli Yürütme ve Yedek Doldurma
#algotrading
#orkestrasyon
#portföy
#kademeli
#stratejiler
#slot yönetimi

"Yanılsama Olmadan Backtestler" serisinin finali. N strateji üzerinden M çifte yönelik bir orkestratör nasıl kurulur, öncelik ve yedek yürütme ile kademeli mod nasıl uygulanır, dual_size nasıl seçilir ve strateji portföyleri neden yalnızca PnL toplayarak geri test edilemez.

Strateji Portföyüne Neden İhtiyaç Duyarsınız

Atıl sermaye ile strateji portföyü Birden fazla strateji sınırlı sermaye için rekabet eder — çoğu atıl beklerken yalnızca birkaçı herhangi bir anda işlem yapar

Bir stratejiyi tam süreçten geçirdiniz. Monte Carlo bootstrap kabul edilebilir bir 5. yüzdelik dilim gösterdi. Walk-forward örnek dışı getirileri doğruladı. Fonlama oranları hesaba katıldı, plato analizi geçildi. Strateji gerçekten işe yarıyor.

Ama zamanın %15'inde işlem yapıyor. Kalan %85'inde sermayeniz atıl bekliyor.

İkinci bir strateji mi çalıştıracaksınız? Üçüncü? Onuncu? Fikir açık. Uygulama ise değil. Strateji portföyü, tek bir bot ile var olmayan sorunlar yaratır:

  • Çakışmalar: iki strateji aynı çifte zıt pozisyonlar açmak istiyor.
  • Kısıtlamalar: borsa/risk yönetimi en fazla KK eş zamanlı pozisyona izin veriyor.
  • Tahsis: her stratejiye sermayenin ne kadarını vermeli?
  • Korelasyon: birbiriyle ilişkili kripto çiftler üzerinde 10 strateji, 10x çeşitlendirme anlamına gelmez.

Kademeli strateji bu sorunları çözen mimari bir kalıptır: birincil strateji tam pozisyon büyüklüğünü alırken, yedek strateji atıl zamanı azaltılmış bir pozisyonla doldurur.

Kademeli Konsept: Birincil + Yedek

Kademeli strateji zaman çizelgesi örtüşmesi

Yüksek Güvenilirlikli Strateji (Birincil)

Birincil, katı giriş kriterlerine sahip bir stratejidir. Örneğin, üç onay seviyesi ile üçlü zaman dilimi: günlük + 4 saatlik + saatlik sinyal, volatilite ve hacim filtresi ile.

Özellikler:

  • Az işlem (backtest döneminde onlarca)
  • İşlem başına yüksek PnL
  • Pozisyonda düşük süre (%5-15)
  • Her girişe yüksek güven

Yedek Strateji

Yedek, gevşek kriterlere sahip bir stratejidir. Çift zaman dilimi, daha az filtre, daha geniş toleranslar. Daha sık işlem yapar, ancak işlem başına daha düşük avantaj ile.

Özellikler:

  • Daha fazla işlem (dönem boyunca yüzlerce)
  • İşlem başına orta düzeyde PnL
  • Pozisyonda yüksek süre (%30-50)
  • Orta güven — azaltılmış pozisyon büyüklüğü ile telafi edilir

Kademeli Mod

zaman çizelgesi:  ──────────────────────────────────────────────────
birincil:         ___████___________________████████____███________
yedek:            ███____███████████████████________████___████████

sermaye:          [dual][ tam ][ dual_size ][  tam  ][ dual  ]

Birincil bir pozisyon açtığında — yedek sessizleşir (veya kapatılır). Birincil atıl olduğunda — yedek azaltılmış bir pozisyonla (dual_size) işlem yapar. Öncelik koşulsuzdur: birincil her zaman yedeği devre dışı bırakır.

Örnekler İçin Stratejiler

Seri boyunca üç strateji kullandık. 750 günlük dönem için parametreleri şöyle:

Parametre Strateji A Strateji B Strateji C
PnL +%55 +%27 +%300
İşlemler ~500 ~40 ~400
İşlem süresi ~%15 ~%5 ~%45
MaxDD ~%0.9 ~%0.75 ~%17
PnL/aktif gün %0.49/g %0.72/g %0.89/g
Karakter Orta aktivite Nadir, yüksek güven Sık, agresif

Aktif Süre Başına PnL bölümünde gösterdiğimiz gibi, ham PnL'ye ve PnL/aktif gün'e göre sıralama farklı sonuçlar üretir. Kademeli orkestrasyon için önemli olan ikinci metriktir.

Optimal dual_size

dual_size optimizasyon yüzeyi dual_size üzerinde ızgara araması bir Sharpe oranı zirvesi ortaya koyar — çok büyük drawdown'ı artırır, çok küçük atıl zamanı boşa harcar

Seçim Problemi

dual_size, tam pozisyonun yedek stratejinin aldığı kesimidir. Temel kademeli parametredir:

  • Çok büyük (örn. 0.5 = %50): birincil ve yedek aynı anda aktif olduğunda, toplam maruziyet = hedefin %150'si. Drawdown ikiye katlanır. Kayıp-kâr asimetrisi bunu orantısız derecede pahalı kılar.

  • Çok küçük (örn. 0.01 = %1): yedek atıl zamanın %85'ini doldurur ama kuruşlar kazanır. Sermaye fiilen atıl bekler.

  • Optimal: yedek, birincil ile eş zamanlı çalışma sırasında drawdown'ı kritik ölçüde artırmadan anlamlı PnL katkısı sağlar.

Formalizasyon

Şöyle olsun:

  • PpP_p — birim zaman başına birincil PnL
  • PfP_f — birim zaman başına yedek PnL
  • tpt_p — pozisyonda sürenin kesimi (birincil)
  • tft_f — pozisyonda sürenin kesimi (yedek)
  • dd — dual_size (0..1)
  • toverlapt_{overlap} — her ikisinin de pozisyonda olduğu sürenin kesimi

Toplam kademeli PnL:

PnLcascade=Pptp+dPf(tftoverlap)\text{PnL}_{cascade} = P_p \cdot t_p + d \cdot P_f \cdot (t_f - t_{overlap})

Toplam MaxDD (en kötü durum — tam korelasyon):

DDcascadeDDp+dDDf\text{DD}_{cascade} \approx \text{DD}_p + d \cdot \text{DD}_f

Toplam drawdown'u DtargetD_{target} ile kısıtlarsak:

dmax=DtargetDDpDDfd_{max} = \frac{D_{target} - \text{DD}_p}{\text{DD}_f}

Izgara Araması

Pratikte optimal dual_size, kademeli backtest üzerinde ızgara aramasıyla bulunur:

import numpy as np
from dataclasses import dataclass

@dataclass
class CascadeResult:
    dual_size: float
    total_pnl: float
    max_dd: float
    sharpe: float
    pnl_per_active_day: float


def grid_search_dual_size(
    primary_equity: np.ndarray,     # equity eğrisi birincil (dakika barları)
    fallback_equity: np.ndarray,    # equity eğrisi yedek (dakika barları)
    primary_positions: np.ndarray,  # 1 = pozisyonda, 0 = flat
    fallback_positions: np.ndarray,
    grid: np.ndarray = np.arange(0.01, 0.30, 0.005),
) -> list[CascadeResult]:
    """
    dual_size için ızgara araması.

    primary_equity ve fallback_equity log-getirilerdir, dakika barları.
    """
    results = []

    for d in grid:
        fallback_active = fallback_positions & ~primary_positions

        cascade_returns = (
            primary_equity * primary_positions
            + d * fallback_equity * fallback_active
        )

        equity_curve = np.cumprod(1 + cascade_returns)
        peak = np.maximum.accumulate(equity_curve)
        drawdown = (equity_curve - peak) / peak
        max_dd = drawdown.min()

        total_pnl = equity_curve[-1] - 1

        sharpe = (
            np.mean(cascade_returns) / np.std(cascade_returns)
            * np.sqrt(525_600)  # yıl başına dakika
        ) if np.std(cascade_returns) > 0 else 0

        active_minutes = np.sum(primary_positions | fallback_active)
        active_days = active_minutes / (24 * 60)
        pnl_per_day = total_pnl / active_days if active_days > 0 else 0

        results.append(CascadeResult(
            dual_size=d,
            total_pnl=total_pnl,
            max_dd=max_dd,
            sharpe=sharpe,
            pnl_per_active_day=pnl_per_day,
        ))

    return sorted(results, key=lambda r: r.sharpe, reverse=True)

Kripto stratejileri için tipik optimum: dual_size 0.05-0.10 aralığında (%5-10 tam pozisyon). Birincil olarak Strateji B (MaxDD %0.75) ve yedek olarak Strateji A (MaxDD %0.9) ile:

dmax=2%0.75%0.9%=1.39d_{max} = \frac{2\% - 0.75\%}{0.9\%} = 1.39

Drawdown kısıtı bağlayıcı değil — optimum kademeli Sharpe tarafından belirlenir. Pratikte, ızgara araması genellikle d0.068d \approx 0.068 (%6.8) verir.

Puana Dayalı Tahsis

Puana dayalı strateji sıralaması Bileşik puana göre sıralanan stratejiler — güven düzeltmesi küçük örnekleri cezalandırır, fonlama maliyetleri net avantajı azaltır

İkiden fazla strateji olduğunda, kademeli yapı puana dayalı tahsise geneller.

Aktif Süre Başına PnL ile Sıralama

Aktif Süre Başına PnL bölümünde ayrıntılı olarak açıklandığı üzere, strateji puanı şunlar hesaba katılarak hesaplanır:

  1. Aktif gün başına PnL — sermaye kullanım verimliliği
  2. Güven düzeltmesi — küçük örnekler için ceza (t-dağılımı)
  3. Fonlama maliyetleri — kaldıracın gerçek maliyeti (Fonlama oranları)
  4. MaxLev — drawdown değerlendirmesi ile ölçekleme (Kayıp-kâr asimetrisi)

score=PnLnet/dayverimlilik×365ffillyıllıklandır×MaxLevo¨lc¸ek×cconfgu¨venilirlik\text{score} = \underbrace{\text{PnL}_{net/day}}_{\text{verimlilik}} \times \underbrace{365 \cdot f_{fill}}_{\text{yıllıklandır}} \times \underbrace{\text{MaxLev}}_{\text{ölçek}} \times \underbrace{c_{conf}}_{\text{güvenilirlik}}

Nadir Stratejiler İçin Güven Düzeltmesi

40 işlemle Strateji B ciddi bir ceza gerektirir. Güven aralığının alt sınırını kullanıyoruz:

cconf=max(0, rˉtα/2,n1snrˉ)c_{conf} = \max\left(0,\ \frac{\bar{r} - t_{\alpha/2, n-1} \cdot \frac{s}{\sqrt{n}}}{\bar{r}}\right)

import scipy.stats as st
import numpy as np

def confidence_factor(trade_returns: np.ndarray, confidence: float = 0.95) -> float:
    """Güven faktörü: 0..1, küçük örnekler için ceza."""
    n = len(trade_returns)
    if n < 10:
        return 0.0

    mean_r = np.mean(trade_returns)
    if mean_r <= 0:
        return 0.0

    se = np.std(trade_returns, ddof=1) / np.sqrt(n)
    t_crit = st.t.ppf(1 - (1 - confidence) / 2, df=n - 1)
    ci_lower = mean_r - t_crit * se

    return max(0.0, ci_lower / mean_r)

cf_b = confidence_factor(np.random.normal(0.0067, 0.028, 40))

cf_a = confidence_factor(np.random.normal(0.0011, 0.008, 500))

Fonlama Maliyeti Entegrasyonu

Sürekli vadeli işlemlerde, fonlama her 8 saatte bir ödenir. LL kaldıraç ve ortalama rfr_f oranı ile:

Fonlamagu¨nlu¨k=3rfL\text{Fonlama}_{günlük} = 3 \cdot r_f \cdot L

MaxLev = 55x ve ortalama fonlama oranı %0.01 olan Strateji A için:

Fonlamagu¨nlu¨k=3×0.0001×55=0.0165=1.65%/gu¨n\text{Fonlama}_{günlük} = 3 \times 0.0001 \times 55 = 0.0165 = 1.65\%/\text{gün}

PnL/aktif gün = %0.49 ile net PnL negatif: 0.49%1.65%=1.16%0.49\% - 1.65\% = -1.16\%/gün. Strateji, tam kaldıraçla kârsızdır. Ayrıntılı analiz için Fonlama Oranları Kaldıracınızı Öldürüyor.

Çok Stratejili Orkestratör

Orkestratör slot tahsisi ve öncelik kuyruğu

Mimari

Orkestratör, MM alım satım çifti üzerinde NN stratejiyi yönetir. Potansiyel pozisyonların toplam sayısı: N×MN \times M. Ancak sermaye sınırlıdır — en fazla KK eş zamanlı pozisyona (slot) izin verilir.

┌─────────────────────────────────────────────┐
│                ORKESTRATÖR                   │
│                                              │
│  Sinyal Kuyruğu (puana göre sıralı):        │
│  ┌──────────────────────────────────────┐    │
│  │ 1. Strateji C × ETHUSDT  puan=223  │    │
│  │ 2. Strateji B × BTCUSDT  puan=142  │    │
│  │ 3. Strateji A × SOLUSDT  puan=100  │    │
│  │ 4. Strateji C × BTCUSDT  puan=89   │    │
│  │ 5. Strateji A × ETHUSDT  puan=76   │    │
│  └──────────────────────────────────────┘    │
│                                              │
│  Aktif Slotlar (max_parallel = 3):           │
│  ┌──────────────────────────────────────┐    │
│  │ Slot 1: Strateji C × ETHUSDT [TAM] │    │
│  │ Slot 2: Strateji B × BTCUSDT [TAM] │    │
│  │ Slot 3: Strateji A × SOLUSDT [DUAL]│    │
│  └──────────────────────────────────────┘    │
│                                              │
│  Çakışma Kuralları:                          │
│  - Çift başına bir pozisyon                  │
│  - Birincil, aynı çiftte yedeği devre dışı  │
│  - Çapraz çift slotları için yüksek puan    │
└─────────────────────────────────────────────┘

Slot Yönetimi

from dataclasses import dataclass, field
from enum import Enum
from typing import Optional
import heapq
import time


class SlotType(Enum):
    FULL = "full"        # birincil strateji, %100 pozisyon
    DUAL = "dual"        # yedek strateji, dual_size pozisyon


@dataclass
class Signal:
    strategy_id: str
    pair: str
    direction: str       # "long" | "short"
    score: float
    is_primary: bool     # birincil mi yedek mi
    timestamp: float


@dataclass(order=True)
class Slot:
    """Tek bir orkestratör slotu."""
    priority: float = field(compare=True)  # min-heap için negatif puan
    strategy_id: str = field(compare=False)
    pair: str = field(compare=False)
    slot_type: SlotType = field(compare=False)
    entry_time: float = field(compare=False)


class Orchestrator:
    """
    Kademeli modlu çok stratejili orkestratör.

    max_parallel_positions slotu içinde N strateji x M çifti yönetir.
    Birincil stratejiler yedek üzerinde koşulsuz önceliğe sahiptir.
    """

    def __init__(
        self,
        max_parallel_positions: int = 10,
        dual_size: float = 0.068,
        min_score: float = 0,
    ):
        self.max_parallel = max_parallel_positions
        self.dual_size = dual_size
        self.min_score = min_score

        self.active_slots: dict[str, Slot] = {}  # çift -> Slot
        self.pending_signals: list[Signal] = []

    def on_signal(self, signal: Signal) -> Optional[dict]:
        """
        Yeni bir sinyali işle. Bir eylem veya None döndürür.

        Eylemler:
        - {"action": "open", "pair": ..., "size": ..., "slot_type": ...}
        - {"action": "replace", "pair": ..., "close_strategy": ..., "open_strategy": ...}
        - None (sinyal reddedildi)
        """
        if signal.score < self.min_score:
            return None

        pair = signal.pair

        if pair in self.active_slots:
            existing = self.active_slots[pair]

            if signal.is_primary and existing.slot_type == SlotType.DUAL:
                self.active_slots[pair] = Slot(
                    priority=-signal.score,
                    strategy_id=signal.strategy_id,
                    pair=pair,
                    slot_type=SlotType.FULL,
                    entry_time=signal.timestamp,
                )
                return {
                    "action": "replace",
                    "pair": pair,
                    "close_strategy": existing.strategy_id,
                    "open_strategy": signal.strategy_id,
                    "size": 1.0,
                }

            if signal.score > -existing.priority:
                slot_type = SlotType.FULL if signal.is_primary else SlotType.DUAL
                size = 1.0 if signal.is_primary else self.dual_size
                self.active_slots[pair] = Slot(
                    priority=-signal.score,
                    strategy_id=signal.strategy_id,
                    pair=pair,
                    slot_type=slot_type,
                    entry_time=signal.timestamp,
                )
                return {
                    "action": "replace",
                    "pair": pair,
                    "close_strategy": existing.strategy_id,
                    "open_strategy": signal.strategy_id,
                    "size": size,
                }

            return None  # mevcut daha yüksek önceliğe sahip

        if len(self.active_slots) < self.max_parallel:
            slot_type = SlotType.FULL if signal.is_primary else SlotType.DUAL
            size = 1.0 if signal.is_primary else self.dual_size

            self.active_slots[pair] = Slot(
                priority=-signal.score,
                strategy_id=signal.strategy_id,
                pair=pair,
                slot_type=slot_type,
                entry_time=signal.timestamp,
            )
            return {
                "action": "open",
                "pair": pair,
                "strategy": signal.strategy_id,
                "size": size,
                "slot_type": slot_type,
            }

        worst_pair = min(
            self.active_slots,
            key=lambda p: -self.active_slots[p].priority,
        )
        worst_slot = self.active_slots[worst_pair]

        if signal.score > -worst_slot.priority:
            del self.active_slots[worst_pair]

            slot_type = SlotType.FULL if signal.is_primary else SlotType.DUAL
            size = 1.0 if signal.is_primary else self.dual_size

            self.active_slots[pair] = Slot(
                priority=-signal.score,
                strategy_id=signal.strategy_id,
                pair=pair,
                slot_type=slot_type,
                entry_time=signal.timestamp,
            )
            return {
                "action": "replace",
                "pair": pair,
                "close_strategy": worst_slot.strategy_id,
                "close_pair": worst_pair,
                "open_strategy": signal.strategy_id,
                "size": size,
            }

        return None  # tüm aktif slotların puanı daha yüksek

    def on_exit(self, pair: str) -> None:
        """Strateji bir pozisyonu kapattı."""
        if pair in self.active_slots:
            del self.active_slots[pair]

    def utilization(self) -> float:
        """Mevcut slot kullanımı."""
        return len(self.active_slots) / self.max_parallel

    def fill_efficiency_snapshot(self) -> float:
        """Ağırlıklı kullanım: FULL=1.0, DUAL=dual_size."""
        total = sum(
            1.0 if s.slot_type == SlotType.FULL else self.dual_size
            for s in self.active_slots.values()
        )
        return total / self.max_parallel

Çakışma Çözümü

Üç çakışma seviyesi:

Seviye 1 — Aynı çift, aynı yön. Daha yüksek puanlı strateji kazanır. Her ikisi de birincilse — puan belirler. Biri birincil diğeri yedekse — birincil koşulsuz olarak kazanır.

Seviye 2 — Aynı çift, zıt yön. Yasak: aynı çifte aynı anda hem long hem short olamazsınız. En yüksek puanlı strateji kazanır.

Seviye 3 — Çapraz çift rekabeti. Tüm slotlar dolduğunda, yeni bir sinyal en düşük puanlı slotu tahliye eder. Bu bir öncelik kuyruğu olarak işlev görür.

Kademeli Backtesting: Metodoloji

Kademeli stratejilerin ortak simülasyonu Ortak simülasyon: örtüşme bölgeleri ve birleşik kademeli sonuç ile birincil ve yedek equity eğrileri

Neden Sadece PnL Toplayamazsınız

Naif yaklaşım: her stratejiyi ayrı ayrı geri test edin, PnL'yi toplayın. Bu, üç nedenden dolayı şişirilmiş bir sonuç üretir:

  1. Zaman örtüşmesi. Birincil ve yedek aynı anda aktif olduğunda, yedek işlem yapmamalıdır (veya dual_size ile işlem yapar). Basit toplama bu örtüşmeyi görmezden gelir.

  2. Sermaye kısıtı. Toplam pozisyon sınırlıdır. 5 strateji aynı anda açmak istiyorsa ama yalnızca 3 slot varsa — iki strateji giremez. PnL'leri sayılamaz.

  3. İşlem maliyetleri. Kademeli geçiş (yedeği kapatma, birincili açma) bireysel backtestlerde bulunmayan ek komisyonlar üretir.

Ortak Simülasyon

Doğru kademeli backtest, paylaşılan bir zaman çizelgesinde tüm stratejilerin ortak simülasyonudur:

import numpy as np
from typing import NamedTuple


class Trade(NamedTuple):
    strategy: str
    pair: str
    entry_time: int      # dakika indeksi
    exit_time: int       # dakika indeksi
    pnl_per_minute: float  # dakika başına log-getiri
    is_primary: bool
    score: float


def backtest_cascade(
    all_trades: list[Trade],
    total_minutes: int,
    max_slots: int = 10,
    dual_size: float = 0.068,
    switch_cost: float = 0.0006,  # %0.06 gidiş-dönüş
) -> dict:
    """
    Kademeli portföyün ortak simülasyonu.

    Her dakikadan geçin, orkestratör kurallarını uygulayın,
    örtüşme ve slot kısıtlarını hesaba katarak PnL hesaplayın.
    """
    entries = {}
    exits = {}
    active_trades = {}  # trade_id -> Trade

    for i, trade in enumerate(all_trades):
        entries.setdefault(trade.entry_time, []).append((i, trade))
        exits.setdefault(trade.exit_time, []).append((i, trade))

    active_slots = {}     # çift -> (trade_id, SlotType)
    equity = np.ones(total_minutes)
    switch_costs_total = 0.0

    for t in range(1, total_minutes):
        for trade_id, trade in exits.get(t, []):
            if trade.pair in active_slots:
                slot_id, _ = active_slots[trade.pair]
                if slot_id == trade_id:
                    del active_slots[trade.pair]

        new_signals = sorted(
            entries.get(t, []),
            key=lambda x: x[1].score,
            reverse=True,
        )

        for trade_id, trade in new_signals:
            pair = trade.pair

            if pair in active_slots:
                existing_id, existing_type = active_slots[pair]
                existing_trade = all_trades[existing_id]

                if trade.is_primary and existing_type == SlotType.DUAL:
                    active_slots[pair] = (trade_id, SlotType.FULL)
                    switch_costs_total += switch_cost
                    continue

                if trade.score > existing_trade.score:
                    slot_type = SlotType.FULL if trade.is_primary else SlotType.DUAL
                    active_slots[pair] = (trade_id, slot_type)
                    switch_costs_total += switch_cost
            elif len(active_slots) < max_slots:
                slot_type = SlotType.FULL if trade.is_primary else SlotType.DUAL
                active_slots[pair] = (trade_id, slot_type)

        minute_return = 0.0
        for pair, (trade_id, slot_type) in active_slots.items():
            trade = all_trades[trade_id]
            size = 1.0 if slot_type == SlotType.FULL else dual_size
            minute_return += trade.pnl_per_minute * size

        equity[t] = equity[t - 1] * (1 + minute_return)

    peak = np.maximum.accumulate(equity)
    max_dd = ((equity - peak) / peak).min()
    total_pnl = equity[-1] - 1 - switch_costs_total

    return {
        "total_pnl": total_pnl,
        "max_dd": max_dd,
        "switch_costs": switch_costs_total,
        "equity_curve": equity,
    }

Geçişteki İşlem Maliyeti

Her kademeli geçiş (yedek -> birincil) şunları gerektirir:

  1. Yedek pozisyonu kapatma: taker ücreti (Binance vadeli işlemlerde %0.04)
  2. Birincil pozisyonu açma: taker ücreti (%0.04)
  3. Spread: ~%0.01-0.02

Toplam geçiş maliyeti: dönem başına ~%0.06-0.10. 100 geçişle:

Gec¸is¸ maliyetleri=100×0.0008=8%\text{Geçiş maliyetleri} = 100 \times 0.0008 = 8\%

Bu önemli bir miktardır. Sık geçişli bir kademeli yapı, işlem maliyetleri nedeniyle tek bir stratejinin altında kalabilir.

Çok Çiftli Uzantı: M Çifte N Strateji

Çok çiftli strateji ağı N stratejinin M alım satım çiftine bağlandığı ağ — korelasyon gücü etkin çeşitlendirmeyi belirler

Kombinasyon Uzayı

10 çifte 3 strateji = 30 potansiyel sinyal. max_slots = 5 ile, orkestratör en yüksek 5 puanlıyı seçer. Bu bir kombinatoriyel problemdir: her anda (305)=142506\binom{30}{5} = 142\,506 olası portföy.

Pratikte, açgözlü bir algoritma (puana göre sırala, yukarıdan aşağıya doldur) O(NMlogK)O(N \cdot M \cdot \log K) ile neredeyse optimal sonuçlar üretir.

Çiftler Arasındaki Korelasyon

Kripto çiftleri güçlü korelasyona sahiptir. BTC düşer — ETH, SOL, AVAX birlikte düşer. Bu, 5 farklı çiftteki 5 long pozisyonun etkin olarak "kripto piyasası" üzerindeki tek büyük pozisyon olduğu anlamına gelir.

Sinyal Korelasyonu bölümünde ayrıntılı olarak analiz ettiğimiz gibi, bağımsız pozisyonların etkin sayısı:

Neff=N1+(N1)ρˉN_{eff} = \frac{N}{1 + (N-1) \cdot \bar{\rho}}

burada ρˉ\bar{\rho} çiftler arasındaki ortalama korelasyondur.

ρˉ=0.7\bar{\rho} = 0.7 ve N=5N = 5 için:

Neff=51+4×0.7=53.8=1.32N_{eff} = \frac{5}{1 + 4 \times 0.7} = \frac{5}{3.8} = 1.32

İlişkili çiftlerdeki beş pozisyon 1.3 bağımsız pozisyona eşdeğerdir. Çeşitlendirme neredeyse yok.

Kademeli İçin Pratik Çıkarımlar

def effective_diversification(
    positions: list[dict],  # [{"pair": "BTCUSDT", "direction": "long"}, ...]
    correlation_matrix: np.ndarray,
    pair_index: dict[str, int],
) -> float:
    """
    Açık pozisyonların etkin çeşitlendirmesini hesapla.

    Döndürür:
        N_eff / N — çeşitlendirme katsayısı (0..1)
    """
    n = len(positions)
    if n <= 1:
        return 1.0

    total_corr = 0.0
    pairs_count = 0

    for i in range(n):
        for j in range(i + 1, n):
            idx_i = pair_index[positions[i]["pair"]]
            idx_j = pair_index[positions[j]["pair"]]

            rho = correlation_matrix[idx_i, idx_j]

            if positions[i]["direction"] != positions[j]["direction"]:
                rho = -rho

            total_corr += rho
            pairs_count += 1

    avg_rho = total_corr / pairs_count if pairs_count > 0 else 0
    n_eff = n / (1 + (n - 1) * max(0, avg_rho))

    return n_eff / n


Orkestratör, slotları doldururken korelasyonu hesaba katmalıdır. İki seçenek:

  1. Çeşitlendirme bonusu: sıralamada, ilişkisiz çiftlerdeki stratejilerin puanına bonus ekle.
  2. Korelasyon sınırı: ilişkili çiftlerde aynı yönlü pozisyon sayısını sınırla.

Kademeli Optimizasyon Boru Hattı

Sekiz aşamalı optimizasyon boru hattı Veri hazırlamadan canlı orkestrasyona doğrulama yoluyla sekiz bağlı aşama — her biri bir öncekinin üzerine inşa edilir

Veriden üretime kadar olan tam boru hattı 8 aşamadan oluşur:

Aşama 0: Veri Hazırlama

Geçmiş verileri yükleyin, çok zaman dilimli erişim için Parquet önbelleği oluşturun. Verimli önbellekleme olmadan, sonraki aşamalar kabul edilemez ölçüde yavaştır.

Aşama 1: TF + Uzunluk (Tepe Tırmanma Izgarası)

Temel zaman dilimini ve gösterge pencere uzunluklarını seçin. Kaba ızgara: TF {1m, 5m, 15m, 1h, 4h}'den, Uzunluk {10, 20, 50, 100, 200}'den. En iyi ızgara noktasından tepe tırmanma.

Aşama 2: Ayrım (Koordinat İnişi, 12 Parametre)

Ayrım parametrelerini (girişler/çıkışlar) optimize edin. 12 parametre üzerinde Koordinat inişi — gösterge eşikleri, filtreler, stop-loss'lar, take-profit'ler. Koordinat inişi, yüksek boyutlu deterministik amaç işlevleri için Optuna'dan daha ucuzdur.

Aşama 3: Meta-Parametreler (Koordinat İnişi)

Meta-parametreler: maksimum tutma süresi, çıkış için minimum PnL, trailing stop yapılandırması. Yine koordinat inişi. Plato analizi ile sağlamlığı kontrol edin — optimum nokta gibiyse, strateji aşırı optimize edilmiştir.

Aşama 4: Kombo Optimizasyonu

Çiftler (Birincil, Yedek) üzerinde ızgara araması. Her kombinasyon için: dual_size seçin, ortak simülasyon yoluyla kademeli PnL hesaplayın.

Aşama 5: Doğrulama

Çok seviyeli doğrulama:

Aşama 6: Sıralama ve Seçim

Kademeli kombinasyonları puana göre sıralayın. İlk K kombinasyon Aşama 7'ye geçer. Puan, güven düzeltmesini, fonlama maliyetlerini ve fill_efficiency'yi hesaba katar.

Aşama 7: Orkestrasyon

Son aşama: kademeli modda NN strateji ve MM çift üzerinde orkestratörü başlatın. Slot yönetimi, öncelik kuyruğu, çakışma çözümü — yukarıda açıklanan her şey.

Performans Analizi: Kademeli ile Bireysel Karşılaştırma

Kademeli ile bireysel strateji performansı Yan yana karşılaştırma: kademeli portföy, atıl süre kullanımı sayesinde bireysel stratejileri geride bırakır

Teorik Kademeli Avantaj

Birincil, PnL/gün = %0.49 ile zamanın tp=%15t_p = \%15'inde işlem yaptığını varsayalım. Yedek, PnL/gün = %0.89 ile tf=%45t_f = \%45 işlem yapıyor. Örtüşme = tp×tf=%6.75t_p \times t_f = \%6.75 (bağımsızlık varsayımıyla).

Yalnızca birincil (Strateji A):

Yıllık PnL=0.49%×0.15×365=26.8%\text{Yıllık PnL} = 0.49\% \times 0.15 \times 365 = 26.8\%

Kademeli (A birincil + C yedek):

Yıllık PnL=0.49%×0.15×365+0.068×0.89%×(0.450.0675)×365=26.8%+8.4%=35.2%\text{Yıllık PnL} = 0.49\% \times 0.15 \times 365 + 0.068 \times 0.89\% \times (0.45 - 0.0675) \times 365 = 26.8\% + 8.4\% = 35.2\%

Kademeli kazanım: yedekten PnL'ye +%31, minimal drawdown artışı ile (0.068×17%=1.16%0.068 \times 17\% = 1.16\% MaxDD'ye eklendi).

Kademeli Ne Zaman Yardımcı Olmaz

Kademeli şu durumlarda etkisizdir:

  1. Birincil zamanın >%80'inde aktif. Az atıl zaman — yedeğin sığacağı yer yok.
  2. Stratejiler yüksek korelasyona sahip. Birincil ve yedek aynı anda sinyal üretir — örtüşme yüksek ve yedek tam olarak birincil de atıl olduğunda atıl kalır.
  3. Geçiş maliyetleri yedek PnL'ini aşıyor. Sık geçişle, kademeli komisyonlar yedek kârlarını yer bitirir.
  4. dual_size çok küçük. d=0.01d = 0.01'de, yedek potansiyelinin %1'ini kazanır — komisyonların altında.

Karşılaştırma Tablosu

Yapılandırma Yıllık PnL MaxDD Sharpe Geçiş maliyetleri
Yalnızca Strateji A %26.8 %0.9 1.42 0
Yalnızca Strateji C %146.1 %17 1.15 0
Kademeli A+C (d=0.068) %35.2 %2.06 1.58 ~%1.2
Kademeli B+A (d=0.068) %19.4 %1.36 1.71 ~%0.3
3 stratejili orkestratör %48.7 %3.1 1.63 ~%2.1

Kademeli A+C: birincil A, yedek C'den +%8.4 kazanır. Sharpe, atıl süre kullanımı sayesinde yükselir. MaxDD ılımlı büyür (0.9%+0.068×17%2.06%0.9\% + 0.068 \times 17\% \approx 2.06\%).

Orkestrasyon: Pratikte fill_efficiency

Fill efficiency göstergesi ve ısı haritası ~%78 fill efficiency: ısı haritası stratejiler ve çiftler arasında zaman kullanımını gösteriyor, parlak hücreler aktif alım satımı gösteriyor

fill_efficiency parametresi, orkestratörün atıl zamanın gerçekte ne kadarını kullandığını belirler. Aktif Süre Başına PnL bölümünde gösterildiği gibi, üç yöntemle tahmin edilebilir:

  1. Sabit sabit (0.80) — kaba ama evrensel
  2. (1p)Neff(1-p)^{N_{eff}} ile analitik tahmin — korelasyonu hesaba katar
  3. Veriden simülasyon — en doğru

10 çifte 3 strateji içeren bir kademeli için:

def cascade_fill_efficiency(
    strategies: list[dict],   # [{"trading_time": 0.15, "is_primary": True}, ...]
    n_pairs: int = 10,
    correlation_factor: float = 3.0,
) -> float:
    """Kademeli portföy için fill_efficiency tahmini."""
    n_eff = n_pairs / correlation_factor

    primary_times = [s["trading_time"] for s in strategies if s["is_primary"]]
    p_primary = 1 - np.prod([(1 - t) ** n_eff for t in primary_times])

    fallback_times = [s["trading_time"] for s in strategies if not s["is_primary"]]
    p_fallback = 1 - np.prod([(1 - t) ** n_eff for t in fallback_times])

    fill = p_primary + (1 - p_primary) * p_fallback

    return min(fill, 1.0)

strategies = [
    {"trading_time": 0.05, "is_primary": True},   # Strateji B
    {"trading_time": 0.15, "is_primary": True},    # Strateji A
    {"trading_time": 0.45, "is_primary": False},   # Yedek olarak Strateji C
]

eff = cascade_fill_efficiency(strategies, n_pairs=10, correlation_factor=3.0)

Pratik Öneriler

Pratik mühendislik kontrol listesi Kademeli dağıtım için altı temel öneri — küçük başlamaktan uyarlanabilir yeniden kalibrasyona

1. İki Strateji ile Başlayın

20 çifte 10 stratejiyi hemen başlatmayın. 3-5 çifte bir birincil + bir yedek ile başlayın. Ortak simülasyonun gerçek davranışla eşleştiğinden emin olun. Backtest-canlı paritesi kritiktir: kademeli backtest canlıdan %5-10 bile sapıyorsa — orkestratör mantığında bir hata var demektir.

2. Grid Search'ten dual_size, Sezgiden Değil

Optimal dual_size belirli strateji çiftine bağlıdır. %6.8 bir kılavuzdur, evrensel bir sabit değildir. %0.5 adımlarla %1'den %30'a kadar ızgara araması yapın ve Sharpe maksimumunu seçin.

3. Slot Limiti Mimariyi Tanımlar

max_slots = 1 ile, kademeli basit strateji geçişine dönüşür. max_slots = 50 ile, kısıt bağlayıcı değil ve sorun bağımsız bir portföye indirgenir. İlginç bölge: max_slots = 3-10, burada slot yönetimi gerçekten sonuçları etkiler.

4. Gecikmeyi Hesaba Katın

Canlı alım satımda, kademeli geçiş anlık değildir. Yedek pozisyonu kapatma + birincili açma = 2 API çağrısı + ağ gecikmesi + borsa eşleştirme. Oynaklı bir piyasada, fiyat 200-500ms içinde hareket edebilir. Bir kayma bütçesi oluşturun.

5. fill_efficiency'yi İzleyin

Üretimdeki gerçek fill_efficiency'yi takip edin. Backteste göre önemli ölçüde düşükse — orkestratör atıl zamanı beklendiği gibi kullanmıyordur. Nedenler: API gecikmeleri, reddedilen emirler, marjin kısıtlamaları.

6. Uyarlanabilir Optimizasyon Kullanın

Kademeli parametreler (dual_size, puan ağırlıkları, slot sınırları) statik olmamalıdır. Taze veriler üzerinde periyodik yeniden kalibrasyon için uyarlanabilir drill-down kullanın. Piyasa değişir — kademeli parametreler de değişmelidir.

"Yanılsama Olmadan Backtestler" Serisi: Özet

Seri bilgi haritası Tam sistem mimarisi: matematikten doğrulamadan canlı orkestrasyona 13 birbirine bağlı modül

Bu makale, 13+ makaleden oluşan bir serinin finalidir. Her makale, backtestten üretime giden yolda belirli bir sorunu ele aldı. İşte nasıl bağlandıkları:

Temel: Getiri Matematiği

Kayıp-Kâr Asimetrisi — getirilerin çarpımsal doğası, volatilite sürüklenmesi, Kelly kriteri. Bu, izleyen her şeyin matematiksel temelidir: MaxDD neden kaldıracı belirler, Sharpe neden ham PnL'den daha önemlidir, simetrik R:R ile %50 kazanma oranının neden kârsız olduğu.

Doğrulama: Güven Aralıkları ve Sağlamlık

Monte Carlo Bootstrap — tek noktalı tahmini güven aralıklı bir dağılıma dönüştürme. Herhangi bir metrik (PnL, MaxDD, Sharpe) yalnızca güven aralığı ile anlam ifade eder.

Walk-Forward Optimizasyonu — örnek dışı doğrulama. Geçmiş verilere dayalı backtest bir IS sonucudur; WFO stratejinin yeni veriler üzerinde nasıl performans gösterdiğini ortaya koyar.

Plato Analizi — parametre sağlamlığı kontrolü. Optimum nokta gibiyse, strateji aşırı optimize edilmiştir.

Backtest-Canlı Paritesi — backtesti gerçek sonuçlarla karşılaştırma. Ölçeklendirmeden önceki son kontrol.

Gerçekçi Maliyetler: Fonlama ve Kaldıraç

Fonlama Oranları Kaldıracı Öldürüyor — sürekli vadeli işlemlerde kaldıracın gizli maliyeti. Fonlamayı hesaba katmadan, güzel bir backtest zarara dönüşür.

Fonlama Oranı Arbitrajı — borsalar arası stratejiler aracılığıyla fonlamayı giderden gelir kaynağına nasıl dönüştürürsünüz.

Metrikler ve Sıralama

Aktif Süre Başına PnL — portföyde stratejileri sıralamak için metrik. Ham PnL ölçeklenmez; PnL/aktif gün ölçeklenir.

Sinyal Korelasyonu — ilişkili çift portföyünde etkin çeşitlendirme.

Altyapı ve Optimizasyon

Çok Zaman Dilimli Backtestler için Parquet Önbelleği — hızlı iterasyonlar için veri altyapısı.

Uyarlanabilir Drill-Down — uyarlanabilir optimizasyon: kaba ızgara -> umut verici bölgelerde ince ayar.

Optuna ile Koordinat İnişi Karşılaştırması — optimizer seçimi: gürültülü amaçlarla düşük boyutlar için Optuna, düzgün amaçlarla yüksek boyutlar için koordinat inişi.

Polars ile Pandas Karşılaştırması — backtesting için DataFrame işlem performansı.

Orkestrasyon (Bu Makale)

Kademeli Stratejiler — önceki tüm bileşenleri çalışan bir sisteme birleştirme. Puana dayalı tahsis, PnL/aktif süre, güven düzeltmesi, fonlama maliyetlerini kullanır. Kademeli mod atıl zamanı doldurur. Ortak simülasyon portföyü doğrular. Monte Carlo bootstrap, kademeli PnL için güven aralıkları sağlar.

Her makale bağımsız bir modüldür. Birlikte, veri yüklemeden bir strateji portföyünün canlı orkestrasyonuna kadar eksiksiz bir boru hattı oluştururlar.

Sonuç

Kademeli, strateji portföylerine tek yaklaşım değildir. Ancak en basit ve pratik yaklaşımlardan biridir: birincil strateji tam kapasiteyle işlem yaparken, yedek atıl zamanı azaltılmış bir pozisyonla doldurur. İki temel parametre (dual_size ve max_slots), çoğu yapılandırma için yeterli esneklik sağlar.

Üç çıkarım:

  1. Kademeli yalnızca ortak simülasyon yoluyla geri test edilmelidir. Bireysel PnL'yi toplamak sonuçları şişirir. Geçiş maliyetleri, örtüşme, slot kısıtlamaları — bunların tümü yalnızca ortak simülasyonda yakalanır.

  2. dual_size, PnL ile drawdown dengesini belirler. Tipik optimum %5-10'dur. Sharpe üzerinde ızgara araması güvenilir bir seçim yöntemidir.

  3. Orkestratör, puana dayalı bir öncelik kuyruğudur. Her şey her sinyal için tek bir sayıya (puana) indirgenir. Puan = f(PnL/aktif gün, MaxLev, güven, fonlama). En yüksek puanlı stratejiler slotları alır. Gerisini bekler.

"Yanılsama Olmadan Backtestler" serisi bir şeyi gösteriyor: güzel bir backtest ile gerçek kâr arasında düzinelerce tuzak yatıyor. Her makale birini ortadan kaldırıyor. Kademeli orkestrasyon son adımdır: doğrulanmış stratejiler kümesini çalışan bir portföye dönüştürme.


Faydalı Bağlantılar

  1. López de Prado — Finansal Makine Öğrenmesinde Gelişmeler: Portföy Yapısı
  2. Pardo, R. — Alım Satım Stratejilerinin Değerlendirilmesi ve Optimizasyonu
  3. Ernest Chan — Algoritmik Alım Satım: Kazanan Stratejiler ve Gerekçeleri
  4. Perry Kaufman — Alım Satım Sistemleri ve Yöntemleri, Portföy Tahsisi Bölümü
  5. Tomasini, Jaekle — Alım Satım Sistemleri: Sistem Geliştirme ve Portföy Optimizasyonuna Yeni Bir Yaklaşım
  6. Bailey, D.H. & López de Prado — Deflate Edilmiş Sharpe Oranı
  7. Markowitz, H. — Portföy Seçimi (1952)
  8. Kelly, J.L. — Bilgi Oranının Yeni Bir Yorumu (1956)

Alıntı

@article{soloviov2026cascadestrategies,
  author = {Soloviov, Eugen},
  title = {Kademeli Stratejiler: Öncelikli Yürütme ve Yedek Doldurma},
  year = {2026},
  url = {https://marketmaker.cc/tr/blog/post/cascade-strategies-orchestration},
  version = {0.1.0},
  description = {"Yanılsama Olmadan Backtestler" serisinin finali. N strateji x M çift üzerinden bir orkestratör nasıl kurulur, öncelik ve yedek doldurma ile kademeli mod nasıl uygulanır, dual_size nasıl seçilir ve strateji portföyleri neden PnL toplanarak geri test edilemez.}
}
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.