← Maqolalarga qaytish
March 8, 2026
5 daqiqa o'qish

Kaskad strategiyalar: Zaxira bilan to'ldirishli ustuvor bajarish

Kaskad strategiyalar: Zaxira bilan to'ldirishli ustuvor bajarish
#algotrading
#orchestration
#portfolio
#cascade
#strategies
#slot management

"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

Bo'sh turgan kapitali bor strategiyalar portfeli 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 KK 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

Kaskad strategiyasining vaqt jadvali

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 optimallashtirish sirti 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:

  • PpP_p — asosiy strategiyaning vaqt birligiga PnL
  • PfP_f — zaxiraning vaqt birligiga PnL
  • tpt_p — pozitsiyada bo'lish vaqtining ulushi (asosiy)
  • tft_f — pozitsiyada bo'lish vaqtining ulushi (zaxira)
  • dd — dual_size (0..1)
  • toverlapt_{overlap} — ikkalasi ham pozitsiyada bo'lgan vaqt ulushi

Umumiy kaskad PnL:

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

Umumiy MaxDD (eng yomon holat — to'liq korrelyatsiya):

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

Agar umumiy prosadkani DtargetD_{target} bilan cheklasak:

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

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%):

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

Prosadka cheklovi bog'lovchi emas — optimal qiymat kaskad Sharp koeffitsienti bilan aniqlanadi. Amalda panjara qidiruvi odatda d0.068d \approx 0.068 (6.8%) beradi.

Ball asosidagi taqsimlash

Ball asosida strategiyalarni reytinglash 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:

  1. Faol kunga PnL — kapitaldan foydalanish samaradorligi
  2. Ishonch tuzatishi — kichik namunalar uchun jarima (t-taqsimot)
  3. Funding xarajatlari — levarejning haqiqiy narxi (Funding stavkalari)
  4. MaxLev — prosadkani hisobga olgan holda masshtablash (Zarar-foyda assimetriyasi)

score=PnLnet/daysamaradorlik×365ffillyillashtirish×MaxLevmasshtab×cconfishonchlilik\text{score} = \underbrace{\text{PnL}_{net/day}}_{\text{samaradorlik}} \times \underbrace{365 \cdot f_{fill}}_{\text{yillashtirish}} \times \underbrace{\text{MaxLev}}_{\text{masshtab}} \times \underbrace{c_{conf}}_{\text{ishonchlilik}}

Kamdan-kam strategiyalar uchun ishonch tuzatishi

