File size: 5,222 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
/**
 * Schema de Prisma ORM para base de datos SQLite.
 *
 * Define 6 modelos: User, Market, AISignal, Position, Watchlist, Alert.
 * Relaciones:
 *   - Market 1:N AISignal, Position, Watchlist, Alert
 *   - User   1:N Position, Watchlist, Alert
 *
 * No modificar sin consenso del equipo. Generar migraciones con:
 *   npx prisma migrate dev
 *   npx prisma generate
 */

generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "sqlite"
  url      = env("DATABASE_URL")
}

model User {
  id             Int        @id @default(autoincrement())
  email          String     @unique
  passwordHash   String
  isActive       Boolean    @default(true)
  telegramChatId String?                         // Configurado manualmente para demo
  createdAt      DateTime   @default(now())
  updatedAt      DateTime   @updatedAt

  positions Position[]
  watchlist Watchlist[]
  alerts    Alert[]
}

model Market {
  id           String     @id                // ID nativo de Polymarket
  question     String                         // Texto de la pregunta del mercado
  category     String?                        // politics | crypto | economics | sports
  countryCode  String?                        // ISO2 — usado por Leaflet para burbujas
  yesPrice     Float?                         // Precio YES: 0.0 a 1.0
  noPrice      Float?                         // Precio NO: 0.0 a 1.0
  volumeEur    Float?                         // Volumen en Eur
  liquidityEur Float?                         // Liquidez en Eur
  spread       Float?                         // Bid/ask spread (0-1, ej 0.02 = 2c)
  bestBid      Float?                         // Mejor oferta de compra
  bestAsk      Float?                         // Mejor oferta de venta
  clobTokenId  String?                        // YES outcome CLOB token ID (para prices-history)
  analyzable   Boolean    @default(true)      // Si la IA tiene edge plausible aqui
  status       String     @default("active")  // active | closed | resolved
  closesAt     DateTime?                      // Fecha de cierre del mercado
  lastSynced   DateTime   @default(now())     // Ultima sincronizacion de precios

  signals   AISignal[]
  positions Position[]
  watchlist Watchlist[]
  alerts    Alert[]
}

model AISignal {
  id           Int      @id @default(autoincrement())
  marketId     String
  market       Market   @relation(fields: [marketId], references: [id], onDelete: Cascade)
  signal       String                          // bullish | bearish | neutral
  confidence   Float                           // 0.0 a 1.0
  summary      String?                         // 2 frases generadas por Qwen3
  keyRisk      String?                         // 1 frase de riesgo principal
  newsCount    Int      @default(0)            // Titulares relevantes usados
  modelVersion String   @default("Qwen3-8B")   // Modelo LLM que genero la senal
  impliedProb  Float?                          // Probabilidad implicita YES al generar (0-1)
  fairProb     Float?                          // Probabilidad "justa" segun IA (0-1)
  edgePoints   Float?                          // (fairProb - impliedProb) * 100, signo conserva direccion
  generatedAt  DateTime @default(now())

  @@index([marketId, generatedAt])
}

model Position {
  id            Int      @id @default(autoincrement())
  userId        Int
  user          User     @relation(fields: [userId], references: [id], onDelete: Cascade)
  marketId      String
  market        Market   @relation(fields: [marketId], references: [id], onDelete: Cascade)
  outcome       String                          // YES | NO
  amountEur     Float                           // Capital virtual apostado
  entryPrice    Float                           // Precio al abrir la posicion
  currentPrice  Float?                          // Precio actual (actualizado por scheduler)
  pnl           Float    @default(0)            // Profit and Loss calculado
  kellyFraction Float?                          // Fraccion de Kelly al abrir
  status        String   @default("open")       // open | closed
  openedAt      DateTime @default(now())
  closedAt      DateTime?

  @@index([userId, status])
  @@index([marketId])
}

model Watchlist {
  id              Int      @id @default(autoincrement())
  userId          Int
  user            User     @relation(fields: [userId], references: [id], onDelete: Cascade)
  marketId        String
  market          Market   @relation(fields: [marketId], references: [id], onDelete: Cascade)
  alertThreshold  Float?                       // Umbral de precio para alerta Telegram
  createdAt       DateTime @default(now())

  @@unique([userId, marketId])
  @@index([userId])
}

model Alert {
  id       Int      @id @default(autoincrement())
  userId   Int
  user     User     @relation(fields: [userId], references: [id], onDelete: Cascade)
  marketId String
  market   Market   @relation(fields: [marketId], references: [id], onDelete: Cascade)
  type     String                          // price_threshold | signal_change
  message  String                          // Texto enviado por Telegram
  sentAt   DateTime @default(now())

  @@index([userId, sentAt])
  @@index([marketId])
}