feat: time-aware trade maturity classification

- Add _trade_maturity() helper: classifies trades by % of horizon elapsed
  (trop_tot <10%, debut 10-35%, mature 35-75%, fin_horizon >75%)
- Fix horizon_days fallback chain in log_trade_entries (default 30→90)
- journal.py: enrich each MTM trade with maturity dict + horizon_days
- reasoning.py: portfolio report segments trades by maturity; GPT-4o
  draws lessons only from matures (≥35% elapsed), never from trop_tot
- auto_cycle.py: 90d window, maturity-aware prompt with timing rules
- JournalDeBord.tsx: maturity badge with emoji, label, progress bar
  and day counter (Xj / Yj Z%) replacing plain days_held column

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
OpenSquared
2026-06-16 23:49:33 +02:00
parent 4bbcd7a3a6
commit 9075762dd5
5 changed files with 223 additions and 57 deletions

View File

@@ -394,7 +394,7 @@ function TradeMtmSection({ days }: { days: number }) {
<th className="text-right px-3 py-2 font-medium">Date</th>
<th className="text-right px-3 py-2 font-medium">Prix entrée</th>
<th className="text-right px-3 py-2 font-medium">Prix actuel</th>
<th className="text-right px-3 py-2 font-medium">J</th>
<th className="text-right px-3 py-2 font-medium">Maturité</th>
<th className="text-right px-3 py-2 font-medium">P&L th.</th>
<th className="px-3 py-2 font-medium w-8"></th>
</tr>
@@ -451,8 +451,37 @@ function TradeMtmSection({ days }: { days: number }) {
<td className="px-3 py-2 text-right font-mono text-slate-300 text-[11px]">
{t.current_price != null ? t.current_price.toFixed(2) : '—'}
</td>
<td className="px-3 py-2 text-right text-slate-600 text-[11px]">
{t.days_held != null ? t.days_held : '—'}
<td className="px-3 py-2 text-right">
{t.maturity ? (
<div className="flex flex-col items-end gap-0.5">
<span className={clsx('text-[10px] font-semibold',
t.maturity.status === 'trop_tot' ? 'text-slate-500' :
t.maturity.status === 'debut' ? 'text-yellow-400' :
t.maturity.status === 'mature' ? 'text-emerald-400' :
'text-orange-400'
)}>
{t.maturity.emoji} {t.maturity.label}
</span>
<span className="text-[9px] text-slate-600 font-mono">
{t.days_held ?? 0}j / {t.horizon_days ?? 90}j ({t.maturity.ratio_pct}%)
</span>
<div className="w-16 h-1 bg-slate-700 rounded-full overflow-hidden">
<div
className={clsx('h-full rounded-full',
t.maturity.status === 'trop_tot' ? 'bg-slate-600' :
t.maturity.status === 'debut' ? 'bg-yellow-500' :
t.maturity.status === 'mature' ? 'bg-emerald-500' :
'bg-orange-500'
)}
style={{ width: `${Math.min(t.maturity.ratio_pct, 100)}%` }}
/>
</div>
</div>
) : (
<span className="text-slate-700 text-[11px]">
{t.days_held != null ? `${t.days_held}j` : '—'}
</span>
)}
</td>
<td className="px-3 py-2 text-right">
<PnlBadge pnl={t.pnl_pct} />