← Макалаларга кайтуу
March 8, 2026
5 мүн окуу

Каскаддык стратегиялар: Резерв менен толтуруу аркылуу приоритеттик аткаруу

Каскаддык стратегиялар: Резерв менен толтуруу аркылуу приоритеттик аткаруу
#algotrading
#orchestration
#portfolio
#cascade
#strategies
#slot management

«Иллюзиясыз бэктесттер» сериясынын финалы. N стратегия жана M жуп негизинде оркестраторду кантип куруу керек, приоритет жана резервдик аткаруусу бар каскаддык режимди ишке ашыруу, dual_size тандоо жана эмне үчүн стратегиялар портфелин жөн гана PnL кошуу аркылуу бэктестилөөгө болбойт.

Эмне үчүн стратегиялар портфели керек

Бош турган капиталы бар стратегиялар портфели Бир нече стратегия чектелген капитал үчүн атаандашат — алардын көбү бош турат, ар бир учурда болгону бир нечеси гана соода кылат

Сиз стратегияны толук конвейерден өткөрдүңүз. Монте-Карло бутстрап кабыл алынуучу 5-процентилди көрсөттү. Walk-forward үлгүдөн тышкаркы кирешелерди ырастады. Фандинг чендери эсептелди, плато анализи өттү. Стратегия чындап эле иштейт.

Бирок ал убакыттын 15%-ында гана соода кылат. Калган 85% убакытта сиздин капиталыңыз бош турат.

Экинчи стратегияны иштетүү керекпи? Үчүнчүсүн? Онунчусун? Идея айкын. Ишке ашыруу — жок. Стратегиялар портфели бир боттун өзүндө жок болгон көйгөйлөрдү жаратат:

  • Конфликттер: эки стратегия бир жуп боюнча карама-каршы позицияларды ачкысы келет.
  • Чектөөлөр: биржа/тобокелдик менеджмент бир убакытта KK ашпаган позицияларга гана уруксат берет.
  • Бөлүштүрүү: ар бир стратегияга капиталдын кайсы бөлүгүн берүү керек?
  • Корреляция: корреляцияланган крипто жуптарында 10 стратегия — бул 10 эсе диверсификация эмес.

Каскаддык стратегия — бул көйгөйлөрдү чечкен архитектуралык үлгү: негизги стратегия толук позиция көлөмүн алат, ал эми резервдик стратегия бош убакытты азайтылган позиция менен толтурат.

Каскад түшүнүгү: Негизги + Резерв

Каскаддык стратегиянын убакыт тилкеси

Жогорку ишенимдүүлүктөгү стратегия (Негизги)

Негизги — катуу кирүү критерийлери бар стратегия. Мисалы, үч ырастоочу деңгээли бар үч эселенген убакыт тилкеси: күндүзгү + 4 сааттык + сааттык сигнал, волатилдик жана көлөмдү чыпкалоо менен.

Мүнөздөмөлөрү:

  • Аз бүтүмдөр (бэктест мезгилинде ондогон)
  • Бүтүмгө жогорку PnL
  • Позицияда болуу убактысы аз (5-15%)
  • Ар бир киришке жогорку ишеним

Резервдик стратегия

Резерв — жеңилдетилген критерийлери бар стратегия. Кош убакыт тилкеси, аз чыпкалар, кеңири чектер. Ал көбүрөөк соода кылат, бирок бүтүмгө шаккан артыкчылыгы төмөн.

Мүнөздөмөлөрү:

  • Көбүрөөк бүтүмдөр (мезгилде жүздөгөн)
  • Бүтүмгө орточо PnL
  • Позицияда болуу убактысы жогору (30-50%)
  • Орточо ишеним — азайтылган позиция көлөмү менен компенсацияланат

Каскаддык режим

timeline:  ──────────────────────────────────────────────────
primary:   ___████___________________████████____███________
fallback:  ███____███████████████████________████___████████

capital:   [dual][ full ][ dual_size ][  full  ][ dual  ]

Негизги стратегия позиция ачканда — резерв унчукпай калат (же жабылат). Негизги бош турганда — резерв азайтылган позиция менен (dual_size) соода кылат. Приоритет шексиз: негизги ар дайым резервди ордунан кетирет.

Мисалдар үчүн стратегиялар

Серия боюнча биз үч стратегияны колдондук. Алардын 750 күндүк мезгилге арналган параметрлери:

Параметр Стратегия A Стратегия B Стратегия C
PnL +55% +27% +300%
Бүтүмдөр ~500 ~40 ~400
Соода убактысы ~15% ~5% ~45%
MaxDD ~0.9% ~0.75% ~17%
Активдүү күнгө PnL 0.49%/к 0.72%/к 0.89%/к
Мүнөзү Орточо активдүүлүк Сейрек, жогорку ишеним Кыйла жиш, агрессивдүү

