""" 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, ))