Kademeli Stratejiler: Öncelikli Yürütme ve Yedek Doldurma
"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
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 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

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 ü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:
- — birim zaman başına birincil PnL
- — birim zaman başına yedek PnL
- — pozisyonda sürenin kesimi (birincil)
- — pozisyonda sürenin kesimi (yedek)
- — dual_size (0..1)
- — her ikisinin de pozisyonda olduğu sürenin kesimi
Toplam kademeli PnL:
Toplam MaxDD (en kötü durum — tam korelasyon):
Toplam drawdown'u ile kısıtlarsak:
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:
Drawdown kısıtı bağlayıcı değil — optimum kademeli Sharpe tarafından belirlenir. Pratikte, ızgara araması genellikle (%6.8) verir.
Puana Dayalı Tahsis
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:
- Aktif gün başına PnL — sermaye kullanım verimliliği
- Güven düzeltmesi — küçük örnekler için ceza (t-dağılımı)
- Fonlama maliyetleri — kaldıracın gerçek maliyeti (Fonlama oranları)
- MaxLev — drawdown değerlendirmesi ile ölçekleme (Kayıp-kâr asimetrisi)
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:
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. kaldıraç ve ortalama oranı ile:
MaxLev = 55x ve ortalama fonlama oranı %0.01 olan Strateji A için:
PnL/aktif gün = %0.49 ile net PnL negatif: /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

Mimari
Orkestratör, alım satım çifti üzerinde stratejiyi yönetir. Potansiyel pozisyonların toplam sayısı: . Ancak sermaye sınırlıdır — en fazla 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
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:
-
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.
-
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.
-
İş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:
- Yedek pozisyonu kapatma: taker ücreti (Binance vadeli işlemlerde %0.04)
- Birincil pozisyonu açma: taker ücreti (%0.04)
- Spread: ~%0.01-0.02
Toplam geçiş maliyeti: dönem başına ~%0.06-0.10. 100 geçişle:
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
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 olası portföy.
Pratikte, açgözlü bir algoritma (puana göre sırala, yukarıdan aşağıya doldur) 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ı:
burada çiftler arasındaki ortalama korelasyondur.
ve için:
İ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:
- Çeşitlendirme bonusu: sıralamada, ilişkisiz çiftlerdeki stratejilerin puanına bonus ekle.
- Korelasyon sınırı: ilişkili çiftlerde aynı yönlü pozisyon sayısını sınırla.
Kademeli 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:
- Çok sembol: strateji optimizasyon çifti değil 10+ çifte test edildi
- Walk-forward: kayan IS/OOS penceresi
- Parametre kararlılığı: her aşamada plato analizi
- Monte Carlo bootstrap: kademeli PnL için güven aralıkları
- Backtest-canlı paritesi: backtesti kağıt alım satım ile karşılaştırma
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 strateji ve ç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
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 'inde işlem yaptığını varsayalım. Yedek, PnL/gün = %0.89 ile işlem yapıyor. Örtüşme = (bağımsızlık varsayımıyla).
Yalnızca birincil (Strateji A):
Kademeli (A birincil + C yedek):
Kademeli kazanım: yedekten PnL'ye +%31, minimal drawdown artışı ile ( MaxDD'ye eklendi).
Kademeli Ne Zaman Yardımcı Olmaz
Kademeli şu durumlarda etkisizdir:
- Birincil zamanın >%80'inde aktif. Az atıl zaman — yedeğin sığacağı yer yok.
- 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.
- Geçiş maliyetleri yedek PnL'ini aşıyor. Sık geçişle, kademeli komisyonlar yedek kârlarını yer bitirir.
- dual_size çok küçük. '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 ().
Orkestrasyon: Pratikte fill_efficiency
~%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:
- Sabit sabit (0.80) — kaba ama evrensel
- ile analitik tahmin — korelasyonu hesaba katar
- 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
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
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:
-
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.
-
dual_size, PnL ile drawdown dengesini belirler. Tipik optimum %5-10'dur. Sharpe üzerinde ızgara araması güvenilir bir seçim yöntemidir.
-
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
- López de Prado — Finansal Makine Öğrenmesinde Gelişmeler: Portföy Yapısı
- Pardo, R. — Alım Satım Stratejilerinin Değerlendirilmesi ve Optimizasyonu
- Ernest Chan — Algoritmik Alım Satım: Kazanan Stratejiler ve Gerekçeleri
- Perry Kaufman — Alım Satım Sistemleri ve Yöntemleri, Portföy Tahsisi Bölümü
- Tomasini, Jaekle — Alım Satım Sistemleri: Sistem Geliştirme ve Portföy Optimizasyonuna Yeni Bir Yaklaşım
- Bailey, D.H. & López de Prado — Deflate Edilmiş Sharpe Oranı
- Markowitz, H. — Portföy Seçimi (1952)
- 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.}
}
Yazarlar
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.