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:
@@ -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>
|
||||
))}
|
||||
|
||||
Reference in New Issue
Block a user