File size: 5,375 Bytes
71b8eb2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

**PolySignal** β€” a real-time prediction market intelligence dashboard. It fetches market data from Polymarket, enriches it with financial news via Finnhub, runs AI sentiment analysis (ModernFinBERT + Qwen3-8B), and displays signals on an interactive world map. No real trading β€” pure analysis and virtual simulation. Built for CIFO Barcelona La Violeta Hackathon (May 2026). UI and docs are in **Spanish**, base currency is **Euro (€)**.

## Common Commands

```bash
# Development (backend + frontend simultaneously)
npm run dev:all

# Backend only (with --watch)
npm run dev

# Frontend only (Vite @ :5173)
npm run dev:frontend

# Production build (frontend)
npm run build:frontend

# Database
npm run db:migrate    # Apply Prisma migrations
npm run db:generate   # Regenerate Prisma client
npm run db:studio     # Open Prisma Studio GUI

# Docker
docker-compose up --build   # Full stack on port 7860
```

Node.js **β‰₯24.0.0** is required (enforced in package.json engines).

First-time setup:
```bash
npm install
cp .env.example .env   # then fill in API keys
npm run db:migrate && npm run db:generate
```

## Architecture

Monorepo with two workspaces: `backend/` (Express 5 + Socket.io) and `frontend/` (Vanilla JS + Vite).

### Backend (`backend/src/`)

Follows a strict **Controller β†’ Service β†’ Repository β†’ Client** layered pattern. Each domain lives in its own directory:

| Directory | Responsibility |
|-----------|---------------|
| `markets/` | Polymarket Gamma API client + sync job |
| `signals/` | AI pipeline (aiPipeline.js) + signals service |
| `finnhub/` | Finnhub news client + service |
| `positions/` | Virtual position simulator + Kelly Criterion sizing |
| `watchlist/` | User-saved markets |
| `alerts/` | Telegram Bot notification delivery |
| `auth/` | JWT + bcryptjs login |
| `socket/` | Socket.io broadcaster |
| `middlewares/` | Auth, validation, error handling, rate limiting |
| `utils/` | Pino logger, HTTP client, Prisma singleton |

Entry points: `src/index.js` (HTTP server + Socket.io setup) β†’ `src/app.js` (Express middleware + route mounting) β†’ `src/scheduler.js` (cron jobs).

Environment/config validated at startup via **Zod** in `src/config.js` β€” the app crashes fast if required env vars are missing.

### Scheduler Jobs

| Job | Frequency | What it does |
|-----|-----------|-------------|
| `syncMarkets` | 30s | Fetches top 100 active Polymarket markets, broadcasts price changes via Socket.io |
| `generateSignals` | 5m | Runs full AI pipeline on top 20 active markets |
| `updatePositionsPnL` | 30s | Recalculates P&L for open virtual positions |
| `processAlerts` | 1m | Checks watchlist thresholds, fires Telegram alerts |

### AI Signal Pipeline (`signals/aiPipeline.js`)

Three-phase flow with automatic fallbacks:

1. **News filtering** β€” Finnhub headlines β†’ ModernFinBERT (HF Space) β†’ keep only scores β‰₯ 0.65, drop neutral
2. **Signal generation** β€” market data + filtered news β†’ Qwen3-8B (HF Space) β†’ `{ signal, confidence, summary, keyRisk }`
3. **Fallback chain**: HF Space β†’ HF direct inference API β†’ OpenRouter (deepseek-chat) β†’ rule-based (price-trend logic)

### Frontend (`frontend/src/`)

Single-page app with no framework. Key modules:

| File | Role |
|------|------|
| `app.js` | SPA routing, DOM rendering, Socket.io listeners |
| `api.js` | REST client wrapper with JWT token management |
| `map.js` | Leaflet world map (bubble size = volume, color = signal) |
| `charts.js` | Chart.js sparklines + 7-day price history |
| `simulator.js` | Virtual buy/sell logic |
| `filters.js` | Market filtering by category, country, continent, trend |

Vite proxies `/api` and `/socket.io` to backend (`localhost:7860`) during development.

### Database (SQLite via Prisma)

Schema at `backend/prisma/schema.prisma`. Key models:
- `Market` β€” Polymarket data (prices, volume, category, country code)
- `AISignal` β€” sentiment signals with confidence and risk summary
- `Position` β€” virtual trades with entry/exit prices and P&L
- `Watchlist` / `Alert` β€” user market tracking and Telegram history
- `User` β€” auth + optional Telegram chat ID

### Real-time Communication

REST API at `/api/v1/*` + WebSocket events via Socket.io:
- `market_update` β€” price/volume changes
- `ai_signal` β€” new sentiment signals
- `price_alert` β€” watchlist threshold triggers

## Deployment Target

The app is designed to run on **HuggingFace Spaces** (port 7860). The Dockerfile uses `node:22-slim`, installs backend deps, copies the frontend `dist/`, runs Prisma generate, and starts the server. The frontend is served as static files by Express in production.

## Key Environment Variables

See `.env.example` for the full list. Critical ones:

```
HF_SPACE_MODERNFINBERT_URL   # HuggingFace Space for FinBERT
HF_SPACE_QWEN_URL            # HuggingFace Space for Qwen3-8B
HF_TOKEN                     # HF inference API key (fallback)
OPENROUTER_API_KEY           # LLM fallback if HF is down
FINNHUB_API_KEY              # News source
TELEGRAM_BOT_TOKEN           # Alert delivery
JWT_SECRET                   # Must be β‰₯32 characters
PORT=7860                    # Required by HF Spaces
DATABASE_URL=file:./backend/prisma/polysignal.db
```