← Makalelere geri dön
March 18, 2026
5 dakikalık okuma

Aktif Zamana Göre PnL: Strateji Sıralamalarını Değiştiren Metrik

Aktif Zamana Göre PnL: Strateji Sıralamalarını Değiştiren Metrik
#algotrading
#backtest
#metrikler
#PnL
#orkestrasyon
#portföy
#risk yönetimi

İki stratejiniz var. Birincisi: PnL +%300, 418 işlem, zamanın %45'inde pozisyon açık. İkincisi: PnL +%27, 38 işlem, zamanın %5'inde pozisyon açık. Hangisi daha iyi?

Birincisini seçtiyseniz — yanlış cevap verdiniz. İşte nedeni.

Ham PnL'nin Sorunu

Ham PnL — tüm backtest dönemi boyunca toplam getiri — stratejinin zamanın ne kadarında pozisyonda olduğunu hesaba katmaz. +%300 ve %45 işlem süresine sahip bir strateji, sermayenizi zamanın yarısından azında kullanır. Kalan %55'lik sürede sermaye atıl kalır.

+%27 ve %5 işlem süresine sahip bir strateji, sermayeyi yalnızca zamanın %5'inde kullanır — ancak kalan %95 diğer stratejiler için kullanılabilir durumdadır.

Bir orkestratör aracılığıyla bir strateji portföyü yürütüyorsanız, bir stratejinin boş zamanı diğerleri tarafından doldurulur. O zaman temel metrik, bir stratejinin yıl boyunca ne kadar kazandığı değil, aktif zamanın birimi başına ne kadar kazandığı olur.

Efektif Getiri Formülü

PnL per active day strategy ranking comparison

Temel Hesaplama

PnLdaily=Total PnLActive days\text{PnL}_{daily} = \frac{\text{Total PnL}}{\text{Active days}}

Annualizedraw=PnLdaily×365\text{Annualized}_{raw} = \text{PnL}_{daily} \times 365

Annualizedeffective=Annualizedraw×fill_efficiency\text{Annualized}_{effective} = \text{Annualized}_{raw} \times \text{fill\_efficiency}

burada:

  • Active days — pozisyonlardaki toplam süre (gün cinsinden)
  • fill_efficiency — orkestratörün sinyallerle doldurabileceği zaman dilimi (0...1)
def pnl_per_active_time(
    total_pnl: float,        # toplam PnL, %
    test_period_days: int,    # backtest süresi, gün
    trading_time_pct: float,  # aktif zaman oranı, 0..1
    fill_efficiency: float = 0.80,  # slot doluluk verimliliği
) -> dict:
    """
    Aktif zaman başına efektif getiriyi hesaplar.
    """
    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,
    }

Gerçek Stratejilerin Yeniden Hesaplanması

Dönem: 750 gün (25 ay), fill_efficiency = 0.80:

Strateji PnL İşlem süresi Aktif gün PnL/gün Yıllıklaştırılmış (x0.8)
Strateji C +%300 %45 337.5 %0.89/g %259
Strateji B +%27 %5 37.5 %0.72/g %210
Strateji A +%58 %15 112.5 %0.51/g %150

Ham PnL'ye göre: Strateji C (%300) >> Strateji A (%58) >> Strateji B (%27). Efektif getiriye göre: Strateji C (%259) > Strateji B (%210) > Strateji A (%150).

%27 PnL'li Strateji B, %300 PnL'li Strateji C ile karşılaştırılabilir hale geliyor — çünkü 9 kat daha az aktif sürede aynı parayı kazanıyor. Kalan %95'lik süre diğer stratejilerle doldurulabilir.

Doğrusal ve Bileşik Ekstrapolasyon

Yukarıdaki formül doğrusaldır. Daha basit ve daha tutucudur. Bileşik varyant, kâr yeniden yatırımını hesaba katar:

Daily return (compound)=(1+Total PnL)1/Active days1\text{Daily return (compound)} = (1 + \text{Total PnL})^{1/\text{Active days}} - 1

Annualizedcompound=(1+Daily return)365×fill_eff1\text{Annualized}_{compound} = (1 + \text{Daily return})^{365 \times \text{fill\_eff}} - 1

import numpy as np

def compound_annualized(total_pnl_pct, active_days, fill_efficiency=0.80):
    """Bileşik ekstrapolasyon."""
    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)

Bileşik ekstrapolasyonla Strateji B, Strateji C'yi geride bırakır: %540'a karşı %231. Sıralama tersine döner.

Öneri: sıralama için doğrusal ekstrapolasyon kullanın. Daha tutucudur ve az sayıda işlem üzerinde aşırı uydurmayı ödüllendirmeye daha az eğilimlidir.

Tuzak: Az Sayıda İşlem

38 işlem ve PnL/gün = %0.72 ile Strateji B cazip görünüyor. Ancak 38 işlem istatistiksel açıdan zayıf bir örnektir. Yüksek PnL/gün, şanslı bir tesadüfün sonucu olabilir.

