Активдүү убакыт боюнча PnL: стратегиялардын рейтингин өзгөрткөн көрсөткүч
Сизде эки стратегия бар. Биринчиси: PnL +300%, 418 бүтүм, позиция убакыттын 45%-ында ачык. Экинчиси: PnL +27%, 38 бүтүм, позиция убакыттын 5%-ында ачык. Кайсынысы жакшыраак?
Эгер сиз биринчисин тандаган болсоңуз — жообуңуз туура эмес. Себеби төмөндө.
Таза PnL менен байланышкан көйгөй
Таза PnL — бүт бэктест мезгилиндеги жалпы кирешелүүлүк — стратегиянын убакыттын кайсы бөлүгүндө позицияда болгонун эске албайт. +300% жана 45% соода убактысы бар стратегия капиталыңызды жарымынан аз убакытта колдонот. Калган 55% убакытта капитал бош турат.
+27% жана 5% соода убактысы бар стратегия капиталды 5% гана убакытта колдонот — бирок калган 95% убакыт башка стратегиялар үчүн жеткиликтүү.
Эгер сиз стратегиялар портфелин оркестратор аркылуу иштетсеңиз, бир стратегиянын бош убактысын башкалары толтурат. Ошондо негизги көрсөткүч стратегия бир жылда канча тапканы эмес, ал активдүү убакыттын бир бирдигине канча табары болуп калат.
Натыйжалуу кирешелүүлүк формуласы

