File size: 4,459 Bytes
f4efb0e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
/**
 * 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])
}