Kaskad strategiyalar: Zaxira bilan to'ldirishli ustuvor bajarish
"Illyuziyasiz bektestlar" seriyasining finali. N strategiya va M juftlik asosida orkestratorni qanday qurish kerak, ustuvorlik va zaxira bajarilishi bo'lgan kaskad rejimini amalga oshirish, dual_size tanlash va nega strategiyalar portfelini shunchaki PnL qo'shish orqali bektestlab bo'lmasligi haqida.
Nega strategiyalar portfeli kerak
Bir nechta strategiya cheklangan kapital uchun raqobatlashadi — ularning ko'pi bo'sh turadi, har bir lahzada faqat bir nechtasi savdo qiladi
Siz strategiyani to'liq konveyerdan o'tkazdingiz. Monte-Karlo butstrap qabul qilinadigan 5-protsentilni ko'rsatdi. Walk-forward namunadan tashqari daromadlarni tasdiqladi. Funding stavkalari hisobga olindi, plato tahlili o'tdi. Strategiya haqiqatan ham ishlaydi.
Lekin u vaqtning 15%-ida savdo qiladi. Qolgan 85% vaqtda sizning kapitalingiz bo'sh turadi.
Ikkinchi strategiyani ishga tushirasizmi? Uchinchisini? O'ninchisini? G'oya aniq. Amalga oshirish — yo'q. Strategiyalar portfeli bitta botning o'zida bo'lmagan muammolarni yaratadi:
- Ziddiyatlar: ikkita strategiya bir juftlik bo'yicha qarama-qarshi pozitsiyalarni ochmoqchi bo'ladi.
- Cheklovlar: birja/tavakkalchilik menejmenti bir vaqtning o'zida dan ortiq bo'lmagan pozitsiyalarga ruxsat beradi.
- Taqsimlash: har bir strategiyaga kapitalning qaysi qismini berish kerak?
- Korrelyatsiya: korrelyatsiyalangan kripto juftliklarida 10 ta strategiya — bu 10 marta diversifikatsiya emas.
Kaskad strategiyasi — bu muammolarni hal qiladigan arxitektura namunasi: asosiy strategiya to'liq pozitsiya hajmini oladi, zaxira strategiya esa bo'sh vaqtni kamaytirilgan pozitsiya bilan to'ldiradi.
Kaskad kontseptsiyasi: Asosiy + Zaxira