Güven aralığı düzeltmeli puanlama

Küçük örnekleri cezalandırmak için t-dağılımını kullanıyoruz:

CIlower=rˉtα/2,n1×sn\text{CI}_{lower} = \bar{r} - t_{\alpha/2, n-1} \times \frac{s}{\sqrt{n}}

burada rˉ\bar{r} işlem başına ortalama getiri, ss standart sapma, nn işlem sayısı, tα/2,n1t_{\alpha/2, n-1} t-dağılımı kantilidir.

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:
    """
    Örneklem büyüklüğü düzeltmeli strateji sıralaması.
    """
    n = len(trade_returns)
    if n < min_trades:
        return {"score": 0, "reason": f"Çok az işlem ({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 = [...]  # her işlem için tutma saatleri
    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,
    }

Güven düzeltmesinin etkisi

Strateji İşlem Ort. getiri SE GA alt Güven faktörü Düzeltilmiş puan
Strateji B 38 %0.71 %0.28 %0.14 0.20 %210 x 0.20 = %42
Strateji C 418 %0.72 %0.05 %0.62 0.86 %259 x 0.86 = %223
Strateji A 491 %0.12 %0.02 %0.08 0.67 %150 x 0.67 = %100

Güven düzeltmesinden sonra Strateji C güvenle önde: 418 işlem dar bir GA ve yüksek güven faktörü sağlıyor. 38 işlemli Strateji B cezalandırılıyor — "muhteşem" performansı varyansın sonucu olabilir.

fill_efficiency: Nereden Alınır

Fill efficiency and orchestrator slot allocation

fill_efficiency parametresi şu soruyu yanıtlar: "Orkestratör sermayeyi zamanın ne kadarında çalıştırabilir?"

Seçenek 1: Sabit sabit

En basit yaklaşım: tüm stratejiler için fill_efficiency = 0.80. Orkestratörün boş zamanın %80'ini diğer stratejiler/çiftlerle doldurduğunu varsayar.

Avantaj: tümü için aynı, karşılaştırması kolay. Dezavantaj: stratejiler arasındaki korelasyonu hesaba katmaz.

Seçenek 2: Analitik tahmin

NN çiftiniz varsa, her biri zamanın p%p\%'inde aktifse, en az birinin aktif olma olasılığı:

P(1 active)=1(1p)NP(\geq 1\ \text{active}) = 1 - (1 - p)^N

Ancak kripto paralar yüksek korelasyona sahiptir — BTC, ETH, SOL ve geri kalanını da sürükler. Bağımsız çiftlerin efektif sayısı:

Neff=Ncorrelation factorN_{eff} = \frac{N}{\text{correlation factor}}

def estimate_fill_efficiency(
    trading_time_pct: float,
    n_pairs: int,
    correlation_factor: float = 3.0,  # kripto — yüksek korelasyon
    max_slots: int = 10,
) -> float:
    """
    fill_efficiency analitik tahmini.

    Args:
        trading_time_pct: bir strateji için aktif zaman oranı
        n_pairs: işlem çifti sayısı
        correlation_factor: korelasyon katsayısı (1=bağımsız, 5=güçlü)
        max_slots: maksimum eşzamanlı pozisyon sayısı
    """
    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 aktivite ve 10 korelasyonlu çiftle Strateji B için fill_efficiency yalnızca ~%16'dır. Bu, efektif getiriyi dramatik biçimde düşürür.

Seçenek 3: Verilerden simülasyon

En doğru yaklaşım, tüm stratejileri tüm çiftler üzerinde çalıştırmak ve gerçek slot kullanımını hesaplamaktır:

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:
    """
    Gerçek orkestratör slot kullanımını simüle eder.
    """
    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

Nihai Sıralama Formülü

Tüm bileşenlerin birleştirilmesi:

def strategy_score(
    trades: list,
    test_period_days: int,
    fill_efficiency: float = 0.80,
    min_trades: int = 30,
    funding_rate: float = 0.0001,
) -> float:
    """
    Strateji sıralaması için nihai puan.

    Şunları hesaba katar:
    - Aktif gün başına PnL (sermaye kullanım verimliliği)
    - MaxLev (riske göre düzeltilmiş ölçeklendirme)
    - Güven düzeltmesi (küçük örneklem cezası)
    - Fonlama maliyetleri (kaldıraçta gerçekçi maliyetler)
    """
    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  # %cinsinden
    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

Serideki Diğer Metriklerle Bağlantı

