feat(ledger): 重构盈亏计算引擎,实装摊薄/平均成本双重指标与持仓天数统计
This commit is contained in:
+49
-21
@@ -15,11 +15,13 @@ const CHART_COLORS = [
|
||||
];
|
||||
|
||||
export default async function DashboardPage() {
|
||||
const { positions, totalCnyValue, chartData, totalPnlCny } = await getPortfolioSummary();
|
||||
const { positions, totalCnyValue, chartData, totalPnlCny, unrealizedPnlCny } = await getPortfolioSummary();
|
||||
|
||||
const formattedTotal = formatAmount(totalCnyValue);
|
||||
const formattedPnl = formatAmount(totalPnlCny);
|
||||
const pnlIsPositive = new Big(totalPnlCny).gte(0);
|
||||
const formattedTotalPnl = formatAmount(totalPnlCny);
|
||||
const formattedUnrealized = formatAmount(unrealizedPnlCny);
|
||||
const totalPnlIsPositive = new Big(totalPnlCny).gte(0);
|
||||
const unrealizedIsPositive = new Big(unrealizedPnlCny).gte(0);
|
||||
|
||||
const displayChartData = chartData.map((item) => ({
|
||||
...item,
|
||||
@@ -50,11 +52,19 @@ export default async function DashboardPage() {
|
||||
总资产 (CNY)
|
||||
</span>
|
||||
</div>
|
||||
<div className="mt-2 flex items-center gap-2">
|
||||
<span className="text-sm text-muted-foreground">总盈亏:</span>
|
||||
<span className={`text-lg font-semibold ${pnlIsPositive ? 'text-green-500' : 'text-red-500'}`}>
|
||||
{pnlIsPositive ? '+' : ''}{formattedPnl}
|
||||
</span>
|
||||
<div className="mt-3 flex flex-wrap gap-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-sm text-muted-foreground">持仓盈亏:</span>
|
||||
<span className={`text-lg font-semibold ${unrealizedIsPositive ? 'text-green-500' : 'text-red-500'}`}>
|
||||
{unrealizedIsPositive ? '+' : ''}{formattedUnrealized}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-sm text-muted-foreground">总盈亏:</span>
|
||||
<span className={`text-lg font-semibold ${totalPnlIsPositive ? 'text-green-500' : 'text-red-500'}`}>
|
||||
{totalPnlIsPositive ? '+' : ''}{formattedTotalPnl}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
@@ -72,6 +82,12 @@ export default async function DashboardPage() {
|
||||
const posPnlPositive = posPnl.gte(0);
|
||||
const formattedPosPnl = formatAmount(pos.pnlCny);
|
||||
|
||||
const posPnlNative = new Big(pos.pnlNative);
|
||||
const posPnlNativePositive = posPnlNative.gte(0);
|
||||
|
||||
const avgCostFormatted = new Big(pos.avgCost).gt(0) ? formatAmount(pos.avgCost) : '-';
|
||||
const dilutedCostFormatted = new Big(pos.dilutedCost).gt(0) ? formatAmount(pos.dilutedCost) : '-';
|
||||
|
||||
return (
|
||||
<Card key={pos.assetId}>
|
||||
<CardHeader>
|
||||
@@ -97,24 +113,36 @@ export default async function DashboardPage() {
|
||||
<span className="text-muted-foreground">结算币种</span>
|
||||
<span className="font-medium">{pos.baseCurrency}</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span className="text-muted-foreground">平均成本</span>
|
||||
<span className="font-medium">
|
||||
¥{avgCostFormatted}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span className="text-muted-foreground">摊薄成本</span>
|
||||
<span className="font-medium">
|
||||
¥{dilutedCostFormatted}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span className="text-muted-foreground">持仓天数</span>
|
||||
<span className="font-medium">
|
||||
{pos.holdingDays} 天
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span className="text-muted-foreground">持仓成本 ({pos.baseCurrency})</span>
|
||||
<span className="font-medium">
|
||||
{formatAmount(pos.totalCostNative)}
|
||||
</span>
|
||||
</div>
|
||||
{(() => {
|
||||
const posPnlNative = new Big(pos.pnlNative);
|
||||
const posPnlNativePositive = posPnlNative.gte(0);
|
||||
return (
|
||||
<div className="flex justify-between">
|
||||
<span className="text-muted-foreground">当前盈亏 ({pos.baseCurrency})</span>
|
||||
<span className={`font-semibold ${posPnlNativePositive ? 'text-green-500' : 'text-red-500'}`}>
|
||||
{posPnlNativePositive ? '+' : ''}{formatAmount(pos.pnlNative)}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
})()}
|
||||
<div className="flex justify-between">
|
||||
<span className="text-muted-foreground">当前盈亏 ({pos.baseCurrency})</span>
|
||||
<span className={`font-semibold ${posPnlNativePositive ? 'text-green-500' : 'text-red-500'}`}>
|
||||
{posPnlNativePositive ? '+' : ''}{formatAmount(pos.pnlNative)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex justify-between opacity-50">
|
||||
<span className="text-muted-foreground">成本 (CNY)</span>
|
||||
<span className="font-medium">
|
||||
@@ -124,7 +152,7 @@ export default async function DashboardPage() {
|
||||
<div className="flex justify-between opacity-50">
|
||||
<span className="text-muted-foreground">盈亏 (CNY)</span>
|
||||
<span className={`font-semibold ${posPnlPositive ? 'text-green-500' : 'text-red-500'}`}>
|
||||
{posPnlPositive ? '+' : ''}{formatAmount(pos.pnlCny)}
|
||||
{posPnlPositive ? '+' : ''}{formattedPosPnl}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user