Yuqori ishonchlilikdagi strategiya (Asosiy)
Asosiy — qat'iy kirish mezonlariga ega strategiya. Masalan, uchta tasdiqlovchi darajaga ega uch karra vaqt oralig'i: kunlik + 4-soatlik + soatlik signal, volatillik va hajmni filtrlash bilan.
Xususiyatlari:
- Kam bitimlar (bektest davrida o'nlab)
- Bitimga yuqori PnL
- Pozitsiyada bo'lish vaqti kam (5-15%)
- Har bir kirishga yuqori ishonch
Zaxira strategiya
Zaxira — yumshatilgan mezonlarga ega strategiya. Ikki karra vaqt oralig'i, kamroq filtrlar, kengroq chegaralar. U tez-tez savdo qiladi, lekin bitimga nisbatan ustunligi past.
Xususiyatlari:
- Ko'proq bitimlar (davrda yuzlab)
- Bitimga o'rtacha PnL
- Pozitsiyada bo'lish vaqti yuqori (30-50%)
- O'rtacha ishonch — kamaytirilgan pozitsiya hajmi bilan qoplanadi
Kaskad rejimi
timeline: ──────────────────────────────────────────────────
primary: ___████___________________████████____███________
fallback: ███____███████████████████________████___████████
capital: [dual][ full ][ dual_size ][ full ][ dual ]
Asosiy strategiya pozitsiya ochganda — zaxira jim turadi (yoki yopiladi). Asosiy bo'sh turganda — zaxira kamaytirilgan pozitsiya bilan (dual_size) savdo qiladi. Ustuvorlik shartsiz: asosiy har doim zaxirani siqib chiqaradi.
Misollar uchun strategiyalar
Seriya davomida biz uchta strategiyadan foydalandik. Ularning 750 kunlik davr uchun parametrlari:
| Parametr | Strategiya A | Strategiya B | Strategiya C |
|---|---|---|---|
| PnL | +55% | +27% | +300% |
| Bitimlar | ~500 | ~40 | ~400 |
| Savdo vaqti | ~15% | ~5% | ~45% |
| MaxDD | ~0.9% | ~0.75% | ~17% |
| Faol kunga PnL | 0.49%/k | 0.72%/k | 0.89%/k |
| Xarakteri | O'rtacha faollik | Kamdan-kam, yuqori ishonch | Tez-tez, tajovuzkor |
Faol vaqtga nisbatan PnL maqolasida ko'rsatganimizdek, xom PnL bo'yicha va faol kunga PnL bo'yicha reytinglash turli natijalarni beradi. Kaskad orkestratsiyasi uchun muhimi — ikkinchi ko'rsatkich.
Optimal dual_size
dual_size bo'yicha panjara qidiruvi Sharp koeffitsientining cho'qqisini ko'rsatadi — juda katta bo'lsa prosadkani oshiradi, juda kichik bo'lsa bo'sh vaqtni behuda sarflaydi
Tanlash muammosi
dual_size — zaxira strategiya oladigan to'liq pozitsiyaning ulushi. Bu asosiy kaskad parametri:
-
Juda katta (masalan, 0.5 = 50%): asosiy va zaxira bir vaqtda faol bo'lganda, umumiy ekspozitsiya = maqsadning 150%. Prosadka ikki barobar oshadi. Zarar-foyda assimetriyasi buni juda qimmatga tushiradi.
-
Juda kichik (masalan, 0.01 = 1%): zaxira bo'sh vaqtning 85%-ini to'ldiradi, lekin tiyinlab topadi. Kapital amalda bo'sh turadi.
-
Optimal: zaxira asosiy bilan bir vaqtda ishlash paytida prosadkani jiddiy oshirmasdan mazmunli PnL qo'shadi.
Formallashtirish
Belgilaymiz:
- — asosiy strategiyaning vaqt birligiga PnL
- — zaxiraning vaqt birligiga PnL
- — pozitsiyada bo'lish vaqtining ulushi (asosiy)
- — pozitsiyada bo'lish vaqtining ulushi (zaxira)
- — dual_size (0..1)
- — ikkalasi ham pozitsiyada bo'lgan vaqt ulushi
Umumiy kaskad PnL:
Umumiy MaxDD (eng yomon holat — to'liq korrelyatsiya):
Agar umumiy prosadkani bilan cheklasak:
Panjara qidiruvi
Amalda optimal dual_size kaskad bektestda panjara qidiruvi orqali topiladi:
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 curve primary (minute bars)
fallback_equity: np.ndarray, # equity curve fallback (minute bars)
primary_positions: np.ndarray, # 1 = in position, 0 = flat
fallback_positions: np.ndarray,
grid: np.ndarray = np.arange(0.01, 0.30, 0.005),
) -> list[CascadeResult]:
"""
Grid search for dual_size.
primary_equity and fallback_equity are log-returns, minute bars.
"""
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) # minutes per year
) 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 strategiyalari uchun tipik optimum: dual_size 0.05-0.10 oralig'ida (to'liq pozitsiyaning 5-10%). Strategiya B asosiy sifatida (MaxDD 0.75%) va Strategiya A zaxira sifatida (MaxDD 0.9%):
Prosadka cheklovi bog'lovchi emas — optimal qiymat kaskad Sharp koeffitsienti bilan aniqlanadi. Amalda panjara qidiruvi odatda (6.8%) beradi.
Ball asosidagi taqsimlash
Kompozit ball bo'yicha reytinglangan strategiyalar — ishonch tuzatishi kichik namunalarni jazolaydi, funding xarajatlari sof ustunlikni kamaytiradi
Ikkitadan ortiq strategiya bo'lganda, kaskad ball asosidagi taqsimlashga umumlashtiriladi.
Faol vaqtga nisbatan PnL bo'yicha reytinglash
Faol vaqtga nisbatan PnL maqolasida batafsil tasvirlanganidek, strategiya balli quyidagilarni hisobga olib hisoblanadi:
- Faol kunga PnL — kapitaldan foydalanish samaradorligi
- Ishonch tuzatishi — kichik namunalar uchun jarima (t-taqsimot)
- Funding xarajatlari — levarejning haqiqiy narxi (Funding stavkalari)
- MaxLev — prosadkani hisobga olgan holda masshtablash (Zarar-foyda assimetriyasi)
Kamdan-kam strategiyalar uchun ishonch tuzatishi
40 ta bitimga ega Strategiya B jiddiy jarimani talab qiladi. Biz ishonch oralig'ining pastki chegarasidan foydalanamiz:
import scipy.stats as st
import numpy as np
def confidence_factor(trade_returns: np.ndarray, confidence: float = 0.95) -> float:
"""Confidence factor: 0..1, penalty for small samples."""
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))
Funding xarajatini birlashtirish
Doimiy fyucherslarda funding har 8 soatda to'lanadi. levareji va o'rtacha stavkasi bilan:
MaxLev = 55x va o'rtacha funding stavkasi 0.01% bo'lgan Strategiya A uchun:
Faol kunga PnL = 0.49% bo'lganda, sof PnL manfiy: /kun. Strategiya to'liq levarejda foydasiz. Batafsil tahlil Funding stavkalari levarejingizni o'ldiradi maqolasida.
Ko'p strategiyali orkestrator

