fix: Total P&L now uses actual account growth instead of API value

- Changed Total P&L display from tradingStats.totalPnL (closed trades only)
  to currentCapital - STARTING_CAPITAL (actual account growth)
- Now matches Net Profit calculation (22 vs previous 77)
- Both metrics now show consistent, accurate profit figures
This commit is contained in:
mindesbunister
2026-01-14 20:32:20 +01:00
parent ed18c9e70f
commit 46c4ee412c
2 changed files with 409 additions and 9 deletions

View File

@@ -40,6 +40,7 @@ export default function ProjectionPage() {
const [selectedScenario, setSelectedScenario] = useState<number>(70)
const [tradingStats, setTradingStats] = useState<TradingStats | null>(null)
const [totalDeposits, setTotalDeposits] = useState<number>(0)
const [btcPrice, setBtcPrice] = useState<number>(100000) // Default, will be fetched
// Strategy Parameters (Jan 2026)
const STARTING_CAPITAL = 1437 // Actual starting balance when v11.2 went live
@@ -59,6 +60,17 @@ export default function ProjectionPage() {
useEffect(() => {
async function fetchData() {
try {
// Fetch current BTC price from CoinGecko
try {
const btcResponse = await fetch('https://api.coingecko.com/api/v3/simple/price?ids=bitcoin&vs_currencies=usd')
const btcData = await btcResponse.json()
if (btcData.bitcoin?.usd) {
setBtcPrice(btcData.bitcoin.usd)
}
} catch (btcError) {
console.error('Failed to fetch BTC price, using default:', btcError)
}
// Fetch account summary
const accountResponse = await fetch('/api/drift/account-summary')
const accountData = await accountResponse.json()
@@ -141,14 +153,35 @@ export default function ProjectionPage() {
return months
}
// Pre-calculate all scenarios
const scenarios: ScenarioResult[] = [
// Calculate current actual monthly rate
const daysElapsed = Math.max(1, Math.floor((new Date().getTime() - START_DATE.getTime()) / (1000 * 60 * 60 * 24)))
const actualMonthlyRate = currentCapital > STARTING_CAPITAL && daysElapsed >= 1
? Math.round((Math.pow(currentCapital / STARTING_CAPITAL, 30 / daysElapsed) - 1) * 100) / 100
: 0
// Pre-calculate all scenarios (including current rate if positive)
const baseScenarios: ScenarioResult[] = [
{ monthlyRate: 0.50, label: '50%/mo (Very Conservative)', finalNetWorth: 0, finalTrading: 0, finalBTC: 0, totalCashOut: 0, color: 'text-gray-400' },
{ monthlyRate: 0.70, label: '70%/mo (Base Case)', finalNetWorth: 0, finalTrading: 0, finalBTC: 0, totalCashOut: 0, color: 'text-green-400' },
{ monthlyRate: 0.90, label: '90%/mo (Optimistic)', finalNetWorth: 0, finalTrading: 0, finalBTC: 0, totalCashOut: 0, color: 'text-blue-400' },
{ monthlyRate: 1.10, label: '110%/mo (Strong)', finalNetWorth: 0, finalTrading: 0, finalBTC: 0, totalCashOut: 0, color: 'text-purple-400' },
{ monthlyRate: 1.50, label: '150%/mo (Exceptional)', finalNetWorth: 0, finalTrading: 0, finalBTC: 0, totalCashOut: 0, color: 'text-yellow-400' },
].map(scenario => {
]
// Add current rate as first scenario if it's a valid positive rate
if (actualMonthlyRate > 0 && !loading) {
baseScenarios.unshift({
monthlyRate: actualMonthlyRate,
label: `${Math.round(actualMonthlyRate * 100)}%/mo (Current Actual)`,
finalNetWorth: 0,
finalTrading: 0,
finalBTC: 0,
totalCashOut: 0,
color: 'text-cyan-400'
})
}
const scenarios = baseScenarios.map(scenario => {
const projection = generateProjection(scenario.monthlyRate)
const final = projection[projection.length - 1]
return {
@@ -172,6 +205,12 @@ export default function ProjectionPage() {
}).format(value)
}
// Format BTC values with both USD and BTC quantity
const formatBTC = (usdValue: number) => {
const btcAmount = usdValue / btcPrice
return `${formatCurrency(usdValue)} / ${btcAmount.toFixed(2)} BTC`
}
return (
<div className="min-h-screen bg-gray-900 text-white p-8">
<div className="max-w-7xl mx-auto">
@@ -265,8 +304,8 @@ export default function ProjectionPage() {
<div className="grid grid-cols-2 md:grid-cols-5 gap-4 mb-6">
<div className="bg-gray-700 rounded p-4">
<div className="text-sm text-gray-400">Total P&L</div>
<div className={`text-2xl font-bold ${tradingStats?.totalPnL && tradingStats.totalPnL >= 0 ? 'text-green-400' : 'text-red-400'}`}>
{loading ? '...' : tradingStats ? formatCurrency(tradingStats.totalPnL) : '$0'}
<div className={`text-2xl font-bold ${currentCapital - STARTING_CAPITAL >= 0 ? 'text-green-400' : 'text-red-400'}`}>
{loading ? '...' : formatCurrency(currentCapital - STARTING_CAPITAL)}
</div>
</div>
<div className="bg-gray-700 rounded p-4">
@@ -347,6 +386,21 @@ export default function ProjectionPage() {
<div className="bg-gray-800 rounded-lg p-6 mb-8 border border-gray-700">
<h2 className="text-xl font-semibold mb-4">🎯 Select Monthly Return Scenario</h2>
<div className="flex flex-wrap gap-2 mb-6">
{/* Current Actual Rate Button */}
{actualMonthlyRate > 0 && !loading && (
<button
onClick={() => setSelectedScenario(Math.round(actualMonthlyRate * 100))}
className={`px-4 py-2 rounded-lg font-semibold transition-all border-2 ${
selectedScenario === Math.round(actualMonthlyRate * 100)
? 'bg-cyan-600 text-white border-cyan-400'
: 'bg-cyan-900/50 text-cyan-300 hover:bg-cyan-800/50 border-cyan-700'
}`}
>
Current: {Math.round(actualMonthlyRate * 100)}%/mo
</button>
)}
{/* Preset Scenarios */}
{[50, 70, 90, 110, 150].map(rate => (
<button
key={rate}
@@ -374,7 +428,7 @@ export default function ProjectionPage() {
</div>
<div>
<div className="text-sm text-gray-400">BTC Holdings</div>
<div className="text-xl font-bold text-orange-400">{formatCurrency(finalMonth.btcHoldings)}</div>
<div className="text-xl font-bold text-orange-400">{formatBTC(finalMonth.btcHoldings)}</div>
</div>
<div>
<div className="text-sm text-gray-400">Cash Withdrawn</div>
@@ -412,7 +466,7 @@ export default function ProjectionPage() {
>
<td className={`py-3 ${scenario.color} font-semibold`}>{scenario.label}</td>
<td className="py-3 text-right">{formatCurrency(scenario.finalTrading)}</td>
<td className="py-3 text-right text-orange-400">{formatCurrency(scenario.finalBTC)}</td>
<td className="py-3 text-right text-orange-400">{formatBTC(scenario.finalBTC)}</td>
<td className="py-3 text-right text-purple-400">{formatCurrency(scenario.totalCashOut)}</td>
<td className="py-3 text-right font-bold">{formatCurrency(scenario.finalNetWorth)}</td>
</tr>
@@ -449,10 +503,10 @@ export default function ProjectionPage() {
{month.withdrawal > 0 ? `-${formatCurrency(month.withdrawal)}` : '-'}
</td>
<td className="py-2 text-right text-orange-400">
{month.btcSweep > 0 ? formatCurrency(month.btcSweep) : '-'}
{month.btcSweep > 0 ? formatBTC(month.btcSweep) : '-'}
</td>
<td className="py-2 text-right">{formatCurrency(month.endCapital)}</td>
<td className="py-2 text-right text-orange-400">{formatCurrency(month.btcHoldings)}</td>
<td className="py-2 text-right text-orange-400">{formatBTC(month.btcHoldings)}</td>
<td className="py-2 text-right font-bold">{formatCurrency(month.netWorth)}</td>
</tr>
))}