Bu metrik, önceki makalelerdeki araçların yerini almaz, tamamlar:

  • Kayıp-Kâr Asimetrisi: maksimum düşüş, puan formülüne giren MaxLev'i belirler. Düşüş ne kadar derin olursa, kurtarma asimetrisi nedeniyle doğrusal olmayan biçimde puan o kadar düşük olur.

  • Monte Carlo bootstrap: bootstrap'tan gelen güven aralıkları, güven faktörünün t-dağılımından daha doğru bir tahminini sağlar. t-dağılımından gelen GA'yı bootstrap'ın 5. persantiliyle değiştirebilirsiniz.

  • Fonlama oranları: fonlama maliyetleri aktif gün başına PnL'den çıkarılır. Yüksek kaldıraç ve düşük PnL/gün ile fonlama, net puanı negatif yapabilir — strateji, pozitif ham PnL'ye rağmen gerçekte kârsızdır.

Bu Neden Orkestrasyon için Önemli

Aktif zaman başına PnL, bir orkestratördeki stratejileri sıralamak için birincil metriktir. Birden fazla strateji aynı slot için rekabet ettiğinde, en yüksek puana (güven düzeltmesi hesaba katılarak) sahip olan kazanır.

Pratikte bu, şaşırtıcı kararlara yol açar: "mütevazı" ham PnL'ye sahip ancak pozisyonda kısa süre kalan stratejiler, genellikle yüksek PnL'ye sahip ancak uzun pozisyonlu "gösterişli" stratejilere öncelik verilir. Eski stratejiler, onlarca stratejilik bir portföyde sermayeyi daha verimli kullanır.

Temel içgörü: ölçeklenen tek metrik aktif gün başına PnL'dir. Ham PnL ölçeklenmez: aynı stratejiyi iki kez çalıştıramazsınız. Ancak boş zamanı diğer stratejilerle doldurabilirsiniz — ve aktif gün başına PnL, bir portföyde ne kadar kazanacağınızı doğru şekilde tahmin eder.

Sonuç

Ham yıllık PnL, kullanışlı ama yanıltıcı bir metriktir. Yatırımcının en önemli kaynağını hesaba katmaz — sermayenin çalıştığı zamanı.

Üç çıkarım:

  1. Aktif gün başına PnL'yi hesaplayın. 38 günlük pozisyonda +%27'lik bir strateji = +%0.72/gün. 338 günlük pozisyonda +%300'lük bir strateji = +%0.89/gün. Fark 11 kat değil, 1.2 kattır.

  2. fill_efficiency'yi hesaba katın. Korelasyonlu kripto çiftlerinden oluşan bir portföyde fill_efficiency göründüğünden düşüktür. 10 çift, 10 kat çeşitlendirme anlamına gelmez. correlation_factor = 3 ile efektif çift sayısı yalnızca ~3'tür.

  3. Küçük örnekleri cezalandırın. Ortalama +%0.71 olan 38 işlem, +%0.14 ile +%1.28 arasında bir GA verir. Ortalama +%0.72 olan 418 işlem, +%0.62 ile +%0.82 arasında bir GA verir. Ortalamaların neredeyse aynı olmasına rağmen ikinci strateji daha güvenilirdir.

Aktif zamana göre PnL metriği, PnL@MaxLev'in yerini almaz — sermaye kullanım verimliliği boyutunu ekleyerek onu tamamlar. Tek bir strateji için PnL@ML yeterlidir. Strateji portföyü için aktif zamana göre PnL zorunludur.


Kaynaklar

  1. Lopez de Prado — Advances in Financial Machine Learning: The Sharpe Ratio
  2. Pardo, R. — The Evaluation and Optimization of Trading Strategies
  3. Bailey, D.H. & Lopez de Prado — The Deflated Sharpe Ratio
  4. Kelly, J.L. — A New Interpretation of Information Rate (1956)
  5. Quantopian — Lecture on Strategy Evaluation Metrics
  6. Ernest Chan — Algorithmic Trading: Portfolio Management

Atıf

@article{soloviov2026pnlactivetime,
  author = {Soloviov, Eugen},
  title = {PnL by Active Time: The Metric That Changes Strategy Rankings},
  year = {2026},
  url = {https://marketmaker.cc/tr/blog/post/pnl-active-time-metric},
  version = {0.1.0},
  description = {Ham yıllık PnL'nin neden farklı işlem sürelerine sahip stratejileri karşılaştırmak için zayıf bir metrik olduğu. Efektif getiri nasıl hesaplanır, fill\_efficiency neden gereklidir ve neden \%27 PnL'li bir strateji \%300'lü birini geçebilir.}
}
Sorumluluk Reddi: Bu makalede sağlanan bilgiler yalnızca eğitim ve bilgilendirme amaçlıdır ve finansal, yatırım veya ticaret tavsiyesi niteliği taşımaz. Kripto para ticareti önemli bir kayıp riski içerir.

Yazarlar

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

Piyasanın Önünde Olun

Özel yapay zeka ticaret içgörüleri, piyasa analizi ve platform güncellemeleri için bültenimize abone olun.

Gizliliğinize saygı duyuyoruz. İstediğiniz zaman abonelikten çıkabilirsiniz.