fix: PnL montant via entry_price fallback, Trades du cycle restructuré, Dernier Cycle trades ajoutés

- PnL simulated: Investi ~ et P&L € calculés avec entry_price quand
  capital_invested est null (label ~ pour indiquer estimation)
- Dernier Cycle: rename trades loggés -> trades ajoutés + mini mention
  des non-loggés quand patternsAdded > tradesLogged
- Trades du cycle: supprime le grand header +N Trades (info déplacée
  dans Dernier Cycle); layout inversé — trade en primaire, pattern
  en secondaire petit en dessous; trades non loggés affichés en gris

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
OpenSquared
2026-06-19 22:27:27 +02:00
parent defff620b2
commit 38c9aadb6b

View File

@@ -314,9 +314,12 @@ export default function Dashboard() {
const losers = withPnl.filter((t: any) => t.pnl_pct < 0).length
const closedTrades = trades.filter((t: any) => t.status === 'closed')
const openTrades = trades.filter((t: any) => t.status !== 'closed')
const totalCapital = trades.reduce((s: number, t: any) => s + (t.capital_invested ?? 0), 0)
// Use capital_invested if set, otherwise entry_price as proxy cost
const totalCapital = trades.reduce((s: number, t: any) =>
s + (t.capital_invested ?? t.entry_price ?? 0), 0)
const totalProfit = withPnl.reduce((s: number, t: any) =>
s + ((t.capital_invested ?? 0) * t.pnl_pct / 100), 0)
s + ((t.capital_invested ?? t.entry_price ?? 0) * t.pnl_pct / 100), 0)
const isEstimated = trades.some((t: any) => t.capital_invested == null && t.entry_price != null)
const targetHit = trades.filter((t: any) => t.alert_type === 'target_reached').length
const stopHit = trades.filter((t: any) => t.alert_type === 'stop_loss').length
@@ -356,7 +359,9 @@ export default function Dashboard() {
</div>
<div className="mt-2 pt-2 border-t border-slate-700/30 flex justify-between items-end">
<div>
<div className="text-[10px] text-slate-500 mb-0.5">Investi</div>
<div className="text-[10px] text-slate-500 mb-0.5">
Investi{isEstimated ? ' ~' : ''}
</div>
<div className="text-2xl font-bold font-mono text-slate-300">
{totalCapital > 0 ? `${totalCapital.toFixed(0)}` : '—'}
</div>
@@ -467,6 +472,7 @@ export default function Dashboard() {
const patternsAdded: number = last?.patterns_added ?? 0
const patternsScored: number = last?.patterns_scored ?? 0
const tradesLogged: number = cycleTrades.length
const tradesNotLogged: number = Math.max(0, patternsAdded - tradesLogged)
const tradesClosed = cycleTrades.filter((t: any) => t.status === 'closed').length
const commentary = last?.commentary
? (() => { try { return typeof last.commentary === 'string' ? JSON.parse(last.commentary) : last.commentary } catch { return null } })()
@@ -486,8 +492,11 @@ export default function Dashboard() {
<div className="text-[9px] text-slate-500 mt-0.5">patterns ajoutés</div>
</div>
<div className="bg-dark-700/60 rounded px-2 py-2">
<div className="text-xl font-bold text-blue-400">{tradesLogged}</div>
<div className="text-[9px] text-slate-500 mt-0.5">trades loggés</div>
<div className="text-xl font-bold text-blue-400">+{tradesLogged}</div>
<div className="text-[9px] text-slate-500 mt-0.5">trades ajoutés</div>
{tradesNotLogged > 0 && (
<div className="text-[8px] text-slate-700 mt-0.5">{tradesNotLogged} non-loggé{tradesNotLogged > 1 ? 's' : ''}</div>
)}
</div>
<div className="bg-dark-700/60 rounded px-2 py-1.5">
<div className="text-base font-bold text-emerald-400">{patternsScored}</div>
@@ -591,7 +600,6 @@ export default function Dashboard() {
{/* Trades du dernier cycle — détaillé */}
{(() => {
const tradeCount = cycleTrades.length
// Build a map: pattern_id → trade for quick lookup
const tradeByPattern: Record<string, any> = {}
for (const t of cycleTrades) {
@@ -607,11 +615,8 @@ export default function Dashboard() {
<span className="section-title mb-0 text-[10px]">📥 Trades du cycle</span>
<ArrowUpRight className="w-3 h-3 text-slate-600" />
</div>
<div className="text-xl font-bold text-emerald-400 font-mono">
{tradeCount > 0 ? `+${tradeCount} Trade${tradeCount > 1 ? 's' : ''}` : lastCycle ? '+0 Trades' : '—'}
</div>
{rows.length > 0 ? (
<div className="space-y-1.5 mt-1.5">
<div className="space-y-2 mt-1">
{rows.slice(0, 3).map((p: any, i: number) => {
const t = tradeByPattern[p.id]
const logged = !!t
@@ -620,38 +625,41 @@ export default function Dashboard() {
const pnl: number | null = t?.pnl_pct ?? null
const isBear = t?.strategy?.toLowerCase().includes('put') || t?.strategy?.toLowerCase().includes('bear')
return (
<div key={i} className="text-[10px]">
{/* Pattern name (origin) */}
<div className="flex items-center gap-1 mb-0.5">
<span className={clsx('text-[8px] font-bold shrink-0 w-3', logged ? 'text-emerald-400' : 'text-slate-600')}>
{logged ? '✓' : '○'}
</span>
<span className="text-slate-400 truncate flex-1 italic">{p.name}</span>
{score !== null && (
<span className={clsx('font-mono shrink-0 text-[9px] font-bold', scoreColor(score))}>{score}</span>
)}
</div>
{/* Trade details if logged */}
{logged && (
<div className="flex items-center gap-1.5 ml-3">
<span className="shrink-0 text-[9px]">{isBear ? '🐻' : '🐂'}</span>
<span className="font-mono text-slate-300 font-semibold">{t.underlying ?? '—'}</span>
<span className="text-slate-600 truncate flex-1">{t.strategy ?? ''}</span>
{ev !== null && (
<span className={clsx('font-mono shrink-0 text-[9px]', ev >= 0.5 ? 'text-emerald-400' : ev >= 0 ? 'text-yellow-400' : 'text-red-400')}>
EV{ev >= 0 ? '+' : ''}{ev.toFixed(2)}
</span>
)}
{pnl !== null && (
<span className={clsx('font-mono font-bold shrink-0', pnl >= 0 ? 'text-emerald-400' : 'text-red-400')}>
{pnl >= 0 ? '+' : ''}{pnl.toFixed(1)}%
</span>
<div key={i}>
{logged ? (
<>
{/* Trade row — primary */}
<div className="flex items-center gap-1.5 text-[10px]">
<span className="shrink-0">{isBear ? '🐻' : '🐂'}</span>
<span className="font-mono text-slate-200 font-bold w-10 truncate">{t.underlying ?? '—'}</span>
<span className="text-slate-400 truncate flex-1">{t.strategy ?? ''}</span>
{ev !== null && (
<span className={clsx('font-mono shrink-0 text-[9px]', ev >= 0.5 ? 'text-emerald-400' : ev >= 0 ? 'text-yellow-400' : 'text-red-400')}>
EV{ev >= 0 ? '+' : ''}{ev.toFixed(2)}
</span>
)}
{score !== null && (
<span className={clsx('font-mono shrink-0 font-bold', scoreColor(score))}>{score}</span>
)}
{pnl !== null && (
<span className={clsx('font-mono font-bold shrink-0', pnl >= 0 ? 'text-emerald-400' : 'text-red-400')}>
{pnl >= 0 ? '+' : ''}{pnl.toFixed(1)}%
</span>
)}
</div>
{/* Pattern name — secondary */}
<div className="text-[8px] text-slate-600 italic truncate mt-0.5 ml-4">{p.name}</div>
</>
) : (
<div className="flex items-center gap-1.5 text-[10px]">
<span className="text-slate-700 shrink-0"></span>
<span className="text-slate-600 truncate flex-1 italic">{p.name}</span>
{score !== null && (
<span className={clsx('font-mono shrink-0 text-[9px]', scoreColor(score))}>{score}</span>
)}
<span className="text-[8px] text-slate-700 shrink-0">non loggé</span>
</div>
)}
{!logged && (
<div className="ml-3 text-[9px] text-slate-700">score insuffisant non loggé</div>
)}
</div>
)
})}