YAML Metadata Warning:empty or missing yaml metadata in repo card
Check out the documentation for more information.
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
# 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):
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, tipografiasSyne+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
# 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
- Crear Space tipo "Docker"
- Subir codigo (
git push) - Configurar Secrets en la interfaz de HF con las variables de
.env - El contenedor expone el puerto 7860 automaticamente
Docker local (opcional)
# 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
# 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)
# Alertas
TELEGRAM_BOT_TOKEN= # Bot de alertas (@BotFather)
# 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