Активдүү убакытка карата PnL макаласында көрсөткөндөй, чийки PnL боюнча жана активдүү күнгө PnL боюнча рейтинг ар башка натыйжаларды берет. Каскаддык оркестрация үчүн маанилүү болгону — экинчи көрсөткүч.

Оптималдуу dual_size

dual_size оптималдаштыруу бети dual_size боюнча тор издөө Шарп коэффициентинин чокусун көрсөтөт — өтө чоңу просадканы жогорулатат, өтө кичинеси бош убакытты текке кетирет

Тандоо көйгөйү

dual_size — резервдик стратегия алган толук позициянын үлүшү. Бул негизги каскад параметри:

  • Өтө чоң (мисалы, 0.5 = 50%): негизги жана резерв бир учурда активдүү болгондо, жалпы экспозиция = максаттын 150%. Просадка эки эсе өсөт. Чыгым-пайда асимметриясы муну өтө кымбат кылат.

  • Өтө кичине (мисалы, 0.01 = 1%): резерв бош убакыттын 85%-ын толтурат, бирок тыйынды гана табат. Капитал иш жүзүндө бош турат.

  • Оптималдуу: резерв негизги менен бир учурда иштөө учурунда просадканы кризистик түрдө жогорулатпай туруп, маанилүү PnL кошот.

Формализация

Белгилейли:

  • PpP_p — негизги стратегиянын убакыт бирдигине PnL
  • PfP_f — резервдин убакыт бирдигине PnL
  • tpt_p — позицияда болуу убактысынын үлүшү (негизги)
  • tft_f — позицияда болуу убактысынын үлүшү (резерв)
  • dd — dual_size (0..1)
  • toverlapt_{overlap} — экөө тең позицияда болгон убакыттын үлүшү

Жалпы каскаддык PnL:

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

Жалпы MaxDD (эң начар учур — толук корреляция):

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

Эгер жалпы просадканы DtargetD_{target} менен чектесек:

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

Тор издөө

Практикада оптималдуу dual_size каскаддык бэктестте тор издөө аркылуу табылат:

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)

Крипто стратегиялары үчүн типтүү оптимум: dual_size 0.05-0.10 диапазонунда (толук позициянын 5-10%). Стратегия B негизги катары (MaxDD 0.75%) жана Стратегия A резерв катары (MaxDD 0.9%):

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

Просадка чектөөсү байланыштыруучу эмес — оптималдуу маани каскаддык Шарп коэффициенти менен аныкталат. Практикада тор издөө көбүнчө d0.068d \approx 0.068 (6.8%) берет.

Балл негизиндеги бөлүштүрүү

Балл негизинде стратегияларды рейтингдөө Композиттик балл боюнча рейтингделген стратегиялар — ишеним көрсөткүчүн тууралоо кичине үлгүлөрдү жазалайт, фандинг чыгымдары таза артыкчылыкты азайтат

Экиден көп стратегия болгондо, каскад балл негизиндеги бөлүштүрүүгө чейин жалпыланат.

Активдүү убакытка карата PnL боюнча рейтингдөө

Активдүү убакытка карата PnL макаласында толук сүрөттөлгөндөй, стратегиянын баллы төмөнкүлөрдү эске алуу менен эсептелет:

  1. Активдүү күнгө PnL — капиталды пайдалануунун натыйжалуулугу
  2. Ишеним көрсөткүчүн тууралоо — кичине үлгүлөр үчүн жаза (t-бөлүштүрүү)
  3. Фандинг чыгымдары — левериждин чыныгы наркы (Фандинг чендери)
  4. MaxLev — просадканы эске алуу менен масштабдоо (Чыгым-пайда асимметриясы)

score=PnLnet/dayнатыйжалуулук×365ffillжылдандыруу×MaxLevмасштаб×cconfишенимдүүлүк\text{score} = \underbrace{\text{PnL}_{net/day}}_{\text{натыйжалуулук}} \times \underbrace{365 \cdot f_{fill}}_{\text{жылдандыруу}} \times \underbrace{\text{MaxLev}}_{\text{масштаб}} \times \underbrace{c_{conf}}_{\text{ишенимдүүлүк}}

Сейрек стратегиялар үчүн ишеним көрсөткүчүн тууралоо

40 бүтүмү бар Стратегия B олуттуу жаза талап кылат. Биз ишеним интервалынын төмөнкү чегин колдонобуз:

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

Фандинг чыгымын бириктирүү

Түбөлүк фьючерстерде фандинг ар 8 саат сайын төлөнөт. LL левериджи жана орточо чен rfr_f болгондо:

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

MaxLev = 55x жана орточо фандинг чени 0.01% болгон Стратегия A үчүн:

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

