Faol vaqt bo'yicha PnL: strategiyalar reytingini o'zgartiradigan ko'rsatkich
Sizda ikkita strategiya bor. Birinchisi: PnL +300%, 418 bitim, pozitsiya vaqtning 45%-ida ochiq. Ikkinchisi: PnL +27%, 38 bitim, pozitsiya vaqtning 5%-ida ochiq. Qaysi biri yaxshiroq?
Agar siz birinchisini tanlagan bo'lsangiz — javobingiz noto'g'ri. Mana sababi.
Xom PnL bilan bog'liq muammo
Xom PnL — butun bektest davridagi umumiy daromad — strategiya vaqtning qancha qismida pozitsiyada bo'lganini hisobga olmaydi. +300% va 45% savdo vaqtiga ega strategiya kapitalingizdan yarmidan kamroq vaqt foydalanadi. Qolgan 55% vaqt davomida kapital bo'sh turadi.
+27% va 5% savdo vaqtiga ega strategiya kapitaldan atigi 5% vaqt foydalanadi — ammo qolgan 95% vaqt boshqa strategiyalar uchun mavjud bo'ladi.
Agar siz strategiyalar portfelini orkestrator orqali boshqarsangiz, bir strategiyaning bo'sh vaqti boshqalari tomonidan to'ldiriladi. Shunda asosiy ko'rsatkich strategiya bir yilda qancha topgani emas, balki faol vaqtning bir birligiga qancha topishi bo'ladi.
Samarali daromad formulasi

Asosiy hisoblash
bu yerda:
- Active days — pozitsiyalarda o'tkazilgan umumiy vaqt (kunlarda)
- fill_efficiency — orkestrator signallar bilan to'ldira oladigan vaqt ulushi (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,
}
Haqiqiy strategiyalarni qayta hisoblash
Davr: 750 kun (25 oy), fill_efficiency = 0.80:
| Strategiya | PnL | Savdo vaqti | Active days | PnL/kun | Yillik (x0.8) |
|---|---|---|---|---|---|
| Strategiya C | +300% | 45% | 337.5 | 0.89%/k | 259% |
| Strategiya B | +27% | 5% | 37.5 | 0.72%/k | 210% |
| Strategiya A | +58% | 15% | 112.5 | 0.51%/k | 150% |
Xom PnL bo'yicha: Strategiya C (300%) >> Strategiya A (58%) >> Strategiya B (27%). Samarali daromad bo'yicha: Strategiya C (259%) > Strategiya B (210%) > Strategiya A (150%).
27% PnL bilan Strategiya B 300% PnL bilan Strategiya C bilan taqqoslanadigan bo'lib chiqadi — chunki u xuddi shu pulni 9 marta kamroq faol vaqtda topadi. Qolgan 95% vaqtni boshqa strategiyalar bilan to'ldirish mumkin.
Chiziqli va murakkab ekstrapolatsiya
Yuqoridagi formula chiziqli. U soddaroq va konservativroq. Murakkab varianti foydani qayta investitsiya qilishni hisobga oladi:
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)
Murakkab ekstrapolatsiya bilan Strategiya B Strategiya C-ni ortda qoldiradi: 540% ga qarshi 231%. Reyting teskari bo'lib qoladi.
Tavsiya: reyting uchun chiziqli ekstrapolatsiyadan foydalaning. U konservativroq va bitimlar soni kam bo'lganda overfitting-ni mukofotlashga kamroq moyil.
Tuzoq: bitimlar sonining kamligi
38 bitimi va PnL/kun = 0.72% bo'lgan Strategiya B jozibali ko'rinadi. Ammo 38 bitim statistik jihatdan zaif namuna hisoblanadi. Yuqori PnL/kun omadli tasodifning natijasi bo'lishi mumkin.
Ishonch darajasiga moslashtirilgan baholash
Kichik namunalarni jazolash uchun t-taqsimotdan foydalanamiz:
bu yerda — bitim boshiga o'rtacha daromad, — standart og'ish, — bitimlar soni, — t-taqsimotning kvantili.
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,
}
Ishonch darajasini moslashtirishning ta'siri
| Strategiya | Bitimlar | O'rt. daromad | SE | CI quyi | Ishonch koef. | Moslashtirilgan ball |
|---|---|---|---|---|---|---|
| Strategiya B | 38 | 0.71% | 0.28% | 0.14% | 0.20 | 210% x 0.20 = 42% |
| Strategiya C | 418 | 0.72% | 0.05% | 0.62% | 0.86 | 259% x 0.86 = 223% |
| Strategiya A | 491 | 0.12% | 0.02% | 0.08% | 0.67 | 150% x 0.67 = 100% |
Ishonch darajasini moslashtirgandan so'ng, Strategiya C ishonchli tarzda yetakchilik qiladi: 418 bitim tor CI va yuqori ishonch koeffitsientini beradi. 38 bitimga ega Strategiya B jazolanadi — uning "ajoyib" natijasi dispersiya natijasi bo'lishi mumkin.
fill_efficiency: uni qayerdan olish mumkin

