Execution Algorithms
All delta neutral entries and exits use execution algorithms to minimise market impact and maintain maker-only fills. Three algorithms are available: TWAP, POV (Participation Rate), and Iceberg.
TWAP — Time-Weighted Average Price
TWAP splits a large order into equal-sized slices and places them at randomised intervals over a set time window. The goal is to blend into normal market flow rather than hitting the book with a single large order.
How it works:
- Total size is divided into N slices (default: 5)
- Each slice is placed as a post-only limit order at the current best bid/ask
- If a slice fails to fill within its window, the algorithm adjusts price and retries
- Intervals are randomised ±20% around the base interval to avoid detectable patterns
- If market moves against the position during execution (drawdown breach), TWAP pauses and reassesses
When it's used:
TWAP is the default algorithm for both legs of a delta neutral entry and exit. Both the long leg and short leg execute in parallel, each with their own TWAP instance.
Parameters:
| Parameter | Default | Description |
|---|---|---|
| Execute Over | 5 minutes | Total time window for the full entry or exit |
| Slices | 5 | Number of child orders the total size is split into |
| Post-Only | Always on | All orders are maker-only — taker fills are rejected |
The Execute Over selector appears in the deploy panel (2m / 5m / 10m / 30m). Larger positions benefit from longer windows to avoid adverse price impact. Smaller positions can use 2m without meaningful slippage.
POV — Target Participation Rate
POV (Percentage of Volume) targets a specific fraction of market trading volume rather than filling over a fixed time window. Instead of placing N slices in T minutes, it sizes each slice proportionally to recent volume and places orders only when volume conditions are met.
How it works:
- System estimates current market volume rate from recent trades
- Child order size = target participation rate × estimated volume per interval
- Orders are placed at maker prices, cancelled and repriced if the market moves
- If volume drops (thin market), execution slows automatically to avoid outsized impact
When to use POV over TWAP:
- During low-liquidity windows where a fixed time window would result in the algo consuming a large % of volume regardless
- For larger positions where participation rate matters more than completion time
- When you want execution to naturally slow during quiet periods
Iceberg Orders
Iceberg hides large orders by showing only a small visible portion on the book at any time. As each visible slice fills, a new slice replaces it automatically — keeping total order size hidden from the market.
How it works:
- A large parent order is created (e.g., $50,000 notional)
- Only a small child order is posted to the book (e.g., $2,000 visible)
- When the child fills, the next child is immediately placed
- Total fills are tracked against the parent; execution completes when the parent is fully filled
When to use Iceberg:
- When total position size is large enough that showing it on the book would invite front-running
- On exchanges with low depth where a visible large order would significantly impact the mid price
- When you want to maintain consistent queue position without revealing your full size
Fee Structure
All three algorithms use post-only (maker) orders exclusively.
| Fee | Notes | |
|---|---|---|
| Maker (post-only) | 0.00% | All execution algorithms enforce this |
| Taker | 0.03% | Never used — orders are cancelled if they'd cross |
The deploy panel shows a Fee Breakdown before you deploy:
- Open fees (2 legs)
- Close fees (2 legs)
- Gross monthly yield
- Net monthly yield (after fees)
- Break-even time (how many funding intervals to recover open+close cost)
A break-even of 4 hours means the position covers its fees after one 8-hour funding payment.
Automated Exit Conditions
The bot includes a background monitor that checks each active position every 5 minutes and exits automatically when conditions are met. This is off by default — enable it per-session via Settings → Auto-Exit Monitoring.
When disabled, positions stay open until you manually trigger an exit from the position card. When enabled, the monitor runs continuously and fires the same TWAP exit path as a manual close.
Every 5 minutes, the monitor checks each active position against five exit conditions. They are evaluated in order — the first one that fires triggers an exit.
Condition 1 — Symbol unavailable
If the symbol no longer appears in the live funding rate data for either the long exchange or the short exchange, the position exits immediately. This covers delistings, exchange outages, or API failures where rates can't be fetched.
Condition 2 — Spread narrowed below threshold
current_spread = short_rate - long_rate (both in %/hr)
exit if: current_spread < exit_spread_threshold (default: 0.005%/hr)
The spread is recalculated from live rates on every check. When the spread compresses to the point it no longer justifies holding — even if still technically positive — the position exits.
Condition 3 — Spread flipped negative
exit if: current_spread < 0
A separate guard below condition 2. If the spread inverts (you're now paying more to hold than you're earning), exits immediately regardless of threshold setting.
Condition 4 — Max holding time
exit if: holding_hours >= 168 (7 days, configurable)
holding_hours is calculated from entry_time in UTC. Positions are not held indefinitely regardless of spread performance.
Condition 5 — Take profit / Stop loss
total_pnl = funding_collected + realized_pnl
pnl_pct = (total_pnl / (size_usd × 2)) × 100
exit if: pnl_pct >= +5.0% (take profit, configurable)
exit if: pnl_pct <= -2.0% (stop loss, configurable)
size_usd × 2 is the total capital deployed — both the long and short leg. A $1,000 position has a $2,000 base for percentage purposes.
funding_collected is the running sum of funding payments received. realized_pnl is the price difference between entry and current mark. The stop loss fires when the combined total goes negative enough to breach the threshold, not just on price action alone.
Exit Execution Sequence
When any condition fires, exit_position runs immediately:
Step 1 — Parallel TWAP close
Both legs close simultaneously via the TWAP executor:
- Long leg: places sell orders on the long exchange
- Short leg: places buy orders on the short exchange
- Both run in parallel via
asyncio.gather
Fill progress is polled every 3 seconds. TWAP slices are post-only throughout.
Step 2 — Remainder chase
After the TWAP window closes, any unfilled remainder enters a chase phase:
- Up to 3 attempts, each with a 120-second window
- Attempt 3 enables
force_taker=True— the order will cross the spread if maker fails
Step 3 — Force taker
If remainder is still > $1 after the chase phase, a final taker close runs with a 30-second deadline. This is the only point in the entire lifecycle where a taker order is placed.
Step 4 — Emergency close
If the TWAP executor itself returns None (internal failure, not just unfilled), an emergency market close fires immediately on that leg (post_only=False). This is a fallback to the step 2/3 chase — it runs if TWAP never started, not if it started but left remainder.
Step 5 — Manual intervention
If both the TWAP and emergency close fail on either leg, a CRITICAL log is written:
BOTH TWAP and emergency close failed for [long/short] — MANUAL INTERVENTION REQUIRED
The position remains in state with realized PnL set to 0.0 for the failed leg.
PnL calculation after exit
Once both legs close:
long_size_coins = size_usd / long_entry_price
short_size_coins = size_usd / short_entry_price
long_pnl = (exit_long_vwap - long_entry_price) × long_size_coins
short_pnl = (short_entry_price - exit_short_vwap) × short_size_coins
realized_pnl = long_pnl + short_pnl
total_pnl = funding_collected + realized_pnl
exit_long_vwap and exit_short_vwap are the actual volume-weighted average prices from the TWAP fills, not the mark price at the time of exit trigger.
The position is then removed from active state and written to disk.
Delta Drift
Delta drift measures how far the combined position has deviated from true neutral. It's shown as a gauge on the position card, ranging from "Short bias" to "Neutral" to "Long bias."
The gauge activates once the position is deployed. Before entry it shows "No active position."
Drift accumulates when:
- Price diverges between the two exchanges during the holding period — the long and short sides don't move in perfect lockstep
- One TWAP leg fills faster than the other during entry, leaving you temporarily directional
- Funding payments land at different times on each venue, shifting the net PnL slightly off zero
The gauge is visual feedback only — the bot does not automatically rebalance based on drift. Rebalancing or closing is done manually via the position card.