Активдүү күнгө PnL = 0.49% болгондо, таза PnL терс: 0.49%1.65%=1.16%0.49\% - 1.65\% = -1.16\%/күн. Стратегия толук левериджде пайдасыз. Толук анализ Фандинг чендери сиздин левериджиңизди өлтүрөт макаласында.

Көп стратегиялуу оркестратор

Оркестратордун слот бөлүштүрүүсү жана приоритет кезеги

Архитектура

Оркестратор MM соода жуптарында NN стратегияны башкарат. Мүмкүн болгон позициялардын жалпы саны: N×MN \times M. Бирок капитал чектелген — бир учурда KK ашпаган позицияларга (слоттарга) гана уруксат берилет.

┌─────────────────────────────────────────────┐
│                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    │
└─────────────────────────────────────────────┘

Слоттарды башкаруу

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

Конфликттерди чечүү

Үч деңгээлдүү конфликт:

1-деңгээл — Бир жуп, бир багыт. Жогорку баллы бар стратегия жеңет. Эгер экөө тең негизги болсо — балл жеңүүчүнү аныктайт. Эгер бирөө негизги, экинчиси резерв болсо — негизги шексиз жеңет.

2-деңгээл — Бир жуп, карама-каршы багыт. Тыюу салынган: бир жупта бир учурда лонг да, шорт да боло албайсыз. Эң жогорку баллы бар стратегия жеңет.

3-деңгээл — Жуп аралык атаандаштык. Бардык слоттор бош болбогондо, жаңы сигнал эң төмөн баллы бар слотту ордунан кетирет. Бул приоритет кезеги катары иштейт.

Каскаддык бэктестилөө: Методология

Каскаддык стратегиялардын биргелешкен симуляциясы Биргелешкен симуляция: кабатташуу зоналары бар негизги жана резервдик equity ийри сызыктары жана бириктирилген каскаддык натыйжа

Эмне үчүн жөн гана PnL кошууга болбойт

Жөнөкөй ыкма: ар бир стратегияны өзүнчө бэктестилөө, PnL кошуу. Бул үч себеп боюнча шишиктелген натыйжа берет:

  1. Убакыттын кабатташуусу. Негизги жана резерв бир учурда активдүү болгондо, резерв соода кылбашы керек (же dual_size менен соода кылат). Жөнөкөй кошуу бул кабатташууну эске алган жок.

  2. Капитал чектөөсү. Жалпы позиция чектелген. Эгер 5 стратегия бир учурда ачылгысы келсе, бирок болгону 3 слот болсо — эки стратегия кирбейт. Алардын PnL эсептелбейт.

  3. Транзакция чыгымдары. Каскаддык которуу (резервди жабуу, негизгини ачуу) жеке бэктесттерде жок кошумча комиссияларды жаратат.

Биргелешкен симуляция

Туура каскаддык бэктест — бул бардык стратегиялардын жалпы убакыт тилкесиндеги биргелешкен симуляциясы:

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,
    }

Которуудагы транзакция чыгымы

Ар бир каскаддык которуу (резервден -> негизгиге) талап кылат:

  1. Резерв позициясын жабуу: taker комиссиясы (Binance фьючерстеринде 0.04%)
  2. Негизги позицияны ачуу: taker комиссиясы (0.04%)
  3. Спред: ~0.01-0.02%

Жалпы которуу чыгымы: которуу үчүн ~0.06-0.10%. Мезгил ичинде 100 которуу болгондо:

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

Бул олуттуу сумма. Кыйла жиш которулган каскад транзакция чыгымдарынан улам жалгыз стратегиядан төмөн натыйжа көрсөтүшү мүмкүн.

Көп жуптуу кеңейтүү: M жупта N стратегия

Көп жуптуу стратегия тармагы N стратегияны M соода жубу менен байланыштырган тармак — корреляция күчү натыйжалуу диверсификацияны аныктайт

Комбинация мейкиндиги

10 жупта 3 стратегия = 30 мүмкүн болгон сигнал. max_slots = 5 болгондо, оркестратор балл боюнча эң жогорку 5-ти тандайт. Бул комбинаторикалык маселе: ар бир учурда (305)=142506\binom{30}{5} = 142\,506 мүмкүн болгон портфель.

Практикада гриди алгоритм (балл боюнча иреттөө, жогорудан төмөн карай толтуруу) O(NMlogK)O(N \cdot M \cdot \log K) убактысында оптималдууга жакын натыйжаларды берет.

Жуптар арасындагы корреляция

Крипто жуптары катуу корреляцияланган. BTC түшөт — ETH, SOL, AVAX бирге түшөт. Бул 5 башка жупта 5 лонг позиция иш жүзүндө "крипто рыногундагы" бир чоң позиция дегенди билдирет.

