fix: implement fund dissolution for insolvent funds after SELL_SHORT trades
Build Images and Deploy / Update-PROD-Stack (push) Successful in 1m21s

This commit is contained in:
2026-03-20 14:37:17 -04:00
parent 682c76128c
commit c1bcac8a30
2 changed files with 65 additions and 2 deletions
+1 -1
View File
@@ -112,7 +112,7 @@ export async function DELETE(
const portfolioValue = fund.user.positions.reduce((sum, p) => {
const val = p.positionType === 'LONG'
? p.shares * p.hashtag.currentPrice
: p.avgBuyPrice * p.shares - (p.hashtag.currentPrice - p.avgBuyPrice) * p.shares
: (2 * p.avgBuyPrice - p.hashtag.currentPrice) * p.shares
return sum + val
}, 0)
const nav = calcFundNav(fund.user.balance + portfolioValue, fund.sharesOutstanding)
+64 -1
View File
@@ -2,7 +2,7 @@ import { NextRequest, NextResponse } from 'next/server'
import { getServerSession } from 'next-auth'
import { authOptions } from '@/lib/auth'
import { prisma } from '@/lib/prisma'
import { calcTrade } from '@/lib/pricing'
import { calcTrade, calcFundNav } from '@/lib/pricing'
import { formatCurrency } from '@/lib/utils'
import { z } from 'zod'
@@ -169,5 +169,68 @@ export async function POST(req: NextRequest) {
}
}
// If this was a fund's SELL_SHORT and the fund is now insolvent, dissolve it
if (type === 'SELL_SHORT' && fundId) {
const updatedFundUser = await prisma.user.findUnique({
where: { id: user.id },
select: { balance: true, fund: { select: { id: true } } },
})
if (updatedFundUser && updatedFundUser.balance < 0 && updatedFundUser.fund) {
await dissolveFund(updatedFundUser.fund.id)
return NextResponse.json({ ok: true, dissolved: true })
}
}
return NextResponse.json({ ok: true })
}
/**
* Dissolves an insolvent fund: pays investors at current mark-to-market NAV
* (floored to $0), then deletes the HedgeFund record and its shadow User.
* Called automatically when a SELL_SHORT trade pushes a fund balance negative.
*/
async function dissolveFund(fundId: string) {
const fund = await prisma.hedgeFund.findUnique({
where: { id: fundId },
select: {
id: true,
userId: true,
sharesOutstanding: true,
investments: { select: { userId: true, shares: true } },
user: {
select: {
balance: true,
positions: {
where: { shares: { gt: 0 } },
select: {
shares: true,
avgBuyPrice: true,
positionType: true,
hashtag: { select: { currentPrice: true } },
},
},
},
},
},
})
if (!fund) return
const portfolioValue = fund.user.positions.reduce((sum, p) => {
const val =
p.positionType === 'LONG'
? p.shares * p.hashtag.currentPrice
: (2 * p.avgBuyPrice - p.hashtag.currentPrice) * p.shares
return sum + val
}, 0)
const nav = calcFundNav(fund.user.balance + portfolioValue, fund.sharesOutstanding)
for (const inv of fund.investments) {
const payout = Math.max(0, inv.shares * nav)
if (payout > 0) {
await prisma.user.update({ where: { id: inv.userId }, data: { balance: { increment: payout } } })
}
}
await prisma.hedgeFund.delete({ where: { id: fund.id } })
await prisma.user.delete({ where: { id: fund.userId } })
}