Initial project scaffold: five quant strategies for Hyperliquid Testnet
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/.
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
"""
|
||||
Order Book Imbalance strategy.
|
||||
|
||||
Enters positions when bid/ask volume at the top of the order book
|
||||
shows a significant directional skew. The idea: when one side of
|
||||
the book is much heavier, price tends to move toward the thinner
|
||||
side as the heavy side absorbs market orders.
|
||||
"""
|
||||
from nautilus_trader.trading.strategy import Strategy
|
||||
from nautilus_trader.config import StrategyConfig
|
||||
|
||||
|
||||
class OrderBookImbalanceConfig(StrategyConfig, frozen=True):
|
||||
instrument_id: str
|
||||
depth: int = 10
|
||||
imbalance_threshold: float = 0.6
|
||||
trade_size: float = 0.001
|
||||
max_position: float = 0.003
|
||||
cooldown_bars: int = 5
|
||||
|
||||
|
||||
class OrderBookImbalance(Strategy):
|
||||
"""
|
||||
Trades on L2 order book imbalance.
|
||||
|
||||
- imbalance > threshold -> bid side heavy -> buy
|
||||
- imbalance < 1-threshold -> ask side heavy -> sell
|
||||
"""
|
||||
|
||||
def __init__(self, config: OrderBookImbalanceConfig) -> None:
|
||||
super().__init__(config)
|
||||
self.config = config
|
||||
self.bars_since_last_trade = 0
|
||||
|
||||
def on_start(self) -> None:
|
||||
self.subscribe_order_book_deltas(
|
||||
self.config.instrument_id,
|
||||
depth=self.config.depth,
|
||||
)
|
||||
self.log.info(
|
||||
f"OFI started on {self.config.instrument_id} "
|
||||
f"(depth={self.config.depth})"
|
||||
)
|
||||
|
||||
def on_order_book_deltas(self, deltas) -> None:
|
||||
self.bars_since_last_trade += 1
|
||||
if self.bars_since_last_trade < self.config.cooldown_bars:
|
||||
return
|
||||
|
||||
book = self.cache.order_book(self.config.instrument_id)
|
||||
if not book or len(book.bids) == 0 or len(book.asks) == 0:
|
||||
return
|
||||
|
||||
depth = min(self.config.depth, len(book.bids), len(book.asks))
|
||||
bid_vol = sum(book.bids[i].size for i in range(depth))
|
||||
ask_vol = sum(book.asks[i].size for i in range(depth))
|
||||
total = bid_vol + ask_vol
|
||||
if total == 0:
|
||||
return
|
||||
|
||||
imbalance = bid_vol / total
|
||||
pos = self.portfolio.net_position(self.config.instrument_id)
|
||||
|
||||
if imbalance > self.config.imbalance_threshold and pos <= 0:
|
||||
self._enter("BUY")
|
||||
self.bars_since_last_trade = 0
|
||||
elif imbalance < (1 - self.config.imbalance_threshold) and pos >= 0:
|
||||
self._enter("SELL")
|
||||
self.bars_since_last_trade = 0
|
||||
|
||||
def _enter(self, side: str) -> None:
|
||||
self.submit_order(self.order_factory.market(
|
||||
instrument_id=self.config.instrument_id,
|
||||
order_side=side,
|
||||
quantity=self.config.trade_size,
|
||||
))
|
||||
Reference in New Issue
Block a user