Сигналдар корреляциясы макаласында толук анализделгендей, көз каранды эмес позициялардын натыйжалуу саны:

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

мында ρˉ\bar{\rho} — жуптар арасындагы орточо корреляция.

ρˉ=0.7\bar{\rho} = 0.7 жана N=5N = 5 болгондо:

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

Корреляцияланган жуптардагы беш позиция 1.3 көз каранды эмес позицияга тете. Диверсификация иш жүзүндө жок.

Каскад үчүн практикалык натыйжалар

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


Оркестратор слоттарды толтурганда корреляцияны эске алышы керек. Эки вариант бар:

  1. Диверсификация бонусу: иреттөө учурунда корреляцияланбаган жуптардагы стратегиялардын баллына бонус кошуу.
  2. Корреляция чеги: корреляцияланган жуптардагы бир багыттагы позициялардын санын чектөө.

Каскаддык оптималдаштыруу конвейери

Сегиз этаптуу оптималдаштыруу конвейери Маалыматтарды даярдоодон баштап ырастоо жана тирүү оркестрацияга чейинки сегиз байланышкан этап — ар бири мурункусуна негизделет

Маалыматтардан өндүрүшкө чейинки толук конвейер 8 этаптан турат:

0-этап: Маалыматтарды даярдоо

Тарыхый маалыматтарды жүктөө, көп убакыт тилкелүү жетүү үчүн Parquet кэшин куруу. Натыйжалуу кэштөөсүз кийинки этаптар жол берилгис жай болот.

1-этап: TF + Узундук (Тоо чокусуна чыгуу тору)

Базалык убакыт тилкесин жана индикатор терезелеринин узундугун тандоо. Дөкүрөк тор: TF {1m, 5m, 15m, 1h, 4h} топтомунан, Length {10, 20, 50, 100, 200} топтомунан. Эң жакшы тор чекитинен баштап тоо чокусуна чыгуу.

2-этап: Бөлүнүү (Координаттык түшүү, 12 параметр)

Бөлүнүү параметрлерин (кирүүлөр/чыгуулар) оптималдаштыруу. 12 параметр боюнча координаттык түшүү — индикатор чектери, чыпкалар, стоп-лосстор, тейк-профиттер. Жогорку өлчөмдүү детерминисттик максат функциялары үчүн координаттык түшүү Optuna-дан арзаныраак.

3-этап: Мета-параметрлер (Координаттык түшүү)

Мета-параметрлер: максималдуу кармоо убактысы, чыгуу үчүн минималдуу PnL, трейлинг-стоп конфигурациясы. Дагы координаттык түшүү. Плато анализи аркылуу туруктуулукту текшерүү — эгер оптимум чекиттүү болсо, стратегия ашыкча оптималдаштырылган.

4-этап: Комбо оптималдаштыруу

Жуптар (Негизги, Резерв) боюнча тор издөө. Ар бир комбинация үчүн: dual_size тандоо, биргелешкен симуляция аркылуу каскаддык PnL эсептөө.

5-этап: Ырастоо

Көп деңгээлдүү ырастоо:

6-этап: Рейтингдөө жана тандоо

Каскаддык комбинацияларды балл боюнча рейтингдөө. Топ-K комбинациялар 7-этапка өтөт. Балл ишеним көрсөткүчүн тууралоону, фандинг чыгымдарын жана fill_efficiency-ди эске алат.

7-этап: Оркестрация

Акыркы этап: оркестраторду каскаддык режимде NN стратегия жана MM жупта иштетүү. Слоттарды башкаруу, приоритет кезеги, конфликттерди чечүү — жогоруда сүрөттөлгөндүн бардыгы.

Натыйжалуулук анализи: Каскад менен жеке стратегиялар

Каскад менен жеке стратегиялардын натыйжалуулугун салыштыруу Катар салыштыруу: каскаддык портфель бош убакытты пайдалануу аркылуу жеке стратегиялардан ашып түшөт

Теориялык каскад артыкчылыгы

Негизги убакыттын tp=15%t_p = 15\%-ында PnL/күн = 0.49% менен соода кылат деп эсептейли. Резерв tf=45%t_f = 45\% убакытта PnL/күн = 0.89% менен соода кылат. Кабатташуу = tp×tf=6.75%t_p \times t_f = 6.75\% (көз карандысыздыкты болжолдоо менен).

Жалгыз негизги (Стратегия A):

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

Каскад (A негизги + C резерв):

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\%

Каскад пайдасы: резервден PnL-ге +31%, ал эми просадка минималдуу өсөт (0.068×17%=1.16%0.068 \times 17\% = 1.16\% MaxDD-ге кошулат).

Каскад жардам бербеген учурлар

