# BOTTEST Changelog

## V11 — Momentum Taker (2026-05-27 → presente)

Cambio de paradigma: de V10 market maker (quotear ambos lados, 0% fees) a **taker direccional**.

**Concepto:** Monitorear precio Binance en tiempo real. Cuando se detecta movimiento
significativo vs strike del mercado Polymarket → comprar YES (sube) o NO (baja) al
ask real del CLOB. Una entrada por ventana máximo. Acepta 2% taker fee porque
WR ~70-80% lo compensa.

**Por qué V10 fracasó:** -$66 PnL, 32% WR. EWMA siempre convergía a fair=0.500
(cero edge predictivo) + adverse selection (80% fills ejecutados por arbers).

### Arquitectura

Módulos en `v11/`:
- `config.py` — Parámetros centrales, ASSET_CONFIGS, WINDOW_CONFIGS, Config dataclass
- `feeds.py` — BinanceFeed (precio real-time) + CLOBFeed (best_ask real vía WebSocket)
- `market.py` — Descubrimiento de mercados, strike, resolución outcome (API Polymarket)
- `strategy.py` — MomentumStrategy: evalúa señal direccional
- `executor.py` — TakerExecutor: órdenes aggressive limit, verify_fill, cancel
- `bot.py` — Loop principal: tick cada segundo, gestión ventanas, settle
- `logger.py` — CSV logging (trades.csv, windows.csv) + state.json
- `main.py` — Entry point con argparse (`--asset`, `--window`, `--live`)

Dashboards:
- `v11_multi_dashboard.html` — BTC+ETH+SOL combinado (desktop + mobile)
- `v11_dashboard.html` — Individual por asset
- `v11_multi_api.php` / `v11_api.php` — APIs PHP que leen CSVs y state

### Historial de cambios

#### 2026-05-27: Creación y lanzamiento

1. **V11 creado** (08:00) — config, feeds (solo Binance), market, strategy, executor, bot, logger, main
2. **Soporte 5min + 15min** (08:30) — `--window 5/15`, timing configurable por duración
3. **Paper BTC** (09:00) — Lanzados 15m y 5m paper
4. **BUG: fills a precio fijo** (15:00) — Trades a $0.52 sin CLOB real. 100% WR ficticio.
5. **FIX: CLOBFeed** (15:30) — WebSocket CLOB para best_ask real. Strategy requiere ask>0.
6. **Datos limpios** (16:30) — Reinicio con precios reales
7. **Preparación LIVE** (17:00) — Balance check, aggressive limit, verify_fill, cancel_all
8. **LIVE lanzado** (17:07) — 5m + 15m. Balance inicial: $42.71

#### 2026-05-27: Bug órdenes múltiples

9. **BUG CRÍTICO: órdenes duplicadas** (17:16) — Bot envió 5 órdenes en vez de 1:
   - verify_fill retornaba None (2s insuficiente)
   - cancel_order fallaba (`'str' object has no attribute 'orderID'`)
   - `_entered` se ponía True solo DESPUÉS de verify → siguiente tick enviaba otra orden
   - Resultado: 24.85 shares × $0.66 = $16.40 en vez de $5
10. **FIX parcial** (17:19) — `_entered=True` antes de verify. Pero cancel usaba método inexistente.
11. **FIX DEFINITIVO** (~19:30) — `_entered=True` PERMANENTE antes de verify. verify_fill solo informativo. Cap size a 1.5× lo ordenado. NUNCA resetear _entered. `cancel_order` usa `client.cancel_order()`.
12. **Relanzado** (~19:30) — Balance: $84.40 (subió por wins amplificados con duplicadas — suerte)

#### 2026-05-28: Multi-asset + optimización

13. **Multi-asset** (06:18) — ETH y SOL 5m paper añadidos
14. **CLOB confirm filter** (~10:00) — ETH/SOL tenían 33-46% WR por divergencia strike Binance/Polymarket. Fix: `min_ask_confirm=0.45` → solo entrar si CLOB confirma dirección.
15. **ETH/SOL LIVE** (~12:00) — $3/trade, filtro CLOB activado
16. **BTC 15m muerto** (~12:00) — Sesión tmux desapareció. PnL: -$0.41 total en dashboard, pero -$17.71 on-chain (6 losses seguidas en última fase).

#### 2026-05-28: Auditoría balance + per-asset thresholds

