feat(dashboard): 构建基于 Big.js 的持仓聚合引擎与总览卡片
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
'use server';
|
||||
|
||||
import { db } from '@/db';
|
||||
import { transactions, assets } from '@/db/schema';
|
||||
import Big from 'big.js';
|
||||
import { desc } from 'drizzle-orm';
|
||||
|
||||
export async function getPortfolioPositions() {
|
||||
const allTransactions = await db
|
||||
.select({
|
||||
txType: transactions.txType,
|
||||
quantity: transactions.quantity,
|
||||
assetId: transactions.assetId,
|
||||
assetSymbol: assets.symbol,
|
||||
assetType: assets.type,
|
||||
assetBaseCurrency: assets.baseCurrency,
|
||||
})
|
||||
.from(transactions)
|
||||
.leftJoin(assets, assets.id.eq(transactions.assetId))
|
||||
.orderBy(desc(transactions.executedAt));
|
||||
|
||||
const holdings = new Map<string, {
|
||||
assetId: string;
|
||||
symbol: string;
|
||||
type: string;
|
||||
quantity: Big;
|
||||
baseCurrency: string;
|
||||
}>();
|
||||
|
||||
for (const tx of allTransactions) {
|
||||
if (!tx.assetId) continue;
|
||||
|
||||
const existing = holdings.get(tx.assetId);
|
||||
if (!existing) {
|
||||
holdings.set(tx.assetId, {
|
||||
assetId: tx.assetId,
|
||||
symbol: tx.assetSymbol || tx.assetId,
|
||||
type: tx.assetType || 'CASH',
|
||||
quantity: new Big('0'),
|
||||
baseCurrency: tx.assetBaseCurrency || '',
|
||||
});
|
||||
}
|
||||
|
||||
const holding = holdings.get(tx.assetId)!;
|
||||
|
||||
if (tx.txType === 'BUY' || tx.txType === 'AIRDROP') {
|
||||
holding.quantity = holding.quantity.plus(tx.quantity);
|
||||
} else if (tx.txType === 'SELL') {
|
||||
holding.quantity = holding.quantity.minus(tx.quantity);
|
||||
} else if (tx.txType === 'DIVIDEND') {
|
||||
holding.quantity = holding.quantity.plus(tx.quantity);
|
||||
}
|
||||
}
|
||||
|
||||
const result = [];
|
||||
for (const [_, holding] of holdings) {
|
||||
if (holding.quantity.lte(0)) continue;
|
||||
result.push({
|
||||
assetId: holding.assetId,
|
||||
symbol: holding.symbol,
|
||||
type: holding.type,
|
||||
quantity: holding.quantity.toString(),
|
||||
baseCurrency: holding.baseCurrency,
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
Reference in New Issue
Block a user