40 ta bitimga ega Strategiya B jiddiy jarimani talab qiladi. Biz ishonch oralig'ining pastki chegarasidan foydalanamiz:

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:
    """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. LL levareji va o'rtacha rfr_f stavkasi bilan:

Fundingdaily=3rfL\text{Funding}_{daily} = 3 \cdot r_f \cdot L

MaxLev = 55x va o'rtacha funding stavkasi 0.01% bo'lgan Strategiya A uchun:

Fundingdaily=3×0.0001×55=0.0165=1.65%/kun\text{Funding}_{daily} = 3 \times 0.0001 \times 55 = 0.0165 = 1.65\%/\text{kun}

Faol kunga PnL = 0.49% bo'lganda, sof PnL manfiy: 0.49%1.65%=1.16%0.49\% - 1.65\% = -1.16\%/kun. Strategiya to'liq levarejda foydasiz. Batafsil tahlil Funding stavkalari levarejingizni o'ldiradi maqolasida.

Ko'p strategiyali orkestrator

Orkestratorning slot taqsimlash va ustuvorlik navbati

Arxitektura

Orkestrator MM savdo juftliklarida NN strategiyani boshqaradi. Potentsial pozitsiyalarning umumiy soni: N×MN \times M. Lekin kapital cheklangan — bir vaqtning o'zida KK 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

Kaskad strategiyalarning birgalikdagi simulyatsiyasi 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:

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

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

  3. 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:

  1. Zaxira pozitsiyasini yopish: taker komissiyasi (Binance fyucherslarida 0.04%)
  2. Asosiy pozitsiyani ochish: taker komissiyasi (0.04%)
  3. Spred: ~0.01-0.02%

Umumiy almashtirish xarajati: almashtirish uchun ~0.06-0.10%. Davr davomida 100 ta almashtirish bo'lganda:

Switch costs=100×0.0008=8%\text{Switch costs} = 100 \times 0.0008 = 8\%

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

Ko'p juftlik strategiya tarmog'i 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 (305)=142506\binom{30}{5} = 142\,506 mumkin bo'lgan portfel.

Amalda ochko'z algoritm (ball bo'yicha saralash, yuqoridan pastga to'ldirish) O(NMlogK)O(N \cdot M \cdot \log K) 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:

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

bunda ρˉ\bar{\rho} — juftliklar orasidagi o'rtacha korrelyatsiya.

ρˉ=0.7\bar{\rho} = 0.7 va N=5N = 5 bo'lganda:

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

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:

  1. Diversifikatsiya bonusi: reytinglashda korrelyatsiyalanmagan juftliklardagi strategiyalarning balliga bonus qo'shish.
  2. Korrelyatsiya chegarasi: korrelyatsiyalangan juftliklardagi bir yo'nalishdagi pozitsiyalar sonini cheklash.

Kaskad optimallashtirish konveyeri

Sakkiz bosqichli 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:

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 NN strategiya va MM juftlikda ishga tushirish. Slotlarni boshqarish, ustuvorlik navbati, ziddiyatlarni hal qilish — yuqorida tasvirlanganning barchasi.

Samaradorlik tahlili: Kaskad va individual strategiyalar

Kaskad va individual strategiyalar samaradorligini solishtirish Yonma-yon solishtirish: kaskad portfeli bo'sh vaqtdan foydalanish orqali individual strategiyalardan ustun keladi

Nazariy kaskad ustunligi

Asosiy vaqtning tp=15%t_p = 15\%-ida PnL/kun = 0.49% bilan savdo qiladi deb faraz qilaylik. Zaxira tf=45%t_f = 45\% vaqtda PnL/kun = 0.89% bilan savdo qiladi. Qoplanish = tp×tf=6.75%t_p \times t_f = 6.75\% (mustaqillikni faraz qilib).

Yolg'iz asosiy (Strategiya A):

Annual PnL=0.49%×0.15×365=26.8%\text{Annual PnL} = 0.49\% \times 0.15 \times 365 = 26.8\%

Kaskad (A asosiy + C zaxira):

Annual PnL=0.49%×0.15×365+0.068×0.89%×(0.450.0675)×365=26.8%+8.4%=35.2%\text{Annual 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\%

Kaskad foydasi: zaxiradan PnL ga +31%, prosadka esa minimal oshadi (0.068×17%=1.16%0.068 \times 17\% = 1.16\% MaxDD ga qo'shiladi).

Kaskad yordam bermaydigan holatlar

Kaskad quyidagi holatlarda samarasiz:

  1. Asosiy vaqtning >80%-ida faol. Bo'sh vaqt kam — zaxira uchun joy yo'q.
  2. Strategiyalar kuchli korrelyatsiyalangan. Asosiy va zaxira bir vaqtda signal beradi — qoplanish yuqori, zaxira esa aynan asosiy ham bo'sh turganda bo'sh turadi.
  3. Almashtirish xarajatlari zaxira PnL dan oshib ketadi. Tez-tez almashtirish paytida kaskad komissiyalari zaxira foydasini yeydi.
  4. dual_size juda kichik. d=0.01d = 0.01 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 (0.9%+0.068×17%2.06%0.9\% + 0.068 \times 17\% \approx 2.06\%).

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 μ\mu va volatilligi σ\sigma bo'lgan i.i.d. daromadlarni topsin, shuning uchun uning davriy Sharp koeffitsienti s=μ/σs = \mu/\sigma. Quyidagi har bir Sharp koeffitsienti 252\sqrt{252} bilan yillashtirilgan; ishlab chiqilgan misolda davriga s=0.05s = 0.05 qo'llaniladi (yillashtirilgani s252=0.79s\sqrt{252} = 0.79), 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 pp qismida bozorda bo'lgan — qolganida tekis (nol daromad, lekin nol tavakkalchilik ham) — strategiyaning to'liq vaqt jadvalidagi Sharp koeffitsienti

SRfull=ps.SR_{\text{full}} = \sqrt{p}\,\cdot s.

Bo'sh vaqt o'rtacha qiymatni pp ga suyultiradi, lekin standart og'ishni faqat p\sqrt{p} ga suyultiradi, shuning uchun nisbat p\sqrt{p} sifatida pasayadi: p=0.25p = 0.25 da yillashtirilgan Sharp 0.790.79 dan 0.390.39 gacha tushadi. Shuning uchun vaqtning 15%15\%-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. kk ta vaqt bo'yicha kesishmaydigan strategiyalarni ular birgalikda butun vaqt jadvalini qoplaydigan qilib joylashtiring — har bir davrda aynan bittasi faol — va birlashtirilgan oqim yana N(μ,σ)N(\mu,\sigma) bo'ladi:

SRcascade=s(k dan mustaqil).SR_{\text{cascade}} = s \qquad(\text{$k$ dan mustaqil}).

1/k1/k qamrov bilan bitta strategiyadan to'liq qamrov bilan kk ta strategiyaga o'tish Sharpni k\sqrt{k} marta ko'taradi — tasdiqlangan: k=21.42×k=2 \to 1.42\times, k=41.96×k=4 \to 1.96\times, k=82.93×k=8 \to 2.93\times — lekin u ss 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 kk strategiyani ketma-ket ishga tushiradi. Ularni bir vaqtda ishga tushirish uchun sizga kk birlik (yoki kk marta levaraj) kerak bo'lardi; kaskad barcha kk ustunlikni bitta birlikda yig'adi. Bu — tavakkalchilik g'alabasi emas, kapital samaradorligi g'alabasi.

Korrelyatsion diversifikatsiya: Kaskad voz kechadigan √N

Ikkinchi turi NN ta strategiyani bir vaqtda ishga tushiradi, kapitalni bo'ladi va ularni har bir lahzada o'rtachalaydi. Juft bo'yicha ρ\rho korrelyatsiyasiga ega tengkorrelyatsiyalangan strategiyalar uchun,

SRparallel=N1+(N1)ρ  s,SR_{\text{parallel}} = \sqrt{\frac{N}{1 + (N-1)\rho}}\;\cdot s,

bu korrelyatsiyalanmagan holda (ρ=0\rho = 0) Ns\sqrt{N}\,s bo'ladi va ρ1\rho \to 1 bo'lgan sari ss ga qarab siqiladi. Tasdiqlangan: 8 ta korrelyatsiyalanmagan strategiya yillashtirilgan 2.252.25 ga yetadi (ya'ni 8×0.79\sqrt{8}\times 0.79), lekin ρ=0.5\rho = 0.5 da o'sha sakkiztasi faqat 1.041.04 ga yetadi — yagona strategiyadan sal yaxshiroq xolos. Bu aynan ko'p juftlik bo'limidagi samarali diversifikatsiya chegirmasi: korrelyatsiya — N\sqrt{N} ga solingan soliq.

Kelly taqsimoti — korrelyatsion optimum. O'rtacha qiymatlar vektori μ\boldsymbol\mu va kovariatsiya Σ\Sigma bilan, o'sish-optimal og'irliklar w=Σ1μ\mathbf{w}^{*} = \Sigma^{-1}\boldsymbol\mu, ular erishadigan Sharp esa

SRKelly=μΣ1μ,SR_{\text{Kelly}} = \sqrt{\boldsymbol\mu^{\top} \Sigma^{-1} \boldsymbol\mu},

bu N/(1+(N1)ρ)s\sqrt{N/(1+(N-1)\rho)}\,s masshtablashni aniq takrorlaydi (simulyatsiya va yopiq formula mos keladi: 8 ta korrelyatsiyalanmagan uchun 2.252.25, ρ=0.5\rho=0.5 da 1.001.00). Σ1μ\Sigma^{-1}\boldsymbol\mu — "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 (NN birlik)
Sharp masshtablanishi ss da to'yinadi (bo'sh joyni to'ldiradi, to'liq qamrovga k\sqrt{k}) N/(1+(N1)ρ)s\sqrt{N/(1+(N-1)\rho)}\,\cdot s
Tavakkalchilikni kamaytirish yo'q (har bir lahzada bitta strategiya) N\sqrt{N}, korrelyatsiya bilan chegaralangan
Nimani sotib oladi kapital samaradorligi, bozorda bo'lish vaqti tavakkalchilikka moslashtirilgan daromad

Bu ikkalasi ko'paytiriladi. Real orkestrator har bir lahzada mm ta (kuchsiz korrelyatsiyalangan) strategiyani ishga tushiradi — lahzalik m\sqrt{m} ni sotib oladi — va bu oynalarni vaqt bo'yicha joylashtiradi, kapitalni qayta ishlatadi. Umumiy Sharp lahzalik tuzilma bilan belgilanadi,

SRbook=m1+(m1)ρ  s,SR_{\text{book}} = \sqrt{\frac{m}{1 + (m-1)\rho}}\;\cdot s,

m=1,2,4m = 1, 2, 4 da tasdiqlangan (yillashtirilgan 0.790.79, 1.141.14, 1.58=ms1.58 = \sqrt{m}\,s), vaqtinchalik joylashtirish esa kapital samaradorligini ko'paytiradi, Sharpni emas.

Chegara qayerda. Sof kaskad — bir vaqtda bitta strategiya faol — umumiy Sharpi aniq ss 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. ss dan oshish uchun lahza ichida diversifikatsiya qilish kerak — bir nechta kuchsiz korrelyatsiyalangan strategiyani bir vaqtda ishga tushirish — va bu foyda N\sqrt{N} 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 N\sqrt{N} 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

To'ldirish samaradorligi ko'rsatkichi va issiqlik xaritasi ~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:

  1. Doimiy konstanta (0.80) — qo'pol, lekin universal
  2. Analitik baho (1p)Neff(1-p)^{N_{eff}} orqali — korrelyatsiyani hisobga oladi
  3. 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

Amaliy muhandislik tekshirish ro'yxati 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

Seriya bilim xaritasi 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:

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

  2. dual_size PnL va prosadka o'rtasidagi muvozanatni belgilaydi. Tipik optimum — 5-10%. Sharpe bo'yicha panjara qidiruvi — ishonchli tanlash usuli.

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

  1. López de Prado — Advances in Financial Machine Learning: Portfolio Construction
  2. Pardo, R. — The Evaluation and Optimization of Trading Strategies
  3. Ernest Chan — Algorithmic Trading: Winning Strategies and Their Rationale
  4. Perry Kaufman — Trading Systems and Methods, Chapter on Portfolio Allocation
  5. Tomasini, Jaekle — Trading Systems: A New Approach to System Development and Portfolio Optimisation
  6. Bailey, D.H. & López de Prado — The Deflated Sharpe Ratio
  7. Markowitz, H. — Portfolio Selection (1952)
  8. 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.}
}
blog.disclaimer

Authors

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

Bozordan bir qadam oldinda bo'ling

Sun'iy intellekt savdo tahlillari, bozor tahlili va platforma yangiliklari uchun bizning xabarnomaga obuna bo'ling.

Biz sizning maxfiyligingizni hurmat qilamiz. Istalgan vaqtda obunadan chiqishingiz mumkin.