Каскад төмөнкү учурларда натыйжасыз:

  1. Негизги убакыттын >80%-ында активдүү. Бош убакыт аз — резерв үчүн орун жок.
  2. Стратегиялар катуу корреляцияланган. Негизги жана резерв бир учурда сигнал берет — кабатташуу жогору, ал эми резерв так негизги бош турганда бош турат.
  3. Которуу чыгымдары резерв PnL-ден ашып түшөт. Кыйла жиш которуу учурунда каскад комиссиялары резерв пайдасын жеп коет.
  4. dual_size өтө кичине. d=0.01d = 0.01 болгондо, резерв өз потенциалынын 1%-ын гана табат — комиссиялардан төмөн.

Салыштыруу таблицасы

Конфигурация Жылдык PnL MaxDD Sharpe Которуу чыгымдары
Жалгыз Стратегия A 26.8% 0.9% 1.42 0
Жалгыз Стратегия C 146.1% 17% 1.15 0
Каскад A+C (d=0.068) 35.2% 2.06% 1.58 ~1.2%
Каскад B+A (d=0.068) 19.4% 1.36% 1.71 ~0.3%
3 стратегиялуу оркестратор 48.7% 3.1% 1.63 ~2.1%

Каскад A+C: негизги A резерв C-дан +8.4% алат. Sharpe бош убакытты пайдалануу аркылуу өсөт. MaxDD орточо өсөт (0.9%+0.068×17%2.06%0.9\% + 0.068 \times 17\% \approx 2.06\%).

Убактылуу диверсификациянын математикасы

Жогорудагы каскад артыкчылыгы — бош убакытты толтуруу — стратегиялар китебин диверсификациялоонун эки түп-тамырынан ар башка жолунун бирөө, жана алар ар башкача масштабдалат. Айырманы туура түшүнүү каскаддын так эмнени сатып ала аларын жана сатып ала албасын көрсөтөт.

Базалык стратегия активдүү мезгил боюнча орточо мааниси μ\mu жана волатилдиги σ\sigma бар i.i.d. кирешелерди тапты дейли, ошондуктан анын мезгилдик Шарп коэффициенти s=μ/σs = \mu/\sigma. Төмөндөгү ар бир Шарп коэффициенти 252\sqrt{252} менен жылдандырылган; иштелип чыккан мисалда мезгилине s=0.05s = 0.05 колдонулат (жылдандырылганы s252=0.79s\sqrt{252} = 0.79), ал эми ар бир сан жабык формула менен эки ондук белгиге чейин дал келген 2,000,000-мезгилдик симуляциядан алынган.

Убактылуу диверсификация: Бош убакытты толтуруу

Убакыттын болгону pp бөлүгүндө рынокто болгон — калганында тегиз (нөлдүк киреше, бирок нөлдүк тобокелдик да) — стратегиянын толук убакыт тилкесиндеги Шарп коэффициенти

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

Бош убакыт орточо маанини pp-ге суюлтат, бирок стандарттык четтөөнү болгону p\sqrt{p}-ге суюлтат, ошондуктан катыш p\sqrt{p} катары төмөндөйт: p=0.25p = 0.25 болгондо жылдандырылган Шарп 0.790.79-дан 0.390.39-га чейин түшөт. Ошондуктан убакыттын 15%15\%-ында соода кылган жогорку ишенимдүү негизги стратегия толук equity ийри сызыгында орточо болуп көрүнөт.

Каскад бул бош убакытты толтурат. kk убакыт боюнча кесилишпеген стратегияларды алар чогуу бүтүндөй убакыт тилкесин жаба тургандай орнотуңуз — ар бир мезгилде так бирөө активдүү — жана бириктирилген агым кайрадан N(μ,σ)N(\mu,\sigma) болот:

SRcascade=s(k-дан көз каранды эмес).SR_{\text{cascade}} = s \qquad(\text{$k$-дан көз каранды эмес}).

1/k1/k жабуу менен бир стратегиядан толук жабуу менен kk стратегияга өтүү Шарпты k\sqrt{k} эсе жогорулатат — ырасталган: 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 — бирок ал ss-те каныгат, б.а. жалгыз стратегиянын мезгилдик Шарп коэффициентинде. Убактылуу диверсификация китептин тобокелдикке туураланган кирешесин бир стратегия соода кылып жатканда тапкандан жогору көтөрө албайт, себеби ар бир мезгилде сиз так бир стратегияны кармайсыз: орточолоо жок, дисперсияны азайтуу жок. Ал сатып ала турганы — капиталды кайра пайдалануу: бир бирдик капитал бардык kk стратегияны ирети менен иштетет. Аларды бир мезгилде иштетүү үчүн сизге kk бирдик (же kk эсе левериж) керек болмок; каскад бардык kk артыкчылыкты бир бирдикте чогултат. Бул — тобокелдик жеңиши эмес, капитал натыйжалуулугунун жеңиши.

