Model perpetual funding payments¶
Perpetuals settle funding on a fixed schedule — every 8 hours on Binance UM / Bybit linear / OKX swap, every hour on some Bitget markets. The payment per settlement is:
Sign convention: a long (positive position) pays out when rate is positive (amount negative). A short receives in the same regime. Negative rate flips both signs.
FundingSchedule walks the schedule and produces a FundingPayment
record per (settlement timestamp, symbol). The strategy / backtest
runner is responsible for routing the amount into equity, since
flox's BacktestResult is fill-driven and does not own a
periodic-event clock of its own.
Construct a schedule¶
"""Apply perpetual funding to open positions during a backtest tick loop."""
import flox_py as flox
HOUR = 3600 * 1_000_000_000
# Canned 8-hour profile (Binance UM futures cadence).
sched = flox.FundingSchedule.binance_um_futures()
sched.set_constant_rate(0.0001) # 0.01% per 8h
# Or recorded tape from an archive:
# sched = flox.FundingSchedule.tape([(t0, 0.00012), (t0 + 8*HOUR, -0.00005), ...])
# Strategy tick loop:
# - feed current per-symbol position + mark price
# - schedule emits one event per (symbol, boundary) crossed since last call
# - amount is signed: positive = received, negative = paid
events = sched.tick(now_ns=9 * HOUR,
symbols=[1, 2], # BTC, ETH
positions=[1.0, -2.0], # long 1 BTC, short 2 ETH
mark_prices=[50_000.0, 3_000.0])
for ev in events:
print(f"t={ev.timestamp_ns:>20} sym={ev.symbol} "
f"rate={ev.rate:.5f} amount={ev.amount:+.4f}")
Canned profiles¶
| Profile | Interval | Default rate |
|---|---|---|
binance_um_futures |
8h | 0 |
bybit_linear |
8h | 0 |
okx_swap |
8h | 0 |
bitget_hourly |
1h | 0 |
All profiles default to a zero rate — callers must supply a real
rate either via set_constant_rate or by attaching a recorded
tape via tape(...).
Tape mode¶
For research that wants the exchange's actually-published rates
(not a flat assumption), use FundingSchedule.tape([(ts, rate),
...]). Each tape event triggers one payment at its timestamp.
Per-symbol tape¶
Real venues publish different rates per symbol per settlement. To
capture that, use a per-symbol tape: each row is
(timestamp_ns, symbol, funding_rate). Symbols without an entry at
a given settlement timestamp fall back to the constant rate (which
defaults to 0; override with set_constant_rate).
Sample CSV:
timestamp_ns,symbol,funding_rate
1700000000000000000,1,0.0001
1700000000000000000,2,-0.0002
1700028800000000000,1,0.0003
Integration recipe¶
A typical backtest tick loop:
last_ns = 0
for event in market_data:
pos, mark = current_position(event.symbol), event.mark_price
payments = sched.tick(event.timestamp_ns, [event.symbol], [pos], [mark])
for p in payments:
equity += p.amount
ledger.append(p)
last_ns = event.timestamp_ns
Notes¶
- The schedule's internal cursor (
last_tick_ns) advances on every tick. To restart a backtest, callreset(). - Per-day settlement venues (BitMEX-style daily funding) need a
custom interval — pass
24 * 3600 * 1_000_000_000toset_constant. - The signed position should reflect the actual exposure at the funding boundary, not the average over the period. Snap the position at the boundary timestamp.
- For multi-symbol portfolios pass parallel arrays — one tick call walks every symbol per boundary.