Skip to content

anthonydavalos/betsniper

Repository files navigation

🎯 BetSniper V3 - Quantitative Sports Arbitrage Engine

Sistema de Trading Deportivo Automatizado con Gestión de Riesgo Cuantitativo

Node.js React License

🔐 Cybersecurity Perspective

This project demonstrates real-world techniques used in:

  • WebSocket traffic analysis
  • Real-time system behavior monitoring
  • Reverse engineering of event-driven platforms

📖 Tabla de Contenidos


🌟 Descripción General

BetSniper V3 es un sistema de trading deportivo de alta frecuencia diseñado bajo principios de finanzas cuantitativas. El sistema opera como un arbitrajista algorítmico, cruzando datos en tiempo real entre un "Sharp Bookie" (Pinnacle/Arcadia - fuente de probabilidades reales) y un "Soft Bookie" (Altenar/DoradoBet - mercado objetivo) para identificar ineficiencias y ejecutar estrategias de valor esperado positivo (EV+).

¿Cómo Funciona?

  1. Ingesta de Datos: Conexión WebSocket con Pinnacle para obtener cuotas "sin margen" (Fair Odds) que representan la probabilidad real de los eventos.
  2. Escaneo de Mercado: Análisis continuo de cuotas de Altenar (DoradoBet) para detectar discrepancias con las probabilidades reales.
  3. Cálculo Matemático: Aplicación de Kelly Criterion con perfiles de riesgo dinámicos para determinar el tamaño óptimo de cada apuesta.
  4. Ejecución Simulada: Sistema de Paper Trading que simula la ejecución de apuestas y trackea P&L en tiempo real.
  5. Monitoreo Continuo: Seguimiento del estado de apuestas activas y liquidación automática basada en resultados reales.

🆕 Cambios Recientes (Último Commit)

Esta sección resume lo implementado desde el último commit para dejar trazabilidad técnica y operativa.

Actualización 2026-04-05 (v3.4.32)

  • Indicador de cierre reciente de Double Chance (DC):
    • El backend ahora detecta eventos donde DC estaba disponible y se cerró recientemente.
    • El preview expone en diagnostics:
      • dcClosedRecentlyCount
      • dcClosedRecentWindowMinutes
      • dcClosedRecentlySample
    • La UI de ARBITRAJE muestra un badge preventivo DC cerrado recientemente para visibilidad operativa.
  • Anti-fantasma de DC en caché Altenar:
    • En la ingesta masiva de Altenar, si el feed actual no trae DC, ya no se hereda el DC previo.
    • Esto elimina persistencia de cuotas DC stale por reciclaje de snapshot anterior.
  • Guardia de arbitraje cross-provider (consistencia operativa):
    • El preview exige por defecto oportunidades cruzadas entre providers (Altenar + Arcadia/Pinnacle).
    • Se agrega diagnóstico skippedSameProvider para auditar descartes por same-book.
  • Nueva configuración documentada en .env.example:
    • ARBITRAGE_DC_CLOSED_RECENT_WINDOW_MINUTES=15

Actualización 2026-04-05 (v3.4.31)

  • Validación anti-stale extendida (burn-in 60m):
    • Se ejecutó corrida t0..t60 con evidencia por muestras y por historial persistido.
    • Resultado: criterio cumplido (p95 skippedStaleAltenar < 10) en ambas lecturas.
    • Se agregó guía operativa de ejecución/lectura en docs/ops/arbitrage-stale-burnin.md.
  • UI de riesgo arbitraje con recálculo automático:
    • Al cambiar stakeMode, stakeFixedAmount, caps o umbrales, la preview se refresca automáticamente con debounce.
    • Objetivo: evitar desalineación visual entre “Stake final ticket” y “Split Recomendado”.
  • Ajuste de defaults en .env.example (scheduler linked anti-stale):
    • ALTENAR_PREMATCH_SCHEDULER_LINKED_MAX_INTERVAL_MS=90000
    • ALTENAR_PREMATCH_SCHEDULER_STALE_SWEEP_THRESHOLD_MS=120000
    • ALTENAR_PREMATCH_SCHEDULER_STALE_SWEEP_MAX_EVENTS_PER_TICK=16
  • Aliases dinámicos (mantenimiento rutinario):
    • Se actualizaron entradas en src/utils/dynamicAliases.json para mantener precisión de matching.

Actualización 2026-04-04 (v3.4.30)

  • Pre-flight dual con refresh Altenar on-demand (anti-hedge por cuota stale):
    • Antes de ejecutar Arcadia -> Altenar, el pre-flight fuerza GetEventDetails del evento Altenar objetivo.
    • Si el refresh falla o el evento desaparece, la ejecución dual se bloquea con reason explícito (altenar-refresh-failed) antes de disparar Arcadia.
  • Preview de arbitraje con refresh dirigido por evento:
    • GET /api/opportunities/arbitrage/preview ahora acepta:
      • refreshAltenarNow=1
      • refreshAltenarEventId=<altenarEventId>
    • Respuesta incluye bloque onDemandRefresh.altenarEvent para trazabilidad de éxito/fallo del refresh en tiempo real.
  • Hardening anti-stale en motor de arbitraje:
    • Se excluyen oportunidades cuando altenarUpcoming.lastUpdated supera ARBITRAGE_ALTENAR_MAX_ODD_AGE_MS.
    • Se añade diagnóstico skippedStaleAltenar para auditoría operativa.
  • Scheduler Altenar con capacidad adaptativa (base + burst):
    • Se parametrizan concurrency y batch por .env.
    • Activación automática de burst cuando hay backlog stale en eventos vinculados y cercanos al inicio.
    • Logs de capacidad por ciclo para observabilidad (BASE/BURST, métricas stale vinculadas).

Actualización 2026-04-03 (v3.4.29)

  • Nuevo canal prematch por WS Arcadia (MQTT) con fallback automático a polling y degradación desde db.upcomingMatches para evitar feed vacío.
  • Nuevo endpoint de observabilidad operativa:
    • GET /api/pinnacle/prematch-ws-health
    • devuelve mode, wsConnected, wsStale, timestamps clave y cobertura (fixtures, subscribedTopics).
  • La pestaña Prematch del frontend muestra un banner de estado en tiempo real:
    • WS conectado,
    • fallback polling (con reason),
    • deshabilitado por entorno,
    • o health no disponible.
  • Nueva bandera de control granular para workers:
    • DISABLE_PINNACLE_PREMATCH_WS=true/false.

Smoke recomendado:

curl -sS http://localhost:3000/api/pinnacle/prematch-ws-health

Actualización 2026-04-01 (v3.4.24)

  • Fase 1 (Semi-Auto por pata en ARBITRAGE):
    • client/src/App.jsx ahora permite ejecutar pata individual desde cada tarjeta de arbitraje cuando el provider de la pata es Altenar.
    • Si la pata es Arcadia/Pinnacle se muestra como referencia, sin envío automático en Fase 1.
    • Se agregó conversión de legs de arbitraje (1x2 y DC+opuesto) a oportunidad ejecutable para flujo semi-auto.
  • Dry-run obligatorio antes de envío real (Booky):
    • Antes de confirm real se ejecuta POST /api/booky/real/dryrun/:id de forma obligatoria.
    • Si el dry-run falla, el ticket se cancela y no se envía placeWidget.
    • El resumen del dry-run (stake/odd/requestId) se integra en el bloque de confirmación visible en UI.
  • Fase 2 (Ejecución Dual Secuencial Arcadia -> Altenar):
    • Nuevo botón Ejecutar Dual por oportunidad en la pestaña ARBITRAGE.
    • Secuencia operativa: Arcadia (prepare -> dryrun -> confirm-fast) y luego Altenar (prepare -> dryrun -> confirm-fast).
    • Selección automática de patas por provider (Arcadia y Altenar) priorizando la de mayor stake por lado.
    • Outcomes explícitos en UI:
      • CONFIRMED (ambas patas confirmadas),
      • REJECTED (falla Arcadia antes de ejecutar Altenar),
      • HEDGE_REQUIRED (Arcadia confirmada y Altenar rechazada o incierta).
  • Control de ejecución concurrente:
    • Lock por tarjeta de arbitraje para evitar doble disparo simultáneo durante la secuencia dual.

Actualización 2026-04-01 (v3.4.23)

  • Se dejó operativo el pipeline de Double Chance + opuesto 1x2 para Sprint A.1 de arbitraje.
  • Hardening en persistencia de mercados DC para evitar pérdida de datos entre ingesta, cache-first y scanner:
    • scripts/ingest-pinnacle.js ahora persiste odds.doubleChance derivado desde 1x2.
    • src/services/prematchScannerService.js conserva doubleChance al hidratar upcomingMatches desde cache prematch.
    • scripts/ingest-altenar.js y src/services/altenarPrematchScheduler.js ahora mapean DC con shape real de Altenar (desktopOddIds/mobileOddIds, typeId 9/10/11).
  • Validación operativa posterior al ajuste:
    • upcomingMatches=63, pinnacleWithDC=59
    • altenarUpcoming=81, altenarWithDC=79
    • linked=30, linkedAltWithDc=30
  • Resultado: cobertura técnica de DC ya habilitada en ambos lados para oportunidades de arbitraje de 2 patas; si no hay oportunidades en un snapshot puntual, la causa pasa a ser de pricing/mercado y no de falta de datos DC.

Actualización 2026-03-29 (v3.4.22)

  • Pinnacle ahora tiene recuperación histórica completa y reconciliación local:
    • GET /api/pinnacle/history sincroniza apuestas remotas de Arcadia y las cruza por providerBetId con portfolio.
    • GET /api/pinnacle/account soporta refresh/historial opcional para snapshot operativo.
  • Se agregó cálculo de PnL real de Pinnacle anclado a cashflow externo (/transactions):
    • base de capital por depósitos/retiros,
    • PnL mostrado = balance actual - base externa,
    • evita mezclar saldo Pinnacle con PnL de Booky.
  • UI de Auto Placement reforzada:
    • proveedor manual runtime (BOOKY/PINNACLE),
    • badge/botón de PINNACLE con sync manual de historial (SYNC PINNACLE al hover, SYNC... en progreso),
    • layout responsive para evitar desbordes en la tarjeta.
  • Finalizados mejorado con trazabilidad por origen:
    • badge de origen (BOOKY/PINNACLE/SIM) y filtro por proveedor (ALL/BOOKY/PINNACLE/SIM).
  • Nuevos scripts de operación/diagnóstico Pinnacle:
    • npm run capture:pinnacle:account
    • npm run capture:pinnacle:account:headless
    • scripts/debug-pinnacle-history-endpoints.js

Detalle técnico completo en CHANGELOG.md, entrada v3.4.22.

SOP Operativo Rápido (v3.4.22)

Checklist recomendado para operación diaria post-release:

  1. Verificar cuenta Pinnacle:
  • GET /api/pinnacle/account?refresh=1
  • Confirmar balance, pnl.total, pnl.baseCapital y pnl.baseCapitalSource.
  1. Verificar sync histórico:
  • GET /api/pinnacle/history?refresh=1&status=settled&days=180&limit=500
  • Confirmar success=true y revisar reconcileStats.touchedCount.
  1. Verificar UI:
  • En Auto Placement, seleccionar PINNACLE.
  • Confirmar badge azul (PINNACLE) y acción de sync (SYNC PINNACLE al hover).
  • Ejecutar sync manual y validar estado SYNC... + mensaje de éxito.
  1. Validar consistencia financiera:
  • El PnL mostrado para Pinnacle debe seguir: PnL = balance actual - base de capital externa.
  1. Monitoreo mínimo:
  • 1 sync manual diario de control.
  • Revisar si hay error en transactions o history.

Variables recomendadas para ajustar en producción:

  • PINNACLE_PNL_WINDOW_DAYS
  • PINNACLE_PNL_BASE_CAPITAL (solo fallback)
  • PINNACLE_HISTORY_DEFAULT_DAYS
  • PINNACLE_HISTORY_DEFAULT_STATUS

Actualización 2026-03-29 (v3.4.21)

  • Selector de proveedor de auto-placement en runtime (booky o pinnacle) en scannerService.
  • Nuevo endpoint de consulta: GET /api/opportunities/live/placement-provider.
  • Nuevo endpoint de cambio en caliente: POST /api/opportunities/live/placement-provider.
  • Diagnóstico de arranque/decisión live ahora expone proveedor activo y opciones permitidas.

Actualización 2026-03-29 (v3.4.20)

  • Auto-placement ahora soporta múltiples estrategias por configuración (AUTO_SNIPE_ALLOWED_TYPES), con default activo para LIVE_SNIPE, LA_VOLTEADA y LIVE_VALUE.
  • Se elimina la restricción hardcodeada que dejaba fuera LIVE_VALUE del motor automático.
  • Diagnóstico runtime mejorado: GET /api/opportunities/live/diagnostics ahora incluye scanner.autoPlacementAllowedTypes.
  • El reason de descarte evoluciona de not-snipe a type-not-enabled cuando el tipo no está habilitado.
  • Se expandió nuevamente src/utils/dynamicAliases.json para fortalecer matching internacional (variantes de nombre/equipo/selección).
  • Detalle completo en CHANGELOG.md, entrada v3.4.20.

Actualización 2026-03-26 (v3.4.19)

  • Finalizados ahora diferencia correctamente la fuente de ejecución:
    • BOOKY para remoto,
    • REAL para real local,
    • SIM solo para simulado.
  • Se corrigió inconsistencia visual de marcador en filas del mismo partido (1-1 vs ?-?) mediante fallback de score por eventId/match.
  • En pestaña Finalizados, el Ticket <providerBetId> ya se muestra también para filas reales locales (isRealHistory).
  • Se expandió src/utils/dynamicAliases.json con aliases adicionales para mejorar matching (variantes ortográficas, ligas menores y U21).
  • Detalle completo en CHANGELOG.md, entrada v3.4.19.