Корреляциялык диверсификация: Каскад баш тарткан √N

Экинчи түрү NN стратегияны бир мезгилде иштетет, капиталды бөлүштүрөт жана аларды ар бир мезгилде орточолойт. Жуп боюнча ρ\rho корреляциясы бар тең корреляцияланган стратегиялар үчүн,

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

бул корреляцияланбаган учурда (ρ=0\rho = 0) Ns\sqrt{N}\,s болот жана ρ1\rho \to 1 болгон сайын ss-ке карай кысылат. Ырасталган: 8 корреляцияланбаган стратегия жылдандырылган 2.252.25-ке жетет (б.а. 8×0.79\sqrt{8}\times 0.79), бирок ρ=0.5\rho = 0.5 болгондо ошол сегиз болгону 1.041.04-ке жетет — жалгыз стратегиядан бир аз гана жакшы. Бул так көп жуптуу бөлүмдөгү натыйжалуу диверсификация дисконту: корреляция — N\sqrt{N}-га салынган салык.

Kelly бөлүштүрүүсү — корреляциялык оптимум. Орточо маанилер вектору μ\boldsymbol\mu жана ковариация Σ\Sigma болгондо, өсүшкө оптималдуу салмактар w=Σ1μ\mathbf{w}^{*} = \Sigma^{-1}\boldsymbol\mu, ал эми алар жеткен Шарп

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

бул N/(1+(N1)ρ)s\sqrt{N/(1+(N-1)\rho)}\,s масштабдоону так кайталайт (симуляция жана жабык формула дал келет: 8 корреляцияланбаган үчүн 2.252.25, ρ=0.5\rho=0.5 болгондо 1.001.00). Σ1μ\Sigma^{-1}\boldsymbol\mu — "ар бир мезгилде орточолоо" жазылганда кандай көрүнөрү.

Каскад иш жүзүндө эмнени сатып алат жана анын чеги

Өлчөм Убактылуу (каскад) Корреляциялык (параллель)
Стратегиялар качан иштейт убакыт боюнча кесилишпейт бир мезгилде
Капитал кайра пайдаланылат (1 бирдик баарын иштетет) бөлүштүрүлөт / леверижделенет (NN бирдик)
Шарп масштабдоосу ss-те каныгат (бош орунду толтурат, толук жабууга k\sqrt{k}) N/(1+(N1)ρ)s\sqrt{N/(1+(N-1)\rho)}\,\cdot s
Тобокелдикти азайтуу жок (ар бир мезгилде бир стратегия) N\sqrt{N}, корреляция менен чектелген
Эмнени сатып алат капитал натыйжалуулугу, рыногдо болуу убактысы тобокелдикке туураланган киреше

Бул экөө көбөйтүлөт. Чыныгы оркестратор ар бир мезгилде mm (алсыз корреляцияланган) стратегияны иштетет — мезгилдик m\sqrt{m}-ды сатып алат — жана бул терезелерди убакыт боюнча жайгаштырат, капиталды кайра пайдаланат. Жалпы Шарп мезгилдик түзүлүш менен аныкталат,

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

m=1,2,4m = 1, 2, 4 болгондо ырасталган (жылдандырылган 0.790.79, 1.141.14, 1.58=ms1.58 = \sqrt{m}\,s), ал эми убактылуу жайгаштыруу капитал натыйжалуулугун көбөйтөт, Шарпты эмес.

Чек кайда. Таза каскад — бир мезгилде бир стратегия активдүү — жалпы Шарпы так ss-ке тете, кезекте канча стратегия күтүп тургандыгына карабастан. Ал рыногдо болуу убактысын жана капитал натыйжалуулугун максималдаштырат, бирок анын тобокелдикке туураланган кирешеси бир стратегиянын кирешеси менен чектелген. ss-тен ашуу үчүн мезгил ичинде диверсификациялоо керек — бир нече алсыз корреляцияланган стратегияны бир мезгилде иштетүү — жана бул пайда N\sqrt{N} менен чектелет жана корреляция менен жоюлат. Дизайн эрежеси мындан келип чыгат: бош убакытты бир бирдик капиталда арзан кайтаруу үчүн каскадды колдонуңуз, ал эми сейрек кездешкен, кымбат N\sqrt{N} бир мезгилдеги диверсификацияны чын эле корреляцияланбаган стратегияларга гана коротуңуз. Корреляцияланган стратегияларды чогултуу — убакыт боюнча же параллель түрдө — иш жүзүндө эч нерсе бербейт.

Оркестрация: Иш жүзүндөгү fill_efficiency

