diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index cab1e15..aa15f24 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -138,6 +138,26 @@ - **Previous Config:** Was 5x fixed (Dec 26, 2025) - **Last Updated:** Jan 2, 2026 +### Projection Page & Analytics Stats (Jan 13, 2026 - UPDATED) +- **Purpose:** 12-month financial projection dashboard showing actual vs planned progress +- **File:** `app/projection/page.tsx` +- **Configuration:** + * `STARTING_CAPITAL = 1437` (USD, actual capital when v11.2 went live) + * `START_DATE = '2026-01-06'` (v11.2 production start date) + * Fetches from: `/api/analytics/stats?days=365` +- **Stats Filtering (lib/database/views.ts getTradingStats):** + * **v11.2 Filter:** Only includes trades with `indicatorVersion='v11.2'` + * **Manual Trades:** Includes `signalSource='manual'` from last 7 days only + * **Cleanup Exclusion:** Excludes phantom/cleanup trades: `exitReason NOT IN ['DUPLICATE_CLEANUP', 'PHANTOM_CLEANUP', 'phantom']` +- **v11.2 Performance (Jan 6-13, 2026):** + * Trades: 6 (5 wins, 1 loss) + * Win Rate: 83.3% + * Total P&L: +$406.04 + * Avg Win: $120.20, Avg Loss: -$194.96 + * Current Capital: $1,802.40 (from Drift) +- **Why Filter:** Previous stats included old indicator versions and phantom cleanup trades, skewing metrics +- **Last Updated:** Jan 13, 2026 + ### Supporting Systems (All Active) - **Stop Hunt Revenge:** Quality 85+ signals get auto re-entry on reversal (90s confirmation) - **Smart Validation Queue:** Quality 50-89 signals queued for 90-minute price confirmation diff --git a/app/projection/page.tsx b/app/projection/page.tsx index 0f6e6c8..cb6d642 100644 --- a/app/projection/page.tsx +++ b/app/projection/page.tsx @@ -25,16 +25,27 @@ interface ScenarioResult { 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(0) const [loading, setLoading] = useState(true) const [selectedScenario, setSelectedScenario] = useState(70) + const [tradingStats, setTradingStats] = useState(null) + const [totalDeposits, setTotalDeposits] = useState(0) // Strategy Parameters (Jan 2026) - const STARTING_CAPITAL = 1400 + 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-15') + const START_DATE = new Date('2026-01-06') // When v11.2 went live // Tiered Withdrawal Rules const getWithdrawal = (capital: number): number => { @@ -46,20 +57,40 @@ export default function ProjectionPage() { } useEffect(() => { - async function fetchCurrentCapital() { + async function fetchData() { try { - const response = await fetch('/api/drift/account-summary') - const data = await response.json() - if (data.success) { - setCurrentCapital(data.freeCollateral || 0) + // 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 capital:', error) + console.error('Failed to fetch data:', error) } finally { setLoading(false) } } - fetchCurrentCapital() + fetchData() }, []) // Generate projection for a given monthly rate @@ -226,6 +257,92 @@ export default function ProjectionPage() { + {/* ACTUAL PROGRESS vs PLAN */} +
+

📈 Actual Progress vs Plan

+ + {/* Summary Stats */} +
+
+
Total P&L
+
= 0 ? 'text-green-400' : 'text-red-400'}`}> + {loading ? '...' : tradingStats ? formatCurrency(tradingStats.totalPnL) : '$0'} +
+
+
+
Total Trades
+
+ {loading ? '...' : tradingStats?.totalTrades || 0} +
+
+
+
Win Rate
+
= 50 ? 'text-green-400' : 'text-yellow-400'}`}> + {loading ? '...' : tradingStats ? `${tradingStats.winRate.toFixed(1)}%` : '0%'} +
+
+
+
Capital Change
+
= 0 ? 'text-green-400' : 'text-red-400'}`}> + {loading ? '...' : `${((currentCapital - STARTING_CAPITAL) / STARTING_CAPITAL * 100).toFixed(1)}%`} +
+
+
+
Net Profit
+
= 0 ? 'text-green-400' : 'text-red-400'}`}> + {loading ? '...' : formatCurrency(currentCapital - STARTING_CAPITAL)} +
+
+
+ + {/* Plan Comparison */} +
+

Expected vs Actual (70%/mo target)

+ {(() => { + 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 ( +
+
+ Days Since Start (Jan 6, 2026): + {daysElapsed} days ({monthsElapsed.toFixed(1)} months) +
+
+ Expected Capital @ 70%/mo: + {formatCurrency(expectedCapital)} (+{formatCurrency(expectedGain)}) +
+
+ Current Capital: + = 0 ? 'text-green-400' : 'text-red-400'}`}> + {loading ? '...' : formatCurrency(currentCapital)} ({actualGain >= 0 ? '+' : ''}{formatCurrency(actualGain)}) + +
+
+ Status vs Plan: + + {loading ? '...' : onTrack ? '✅ ON TRACK' : '⚠️ BEHIND'} + ({formatCurrency(currentCapital - expectedCapital)}, {((currentCapital / expectedCapital - 1) * 100).toFixed(1)}%) + +
+
+ Actual Daily Return: + = 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)`} + +
+
+ ) + })()} +
+
+ {/* Scenario Selector */}

🎯 Select Monthly Return Scenario

diff --git a/lib/database/views.ts b/lib/database/views.ts index b864fa5..f41dbc1 100644 --- a/lib/database/views.ts +++ b/lib/database/views.ts @@ -130,7 +130,8 @@ export async function getTradesWithNetContext() { } /** - * Get trading statistics (excludes test trades) + * Get trading statistics for v11.2 + recent manual trades + * Updated Jan 12, 2026: Now filters for v11.2 indicator version OR manual trades from last 7 days */ export async function getTradingStats(days: number = 30) { const prisma = getPrismaClient() @@ -138,14 +139,27 @@ export async function getTradingStats(days: number = 30) { const since = new Date() since.setDate(since.getDate() - days) + // For manual trades, only include those from last 7 days + const manualCutoff = new Date() + manualCutoff.setDate(manualCutoff.getDate() - 7) + const trades = await prisma.trade.findMany({ where: { createdAt: { gte: since }, status: 'closed', isTestTrade: false, // Real trades only + // Exclude phantom/cleanup trades - not real exits + exitReason: { + notIn: ['DUPLICATE_CLEANUP', 'PHANTOM_CLEANUP', 'phantom'] + }, OR: [ - { signalSource: null }, // Old trades without signalSource - { signalSource: { not: 'manual' } }, // Exclude manual Telegram trades + // v11.2 indicator signals (any age within 'days' period) + { indicatorVersion: 'v11.2' }, + // Manual trades only from last 7 days + { + signalSource: 'manual', + createdAt: { gte: manualCutoff } + }, ], }, })