17. **Auditoría balance/PnL** (~15:00):
   - Discrepancia $6.53 entre balance delta y PnL delta
   - Causa: pérdidas BTC 15m on-chain (-$17.71 el 28 mayo) no tracked por dashboard + diferencias per-trade (fill price, fee rounding)
   - Verificado con historial on-chain completo (Buy USDC out + Redeem USDC in)

18. **Entry size ETH/SOL: $3→$5** (~16:00):
   - $3 generaba demasiado poco EV absoluto por trade
   - `max_entry_price=0.55` para ETH/SOL (>0.55 WR cae a ~50%, EV negativo)

19. **Per-asset momentum thresholds** (~17:00):
   - Análisis de volatilidad: BTC avg 0.065%, ETH avg 0.096%, SOL avg 0.089%
   - El threshold global 0.05% era ruido para ETH/SOL (25% WR en zona 0.05-0.07%) pero señal para BTC (69% WR)
   - Fix: BTC=0.0005 (0.05%), ETH=0.0007 (0.07%), SOL=0.0007 (0.07%)
   - `strategy.py` y `bot.py` usan `cfg.momentum_threshold` en vez de constante global
   - Se descartó auto-calibración: insuficientes datos, riesgo V9 calibration loop

### Parámetros actuales (2026-05-28)

| Parámetro | BTC | ETH | SOL |
|-----------|-----|-----|-----|
| momentum_threshold | 0.05% | 0.07% | 0.07% |
| entry_size_usd | $5 | $5 | $5 |
| max_entry_price | 0.65 | 0.55 | 0.55 |
| min_ask_confirm | 0.20 | 0.45 | 0.45 |
| taker_fee | 2% | 2% | 2% |

Ventana 5min: signal_start=270s (4.5min), signal_stop=60s (1min)

### Estado (2026-05-28 ~17:00 UTC)

- **BTC 5m live**: ~70 trades, +$130, ~70% WR — asset estrella
- **ETH 5m live**: ~20 trades, ~-$5 — reconfigurado con threshold 0.07%
- **SOL 5m live**: ~25 trades, ~-$3 — reconfigurado con threshold 0.07%
- **BTC 15m**: MUERTO, no relanzar
- Tmux: `v11_btc_5m_live`, `v11_eth_5m_live`, `v11_sol_5m_live`, `v11_eth_5m`, `v11_sol_5m`

### Lecciones aprendidas

1. **SIEMPRE usar CLOB real** para precios paper/live. Nunca inventar precios (bug del $0.52 fijo).
2. **`_entered=True` ANTES de verify** para evitar duplicadas. Nunca resetear.
3. **Settle siempre del API Polymarket**, nunca inferir de Binance.
4. **Threshold no es universal**: cada asset tiene volatilidad distinta. 0.05% es señal para BTC pero ruido para ETH/SOL.
5. **min_ask_confirm** esencial para assets con divergencia Binance/Polymarket.
6. **Tmux sessions**: crear shell vacío primero, luego enviar comando — evita que la sesión muera con el proceso.

---

## V9.11 — 2026-05-23

**Problema detectado:** V9.10 deshabilitó calibración y subió MAX_FAIR a 0.95,
recreando exactamente el estado V9.7b (que perdió $74). El bot dejó de operar
porque sin calibración el fair_up crudo llega a 0.85-0.90, y con MAX_FAIR=0.80
original se bloqueaba, pero con 0.95 se abría a trades con sesgo UP sin corrección.

**Patrón cíclico identificado (V9.7→V9.10):**
1. Modelo crudo tiene sesgo UP → muchas pérdidas BUY YES falsas
2. Se añade calibración agresiva → PAV aplasta todo a ~0.44 → no entra trades
3. Se deshabilita calibración → vuelve el sesgo UP crudo → pérdidas
4. Repetir

**Solución V9.11:** Romper el ciclo restaurando la base V9.6 (única versión
probada rentable, +$32/noche) y conservar solo los bug fixes de infraestructura
de V9.10.

### Cambios aplicados:

| Parámetro | V9.10 (roto) | V9.11 | Fuente |
|-----------|-------------|-------|--------|
| `USE_CALIBRATION` | `False` | `True` | V9.6 |
| `CALIB_BLEND` | `0.50` | `0.90` | V9.6 |
| `CALIB_WINDOW_DAYS` | `5` | `5` | V9.6 |
| `MAX_FAIR` | `0.95` | `0.80` | V9.6 |
| `MIN_FAIR` | `0.05` | `0.20` | V9.6 |
| `EDGE_DIR` | `0.18` | `0.15` | V9.6 |
| `EDGE_DIR_NO` | `0.15` | `0.12` | V9.6 |
| `MIN_DIR_PRICE` | `0.35` | `0.30` | V9.6 |
| `MAX_DIR_PRICE` | `0.65` | `0.70` | V9.6 |
| BTC `edge_dir` | `0.18` | `0.15` | V9.6 |
| ETH `edge_dir` | `0.20` | `0.18` | V9.6 |
| XRP `edge_dir` | `0.22` | `0.20` | V9.6 |
| `momentum_min_pct` | varios inflados | V9.6 originales | V9.6 |

### Conservado de V9.10 (bug fixes probados):

- `ENABLE_MODE_A = False` — fees 2 patas hacen Mode A no rentable
- `ENABLE_BUY_NO = False` — modelo no predice bajadas, WR invertido
- Calibración lee dirs `_live` (530 trades propios BTC)
- Fix `_bin_idx()` floating-point
- `CALIB_MIN_BINS = 3` — gate mínimo bins
- `LOSS_STREAK_MAX/COOL` — cooldown rachas
- `MIN_EV_POST_CALIB` — gate EV post-calibración
- `SIGMA_FLOOR_ANNUAL` — evita sigma extremo en mercados calmos

### Regla para futuras versiones:

**NO deshabilitar calibración ni subir MAX_FAIR > 0.80.** Eso recrea V9.7b.
Si la calibración no funciona, ajustar `CALIB_BLEND` (bajar de 0.90 hacia 0.50)
para dar más peso al modelo, pero nunca desactivar del todo.

---

## V9.10 — 2026-05-22

**Problema detectado:** WR cayó de 46.7% (histórico) a 35% en últimas 24h. BTC live
perdió $38.25 en 100 trades, con 87/100 siendo BUY YES. El modelo dice fair_up~0.78
pero la realidad es ~42% Up. La calibración no estaba corrigiendo.

### Root causes identificados:

1. **Calibración no leía trades live** — `calibration.py` solo leía `logs/btc/` (paper),
   ignorando `logs/btc_live/` con 539 trades en 5 días. El bot live calibraba con datos
   ajenos, no con su propia experiencia.

2. **Sin gate mínimo de calibración** — Si un bin no tenía datos, el bot usaba fair_up
   crudo del modelo (0.78) en vez del calibrado (0.45). Con edge falso de 0.36 entraba
   trades con EV negativo.

3. **Bug floating-point en bins** — `int(0.70/0.10)` = 6 en Python (no 7) por precisión
   float. Afectaba bins 0.30, 0.60, 0.70 — datos mal clasificados.

4. **Sin protección ante rachas perdedoras** — El bot seguía operando igual tras 10
   pérdidas consecutivas.

### Cambios aplicados:

| Cambio | Archivo | Detalle |
|--------|---------|---------|
| Calibración incluye `_live` dirs | `calibration.py` | `asset_dirs` ahora incluye `btc_live`, `eth_live`, etc. `is_own` reconoce ambos. Duplica datos disponibles. |
| Fix floating-point bins | `calibration.py` | Nuevo `_bin_idx()` con `+1e-9` epsilon. Usado en `apply()` y `_load()`. |
| Gate mínimo calibración | `strategy.py` | `CALIB_MIN_BINS=3`: no opera Mode B sin ≥3 bins calibrados. |
| EV post-calibración | `strategy.py` | `MIN_EV_POST_CALIB=0.02`: bloquea trades donde edge-fees < 0.02/share. |
| Cooldown rachas | `strategy.py` + `bot.py` | `LOSS_STREAK_MAX=5`, `LOSS_STREAK_COOL=3`: 5 pérdidas → pausa 3 ventanas. |
| Nuevos contadores | `bot.py` | `calib_blocked`, `ev_blocked`, `cooldown_blocked`, `loss_streak` en stats. |
| Mode A desactivado | `config.py` + `bot.py` | `ENABLE_MODE_A=False`. Fees taker de 2 patas (~$0.035/sh) hacen breakeven en combo=0.965. Los 2 trades live perdieron $0.29. MAX_COMBO bajado a 0.94 por si se reactiva. |
| BUY NO desactivado | `config.py` + `strategy.py` | `ENABLE_BUY_NO=False`. Calibración PAV aplasta todo fair_up a ~0.44 → sesgo bearish permanente. NO histórico: 112 trades, 42% WR, más edge=peor resultado (señal invertida). 92% del PnL viene de YES. |
| Calibración desactivada | `config.py` | `USE_CALIBRATION=False`. Con NO desactivado, la calibración solo destruye señal YES. Raw model YES: 52% WR, $360. Calibrado YES (blend=0.95): 38% WR, $85. El bot queda inerte si calibración aplasta fair_up a 0.44. |
| MAX_FAIR 0.80→0.95 | `config.py` | Sin calibración el raw fair_up llega a 0.85-0.90. MAX_FAIR=0.80 bloqueaba el 12% de entries incluyendo bucket 0.80-0.90 (68% WR, $35 PnL). MIN_FAIR 0.20→0.05 simétrico. |