fill_efficiency parametri quyidagi savolga javob beradi: "Orkestrator kapitalni qancha vaqt ishlab turishini ta'minlay oladi?"
1-variant: qat'iy konstanta
Eng sodda yondashuv: barcha strategiyalar uchun fill_efficiency = 0.80. Orkestrator bo'sh vaqtning 80%-ini boshqa strategiyalar/juftliklar bilan to'ldiradi deb faraz qilinadi.
Afzalligi: barchasi uchun bir xil, solishtirish oson. Kamchiligi: strategiyalar orasidagi korrelyatsiyani hisobga olmaydi.
2-variant: analitik baholash
Agar sizda ta juftlik bo'lsa, har biri vaqt faol bo'lsa, kamida bittasi faol bo'lish ehtimoli:
Ammo kriptovalyutalar yuqori darajada korrelyatsiyalangan — BTC ETH, SOL va qolganlarini o'zi bilan birga tortadi. Mustaqil juftliklarning samarali soni:
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% faollik va 10 ta korrelyatsiyalangan juftlikka ega Strategiya B uchun fill_efficiency atigi ~16% ni tashkil qiladi. Bu samarali daromadni keskin kamaytiradi.
3-variant: ma'lumotlar asosida simulyatsiya
Eng aniq yondashuv — barcha strategiyalarni barcha juftliklarda ishga tushirish va haqiqiy slot ishlatilishini hisoblash:
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
Yakuniy reyting formulasi
Barcha komponentlarni birlashtirish:
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
Seriyaning boshqa ko'rsatkichlari bilan bog'liqligi
Bu ko'rsatkich oldingi maqolalardagi vositalarni almashtirmaydi, balki to'ldiradi:
-
Loss-Profit Asymmetry: maksimal drawdown MaxLev-ni belgilaydi, u ball formulasiga kiradi. Drawdown qanchalik chuqur bo'lsa, ball shunchalik past bo'ladi — tiklanish assimetriyasi tufayli chiziqli bo'lmagan tarzda.
-
Monte Carlo bootstrap: bootstrapdan olingan ishonch intervallari t-taqsimotga qaraganda ishonch koeffitsientining aniqroq bahosini beradi. t-taqsimotdagi CI-ni bootstrapning 5-protsentili bilan almashtirish mumkin.
-
Funding rates: funding xarajatlari faol kun boshiga PnL-dan chegiriladi. Yuqori leverage va past PnL/kun bilan funding sof ballni manfiy qilishi mumkin — strategiya musbat xom PnL-ga qaramay aslida foydasiz bo'ladi.
Bu orkestratsiya uchun nima uchun muhim
Faol vaqt bo'yicha PnL — orkestratorda strategiyalarni reytinglash uchun asosiy ko'rsatkich. Bir nechta strategiya bir xil slot uchun raqobatlashganda, eng yuqori ball (ishonch darajasini moslashtirishni hisobga olgan holda) g'olib chiqadi.
Amalda, bu kutilmagan qarorlarga olib keladi: "kamtarona" xom PnL-ga ega, lekin pozitsiyada qisqa vaqt bo'lgan strategiyalar ko'pincha yuqori PnL-ga, lekin uzoq pozitsiyalarga ega "jozibali" strategiyalardan ustunlikka ega bo'ladi. Birinchilari o'nlab strategiyalar portfelida kapitaldan samaraliroq foydalanadi.
Asosiy xulosa: masshtablanadigan yagona ko'rsatkich — faol kun boshiga PnL. Xom PnL masshtablanmaydi: siz bir xil strategiyani ikki marta ishga tushira olmaysiz. Ammo bo'sh vaqtni boshqa strategiyalar bilan to'ldirish mumkin — va faol kun boshiga PnL portfelda qancha topishingizni aniq bashorat qiladi.
Xulosa
Xom yillik PnL qulay, ammo aldamchi ko'rsatkich. U treyderning eng muhim resursi — kapital ishlaydigan vaqtni hisobga olmaydi.
Uchta asosiy xulosa:
-
Faol kun boshiga PnL-ni hisoblang. Pozitsiyada 38 kun davomida +27% beradigan strategiya = +0.72%/kun. 338 kun davomida +300% beradigan strategiya = +0.89%/kun. Farq 11 marta emas, balki 1.2 marta.
-
fill_efficiency-ni hisobga oling. Korrelyatsiyalangan kripto juftliklari portfelida fill_efficiency ko'rinishidan pastroq. 10 ta juftlik 10 martalik diversifikatsiyaga teng emas. correlation_factor = 3 bo'lganda, juftliklarning samarali soni atigi ~3 ni tashkil qiladi.
-
Kichik namunalarni jazolang. O'rtacha +0.71% bilan 38 bitim +0.14% dan +1.28% gacha CI beradi. +0.72% bilan 418 bitim +0.62% dan +0.82% gacha CI beradi. O'rtacha qiymatlar deyarli bir xil bo'lsa-da, ikkinchi strategiya ishonchliroq.
Faol vaqt bo'yicha PnL ko'rsatkichi PnL@MaxLev-ni almashtirmaydi — u kapitaldan foydalanish samaradorligi o'lchamini qo'shish orqali uni to'ldiradi. Bitta strategiya uchun PnL@ML yetarli. Strategiyalar portfeli uchun faol vaqt bo'yicha PnL zarur.
Manbalar
- 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
Iqtibos
@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.