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