feat: saxo history

This commit is contained in:
OpenSquared
2026-07-18 18:26:07 +02:00
parent 6cfa36b710
commit ff242bd2a6
9 changed files with 258 additions and 20 deletions

View File

@@ -1,7 +1,8 @@
import { useState, useEffect } from 'react'
import { Link } from 'react-router-dom'
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
import { useSources, useUpdateSources, useUpdateApiKeys, useConfig, useAiStatus, useAnalysisConfig, useSaveAnalysisConfig, useCycleStatus, useUpdateCycleConfig, useTriggerCycle, useCycleStepCatalog, useRiskProfiles, useUpsertProfile, useDeleteProfile, useExitDefaults, useSaveExitDefaults, useOptionsGate, useSaveOptionsGate, useTechIndicatorsConfig, useSaveTechIndicatorsConfig, useInstrumentsWatchlist, useAddWatchlistInstrument, useRemoveWatchlistInstrument, validateTicker, useSaxoStatus, useDisconnectSaxo, useSaxoWatchlist, useUpdateSaxoWatchlist, useSnapshotSaxoNow, type CycleStepDef } from '../hooks/useApi'
import { Settings, Key, Globe, CheckCircle, XCircle, AlertCircle, Save, Eye, EyeOff, Brain, SlidersHorizontal, RefreshCw, Zap, Plus, Trash2, Pencil, X, Lock, Gauge, DollarSign, TrendingUp, ShieldAlert, DatabaseBackup, Radar, Link2, Unlink, Camera } from 'lucide-react'
import { useSources, useUpdateSources, useUpdateApiKeys, useConfig, useAiStatus, useAnalysisConfig, useSaveAnalysisConfig, useCycleStatus, useUpdateCycleConfig, useTriggerCycle, useCycleStepCatalog, useRiskProfiles, useUpsertProfile, useDeleteProfile, useExitDefaults, useSaveExitDefaults, useOptionsGate, useSaveOptionsGate, useTechIndicatorsConfig, useSaveTechIndicatorsConfig, useInstrumentsWatchlist, useAddWatchlistInstrument, useRemoveWatchlistInstrument, validateTicker, useSaxoStatus, useDisconnectSaxo, useSaxoWatchlist, useUpdateSaxoWatchlist, useSnapshotSaxoNow, useValidateSaxoWatchlist, type CycleStepDef } from '../hooks/useApi'
import { Settings, Key, Globe, CheckCircle, XCircle, AlertCircle, Save, Eye, EyeOff, Brain, SlidersHorizontal, RefreshCw, Zap, Plus, Trash2, Pencil, X, Lock, Gauge, DollarSign, TrendingUp, ShieldAlert, DatabaseBackup, Radar, Link2, Unlink, Camera, ShieldCheck, ExternalLink } from 'lucide-react'
import clsx from 'clsx'
const API = ''
@@ -426,6 +427,7 @@ function SaxoConnectionCard() {
const disconnect = useDisconnectSaxo()
const updateWatchlist = useUpdateSaxoWatchlist()
const snapshotNow = useSnapshotSaxoNow()
const { data: validation, refetch: runValidation, isFetching: validating } = useValidateSaxoWatchlist()
const [input, setInput] = useState('')
const [snapMsg, setSnapMsg] = useState('')
@@ -508,8 +510,23 @@ function SaxoConnectionCard() {
)}
</div>
<div className="text-xs text-slate-500 mb-2">
Watchlist snapshotée périodiquement (historique Date/Spot/Expiry/Strike/Bid/Ask/Mid/VolatilityPct/Greeks) :
<div className="flex items-center justify-between mb-2">
<div className="text-xs text-slate-500">
Watchlist snapshotée périodiquement (historique Date/Spot/Expiry/Strike/Bid/Ask/Mid/VolatilityPct/Greeks) :
</div>
<div className="flex items-center gap-2 shrink-0">
<button
onClick={() => runValidation()}
disabled={!status?.connected || validating}
title="Vérifie que chaque symbole résout bien vers un vrai instrument Saxo"
className="flex items-center gap-1 text-xs text-slate-400 hover:text-slate-200 border border-slate-700/50 px-2 py-1 rounded disabled:opacity-40"
>
<ShieldCheck className={clsx('w-3.5 h-3.5', validating && 'animate-pulse')} /> Vérifier l'orthographe
</button>
<Link to="/saxo-history" className="flex items-center gap-1 text-xs text-blue-400 hover:text-blue-300">
<ExternalLink className="w-3.5 h-3.5" /> Historique
</Link>
</div>
</div>
<div className="flex items-center gap-2 mb-2">
<input
@@ -525,15 +542,22 @@ function SaxoConnectionCard() {
</button>
</div>
<div className="flex flex-wrap gap-1.5">
{symbols.map(sym => (
<span key={sym} className="badge-blue flex items-center gap-1.5">
{sym}
<button onClick={() => handleSnapshotNow(sym)} title="Snapshot maintenant" className="hover:text-white">
<Camera className="w-3 h-3" />
</button>
<button onClick={() => removeSymbol(sym)}><X className="w-3 h-3" /></button>
</span>
))}
{symbols.map(sym => {
const check = validation?.find(v => v.symbol === sym.toUpperCase())
return (
<span key={sym} className={clsx('badge flex items-center gap-1.5',
check ? (check.valid ? 'badge-green' : 'badge-red') : 'badge-blue')}
title={check && !check.valid ? check.error ?? 'Symbole non résolu par Saxo' : undefined}
>
{sym}
{check && (check.valid ? <CheckCircle className="w-3 h-3" /> : <XCircle className="w-3 h-3" />)}
<button onClick={() => handleSnapshotNow(sym)} title="Snapshot maintenant" className="hover:text-white">
<Camera className="w-3 h-3" />
</button>
<button onClick={() => removeSymbol(sym)}><X className="w-3 h-3" /></button>
</span>
)
})}
</div>
{snapMsg && <div className="text-[10px] text-slate-500 mt-2">{snapMsg}</div>}
</div>