Files
sitemente/components/mission-control/TradingPanel.tsx
T
2026-02-25 10:30:06 +01:00

717 lines
28 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.
This file contains Unicode characters that might be confused with other characters. 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'
import { TradingTools } from './TradingTools'
import { TradingReports } from './TradingReports'
import { MacroReportGenerator } from './MacroReportGenerator'
type TradingTab = 'research' | 'strategies' | 'execution' | 'journal' | 'tools'
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
traderStyle?: string // NEW: tracks which trader style was used
}
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']
}
]
// Execute Trade Modal Component
function ExecuteTradeModal({
traders,
selectedTrader,
onClose,
onTradeExecuted
}: {
traders: Trader[]
selectedTrader: string
onClose: () => void
onTradeExecuted: (trade: Trade) => void
}) {
const [form, setForm] = useState({
pair: 'BTC/USD',
direction: 'long' as 'long' | 'short',
isDemo: true,
entryPrice: '',
timeframe: '4H',
setup: ''
})
const submitTrade = () => {
const trader = traders.find(t => t.id === selectedTrader)
const trade: Trade = {
id: Date.now().toString(),
trader: trader?.name || 'Unknown',
pair: form.pair,
direction: form.direction,
entryPrice: parseFloat(form.entryPrice) || 0,
status: 'open',
isDemo: form.isDemo,
openedAt: new Date().toISOString(),
setup: form.setup || `${trader?.name} setup`,
timeframe: form.timeframe,
reason: '',
notes: '',
traderStyle: selectedTrader
}
// Save to API
fetch('/api/trading/trades', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(trade)
}).then(() => {
onTradeExecuted(trade)
})
}
const trader = traders.find(t => t.id === selectedTrader)
return (
<div className="bg-[#1a1625] border border-white/20 rounded-xl max-w-lg w-full">
<div className="p-4 border-b border-white/10 flex justify-between items-center">
<h2 className="text-xl font-bold text-white">Execute Trade</h2>
<button onClick={onClose} className="text-white/50 hover:text-white text-2xl">×</button>
</div>
<div className="p-4 space-y-4">
{/* Trader Style Badge */}
<div className="p-3 bg-brand-pink/20 border border-brand-pink/30 rounded-lg">
<p className="text-xs text-white/50">Trading Style</p>
<p className="font-bold text-white">{trader?.name}</p>
</div>
{/* Pair */}
<div>
<label className="block text-sm text-white/70 mb-1">Trading Pair</label>
<select
value={form.pair}
onChange={e => setForm({...form, pair: e.target.value})}
className="w-full bg-white/10 border border-white/20 rounded-lg px-3 py-2 text-white"
>
<option value="BTC/USD">BTC/USD</option>
<option value="ETH/USD">ETH/USD</option>
<option value="SOL/USD">SOL/USD</option>
<option value="EUR/USD">EUR/USD</option>
<option value="GBP/USD">GBP/USD</option>
</select>
</div>
{/* Direction */}
<div className="flex gap-2">
<button
onClick={() => setForm({...form, direction: 'long'})}
className={`flex-1 py-2 rounded-lg font-medium ${
form.direction === 'long' ? 'bg-green-500 text-white' : 'bg-white/10 text-white/70'
}`}
>
📈 LONG
</button>
<button
onClick={() => setForm({...form, direction: 'short'})}
className={`flex-1 py-2 rounded-lg font-medium ${
form.direction === 'short' ? 'bg-red-500 text-white' : 'bg-white/10 text-white/70'
}`}
>
📉 SHORT
</button>
</div>
{/* Entry Price */}
<div>
<label className="block text-sm text-white/70 mb-1">Entry Price</label>
<input
type="number"
value={form.entryPrice}
onChange={e => setForm({...form, entryPrice: e.target.value})}
placeholder="0.00"
className="w-full bg-white/10 border border-white/20 rounded-lg px-3 py-2 text-white"
/>
</div>
{/* Timeframe */}
<div>
<label className="block text-sm text-white/70 mb-1">Timeframe</label>
<select
value={form.timeframe}
onChange={e => setForm({...form, timeframe: e.target.value})}
className="w-full bg-white/10 border border-white/20 rounded-lg px-3 py-2 text-white"
>
<option value="15m">15 min</option>
<option value="1H">1 Hour</option>
<option value="4H">4 Hour</option>
<option value="Daily">Daily</option>
<option value="Weekly">Weekly</option>
</select>
</div>
{/* Demo/Real */}
<div className="flex gap-2">
<button
onClick={() => setForm({...form, isDemo: true})}
className={`flex-1 py-2 rounded-lg font-medium ${
form.isDemo ? 'bg-blue-500 text-white' : 'bg-white/10 text-white/70'
}`}
>
🎯 Demo
</button>
<button
onClick={() => setForm({...form, isDemo: false})}
className={`flex-1 py-2 rounded-lg font-medium ${
!form.isDemo ? 'bg-yellow-500 text-white' : 'bg-white/10 text-white/70'
}`}
>
💰 Real
</button>
</div>
{/* Setup Note */}
<div>
<label className="block text-sm text-white/70 mb-1">Setup/Notes</label>
<input
value={form.setup}
onChange={e => setForm({...form, setup: e.target.value})}
placeholder="What setup is this?"
className="w-full bg-white/10 border border-white/20 rounded-lg px-3 py-2 text-white"
/>
</div>
<button
onClick={submitTrade}
className="w-full py-3 bg-brand-pink text-white rounded-lg font-bold hover:bg-[#ff7bc0] transition"
>
🚀 Execute Trade
</button>
</div>
</div>
)
}
export function TradingPanel() {
const [activeTab, setActiveTab] = useState<TradingTab>('research')
const [traders, setTraders] = useState<Trader[]>(defaultTraders)
const [selectedTrader, setSelectedTrader] = useState<string>('dopetrades')
const [showFullAnalysis, setShowFullAnalysis] = useState<string | null>(null)
const [showExecuteTrade, setShowExecuteTrade] = useState(false)
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 },
{ id: 'tools', label: '🧮 Tools', count: 4 },
{ id: 'macro', label: '📈 Trading Reports', count: 0 },
]
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={() => setShowFullAnalysis(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
onClick={() => setShowExecuteTrade(true)}
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>
{/* Per-Trader Stats */}
<div className="mb-4">
<h4 className="text-sm font-medium text-white/70 mb-2">📊 Performance by Trader Style</h4>
<div className="grid grid-cols-1 gap-2">
{traders.filter(t => t.status !== 'paused').map(trader => {
const traderTrades = trades.filter(t => t.traderStyle === trader.id)
const closedTrades = traderTrades.filter(t => t.status === 'closed' && !t.isDemo)
const wins = closedTrades.filter(t => (t.pnl || 0) > 0).length
const totalPnl = closedTrades.reduce((sum, t) => sum + (t.pnl || 0), 0)
const winRate = closedTrades.length > 0 ? Math.round((wins / closedTrades.length) * 100) : 0
return (
<div key={trader.id} className="p-3 bg-white/5 rounded-lg border border-white/10">
<div className="flex justify-between items-center">
<div>
<span className="font-medium text-white">{trader.name}</span>
<span className="ml-2 text-xs text-white/50">({traderTrades.length} trades)</span>
</div>
<div className="text-right">
<span className={`font-bold ${totalPnl >= 0 ? 'text-green-400' : 'text-red-400'}`}>
${totalPnl.toFixed(2)}
</span>
<span className="ml-2 text-xs text-white/50">| {winRate}% WR</span>
</div>
</div>
</div>
)
})}
</div>
</div>
{/* Overall 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>
)}
{/* Tools Tab */}
{activeTab === 'tools' && (
<div className="space-y-4">
<TradingTools />
<TradingReports />
</div>
)}
{/* Macro Report Tab */}
{activeTab === 'macro' && (
<MacroReportGenerator />
)}
{/* Full Analysis Modal */}
{showFullAnalysis && (
<div className="fixed inset-0 bg-black/80 backdrop-blur-sm z-50 flex items-center justify-center p-4">
<div className="bg-[#1a1625] border border-white/20 rounded-xl max-w-2xl w-full max-h-[80vh] overflow-y-auto">
{(() => {
const trader = traders.find(t => t.id === showFullAnalysis)
if (!trader) return null
return (
<div className="p-6">
<div className="flex justify-between items-start mb-6">
<div>
<h2 className="text-2xl font-bold text-white">{trader.name}</h2>
<p className="text-white/50">{trader.framesAnalyzed} frames analyzed {trader.status}</p>
</div>
<button
onClick={() => setShowFullAnalysis(null)}
className="text-white/50 hover:text-white text-2xl"
>
×
</button>
</div>
<div className="space-y-6">
{/* Patterns */}
<div>
<h3 className="text-brand-pink font-medium mb-2">📊 Patterns</h3>
<div className="flex flex-wrap gap-2">
{trader.patterns.map(p => (
<span key={p} className="px-3 py-1 bg-white/10 rounded-full text-sm text-white/80">{p}</span>
))}
</div>
</div>
{/* Entry Rules */}
<div>
<h3 className="text-green-400 font-medium mb-2"> Entry Rules</h3>
<ul className="space-y-1">
{trader.entryRules.map((r, i) => (
<li key={i} className="text-white/80 text-sm flex gap-2">
<span className="text-green-400"></span>
{r}
</li>
))}
</ul>
</div>
{/* Exit Rules */}
<div>
<h3 className="text-red-400 font-medium mb-2">🚪 Exit Rules</h3>
<ul className="space-y-1">
{trader.exitRules.map((r, i) => (
<li key={i} className="text-white/80 text-sm flex gap-2">
<span className="text-red-400"></span>
{r}
</li>
))}
</ul>
</div>
{/* Indicators */}
<div>
<h3 className="text-blue-400 font-medium mb-2">📈 Indicators</h3>
<div className="flex flex-wrap gap-2">
{trader.indicators.map(i => (
<span key={i} className="px-3 py-1 bg-blue-500/10 border border-blue-500/20 rounded-full text-sm text-blue-300">{i}</span>
))}
</div>
</div>
{/* Risk Params */}
<div>
<h3 className="text-yellow-400 font-medium mb-2"> Risk Parameters</h3>
<ul className="space-y-1">
{trader.riskParams.map((r, i) => (
<li key={i} className="text-white/80 text-sm flex gap-2">
<span className="text-yellow-400"></span>
{r}
</li>
))}
</ul>
</div>
{/* Timeframe */}
<div>
<h3 className="text-purple-400 font-medium mb-2"> Timeframe</h3>
<p className="text-white/80">{trader.timeframe}</p>
</div>
{/* Notes */}
{trader.notes && (
<div>
<h3 className="text-white/50 font-medium mb-2">📝 Notes</h3>
<p className="text-white/70 text-sm italic">{trader.notes}</p>
</div>
)}
</div>
<div className="mt-6 pt-4 border-t border-white/10 flex gap-3">
<button
onClick={() => {
setSelectedTrader(trader.id)
setShowFullAnalysis(null)
setActiveTab('strategies')
}}
className="flex-1 py-2 bg-brand-pink text-white rounded-lg font-medium hover:bg-brand-pink/80 transition"
>
Use This Strategy
</button>
<button
onClick={() => setShowFullAnalysis(null)}
className="px-4 py-2 border border-white/20 text-white/70 rounded-lg hover:text-white transition"
>
Close
</button>
</div>
</div>
)
})()}
</div>
</div>
)}
{/* Execute Trade Modal */}
{showExecuteTrade && (
<div className="fixed inset-0 bg-black/80 backdrop-blur-sm z-50 flex items-center justify-center p-4">
<ExecuteTradeModal
traders={traders}
selectedTrader={selectedTrader}
onClose={() => setShowExecuteTrade(false)}
onTradeExecuted={(trade) => {
setTrades([trade, ...trades])
setShowExecuteTrade(false)
}}
/>
</div>
)}
</div>
)
}