Толтуруу натыйжалуулугунун көрсөткүчү жана жылуулук картасы ~78% толтуруу натыйжалуулугу: жылуулук картасы стратегиялар жана жуптар боюнча убакытты пайдаланууну көрсөтөт, жарык клеткалар активдүү соодасын билдирет

fill_efficiency параметри оркестратордун бош убакыттын кайсы бөлүгүн чын эле пайдаланарын аныктайт. Активдүү убакытка карата PnL макаласында көрсөтүлгөндөй, аны үч жол менен баалоого болот:

  1. Туруктуу константа (0.80) — дөкүрөк, бирок универсалдуу
  2. Аналитикалык баа (1p)Neff(1-p)^{N_{eff}} аркылуу — корреляцияны эске алат
  3. Маалыматтардан симуляция — эң так

10 жупта 3 стратегиясы бар каскад үчүн:

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)

Практикалык сунуштар

Практикалык инженердик текшерүү тизмеси Каскадды жайгаштыруу үчүн алты негизги сунуш — кичине баштоодон адаптивдүү кайра калибрлөөгө чейин

1. Эки стратегиядан баштаңыз

20 жупта дароо 10 стратегияны иштетпеңиз. 3-5 жупта бир негизги + бир резервден баштаңыз. Биргелешкен симуляциянын чыныгы жүрүм-турумга дал келерине көз жеткириңиз. Бэктест-тирүү паритети абдан маанилүү: эгер каскаддык бэктест тирүү соодадан 5-10%-га ажыраса — оркестратор логикасында ката бар.

2. dual_size тор издөөдөн, интуициядан эмес

Оптималдуу dual_size стратегиялардын так жубуна көз каранды. 6.8% — багыт, универсалдуу константа эмес. 1%-дан 30%-га чейин 0.5% кадам менен тор издөөнү жүргүзүп, Шарп максимумун тандаңыз.

3. Слот чеги архитектураны аныктайт

max_slots = 1 болгондо каскад жөнөкөй стратегия алмаштырууга айланат. max_slots = 50 болгондо чектөө байланыштыруучу эмес, жана маселе көз каранды эмес портфелге чейин азаят. Кызыктуу зона: max_slots = 3-10, мында слоттарды башкаруу натыйжаларга чын эле таасир этет.

4. Кечигүүнү эске алыңыз

Тирүү соодада каскаддык которуу шак эле болбойт. Резерв позициясын жабуу + негизгини ачуу = 2 API чакыруусу + тармак кечигүүсү + биржа дал келтирүүсү. Волатилдүү рынокто баа 200-500мс ичинде кыймылдашы мүмкүн. Слиппедж бюджетин пландаштырыңыз.

5. fill_efficiency-ди көзөмөлдөңүз

Өндүрүштөгү чыныгы fill_efficiency-ди көзөмөлдөңүз. Эгер ал бэктестелгенден олуттуу төмөн болсо — оркестратор күтүлгөндөй бош убакытты пайдаланбайт. Себептери: API кечигүүлөрү, кабыл алынбаган тапшырыктар, маржа чектөөлөрү.

6. Адаптивдүү оптималдаштырууну колдонуңуз

Каскад параметрлери (dual_size, балл салмактары, слот чектери) статикалык болбошу керек. Жаңы маалыматтар боюнча мезгилдүү кайра калибрлөө үчүн адаптивдүү терең түшүү колдонуңуз. Рынок өзгөрөт — каскад параметрлери да ошого жараша өзгөрүшү керек.

«Иллюзиясыз бэктесттер» сериясы: Корутунду

Серия билим картасы Толук система архитектурасы: математикадан ырастоого чейин жана тирүү оркестрацияга чейинки 13 өз ара байланышкан модуль

Бул макала 13+ макаладан турган сериянын финалы. Ар бир макала бэктесттен өндүрүшкө чейинки жолдогу бир так маселени чечти. Алардын кантип байланышары мындай:

Негиз: Киреше математикасы

Чыгым-пайда асимметриясы — кирешелердин мультипликативдик табияты, волатилдик суюлтуусу, Kelly критерийи. Бул — андан аркынын бардыгы үчүн математикалык негиз: эмне үчүн MaxDD левериждди аныктайт, эмне үчүн Sharpe чийки PnL-ден маанилүүрөөк, эмне үчүн симметриялуу R:R менен 50% жеңүү ылдамдыгы пайдасыз.

Ырастоо: Ишеним интервалдары жана бекемдик

Монте-Карло бутстрап — жалгыз чекиттик бааны ишеним интервалдары бар бөлүштүрүүгө айландыруу. Каалагандай көрсөткүч (PnL, MaxDD, Sharpe) болгону ишеним интервалы менен маанилүү.

