docs: Document projection page v11.2 filter and cleanup trade exclusion (Jan 13, 2026)

- STARTING_CAPITAL set to 1437 (actual v11.2 start capital)
- START_DATE set to Jan 6, 2026 (v11.2 production start)
- getTradingStats filters for v11.2 indicator + recent manual trades
- Excludes cleanup trades (DUPLICATE_CLEANUP, PHANTOM_CLEANUP, phantom)
- Current v11.2 performance: 6 trades, 83.3% WR, +$406.04 P&L
This commit is contained in:
mindesbunister
2026-01-13 11:12:53 +01:00
parent f6424c4d04
commit ed18c9e70f
3 changed files with 163 additions and 12 deletions

View File

@@ -138,6 +138,26 @@
- **Previous Config:** Was 5x fixed (Dec 26, 2025) - **Previous Config:** Was 5x fixed (Dec 26, 2025)
- **Last Updated:** Jan 2, 2026 - **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) ### Supporting Systems (All Active)
- **Stop Hunt Revenge:** Quality 85+ signals get auto re-entry on reversal (90s confirmation) - **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 - **Smart Validation Queue:** Quality 50-89 signals queued for 90-minute price confirmation

View File

@@ -25,16 +25,27 @@ interface ScenarioResult {
color: string 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() { export default function ProjectionPage() {
const [currentCapital, setCurrentCapital] = useState<number>(0) const [currentCapital, setCurrentCapital] = useState<number>(0)
const [loading, setLoading] = useState(true) const [loading, setLoading] = useState(true)
const [selectedScenario, setSelectedScenario] = useState<number>(70) const [selectedScenario, setSelectedScenario] = useState<number>(70)
const [tradingStats, setTradingStats] = useState<TradingStats | null>(null)
const [totalDeposits, setTotalDeposits] = useState<number>(0)
// Strategy Parameters (Jan 2026) // 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 TRADING_CAP = 50000
const BTC_MONTHLY_GROWTH = 0.05 // 5% per month (60% annual) 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 // Tiered Withdrawal Rules
const getWithdrawal = (capital: number): number => { const getWithdrawal = (capital: number): number => {
@@ -46,20 +57,40 @@ export default function ProjectionPage() {
} }
useEffect(() => { useEffect(() => {
async function fetchCurrentCapital() { async function fetchData() {
try { try {
const response = await fetch('/api/drift/account-summary') // Fetch account summary
const data = await response.json() const accountResponse = await fetch('/api/drift/account-summary')
if (data.success) { const accountData = await accountResponse.json()
setCurrentCapital(data.freeCollateral || 0) 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) { } catch (error) {
console.error('Failed to fetch capital:', error) console.error('Failed to fetch data:', error)
} finally { } finally {
setLoading(false) setLoading(false)
} }
} }
fetchCurrentCapital() fetchData()
}, []) }, [])
// Generate projection for a given monthly rate // Generate projection for a given monthly rate
@@ -226,6 +257,92 @@ export default function ProjectionPage() {
</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 ${tradingStats?.totalPnL && tradingStats.totalPnL >= 0 ? 'text-green-400' : 'text-red-400'}`}>
{loading ? '...' : tradingStats ? formatCurrency(tradingStats.totalPnL) : '$0'}
</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 */} {/* Scenario Selector */}
<div className="bg-gray-800 rounded-lg p-6 mb-8 border border-gray-700"> <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> <h2 className="text-xl font-semibold mb-4">🎯 Select Monthly Return Scenario</h2>

View File

@@ -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) { export async function getTradingStats(days: number = 30) {
const prisma = getPrismaClient() const prisma = getPrismaClient()
@@ -138,14 +139,27 @@ export async function getTradingStats(days: number = 30) {
const since = new Date() const since = new Date()
since.setDate(since.getDate() - days) 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({ const trades = await prisma.trade.findMany({
where: { where: {
createdAt: { gte: since }, createdAt: { gte: since },
status: 'closed', status: 'closed',
isTestTrade: false, // Real trades only isTestTrade: false, // Real trades only
// Exclude phantom/cleanup trades - not real exits
exitReason: {
notIn: ['DUPLICATE_CLEANUP', 'PHANTOM_CLEANUP', 'phantom']
},
OR: [ OR: [
{ signalSource: null }, // Old trades without signalSource // v11.2 indicator signals (any age within 'days' period)
{ signalSource: { not: 'manual' } }, // Exclude manual Telegram trades { indicatorVersion: 'v11.2' },
// Manual trades only from last 7 days
{
signalSource: 'manual',
createdAt: { gte: manualCutoff }
},
], ],
}, },
}) })