- Extended getActualFillPriceFromTx() to also extract entry fill prices - openPosition() now uses actual fill from tx logs (was using oracle price) - closePosition() already fixed to use actual exit fills - Both prices now extracted via Drift SDK LogParser OrderActionRecord events - Eliminates ~0.08% entry and exit price discrepancies vs Drift UI - P&L calculations now 100% accurate to actual execution prices Files changed: - lib/drift/orders.ts (entry extraction in openPosition) - docs/COMMON_PITFALLS.md (updated Pitfall #89) Expected impact: Entry AND exit prices match Drift exactly on all future trades
395 lines
17 KiB
TypeScript
395 lines
17 KiB
TypeScript
'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
|
||
}
|
||
|
||
export default function ProjectionPage() {
|
||
const [currentCapital, setCurrentCapital] = useState<number>(0)
|
||
const [loading, setLoading] = useState(true)
|
||
const [selectedScenario, setSelectedScenario] = useState<number>(70)
|
||
|
||
// Strategy Parameters (Jan 2026)
|
||
const STARTING_CAPITAL = 1400
|
||
const TRADING_CAP = 50000
|
||
const BTC_MONTHLY_GROWTH = 0.05 // 5% per month (60% annual)
|
||
const START_DATE = new Date('2026-01-15')
|
||
|
||
// 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 fetchCurrentCapital() {
|
||
try {
|
||
const response = await fetch('/api/drift/account-summary')
|
||
const data = await response.json()
|
||
if (data.success) {
|
||
setCurrentCapital(data.freeCollateral || 0)
|
||
}
|
||
} catch (error) {
|
||
console.error('Failed to fetch capital:', error)
|
||
} finally {
|
||
setLoading(false)
|
||
}
|
||
}
|
||
fetchCurrentCapital()
|
||
}, [])
|
||
|
||
// 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
|
||
}
|
||
|
||
// Pre-calculate all scenarios
|
||
const scenarios: 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 => {
|
||
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)
|
||
}
|
||
|
||
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 < $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 < $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 < $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>
|
||
|
||
{/* 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">
|
||
{[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">{formatCurrency(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">{formatCurrency(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 ? formatCurrency(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 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>
|
||
)
|
||
}
|