Walk-Forward оптималдаштыруу — үлгүдөн тышкаркы ырастоо. Тарыхый маалыматтардагы бэктест — IS натыйжасы; WFO стратегиянын жаңы маалыматтарда кандай иштээрин көрсөтөт.

Плато анализи — параметр бекемдигин текшерүү. Эгер оптимум чекиттүү болсо, стратегия ашыкча оптималдаштырылган.

Бэктест-тирүү паритети — бэктестти чыныгы натыйжалар менен салыштыруу. Масштабдоо алдындагы акыркы текшерүү.

Чыныгы чыгымдар: Фандинг жана левериж

Фандинг чендери левериждиңизди өлтүрөт — түбөлүк фьючерстердеги левериждин жашыруун наркы. Фандингди эске албай, кооз бэктест чыгымга айланат.

Фандинг ченинин арбитражы — фандингди биржалар аралык стратегиялар аркылуу чыгымдан киреше булагына кантип айландырса болот.

Көрсөткүчтөр жана рейтингдөө

Активдүү убакытка карата PnL — портфелдеги стратегияларды рейтингдөө үчүн көрсөткүч. Чийки PnL масштабдалбайт; PnL/активдүү күн масштабдалат.

Сигналдар корреляциясы — корреляцияланган жуптар портфелиндеги натыйжалуу диверсификация.

Инфраструктура жана оптималдаштыруу

Көп убакыт тилкелүү бэктесттер үчүн Parquet кэши — тез итерациялар үчүн маалымат инфраструктурасы.

Адаптивдүү терең түшүү — адаптивдүү оптималдаштыруу: дөкүрөк тор -> перспективалуу зоналарда тактоо.

Optuna жана координаттык түшүү — оптимизатор тандоо: ызы-чуулуу максат функциялары бар төмөн өлчөмдөргө Optuna, тегиз максат функциялары бар жогорку өлчөмдөргө координаттык түшүү.

Polars жана Pandas — бэктестилөө үчүн DataFrame операцияларынын натыйжалуулугу.

Оркестрация (Ушул макала)

Каскаддык стратегиялар — мурунку бардык компоненттерди иштеген системага бириктирүү. Балл негизиндеги бөлүштүрүү активдүү убакытка PnL-ди, ишеним көрсөткүчүн тууралоону, фандинг чыгымдарын колдонот. Каскаддык режим бош убакытты толтурат. Биргелешкен симуляция портфелди ырастайт. Монте-Карло бутстрап каскаддык PnL үчүн ишеним интервалдарын берет.

Ар бир макала — көз каранды эмес модуль. Чогуу алар маалыматтарды жүктөөдөн стратегиялар портфелинин тирүү оркестрациясына чейинки толук конвейерди түзөт.

Корутунду

Каскад — стратегиялар портфелине жалгыз ыкма эмес. Бирок ал эң жөнөкөй жана практикалык ыкмалардын бирөө: негизги стратегия толук кубатта соода кылат, резерв бош убакытты азайтылган позиция менен толтурат. Эки негизги параметр (dual_size жана max_slots) көптөгөн конфигурациялар үчүн жетиштүү ийкемдүүлүк берет.

Үч маанилүү корутунду:

  1. Каскад болгону биргелешкен симуляция аркылуу гана бэктестелиши керек. Жеке PnL-ди кошуу натыйжаларды шишиктет. Которуу чыгымдары, кабатташуу, слот чектөөлөрү — мунун бардыгы болгону биргелешкен симуляцияда камтылат.

  2. dual_size PnL менен просадка ортосундагы тең салмактуулукту аныктайт. Типтүү оптимум — 5-10%. Sharpe боюнча тор издөө — ишенимдүү тандоо ыкмасы.

  3. Оркестратор — балл негизиндеги приоритет кезеги. Баары ар бир сигнал үчүн бир санга (баллга) азаят. Score = f(PnL/активдүү күн, MaxLev, ишеним, фандинг). Эң жогорку баллы бар стратегиялар слот алат. Калгандары күтөт.

«Иллюзиясыз бэктесттер» сериясы бир нерсени көрсөтөт: кооз бэктест менен чыныгы пайданын ортосунда ондогон тузак жатат. Ар бир макала бирин жоёт. Каскаддык оркестрация — акыркы кадам: ырасталган стратегиялар жыйындысын иштеген портфелге айландыруу.


Пайдалуу шилтемелер

  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)

Дайынжай

@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

Рынктан бир кадам алдыда болуңуз

AI соода аналитикасы, рынок талдоолору жана платформа жаңылыктары үчүн биздин жаңылыктар бюллетенине жазылыңыз.

Биз сиздин купуялыгыңызды урматтайбыз. Каалаган убакта жазылымдан чыга аласыз.