"""Vanta Crunch — Model base class.

Participants subclass TrackerBase and implement predict().

Data flow:
  1. tick(data) is called continuously with live Binance candle data.
     Use it to accumulate state (indicators, features, history).
  2. predict(symbol, horizon_seconds, step_seconds) is called every 15 seconds.
     Return {"expected_return": float} — your alpha signal.

     Positive = expecting price to go up.
     Negative = expecting price to go down.
     Magnitude = conviction.

Example:
    class MyTracker(TrackerBase):
        def __init__(self):
            super().__init__()
            self.prices = {}

        def tick(self, data):
            super().tick(data)
            symbol = data.get("symbol", "BTC")
            for candle in data.get("candles_1m", []):
                self.prices.setdefault(symbol, []).append(candle["close"])

        def predict(self, symbol, horizon_seconds, step_seconds):
            prices = self.prices.get(symbol, [])
            if len(prices) < 20:
                return {"expected_return": 0.0}
            sma_fast = sum(prices[-5:]) / 5
            sma_slow = sum(prices[-20:]) / 20
            signal = (sma_fast - sma_slow) / sma_slow
            return {"expected_return": signal}
"""

from __future__ import annotations

from collections import defaultdict


class TrackerBase:
    """Base class for Vanta Crunch participant models.

    Subclass this and implement predict(). Optionally override tick()
    to build custom state from the live data stream.
    """

    def __init__(self):
        self._history: dict[str, list[dict]] = defaultdict(list)
        self._latest_data: dict | None = None

    def tick(self, data: dict) -> None:
        """Receive live market data. Called once per new 1-minute candle.

        Each call delivers only **new** candles since the last tick:
          - candles_1m:  always exactly one new 1-minute candle
          - candles_5m:  one new bar when a 5-min bucket completes (else [])
          - candles_15m: one new bar when a 15-min bucket completes (else [])
          - candles_1h:  one new bar when a 1-hour bucket completes (else [])

        The first tick after startup contains the full warmup window so that
        indicators can bootstrap from historical data.

        Models should feed these candles directly into stateful indicators
        (EMA, RSI, etc.) — do NOT recompute from full history each tick.

        Default implementation stores 1m candles per symbol. Override to
        build custom features, indicators, or state.

        Args:
            data: Dict with keys:
                - symbol (str): "BTCUSDT" or "ETHUSDT"
                - asof_ts (int): unix timestamp of the current candle
                - candles_1m (list[dict]): new OHLCV candle(s)
                - candles_5m (list[dict]): new 5m bar if bucket rolled, else []
                - candles_15m (list[dict]): new 15m bar if bucket rolled, else []
                - candles_1h (list[dict]): new 1h bar if bucket rolled, else []
        """
        self._latest_data = data
        symbol = data.get("symbol", "BTC")
        candles = data.get("candles_1m", [])
        if candles:
            self._history[symbol].extend(candles)

    def predict(self, symbol: str, horizon_seconds: int, step_seconds: int) -> dict:
        """Produce an alpha signal for the given symbol.

        Called every `step_seconds` (15s). Return your expected return over
        the next `horizon_seconds` (300s / 5 minutes).

        Args:
            symbol: Trading pair symbol ("BTC" or "ETH").
            horizon_seconds: How far forward to predict (300 = 5 minutes).
            step_seconds: How often predict() is called (15 seconds).

        Returns:
            Dict with "expected_return" (float).
            Positive = expecting price up, negative = expecting down.
            Magnitude = conviction.
        """
        raise NotImplementedError("Implement predict() in your model")
