"""
V11 — Momentum Taker para Polymarket Up/Down (5min y 15min).

Cambio de paradigma respecto a V10:
  - De maker (quotear ambos lados) a taker direccional
  - Señal: movimiento de Binance vs strike > MOMENTUM_THRESHOLD
  - Si BTC sube > 0.05% del strike → comprar YES
  - Si BTC baja > 0.05% del strike → comprar NO
  - Una entrada por ventana máximo
  - Acepta 2% taker fee porque WR ~80% lo compensa
  - Soporta ventanas de 5min y 15min (--window 5 / --window 15)

Backtest (34 ventanas V10, 15min):
  Threshold 0.05%: 74 trades, WR=80%, PnL=$+163 ($+2.20/trade)
"""
import json, math, os
from dataclasses import dataclass, field
from datetime import datetime, timezone
from pathlib import Path


# -- Infraestructura ----------------------------------------------------------

GAMMA_API  = "https://gamma-api.polymarket.com/events/slug"
PRICE_API  = "https://polymarket.com/api/crypto/crypto-price"
CLOB_WS    = "wss://ws-subscriptions-clob.polymarket.com/ws/market"
HOST       = "https://clob.polymarket.com"
CHAIN_ID   = 137
UA         = {"User-Agent": "Mozilla/5.0"}

# -- Momentum Strategy -------------------------------------------------------

# Señal: |price/strike - 1| > MOMENTUM_THRESHOLD → entrar
MOMENTUM_THRESHOLD = 0.0005    # 0.05% — sweet spot del backtest

# Entry
ENTRY_SIZE_USD     = 5.0       # USD por trade
MIN_SHARES         = 5.0
TICK_SIZE          = 0.01

# Filtros de precio
# En momentum taker, compramos cuando tenemos señal direccional.
# A precio 0.50 el payoff es 1:1 → necesitamos WR>52% para ser rentables
# A precio 0.60 el payoff es 0.67:1 → necesitamos WR>60%
# A precio 0.65 el payoff es 0.54:1 → necesitamos WR>65%
# Con WR~80%, hasta precio 0.65 es rentable (EV positivo)
MAX_ENTRY_PRICE    = 0.65      # Cap para mantener EV positivo con 80% WR
MIN_ENTRY_PRICE    = 0.20      # No comprar demasiado barato (mercado raro)

# Taker fee
TAKER_FEE_RATE     = 0.02      # 2% taker fee en Polymarket

# Paper simulation
PAPER_SLIPPAGE     = 0.01      # Slippage simulado: pagamos 1c más del mid

# Defense mechanisms
LOSS_STREAK_MAX    = 3         # consecutive losses → trigger cooldown
LOSS_STREAK_COOL   = 3         # windows to skip during cooldown
DAILY_LOSS_LIMIT   = 15.0      # max daily loss (USD) per bot → auto-stop

# Settle — esperar outcome oficial de Polymarket (NUNCA inferir)
OUTCOME_MAX_WAIT   = 900       # 15min max — siempre tenemos posición

# -- Configuración por duración de ventana ------------------------------------

WINDOW_CONFIGS = {
    5: {
        "win_secs": 300,
        "slug_tag": "5m",
        "price_variant": "fiveminute",
        "signal_start_tleft": 270,    # Empezar con 4.5min restantes (30s warmup)
        "signal_stop_tleft": 60,      # Parar con 1min restante
    },
    15: {
        "win_secs": 900,
        "slug_tag": "15m",
        "price_variant": "fifteenminute",
        "signal_start_tleft": 840,    # Empezar con 14min restantes
        "signal_stop_tleft": 120,     # Parar con 2min restantes
    },
}

# -- Per-asset ----------------------------------------------------------------