Actualización 2026-03-22 (v3.4.17)

  • Finalizados REAL ahora soporta historial completo sin truncamiento por ventana corta (historyLimit=0) y con hidratación forzada inicial en pestaña FINISHED.
  • Se corrigió cierre stale en polling de frontend para que el intervalo respete tab/modo actual al decidir límites de historial.
  • Se endureció sincronización remota con detección de caché parcial (limitBound) y bypass automático cuando se requiere fetchAll.
  • Auto-snipe agrega confirmación SIM explícita y reintento único ante re-quote.
  • Drift de odds en confirmación Booky pasa a ser configurable por entorno (BOOKY_LIVE_MAX_ODD_DRIFT, BOOKY_PREMATCH_MAX_ODD_DRIFT).
  • Detalle completo en CHANGELOG.md, entrada v3.4.17.

Actualización 2026-03-24 (v3.4.18)

  • Nuevo endpoint de diagnóstico live: GET /api/opportunities/live/diagnostics con pipeline (raw/dedup/stable/final), razones agregadas y eventos recientes.
  • LIVE_SNIPE ahora expone razones pre-oportunidad (por ejemplo ev_non_positive, stake_below_1, real_prob_invalid) para auditoría real de por qué no entra una señal.
  • Requote de provider (providerCode=4) corregido end-to-end:
    • Backend preserva BOOKY_PLACEWIDGET_REQUOTE_REQUIRED.
    • Frontend muestra mensaje específico de re-quote y permite reintento inmediato guiado.
  • Integridad de marcador en monitor reforzada:
    • eliminación de 0-0 falsos,
    • normalización de score,
    • etiqueta DESYNC,
    • fallback STALE en micro-cortes de feed.
  • Detalle completo en CHANGELOG.md, entrada v3.4.18.

1) Arcadia Gateway: auto-refresh estable y sin bucles

  • server.js ahora puede levantar services/pinnacleGateway.js automáticamente (PINNACLE_GATEWAY_AUTOSTART=true).
  • Se añadió cooldown de relanzamiento (PINNACLE_GATEWAY_AUTOSTART_MIN_INTERVAL_MS) para evitar apertura repetitiva de Puppeteer/login.
  • Watchdog para trigger stale con frecuencia configurable (PINNACLE_GATEWAY_TRIGGER_CHECK_INTERVAL_MS).
  • Nuevo script de operación: npm run pinnacle:gateway.

2) Stale detection más precisa en LIVE

  • src/services/liveValueScanner.js incorpora guard de desync de reloj Pinnacle vs Altenar con umbral configurable (PINNACLE_STALE_TIME_DIFF_MINUTES).
  • El trigger de stale se emite solo con persistencia (>=2 hits por evento) y respeta cooldown (PINNACLE_STALE_TRIGGER_MIN_INTERVAL_MS).
  • Ajuste de alineación de reloj visual solo para drift pequeño (<=1 min), evitando maquillar sockets congelados.

3) Pinnacle Gateway con auto-login opcional

  • services/pinnacleGateway.js agrega auto-login best-effort (frames + selectores comunes) controlado por:
    • PINNACLE_AUTO_LOGIN_ENABLED
    • PINNACLE_LOGIN_USERNAME
    • PINNACLE_LOGIN_PASSWORD
  • Nuevos controles de stale-check/grace:
    • PINNACLE_STALE_CHECK_INTERVAL_MS
    • PINNACLE_STALE_RELOAD_ALLOW_DURING_GRACE
  • El mínimo de sockets Arcadia queda configurable y operativo desde 1 (PINNACLE_ARCADIA_MIN_SOCKETS=1).

4) Pipeline LIVE más observable y estable

  • src/services/scannerService.js ahora loguea pipeline raw/dedup/stable/final para explicar por qué una detección interna puede no llegar al payload final del endpoint.
  • Se corrigió el filtro de estabilidad cuando QUOTE_STABILITY_MIN_HITS <= 1 para no exigir confirmación adicional innecesaria.

5) UX de token Booky más resiliente

  • client/src/App.jsx mejora el auto-renew silencioso del token Booky:
    • Si /api/booky/token/renew falla o no inicia, reduce el tiempo para reintento corto (sin esperar todo el cooldown largo).
    • Si el backend responde busy, respeta cooldown normal para no spamear renovaciones.

6) Config y matching

  • .env.example fue actualizado con todos los nuevos knobs de Arcadia/Gateway/autologin.
  • src/utils/dynamicAliases.json añade aliases nuevos para reforzar matching en ligas con nombres variantes.

7) AUTO_SNIPE con outcome final y razones auditables

  • src/services/scannerService.js ahora deja resultado final por intento con estado:
    • CONFIRMED
    • REJECTED
    • UNCERTAIN
  • Si una oportunidad LIVE_SNIPE queda en manual, se registra reason=... para evitar silent-drop.
  • El arranque del motor auto-snipe loguea parametros efectivos (enabled/dryrun/minEV/minStake/hourlyCap).

8) Reentry policy segura para LIVE_SNIPE

  • Se incorporan guardas de reentrada para evitar entradas casi identicas sin mejora de precio:
    • AUTO_SNIPE_REENTRY_MIN_ODD_IMPROVEMENT_PCT
    • AUTO_SNIPE_REENTRY_MIN_ODD_POINTS
    • AUTO_SNIPE_MAX_ENTRIES_PER_PICK
  • Si no hay mejora material o se supero el limite por pick, el flujo marca razon explicita (reentry-no-improvement, reentry-cap).

9) Matcher High Confidence (Manual Assist)

  • client/src/components/ManualMatcher.jsx incorpora motor de sugerencias con score compuesto.
  • Nuevas acciones operativas para lote controlado:
    • SUGERIR
    • APLICAR
    • APLICAR TOP 20
  • El score combina similitud home/away, riesgo de swap, ventana temporal y contexto de liga/pais.

10) Evidencia provider preservada en rechazos Booky

  • src/services/bookySemiAutoService.js conserva providerStatus/providerBody/requestId al envolver errores de placeWidget.
  • Esto mejora auditoria de rechazos definitivos (BOOKY_PLACEWIDGET_REJECTED) y post-mortem de apuestas reales.

11) Separación Arcadia vs Booky en perfiles de Chrome

  • services/pinnacleGateway.js ahora usa perfil dedicado de Pinnacle por defecto (data/pinnacle/chrome-profile).
  • Se elimina el acoplamiento por defecto con BOOK_PROFILE en Arcadia.
  • Se mantiene override por entorno con PINNACLE_CHROME_PROFILE_DIR.

12) Arcadia login más preciso con estado de sesión

  • services/pinnacleGateway.js detecta explícitamente cuando ya hay sesión activa en Pinnacle (Account-Menu, bankroll/deposito).
  • Solo intenta autologin cuando detecta el bloque real de login (header-login-loginButton, Forms-Element-username/password).
  • Se refuerza compatibilidad con selectores de header (input#username, input#password, submit en header-login-loginButton).

13) Autologin ACity movido al lugar correcto (scripts Booky)

  • scripts/extract-booky-auth-token.js y scripts/capture-altenar-betslip.js implementan flujo ACity:
    • trigger button#login / #login,
    • detección de input[name="user"],
    • submit robusto (INICIAR SESION / INGRESAR + fallbacks).
  • Si ya está logueado (header con MIS APUESTAS + DEPOSITAR), omite login automático.

14) Configuración de perfil dedicado para Pinnacle

  • .env.example documenta:
    • PINNACLE_CHROME_PROFILE_DIR=data/pinnacle/chrome-profile
  • Recomendación operativa: mantener Arcadia y Booky en perfiles separados para evitar contaminación de sesión.

15) Sync automático de token Altenar a Google Sheets

  • scripts/extract-booky-auth-token.js captura y persiste:
    • ALTENAR_BOOKY_AUTH_TOKEN (JWT para flujo real placeWidget), y
    • ALTENAR_WIDGET_AUTH_TOKEN (token crudo de api/widget para scanner/live/prematch).
  • La sincronizacion a Google Sheets se ejecuta solo para ALTENAR_BOOKY_AUTH_TOKEN.
  • Variable de entorno usada para el webhook:
    • GSHEETS_TOKEN_WEBHOOK_URL
  • Politica de seguridad:
    • .env.example solo incluye ejemplo/comentario de la variable.
    • .env (local, no versionado) debe contener la URL real del Apps Script.
  • Comportamiento operativo:
    • Si el webhook no esta definido, la captura de token continua sin fallo.
    • Si el webhook falla, se registra warning/error pero no se bloquea la renovacion del token.

Secuencia exacta de actualizacion (timing):

  1. Puppeteer detecta request Altenar con Authorization valido.
  2. El script actualiza .env (ALTENAR_BOOKY_AUTH_TOKEN=Bearer ... y/o ALTENAR_WIDGET_AUTH_TOKEN=<raw>).
  3. Inmediatamente en la misma ejecucion hace POST al webhook con { token: "Bearer ..." }.
  4. El Apps Script (doPost) recibe payload y actualiza TOKEN!A1.
  5. El proceso de extraccion finaliza como exitoso.

Nota: la sincronizacion a Sheets no se ejecuta antes del guardado en .env; siempre ocurre despues del upsert local del token.

Ejemplo esperado de payload enviado al webhook:

{
  "token": "Bearer <jwt>"
}

16) Renovación automática del token widget (401/403) con anti-bucle

  • El backend no renueva por intervalo fijo; renueva de forma reactiva solo cuando Altenar responde con error de autenticación (401 o 403) en endpoints críticos del scanner (GetLivenow, GetEventDetails, GetUpcoming).
  • Al detectar 401/403, se dispara en segundo plano el script de captura de token con Puppeteer para refrescar ALTENAR_WIDGET_AUTH_TOKEN.
  • Para evitar bucles (múltiples scanners fallando al mismo tiempo), la renovación respeta un cooldown global por proceso:
    • ALTENAR_WIDGET_TOKEN_RENEW_COOLDOWN_MS (default recomendado: 120000).
  • El tiempo máximo de la captura automática está controlado por:
    • ALTENAR_WIDGET_TOKEN_RENEW_TIMEOUT_MS (default recomendado: 90000).

Comportamiento operativo:

  1. Llega 401/403 en una llamada Altenar del scanner.
  2. Se intenta lanzar una renovación automática (si no está en cooldown).
  3. Si ya hubo intento reciente, se suprime el nuevo intento y se registra aviso con segundos restantes.
  4. Si la captura logra nuevo token en .env, se recomienda reiniciar backend para recargar entorno de forma limpia.

Guía de tuning:

  • Operación normal: ALTENAR_WIDGET_TOKEN_RENEW_COOLDOWN_MS=60000 a 120000.
  • Alta carga/ruido de feed: mantener 120000 o subir a 180000.
  • Debug puntual: 30000 temporalmente.
  • Evitar valores demasiado bajos (<15000) para no abrir flujos Puppeteer en ráfaga.

17) Real placement más robusto ante 401/403 y mismatch de integración

  • src/services/bookySemiAutoService.js ahora distingue explícitamente auth failures (401/403) de rechazos de mercado.
  • Si placeWidget devuelve 401/403, el backend responde BOOKY_TOKEN_RENEWAL_REQUIRED (428) con diagnóstico y disparo de renovación asistida.
  • Se valida que la integración del JWT (payload.Integration) coincida con ALTENAR_INTEGRATION; si no coincide, se bloquea confirmación con motivo claro.
  • GET /api/booky/token-health expone señales adicionales:
    • tokenIntegration
    • tokenUserName
    • integrationMismatch

18) Preparación de apuesta real alineada con auth widget

  • En preparación de real placement, GetEventDetails usa configuración pública unificada (getAltenarPublicRequestConfig) para evitar desalineación de headers/auth.
  • Si GetEventDetails responde 401/403, se retorna BOOKY_WIDGET_TOKEN_RENEWAL_REQUIRED con eventId y estado de auto-renew.

19) Recuperación automática de ticket en desincronización UI

  • client/src/App.jsx añade recuperación controlada cuando aparece ticket no encontrado al confirmar:
    • Busca un DRAFT vigente de la misma oportunidad (eventId + selection + market).
    • Reintenta confirm una sola vez con ese ticket recuperado.
    • Si confirma, evita falso negativo y refresca estado en caliente.

20) Arcadia Live: throttling HTTP cuando el WS está sano

  • services/pinnacleLight.js reduce polling HTTP live redundante cuando:
    • el websocket está abierto,
    • hubo frames recientes,
    • el último snapshot HTTP sigue fresco.
  • Se mantiene refresh HTTP periódico para consistencia y limpieza de mercados huérfanos.
  • Nuevos knobs opcionales:
    • PINNACLE_LIVE_HTTP_MAX_STALE_MS (default interno 20000)
    • PINNACLE_LIVE_WS_FRESH_WINDOW_MS (default interno 8000)

🚀 Características Principales

🧠 Motor Cuantitativo (Quant Core)

Simultaneous Kelly Criterion con Amortiguación Logarítmica

  • Implementación avanzada del criterio de Kelly que evita los "cortes arbitrarios" (Hard Caps).
  • Usa una función de saturación exponencial: Stake = Cap × (1 - e^(-Kelly/Cap)).
  • Permite que apuestas con ventaja masiva reciban más capital sin comprometer la seguridad del bankroll.
  • Risk of Ruin (ROR): < 0.5% mediante control asintótico.

