226 lines
7.2 KiB
Plaintext
226 lines
7.2 KiB
Plaintext
generator client {
|
|
provider = "prisma-client-js"
|
|
}
|
|
|
|
datasource db {
|
|
provider = "postgresql"
|
|
url = env("DATABASE_URL")
|
|
}
|
|
|
|
model User {
|
|
id String @id @default(cuid())
|
|
username String @unique // lowercase, used for URLs/lookups
|
|
displayUsername String? // original casing chosen by user
|
|
passwordHash String
|
|
balance Float @default(2000)
|
|
researchPoints Int @default(1)
|
|
isAdmin Boolean @default(false)
|
|
lastLotteryAt DateTime? // tracks daily lottery cooldown
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
isFund Boolean @default(false)
|
|
isHidden Boolean @default(false) // hidden from leaderboards and public listings
|
|
|
|
positions Position[]
|
|
trades Trade[]
|
|
passwordResets PasswordReset[]
|
|
managedFunds FundManager[]
|
|
fund HedgeFund?
|
|
fundInvestments FundInvestment[]
|
|
portfolioHistory UserPortfolioHistory[]
|
|
fundApplication FundApplication?
|
|
}
|
|
|
|
model HedgeFund {
|
|
id String @id @default(cuid())
|
|
name String @unique
|
|
slug String @unique // lowercase, URL-safe
|
|
userId String @unique // shadow User account that holds positions/trades/balance
|
|
user User @relation(fields: [userId], references: [id])
|
|
sharesOutstanding Float @default(0) // total fund shares currently in circulation
|
|
createdAt DateTime @default(now())
|
|
|
|
managers FundManager[]
|
|
investments FundInvestment[]
|
|
navHistory FundNavHistory[]
|
|
trades Trade[]
|
|
|
|
@@index([slug])
|
|
}
|
|
|
|
model FundInvestment {
|
|
id String @id @default(cuid())
|
|
fundId String
|
|
fund HedgeFund @relation(fields: [fundId], references: [id], onDelete: Cascade)
|
|
userId String
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
shares Float // fund shares held by this investor
|
|
avgNavAtBuy Float // NAV per share at time of purchase (for display only)
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@unique([fundId, userId])
|
|
@@index([userId])
|
|
}
|
|
|
|
model FundManager {
|
|
id String @id @default(cuid())
|
|
fundId String
|
|
fund HedgeFund @relation(fields: [fundId], references: [id], onDelete: Cascade)
|
|
userId String
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
addedAt DateTime @default(now())
|
|
|
|
@@unique([fundId, userId])
|
|
}
|
|
|
|
model PasswordReset {
|
|
id String @id @default(cuid())
|
|
token String @unique @default(uuid())
|
|
userId String
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
expiresAt DateTime
|
|
used Boolean @default(false)
|
|
createdAt DateTime @default(now())
|
|
|
|
@@index([token])
|
|
}
|
|
|
|
model Hashtag {
|
|
id String @id @default(cuid())
|
|
tag String @unique // lowercase, no #
|
|
displayTag String // original case as entered
|
|
currentPrice Float @default(0.25)
|
|
isActive Boolean @default(true)
|
|
isBanned Boolean @default(false)
|
|
// Consecutive zero-result count (informational)
|
|
zeroCount Int @default(0)
|
|
// Earliest time this hashtag can be deactivated (set on research + when last position closes)
|
|
activeUntil DateTime?
|
|
lastUpdated DateTime @default(now())
|
|
createdAt DateTime @default(now())
|
|
|
|
priceHistory PriceHistory[]
|
|
positions Position[]
|
|
trades Trade[]
|
|
relatedFrom RelatedHashtag[] @relation("RelatedFrom")
|
|
relatedTo RelatedHashtag[] @relation("RelatedTo")
|
|
|
|
@@index([isActive, lastUpdated])
|
|
}
|
|
|
|
model RelatedHashtag {
|
|
id String @id @default(cuid())
|
|
hashtagId String
|
|
hashtag Hashtag @relation("RelatedFrom", fields: [hashtagId], references: [id], onDelete: Cascade)
|
|
relatedTag String // lowercase tag name (may not yet be in Hashtag table)
|
|
relatedId String? // set if the related hashtag exists in the DB
|
|
related Hashtag? @relation("RelatedTo", fields: [relatedId], references: [id], onDelete: SetNull)
|
|
coOccurrences Int @default(1)
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@unique([hashtagId, relatedTag])
|
|
@@index([hashtagId, coOccurrences])
|
|
}
|
|
|
|
model PriceHistory {
|
|
id String @id @default(cuid())
|
|
hashtagId String
|
|
hashtag Hashtag @relation(fields: [hashtagId], references: [id], onDelete: Cascade)
|
|
price Float
|
|
postsPerHour Float
|
|
recordedAt DateTime @default(now())
|
|
|
|
@@index([hashtagId, recordedAt])
|
|
}
|
|
|
|
model FundNavHistory {
|
|
id String @id @default(cuid())
|
|
fundId String
|
|
fund HedgeFund @relation(fields: [fundId], references: [id], onDelete: Cascade)
|
|
nav Float
|
|
totalValue Float
|
|
recordedAt DateTime @default(now())
|
|
|
|
@@index([fundId, recordedAt])
|
|
}
|
|
|
|
model UserPortfolioHistory {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
totalValue Float
|
|
portfolioValue Float
|
|
recordedAt DateTime @default(now())
|
|
|
|
@@index([userId, recordedAt])
|
|
}
|
|
|
|
model Position {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
hashtagId String
|
|
hashtag Hashtag @relation(fields: [hashtagId], references: [id])
|
|
shares Float @default(0)
|
|
positionType PositionType
|
|
avgBuyPrice Float
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
|
|
@@unique([userId, hashtagId, positionType])
|
|
@@index([userId])
|
|
@@index([hashtagId])
|
|
}
|
|
|
|
model Trade {
|
|
id String @id @default(cuid())
|
|
userId String
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
hashtagId String?
|
|
hashtag Hashtag? @relation(fields: [hashtagId], references: [id])
|
|
fundId String?
|
|
fund HedgeFund? @relation(fields: [fundId], references: [id], onDelete: SetNull)
|
|
type TradeType
|
|
shares Float
|
|
price Float // price per share at time of trade (or win amount for LOTTERY_WIN)
|
|
total Float // cost/proceeds of the trade
|
|
profit Float @default(0) // realized P&L (for SELL trades and LOTTERY_WIN)
|
|
createdAt DateTime @default(now())
|
|
|
|
@@index([userId])
|
|
@@index([hashtagId])
|
|
@@index([fundId])
|
|
@@index([createdAt])
|
|
}
|
|
|
|
enum PositionType {
|
|
LONG
|
|
SHORT
|
|
}
|
|
|
|
enum TradeType {
|
|
BUY_LONG
|
|
SELL_LONG
|
|
BUY_SHORT
|
|
SELL_SHORT
|
|
LOTTERY_WIN
|
|
LIQUIDATE_LONG
|
|
LIQUIDATE_SHORT
|
|
DONATION // keepHistory reset: user was in the green — donated their portfolio
|
|
BANKRUPTCY // keepHistory reset: user was in the red — debts cleared
|
|
ACCOUNT_OPEN // keepHistory reset: new $2000 account opening entry
|
|
FUND_INVEST // invested cash into a hedge fund
|
|
FUND_REDEEM // redeemed shares from a hedge fund
|
|
}
|
|
|
|
model FundApplication {
|
|
id String @id @default(cuid())
|
|
userId String @unique
|
|
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
fundName String
|
|
reason String
|
|
createdAt DateTime @default(now())
|
|
}
|