ASSET_CONFIGS = {
    "btc": {
        "binance_stream":   "btcusdt@trade",
        "slug_base":        "btc-updown",
        "price_symbol":     "BTC",
        "min_ask_confirm":  0.30,    # BTC: filtrar precios <0.30 (WR=37% en 0.20-0.30)
        "entry_size_usd":   5.0,
        "momentum_threshold": 0.0005,  # 0.05% — probado con 70 trades, 69% WR
    },
    "eth": {
        "binance_stream":   "ethusdt@trade",
        "slug_base":        "eth-updown",
        "price_symbol":     "ETH",
        "min_ask_confirm":  0.45,    # ETH: solo entrar si CLOB ya confirma dirección
        "entry_size_usd":   5.0,
        "max_entry_price":  0.53,    # >0.53 WR ~50-53%, EV negativo con 2% fee
        "momentum_threshold": 0.0007,  # 0.07% — ETH 50% más volátil, 0.05% es ruido (25% WR)
    },
    "sol": {
        "binance_stream":   "solusdt@trade",
        "slug_base":        "sol-updown",
        "price_symbol":     "SOL",
        "min_ask_confirm":  0.45,    # SOL: solo entrar si CLOB ya confirma dirección
        "entry_size_usd":   5.0,
        "max_entry_price":  0.53,    # >0.53 WR ~50-53%, EV negativo con 2% fee
        "momentum_threshold": 0.0007,  # 0.07% — SOL más volátil, marginal zone no es rentable
    },
    "xrp": {
        "binance_stream":   "xrpusdt@trade",
        "slug_base":        "xrp-updown",
        "price_symbol":     "XRP",
        "min_ask_confirm":  0.45,
        "max_entry_price":  0.53,
        "momentum_threshold": 0.0007,
    },
    "doge": {
        "binance_stream":   "dogeusdt@trade",
        "slug_base":        "doge-updown",
        "price_symbol":     "DOGE",
        "min_ask_confirm":  0.45,
        "max_entry_price":  0.53,
        "momentum_threshold": 0.0007,
    },
    "bnb": {
        "binance_stream":   "bnbusdt@trade",
        "slug_base":        "bnb-updown",
        "price_symbol":     "BNB",
        "min_ask_confirm":  0.45,
        "max_entry_price":  0.53,
        "momentum_threshold": 0.0007,
    },
}


# -- Config resuelta ----------------------------------------------------------

@dataclass
class Config:
    asset: str
    live: bool
    window: int = 15            # 5 o 15 minutos
    wallet_id: str = "default"  # identificador del wallet (ej: "w1", "w2")
    thresh_mult: float = 1.0    # multiplicador del threshold (1.0=normal, 1.25=+25%)

    binance_ws: str = ""
    slug_prefix: str = ""
    price_symbol: str = ""
    price_variant: str = ""
    win_secs: int = 900
    signal_start_tleft: int = 840
    signal_stop_tleft: int = 120
    min_ask_confirm: float = 0.20
    entry_size_usd: float = 5.0
    max_entry_price: float = 0.65   # per-asset override
    momentum_threshold: float = 0.0005  # per-asset override

    log_dir: Path = field(default_factory=lambda: Path("."))
    trades_csv: Path = field(default_factory=lambda: Path("."))
    windows_csv: Path = field(default_factory=lambda: Path("."))
    state_json: Path = field(default_factory=lambda: Path("."))

    def __post_init__(self):
        acfg = ASSET_CONFIGS[self.asset]
        wcfg = WINDOW_CONFIGS[self.window]

        self.binance_ws   = f"wss://stream.binance.com:9443/ws/{acfg['binance_stream']}"
        self.slug_prefix  = f"{acfg['slug_base']}-{wcfg['slug_tag']}-"
        self.price_symbol = acfg["price_symbol"]
        self.price_variant = wcfg["price_variant"]
        self.win_secs     = wcfg["win_secs"]
        self.signal_start_tleft = wcfg["signal_start_tleft"]
        self.signal_stop_tleft  = wcfg["signal_stop_tleft"]
        self.min_ask_confirm    = acfg.get("min_ask_confirm", 0.20)
        self.max_entry_price    = acfg.get("max_entry_price", MAX_ENTRY_PRICE)
        self.momentum_threshold = acfg.get("momentum_threshold", MOMENTUM_THRESHOLD) * self.thresh_mult
        self.entry_size_usd     = acfg.get("entry_size_usd", ENTRY_SIZE_USD)

        win_tag = f"{self.window}m"
        # Prefijo de wallet en el directorio de logs (excepto para "default" o "w1")
        wallet_prefix = f"{self.wallet_id}_" if self.wallet_id not in ("default", "w1") else ""
        if self.live:
            sub = f"{wallet_prefix}{self.asset}_momentum_{win_tag}_live"
        else:
            sub = f"{wallet_prefix}{self.asset}_momentum_{win_tag}"
        base = Path(__file__).parent / "logs" / sub
        self.log_dir      = base
        self.trades_csv   = base / "trades.csv"
        self.windows_csv  = base / "windows.csv"
        self.state_json   = base / "state.json"


# -- Utilidades ---------------------------------------------------------------

def iso(ts: int) -> str:
    return datetime.fromtimestamp(ts, tz=timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")

def snap_price(price: float) -> float:
    return round(max(0.01, min(0.99, round(price / TICK_SIZE) * TICK_SIZE)), 2)

def write_json(path: Path, data: dict):
    tmp = path.with_suffix(".tmp")
    try:
        tmp.write_text(json.dumps(data, ensure_ascii=False, indent=2))
        tmp.replace(path)
    except Exception as e:
        print(f"[json-write] {path.name}: {e}")