Risk Profiles Dinámicos El sistema ajusta automáticamente la agresividad de las apuestas según la volatilidad inherente de cada estrategia:

Estrategia Fracción Kelly Volatilidad Caso de Uso
PREMATCH_VALUE 0.25 (1/4) Baja Cuotas pre-partido con datos históricos sólidos
LIVE_VALUE 0.125 (1/8) Media Arbitraje en vivo con ruido de mercado
LIVE_SNIPE 0.10 (1/10) Alta "La Volteada" - Eventos de alta incertidumbre

NAV-Based Staking (Net Asset Value)

  • Calcula el tamaño de posición sobre Balance Líquido + Exposición Activa.
  • Evita la "sub-inversión" sistemática que ocurre al tener múltiples apuestas simultáneas.
  • Ejemplo: Con $1000 en balance y $200 en apuestas activas, el sistema calcula sobre NAV = $1200.

🕵️ Scanners Especializados

1. Arcadia Gateway (Truth Source)

  • Conexión WebSocket de baja latencia con Pinnacle API.
  • Extracción de cuotas "Fair" (sin margen de casa) mediante eliminación de Vig.
  • Auto-renovación de sesión mediante Puppeteer cuando el token caduca.
  • Detección de datos congelados (Stale Data) y reinicio automático.

2. Pre-Match Scanner

  • Escaneo diario de eventos próximos (ventana de 48h).
  • Cruza cuotas de Pinnacle vs Altenar identificando oportunidades pre-partido.
  • Matcher inteligente con Fuzzy Logic + Levenshtein Distance para normalización de nombres.

3. Live Scanner ("The Sniper")

  • Escaneo de alta frecuencia con polling adaptativo (~2s a ~7s según actividad/errores).
  • Detección de dos tipos de oportunidades en tiempo real:
    • Value Bets Live: Discrepancias de cuotas en eventos en juego.
    • "La Volteada": Estrategia especializada (ver sección Estrategias).

4. Monitor Dashboard

  • Vista comparativa en tiempo real de cuotas Pinnacle vs Altenar.
  • Indicadores visuales de tendencia (flechas arriba/abajo) y pulsos de actualización.
  • Detección de eventos "desvinculados" (sin match Pinnacle).

🛡️ Sistemas de Seguridad

Zombie Protocol (Auto-Recovery)

  • Detecta eventos que desaparecen de los feeds en vivo (suspensiones, finalizaciones prematuras).
  • Consulta automática a la API de Resultados (GetEventResults) para liquidación precisa.
  • Previene apuestas "colgadas" en estado PENDING indefinidamente.

Duplicate Bet Prevention

  • Sistema de locks en memoria (processingBets Set) para evitar apuestas duplicadas.
  • Filtro de Blacklist persistente para eventos descartados manualmente.
  • Validación de stake mínimo (S/1.00) antes de registrar oportunidades.

Stale Data Detection

  • Compara tiempos de partido entre Pinnacle y Altenar.
  • Si la diferencia supera 3 minutos, gatilla reinicio automático del WebSocket.
  • Archivo trigger (pinnacle_stale.trigger) para comunicación inter-proceso.

🎨 Interfaz de Usuario (React + TailwindCSS)

Dashboard Multi-Pestaña

  1. Pre-Match: Lista de oportunidades futuras con cálculo de EV y Kelly.
  2. En Vivo: Oportunidades detectadas en tiempo real ("La Volteada" + Value Bets).
  3. Activas: Apuestas en curso con tracking de marcador y tiempo en vivo.
  4. Historial: Registro completo de apuestas liquidadas con P&L y estadísticas.
  5. Monitor: Comparador visual de cuotas Pinnacle vs Altenar (modo profesional).
  6. Matcher: Herramienta manual para vincular eventos no detectados automáticamente.

📊 Estrategias de Trading

1. Pre-Match Value Betting

Descripción: Detección de discrepancias entre cuotas pre-partido.

Flujo:

  1. Ingesta diaria de eventos próximos desde Pinnacle (cuotas "Fair").
  2. Normalización de nombres de equipos y ligas (Fuzzy Matching).
  3. Comparación con cuotas de Altenar en el mismo evento.
  4. Identificación de valor cuando: Prob_Real × Cuota_Altenar > 1.

Ventajas:

  • Datos estables (no volátiles).
  • Mayor tiempo para análisis manual.
  • Menor riesgo de cambios bruscos.

Riesgo: Bajo (0.25 Kelly).


2. Live Value Betting

Descripción: Arbitraje algorítmico en eventos en curso.

Flujo:

  1. Escaneo continuo de partidos en vivo con polling adaptativo (~2s a ~7s según actividad y errores).
  2. Comparación de cuotas actualizadas en tiempo real.
  3. Detección de valor positivo mediante Fair Odds de Pinnacle Live.
  4. Ejecución si Kelly sugiere stake ≥ S/1.00.

Ventajas:

  • Oportunidades frecuentes.
  • Cuotas más volátiles = mayor margen.

Riesgo: Medio (0.125 Kelly).


3. "La Volteada" (Live Snipe Strategy)

Descripción: Estrategia propietaria que detecta remontadas potenciales.

Condiciones de Entrada:

  1. Perfil del Evento: Favorito Pre-Match (Probabilidad Real > 55%).
  2. Estado del Partido: Favorito va perdiendo por exactamente 1 gol.
  3. Ventana Temporal: Minuto 15 - 80 del partido.
  4. Validación de Dominancia:
    • Sin expulsiones (Red Cards = 0).
    • Estadísticas de dominio (Posesión, Tiros) favorables al favorito (opcional).

Lógica Matemática:

  • Recalcula probabilidad de remontada usando cuotas Pinnacle Live.
  • Aplica Kelly ultra-conservador (0.10) por alta volatilidad.
  • Busca cuota de Altenar inflada (típicamente > 2.5x para el favorito).

Ejemplo Real:

Tigres UANL (Favorito Pre-Match: ~70%) vs Pumas
Score Actual: 0-1 (Tigres perdiendo) - Minuto 35'
Cuota Pinnacle Live (Tigres): 1.50 → Prob Real: ~60%
Cuota Altenar (Tigres): 2.20 → EV = 32%
Kelly (0.10): Stake sugerido = $8 (NAV = $1200)

Ventajas:

  • Aprovecha pánico de mercado (Altenar sobrevalora al underdog).
  • Alta frecuencia en ligas volátiles.

Riesgo: Alto (0.10 Kelly). Requiere liquidación rápida.


4. Next Goal Value (Totales)

Descripción: Detección de presión ofensiva para mercados Over/Under.

Condiciones:

  1. Equipo dominante con > 60% posesión.
  2. Diferencia de tiros a puerta > 3.
  3. Minuto > 60'.

Objetivo: Apostar a "Over 2.5" o "Over 3.5" cuando el partido está "caliente".

Estado: Experimental (requiere calibración).


� Gestión Financiera (Portfolio Theory)

Kelly Criterion: La Matemática Detrás

El criterio de Kelly determina la fracción óptima del bankroll a arriesgar en función de la ventaja estadística:

$$f^* = \frac{bp - q}{b}$$

Donde:

  • p = Probabilidad Real de ganar (Pinnacle Fair Odds)
  • q = Probabilidad de perder (1 - p)
  • b = Ganancia neta por unidad apostada (Cuota - 1)

Problema del Kelly Puro: En su forma original, Kelly puede sugerir apuestas muy grandes (10-20% del bankroll) en situaciones de alta ventaja, exponiendo al trader a alta volatilidad.

Mejoras Implementadas

1. Fractional Kelly

  • Multiplicador conservador sobre el Kelly puro.
  • Reduce volatilidad a cambio de menor tasa de crecimiento.
  • BetSniper usa fracciones adaptativas según volatilidad del mercado.

2. Logarithmic Dampening (Amortiguación) En lugar de cortar arbitrariamente las apuestas grandes, aplicamos:

$$Stake_{Real} = Cap \times (1 - e^{-\frac{Stake_{Kelly}}{Cap}})$$

Efecto:

  • Apuestas pequeñas (< 2%): Crecimiento casi lineal (no penalizadas).
  • Apuestas grandes (> 5%): Crecimiento asintótico hacia el Cap (3.5%).
  • Resultado: Aprovechas ventajas masivas sin arriesgar la ruina.

Gráfica Conceptual:

Stake Real (%)
    │
3.5%├─────────────────────── (Asíntota)
    │                  ╱─
    │               ╱─
2.0%│           ╱─
    │       ╱─
1.0%│   ╱─
    │╱──────────────────────→ Kelly Crudo (%)
    0   2   4   6   8  10

NAV (Net Asset Value)

Definición: Patrimonio Total = Balance Disponible + Stakes en Apuestas Activas.

¿Por qué usarlo?

  • Escenario: Tienes $1000 de balance y 5 apuestas activas de $50 cada una ($250 en juego).
  • Error Común: Calcular Kelly sobre $1000 → Sub-inversión en nuevas oportunidades.
  • Solución NAV: Calcular Kelly sobre $1250 (NAV) → Apuestas proporcionales al patrimonio real.

Implementación:

const currentNAV = portfolio.balance + 
                   portfolio.activeBets.reduce((sum, b) => sum + b.stake, 0);
const kellyStake = calculateKellyStake(realProb, odd, currentNAV, strategy);

Control de Riesgo

Validaciones Pre-Ejecución:

  1. Stake Mínimo: S/1.00 (Evita micro-apuestas poco prácticas).
  2. Liquidez: No apostar más del balance disponible (incluso si NAV lo sugiere).
  3. Duplicate Check: Verificar que no existe apuesta activa en el mismo evento.
  4. Blacklist: Filtrar eventos descartados manualmente.

Liquidación Automática:

  • Pre-Match: Buffer de 2.2 horas post-inicio antes de verificar resultados.
  • Live: Liquidación inmediata si Tiempo >= 90' o evento desaparece del feed.
  • Zombie Bets: Consulta a API de Resultados si GetEventDetails falla.

🛠️ Arquitectura del Sistema

Diagrama de Componentes

