--- title: PolySignal Hackaton emoji: ๐Ÿ“Š colorFrom: blue colorTo: green sdk: docker app_port: 7860 pinned: false --- # PolySignal Dashboard web de inteligencia de mercados de prediccion en tiempo real. ## Que es PolySignal analiza mercados de Polymarket cruzando noticias de Finnhub con modelos de IA (ModernFinBERT + Qwen3-8B) para generar senales de trading (alcista, bajista, neutral). Incluye simulador de posiciones con capital virtual, lista de seguimiento y alertas por Telegram. **No ejecuta ordenes reales.** Es una herramienta de analisis e inteligencia. **Idioma:** Espanol. **Moneda base:** Euro (โ‚ฌ). ## Stack - **Backend:** Node.js 26 + Express.js 5 + Socket.io + node-cron - **ORM:** Prisma 6 + SQLite - **Frontend:** Vanilla JS + Vite 7 + Leaflet.js + Chart.js + Socket.io client - **IA:** HuggingFace Spaces (ModernFinBERT + Qwen3-8B) + OpenRouter fallback - **Datos:** Polymarket Gamma API + Finnhub REST - **Deploy:** HuggingFace Spaces (Docker, puerto 7860) ## Estado del proyecto El backend es **totalmente funcional**: - API REST completa con autenticacion JWT. - Pipeline de IA con cadena de fallback (Spaces HF โ†’ API HF โ†’ OpenRouter โ†’ rule-based). - Scheduler de tareas periodicas (sync mercados, generacion de senales, P&L, alertas). - WebSocket en tiempo real para precios y senales. - Base de datos SQLite con Prisma ORM El frontend **consume datos reales del backend** y tiene **fallback a datos mock** cuando el backend no responde (modo demo sin configuracion). ## Mejoras de utilidad Para que la app sea util mas alla de la demo visual, se han incorporado los siguientes ajustes โ€” todos orientados a que las senales tengan *edge* real y no sean ruido bonito: ### 1. Fetch diversificado por tag (anti-monotonia) El endpoint `/markets` de Polymarket ignora `tag_id` y siempre devuelve la home feed (politica US + World Cup). El cliente usa ahora el endpoint **`/events`** que SI respeta `tag_id`, con un catalogo curado de ~25 tags de alto valor (crypto-prices, fed, stock-market, tech, openai, middle-east, oil-industry, europe, taiwan-election, etc.) y aplana los mercados por tag. Resultado: ~1000 mercados activos diarios distribuidos en 6 categorias (cripto, economia, geopolitica, ciencia, politica, entretenimiento) en lugar de los ~100 dominados por una unica categoria. ### 2. Whitelist de mercados analizables `polymarket.client.js โ†’ isAnalyzable()` flaggea como **no analizables** los mercados donde la IA no tiene edge plausible: - Predicciones de palabras (*"Will Trump say nuclear?"*) - Views de YouTubers, recuentos de tweets - "Before GTA VI"-style memes - Deportes y entretenimiento `signals.service.js` salta la generacion para estos mercados y el frontend pinta el badge **"FUERA DE ALCANCE"** en lugar de fabricar confianza falsa. Asi, cada senal visible es defendible. ### 3. Ground truth de cripto via CoinGecko `utils/coingecko.client.js` resuelve spot prices (BTC, ETH, SOL, DOGE, ADA, XRP) y los inyecta en el prompt de la IA para mercados de precio objetivo: ``` GROUND TRUTH: BTC spot $103,400. Target $150,000 (+45.1% required). Use this to judge whether the implied probability is plausible given typical volatility. ``` Cache TTL 60s โ€” respeta el rate limit gratuito de CoinGecko. ### 4. Edge gap explicito (impliedProb vs fairProb) Cada `AISignal` persiste ahora `impliedProb`, `fairProb` y `edgePoints`. El pipeline mapea `(signal, confidence) โ†’ fairProb`: | Signal | Formula | |--------|---------| | bullish + conf 0.8 | fairProb = 0.5 + 0.8 ร— 0.5 = 0.90 | | bearish + conf 0.8 | fairProb = 0.5 โˆ’ 0.8 ร— 0.5 = 0.10 | | neutral | fairProb = 0.5 | La tarjeta del mercado muestra: `Mercado 65% ยท IA 78% ยท Edge +13pp` โ€” claim cuantitativo en lugar de prosa vaga. ### 5. Spread-aware sizing (Kelly con costes) Polymarket expone `spread`, `bestBid`, `bestAsk` por mercado. `positions/kelly.js โ†’ suggestSize()` resta el spread del edge bruto antes de calcular el tamano de posicion: ``` edgeNeto = |edgePoints/100| - spread fraction = Quarter-Kelly(price, impliedProb + edgeNeto) amount = bankroll * min(0.25, fraction) ``` Mercados con `spread > 5ยข` se marcan como **ilรญquidos** y los botones de compra se desactivan. Endpoint publico: `GET /api/v1/positions/suggestion/:marketId`. ### 6. Distribucion geografica del mapa `map.js` usa **jitter determinista** (hash del marketId โ†’ desplazamiento en bounding-box del pais) para que multiples mercados del mismo pais no se apilen sobre la capital. Mercados sin pais (cripto, indices, AI) se reparten entre **40 hubs financieros** globales (NYC, Sao Paulo, Mumbai, Lagos, Moscu, Yakarta, Sydney, etc.) en vez de caer todos sobre [20,0]. ## Estructura ``` polysignal/ โ”œโ”€โ”€ backend/ # API REST + Servicios + Scheduler โ”‚ โ”œโ”€โ”€ package.json โ”‚ โ”œโ”€โ”€ prisma/ โ”‚ โ”‚ โ”œโ”€โ”€ schema.prisma # Schema SQLite (User, Market, AISignal, Position, Watchlist, Alert) โ”‚ โ”‚ โ”œโ”€โ”€ migrations/ # Migraciones de Prisma โ”‚ โ”‚ โ””โ”€โ”€ seed.js # Usuarios demo (admin + user) โ”‚ โ””โ”€โ”€ src/ โ”‚ โ”œโ”€โ”€ index.js # Entry point: HTTP server + Socket.io + scheduler โ”‚ โ”œโ”€โ”€ app.js # Express: middlewares + rutas + manejo de errores โ”‚ โ”œโ”€โ”€ config.js # Variables de entorno validadas con Zod โ”‚ โ”œโ”€โ”€ scheduler.js # Jobs periodicos (cron): sync, senales IA, PnL, alertas โ”‚ โ”œโ”€โ”€ auth/ # Autenticacion JWT + bcrypt โ”‚ โ”‚ โ”œโ”€โ”€ auth.controller.js โ”‚ โ”‚ โ”œโ”€โ”€ auth.service.js โ”‚ โ”‚ โ”œโ”€โ”€ auth.routes.js โ”‚ โ”‚ โ”œโ”€โ”€ auth.validators.js โ”‚ โ”‚ โ””โ”€โ”€ jwt.js โ”‚ โ”œโ”€โ”€ markets/ # Mercados de Polymarket โ”‚ โ”‚ โ”œโ”€โ”€ markets.controller.js โ”‚ โ”‚ โ”œโ”€โ”€ markets.service.js โ”‚ โ”‚ โ”œโ”€โ”€ markets.routes.js โ”‚ โ”‚ โ”œโ”€โ”€ markets.validators.js โ”‚ โ”‚ โ”œโ”€โ”€ markets.repository.js โ”‚ โ”‚ โ””โ”€โ”€ polymarket.client.js โ”‚ โ”œโ”€โ”€ signals/ # Pipeline de IA (ModernFinBERT + Qwen3-8B) โ”‚ โ”‚ โ”œโ”€โ”€ signals.controller.js โ”‚ โ”‚ โ”œโ”€โ”€ signals.service.js โ”‚ โ”‚ โ”œโ”€โ”€ signals.routes.js โ”‚ โ”‚ โ”œโ”€โ”€ signals.repository.js โ”‚ โ”‚ โ”œโ”€โ”€ aiPipeline.js # Pipeline IA con fallback chain โ”‚ โ”‚ โ””โ”€โ”€ finnhub.client.js # Noticias financieras โ”‚ โ”œโ”€โ”€ positions/ # Simulador de posiciones virtuales โ”‚ โ”‚ โ”œโ”€โ”€ positions.controller.js โ”‚ โ”‚ โ”œโ”€โ”€ positions.service.js โ”‚ โ”‚ โ”œโ”€โ”€ positions.routes.js โ”‚ โ”‚ โ”œโ”€โ”€ positions.validators.js โ”‚ โ”‚ โ”œโ”€โ”€ positions.repository.js โ”‚ โ”‚ โ””โ”€โ”€ kelly.js # Criterio de Kelly (sizing) โ”‚ โ”œโ”€โ”€ watchlist/ # Lista de seguimiento โ”‚ โ”‚ โ”œโ”€โ”€ watchlist.controller.js โ”‚ โ”‚ โ”œโ”€โ”€ watchlist.service.js โ”‚ โ”‚ โ”œโ”€โ”€ watchlist.routes.js โ”‚ โ”‚ โ”œโ”€โ”€ watchlist.validators.js โ”‚ โ”‚ โ””โ”€โ”€ watchlist.repository.js โ”‚ โ”œโ”€โ”€ alerts/ # Alertas por Telegram โ”‚ โ”‚ โ”œโ”€โ”€ alerts.controller.js โ”‚ โ”‚ โ”œโ”€โ”€ alerts.service.js โ”‚ โ”‚ โ”œโ”€โ”€ alerts.routes.js โ”‚ โ”‚ โ”œโ”€โ”€ alerts.repository.js โ”‚ โ”‚ โ””โ”€โ”€ telegram.client.js โ”‚ โ”œโ”€โ”€ middlewares/ # Middlewares reutilizables โ”‚ โ”‚ โ”œโ”€โ”€ validate.js # Validacion Zod generica โ”‚ โ”‚ โ”œโ”€โ”€ requireAuth.js # Autenticacion JWT โ”‚ โ”‚ โ”œโ”€โ”€ rateLimitLogin.js # Rate limit login โ”‚ โ”‚ โ”œโ”€โ”€ errorHandler.js # Manejo centralizado de errores โ”‚ โ”‚ โ””โ”€โ”€ notFound.js # 404 โ”‚ โ”œโ”€โ”€ utils/ # Utilidades compartidas โ”‚ โ”‚ โ”œโ”€โ”€ apiResponse.js # Helpers de respuesta HTTP โ”‚ โ”‚ โ”œโ”€โ”€ httpClient.js # Cliente HTTP con retry + timeout โ”‚ โ”‚ โ”œโ”€โ”€ logger.js # Pino (logs estructurados) โ”‚ โ”‚ โ””โ”€โ”€ prisma.js # Singleton PrismaClient โ”‚ โ””โ”€โ”€ socket/ โ”‚ โ””โ”€โ”€ broadcaster.js # Emisor de eventos Socket.io โ”‚ โ”œโ”€โ”€ frontend/ # SPA Vanilla JS con Vite โ”‚ โ”œโ”€โ”€ index.html # Punto de entrada HTML โ”‚ โ”œโ”€โ”€ package.json โ”‚ โ”œโ”€โ”€ vite.config.js # Proxy a backend + build config โ”‚ โ””โ”€โ”€ src/ โ”‚ โ”œโ”€โ”€ main.js # Entry point de Vite โ”‚ โ”œโ”€โ”€ app.js # Logica principal SPA + Socket.io โ”‚ โ”œโ”€โ”€ api.js # Cliente REST del backend โ”‚ โ”œโ”€โ”€ charts.js # Chart.js (historial + sparklines) โ”‚ โ”œโ”€โ”€ map.js # Leaflet (mapa mundial interactivo) โ”‚ โ”œโ”€โ”€ simulator.js # Simulador de posiciones virtuales โ”‚ โ””โ”€โ”€ style.css # Estilos dark terminal / fintech โ”‚ โ”œโ”€โ”€ spaces/ # HuggingFace Spaces (ZeroGPU) โ”‚ โ”œโ”€โ”€ modernfinbert/ # Space de ModernFinBERT โ”‚ โ”‚ โ”œโ”€โ”€ app.py โ”‚ โ”‚ โ”œโ”€โ”€ requirements.txt โ”‚ โ”‚ โ””โ”€โ”€ README.md โ”‚ โ””โ”€โ”€ qwen3-8b/ # Space de Qwen3-8B โ”‚ โ”œโ”€โ”€ app.py โ”‚ โ”œโ”€โ”€ Dockerfile โ”‚ โ”œโ”€โ”€ requirements.txt โ”‚ โ””โ”€โ”€ README.md โ”‚ โ”œโ”€โ”€ package.json # Root con workspaces + scripts conjuntos โ”œโ”€โ”€ docker-compose.yml # Orquestacion local โ”œโ”€โ”€ Dockerfile # Build para HuggingFace Spaces โ”œโ”€โ”€ .env.example # Variables de entorno de ejemplo โ”œโ”€โ”€ SECURITY_HEALTHCHECK.md # Auditoria de seguridad y arquitectura โ””โ”€โ”€ README.md ``` ## Requisitos - **Node.js >= 26.0.0** - **npm >= 10** (workspaces) ## Instalacion rapida ```bash # 1. Instalar dependencias (root + todos los workspaces) npm install # 2. Configurar variables de entorno cp .env.example .env # Editar .env con tus claves (HF_TOKEN, HF_SPACE_*, OPENROUTER_API_KEY, etc.) # 3. Generar base de datos y cliente Prisma npm run db:migrate npm run db:generate # 4. Iniciar en desarrollo npm run dev:all # Backend + Frontend Vite simultaneamente ``` ## Desarrollo solo frontend Si solo quieres visualizar el dashboard (funciona con datos mock): ```bash cd frontend npm install npm run dev # Abrir http://localhost:5173 ``` El frontend consume datos mock localmente cuando el backend no responde, por lo que el dashboard es totalmente funcional para la demo sin configuracion adicional. ## Arquitectura del Backend El backend sigue una arquitectura **Layered (Controller โ†’ Service โ†’ Repository)**: | Capa | Responsabilidad | Ejemplo | |------|----------------|---------| | **Controller** | Recibir HTTP request, delegar a Service, responder | `markets.controller.js` | | **Service** | Logica de negocio, validaciones, coordinacion | `markets.service.js` | | **Repository** | Acceso a datos via Prisma ORM | `markets.repository.js` | | **Client** | Integracion con APIs externas | `polymarket.client.js`, `finnhub.client.js` | | **Middleware** | Cross-cutting concerns (auth, validacion, rate limiting) | `requireAuth.js`, `validate.js` | ### Pipeline de IA ``` Whitelist analyzable (skip predicciones-de-palabras, sports, memes) โ†“ Noticias (Finnhub) โ†’ relevantes por mercado โ†“ Filtrado (ModernFinBERT Space / API directa) โ†“ (descarta neutrales, score < 0.65) Ground truth crypto (CoinGecko spot โ†’ solo si aplica) โ†“ Generacion de senal (Qwen3-8B Space / API directa) โ†“ Fallback: OpenRouter (deepseek-chat) โ†“ Fallback: Rule-based (precio del mercado) โ†“ Calculo edge: impliedProb vs fairProb โ†’ edgePoints โ†“ Persistencia (SQLite) + Emision Socket.io ``` ### Scheduler (node-cron) | Job | Frecuencia | Descripcion | |-----|-----------|-------------| | syncMarkets | Cada 30s | Sincroniza precios + spread desde Polymarket Gamma (fetch diversificado por tag) | | generateSignals | Cada 5 min | Genera senales IA para 40 mercados diversificados por categoria (solo analyzable=true) | | updatePositionsPnL | Cada 30s | Recalcula P&L de posiciones abiertas | | processAlerts | Cada 60s | Revisa watchlist y envia alertas Telegram | ## Arquitectura del Frontend El frontend es una SPA construida con **Vite 7** como bundler y dev server. ### Caracteristicas visuales - **Estetica dark terminal / fintech:** paleta `#0a0c10`, tipografias `Syne` + `DM Mono`. - **Layout ajustable:** sidebar colapsable, paneles del dashboard colapsables individualmente. - **Mapa global interactivo:** Leaflet con burbujas por pais (tamano = volumen, color = senal IA), jitter determinista para evitar apilamientos y 40 hubs financieros para mercados sin pais. - **Panel de senales IA:** mercados con badges alcista/bajista/neutral, **fila de edge cuantitativa** (`Mercado X% ยท IA Y% ยท Edge ยฑN pp`) y badge **"FUERA DE ALCANCE"** para mercados no analizables. - **Detalle de mercado:** sparklines, historial 7d, analisis IA, simulador de posiciones con **sugerencia de tamano Quarter-Kelly cost-aware** (servida por `GET /positions/suggestion/:marketId`) y deshabilitacion automatica para mercados con spread > 5ยข. - **Vistas adicionales:** Posiciones abiertas, Lista de seguimiento, Historial de alertas. ### Flujo de desarrollo | Servicio | Comando | URL local | |----------|---------|-----------| | Backend (Express + Socket.io) | `npm run dev` | `http://localhost:7860` | | Frontend (Vite + HMR) | `npm run dev:frontend` | `http://localhost:5173` | | **Ambos a la vez** | **`npm run dev:all`** | โ€” | Vite esta configurado con un proxy que redirige automaticamente las peticiones a `/api` y `/socket.io` hacia el backend en el puerto `7860`, eliminando problemas de CORS durante el desarrollo local. ### Scripts disponibles ```bash # Levantar solo el backend npm run dev # Levantar solo el frontend (Vite con hot reload) npm run dev:frontend # Levantar backend y frontend simultaneamente npm run dev:all # Build de produccion del frontend (genera frontend/dist/) npm run build:frontend # Preview del build de produccion npm run preview:frontend # Base de datos npm run db:migrate # Crear/actualizar migraciones npm run db:generate # Generar cliente Prisma npm run db:studio # Explorar BD con Prisma Studio ``` ## Deploy en HuggingFace Spaces 1. Crear Space tipo "Docker" 2. Subir codigo (`git push`) 3. Configurar Secrets en la interfaz de HF con las variables de `.env` 4. El contenedor expone el puerto 7860 automaticamente ### Docker local (opcional) ```bash # Build y run con docker-compose docker-compose up --build # O solo docker build docker build -t polysignal . docker run -p 7860:7860 --env-file .env polysignal ``` ## Variables de entorno ```env # HuggingFace HF_TOKEN= # API key de HuggingFace (Inference API) HF_SPACE_MODERNFINBERT_URL= # URL del Space (ej: usuario/modernfinbert) HF_SPACE_QWEN_URL= # URL del Space (ej: usuario/qwen3-8b) # Fallbacks y datos OPENROUTER_API_KEY= # Fallback LLM si HF esta saturado FINNHUB_API_KEY= # Noticias financieras (finnhub.io) # Base de datos y auth DATABASE_URL=file:./backend/prisma/polysignal.db JWT_SECRET=minimo-32-caracteres # Secreto para firmar JWT # Servidor PORT=7860 # Puerto requerido por HuggingFace Spaces NODE_ENV=production # development | production ``` ## Equipo Hackathon CIFO Barcelona La Violeta โ€” 13-18 mayo 2026 ## Licencia MIT