बैकटेस्ट में परपेचुअल फ़्यूचर्स फ़ंडिंग का लेखांकन
सारांश
यह दस्तावेज़ बैकटेस्ट के दौरान परपेचुअल फ़्यूचर्स पोज़िशनों पर फ़ंडिंग नकदी प्रवाह लागू करने के लिए लेज़र का वर्णन करता है। प्रत्येक फ़ंडिंग टाइमस्टैम्प पर यह नकद समायोजन निकालने के लिए पोज़िशन की चिह्नित मात्रा, मौजूदा मार्क, किसी भी कॉन्ट्रैक्ट गुणक और फ़ंडिंग दर का उपयोग करता है। इसलिए धनात्मक फ़ंडिंग दर का लॉन्ग और शॉर्ट पोज़िशन पर उलटा असर होता है।
लेज़र टाइमस्टैम्प को UTC में सामान्यीकृत करता है, गायब या डुप्लिकेट सेटलमेंट कुंजियों और गैर-सीमित दरों को अस्वीकार करता है, और एक टाइमस्टैम्प को दो बार लागू होने से रोकता है। यह ब्रोकर के समय अपडेट को इस तरह लपेटता है कि सेटलमेंट मार्क अपडेट के बाद हो, और संचयी फ़ंडिंग P&L, घटना संख्या तथा सेटलमेंट संख्या बताता है। मेट्रिक्स के लिए ज़रूरी है कि दी गई हर फ़ंडिंग-दर कुंजी इंजन की समयरेखा तक पहुँच चुकी हो। यह तरीका खुली पोज़िशनों के सेटलमेंट समय पर वैध मार्क उपलब्ध होने पर निर्भर करता है; मार्क गायब होने पर यह त्रुटि देता है। यह बैकटेस्ट के लिए लेखांकन अवसंरचना है, ट्रेडिंग रणनीति की लाभप्रदता का प्रमाण नहीं।
मुख्य विचार
- फ़ंडिंग नकदी प्रवाह की गणना पोज़िशन के चिह्न सहित आकार, मार्क किए गए मूल्य, गुणक और फ़ंडिंग दर से की जाती है।
- ब्रोकर द्वारा उस टाइमस्टैम्प के मार्क अपडेट करने के बाद सेटलमेंट लागू होता है।
- UTC सामान्यीकरण और डुप्लिकेट सुरक्षा छूटे हुए या दोहराए गए सेटलमेंट रोकने में मदद करते हैं।
- मेट्रिक्स जाँच में यह आवश्यक है कि दिए गए सभी सेटलमेंट टाइमस्टैम्प इंजन की समयरेखा में हों।
- मौजूदा मार्क के बिना खुली पोज़िशन का सेटलमेंट विफल हो जाता है।
टैग
पूरा पाठ
# funding_backtest.py
```py
"""Funding-settlement accounting for perpetual-futures engine backtests."""
from __future__ import annotations
import math
from datetime import UTC, datetime
from functools import wraps
from typing import Any
import polars as pl
def _as_utc(value: datetime) -> datetime:
return value.replace(tzinfo=UTC) if value.tzinfo is None else value.astimezone(UTC)
class FundingSettlementLedger:
"""Apply position-signed funding during the engine's bar-time update."""
def __init__(self, funding_rates: pl.DataFrame) -> None:
required = {"symbol", "timestamp", "funding_rate"}
missing = required - set(funding_rates.columns)
if missing:
raise ValueError(f"funding rates are missing columns: {sorted(missing)}")
selected = funding_rates.select("symbol", "timestamp", "funding_rate")
if selected.null_count().row(0) != (0, 0, 0):
raise ValueError("funding settlements cannot contain null keys or rates")
if selected.n_unique(["symbol", "timestamp"]) != selected.height:
raise ValueError("funding settlement keys must be unique")
self._rates: dict[datetime, dict[str, float]] = {}
for row in selected.sort("timestamp", "symbol").iter_rows(named=True):
rate = float(row["funding_rate"])
if not math.isfinite(rate):
raise ValueError("funding rates must be finite")
self._rates.setdefault(_as_utc(row["timestamp"]), {})[str(row["symbol"])] = rate
self._rate_count = selected.height
self._settled_timestamps: set[datetime] = set()
self._funding_pnl = 0.0
self._funding_events = 0
self._funding_settlements = 0
self._installed = False
def settle(self, timestamp: datetime, broker: Any) -> float:
"""Settle one timestamp exactly once against positions marked on that bar."""
normalized = _as_utc(timestamp)
if normalized in self._settled_timestamps:
return 0.0
rates = self._rates.get(normalized)
if rates is None:
return 0.0
self._settled_timestamps.add(normalized)
self._funding_settlements += len(rates)
event_cash = 0.0
for symbol, rate in rates.items():
position = broker.positions.get(symbol)
if position is None or float(position.quantity) == 0.0:
continue
mark = broker.get_mark_price(symbol, quantity=position.quantity)
if mark is None:
raise RuntimeError(f"funding settlement has no current mark for {symbol!r}")
event_cash -= (
float(position.quantity)
* float(mark)
* float(getattr(position, "multiplier", 1.0))
* rate
)
if event_cash:
broker.cash = float(broker.cash) + event_cash
self._funding_pnl += event_cash
self._funding_events += 1
return event_cash
def install(self, broker: Any) -> None:
"""Install settlement immediately after each engine mark update."""
if self._installed:
raise RuntimeError("funding settlement ledger is already installed")
original_update_time = broker._update_time
@wraps(original_update_time)
def update_time_with_funding(timestamp, *args, **kwargs):
result = original_update_time(timestamp, *args, **kwargs)
self.settle(timestamp, broker)
return result
broker._update_time = update_time_with_funding
self._installed = True
def metrics(self) -> dict[str, float]:
"""Return cashflows actually presented to the engine timeline."""
if self._funding_settlements != self._rate_count:
raise RuntimeError(
"funding settlement coverage is incomplete: "
f"{self._funding_settlements}/{self._rate_count} keys reached the engine timeline"
)
return {
"funding_pnl": self._funding_pnl,
"funding_events": float(self._funding_events),
"funding_settlements": float(self._funding_settlements),
}
```स्रोत के लाइसेंस के तहत श्रेय सहित पूरा पाठ दिखाया गया है। लाइसेंस: MIT
यह सारांश मूल स्रोत के आधार पर Stratmill के शोध एजेंट ने लिखा है; यह स्रोत की प्रति नहीं है।