"""ETH Ensemble — MTFTrend + StructTrend + GaussianCycle (50/30/20).

ETH-specific model — the BTC model hierarchy does NOT transfer to ETH.
Models that are garbage on BTC are the ETH winners:

  MTFTrend (50%):      ETH IC=+0.059, IR=0.88, no decay — ETH #1
  StructTrend (30%):   ETH IC=+0.054, IR=1.03, no decay — ETH #2
  GaussianCycle (20%): ETH IC=+0.039, consistent on both assets

All three have positive time correlation on ETH (signal improving, not
decaying). This is the opposite of the BTC models.

For comparison, the BTC-optimised models on ETH:
  Enhanced:     ETH IC=+0.040, IR=0.54, decay=-0.99  (60% weaker)
  Adaptive85/15: ETH IC=+0.033, IR=0.50, decay=-0.83

Walk-forward validation (4 rolling 60d folds, 1d horizon, ETH):
  MTFTrend:     IC=+0.059  IR=0.88  Decay=+0.05  Pos=75%
  StructTrend:  IC=+0.054  IR=1.03  Decay=+0.61  Pos=75%
  GaussianCycle: IC=+0.039  IR=0.39  Decay=+0.51  Pos=75%
"""

from __future__ import annotations

from vanta.tracker import TrackerBase
from vanta.examples.mtf_trend_tracker import MTFTrendTracker
from vanta.examples.structural_trend_tracker import StructuralTrendTracker
from vanta.examples.gaussian_cycle_tracker import GaussianCycleTracker


class ETHEnsembleTracker(TrackerBase):
    """50/30/20 blend of MTFTrend + StructTrend + GaussianCycle.

    Args:
        mtf_weight: Weight for MTFTrend signal (default 0.50).
        struct_weight: Weight for StructTrend signal (default 0.30).
        gaussian_weight: Weight for GaussianCycle signal (default 0.20).
    """

    def __init__(
        self,
        mtf_weight: float = 0.50,
        struct_weight: float = 0.30,
        gaussian_weight: float = 0.20,
    ):
        super().__init__()
        self._mtf = MTFTrendTracker()
        self._struct = StructuralTrendTracker()
        self._gaussian = GaussianCycleTracker()
        self._w_m = mtf_weight
        self._w_s = struct_weight
        self._w_g = gaussian_weight

    def tick(self, data: dict) -> None:
        super().tick(data)
        self._mtf.tick(data)
        self._struct.tick(data)
        self._gaussian.tick(data)

    def predict(self, symbol: str, horizon_seconds: int, step_seconds: int) -> dict:
        er_m = self._mtf.predict(symbol, horizon_seconds, step_seconds).get("expected_return", 0.0)
        er_s = self._struct.predict(symbol, horizon_seconds, step_seconds).get("expected_return", 0.0)
        er_g = self._gaussian.predict(symbol, horizon_seconds, step_seconds).get("expected_return", 0.0)

        blended = self._w_m * er_m + self._w_s * er_s + self._w_g * er_g
        return {"expected_return": blended}
