Spaces:
Sleeping
Sleeping
| 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 | |