Files
sitemente/components/mission-control/TradingPanel.tsx
T

356 lines
14 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 { useState, useEffect } from 'react'
import TradingChart from './TradingChart'
type TradingTab = 'research' | 'strategies' | 'execution' | 'journal'
interface Trader {
id: string
name: string
status: 'learning' | 'active' | 'paused'
framesAnalyzed: number
patterns: string[]
entryRules: string[]
exitRules: string[]
indicators: string[]
riskParams: string[]
}
interface Trade {
id: string
trader: string
pair: string
direction: 'long' | 'short'
entryPrice: number
exitPrice?: number
status: 'open' | 'closed' | 'cancelled'
pnl?: number
pnlPercent?: number
reason: string
setup: string
timeframe: string
openedAt: string
closedAt?: string
notes: string
isDemo: boolean
}
const defaultTraders: Trader[] = [
{
id: 'dopetrades',
name: 'DopeTrades',
status: 'learning',
framesAnalyzed: 922,
patterns: ['Accumulation/Distribution', 'Trend Legs', 'Support/Resistance', 'Pop Pattern'],
entryRules: ['Identify trend on higher timeframe', 'Find demand/supply zones', 'Wait for retest', '2.5:1 min risk:reward'],
exitRules: ['Stop below demand (long)', 'Target recent highs', 'Scale 50% at 1:1'],
indicators: ['Price Action Only', 'Horizontal S/R Lines'],
riskParams: ['2.5:1 minimum', 'No revenge trading', 'Paper trading first']
},
{
id: 'gareth_soloway',
name: 'Gareth Soloway',
status: 'learning',
framesAnalyzed: 5970,
patterns: ['Institutional Analysis', 'Yield Curve Signals', 'Cycle Top/Bottom', 'Epic Resistance Rejections'],
entryRules: ['Track institutional activity', 'Wait for yield curve signals', 'Key level breaks with volume', 'Weekly analysis first'],
exitRules: ['Stop below support (long)', 'Target structure highs/lows', '2:1 minimum'],
indicators: ['VWAP', 'MACD', 'Volume', '10-Year Yield', 'Support/Resistance'],
riskParams: ['No-hype sizing', 'Never average down', 'Respect stops', 'Preservation > profits']
}
]
export function TradingPanel() {
const [activeTab, setActiveTab] = useState<TradingTab>('research')
const [traders, setTraders] = useState<Trader[]>(defaultTraders)
const [selectedTrader, setSelectedTrader] = useState<string>('dopetrades')
const [trades, setTrades] = useState<Trade[]>([])
const [journalFilter, setJournalFilter] = useState<'all' | 'demo' | 'real'>('all')
// Load data
useEffect(() => {
loadTraders()
loadTrades()
}, [])
const loadTraders = async () => {
try {
const res = await fetch('/api/trading/traders')
if (res.ok) {
const data = await res.json()
if (data.traders?.length > 0) setTraders(data.traders)
}
} catch (e) {}
}
const loadTrades = async () => {
try {
const res = await fetch('/api/trading/trades')
if (res.ok) {
const data = await res.json()
if (data.trades) setTrades(data.trades)
}
} catch (e) {}
}
const tabs = [
{ id: 'research', label: '🔬 Deep Research', count: traders.filter(t => t.status === 'learning').length },
{ id: 'strategies', label: '🎯 Strategies', count: traders.filter(t => t.status === 'active').length },
{ id: 'execution', label: '⚡ Execution', count: trades.filter(t => t.status === 'open').length },
{ id: 'journal', label: '📔 Journal', count: trades.length },
]
const filteredTrades = trades.filter(t => {
if (journalFilter === 'all') return true
if (journalFilter === 'demo') return t.isDemo
return !t.isDemo
})
const openTrades = trades.filter(t => t.status === 'open')
const closedDemoTrades = trades.filter(t => t.status === 'closed' && t.isDemo)
const closedRealTrades = trades.filter(t => t.status === 'closed' && !t.isDemo)
const totalPnl = closedRealTrades.reduce((sum, t) => sum + (t.pnl || 0), 0)
const winRate = closedRealTrades.length > 0
? Math.round((closedRealTrades.filter(t => (t.pnl || 0) > 0).length / closedRealTrades.length) * 100)
: 0
return (
<div className="space-y-4">
{/* Tab Navigation */}
<div className="flex gap-2 flex-wrap">
{tabs.map(tab => (
<button
key={tab.id}
onClick={() => setActiveTab(tab.id as TradingTab)}
className={`px-4 py-2 rounded-lg text-sm font-medium transition ${
activeTab === tab.id
? 'bg-brand-pink text-white'
: 'bg-white/10 text-white/70 hover:bg-white/20'
}`}
>
{tab.label}
<span className="ml-2 text-xs opacity-70">({tab.count})</span>
</button>
))}
</div>
{/* Research Tab */}
{activeTab === 'research' && (
<div className="border border-white/20 rounded-lg p-4 bg-white/5">
<h3 className="text-lg font-bold mb-4">🔬 Deep Research</h3>
<p className="text-white/60 mb-4">Learn trading strategies from experts by analyzing their content.</p>
<div className="space-y-3">
{traders.map(trader => (
<div
key={trader.id}
className="p-4 rounded-lg bg-white/5 border border-white/10"
>
<div className="flex items-center justify-between mb-2">
<div className="flex items-center gap-3">
<span className="text-2xl">👨🏫</span>
<div>
<p className="font-medium">{trader.name}</p>
<p className="text-xs text-white/50">{trader.framesAnalyzed} frames analyzed</p>
</div>
</div>
<span className={`px-2 py-1 rounded text-xs ${
trader.status === 'learning' ? 'bg-yellow-500/20 text-yellow-400' :
trader.status === 'active' ? 'bg-green-500/20 text-green-400' :
'bg-white/10 text-white/50'
}`}>
{trader.status}
</span>
</div>
{trader.patterns.length > 0 && (
<div className="mt-2 flex flex-wrap gap-1">
{trader.patterns.map(p => (
<span key={p} className="px-2 py-0.5 bg-white/10 rounded text-xs">{p}</span>
))}
</div>
)}
<button
className="mt-3 text-sm text-brand-pink hover:underline"
onClick={() => setSelectedTrader(trader.id)}
>
View full analysis
</button>
</div>
))}
</div>
<button className="mt-4 w-full py-2 border border-dashed border-white/20 rounded-lg text-white/50 hover:border-white/40 hover:text-white transition">
+ Add new trader to research
</button>
</div>
)}
{/* Strategies Tab */}
{activeTab === 'strategies' && (
<div className="border border-white/20 rounded-lg p-4 bg-white/5">
<h3 className="text-lg font-bold mb-4">🎯 Trading Strategies</h3>
<p className="text-white/60 mb-4">Select a trader style to follow for your next trade.</p>
<div className="flex gap-2 flex-wrap mb-4">
{traders.filter(t => t.status === 'active').map(trader => (
<button
key={trader.id}
onClick={() => setSelectedTrader(trader.id)}
className={`px-4 py-2 rounded-lg text-sm font-medium transition ${
selectedTrader === trader.id
? 'bg-brand-pink text-white'
: 'bg-white/10 text-white/70 hover:bg-white/20'
}`}
>
{trader.name}
</button>
))}
</div>
{traders.find(t => t.id === selectedTrader) && (
<div className="p-4 rounded-lg bg-black/30 border border-white/10">
<h4 className="font-medium mb-3">{traders.find(t => t.id === selectedTrader)?.name} Strategy</h4>
<div className="space-y-3 text-sm">
<div>
<p className="text-white/50 text-xs mb-1">Entry Rules</p>
<ul className="list-disc list-inside space-y-1">
{traders.find(t => t.id === selectedTrader)?.entryRules.map(r => (
<li key={r}>{r}</li>
))}
</ul>
{traders.find(t => t.id === selectedTrader)?.entryRules.length === 0 && (
<p className="text-white/30 italic">No entry rules defined yet</p>
)}
</div>
<div>
<p className="text-white/50 text-xs mb-1">Exit Rules</p>
<ul className="list-disc list-inside space-y-1">
{traders.find(t => t.id === selectedTrader)?.exitRules.map(r => (
<li key={r}>{r}</li>
))}
</ul>
</div>
<div>
<p className="text-white/50 text-xs mb-1">Indicators</p>
<div className="flex flex-wrap gap-1">
{traders.find(t => t.id === selectedTrader)?.indicators.map(i => (
<span key={i} className="px-2 py-0.5 bg-blue-500/20 text-blue-400 rounded text-xs">{i}</span>
))}
</div>
</div>
<div>
<p className="text-white/50 text-xs mb-1">Risk Parameters</p>
<ul className="list-disc list-inside space-y-1">
{traders.find(t => t.id === selectedTrader)?.riskParams.map(r => (
<li key={r}>{r}</li>
))}
</ul>
</div>
</div>
<button className="mt-4 w-full py-2 bg-brand-pink rounded-lg font-medium hover:bg-[#ff7bc0] transition">
Execute Trade in {traders.find(t => t.id === selectedTrader)?.name} Style
</button>
</div>
)}
</div>
)}
{/* Execution Tab */}
{activeTab === 'execution' && (
<TradingChart />
)}
{/* Journal Tab */}
{activeTab === 'journal' && (
<div className="border border-white/20 rounded-lg p-4 bg-white/5">
<h3 className="text-lg font-bold mb-4">📔 Trading Journal</h3>
{/* Stats */}
<div className="grid grid-cols-3 gap-3 mb-4">
<div className="p-3 rounded bg-white/5 text-center">
<p className="text-2xl font-bold text-green-400">${totalPnl.toFixed(2)}</p>
<p className="text-xs text-white/50">Real P&L</p>
</div>
<div className="p-3 rounded bg-white/5 text-center">
<p className="text-2xl font-bold">{winRate}%</p>
<p className="text-xs text-white/50">Win Rate</p>
</div>
<div className="p-3 rounded bg-white/5 text-center">
<p className="text-2xl font-bold">{closedRealTrades.length}</p>
<p className="text-xs text-white/50">Real Trades</p>
</div>
</div>
{/* Filter */}
<div className="flex gap-2 mb-4">
{(['all', 'demo', 'real'] as const).map(filter => (
<button
key={filter}
onClick={() => setJournalFilter(filter)}
className={`px-3 py-1 rounded text-xs ${
journalFilter === filter
? 'bg-brand-pink text-white'
: 'bg-white/10 text-white/60'
}`}
>
{filter === 'all' ? 'All' : filter === 'demo' ? 'Demo' : 'Real'}
</button>
))}
</div>
{/* Trades List */}
<div className="space-y-2 max-h-[400px] overflow-y-auto">
{filteredTrades.length === 0 ? (
<p className="text-white/40 text-center py-4">No trades yet</p>
) : (
filteredTrades.map(trade => (
<div
key={trade.id}
className={`p-3 rounded border ${
trade.isDemo
? 'bg-white/5 border-white/10'
: trade.status === 'open'
? 'bg-yellow-500/10 border-yellow-500/30'
: (trade.pnl || 0) > 0
? 'bg-green-500/10 border-green-500/30'
: 'bg-red-500/10 border-red-500/30'
}`}
>
<div className="flex justify-between items-start">
<div>
<span className="font-medium">{trade.pair}</span>
<span className={`ml-2 text-xs px-1.5 py-0.5 rounded ${
trade.direction === 'long' ? 'bg-green-500/20 text-green-400' : 'bg-red-500/20 text-red-400'
}`}>
{trade.direction.toUpperCase()}
</span>
{trade.isDemo && <span className="ml-1 text-xs text-white/40">(Demo)</span>}
</div>
{trade.status === 'closed' && (
<span className={`font-bold ${(trade.pnl || 0) >= 0 ? 'text-green-400' : 'text-red-400'}`}>
{trade.pnl >= 0 ? '+' : ''}{trade.pnl?.toFixed(2)} ({trade.pnlPercent?.toFixed(1)}%)
</span>
)}
</div>
<p className="text-xs text-white/50 mt-1">{trade.setup}</p>
<div className="flex gap-4 mt-2 text-xs text-white/40">
<span>{trade.timeframe}</span>
<span>{new Date(trade.openedAt).toLocaleDateString()}</span>
</div>
</div>
))
)}
</div>
</div>
)}
</div>
)
}