b59dcc3629
Set up the directory structure and wrote placeholder logic for: - Order Book Imbalance: trades on L2 bid/ask skew - Iceberg/TWAP detection: follows whale accumulation patterns - Funding rate arbitrage: delta-neutral carry on perp funding - Pairs trading: BTC/ETH spread mean reversion - Avellaneda-Stoikov market making: optimal bid/ask quoting Also added shared risk manager, portfolio tracker, and a plain-language strategy walkthrough in docs/.
78 lines
2.4 KiB
Python
78 lines
2.4 KiB
Python
"""
|
|
Funding Rate Arbitrage strategy.
|
|
|
|
Hyperliquid pays funding every 8 hours. When the rate is positive,
|
|
longs pay shorts. This strategy:
|
|
|
|
1. Goes LONG spot (no funding payments)
|
|
2. Goes SHORT perp (collects funding)
|
|
3. Maintains delta neutrality
|
|
|
|
The profit comes from funding, not price direction.
|
|
"""
|
|
from nautilus_trader.trading.strategy import Strategy
|
|
from nautilus_trader.config import StrategyConfig
|
|
|
|
|
|
class FundingRateArbConfig(StrategyConfig, frozen=True):
|
|
spot_instrument: str
|
|
perp_instrument: str
|
|
min_funding_rate: float = 0.0001
|
|
rebalance_threshold: float = 0.05
|
|
position_size: float = 0.01
|
|
|
|
|
|
class FundingRateArb(Strategy):
|
|
"""
|
|
Delta-neutral funding rate carry trade.
|
|
|
|
Key idea: funding rate IS the edge. Stay neutral, collect
|
|
the payments.
|
|
"""
|
|
|
|
def __init__(self, config: FundingRateArbConfig) -> None:
|
|
super().__init__(config)
|
|
self.config = config
|
|
self.position_open = False
|
|
|
|
def on_start(self) -> None:
|
|
bar_type = f"{self.config.perp_instrument}-1-MINUTE-LAST-INTERNAL"
|
|
self.subscribe_bars(bar_type)
|
|
self.log.info(
|
|
f"Funding arb: {self.config.spot_instrument} / {self.config.perp_instrument}"
|
|
)
|
|
|
|
def on_bar(self, bar) -> None:
|
|
funding_rate = self._get_funding_rate()
|
|
if funding_rate is None:
|
|
return
|
|
|
|
spot_pos = self.portfolio.net_position(self.config.spot_instrument)
|
|
|
|
if funding_rate > self.config.min_funding_rate and spot_pos == 0:
|
|
self._open()
|
|
self.position_open = True
|
|
elif funding_rate < self.config.min_funding_rate / 2 and self.position_open:
|
|
self._close()
|
|
self.position_open = False
|
|
|
|
def _get_funding_rate(self) -> float | None:
|
|
# TODO: fetch from Hyperliquid API
|
|
return 0.0001
|
|
|
|
def _open(self) -> None:
|
|
self.submit_order(self.order_factory.market(
|
|
instrument_id=self.config.spot_instrument,
|
|
order_side="BUY",
|
|
quantity=self.config.position_size,
|
|
))
|
|
self.submit_order(self.order_factory.market(
|
|
instrument_id=self.config.perp_instrument,
|
|
order_side="SELL",
|
|
quantity=self.config.position_size,
|
|
))
|
|
|
|
def _close(self) -> None:
|
|
self.close_all_positions(self.config.spot_instrument)
|
|
self.close_all_positions(self.config.perp_instrument)
|