### Simulación retroactiva (BTC últimas 24h):

| Métrica | V9.9 | V9.10 (simulado) |
|---------|------|-------------------|
| Trades | 108 | 68 |
| PnL | -$38.25 | +$9.19 |
| Trades evitados | — | 40 (EV negativo) |
| PnL salvado | — | $47.44 |

### Parámetros nuevos en `config.py`:

```python
CALIB_MIN_BINS     = 3     # mínimo bins calibrados para operar Mode B
LOSS_STREAK_MAX    = 5     # pérdidas consecutivas antes de cooldown
LOSS_STREAK_COOL   = 3     # ventanas de pausa
MIN_EV_POST_CALIB  = 0.02  # EV mínimo por share post-calibración
```

### Cómo revertir:
```bash
# Restaurar V9.9: revertir cambios en calibration.py, strategy.py, bot.py, config.py
# Los backups de V9.8 están en v9_backup_v98/
```

---

## V9.1 — 2026-05-14

**Problema detectado:** El modelo GBM tiene un sesgo UP sistemático. En la primera
sesión de V9 (~92 trades, ~8h), el 98% de los trades fueron BUY_YES a pesar de que
el mercado estaba 63% Down. El WR resultante fue 38%, generando -$45 de PnL.

**Dato clave:** Los trades con mayor edge (modelo "más confiado") tenían PEOR WR:
- edge~0.15: 53% WR
- edge~0.20: 21% WR
- edge~0.25: 27% WR

Esto indica que el modelo diverge de la realidad: cuando el mercado baja precios
(más "edge"), el modelo lo interpreta como oportunidad, pero el mercado tiene razón.

### Cambios aplicados:

| Parámetro | V9.0 | V9.1 | Motivo |
|-----------|------|------|--------|
| `USE_DRIFT` | `True` | `False` | El drift de 60s introduce sesgo UP. Cuando BTC sube ligeramente, el drift empuja fair_up hacia arriba, generando señales BUY_YES incluso en mercados bajistas. |
| `CALIB_BLEND` | `0.5` | `0.7` | Con 50/50 modelo/datos, la calibración no corregía suficiente el sesgo UP del modelo. Al dar 70% peso a datos empíricos, la calibración corrige efectivamente: ej. fair=0.55 con bin_rate=0.32 → calibrado=0.39 (BUY_NO en vez de BUY_YES). |
| `SIZE_USD_DIR` | `2.0` | `3.0` | (V9.2, aplicado antes) Más exposición por trade. |
| `SIZE_DIR_MAX_MULT` | `1.0` | `1.5` | (V9.2, aplicado antes) Sizing dinámico: hasta $4.50 con edge alto. |

### Efecto esperado:
- Más trades BUY_NO (antes eran solo 2% del total)
- Señales más equilibradas entre YES/NO según datos empíricos
- WR debería subir porque el bot no apostará sistemáticamente UP

### Cómo revertir:
```bash
cp v9/config_v9.0_backup.py v9/config.py
# Reiniciar bots
```

### Bug fix en main.py:
`Config.use_drift` ahora respeta el flag global `USE_DRIFT` de config.py.
Antes, solo se desactivaba con `--no-drift` en CLI, ignorando el valor en config.py.

---

## V9.0 — 2026-05-13

Versión base. Backup guardado en `v9/config_v9.0_backup.py`.

Parámetros clave:
- EDGE_DIR=0.15, CALIB_BLEND=0.5, USE_DRIFT=True
- SIZE_USD_DIR=2.0, SIZE_DIR_MAX_MULT=1.0
- MIN_DIR_PRICE=0.30, MAX_DIR_PRICE=0.70