Негизги эсептөө
мында:
- Active days — позицияларда өткөрүлгөн жалпы убакыт (күн менен)
- fill_efficiency — оркестратор сигналдар менен толтура ала турган убакыттын үлүшү (0...1)
def pnl_per_active_time(
total_pnl: float, # total PnL, %
test_period_days: int, # backtest length, days
trading_time_pct: float, # fraction of active time, 0..1
fill_efficiency: float = 0.80, # slot fill efficiency
) -> dict:
"""
Calculate effective return per active time.
"""
active_days = test_period_days * trading_time_pct
pnl_per_day = total_pnl / active_days
annualized_raw = pnl_per_day * 365
annualized_effective = annualized_raw * fill_efficiency
return {
"active_days": active_days,
"pnl_per_day": pnl_per_day,
"annualized_raw": annualized_raw,
"annualized_effective": annualized_effective,
}
Реалдуу стратегияларды кайра эсептөө
Мезгил: 750 күн (25 ай), fill_efficiency = 0.80:
| Стратегия | PnL | Соода убактысы | Active days | PnL/күн | Жылдык (x0.8) |
|---|---|---|---|---|---|
| Стратегия C | +300% | 45% | 337.5 | 0.89%/к | 259% |
| Стратегия B | +27% | 5% | 37.5 | 0.72%/к | 210% |
| Стратегия A | +58% | 15% | 112.5 | 0.51%/к | 150% |
Таза PnL боюнча: Стратегия C (300%) >> Стратегия A (58%) >> Стратегия B (27%). Натыйжалуу кирешелүүлүк боюнча: Стратегия C (259%) > Стратегия B (210%) > Стратегия A (150%).
27% PnL менен Стратегия B 300% PnL менен Стратегия C менен барабар болуп чыгат — анткени ал ошол эле акчаны 9 эсе аз активдүү убакытта табат. Калган 95% убакытты башка стратегиялар менен толтурууга болот.
Сызыктуу жана курама экстраполяция
Жогорудагы формула сызыктуу. Ал жөнөкөй жана консервативдүү. Курама варианты пайданы кайра инвестициялоону эске алат:
import numpy as np
def compound_annualized(total_pnl_pct, active_days, fill_efficiency=0.80):
"""Compound extrapolation."""
daily_return = (1 + total_pnl_pct / 100) ** (1 / active_days) - 1
annualized = (1 + daily_return) ** (365 * fill_efficiency) - 1
return annualized * 100
b_compound = compound_annualized(27, 37.5)
c_compound = compound_annualized(300, 337.5)
Курама экстраполяция менен Стратегия B Стратегия C-ни артта калтырат: 540% каршы 231%. Рейтинг тескерисинче болуп калат.
Сунуш: рейтингтөө үчүн сызыктуу экстраполяцияны колдонуңуз. Ал консервативдүү жана бүтүмдөрдүн саны аз болгондо overfitting-ти сыйлоого азыраак ыктымал.
Тузак: бүтүмдөрдүн саны аз
38 бүтүмү жана PnL/күн = 0.72% болгон Стратегия B тартымдуу көрүнөт. Бирок 38 бүтүм статистикалык жактан алсыз үлгү. Жогорку PnL/күн ийгиликтүү кокустуктун натыйжасы болушу мүмкүн.
Ишенимге ылайыкташтырылган баалоо
Кичине үлгүлөрдү жазалоо үчүн t-бөлүштүрүүнү колдонобуз:
мында — бир бүтүмгө орточо кирешелүүлүк, — стандарттык четтөө, — бүтүмдөрдүн саны, — t-бөлүштүрүүнүн квантили.
import scipy.stats as st
import numpy as np
def confidence_adjusted_score(
trade_returns: list,
test_period_days: int,
fill_efficiency: float = 0.80,
min_trades: int = 30,
confidence: float = 0.95,
) -> dict:
"""
Strategy ranking with sample size adjustment.
"""
n = len(trade_returns)
if n < min_trades:
return {"score": 0, "reason": f"Too few trades ({n} < {min_trades})"}
returns = np.array(trade_returns)
mean_ret = np.mean(returns)
se = np.std(returns, ddof=1) / np.sqrt(n)
alpha = 1 - confidence
t_crit = st.t.ppf(1 - alpha / 2, df=n - 1)
ci_lower = mean_ret - t_crit * se
if mean_ret <= 0:
confidence_factor = 0
else:
confidence_factor = max(0, ci_lower / mean_ret)
total_pnl = np.sum(returns)
hold_times = [...] # holding hours for each trade
active_days = sum(hold_times) / 24
pnl_per_day = total_pnl / active_days if active_days > 0 else 0
annualized = pnl_per_day * 365 * fill_efficiency
score = annualized * max_leverage * confidence_factor
return {
"score": score,
"annualized": annualized,
"confidence_factor": confidence_factor,
"ci_lower": ci_lower,
"n_trades": n,
}
Ишеним ылайыкташтыруунун таасири
| Стратегия | Бүтүмдөр | Орт. кирешелүүлүк | SE | CI төмөн | Ишен. фактор | Ылайыкташт. упай |
|---|---|---|---|---|---|---|
| Стратегия B | 38 | 0.71% | 0.28% | 0.14% | 0.20 | 210% x 0.20 = 42% |
| Стратегия C | 418 | 0.72% | 0.05% | 0.62% | 0.86 | 259% x 0.86 = 223% |
| Стратегия A | 491 | 0.12% | 0.02% | 0.08% | 0.67 | 150% x 0.67 = 100% |
Ишеним ылайыкташтыруудан кийин Стратегия C ишенимдүү түрдө алдыда: 418 бүтүм тар CI жана жогорку ишеним факторун берет. 38 бүтүмү бар Стратегия B жазаланат — анын "сонун" натыйжасы дисперсиянын натыйжасы болушу мүмкүн.
fill_efficiency: аны кайдан алуу керек