┌─────────────────────────────────────────────────────────────┐
│                    FRONTEND (React)                         │
│  ┌──────────┬───────────┬──────────┬──────────┬──────────┐ │
│  │ Pre-Match│ En Vivo   │ Activas  │Historial │ Monitor  │ │
│  └────┬─────┴─────┬─────┴─────┬────┴────┬─────┴────┬─────┘ │
│       │           │           │         │          │        │
└───────┼───────────┼───────────┼─────────┼──────────┼───────┘
        │           │           │         │          │
        └───────────┴───────────┴─────────┴──────────┘
                           ▼
        ┌───────────────────────────────────────────────────┐
        │         EXPRESS API (server.js)                   │
        │  ┌─────────────────────────────────────────────┐  │
        │  │   Background Scanner (Bucle Infinito)       │  │
        │  │   - Pre-Match Scan (cada 2 min)             │  │
        │  │   - Live Scan (polling adaptativo)          │  │
        │  │   - Active Bets Monitoring                  │  │
        │  └─────────────────────────────────────────────┘  │
        │                                                    │
        │  ┌─────────────────────────────────────────────┐  │
        │  │   API Routes                                 │  │
        │  │   /api/opportunities                         │  │
        │  │   /api/portfolio                             │  │
        │  │   /api/monitor                               │  │
        │  │   /api/matcher                               │  │
        │  │   /api/booky                                 │  │
        │  └─────────────────────────────────────────────┘  │
        └──────────────┬──────────────────┬────────────────┘
                       │                  │
        ┌──────────────▼─────┐   ┌────────▼──────────────┐
        │  LowDB (db.json)   │   │  Axios Clients        │
        │  - Matches         │   │  - Altenar API        │
        │  - Portfolio       │   │  - Pinnacle (REST)    │
        │  - Blacklist       │   └───────────────────────┘
        └────────────────────┘
                                    ┌───────────────────────┐
                                    │ services/             │
                                    │ pinnacleLight.js      │
                                    │ (Proceso Separado)    │
                                    │                       │
                                    │ ┌──────────────────┐  │
                                    │ │ Puppeteer        │  │
                                    │ │ (Chrome Headless)│  │
                                    │ └────────┬─────────┘  │
                                    │          │            │
                                    │          ▼            │
                                    │   WebSocket Client   │
                                    │   (wss://arcadia)    │
                                    │          │            │
                                    │          ▼            │
                                    │ pinnacle_live.json   │
                                    └──────────────────────┘

Flujo de Datos (Live Trading)

  1. Ingesta (Terminal 2 - pinnacleLight.js):

    • Puppeteer navega a Pinnacle y extrae headers de autenticación.
    • WebSocket abierto en wss://api.arcadia.pinnacle.com/ws.
    • Frames recibidos cada ~500ms, parseados y escritos en data/pinnacle_live.json.
  2. Procesamiento (Terminal 1 - server.js):

    • Background Scanner lee pinnacle_live.json con polling adaptativo (~2s a ~7s).
    • Consulta eventos en vivo desde Altenar (GetLivenow).
    • Matcher fuzzy para vincular eventos Pinnacle ↔ Altenar.
    • Evalúa condiciones de estrategias (Value, Volteada, Next Goal).
    • Calcula Kelly y registra oportunidades en caché.
  3. Presentación (Terminal 3 - client/):

    • Frontend consulta /api/opportunities cada 5s.
    • Renderiza oportunidades en pestaña "En Vivo".
    • Usuario puede ejecutar apuesta manual (botón "APOSTAR").
  4. Ejecución (Paper Trading):

    • placeAutoBet() registra apuesta en db.json.
    • Descuenta stake del balance.
    • Añade a lista activeBets.
  5. Monitoreo:

    • En cada ciclo del scanner, updateActiveBetsWithLiveData() verifica:
      • Si el evento sigue en vivo (actualiza score/tiempo).
      • Si finalizó (consulta GetEventDetails o GetEventResults).
    • Liquida apuesta si hay resultado oficial.

🔧 Guía de Configuración Pre-Operativa

Antes de instalar el sistema necesitas tener cuentas activas en los servicios externos que BetSniper consume. Esta sección detalla qué cuentas abrir, qué datos extraer de cada una y cómo volcarlas al .env.


Paso 1: Servicios a los que debes suscribirte

1A. Pinnacle (obligatorio — fuente de probabilidades reales)

Campo Detalle
Web pinnacle.com
Tipo Sharp Bookie — márgenes muy bajos, acepta ganadores
Qué necesitas Cuenta activa con acceso a la sección "Sports" (Live Soccer visible)
Restricciones No disponible en todos los países. Usar VPN si es necesario (recomendado: servidor NL o MT).
Uso en BetSniper Solo como fuente de cuotas. No se colocan apuestas en Pinnacle.
Coste Gratuito (solo necesitas la cuenta para acceder a la API de cuotas en tiempo real)

Por qué Pinnacle: Su API pública (api.arcadia.pinnacle.com) expone cuotas con márgenes de 2-3%, lo que las convierte en el mejor proxy de probabilidad real del mercado.

1B. DoradoBet (obligatorio para modo live — bookie objetivo principal)

Campo Detalle
Web doradobet.com
Plataforma Altenar (mismo backend que ACity)
Qué necesitas Cuenta registrada con saldo real
Perfil en .env BOOK_PROFILE=doradobet
Uso en BetSniper Detección de value bets + colocación real (si habilitas BOOKY_REAL_PLACEMENT_ENABLED)
Restricciones Disponible principalmente en Perú. Si operas desde otro país, verificar disponibilidad.

1C. Casino Atlantic City — ACity (alternativo — mismo motor Altenar)

Campo Detalle
Web casinoatlanticcity.com/apuestas-deportivas
Plataforma Altenar (misma API, distinto integration y origin)
Qué necesitas Cuenta registrada con saldo real
Perfil en .env BOOK_PROFILE=acity
Uso en BetSniper Alternativa a DoradoBet. Puedes operar en ambas en sesiones separadas.

Nota: DoradoBet y ACity usan el mismo motor Altenar pero con integraciones distintas. BetSniper soporta cambiar entre ellas con un solo comando (npm run book:dorado / npm run book:acity) sin reiniciar el servidor.


Paso 2: Configuración detallada del .env

Copia el archivo de plantilla:

cp .env.example .env

Luego edita .env siguiendo esta guía variable por variable:


🔧 Variables de Sistema

NODE_ENV=development

Usa development siempre. Solo cambiar a production si despliegas en servidor remoto.

PORT=3000

Puerto del backend. Si tienes conflicto con otro proceso, cámbialo (ej. 3001).

TZ=America/Lima

Timezone del proceso. Importante: afecta el horario mostrado en el dashboard y los filtros de ventana prematch nocturna. Ajústar según tu país si no estás en Perú.

DISABLE_BACKGROUND_WORKERS=false
DISABLE_LIVE_SCANNER=false
DISABLE_PREMATCH_SCHEDULER=false
DISABLE_PINNACLE_INGEST_CRON=false
PINNACLE_INGEST_CRON_INTERVAL_MS=7200000
DISABLE_PINNACLE_PREMATCH_WS=false
DISABLE_MONITOR_DASHBOARD=false

# Live tuning
LIVE_VALUE_MIN_EV=0.02
LIVE_VALUE_MIN_DISPLAY_STAKE=0.10
LIVE_VALUE_NON_1X2_STAKE_FACTOR=1
LIVE_SNIPE_REQUIRE_PINNACLE_LIVE=true
LIVE_VALUE_REQUIRE_SCORE_SYNC=true
LIVE_VALUE_SCORE_SYNC_MAX_GOAL_DIFF=0
LIVE_VALUE_ENABLE_STABILITY_FILTER=true
LIVE_VALUE_STABILITY_MIN_HITS=2
LIVE_VALUE_STABILITY_MIN_AGE_MS=4000
LIVE_GLOBAL_STABILITY_ENABLED=true
LIVE_GLOBAL_STABILITY_MIN_HITS=2

# ACity polling-first (default recomendado)
ACITY_SOCKET_ENABLED=false
ACITY_SOCKET_URL=wss://api.casinoatlanticcity.com/api/notifications/?EIO=4&transport=websocket
ACITY_SOCKET_COMPANY=ACP
ACITY_SOCKET_LOGIN_EVENT=server
ACITY_SOCKET_LOGIN_TYPE=login
ACITY_SOCKET_SESSION=
ACITY_HAYWIRE_SUBSCRIBE_ENABLED=false
ACITY_HAYWIRE_TOPICS_EVENT=haywire/topics
ACITY_HAYWIRE_SPORT_ID=66
ACITY_HAYWIRE_LIVE_MODE=live_delay
ACITY_HAYWIRE_PAYLOAD_MODES=type,action,bare
ACITY_HAYWIRE_TOPICS=
ACITY_HAYWIRE_MQTT_ENABLED=false
ACITY_HAYWIRE_MQTT_URLS=
ACITY_HAYWIRE_MQTT_USERNAME=
ACITY_HAYWIRE_MQTT_PASSWORD_MODE=auto
ACITY_HAYWIRE_MQTT_API_KEY=
ACITY_HAYWIRE_MQTT_CONNECT_TIMEOUT_MS=12000
ACITY_HAYWIRE_MQTT_MAX_ATTEMPTS=48
LIVE_SOCKET_REQUOTE_ENABLED=false
LIVE_SOCKET_REQUOTE_MAX_PER_CYCLE=8
LIVE_SOCKET_DIRTY_MAX_AGE_MS=90000
LIVE_SOCKET_REQUOTE_ALLOWED_FAMILIES=match_result,totals,double_chance
PREMATCH_SOCKET_REFRESH_ENABLED=false
PREMATCH_SOCKET_REFRESH_MAX_PER_CYCLE=3
LIVE_HYBRID_SELECTIVE_ENABLED=false
LIVE_HYBRID_REQUIRE_WSAPI_DISABLED=true
LIVE_HYBRID_SOCKET_COLD_MIN_RAW_MESSAGES=12
LIVE_HYBRID_FULL_SCAN_EVERY_N_CYCLES=6
LIVE_HYBRID_SELECTIVE_MAX_PER_CYCLE=8
LIVE_HYBRID_SELECTIVE_ALLOWED_FAMILIES=match_result,totals,double_chance

# Recalculo prematch en caliente al apostar
PREMATCH_REFRESH_RECALCULATE_PINNACLE=true
PREMATCH_PINNACLE_CACHE_TTL_MS=15000

# Ventana híbrida de descarga prematch (deslizante + precarga)
PREMATCH_WINDOW_PRIMARY_HOURS=6
PREMATCH_WINDOW_PREFETCH_HOURS=6
PREMATCH_WINDOW_OVERLAP_MINUTES=30

# Prematch WS Arcadia (MQTT)
PINNACLE_PREMATCH_WS_DISCOVERY_INTERVAL_MS=600000
PINNACLE_PREMATCH_WS_SAVE_INTERVAL_MS=15000
PINNACLE_PREMATCH_WS_HEALTH_CHECK_INTERVAL_MS=12000
PINNACLE_PREMATCH_WS_STALE_MS=70000
PINNACLE_PREMATCH_WS_FALLBACK_POLL_INTERVAL_MS=45000
PINNACLE_PREMATCH_WS_SAFETY_POLL_INTERVAL_MS=360000
PINNACLE_PREMATCH_WS_LEAGUE_LIMIT=120
PINNACLE_PREMATCH_WS_INCLUDE_REG_TOPICS=true
PINNACLE_PREMATCH_WS_INCLUDE_SPC_TOPICS=true

Pon true solo para depurar el servidor Express sin que los scanners consuman CPU.

LIVE_SNIPE_REQUIRE_PINNACLE_LIVE=true mantiene modo estricto (solo entra si hay cuota live PIN). Si estás en día de feed incompleto, puedes bajarlo temporalmente a false.

LIVE_VALUE_REQUIRE_SCORE_SYNC=true + LIVE_VALUE_SCORE_SYNC_MAX_GOAL_DIFF=0 exige marcador idéntico Altenar/Pinnacle. Para no quedarte sin señales, usa LIVE_VALUE_SCORE_SYNC_MAX_GOAL_DIFF=1 o desactiva la guard con LIVE_VALUE_REQUIRE_SCORE_SYNC=false.

Recomendación operativa actual para ACity: usar polling-first (socket/haywire en false) hasta validar deltas reales en tu tenant.

Para ACITY_SOCKET_SESSION: puedes dejarlo vacío y ejecutar npm run analyze:acity:live; el backend intentará extraer la sesión del archivo data/booky/acity-live-socket-analysis.latest.json.

Para haywire/topics: tras authOk se envían suscripciones de odds automáticamente. Puedes ajustar evento, formatos de payload y topics con ACITY_HAYWIRE_*.

La vía ACITY_HAYWIRE_MQTT_* abre una segunda conexión directa MQTT (haywire) para recibir deltas de cuotas sin depender solo de notifications. Si no defines ACITY_HAYWIRE_MQTT_URLS, el servicio deriva hosts desde capturas runtime (requests + storage) y prueba múltiples rutas MQTT.

Recomendación para forcing en producción: define ACITY_HAYWIRE_MQTT_URLS con host real de notificaciones observado en runtime (ej. api-comunicaciones-web.acity.com.pe) y ACITY_HAYWIRE_MQTT_USERNAME con el user detectado (/api/notificaciones/{user}).

Si wsapiSocketsEnabled=[] (tenant sin sockets wsapi), activa LIVE_HYBRID_SELECTIVE_*: el scanner alterna full scan cada N ciclos y, entre medias, hace requote selectivo de 1x2/totales/DC para reducir polling masivo sin perder latencia.

PREMATCH_REFRESH_RECALCULATE_PINNACLE=true fuerza recálculo de realProb prematch justo antes de confirmar/apostar, consultando el feed de Pinnacle. El frontend mostrará el delta instantáneo de cuota/EV/stake/probabilidad en el modal de confirmación.

Ventana híbrida recomendada para ingestas prematch: PREMATCH_WINDOW_PRIMARY_HOURS=6 + PREMATCH_WINDOW_PREFETCH_HOURS=6 con PREMATCH_WINDOW_OVERLAP_MINUTES=30. En la práctica descarga una ventana deslizante now-30m -> now+12h, priorizando el próximo bloque sin quedarte ciego en cambios de hora/latencia.


🎯 Variables de Perfil Altenar (Bookie objetivo)

Estas variables se auto-escriben con npm run book:dorado o npm run book:acity. Pero si prefieres editarlas manualmente:

Para DoradoBet:

BOOK_PROFILE=doradobet
ALTENAR_INTEGRATION=doradobet
ALTENAR_ORIGIN=https://doradobet.com
ALTENAR_REFERER=https://doradobet.com/deportes-en-vivo

Para ACity:

BOOK_PROFILE=acity
ALTENAR_INTEGRATION=casinoatlanticcity
ALTENAR_ORIGIN=https://www.casinoatlanticcity.com
ALTENAR_REFERER=https://www.casinoatlanticcity.com/apuestas-deportivas

Comunes a ambos (no cambiar salvo que el bookie cambie de país):

ALTENAR_COUNTRY_CODE=PE        # Código ISO del país de la cuenta
ALTENAR_CULTURE=es-ES           # Idioma de la API (no cambiar)
ALTENAR_TIMEZONE_OFFSET=300     # UTC-5 (Perú). GMT-4=240, GMT-6=360
ALTENAR_NUM_FORMAT=en-GB        # VITAL: garantiza decimales con punto (1.50 no 1,50)
ALTENAR_DEVICE_TYPE=1           # 1=Desktop. No cambiar.
ALTENAR_SPORT_ID=0              # 0=todos los deportes. 66=solo fútbol.

ALTENAR_NUM_FORMAT=en-GB es crítico. Si usas es-ES, las cuotas llegan como 1,50 y el parser falla silenciosamente.


🔐 Variables de Autenticación Booky (Para apuestas reales)

Esto es necesario solo si quieres ejecutar apuestas reales. En modo Paper Trading puedes omitir esta sección.

Paso 1 — URL de acceso al bookie:

# DoradoBet:
ALTENAR_BOOKY_URL=https://doradobet.com/deportes-en-vivo

# ACity:
ALTENAR_BOOKY_URL=https://www.casinoatlanticcity.com/apuestas-deportivas#/overview

Esta es la URL a la que Puppeteer navega para iniciar sesión y capturar el JWT.

Paso 2 — Credenciales de tu cuenta:

ALTENAR_LOGIN_USERNAME=tu_email_o_usuario
ALTENAR_LOGIN_PASSWORD=tu_contraseña

Se usan para el login automático con Puppeteer. Se leen en tiempo de ejecución del script, nunca se loggean.

Paso 3 — Capturar el JWT real:

Con las credenciales en .env, ejecuta:

# Abre Chrome, inicia sesión automáticamente y espera a que cierres la ventana
npm run token:booky:wait-close

# Alternativa: headless con timeout de 90 segundos
npm run token:booky:timeout

El script escribe automáticamente en tu .env:

ALTENAR_BOOKY_AUTH_TOKEN=Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXV...

El JWT caduca en ~24-72h dependiendo del bookie. Necesitarás renovarlo periódicamente con el mismo comando. Si BOOKY_AUTO_TOKEN_REFRESH_ENABLED=true, el sistema lo notifica automáticamente cuando queden menos de BOOKY_TOKEN_MIN_REMAINING_MINUTES minutos.


🛡️ Variables de Seguridad — Guardas de Placement Real

BOOKY_REAL_PLACEMENT_ENABLED=false

Esta variable es el interruptor principal. Mientras sea false, el sistema NUNCA envía apuestas reales aunque el resto esté configurado. Cámbiala a true solo cuando hayas validado todo el flujo con dry-run.

BOOKY_TOKEN_MIN_REMAINING_MINUTES=2

Si el JWT tiene menos de X minutos de vida, el sistema rechaza el placement y notifica para renovar. Recomendado: 5 para mayor margen.

BOOKY_MIN_EV_PERCENT=2

EV mínimo para permitir placement real. Con 2, solo apuesta si la ventaja esperada es ≥ 2%. Sube a 5 si quieres filtrar solo oportunidades de alta calidad.

BOOKY_MAX_ODD_DROP=0.20

Drop máximo de cuota desde el snapshot del ticket. Si la cuota bajó más de 20% entre cuando detectaste la oportunidad y cuando intentas apostar, el placement se rechaza. Recomendado: 0.15 para mercados volátiles.

BOOKY_AUTO_TOKEN_REFRESH_ENABLED=true

Activa el sistema de alertas automáticas cuando el token está por vencer.

BOOKY_KEEP_REAL_PLACEMENT_ON_TOKEN_REFRESH=false

Si false (recomendado): el sistema desactiva placements reales al detectar token vencido hasta renovación manual. Si true: intenta renovar y retomar solo (solo si BOOKY_AUTO_TOKEN_REFRESH_ENABLED=true).


🧮 Variables del Matcher Pinnacle ↔ Altenar

MATCH_DIAGNOSTIC_LOG=1

Activa logs de diagnóstico del matcher. 0=off (producción silenciosa), 1=resumen por ciclo (recomendado para puesta en marcha), 2=verbose por cada evento.

MATCH_FUZZY_THRESHOLD=0.77

Similitud mínima de Levenshtein entre nombres de equipo para considerar que son el mismo. Rango: 0.0–1.0.

  • 0.90+ → muy estricto, pocos matches, casi sin falsos positivos.
  • 0.70–0.80 → equilibrado (recomendado).
  • <0.65 → permisivo, más matches pero riesgo de falsos positivos.
MATCH_MIN_ACCEPT_SCORE=0.60

Score compuesto mínimo para dar por válido un match (combina similitud de nombre + ventana temporal + contexto de liga). Bajar a 0.50 si hay muchos eventos sin match en ligas menores.

MATCH_TIME_TOLERANCE_MINUTES=5

Ventana temporal primaria (en minutos) para buscar candidatos. Si Pinnacle dice que el partido empieza a las 15:00 y Altenar dice 15:04, con tolerancia de 5 se linked correctamente.

  • Subir a 10 si el diagnóstico muestra time_window_5m como razón dominante de no-match.
  • Bajar a 3 solo si tienes muchos falsos positivos por partidos cercanos en horario.
MATCH_TIME_EXTENDED_TOLERANCE_MINUTES=30

Ventana secundaria para candidatos con alta similitud de nombre. Permite matchear eventos con diferencia de horario maior (ajustes de timezone inesperados). No bajar de 20.


🧹 Variables de Housekeeping (Opcionales)

BOOKY_BALANCE_REFRESH_MS=45000

Cada cuántos milisegundos se refresca el balance real del bookie. 45000 = 45 segundos.

BOOKY_HISTORY_REFRESH_MS=60000

Frecuencia de sincronización del historial remoto de apuestas.

BOOKY_HISTORY_RETENTION_DAYS=30

Cuántos días de historial conservar en db.json. Apuestas más antiguas se purgan automáticamente.

BOOKY_PROFILE_HISTORY_MAX_ITEMS=500

Límite de entradas de historial por perfil en DB. Evita que db.json crezca indefinidamente.

BOOKY_ORPHAN_ACTIVE_GRACE_MS=120000

Ventana de gracia (ms) antes de considerar huérfana una apuesta activa sin providerBetId. Evita falsos positivos por latencia de sincronización.

BOOKY_ORPHAN_ACTIVE_HARD_MAX_MS=1200000

Límite duro (ms) para forzar saneo de activas huérfanas aunque no haya señal fuerte de error en el ticket.


Paso 3: Resumen — ¿qué es obligatorio vs opcional?

Variable / Paso Paper Trading Live Apuestas Reales
Cuenta Pinnacle activa
Cuenta DoradoBet o ACity con saldo
BOOK_PROFILE + ALTENAR_*
ALTENAR_LOGIN_USERNAME + PASSWORD
ALTENAR_BOOKY_AUTH_TOKEN
BOOKY_REAL_PLACEMENT_ENABLED=true
Guards (BOOKY_MIN_EV_PERCENT, etc.) ✅ Recomendado
MATCH_* (matcher tuning) Opcional Opcional

Recomendación para nuevos usuarios: arranca con Paper Trading (sin credenciales de bookie) durante al menos 3-5 días para validar que el matcher detecta correctamente los eventos en tu región antes de habilitar apuestas reales.


📦 Instalación y Despliegue

Requisitos Previos

  • Node.js: v18.0.0 o superior
  • npm: v8.0.0 o superior
  • Sistema Operativo: Windows, macOS o Linux
  • Chromium: Instalado automáticamente por Puppeteer (primer arranque)
  • Memoria RAM: Mínimo 4GB recomendado (2GB para Node.js + 2GB para Chromium)

Instalación Rápida

# 1. Clonar repositorio
git clone https://github.com/tu-usuario/betsniper-v3.git
cd betsniper-v3

# 2. Instalar dependencias del Backend
npm install

# 3. Instalar dependencias del Frontend
cd client
npm install
cd ..

# 4. Configurar variables de entorno (opcional)
cp .env.example .env
# Editar .env si necesitas customizar puertos o configuraciones

Estructura de Archivos Generados

El sistema creará automáticamente estos directorios y archivos en el primer arranque:

data/
├── pinnacle_live.json      # Feed en tiempo real de Pinnacle
├── pinnacle_token.json    # Headers de autenticación (auto-renovado)
└── pinnacle_stale.trigger # Flag para reinicio de socket (auto-generado)

db.json                     # Base de datos local (creada por LowDB)

Modo de Ejecución: Arquitectura de 3 Terminales

Para operación completa, ejecuta estos comandos en paralelo (3 terminales diferentes):

Terminal 1: Servidor Backend (Obligatorio)

Levanta la API REST, la base de datos y el scanner de fondo.

npm run dev

¿Qué hace?

  • Expone API en http://localhost:3000
  • Ejecuta ingesta automática de Pinnacle/Altenar en intervalo configurable (PINNACLE_INGEST_CRON_INTERVAL_MS, default 2h)
  • Scanner de oportunidades Live en bucle (polling adaptativo según actividad)
  • Monitoreo de apuestas activas y liquidación automática

Logs Esperados:

🚀 Servidor BetSniper V3 corriendo en http://localhost:3000
📝 Modo: development
🔄 Background Scanner Iniciado (Modo Seguro Anti-Ban) + AUTO-TRADING ACTIVO
⏰ [CRON] Ejecutando Ingesta Automática de Pinnacle...

Terminal 2: Ingesta Pinnacle (Obligatorio para Live)

Mantiene la conexión WebSocket con Pinnacle y guarda cuotas en tiempo real.

node services/pinnacleLight.js

Primer Arranque (Autenticación):

  • Si no existe data/pinnacle_token.json, el script abrirá una ventana de Chrome automáticamente.
  • Acción Requerida: Inicia sesión manualmente en Pinnacle en esa ventana.
  • Una vez que navegues a la sección "Live Soccer", el script capturará los headers automáticamente.
  • Cierra la ventana de Chrome cuando veas el mensaje 💾 Token actualizado en disco.
  • El script continuará solo con el WebSocket.

Renovación Automática:

  • Si el token expira (cada ~1 hora), el script detecta y abre Chrome nuevamente.
  • Repite el proceso de login manual.

Logs Esperados:

🚀 Starting Pinnacle Auth Scraper (Direct WS)...
✅ Headers cargados y válidos (Generados: 14:32:15).
🔌 Conectando al WebSocket...
✅ WebSocket Conectado! (Esperando frames...)
📡 FRAME: Straight - Updates: 12
💾 Datos guardados en disco (6 eventos).

Terminal 3: Frontend (Obligatorio para UI)

Levanta la interfaz React en modo desarrollo.

cd client
npm run dev

URL: http://localhost:5173

Logs Esperados:

VITE v5.0.0  ready in 324 ms

➜  Local:   http://localhost:5173/
➜  Network: use --host to expose
➜  press h + enter to show help

Herramientas Opcionales (Debugging)

Flujo Booky (perfil, token, captura, smoke)

# 1) Seleccionar perfil operativo
npm run book:acity
# o
npm run book:dorado

# 2) Capturar token auth real (abre Chrome)
npm run token:booky:wait-close

# 3) Capturar payloads placeWidget/betslip
npm run capture:booky

# 4) Verificar que existe captura latest (OBLIGATORIO en clone nuevo/dispositivo nuevo)
# PowerShell / Bash (Git Bash)
curl -sS --compressed http://localhost:3000/api/booky/capture/latest
# Debe devolver: {"success":true,"found":true,...}

# 4.1) Health-check rápido de placeWidget (sin ruido)
# Opcion A (si tienes jq):
curl -sS --compressed http://localhost:3000/api/booky/token-health | jq.exe '{success,authenticated:.token.authenticated,jwtValid:.token.jwtValid,expired:.token.expired,remainingMinutes:.token.remainingMinutes,expIso:.token.expIso}'
curl -sS --compressed "http://localhost:3000/api/booky/account?refresh=1" | jq.exe '{success,balance:.balance.amount,currency:.balance.currency,stale:.balance.stale,updatedAt:.balance.updatedAt,source:.balance.source}'
curl -sS --compressed http://localhost:3000/api/booky/capture/latest | jq.exe '{success,found,profile,generatedAt,totalCaptured}'

# Opcion B (si jq no esta disponible):
curl -sS --compressed http://localhost:3000/api/booky/token-health | python -c "import sys,json; d=json.load(sys.stdin); t=d.get('token') or {}; print({'success':d.get('success'),'authenticated':t.get('authenticated'),'jwtValid':t.get('jwtValid'),'expired':t.get('expired'),'remainingMinutes':t.get('remainingMinutes'),'expIso':t.get('expIso')})"
curl -sS --compressed "http://localhost:3000/api/booky/account?refresh=1" | python -c "import sys,json; d=json.load(sys.stdin); b=d.get('balance') or {}; print({'success':d.get('success'),'amount':b.get('amount'),'currency':b.get('currency'),'stale':b.get('stale'),'updatedAt':b.get('updatedAt'),'source':b.get('source')})"
curl -sS --compressed http://localhost:3000/api/booky/capture/latest | python -c "import sys,json; d=json.load(sys.stdin); print({'success':d.get('success'),'found':d.get('found'),'profile':d.get('profile'),'generatedAt':d.get('generatedAt'),'totalCaptured':d.get('totalCaptured')})"

# Criterio de salud minima para placeWidget:
# - token: authenticated=true, jwtValid=true, expired=false, remainingMinutes > 2
# - wallet: success=true, stale=false, updatedAt reciente
# - captura: found=true, totalCaptured > 0 y generatedAt reciente
# - validacion de payload: POST /api/booky/real/dryrun/:id debe responder success=true

# 5) Ver salud de token y flujo seguro (sin apostar real)
npm run smoke:booky

# 6) Saneo manual de activas huérfanas (si UI muestra EN JUEGO fantasma)
npm run cleanup:booky:orphans

Importante: data/ está en .gitignore, por lo que capture-*.latest.json no viaja con Git. En cada instalación nueva debes ejecutar la captura al menos una vez.

Para pruebas con envío real controlado (solo si habilitas BOOKY_REAL_PLACEMENT_ENABLED=true):

npm run smoke:booky:live

Recomendación: mantener BOOKY_REAL_PLACEMENT_ENABLED=false en desarrollo normal.

Checklist rápido: activar real en 20s

  1. Confirma token vigente: GET /api/booky/token-health (sin expired, con minutos restantes > 2).
  2. Confirma captura lista: GET /api/booky/capture/latest con found: true.
  3. Ejecuta dry-run de la oportunidad antes de enviar real: POST /api/booky/real/dryrun/:id.
  4. Recién ahí habilita BOOKY_REAL_PLACEMENT_ENABLED=true.
  5. Mantén BOOKY_KEEP_REAL_PLACEMENT_ON_TOKEN_REFRESH=false para operación conservadora.
  6. Al terminar sesión, vuelve a BOOKY_REAL_PLACEMENT_ENABLED=false.

Opciones útiles del script de saneo:

# Solo salida JSON
npm run cleanup:booky:orphans -- --json

# Limpiar para un perfil concreto
npm run cleanup:booky:orphans -- --profile=acity

# Usar cache remoto (sin refresh forzado)
npm run cleanup:booky:orphans -- --refresh=false

Scanner Manual (Modo Observador)

Si quieres ver logs detallados de cada oportunidad detectada en tiempo real sin interferir con el servidor:

node scripts/scan_live.js --dry-run

Nota Importante: El flag --dry-run es obligatorio si el servidor ya está corriendo. De lo contrario, ambos procesos intentarían registrar apuestas simultáneamente (riesgo de duplicados).

Salida:

🟢 INICIANDO LIVE SNIPER (Intervalo: 60s) [MODO: OBSERVADOR (Dry Run)]...
   🛡️  Dry Run: No se ejecutarán apuestas, solo detección.
   🎯 Pinnacle Live Found: Home=1.155, Away=11.83 -> RealProb(away)=7.7%

🔥 OPORTUNIDADES EN VIVO DETECTADAS 🔥
┌─────┬──────────────────────┬───────┬──────┬──────────┬─────────┬─────┬──────────┬───────┐
│Match│ CS Constantine (F)   │ Score │ Time │ Strategy │ Real %  │ Odd │ Kelly $  │  EV   │
├─────┼──────────────────────┼───────┼──────┼──────────┼─────────┼─────┼──────────┼───────┤
│ ... │ Afak Relizane (F)    │ 1-0   │ 62'  │ LIVE_VAL │ 7.7%    │37.0 │ $12.30   │185.9% │
└─────┴──────────────────────┴───────┴──────┴──────────┴─────────┴─────┴──────────┴───────┘

Ingesta Manual de Datos

Si quieres forzar una actualización de la base de datos pre-match sin esperar al cron automático:

# Actualizar eventos de Altenar (DoradoBet)
node scripts/ingest-altenar.js

# Actualizar eventos de Pinnacle
node scripts/ingest-pinnacle.js

Uso: Ejecutar una vez al día o antes de sesiones de trading pre-match.


🖥️ Interfaz de Usuario (Dashboard)

Vista General

El dashboard está diseñado para traders profesionales, con 6 pestañas especializadas:

┌─────────────────────────────────────────────────────────────────┐
│ 🎯 BetSniper V3         |Balance: S/1,234.56| ROI: +12.3%|      │
├─────────────────────────────────────────────────────────────────┤
│ [Pre-Match] [En Vivo] [Activas] [Historial] [Monitor] [Matcher]│
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  [Contenido dinámico según pestaña seleccionada]               │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

1. Pre-Match (Oportunidades Futuras)

Propósito: Detectar value bets en eventos que aún no han comenzado.

Columnas:

  • Partido: Nombre del evento (Home vs Away).
  • Liga: Competición y país.
  • Hora: Fecha y hora de inicio (ajustada a timezone local).
  • PIN (Pinnacle): Cuota "Fair" calculada sin margen.
  • ALT (Altenar): Cuota ofrecida por DoradoBet.
  • EV%: Valor Esperado (Expected Value). Ej: 15.2% = Ganancia esperada por cada S/1 apostado.
  • Kelly: Stake sugerido en soles (S/).
  • Acciones: Botón APOSTAR (registra en Paper Trading).

Filtros:

  • Mínimo EV: Solo mostrar ops con EV > 5%.
  • Máximo Tiempo: Eventos en las próximas X horas.

2. EN VIVO (Live Opportunities)

Propósito: Oportunidades detectadas en partidos en curso.

Indicadores Especiales:

  • 🔥 Badge Rojo: "La Volteada" (favorito perdiendo).
  • ⚡ Badge Verde: Value Bet Live (discrepancia de cuota).
  • ⚽ Badge Azul: Next Goal (presión ofensiva).

Información Adicional:

  • Score Actual: 1-0 (actualizado en tiempo real).
  • Minuto: 67' (sincronizado con Pinnacle).
  • Tarjetas Rojas: 🟥 (si hay expulsiones, se desactiva "La Volteada").

Ejemplo:

┌───────────────────────────────────────────────────────────────────────┐
│ 🔥 LIVE SNIPE │ Tigres UANL vs Pumas     │ 0-1 │ 42' │ EV: 28% │ S/8 │
│ Favorito perdiendo. Prob Real: 62% | Cuota ALT: 2.20                  │
│ [📊 VER STATS]  [💰 APOSTAR]                                          │
└───────────────────────────────────────────────────────────────────────┘

3. ACTIVAS (Apuestas en Curso)

Propósito: Monitoreo en tiempo real de apuestas pendientes.

Columnas:

  • Partido: Evento apostado.
  • Selección: Home / Draw / Away (o línea específica si es Total).
  • Stake: Monto apostado (S/).
  • Cuota: Odd en el momento de la apuesta.
  • Score Actual: Marcador en vivo (actualizado cada 5s).
  • Tiempo: Minuto del partido.
  • Estado:
    • 🟢 WINNING (va ganando)
    • 🟡 PENDING (resultado incierto)
    • 🔴 LOSING (va perdiendo)
  • Potencial: Ganancia si gana / Pérdida si pierde.

Acciones:

  • Ver detalles (🔍 Estadísticas completas del partido).
  • Cash Out manual (deshabilitado en Paper Trading).

4. HISTORIAL (Bets Liquidadas)

Propósito: Análisis de rendimiento histórico.

Métricas Agregadas (Header):

Total Apostado: S/1,234.00 | Ganado: S/1,421.30 | ROI: +15.2% | Win Rate: 58.3%

Tabla de Apuestas:

  • Fecha: Timestamp de ejecución.
  • Partido: Evento.
  • Estrategia: PREMATCH / LIVE_SNIPE / LIVE_VALUE.
  • Resultado:WON / ❌ LOST.
  • P&L: Profit/Loss en soles.

Filtros:

  • Por fecha (últimos 7 días, 30 días, todo).
  • Por estrategia.
  • Por resultado (Solo ganadas / Solo perdidas).

Exportación: Botón 📥 Exportar CSV para análisis externo.


5. MONITOR (Comparador de Cuotas)

Propósito: Vista profesional en tiempo real de todos los partidos en vivo.

Layout:

┌──────────────────────┬───────────────────────┬───────────────────────┐
│ PARTIDO / TIEMPO     │ PINNACLE (Live & Pre) │ ALTENAR (Bookie)      │
├──────────────────────┼───────────────────────┼───────────────────────┤
│ Liverpool vs Man Utd │  1   │  X  │  2       │  1   │  X  │  2       │
│ PIN: 72' │ 2-1       │ 1.45 │ 4.5 │ 7.2      │ 1.38 │ 4.8 │ 8.5      │
│ ALT: 72' │ 2-1       │ ▲    │ ●   │ ▼        │ ●    │ ●   │ ●        │
└──────────────────────┴───────────────────────┴───────────────────────┘

Indicadores:

  • ▲ Verde: Cuota subiendo (oportunidad potencial).
  • ▼ Rojo: Cuota bajando.
  • ● Azul: Cuota estable (pulsando = dato fresco).
  • Badges Morados: Cuotas pre-match para referencia.

Columnas Extra:

  • PIN Goals / ALT Goals: Mercados de Totales (Over/Under 2.5, 1.5, 3.5).

Uso: Detectar manualmente oportunidades que el scanner automático podría haber filtrado.


6. MATCHER (Vinculación Manual)

Propósito: Herramienta para que el usuario fuerce matches entre eventos de Pinnacle y Altenar que el sistema no pudo vincular automáticamente.

Casos de Uso:

  • Nombres muy diferentes (ej: "Man City" vs "Manchester City FC").
  • Ligas con nombres ambiguos.
  • Eventos de ligas menores sin cobertura completa.

Flujo:

  1. Lista de eventos Altenar sin match.
  2. Click en SUGERIR para generar candidatos High Confidence.
  3. Revisar sugerencias y aplicar con APLICAR o APLICAR TOP 20.
  4. Para casos puntuales, buscar manualmente en lista de Pinnacle y usar VINCULAR.
  5. El sistema guarda el mapping en db.json.
  6. Futuras detecciones usarán este match guardado y los aliases dinámicos del backend.

🔌 API Endpoints

El servidor expone los siguientes endpoints REST:

Oportunidades

GET /api/opportunities/prematch

  • Descripción: Retorna value bets pre-partido.
  • Respuesta:
{
  "success": true,
  "data": [
    {
      "eventId": 15234567,
      "match": "Real Madrid vs Barcelona",
      "league": "La Liga",
      "date": "2026-02-20T20:00:00.000Z",
      "pinnaclePrice": 2.15,
      "altenarPrice": 2.35,
      "realProb": 46.5,
      "ev": 9.3,
      "kellyStake": 12.50,
      "selection": "Home"
    }
  ]
}

GET /api/opportunities/live

  • Descripción: Retorna oportunidades en tiempo real.
  • Cache: 5 segundos.

GET /api/opportunities/live/placement-provider

  • Descripción: Retorna el proveedor activo para auto-placement.
  • Respuesta: { "success": true, "provider": "booky", "allowed": ["booky", "pinnacle"] }

POST /api/opportunities/live/placement-provider

  • Body: { "provider": "booky" } o { "provider": "pinnacle" }
  • Descripción: Cambia el proveedor de auto-placement en caliente, sin reiniciar backend.

POST /api/opportunities/discard

  • Body: { "eventId": 123456 }
  • Descripción: Añade evento a blacklist (no volverá a mostrarse).

GET /api/opportunities/arbitrage/preview

  • Descripción: Preview de arbitraje prematch (1x2 + DC/opuesto), sin ejecutar apuestas.
  • Query opcional de refresh dirigido Altenar:
    • refreshAltenarNow=1
    • refreshAltenarEventId=<altenarEventId>
  • Uso recomendado: pre-flight dual para validar cuota Altenar en tiempo real justo antes de ejecutar.
  • Respuesta (extracto):
{
  "success": true,
  "count": 12,
  "data": [],
  "onDemandRefresh": {
    "altenarEvent": {
      "success": true,
      "code": "REFRESH_OK",
      "eventId": "14874449",
      "changed": true,
      "updatedAt": "2026-04-04T12:05:31.114Z"
    }
  }
}

Pinnacle / Arcadia

GET /api/pinnacle/prematch-ws-health

  • Descripción: Estado operativo del canal prematch por WS Arcadia.
  • Uso: Diagnóstico rápido de conectividad y fallback desde backend/frontend.
  • Respuesta (resumen):
{
  "success": true,
  "mode": "ws-live",
  "wsConnected": true,
  "wsStale": false,
  "lastUsefulWsFrameAt": "2026-04-03T19:52:04.043Z",
  "lastPollingSyncAt": "2026-04-03T19:48:33.756Z",
  "counts": {
    "fixtures": 198,
    "subscribedTopics": 240
  }
}

Portfolio (Paper Trading)

GET /api/portfolio

  • Descripción: Estado actual del bankroll.
  • Respuesta:
{
  "balance": 1234.56,
  "initialCapital": 1000.00,
  "activeBets": [
    {
      "id": "1708176543210",
      "match": "Tigres vs Pumas",
      "selection": "Home",
      "stake": 8.00,
      "odd": 2.20,
      "status": "PENDING",
      "score": "0-1",
      "liveTime": "42'"
    }
  ],
  "history": [...]
}

POST /api/portfolio/bet

  • Body: Objeto de oportunidad completo.
  • Descripción: Ejecuta apuesta manual (Paper Trading).

POST /api/portfolio/reset

  • Descripción: Resetea portfolio a capital inicial.
  • ⚠️ Peligro: Borra todo el historial.

Monitor

GET /api/monitor/live-odds

  • Descripción: Feed comparativo Pinnacle vs Altenar.
  • Formato: Array de eventos con odds anidadas.
  • Actualización: Tiempo real (lee pinnacle_live.json + consulta Altenar).

Matcher

GET /api/matcher/unlinked

  • Descripción: Eventos de Altenar sin match Pinnacle.

POST /api/matcher/link

  • Body: { "altenarId": 123, "pinnacleId": 456 }
  • Descripción: Fuerza vinculación manual.

Booky (Semi-Auto + Real Controlado)

GET /api/booky/tickets

  • Retorna tickets pendientes + histórico booky.

POST /api/booky/prepare

  • Prepara ticket draft desde una oportunidad.

POST /api/booky/confirm/:id

  • Confirma ticket en modo semi-auto (espejo en portfolio).

POST /api/booky/cancel/:id

  • Cancela ticket draft.

GET /api/booky/token-health

  • Estado del JWT real (exp, minutos restantes, autenticación).

POST /api/booky/token/renew

  • Dispara renovación asistida de token.

GET /api/booky/account?refresh=1&historyLimit=60

  • Snapshot de cuenta real por perfil (balance + historial remoto reconciliado).

GET /api/booky/capture/latest

  • Última captura de payloads en data/booky.

POST /api/booky/real/dryrun/:id

  • Construye payload final placeWidget sin enviar apuesta real.

POST /api/booky/real/confirm/:id

  • Confirmación real estándar (con guardas).

POST /api/booky/real/confirm-fast/:id

  • Confirmación real rápida con manejo de estado incierto y reintento controlado.

⚙️ Configuración Avanzada

Variables de Entorno (.env)

# Core
NODE_ENV=development
PORT=3000
TZ=America/Lima
DISABLE_BACKGROUND_WORKERS=false

# Altenar Profile (set-book-profile.js)
BOOK_PROFILE=doradobet
ALTENAR_INTEGRATION=doradobet
ALTENAR_ORIGIN=https://doradobet.com
ALTENAR_REFERER=https://doradobet.com/deportes-en-vivo
ALTENAR_COUNTRY_CODE=PE
ALTENAR_CULTURE=es-ES
ALTENAR_TIMEZONE_OFFSET=300
ALTENAR_NUM_FORMAT=en-GB
ALTENAR_DEVICE_TYPE=1
ALTENAR_SPORT_ID=0

# Overrides opcionales
# ALTENAR_WIDGET_BASE_URL=https://sb2frontend-altenar2.biahosted.com/api/widget
# ALTENAR_USER_AGENT=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36
# ALTENAR_ACCEPT_LANGUAGE=es-ES,es;q=0.9,en;q=0.8

# Booky / Real Placement
# ALTENAR_BOOKY_URL=https://www.casinoatlanticcity.com/apuestas-deportivas#/overview
# ALTENAR_LOGIN_USERNAME=tu_usuario
# ALTENAR_LOGIN_PASSWORD=tu_password
# ALTENAR_BOOKY_AUTH_TOKEN=Bearer <jwt>
# BOOKY_REAL_PLACEMENT_ENABLED=false
# BOOKY_KEEP_REAL_PLACEMENT_ON_TOKEN_REFRESH=false
# BOOKY_AUTO_TOKEN_REFRESH_ENABLED=true
# BOOKY_TOKEN_MIN_REMAINING_MINUTES=2
# BOOKY_MIN_EV_PERCENT=2
# BOOKY_MAX_ODD_DROP=0.20

# AUTO_SNIPE (rollout controlado)
# AUTO_PLACEMENT_PROVIDER=booky
# AUTO_SNIPE_ENABLED=false
# AUTO_SNIPE_DRY_RUN=true
# AUTO_SNIPE_MIN_EV_PERCENT=3
# AUTO_SNIPE_MIN_STAKE_SOL=2
# AUTO_SNIPE_MAX_BETS_PER_HOUR=4
# AUTO_SNIPE_COOLDOWN_PER_PICK_MS=120000
# AUTO_SNIPE_REQUIRE_REAL_PLACEMENT_ENABLED=true
# AUTO_SNIPE_REENTRY_MIN_ODD_IMPROVEMENT_PCT=8
# AUTO_SNIPE_REENTRY_MIN_ODD_POINTS=0.30
# AUTO_SNIPE_MAX_ENTRIES_PER_PICK=2

# Matcher diagnostics/tuning
# MATCH_DIAGNOSTIC_LOG=1
# MATCH_FUZZY_THRESHOLD=0.77
# MATCH_MIN_ACCEPT_SCORE=0.60
# MATCH_TIME_TOLERANCE_MINUTES=5
# MATCH_TIME_EXTENDED_TOLERANCE_MINUTES=30

# Housekeeping historial/cuenta booky (opcionales)
# BOOKY_BALANCE_REFRESH_MS=45000
# BOOKY_HISTORY_REFRESH_MS=60000
# BOOKY_HISTORY_RETENTION_DAYS=30
# BOOKY_PROFILE_HISTORY_MAX_ITEMS=500

# Rendimiento de endpoint prematch (opcional)
# PREMATCH_CACHE_TTL_MS=20000
# PREMATCH_STREAM_REFRESH_INTERVAL_MS=12000

# Arbitraje: excluir Altenar stale (opcional)
# ARBITRAGE_ALTENAR_MAX_ODD_AGE_MS=900000

# Scheduler adaptativo Altenar (base + burst)
# ALTENAR_PREMATCH_SCHEDULER_LOOP_TICK_MS=20000
# ALTENAR_PREMATCH_SCHEDULER_MAX_CONCURRENCY=3
# ALTENAR_PREMATCH_SCHEDULER_MAX_EVENTS_PER_TICK=6
# ALTENAR_PREMATCH_SCHEDULER_BURST_ENABLED=true
# ALTENAR_PREMATCH_SCHEDULER_BURST_MAX_CONCURRENCY=6
# ALTENAR_PREMATCH_SCHEDULER_BURST_MAX_EVENTS_PER_TICK=14
# ALTENAR_PREMATCH_SCHEDULER_BURST_WINDOW_MINUTES=120
# ALTENAR_PREMATCH_SCHEDULER_BURST_STALE_THRESHOLD_MS=360000
# ALTENAR_PREMATCH_SCHEDULER_BURST_MIN_LINKED_STALE=8
# ALTENAR_PREMATCH_SCHEDULER_CAPACITY_LOG_INTERVAL_MS=60000

Modo de Alta Carga (Sabadazo)

Para mantener el dashboard responsivo cuando sube mucho el volumen de partidos:

DISABLE_BACKGROUND_WORKERS=false
DISABLE_LIVE_SCANNER=false
DISABLE_PREMATCH_SCHEDULER=true
DISABLE_PINNACLE_INGEST_CRON=true
DISABLE_MONITOR_DASHBOARD=true

# Perfil desbloqueo LIVE (temporal)
LIVE_SNIPE_REQUIRE_PINNACLE_LIVE=false
LIVE_VALUE_MIN_EV=0.01
LIVE_VALUE_REQUIRE_SCORE_SYNC=false
LIVE_VALUE_SCORE_SYNC_MAX_GOAL_DIFF=1
LIVE_VALUE_STABILITY_MIN_HITS=1
LIVE_VALUE_STABILITY_MIN_AGE_MS=1500
LIVE_GLOBAL_STABILITY_MIN_HITS=1

Luego, reinicia backend. En días normales vuelve ambos flags a false.

Personalización de Risk Profiles

Editar src/utils/mathUtils.js:

const RISK_PROFILES = {
  'PREMATCH_VALUE': 0.25,   // Cambiar a 0.30 para ser más agresivo
  'LIVE_VALUE': 0.125,      
  'LIVE_SNIPE': 0.10,       // Cambiar a 0.05 para ser ultra-conservador
};

Ajuste de Filtros (Scanner)

Editar src/services/scannerService.js:

// Línea ~135: Filtro de Stake Mínimo
ops = ops.filter(op => op.kellyStake >= 1.00); // Cambiar a 0.50 para capturar más ops

� Guía de Scripts y Comandos

BetSniper incluye dos tipos de scripts: comandos npm (definidos en package.json, llamados con npm run <nombre>) y scripts directos (en scripts/, ejecutados con node scripts/<archivo>.js). Esta guía los organiza por función.


🧩 Comandos npm (package.json)

Servidor Principal

Comando Descripción
npm start Arranca el servidor en modo producción (node server.js). Sin hot-reload.
npm run dev Arranca el servidor en modo desarrollo con nodemon (reinicia al guardar archivos). Recomendado para desarrollo.

Gestión de Perfil Booky

Comando Descripción
npm run book:dorado Cambia el perfil activo a DoradoBet en .env (BOOK_PROFILE, ALTENAR_INTEGRATION, ALTENAR_ORIGIN, ALTENAR_REFERER). Sin reinicio de servidor.
npm run book:acity Cambia el perfil activo a ACity (Casino Atlantic City) en .env. Sin reinicio de servidor.

Ejecutar siempre antes de capturar un token o hacer capture/spy, para que Puppeteer use el perfil correcto.


Extracción de Token JWT (Autenticación Booky)

Comando Modo Descripción
npm run token:booky Headed + timeout auto Abre Chrome con sesión visual. Captura JWT automáticamente tras detectar login. Cierra sol en timeout.
npm run token:booky:wait-close Headed + espera manual Abre Chrome, captura JWT y espera a que el usuario cierre la ventana manualmente. Útil si el login requiere 2FA o captcha.
npm run token:booky:dorado DoradoBet + headed Igual que token:booky pero fuerza perfil DoradoBet aunque .env diga otra cosa.
npm run token:booky:dorado:wait-close DoradoBet + manual Combinación: perfil DoradoBet + espera manual de cierre.
npm run token:booky:acity ACity + headed Fuerza perfil ACity.
npm run token:booky:acity:wait-close ACity + manual ACity + espera manual.

¿Qué hace internamente?

  1. Lee ALTENAR_BOOKY_URL, ALTENAR_LOGIN_USERNAME, ALTENAR_LOGIN_PASSWORD del .env.
  2. Abre Puppeteer, navega al bookie, inicia sesión automáticamente.
  3. Intercepta la respuesta del servidor que contiene el JWT.
  4. Valida que sea un token de usuario autenticado (no guest).
  5. Escribe ALTENAR_BOOKY_AUTH_TOKEN=Bearer eyJ... en tu .env.

Output esperado:

✅ Login detectado. Capturando JWT...
🔑 JWT válido: usuario antho@ejemplo.com | Expira: 2026-03-03T14:22:00Z
💾 Token escrito en .env

Captura de Payload placeWidget

Necesario para entender qué envía el bookie cuando colocas una apuesta real (para construir el payload del confirmRealPlacement).

Comando Modo Descripción
npm run capture:booky Headed + perfil actual Abre Chrome, navega al bookie, captura el payload placeWidget/betslip al hacer click en una apuesta.
npm run capture:booky:headless Headless Igual pero sin ventana visible.
npm run capture:booky:dorado DoradoBet headed ForZa perfil DoradoBet.
npm run capture:booky:dorado:headless DoradoBet headless Fuerza DoradoBet sin ventana.
npm run capture:booky:acity ACity headed Fuerza perfil ACity.
npm run capture:booky:acity:headless ACity headless ACity sin ventana.

El payload capturado se guarda en data/booky/capture-*.json y queda disponible en GET /api/booky/capture/latest.

Requisito operativo: antes de usar /api/booky/real/* valida que GET /api/booky/capture/latest devuelva found: true. Si devuelve found: false, ejecuta de nuevo npm run capture:booky:acity o npm run capture:booky:dorado según perfil.


Captura de Payload Pinnacle Placement (Fase 1)

Necesario para descubrir el contrato real de Pinnacle (endpoint + request body + respuesta) antes de implementar pinnacleSemiAutoService.

Comando Modo Descripción
npm run capture:pinnacle:placement Headed + espera manual Abre Pinnacle con perfil dedicado, captura requests POST/PUT/PATCH candidatos a placement y espera cierre manual del navegador.
npm run capture:pinnacle:placement:headless Headless + timeout 180s Ejecuta captura automática por ventana de tiempo sin UI.

Output de captura:

  • data/pinnacle/capture-placement-<timestamp>.json
  • data/pinnacle/capture-placement.latest.json

Flujo recomendado (seguro):

  1. Ejecutar npm run capture:pinnacle:placement.
  2. En la ventana de Pinnacle, hacer una apuesta manual de prueba hasta el último click de confirmar.
  3. Cerrar navegador para finalizar y persistir captura.
  4. Revisar capture-placement.latest.json y extraer:
  • endpoint exacto,
  • headers requeridos,
  • esquema del payload,
  • semántica de respuesta (confirmado/rechazado/requote).

Tip: si no aparece ningún candidato, repetir con:

node scripts/capture-pinnacle-placement.js --headed --wait-close --all-posts

Spy de Historial y Perfil

Comando Descripción
npm run spy:altenar Auto-detecta parámetros de integración del bookie (integration, countryCode, baseUrl) interceptando tráfico real. Útil si el bookie cambia su configuración. Guarda en data/altenar-profile-*.json.
npm run spy:booky:history Abre Chrome (headed), navega al historial del bookie y captura las respuestas completas de los endpoints de balance e historial. Guarda en data/booky/spy-history-*.json.
npm run spy:booky:history:headless Igual que el anterior pero headless con timeout de 120 segundos.

Smoke Test del Flujo Booky

Comando Descripción
npm run smoke:booky Ejecuta el flujo E2E completo en modo seguro: token-healthaccount snapshotprepare ticketconfirm-fast. No envía ninguna apuesta real aunque BOOKY_REAL_PLACEMENT_ENABLED=true.
npm run smoke:booky:live Igual pero pasa por el path de placement real (requiere BOOKY_REAL_PLACEMENT_ENABLED=true y EV ≥ 0%). Usar solo para validar el flujo completo en ambiente controlado.
npm run health:latency Ejecuta un chequeo de latencia por muestras sobre endpoints críticos (portfolio, live, prematch, booky/account, kelly-diagnostics) para detectar freezes/event-loop blocking.

Recomendación: ejecutar npm run smoke:booky después de cada renovación de token para confirmar que el sistema está operativo.


📥 Scripts de Ingesta Manual (scripts/)

El servidor ejecuta estas ingestas automáticamente, pero puedes forzarlas manualmente:

node scripts/ingest-altenar.js

Actualiza el caché de eventos prematch de Altenar (DoradoBet) en db.json.

  • Smart skip: si los datos tienen menos de 100 minutos, omite la ingesta automáticamente.
  • Forzar: modifica la llamada internámente cambiando force=true o elimina altenarLastUpdate de db.json.
  • Cuándo usarlo: antes de una sesión de trading prematch para asegurar datos frescos.

node scripts/ingest-pinnacle.js

Descarga y normaliza eventos prematch de Pinnacle Arcadia (REST) en db.json.

  • Convierte cuotas americanas a decimales.
  • Calcula probabilidades Fair (sin vig).
  • Cuándo usarlo: una vez al día, o cada vez que quieras refrescar el caché prematch de Pinnacle.

Comandos recomendados:

# Modo normal (con flush incremental)
npm run ingest:pinnacle:force

# Modo seguro para OneDrive (sin flush incremental)
npm run ingest:pinnacle:safe

Si operas sobre carpetas sincronizadas (OneDrive), usa ingest:pinnacle:safe para reducir colisiones de escritura (EPERM/EBUSY).


🔍 Scripts de Scanner Manual

node scripts/scan_live.js [--dry-run]

Ejecuta el scanner live en modo standalone (fuera del servidor) con bucle de 60 segundos.

  • --dry-run / -d: modo observador — detecta oportunidades pero no registra apuestas. Usar siempre si el servidor ya está corriendo.
  • Sin --dry-run: registra apuestas en db.json (usar solo si el servidor está detenido).
# Modo observador (recomendado con servidor activo)
node scripts/scan_live.js --dry-run

# Modo activo (solo si el servidor está detenido)
node scripts/scan_live.js

node scripts/run_linker.js

Ejecuta el scanner prematch en modo standalone.

  • Lee db.json (cache de Pinnacle y Altenar).
  • Cruza eventos y muestra oportunidades prematch detectadas.
  • No registra apuestas. Solo diagnóstico.
node scripts/run_linker.js

🦵 Scripts de Diagnóstico de Base de Datos

node scripts/check_db.js

Muestra un resumen rápido del estado de db.json: cantidad de registros en cada colección (pinnacle, matches, scanned_prematch, etc.).

node scripts/check_db.js
# Output: Keys: ['config','upcomingMatches','portfolio',...]
# Pinnacle Count: 142

node scripts/find_match_in_db.js <termino>

Busca un equipo o partido en db.json y en data/pinnacle_live.json de forma recursiva.

node scripts/find_match_in_db.js "Liverpool"
node scripts/find_match_in_db.js "Tigres"

node scripts/find_live_match.js

Busca un partido específico en el feed live actual de Pinnacle (data/pinnacle_live.json).

node scripts/check_linked_status.js

Verifica el estado de linking de registros específicos en db.json (orientado a depurar por qué un partido concreto no se enlazó).

node scripts/check_odds.js

Muestra las cuotas almacenadas para un partido concreto, cruzando datos de Pinnacle y Altenar.

node scripts/check_mapping.js

Muestra entradas del diccionario mappedTeams en db.json para verificar alias guardados.

node scripts/generate_full_report.cjs

Genera un CSV completo (reporte_completo_partidos.csv) con todos los partidos de Pinnacle y Altenar, mostrando si están enlazados o no. Útil para auditar la calidad del matcher.

node scripts/generate_full_report.cjs
# Crea: reporte_completo_partidos.csv

🧹 Scripts de Mantenimiento de Portfolio

node scripts/reset_database.js

Restablece db.json a los valores por defecto (bankroll 100, historial vacío).

⚠️ Destructivo: borra todo el historial y apuestas activas. Úsa solo en desarrollo o para empezar desde cero.

node scripts/reset_database.js

node scripts/force_settle_bets.js

Liquida forzadamente apuestas activas que están atascadas en estado PENDING.

  • Consulta la API de resultados de Altenar (GetEventResults) para obtener el score final.
  • Aplica la lógica de ganancia/pérdida para cada tipo de pick (home, away, draw, over, under).
  • Útil cuando el Zombie Protocol no pudo liquidar automáticamente.
node scripts/force_settle_bets.js

node scripts/purge_invalid_bets.cjs

Elimina manualmente apuestas con IDs de evento inválidos o corruptos de db.json.

  • Los IDs a purgar están listados en el propio script (TARGET_IDS).
  • Editar el array para añadir IDs si encuentras nuevas apuestas corruptas.
node scripts/purge_invalid_bets.cjs

node scripts/fix_under_2_bets.cjs

Repara apuestas Under con línea under_0 (línea mal parseada). Extrae la línea real del campo market y corrige el pick.

node scripts/fix_under_2_bets.cjs

node scripts/migrate_bankroll.js

Escala el bankroll de db.json a un nuevo valor (por ejemplo de 1,000 a 10,000), aplicando el mismo factor a balance e historial proporcional.

  • Editar el valor newCapital dentro del script antes de ejecutar.
node scripts/migrate_bankroll.js

🧪 Scripts de Testing y Mock

node scripts/mock_pinnacle.js

Genera un archivo data/pinnacle_live.json con datos ficticios (partidos de prueba como Man City vs Liverpool). Permite desarrollar y probar el scanner sin conexión real a Pinnacle.

node scripts/mock_pinnacle.js
# Crea datos de prueba en data/pinnacle_live.json

node scripts/tmp-run-booky-confirm.mjs

Ejecución directa de una confirmación de ticket booky para testing en caliente. Requiere editar el ticketId dentro del archivo antes de ejecutar.

node scripts/tmp-run-booky-confirm.mjs

🐞 Scripts de Debug (Herramientas de Desarrollo)

Scripts de diagnóstico genéricos. No forman parte del flujo normal de operación pero son útiles para investigar problemas en vivo:

Script ¿Cuándo usarlo?
debug_live.js Ver estructura cruda del feed Altenar live (GetLivenow)
debug_live_event.js Inspeccionar un evento live concreto con todos sus detalles
debug_live_markets.js Ver mercados disponibles (abiertos/cerrados) en un evento live
debug_live_names.js Ver nombres de equipos crudos tal como los devuelve Altenar
debug_live_odds.js Comparar cuotas Altenar vs Pinnacle en un evento live
debug_live_structure_v3.js Explorar la estructura JSON completa del endpoint live (versión actual)
debug_matching.js Depurar por qué un par de equipos concretos no se está vinculando
debug_matcher_specific.js Probar findMatch() con el cache de Pinnacle actual (data/pinnacle_live.json)
debug_full_scan.js Ejecutar un scan completo de oportunidades con logging máximo
debug_monitor_link.js Diagnosticar por qué el monitor no muestra cuotas de Arcadia (cuenta linked y pinnacleFound)
debug_pinnacle_structure.js Ver estructura cruda de la respuesta de Pinnacle Arcadia
debug_pinnacle_raw.js Ver respuesta HTTP raw de Pinnacle sin transformar
debug_pinnacle_endpoints.js Probar diferentes endpoints de Pinnacle (matchups, odds, etc.)
debug_pinnacle_markets.cjs Inspeccionar mercados disponibles en un evento de Pinnacle
debug_pinnacle_match_info.cjs Ver información completa (teams, odds, status) de un partido Pinnacle
debug_altenar_markets.js Inspeccionar los mercados (1X2, Totales, BTTS) de un evento Altenar
debug_totals_structure.js Ver estructura de mercados Over/Under para verificar la normalización
debug_scanner_v2.js Analizar el output del scanner paso a paso (versión actual)
audit_date.js Auditoría histórica del portfolio: consulta GetEventResults, compara el score final real con el registrado en db.json, corrige estados WON/LOST erróneos y ajusta el balance. Acepta fecha como argumento.
# Diagnóstico de matching:
node scripts/debug_matching.js
node scripts/debug_matcher_specific.js

# Diagnóstico de monitor/linking:
node scripts/debug_monitor_link.js
node scripts/debug_full_scan.js

# Auditoría histórica del portfolio (corrige scores/estados):
node scripts/audit_date.js 2026-03-01

📊 Resumen rápido: ¿qué ejecutar en cada situación?

Situación Comandos
Arranque diario normal Terminal 1: npm run dev | Terminal 2: node services/pinnacleLight.js | Terminal 3: cd client && npm run dev
Cambiar de bookie npm run book:acity o npm run book:dorado
Renovar token JWT npm run book:doradonpm run token:booky:wait-close
Verificar que todo funciona npm run smoke:booky
Ver oportunidades sin servidor activo node scripts/scan_live.js --dry-run
Forzar actualización datos prematch node scripts/ingest-altenar.js + node scripts/ingest-pinnacle.js
Apuesta atascada en PENDING node scripts/force_settle_bets.js
Matcher no vincula un equipo node scripts/debug_matching.js o node scripts/debug_matcher_specific.js → añadir alias en dynamicAliases.json
Score/resultado de apuesta incorrecto node scripts/audit_date.js YYYY-MM-DD (corrige estados en db.json)
Auditar calidad del matcher node scripts/generate_full_report.cjs
Reiniciar base de datos node scripts/reset_database.js
Testear sin Pinnacle real node scripts/mock_pinnacle.jsnpm run dev

�🚨 Troubleshooting

Problema: "Chromium no se cierra"

Síntoma: Ventana de Chrome queda abierta indefinidamente.

Causa: El token de Pinnacle no se capturó correctamente.

Solución:

  1. Cierra manualmente la ventana de Chrome.
  2. Elimina el archivo: rm data/pinnacle_token.json
  3. Reinicia: node services/pinnacleLight.js
  4. Vuelve a iniciar sesión en Pinnacle cuando se abra Chrome.

Problema: "No aparecen oportunidades en Frontend"

Síntoma: Pestañas "Pre-Match" y "En Vivo" vacías.

Diagnóstico:

  1. Verifica que los 3 procesos estén corriendo (Terminal 1, 2, 3).
  2. Revisa logs de Terminal 1 (servidor) buscando errores.
  3. Abre http://localhost:3000/api/opportunities/live directamente en navegador.

Soluciones:

  • Si la API devuelve []: El matcher no está vinculando eventos. Usa la pestaña "Matcher" para forzar links.
  • Si hay error 500: Revisa que exist data/pinnacle_live.json y contenga datos.
  • Si pinnacle_live.json está vacío: El proceso de Terminal 2 falló. Reinicia.

Problema: "Latencia intermitente / pantallazos en carga"

Síntoma: a veces carga rápido y a veces todo timeout (capital, oportunidades, kelly card).

Diagnóstico rápido:

npm run health:latency

Si aparecen timeouts intermitentes:

  1. Activa modo de alta carga (DISABLE_PREMATCH_SCHEDULER=true, DISABLE_PINNACLE_INGEST_CRON=true; opcional DISABLE_MONITOR_DASHBOARD=true si el monitor está abierto).
  2. Reinicia backend.
  3. Repite npm run health:latency y confirma que portfolio/live quedan estables.

Problema: "Datos congelados (Stale Data)"

Síntoma: Monitor muestra tiempos desactualizados (ej: Altenar en minuto 75', Pinnacle en 70').

Causa: El WebSocket de Pinnacle dejó de recibir frames.

Solución Automática: El sistema detecta esto y crea data/pinnacle_stale.trigger, gatillando reinicio automático del socket.

Solución Manual:

  1. Detén Terminal 2: Ctrl+C
  2. Reinicia: node services/pinnacleLight.js

Problema: "Apuestas Duplicadas"

Síntoma: Mismo evento aparece dos veces en "Activas".

Causa: Ejecutaste scan_live.js SIN el flag --dry-run mientras el servidor estaba corriendo.

Prevención: Usa SIEMPRE --dry-run si el servidor está activo.

Limpieza:

// Abrir db.json y eliminar manualmente la entrada duplicada en activeBets[]

Problema: "Token Booky inválido o vencido"

Síntoma: endpoints /api/booky/real/* responden BOOKY_TOKEN_RENEWAL_REQUIRED.

Solución:

  1. Cambia al perfil correcto: npm run book:acity o npm run book:dorado.
  2. Renueva token: npm run token:booky:wait-close.
  3. Verifica estado: GET /api/booky/token-health.

Problema: "No hay captura latest disponible"

Síntoma: al intentar APOSTAR o usar /api/booky/real/* aparece el error No hay captura latest disponible.

Causa: en ese equipo no existe data/booky/capture-<perfil>.latest.json (normal en clones nuevos porque data/ no se versiona).

Solución:

  1. Verifica el perfil activo: npm run book:acity o npm run book:dorado.
  2. Ejecuta captura para ese perfil:
  • npm run capture:booky:acity
  • o npm run capture:booky:dorado
  1. Confirma que existe latest: GET /api/booky/capture/latest debe devolver found: true.
  2. Reintenta el flujo de apuesta.

Problema: "No se confirmó placeWidget (estado incierto)"

Síntoma: respuesta BOOKY_REAL_CONFIRMATION_UNCERTAIN.

Interpretación: la casa pudo aceptar la apuesta, pero no devolvió confirmación definitiva (timeout/red).

Acción recomendada:

  1. Revisar Open Bets/History en Booky.
  2. Revisar GET /api/booky/account?refresh=1.
  3. No reintentar ciegamente hasta validar si la apuesta ya existe.

Problema: "placeWidget rechazó la apuesta (HTTP 4xx/5xx)"

Síntoma: respuesta BOOKY_REAL_PLACEMENT_REJECTED o BOOKY_PLACEWIDGET_REJECTED.

Dónde auditar:

  1. db.jsonbooky.history[] con status=REAL_REJECTED o REAL_REJECTED_FAST.
  2. Dentro del ticket, revisar realPlacement.diagnostic:
  3. providerStatus, providerCode, providerMessage, providerBody, requestId.

Acción recomendada:

  1. Verificar si el mercado sigue abierto y si la cuota cambió.
  2. Validar token y perfil (/api/booky/token-health, BOOK_PROFILE, ALTENAR_INTEGRATION).
  3. Ejecutar POST /api/booky/real/dryrun/:id antes de reintentar con nuevo ticket.

🗺️ Roadmap

V3.1 (Q1 2026)

  • Integración con Telegram Bot para notificaciones en tiempo real.
  • Exportación de historial a Excel/CSV.
  • Gráficos de rendimiento (Chart.js).

V3.2 (Q2 2026)

  • Soporte para múltiples bookies (Betano, Inkabet).
  • Machine Learning para predicción de líneas de cierre.
  • Backtesting engine con datos históricos.

V4.0 (Q3 2026)

  • Modo Real Trading (conexión directa con APIs de bookies).
  • Hedging automático (cobertura de riesgo).
  • Multi-deporte (NBA, NFL, Tennis).

📄 Licencia

ISC License - Ver archivo LICENSE para detalles.


⚠️ Disclaimer

Este software está orientado a investigación cuantitativa, simulación y operación asistida. El módulo de ejecución real existe pero está protegido por flags y validaciones (BOOKY_REAL_PLACEMENT_ENABLED, guardas de token/valor). El trading deportivo involucra riesgo de pérdida de capital. Usa bajo tu propia responsabilidad.


🤝 Contribuciones

Pull requests son bienvenidos. Para cambios mayores, abre un issue primero para discutir qué te gustaría cambiar.


📧 Contacto


Construido con ❤️ para traders algorítmicos

⭐ Si este proyecto te fue útil, considera darle una estrella en GitHub

Packages

 
 
 

Contributors