Arxitektura
Orkestrator savdo juftliklarida strategiyani boshqaradi. Potentsial pozitsiyalarning umumiy soni: . Lekin kapital cheklangan — bir vaqtning o'zida dan ortiq bo'lmagan pozitsiyalarga (slotlarga) ruxsat beriladi.
┌─────────────────────────────────────────────┐
│ ORCHESTRATOR │
│ │
│ Signal Queue (sorted by score): │
│ ┌──────────────────────────────────────┐ │
│ │ 1. Strategy C × ETHUSDT score=223 │ │
│ │ 2. Strategy B × BTCUSDT score=142 │ │
│ │ 3. Strategy A × SOLUSDT score=100 │ │
│ │ 4. Strategy C × BTCUSDT score=89 │ │
│ │ 5. Strategy A × ETHUSDT score=76 │ │
│ └──────────────────────────────────────┘ │
│ │
│ Active Slots (max_parallel = 3): │
│ ┌──────────────────────────────────────┐ │
│ │ Slot 1: Strategy C × ETHUSDT [FULL] │ │
│ │ Slot 2: Strategy B × BTCUSDT [FULL] │ │
│ │ Slot 3: Strategy A × SOLUSDT [DUAL] │ │
│ └──────────────────────────────────────┘ │
│ │
│ Conflict Rules: │
│ - One position per pair │
│ - Primary displaces fallback on same pair │
│ - Higher score wins for cross-pair slots │
└─────────────────────────────────────────────┘
Slotlarni boshqarish
from dataclasses import dataclass, field
from enum import Enum
from typing import Optional
import heapq
import time
class SlotType(Enum):
FULL = "full" # primary strategy, 100% position
DUAL = "dual" # fallback strategy, dual_size position
@dataclass
class Signal:
strategy_id: str
pair: str
direction: str # "long" | "short"
score: float
is_primary: bool # primary or fallback
timestamp: float
@dataclass(order=True)
class Slot:
"""A single orchestrator slot."""
priority: float = field(compare=True) # negative score for min-heap
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:
"""
Multi-strategy orchestrator with cascade mode.
Manages N strategies x M pairs within max_parallel_positions slots.
Primary strategies have unconditional priority over fallback.
"""
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] = {} # pair -> Slot
self.pending_signals: list[Signal] = []
def on_signal(self, signal: Signal) -> Optional[dict]:
"""
Process a new signal. Returns an action or None.
Actions:
- {"action": "open", "pair": ..., "size": ..., "slot_type": ...}
- {"action": "replace", "pair": ..., "close_strategy": ..., "open_strategy": ...}
- None (signal rejected)
"""
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 # existing has higher priority
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 # all active slots have higher scores
def on_exit(self, pair: str) -> None:
"""Strategy closed a position."""
if pair in self.active_slots:
del self.active_slots[pair]
def utilization(self) -> float:
"""Current slot utilization."""
return len(self.active_slots) / self.max_parallel
def fill_efficiency_snapshot(self) -> float:
"""Weighted utilization: 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
Ziddiyatlarni hal qilish
Uch darajali ziddiyat:
1-daraja — Bir juftlik, bir yo'nalish. Yuqori ballga ega strategiya g'olib bo'ladi. Agar ikkalasi ham asosiy bo'lsa — ball g'olibni belgilaydi. Agar biri asosiy, ikkinchisi zaxira bo'lsa — asosiy shartsiz g'alaba qozonadi.
2-daraja — Bir juftlik, qarama-qarshi yo'nalish. Taqiqlangan: bir juftlikda bir vaqtning o'zida ham long, ham short bo'lolmaysiz. Eng yuqori ballga ega strategiya g'olib bo'ladi.
3-daraja — Juftliklararo raqobat. Barcha slotlar band bo'lganda, yangi signal eng past ballga ega slotni siqib chiqaradi. Bu ustuvorlik navbati sifatida ishlaydi.
Kaskad bektestlash: Metodologiya
Birgalikdagi simulyatsiya: qoplanish zonalari bilan asosiy va zaxira equity egri chiziqlari va birlashtirilgan kaskad natijasi
Nega shunchaki PnL qo'shib bo'lmaydi
Sodda yondashuv: har bir strategiyani alohida bektestlash, PnL ni qo'shish. Bu uch sababga ko'ra shishirilgan natija beradi:
-
Vaqt qoplanishi. Asosiy va zaxira bir vaqtda faol bo'lganda, zaxira savdo qilmasligi kerak (yoki dual_size bilan savdo qiladi). Oddiy qo'shish bu qoplanishni e'tiborga olmaydi.
-
Kapital cheklovi. Umumiy pozitsiya cheklangan. Agar 5 ta strategiya bir vaqtda ochilmoqchi bo'lsa, lekin faqat 3 slot bo'lsa — ikkita strategiya kirmaydi. Ularning PnL hisoblanmaydi.
-
Tranzaksiya xarajatlari. Kaskad almashtirish (zaxirani yopish, asosiyni ochish) alohida bektestlarda bo'lmagan qo'shimcha komissiyalarni keltirib chiqaradi.
Birgalikdagi simulyatsiya
To'g'ri kaskad bektest — bu barcha strategiyalarning umumiy vaqt jadvalidagi birgalikdagi simulyatsiyasi:
import numpy as np
from typing import NamedTuple
class Trade(NamedTuple):
strategy: str
pair: str
entry_time: int # minute index
exit_time: int # minute index
pnl_per_minute: float # log-return per minute
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% round-trip
) -> dict:
"""
Joint simulation of cascade portfolio.
Walk through each minute, apply orchestrator rules,
calculate PnL accounting for overlap and slot constraints.
"""
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 = {} # pair -> (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,
}
Almashtirish paytidagi tranzaksiya xarajati
Har bir kaskad almashtirish (zaxiradan -> asosiyga) talab qiladi:
- Zaxira pozitsiyasini yopish: taker komissiyasi (Binance fyucherslarida 0.04%)
- Asosiy pozitsiyani ochish: taker komissiyasi (0.04%)
- Spred: ~0.01-0.02%
Umumiy almashtirish xarajati: almashtirish uchun ~0.06-0.10%. Davr davomida 100 ta almashtirish bo'lganda:
Bu jiddiy summa. Tez-tez almashtiruvchi kaskad tranzaksiya xarajatlari tufayli yagona strategiyadan past natija ko'rsatishi mumkin.
Ko'p juftlikka kengaytirish: M juftlikda N strategiya
N strategiyani M savdo juftligi bilan bog'lovchi tarmoq — korrelyatsiya kuchi samarali diversifikatsiyani belgilaydi
Kombinatsiya makoni
10 juftlikda 3 strategiya = 30 potentsial signal. max_slots = 5 bo'lganda, orkestrator ball bo'yicha eng yuqori 5 tasini tanlaydi. Bu kombinatorika masalasi: har bir lahzada mumkin bo'lgan portfel.
Amalda ochko'z algoritm (ball bo'yicha saralash, yuqoridan pastga to'ldirish) vaqtida optimalga yaqin natijalarni beradi.
Juftliklar orasidagi korrelyatsiya
Kripto juftliklari kuchli korrelyatsiyalangan. BTC tushadi — ETH, SOL, AVAX birga tushadi. Bu 5 ta har xil juftlikda 5 ta long pozitsiya amalda "kripto bozoridagi" bitta katta pozitsiya degani.
Signal korrelyatsiyasi maqolasida batafsil tahlil qilganimizdek, mustaqil pozitsiyalarning samarali soni:
bunda — juftliklar orasidagi o'rtacha korrelyatsiya.
va bo'lganda:
Korrelyatsiyalangan juftliklardagi besh pozitsiya 1.3 mustaqil pozitsiyaga teng. Diversifikatsiya amalda yo'q.
Kaskad uchun amaliy natijalar
def effective_diversification(
positions: list[dict], # [{"pair": "BTCUSDT", "direction": "long"}, ...]
correlation_matrix: np.ndarray,
pair_index: dict[str, int],
) -> float:
"""
Calculate effective diversification of open positions.
Returns:
N_eff / N — diversification coefficient (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
Orkestrator slotlarni to'ldirishda korrelyatsiyani hisobga olishi kerak. Ikki variant bor:
- Diversifikatsiya bonusi: reytinglashda korrelyatsiyalanmagan juftliklardagi strategiyalarning balliga bonus qo'shish.
- Korrelyatsiya chegarasi: korrelyatsiyalangan juftliklardagi bir yo'nalishdagi pozitsiyalar sonini cheklash.
Kaskad optimallashtirish konveyeri
Ma'lumotlarni tayyorlashdan tortib tasdiqlash va jonli orkestratsiyagacha bo'lgan sakkiz bog'langan bosqich — har biri oldingisiga tayanadi
Ma'lumotlardan ishlab chiqarishgacha bo'lgan to'liq konveyer 8 bosqichdan iborat:
0-bosqich: Ma'lumotlarni tayyorlash
Tarixiy ma'lumotlarni yuklash, ko'p vaqt oralig'ida foydalanish uchun Parquet keshini qurish. Samarali keshsiz keyingi bosqichlar chidab bo'lmas darajada sekin bo'ladi.
1-bosqich: TF + Uzunlik (Tepalikka ko'tarilish panjarasi)
Bazaviy vaqt oralig'ini va indikator oynalarining uzunligini tanlash. Qo'pol panjara: TF {1m, 5m, 15m, 1h, 4h} to'plamidan, Length {10, 20, 50, 100, 200} to'plamidan. Eng yaxshi panjara nuqtasidan boshlab tepalikka ko'tarilish.
2-bosqich: Ajratish (Koordinatali tushish, 12 parametr)
Ajratish parametrlarini (kirishlar/chiqishlar) optimallashtirish. 12 parametr bo'yicha koordinatali tushish — indikator chegaralari, filtrlar, stop-losslar, teyk-profitlar. Yuqori o'lchamli deterministik maqsad funksiyalari uchun koordinatali tushish Optuna-dan arzonroq.
3-bosqich: Meta-parametrlar (Koordinatali tushish)
Meta-parametrlar: maksimal ushlab turish vaqti, chiqish uchun minimal PnL, treyling-stop konfiguratsiyasi. Yana koordinatali tushish. Plato tahlili orqali barqarorlikni tekshirish — agar optimum nuqtasimon bo'lsa, strategiya haddan tashqari optimallashtirilgan.
4-bosqich: Kombo optimallashtirish
Juftliklar (Asosiy, Zaxira) bo'yicha panjara qidiruvi. Har bir kombinatsiya uchun: dual_size tanlash, birgalikdagi simulyatsiya orqali kaskad PnL hisoblash.
5-bosqich: Tasdiqlash
Ko'p darajali tasdiqlash:
- Ko'p belgi: strategiya faqat optimallashtirish juftligida emas, 10+ juftlikda sinaladi
- Walk-forward: siljiydigan IS/OOS oynasi
- Parametr barqarorligi: har bir bosqichda plato tahlili
- Monte-Karlo butstrap: kaskad PnL uchun ishonch oraliqlari
- Bektest-jonli parallelligi: bektestni paper trading bilan solishtirish
6-bosqich: Reytinglash va tanlash
Kaskad kombinatsiyalarini ball bo'yicha reytinglash. Top-K kombinatsiyalar 7-bosqichga o'tadi. Ball ishonch tuzatishi, funding xarajatlari va fill_efficiency ni hisobga oladi.
7-bosqich: Orkestratsiya
Yakuniy bosqich: orkestratorni kaskad rejimida strategiya va juftlikda ishga tushirish. Slotlarni boshqarish, ustuvorlik navbati, ziddiyatlarni hal qilish — yuqorida tasvirlanganning barchasi.
Samaradorlik tahlili: Kaskad va individual strategiyalar
Yonma-yon solishtirish: kaskad portfeli bo'sh vaqtdan foydalanish orqali individual strategiyalardan ustun keladi
Nazariy kaskad ustunligi
Asosiy vaqtning -ida PnL/kun = 0.49% bilan savdo qiladi deb faraz qilaylik. Zaxira vaqtda PnL/kun = 0.89% bilan savdo qiladi. Qoplanish = (mustaqillikni faraz qilib).
Yolg'iz asosiy (Strategiya A):
Kaskad (A asosiy + C zaxira):
Kaskad foydasi: zaxiradan PnL ga +31%, prosadka esa minimal oshadi ( MaxDD ga qo'shiladi).
Kaskad yordam bermaydigan holatlar
Kaskad quyidagi holatlarda samarasiz:
- Asosiy vaqtning >80%-ida faol. Bo'sh vaqt kam — zaxira uchun joy yo'q.
- Strategiyalar kuchli korrelyatsiyalangan. Asosiy va zaxira bir vaqtda signal beradi — qoplanish yuqori, zaxira esa aynan asosiy ham bo'sh turganda bo'sh turadi.
- Almashtirish xarajatlari zaxira PnL dan oshib ketadi. Tez-tez almashtirish paytida kaskad komissiyalari zaxira foydasini yeydi.
- dual_size juda kichik. da, zaxira o'z potentsialining 1%-ini topadi — komissiyalardan past.
Solishtirish jadvali
| Konfiguratsiya | Yillik PnL | MaxDD | Sharpe | Almashtirish xarajatlari |
|---|---|---|---|---|
| Yolg'iz Strategiya A | 26.8% | 0.9% | 1.42 | 0 |
| Yolg'iz Strategiya C | 146.1% | 17% | 1.15 | 0 |
| Kaskad A+C (d=0.068) | 35.2% | 2.06% | 1.58 | ~1.2% |
| Kaskad B+A (d=0.068) | 19.4% | 1.36% | 1.71 | ~0.3% |
| 3 strategiyali orkestrator | 48.7% | 3.1% | 1.63 | ~2.1% |
Kaskad A+C: asosiy A zaxira C dan +8.4% oladi. Sharpe bo'sh vaqtdan foydalanish orqali oshadi. MaxDD o'rtacha oshadi ().
Vaqtinchalik diversifikatsiya matematikasi
Yuqoridagi kaskad ustunligi — bo'sh vaqtni to'ldirish — strategiyalar kitobini diversifikatsiya qilishning ikki tubdan farqli usulidan biri, va ular turlicha masshtablanadi. Farqni to'g'ri tushunish kaskadning aniq nimani sotib ola olishini va ola olmasligini ko'rsatadi.
Bazaviy strategiya faol davr bo'yicha o'rtacha qiymati va volatilligi bo'lgan i.i.d. daromadlarni topsin, shuning uchun uning davriy Sharp koeffitsienti . Quyidagi har bir Sharp koeffitsienti bilan yillashtirilgan; ishlab chiqilgan misolda davriga qo'llaniladi (yillashtirilgani ), har bir raqam esa yopiq formula bilan ikki o'nlik belgigacha mos keladigan 2,000,000-davrli simulyatsiyadan olingan.
Vaqtinchalik diversifikatsiya: Bo'sh vaqtni to'ldirish
Vaqtning faqat qismida bozorda bo'lgan — qolganida tekis (nol daromad, lekin nol tavakkalchilik ham) — strategiyaning to'liq vaqt jadvalidagi Sharp koeffitsienti
Bo'sh vaqt o'rtacha qiymatni ga suyultiradi, lekin standart og'ishni faqat ga suyultiradi, shuning uchun nisbat sifatida pasayadi: da yillashtirilgan Sharp dan gacha tushadi. Shuning uchun vaqtning -ida savdo qiluvchi yuqori ishonchli asosiy strategiya to'liq equity egri chizig'ida o'rtacha bo'lib ko'rinadi.
Kaskad bu bo'sh vaqtni to'ldiradi. ta vaqt bo'yicha kesishmaydigan strategiyalarni ular birgalikda butun vaqt jadvalini qoplaydigan qilib joylashtiring — har bir davrda aynan bittasi faol — va birlashtirilgan oqim yana bo'ladi:
qamrov bilan bitta strategiyadan to'liq qamrov bilan ta strategiyaga o'tish Sharpni marta ko'taradi — tasdiqlangan: , , — lekin u da to'yinadi, ya'ni yagona strategiyaning davriy Sharp koeffitsientida. Vaqtinchalik diversifikatsiya kitobning tavakkalchilikka moslashtirilgan daromadini bitta strategiya savdo qilayotgan paytda topgandan yuqoriga ko'tara olmaydi, chunki har bir lahzada siz aynan bitta strategiyani ushlaysiz: o'rtachalash yo'q, dispersiyani kamaytirish yo'q. U sotib oladigani — kapitaldan qayta foydalanish: bitta birlik kapital barcha strategiyani ketma-ket ishga tushiradi. Ularni bir vaqtda ishga tushirish uchun sizga birlik (yoki marta levaraj) kerak bo'lardi; kaskad barcha ustunlikni bitta birlikda yig'adi. Bu — tavakkalchilik g'alabasi emas, kapital samaradorligi g'alabasi.
Korrelyatsion diversifikatsiya: Kaskad voz kechadigan √N
Ikkinchi turi ta strategiyani bir vaqtda ishga tushiradi, kapitalni bo'ladi va ularni har bir lahzada o'rtachalaydi. Juft bo'yicha korrelyatsiyasiga ega tengkorrelyatsiyalangan strategiyalar uchun,
bu korrelyatsiyalanmagan holda () bo'ladi va bo'lgan sari ga qarab siqiladi. Tasdiqlangan: 8 ta korrelyatsiyalanmagan strategiya yillashtirilgan ga yetadi (ya'ni ), lekin da o'sha sakkiztasi faqat ga yetadi — yagona strategiyadan sal yaxshiroq xolos. Bu aynan ko'p juftlik bo'limidagi samarali diversifikatsiya chegirmasi: korrelyatsiya — ga solingan soliq.
Kelly taqsimoti — korrelyatsion optimum. O'rtacha qiymatlar vektori va kovariatsiya bilan, o'sish-optimal og'irliklar , ular erishadigan Sharp esa
bu masshtablashni aniq takrorlaydi (simulyatsiya va yopiq formula mos keladi: 8 ta korrelyatsiyalanmagan uchun , da ). — "har bir lahzada o'rtachalash" yozilganda qanday ko'rinishi.
Kaskad amalda nimani sotib oladi va uning chegarasi
| O'lcham | Vaqtinchalik (kaskad) | Korrelyatsion (parallel) |
|---|---|---|
| Strategiyalar qachon ishlaydi | vaqt bo'yicha kesishmaydi | bir vaqtda |
| Kapital | qayta ishlatiladi (1 birlik hammasini ishga tushiradi) | bo'linadi / levarejlanadi ( birlik) |
| Sharp masshtablanishi | da to'yinadi (bo'sh joyni to'ldiradi, to'liq qamrovga ) | |
| Tavakkalchilikni kamaytirish | yo'q (har bir lahzada bitta strategiya) | , korrelyatsiya bilan chegaralangan |
| Nimani sotib oladi | kapital samaradorligi, bozorda bo'lish vaqti | tavakkalchilikka moslashtirilgan daromad |
Bu ikkalasi ko'paytiriladi. Real orkestrator har bir lahzada ta (kuchsiz korrelyatsiyalangan) strategiyani ishga tushiradi — lahzalik ni sotib oladi — va bu oynalarni vaqt bo'yicha joylashtiradi, kapitalni qayta ishlatadi. Umumiy Sharp lahzalik tuzilma bilan belgilanadi,
da tasdiqlangan (yillashtirilgan , , ), vaqtinchalik joylashtirish esa kapital samaradorligini ko'paytiradi, Sharpni emas.
Chegara qayerda. Sof kaskad — bir vaqtda bitta strategiya faol — umumiy Sharpi aniq ga teng, navbatda qancha strategiya kutib turganidan qat'i nazar. U bozorda bo'lish vaqtini va kapital samaradorligini maksimallashtiradi, lekin uning tavakkalchilikka moslashtirilgan daromadi bitta strategiyanikidan yuqori bo'la olmaydi. dan oshish uchun lahza ichida diversifikatsiya qilish kerak — bir nechta kuchsiz korrelyatsiyalangan strategiyani bir vaqtda ishga tushirish — va bu foyda bilan chegaralanadi va korrelyatsiya bilan yo'qoladi. Dizayn qoidasi shundan kelib chiqadi: bo'sh vaqtni bitta birlik kapitalda arzon qaytarish uchun kaskaddan foydalaning, kamdan-kam uchraydigan, qimmat bir vaqtlik diversifikatsiyani esa faqat haqiqatan ham korrelyatsiyalanmagan strategiyalarga sarflang. Korrelyatsiyalangan strategiyalarni to'plash — vaqt bo'yicha yoki parallel tarzda — amalda deyarli hech narsa bermaydi.
Orkestratsiya: Amaldagi fill_efficiency
~78% to'ldirish samaradorligi: issiqlik xaritasi strategiyalar va juftliklar bo'yicha vaqtdan foydalanishni ko'rsatadi, yorqin katakchalar faol savdoni bildiradi
fill_efficiency parametri orkestratorning bo'sh vaqtning qaysi qismini haqiqatan ham ishlatishini belgilaydi. Faol vaqtga nisbatan PnL maqolasida ko'rsatilganidek, uni uch usulda baholash mumkin:
- Doimiy konstanta (0.80) — qo'pol, lekin universal
- Analitik baho orqali — korrelyatsiyani hisobga oladi
- Ma'lumotlardan simulyatsiya — eng aniq
10 juftlikda 3 ta strategiyasi bo'lgan kaskad uchun:
def cascade_fill_efficiency(
strategies: list[dict], # [{"trading_time": 0.15, "is_primary": True}, ...]
n_pairs: int = 10,
correlation_factor: float = 3.0,
) -> float:
"""Estimate fill_efficiency for a cascade portfolio."""
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}, # Strategy B
{"trading_time": 0.15, "is_primary": True}, # Strategy A
{"trading_time": 0.45, "is_primary": False}, # Strategy C as fallback
]
eff = cascade_fill_efficiency(strategies, n_pairs=10, correlation_factor=3.0)
Amaliy tavsiyalar
Kaskadni joriy etish uchun oltita asosiy tavsiya — kichikdan boshlashdan moslashuvchan qayta kalibrlashgacha
1. Ikkita strategiyadan boshlang
20 juftlikda darhol 10 ta strategiyani ishga tushirmang. 3-5 juftlikda bitta asosiy + bitta zaxiradan boshlang. Birgalikdagi simulyatsiya haqiqiy xatti-harakatga mos kelishiga ishonch hosil qiling. Bektest-jonli parallelligi juda muhim: agar kaskad bektest jonli savdodan hatto 5-10%-ga farq qilsa — orkestrator mantig'ida xato bor.
2. dual_size panjara qidiruvidan, intuitsiyadan emas
Optimal dual_size strategiyalarning aniq juftligiga bog'liq. 6.8% — yo'nalish, universal konstanta emas. 1%-dan 30%-gacha 0.5% qadam bilan panjara qidiruvini o'tkazing va Sharp maksimumini tanlang.
3. Slot chegarasi arxitekturani belgilaydi
max_slots = 1 bo'lganda kaskad oddiy strategiya almashtirishga aylanadi. max_slots = 50 bo'lganda cheklov bog'lovchi emas va masala mustaqil portfelga qisqaradi. Qiziqarli zona: max_slots = 3-10, bu yerda slotlarni boshqarish natijalarga chinakam ta'sir qiladi.
4. Kechikishni hisobga oling
Jonli savdoda kaskad almashtirish bir zumda bo'lmaydi. Zaxira pozitsiyasini yopish + asosiyni ochish = 2 ta API chaqiruvi + tarmoq kechikishi + birja moslashtirilishi. Volatil bozorda narx 200-500ms ichida harakatlanishi mumkin. Slippage byudjetini rejalashtiring.
5. fill_efficiency ni kuzatib boring
Ishlab chiqarishdagi haqiqiy fill_efficiency ni kuzatib boring. Agar u bektestdan sezilarli darajada past bo'lsa — orkestrator kutilganidek bo'sh vaqtdan foydalanmayapti. Sabablari: API kechikishlari, rad etilgan buyurtmalar, marja cheklovlari.
6. Moslashuvchan optimallashtirishdan foydalaning
Kaskad parametrlari (dual_size, ball og'irliklari, slot chegaralari) statik bo'lmasligi kerak. Yangi ma'lumotlar bo'yicha davriy qayta kalibrlash uchun moslashuvchan chuqur kirish dan foydalaning. Bozor o'zgaradi — kaskad parametrlari ham unga mos ravishda o'zgarishi kerak.
"Illyuziyasiz bektestlar" seriyasi: Xulosa
To'liq tizim arxitekturasi: matematikadan tortib tasdiqlash va jonli orkestratsiyagacha bo'lgan 13 ta o'zaro bog'liq modul
Bu maqola 13+ maqoladan iborat seriyaning finalidir. Har bir maqola bektestdan ishlab chiqarishgacha bo'lgan yo'ldagi bitta aniq muammoni hal qildi. Ularning qanday bog'lanishi mana bunday:
Asos: Daromad matematikasi
Zarar-foyda assimetriyasi — daromadlarning multiplikativ tabiati, volatillik sudralishi, Kelly mezoni. Bu — keyingi hammasi uchun matematik asos: nega MaxDD levarejni belgilaydi, nega Sharpe xom PnL dan muhimroq, nega simmetrik R:R bilan 50% g'alaba tezligi foydasiz.
Tasdiqlash: Ishonch oraliqlari va barqarorlik
Monte-Karlo Butstrap — yagona nuqta bahosini ishonch oraliqlari bilan taqsimotga aylantirish. Har qanday ko'rsatkich (PnL, MaxDD, Sharpe) faqat ishonch oralig'i bilan ma'noga ega.
Walk-Forward optimallashtirish — namunadan tashqari tasdiqlash. Tarixiy ma'lumotlardagi bektest — IS natijasi; WFO strategiyaning yangi ma'lumotlarda qanday ishlashini ko'rsatadi.
Plato tahlili — parametr barqarorligini tekshirish. Agar optimum nuqtasimon bo'lsa, strategiya haddan tashqari optimallashtirilgan.
Bektest-jonli parallelligi — bektestni haqiqiy natijalar bilan solishtirish. Masshtablashdan oldingi yakuniy tekshiruv.
Real xarajatlar: Funding va levaraj
Funding stavkalari levarejingizni o'ldiradi — doimiy fyucherslarda levarejning yashirin narxi. Fundingni hisobga olmasdan, chiroyli bektest zararga aylanadi.
Funding stavkasi arbitraji — funding ni birjalararo strategiyalar orqali xarajatdan daromad manbaiga qanday aylantirish mumkin.
Ko'rsatkichlar va reytinglash
Faol vaqtga nisbatan PnL — portfeldagi strategiyalarni reytinglash uchun ko'rsatkich. Xom PnL masshtablanmaydi; PnL/faol kun masshtablanadi.
Signal korrelyatsiyasi — korrelyatsiyalangan juftliklar portfelidagi samarali diversifikatsiya.
Infratuzilma va optimallashtirish
Ko'p vaqt oralig'idagi bektestlar uchun Parquet keshi — tez iteratsiyalar uchun ma'lumot infratuzilmasi.
Moslashuvchan chuqur kirish — moslashuvchan optimallashtirish: qo'pol panjara -> istiqbolli zonalarda aniqlashtirish.
Optuna va koordinatali tushish — optimizator tanlash: shovqinli maqsad funksiyalari bo'lgan past o'lchamlar uchun Optuna, tekis maqsad funksiyalari bo'lgan yuqori o'lchamlar uchun koordinatali tushish.
Polars va Pandas — bektestlash uchun DataFrame operatsiyalarining samaradorligi.
Orkestratsiya (Ushbu maqola)
Kaskad strategiyalar — oldingi barcha komponentlarni ishlaydigan tizimga birlashtirish. Ball asosidagi taqsimlash faol vaqtga PnL ni, ishonch tuzatishini, funding xarajatlarini qo'llaydi. Kaskad rejimi bo'sh vaqtni to'ldiradi. Birgalikdagi simulyatsiya portfelni tasdiqlaydi. Monte-Karlo butstrap kaskad PnL uchun ishonch oraliqlarini beradi.
Har bir maqola — mustaqil modul. Birgalikda ular ma'lumotlarni yuklashdan strategiyalar portfelining jonli orkestratsiyasigacha bo'lgan to'liq konveyerni tashkil qiladi.
Xulosa
Kaskad — strategiyalar portfeliga yagona yondashuv emas. Lekin u eng sodda va amaliy usullardan biri: asosiy strategiya to'liq quvvatda savdo qiladi, zaxira esa bo'sh vaqtni kamaytirilgan pozitsiya bilan to'ldiradi. Ikkita asosiy parametr (dual_size va max_slots) ko'pgina konfiguratsiyalar uchun yetarli moslashuvchanlik beradi.
Uchta asosiy xulosa:
-
Kaskad faqat birgalikdagi simulyatsiya orqali bektestlanishi kerak. Individual PnL ni qo'shish natijalarni shishiradi. Almashtirish xarajatlari, qoplanish, slot cheklovlari — bularning barchasi faqat birgalikdagi simulyatsiyada aks etadi.
-
dual_size PnL va prosadka o'rtasidagi muvozanatni belgilaydi. Tipik optimum — 5-10%. Sharpe bo'yicha panjara qidiruvi — ishonchli tanlash usuli.
-
Orkestrator — ball asosidagi ustuvorlik navbati. Hammasi har bir signal uchun bitta raqamga (ballga) qisqaradi. Score = f(PnL/faol kun, MaxLev, ishonch, funding). Eng yuqori ballga ega strategiyalar slot oladi. Qolganlari kutadi.
"Illyuziyasiz bektestlar" seriyasi bir narsani ko'rsatadi: chiroyli bektest va haqiqiy foyda o'rtasida o'nlab tuzoqlar yotadi. Har bir maqola bittasini olib tashlaydi. Kaskad orkestratsiyasi — oxirgi qadam: tasdiqlangan strategiyalar to'plamini ishlaydigan portfelga aylantirish.
Foydali havolalar
- López de Prado — Advances in Financial Machine Learning: Portfolio Construction
- Pardo, R. — The Evaluation and Optimization of Trading Strategies
- Ernest Chan — Algorithmic Trading: Winning Strategies and Their Rationale
- Perry Kaufman — Trading Systems and Methods, Chapter on Portfolio Allocation
- Tomasini, Jaekle — Trading Systems: A New Approach to System Development and Portfolio Optimisation
- Bailey, D.H. & López de Prado — The Deflated Sharpe Ratio
- Markowitz, H. — Portfolio Selection (1952)
- Kelly, J.L. — A New Interpretation of Information Rate (1956)
Iqtibos
@article{soloviov2026cascadestrategies,
author = {Soloviov, Eugen},
title = {Cascade Strategies: Priority Execution with Fallback Filling},
year = {2026},
url = {https://marketmaker.cc/ru/blog/post/cascade-strategies-orchestration},
version = {0.1.0},
description = {Finale of the "Backtests Without Illusions" series. How to build an orchestrator from N strategies x M pairs, implement cascade mode with priority and fallback filling, choose dual\_size, and why strategy portfolios cannot be backtested by summing PnL.}
}
Authors
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.