feat: p&l label in portfolio + fixed unnecessary calls to /total

This commit is contained in:
Face 2025-06-11 11:06:05 +03:00
parent 8cba222fe2
commit 99614f853e
10 changed files with 172 additions and 53 deletions

View file

@ -0,0 +1,51 @@
import { auth } from '$lib/auth';
import { error, json } from '@sveltejs/kit';
import { db } from '$lib/server/db';
import { user, userPortfolio, coin } from '$lib/server/db/schema';
import { eq } from 'drizzle-orm';
export async function GET({ request }) {
const session = await auth.api.getSession({ headers: request.headers });
if (!session?.user) {
throw error(401, 'Not authenticated');
}
const userId = Number(session.user.id);
const [userData, holdings] = await Promise.all([
db.select({ baseCurrencyBalance: user.baseCurrencyBalance })
.from(user)
.where(eq(user.id, userId))
.limit(1),
db.select({
quantity: userPortfolio.quantity,
currentPrice: coin.currentPrice
})
.from(userPortfolio)
.innerJoin(coin, eq(userPortfolio.coinId, coin.id))
.where(eq(userPortfolio.userId, userId))
]);
if (!userData[0]) {
throw error(404, 'User not found');
}
let totalCoinValue = 0;
for (const holding of holdings) {
const quantity = Number(holding.quantity);
const price = Number(holding.currentPrice);
totalCoinValue += quantity * price;
}
const baseCurrencyBalance = Number(userData[0].baseCurrencyBalance);
return json({
baseCurrencyBalance,
totalCoinValue,
totalValue: baseCurrencyBalance + totalCoinValue,
currency: '$'
});
}

View file

@ -1,8 +1,8 @@
import { auth } from '$lib/auth';
import { error, json } from '@sveltejs/kit';
import { db } from '$lib/server/db';
import { user, userPortfolio, coin } from '$lib/server/db/schema';
import { eq } from 'drizzle-orm';
import { user, userPortfolio, coin, transaction } from '$lib/server/db/schema';
import { eq, and, sql } from 'drizzle-orm';
export async function GET({ request }) {
const session = await auth.api.getSession({ headers: request.headers });
@ -24,7 +24,8 @@ export async function GET({ request }) {
currentPrice: coin.currentPrice,
symbol: coin.symbol,
icon: coin.icon,
change24h: coin.change24h
change24h: coin.change24h,
coinId: coin.id
})
.from(userPortfolio)
.innerJoin(coin, eq(userPortfolio.coinId, coin.id))
@ -37,21 +38,47 @@ export async function GET({ request }) {
let totalCoinValue = 0;
const coinHoldings = holdings.map(holding => {
const coinHoldings = await Promise.all(holdings.map(async (holding) => {
const quantity = Number(holding.quantity);
const price = Number(holding.currentPrice);
const value = quantity * price;
totalCoinValue += value;
// Calculate average purchase price from buy transactions
const avgPriceResult = await db.select({
avgPrice: sql<number>`
CASE
WHEN SUM(${transaction.quantity}) > 0
THEN SUM(${transaction.totalBaseCurrencyAmount}) / SUM(${transaction.quantity})
ELSE 0
END
`
})
.from(transaction)
.where(
and(
eq(transaction.userId, userId),
eq(transaction.coinId, holding.coinId),
eq(transaction.type, 'BUY')
)
);
const avgPurchasePrice = Number(avgPriceResult[0]?.avgPrice || 0);
const percentageChange = avgPurchasePrice > 0
? ((price - avgPurchasePrice) / avgPurchasePrice) * 100
: 0;
return {
symbol: holding.symbol,
icon: holding.icon,
quantity,
currentPrice: price,
value,
change24h: Number(holding.change24h)
change24h: Number(holding.change24h),
avgPurchasePrice,
percentageChange
};
});
}));
const baseCurrencyBalance = Number(userData[0].baseCurrencyBalance);