feat: trade lifecycle management — close, archive, target/stop alerts
- DB: 9 new columns on trade_entry_prices (status, closed_at, close_reason,
close_note, pnl_realized, close_price, target_pct, stop_loss_pct, signal_threshold)
via ALTER TABLE migration; close_trade(), get_closed_trades(),
update_trade_exit_params() helpers; exit_defaults config key
- Backend: PATCH /trades/{id}/close, PATCH /trades/{id}/exit-params,
GET/PUT /exit-defaults, GET /closed-trades with win-rate/avg-PnL stats;
trade-mtm now computes alert_type (target_reached|stop_loss) per trade
- Journal: new "Fermés" tab with closed trades table + stats banner (win rate,
avg PnL, total PnL, best trade); open trades show Cible/Stop progress bar +
🎯/🛑 alert badges + 1-click close modal (price, reason, note)
- Config: new "Paramètres de sortie" panel — target_pct, stop_loss_pct,
signal_reversal_mode, signal_reversal_threshold with live sliders
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -404,10 +404,60 @@ export const useTradeMtm = (days = 30) =>
|
||||
queryKey: ['journal-mtm', days],
|
||||
queryFn: () => api.get(`/journal/trade-mtm?days=${days}`).then(r => r.data),
|
||||
staleTime: 0,
|
||||
refetchInterval: 5 * 60_000, // re-fetch live prices every 5 minutes
|
||||
refetchInterval: 5 * 60_000,
|
||||
refetchIntervalInBackground: false,
|
||||
})
|
||||
|
||||
export const useClosedTrades = (days = 180) =>
|
||||
useQuery({
|
||||
queryKey: ['journal-closed', days],
|
||||
queryFn: () => api.get(`/journal/closed-trades?days=${days}`).then(r => r.data),
|
||||
staleTime: 30_000,
|
||||
})
|
||||
|
||||
export const useExitDefaults = () =>
|
||||
useQuery({
|
||||
queryKey: ['journal-exit-defaults'],
|
||||
queryFn: () => api.get('/journal/exit-defaults').then(r => r.data),
|
||||
staleTime: 60_000,
|
||||
})
|
||||
|
||||
export const useCloseTrade = () => {
|
||||
const qc = useQueryClient()
|
||||
return useMutation({
|
||||
mutationFn: ({ id, body }: { id: number; body: {
|
||||
close_price: number; pnl_realized?: number;
|
||||
close_reason: string; close_note: string
|
||||
}}) => api.patch(`/journal/trades/${id}/close`, body).then(r => r.data),
|
||||
onSuccess: () => {
|
||||
qc.invalidateQueries({ queryKey: ['journal-mtm'] })
|
||||
qc.invalidateQueries({ queryKey: ['journal-closed'] })
|
||||
qc.invalidateQueries({ queryKey: ['journal-summary'] })
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export const useUpdateExitParams = () => {
|
||||
const qc = useQueryClient()
|
||||
return useMutation({
|
||||
mutationFn: ({ id, body }: { id: number; body: {
|
||||
target_pct?: number; stop_loss_pct?: number; signal_threshold?: number
|
||||
}}) => api.patch(`/journal/trades/${id}/exit-params`, body).then(r => r.data),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: ['journal-mtm'] }),
|
||||
})
|
||||
}
|
||||
|
||||
export const useSaveExitDefaults = () => {
|
||||
const qc = useQueryClient()
|
||||
return useMutation({
|
||||
mutationFn: (body: {
|
||||
target_pct?: number; stop_loss_pct?: number;
|
||||
signal_reversal_mode?: string; signal_reversal_threshold?: number
|
||||
}) => api.put('/journal/exit-defaults', body).then(r => r.data),
|
||||
onSuccess: () => qc.invalidateQueries({ queryKey: ['journal-exit-defaults'] }),
|
||||
})
|
||||
}
|
||||
|
||||
// ── Risk Profiles ─────────────────────────────────────────────────────────────
|
||||
|
||||
export const useRiskProfiles = () =>
|
||||
|
||||
Reference in New Issue
Block a user