polysignal_hackaton / README.md
blackmistcode's picture
Add files using upload-large-folder tool
5160d3e verified
|
Raw
History Blame Contribute Delete
15.8 kB
# 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)
# 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