feat: p&l label in portfolio + fixed unnecessary calls to /total
This commit is contained in:
parent
8cba222fe2
commit
99614f853e
10 changed files with 172 additions and 53 deletions
51
website/src/routes/api/portfolio/summary/+server.ts
Normal file
51
website/src/routes/api/portfolio/summary/+server.ts
Normal 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: '$'
|
||||
});
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
Reference in a new issue