Files
trading_bot_v4/app/projection/page.tsx
mindesbunister 46c4ee412c 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
2026-01-14 20:32:20 +01:00

566 lines
26 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use client'
import { useEffect, useState } from 'react'
interface ProjectionMonth {
month: number
date: string
startCapital: number
profit: number
withdrawal: number
btcSweep: number
endCapital: number
btcHoldings: number
totalCashOut: number
netWorth: number
}
interface ScenarioResult {
monthlyRate: number
label: string
finalNetWorth: number
finalTrading: number
finalBTC: number
totalCashOut: number
color: string
}
interface TradingStats {
totalPnL: number
totalTrades: number
winners: number
losers: number
winRate: number
monthlyPnL: { month: string; pnl: number; trades: number }[]
}
export default function ProjectionPage() {
const [currentCapital, setCurrentCapital] = useState<number>(0)
const [loading, setLoading] = useState(true)
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
const TRADING_CAP = 50000
const BTC_MONTHLY_GROWTH = 0.05 // 5% per month (60% annual)
const START_DATE = new Date('2026-01-06') // When v11.2 went live
// Tiered Withdrawal Rules
const getWithdrawal = (capital: number): number => {
if (capital >= 30000) return 1200
if (capital >= 10000) return 600
if (capital >= 3000) return 300
if (capital >= 1500) return 100
return 0
}
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()
if (accountData.success) {
setCurrentCapital(accountData.drift?.freeCollateral || accountData.freeCollateral || 0)
// Total deposits from Drift
if (accountData.spotPositions?.usdc?.cumulativeDeposits) {
setTotalDeposits(parseInt(accountData.spotPositions.usdc.cumulativeDeposits) / 1e6)
}
}
// Fetch trading stats (all time, not just 30 days)
const statsResponse = await fetch('/api/analytics/stats?days=365')
const statsData = await statsResponse.json()
if (statsData.success && statsData.stats) {
const stats = statsData.stats.realTrades
setTradingStats({
totalPnL: parseFloat(stats.totalPnL?.replace('$', '') || '0'),
totalTrades: stats.total || 0,
winners: stats.winning || 0,
losers: stats.losing || 0,
winRate: parseFloat(stats.winRate?.replace('%', '') || '0'),
monthlyPnL: [] // Would need separate endpoint for monthly breakdown
})
}
} catch (error) {
console.error('Failed to fetch data:', error)
} finally {
setLoading(false)
}
}
fetchData()
}, [])
// Generate projection for a given monthly rate
const generateProjection = (monthlyRate: number): ProjectionMonth[] => {
const months: ProjectionMonth[] = []
let tradingCapital = STARTING_CAPITAL
let btcHoldings = 0
let totalCashOut = 0
for (let month = 1; month <= 12; month++) {
const monthDate = new Date(START_DATE)
monthDate.setMonth(START_DATE.getMonth() + month - 1)
const startCapital = tradingCapital
const profit = tradingCapital * monthlyRate
tradingCapital += profit
// Tiered withdrawal
const withdrawal = getWithdrawal(tradingCapital)
tradingCapital -= withdrawal
totalCashOut += withdrawal
// BTC sweep (excess over $50k)
let btcSweep = 0
if (tradingCapital > TRADING_CAP) {
btcSweep = tradingCapital - TRADING_CAP
tradingCapital = TRADING_CAP
}
// BTC holdings grow + new sweep
btcHoldings = btcHoldings * (1 + BTC_MONTHLY_GROWTH) + btcSweep
const netWorth = tradingCapital + btcHoldings + totalCashOut
months.push({
month,
date: monthDate.toLocaleDateString('en-US', { month: 'short', year: 'numeric' }),
startCapital,
profit,
withdrawal,
btcSweep,
endCapital: tradingCapital,
btcHoldings,
totalCashOut,
netWorth
})
}
return months
}
// 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' },
]
// 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 {
...scenario,
finalNetWorth: final.netWorth,
finalTrading: final.endCapital,
finalBTC: final.btcHoldings,
totalCashOut: final.totalCashOut
}
})
const currentProjection = generateProjection(selectedScenario / 100)
const finalMonth = currentProjection[currentProjection.length - 1]
const formatCurrency = (value: number) => {
return new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 0,
maximumFractionDigits: 0
}).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">
{/* Header */}
<div className="mb-8">
<h1 className="text-4xl font-bold mb-2">📈 Financial Projection</h1>
<p className="text-gray-400">12-Month Strategy: Tiered Withdrawals + $50k Cap + BTC Sweep</p>
</div>
{/* Strategy Rules Card */}
<div className="bg-gray-800 rounded-lg p-6 mb-8 border border-gray-700">
<h2 className="text-xl font-semibold mb-4 text-yellow-400"> Strategy Rules</h2>
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
<div className="bg-gray-700 rounded p-4">
<div className="text-sm text-gray-400">Trading Cap</div>
<div className="text-2xl font-bold text-green-400">$50,000</div>
<div className="text-xs text-gray-500">Max active trading capital</div>
</div>
<div className="bg-gray-700 rounded p-4">
<div className="text-sm text-gray-400">Excess Strategy</div>
<div className="text-2xl font-bold text-orange-400"> BTC</div>
<div className="text-xs text-gray-500">Monthly sweep to Bitcoin</div>
</div>
<div className="bg-gray-700 rounded p-4">
<div className="text-sm text-gray-400">BTC Growth</div>
<div className="text-2xl font-bold text-blue-400">5%/mo</div>
<div className="text-xs text-gray-500">60% annual appreciation</div>
</div>
<div className="bg-gray-700 rounded p-4">
<div className="text-sm text-gray-400">Withdrawals</div>
<div className="text-2xl font-bold text-purple-400">Tiered</div>
<div className="text-xs text-gray-500">$100$300$600$1,200</div>
</div>
</div>
</div>
{/* Tiered Withdrawal Rules */}
<div className="bg-gray-800 rounded-lg p-6 mb-8 border border-gray-700">
<h2 className="text-xl font-semibold mb-4 text-purple-400">💸 Tiered Withdrawal Schedule</h2>
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
<div className="text-center p-3 bg-gray-700 rounded">
<div className="text-sm text-gray-400">Capital &lt; $3k</div>
<div className="text-xl font-bold text-red-400">$100/mo</div>
</div>
<div className="text-center p-3 bg-gray-700 rounded">
<div className="text-sm text-gray-400">Capital &lt; $10k</div>
<div className="text-xl font-bold text-yellow-400">$300/mo</div>
</div>
<div className="text-center p-3 bg-gray-700 rounded">
<div className="text-sm text-gray-400">Capital &lt; $30k</div>
<div className="text-xl font-bold text-blue-400">$600/mo</div>
</div>
<div className="text-center p-3 bg-gray-700 rounded">
<div className="text-sm text-gray-400">Capital $30k</div>
<div className="text-xl font-bold text-green-400">$1,200/mo</div>
</div>
</div>
</div>
{/* Current Status */}
<div className="bg-gray-800 rounded-lg p-6 mb-8 border border-green-500/30">
<h2 className="text-xl font-semibold mb-4 text-green-400">📊 Current Status</h2>
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
<div>
<div className="text-sm text-gray-400">Starting Capital</div>
<div className="text-2xl font-bold">{formatCurrency(STARTING_CAPITAL)}</div>
</div>
<div>
<div className="text-sm text-gray-400">Live Capital</div>
<div className="text-2xl font-bold text-green-400">
{loading ? '...' : formatCurrency(currentCapital)}
</div>
</div>
<div>
<div className="text-sm text-gray-400">Strategy</div>
<div className="text-2xl font-bold text-blue-400">ML v11.2</div>
</div>
<div>
<div className="text-sm text-gray-400">Pyramiding</div>
<div className="text-2xl font-bold text-purple-400">7x + 7x</div>
</div>
</div>
</div>
{/* ACTUAL PROGRESS vs PLAN */}
<div className="bg-gray-800 rounded-lg p-6 mb-8 border border-yellow-500/30">
<h2 className="text-xl font-semibold mb-4 text-yellow-400">📈 Actual Progress vs Plan</h2>
{/* Summary Stats */}
<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 ${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">
<div className="text-sm text-gray-400">Total Trades</div>
<div className="text-2xl font-bold text-blue-400">
{loading ? '...' : tradingStats?.totalTrades || 0}
</div>
</div>
<div className="bg-gray-700 rounded p-4">
<div className="text-sm text-gray-400">Win Rate</div>
<div className={`text-2xl font-bold ${tradingStats?.winRate && tradingStats.winRate >= 50 ? 'text-green-400' : 'text-yellow-400'}`}>
{loading ? '...' : tradingStats ? `${tradingStats.winRate.toFixed(1)}%` : '0%'}
</div>
</div>
<div className="bg-gray-700 rounded p-4">
<div className="text-sm text-gray-400">Capital Change</div>
<div className={`text-2xl font-bold ${currentCapital - STARTING_CAPITAL >= 0 ? 'text-green-400' : 'text-red-400'}`}>
{loading ? '...' : `${((currentCapital - STARTING_CAPITAL) / STARTING_CAPITAL * 100).toFixed(1)}%`}
</div>
</div>
<div className="bg-gray-700 rounded p-4">
<div className="text-sm text-gray-400">Net Profit</div>
<div className={`text-2xl font-bold ${currentCapital - STARTING_CAPITAL >= 0 ? 'text-green-400' : 'text-red-400'}`}>
{loading ? '...' : formatCurrency(currentCapital - STARTING_CAPITAL)}
</div>
</div>
</div>
{/* Plan Comparison */}
<div className="bg-gray-700/50 rounded-lg p-4">
<h3 className="text-md font-semibold mb-3 text-gray-300">Expected vs Actual (70%/mo target)</h3>
{(() => {
const daysElapsed = Math.max(0, Math.floor((new Date().getTime() - START_DATE.getTime()) / (1000 * 60 * 60 * 24)))
const monthsElapsed = daysElapsed / 30
const dailyRate = Math.pow(1.70, 1/30) - 1 // 70%/month converted to daily compound rate
const expectedCapital = STARTING_CAPITAL * Math.pow(1 + dailyRate, daysElapsed)
const expectedGain = expectedCapital - STARTING_CAPITAL
const actualGain = currentCapital - STARTING_CAPITAL
const onTrack = currentCapital >= expectedCapital
return (
<div className="space-y-2">
<div className="flex justify-between items-center">
<span className="text-gray-400">Days Since Start (Jan 6, 2026):</span>
<span className="text-blue-400 font-bold">{daysElapsed} days ({monthsElapsed.toFixed(1)} months)</span>
</div>
<div className="flex justify-between items-center">
<span className="text-gray-400">Expected Capital @ 70%/mo:</span>
<span className="text-yellow-400 font-bold">{formatCurrency(expectedCapital)} (+{formatCurrency(expectedGain)})</span>
</div>
<div className="flex justify-between items-center">
<span className="text-gray-400">Current Capital:</span>
<span className={`font-bold ${actualGain >= 0 ? 'text-green-400' : 'text-red-400'}`}>
{loading ? '...' : formatCurrency(currentCapital)} ({actualGain >= 0 ? '+' : ''}{formatCurrency(actualGain)})
</span>
</div>
<div className="flex justify-between items-center border-t border-gray-600 pt-2 mt-2">
<span className="text-gray-400">Status vs Plan:</span>
<span className={`font-bold ${onTrack ? 'text-green-400' : 'text-red-400'}`}>
{loading ? '...' : onTrack ? '✅ ON TRACK' : '⚠️ BEHIND'}
({formatCurrency(currentCapital - expectedCapital)}, {((currentCapital / expectedCapital - 1) * 100).toFixed(1)}%)
</span>
</div>
<div className="flex justify-between items-center">
<span className="text-gray-400">Actual Daily Return:</span>
<span className={`font-bold ${actualGain >= 0 ? 'text-green-400' : 'text-red-400'}`}>
{loading || daysElapsed === 0 ? '...' : `${((Math.pow(currentCapital / STARTING_CAPITAL, 1 / Math.max(1, daysElapsed)) - 1) * 100).toFixed(2)}%/day`}
{daysElapsed > 0 && !loading && ` (≈ ${((Math.pow(currentCapital / STARTING_CAPITAL, 30 / Math.max(1, daysElapsed)) - 1) * 100).toFixed(0)}%/mo)`}
</span>
</div>
</div>
)
})()}
</div>
</div>
{/* Scenario Selector */}
<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}
onClick={() => setSelectedScenario(rate)}
className={`px-4 py-2 rounded-lg font-semibold transition-all ${
selectedScenario === rate
? 'bg-green-600 text-white'
: 'bg-gray-700 text-gray-300 hover:bg-gray-600'
}`}
>
{rate}%/mo
</button>
))}
</div>
{/* Year-End Summary for Selected Scenario */}
<div className="bg-gray-700 rounded-lg p-4">
<h3 className="text-lg font-semibold mb-3 text-yellow-400">
Year 1 Result @ {selectedScenario}%/mo
</h3>
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
<div>
<div className="text-sm text-gray-400">Trading Account</div>
<div className="text-xl font-bold text-green-400">{formatCurrency(finalMonth.endCapital)}</div>
</div>
<div>
<div className="text-sm text-gray-400">BTC Holdings</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>
<div className="text-xl font-bold text-purple-400">{formatCurrency(finalMonth.totalCashOut)}</div>
</div>
<div>
<div className="text-sm text-gray-400">Total Net Worth</div>
<div className="text-2xl font-bold text-white">{formatCurrency(finalMonth.netWorth)}</div>
</div>
</div>
</div>
</div>
{/* Scenario Comparison Table */}
<div className="bg-gray-800 rounded-lg p-6 mb-8 border border-gray-700">
<h2 className="text-xl font-semibold mb-4">📊 All Scenarios Comparison</h2>
<div className="overflow-x-auto">
<table className="w-full">
<thead>
<tr className="text-left text-gray-400 border-b border-gray-700">
<th className="pb-3">Scenario</th>
<th className="pb-3 text-right">Trading</th>
<th className="pb-3 text-right">BTC Holdings</th>
<th className="pb-3 text-right">Cash Out</th>
<th className="pb-3 text-right">Net Worth</th>
</tr>
</thead>
<tbody>
{scenarios.map((scenario, idx) => (
<tr
key={idx}
className={`border-b border-gray-700 ${
scenario.monthlyRate * 100 === selectedScenario ? 'bg-gray-700' : ''
}`}
>
<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">{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>
))}
</tbody>
</table>
</div>
</div>
{/* Monthly Breakdown Table */}
<div className="bg-gray-800 rounded-lg p-6 mb-8 border border-gray-700">
<h2 className="text-xl font-semibold mb-4">📅 Month-by-Month Breakdown @ {selectedScenario}%/mo</h2>
<div className="overflow-x-auto">
<table className="w-full text-sm">
<thead>
<tr className="text-left text-gray-400 border-b border-gray-700">
<th className="pb-3">Month</th>
<th className="pb-3 text-right">Start</th>
<th className="pb-3 text-right">Profit</th>
<th className="pb-3 text-right">Withdraw</th>
<th className="pb-3 text-right">BTC</th>
<th className="pb-3 text-right">End</th>
<th className="pb-3 text-right">BTC Total</th>
<th className="pb-3 text-right">Net Worth</th>
</tr>
</thead>
<tbody>
{currentProjection.map((month, idx) => (
<tr key={idx} className="border-b border-gray-700 hover:bg-gray-700">
<td className="py-2 font-semibold">{month.date}</td>
<td className="py-2 text-right text-gray-400">{formatCurrency(month.startCapital)}</td>
<td className="py-2 text-right text-green-400">+{formatCurrency(month.profit)}</td>
<td className="py-2 text-right text-purple-400">
{month.withdrawal > 0 ? `-${formatCurrency(month.withdrawal)}` : '-'}
</td>
<td className="py-2 text-right text-orange-400">
{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">{formatBTC(month.btcHoldings)}</td>
<td className="py-2 text-right font-bold">{formatCurrency(month.netWorth)}</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
{/* Data Foundation */}
<div className="bg-gray-800 rounded-lg p-6 mb-8 border border-blue-500/30">
<h2 className="text-xl font-semibold mb-4 text-blue-400">🔬 Data Foundation</h2>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<h3 className="text-lg font-semibold mb-2">ML v11.2 Backtest Results</h3>
<ul className="space-y-1 text-gray-300">
<li> <span className="text-green-400 font-semibold">44 trades</span> over 8 days</li>
<li> <span className="text-green-400 font-semibold">84.09% win rate</span></li>
<li> <span className="text-green-400 font-semibold">3.061 profit factor</span></li>
<li> Extrapolated: ~176%/mo raw</li>
<li> Base case: 70%/mo (conservative)</li>
</ul>
</div>
<div>
<h3 className="text-lg font-semibold mb-2">Pyramiding Configuration</h3>
<ul className="space-y-1 text-gray-300">
<li> Base leverage: <span className="text-purple-400 font-semibold">7x</span></li>
<li> Stack leverage: <span className="text-purple-400 font-semibold">7x</span></li>
<li> Max total: <span className="text-purple-400 font-semibold">14x</span></li>
<li> Max pyramid levels: <span className="text-purple-400 font-semibold">2</span></li>
<li> Window: 4 hours (48 bars)</li>
</ul>
</div>
</div>
</div>
{/* Risk Disclaimer */}
<div className="bg-red-900/20 rounded-lg p-6 border border-red-500/30">
<h2 className="text-xl font-semibold mb-2 text-red-400"> Important Disclaimer</h2>
<p className="text-gray-300 text-sm">
These projections are based on backtested results and are NOT guaranteed. Past performance does not
guarantee future results. Cryptocurrency trading involves substantial risk of loss. The projections
assume consistent market conditions and strategy performance, which may not occur. Only trade with
capital you can afford to lose.
</p>
</div>
{/* Footer */}
<div className="mt-8 text-center text-gray-500 text-sm">
<p>Strategy: ML v11.2 + Pyramiding | Exchange: MEXC (SOL/USDT Perp)</p>
<p className="mt-1">Last Updated: January 2026</p>
</div>
</div>
</div>
)
}