fill_efficiency параметри мындай суроого жооп берет: "Оркестратор капиталды канча убакыт иштетип тура алат?"
1-вариант: туруктуу маани
Эң жөнөкөй ыкма: бардык стратегиялар үчүн fill_efficiency = 0.80. Оркестратор бош убакыттын 80%-ын башка стратегиялар/жуптар менен колдонот деп болжолдойт.
Артыкчылыгы: бардыгы үчүн бирдей, салыштырууга оңой. Кемчилиги: стратегиялардын ортосундагы корреляцияны эске албайт.
2-вариант: аналитикалык баа
Эгер сизде жуп болсо, ар бири убакыт активдүү болсо, кеминде биринин активдүү болуу ыктымалдуулугу:
Бирок криптовалюталар жогорку деңгээлде корреляцияланган — BTC ETH, SOL жана калгандарын өзү менен чогуу тартат. Көз карандысыз жуптардын натыйжалуу саны:
def estimate_fill_efficiency(
trading_time_pct: float,
n_pairs: int,
correlation_factor: float = 3.0, # crypto — high correlation
max_slots: int = 10,
) -> float:
"""
Analytical estimate of fill_efficiency.
Args:
trading_time_pct: fraction of active time for one strategy
n_pairs: number of trading pairs
correlation_factor: correlation coefficient (1=independent, 5=strong)
max_slots: maximum number of simultaneous positions
"""
effective_n = n_pairs / correlation_factor
p_at_least_one = 1 - (1 - trading_time_pct) ** effective_n
expected_active = effective_n * trading_time_pct
utilization = min(expected_active, max_slots) / max_slots
return min(p_at_least_one, utilization)
eff_b = estimate_fill_efficiency(0.05, 10, 3.0)
eff_c = estimate_fill_efficiency(0.45, 10, 3.0)
5% активдүүлүгү жана 10 корреляцияланган жуптары бар Стратегия B үчүн fill_efficiency болгону ~16%. Бул натыйжалуу кирешелүүлүктү кескин төмөндөтөт.
3-вариант: маалыматтардан симуляция
Эң так ыкма — бардык стратегияларды бардык жуптарда иштетип, реалдуу слот пайдалануусун эсептөө:
def simulate_fill_efficiency(
all_signals: dict, # {(strategy, pair): [(entry_time, exit_time), ...]}
max_slots: int = 10,
test_period_minutes: int = 750 * 24 * 60,
) -> float:
"""
Simulate real orchestrator slot utilization.
"""
timeline = np.zeros(test_period_minutes)
for signals in all_signals.values():
for entry_min, exit_min in signals:
timeline[entry_min:exit_min] += 1
capped = np.minimum(timeline, max_slots)
fill_efficiency = np.mean(capped) / max_slots
return fill_efficiency
Акыркы рейтинг формуласы
Бардык бөлүктөрдү бириктирүү:
def strategy_score(
trades: list,
test_period_days: int,
fill_efficiency: float = 0.80,
min_trades: int = 30,
funding_rate: float = 0.0001,
) -> float:
"""
Final score for strategy ranking.
Accounts for:
- PnL per active day (capital usage efficiency)
- MaxLev (risk-adjusted scaling)
- Confidence adjustment (penalty for small sample)
- Funding costs (realistic costs at leverage)
"""
n = len(trades)
if n < min_trades:
return 0
returns = np.array([t.pnl_pct for t in trades])
hold_hours = np.array([t.hold_hours for t in trades])
total_pnl = np.sum(returns)
active_days = np.sum(hold_hours) / 24
pnl_per_day = total_pnl / active_days
equity = np.cumprod(1 + returns / 100)
peak = np.maximum.accumulate(equity)
max_dd = ((equity - peak) / peak).min()
max_lev = max(1, int(50 / abs(max_dd * 100)))
funding_daily = funding_rate * 3 * max_lev * 100 # in %
net_pnl_per_day = pnl_per_day - funding_daily
annualized = net_pnl_per_day * 365 * fill_efficiency
se = np.std(returns, ddof=1) / np.sqrt(n)
mean_ret = np.mean(returns)
if mean_ret <= 0:
return 0
t_crit = st.t.ppf(0.975, df=n - 1)
ci_lower = mean_ret - t_crit * se
conf_factor = max(0, ci_lower / mean_ret)
score = annualized * max_lev * conf_factor
return score
Сериянын башка көрсөткүчтөрү менен байланышы
Бул көрсөткүч мурунку макалалардын куралдарын алмаштырбайт, тескерисинче толуктайт:
-
Loss-Profit Asymmetry: максималдуу просадка MaxLev-ди аныктайт, ал упай формуласына кирет. Просадка канчалык терең болсо, упай ошончолук төмөн болот — сызыктуу эмес түрдө, калыбына келүү асимметриясынан улам.
-
Monte Carlo bootstrap: бутстраптан алынган ишеним интервалдары t-бөлүштүрүүгө караганда ишеним факторунун так баасын берет. t-бөлүштүрүүнүн CI-син бутстраптын 5-процентили менен алмаштырса болот.
-
Funding rates: фандинг чыгымдары активдүү күнгө PnL-ден чегерилет. Жогорку левередж жана төмөн PnL/күн менен фандинг таза упайды терс кылышы мүмкүн — стратегия оң таза PnL-ге карабастан чынында чыгаша алып келет.
Бул оркестрация үчүн эмне үчүн маанилүү
Активдүү убакыт боюнча PnL — оркестраторго стратегияларды рейтингтөө үчүн негизги көрсөткүч. Бир нече стратегия бир эле слот үчүн атаандашканда, эң жогорку упайы бар стратегия (ишеним ылайыкташтырууну эске алуу менен) жеңет.
Практикада бул күтүлбөгөн чечимдерге алып келет: жалпы PnL "орточо" болсо да, позицияда болуу убактысы кыска стратегиялар көбүнчө жогорку PnL, бирок узак позициялары бар "жаркыраган" стратегиялардан артыкчылык алат. Биринчилери ондогон стратегиялар портфелинде капиталды натыйжалуураак колдонот.
Негизги түшүнүк: масштабдала турган жалгыз көрсөткүч — активдүү күнгө PnL. Таза PnL масштабдалбайт: сиз бир эле стратегияны эки жолу иштете албайсыз. Бирок бош убакытты башка стратегиялар менен толтура аласыз — жана активдүү күнгө PnL портфелде канча табарыңызды так болжолдойт.
Корутунду
Таза жылдык PnL — ыңгайлуу, бирок алдамчы көрсөткүч. Ал соодагердин эң маанилүү ресурсун — капитал иштеген убакытты эске албайт.
Үч негизги корутунду:
-
Активдүү күнгө PnL эсептеңиз. Позицияда 38 күндө +27% берген стратегия = +0.72%/күн. 338 күндө +300% берген стратегия = +0.89%/күн. Айырма 11 эсе эмес, 1.2 эсе.
-
fill_efficiency эске алыңыз. Корреляцияланган крипто жуптарынын портфелинде fill_efficiency көрүнгөндөн төмөн. 10 жуп 10 эсе диверсификацияга барабар эмес. correlation_factor = 3 болгондо, жуптардын натыйжалуу саны болгону ~3.
-
Кичине үлгүлөрдү жазалаңыз. Орточо +0.71% менен 38 бүтүм +0.14%-дан +1.28%-га чейинки CI берет. +0.72% менен 418 бүтүм +0.62%-дан +0.82%-га чейинки CI берет. Орточо маанилер дээрлик бирдей болсо да, экинчи стратегия ишенимдүүрөөк.
Активдүү убакыт боюнча PnL көрсөткүчү PnL@MaxLev-ди алмаштырбайт — ал капиталды пайдалануу натыйжалуулугу өлчөмүн кошуу менен толуктайт. Жалгыз стратегия үчүн PnL@ML жетиштүү. Стратегиялар портфели үчүн активдүү убакыт боюнча PnL зарыл.
Шилтемелер
- Lopez de Prado — Advances in Financial Machine Learning: The Sharpe Ratio
- Pardo, R. — The Evaluation and Optimization of Trading Strategies
- Bailey, D.H. & Lopez de Prado — The Deflated Sharpe Ratio
- Kelly, J.L. — A New Interpretation of Information Rate (1956)
- Quantopian — Lecture on Strategy Evaluation Metrics
- Ernest Chan — Algorithmic Trading: Portfolio Management
Шилтеме көрсөтүү
@article{soloviov2026pnlactivetime,
author = {Soloviov, Eugen},
title = {PnL by Active Time: The Metric That Changes Strategy Rankings},
year = {2026},
url = {https://marketmaker.cc/ru/blog/post/pnl-active-time-metric},
version = {0.1.0},
description = {Why raw annual PnL is a poor metric for comparing strategies with different trading time. How to calculate effective return, why you need fill\_efficiency, and why a strategy with 27\% PnL can outperform one with 300\%.}
}
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.