Add paper trading with per-trader style tracking and stats

This commit is contained in:
root
2026-02-23 16:52:10 +00:00
parent fb68d68cf0
commit 5298480c16
+223 -2
View File
@@ -34,6 +34,7 @@ interface Trade {
closedAt?: string
notes: string
isDemo: boolean
traderStyle?: string // NEW: tracks which trader style was used
}
const defaultTraders: Trader[] = [
@@ -61,11 +62,183 @@ const defaultTraders: Trader[] = [
}
]
// 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')
@@ -254,7 +427,9 @@ export function TradingPanel() {
</div>
</div>
<button className="mt-4 w-full py-2 bg-brand-pink rounded-lg font-medium hover:bg-[#ff7bc0] transition">
<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>
@@ -272,7 +447,38 @@ export function TradingPanel() {
<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 */}
{/* 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>
@@ -472,6 +678,21 @@ export function TradingPanel() {
</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>
)
}