feat: instrument analysis

This commit is contained in:
OpenSquared
2026-06-28 16:36:28 +02:00
parent 0693d41d91
commit e4e17330b4
6 changed files with 715 additions and 649 deletions

View File

@@ -41,6 +41,30 @@ const SOURCE_LABELS: Record<string, string> = {
reports: 'Reports',
}
interface DeskRef {
id: number
name: string
type: string
active: boolean
}
const DESK_TYPE_TO_SOURCE: Record<string, string> = {
news: 'news',
eco: 'eco',
fundamental: 'news',
technical: 'technical',
report: 'reports',
}
const DESK_TYPE_COLORS: Record<string, string> = {
news: 'bg-blue-900/30 border-blue-600/50 text-blue-300',
fundamental: 'bg-amber-900/30 border-amber-600/50 text-amber-300',
technical: 'bg-cyan-900/30 border-cyan-600/50 text-cyan-300',
eco: 'bg-emerald-900/30 border-emerald-600/50 text-emerald-300',
report: 'bg-purple-900/30 border-purple-600/50 text-purple-300',
sentiment: 'bg-rose-900/30 border-rose-600/50 text-rose-300',
}
const CAT_COLORS: Record<string, string> = {
event_calendar: 'text-amber-400',
geopolitical: 'text-red-400',
@@ -120,7 +144,6 @@ function BootstrapAction({ action }: { action: ActionDef }) {
// ── Check Market Events config panel ────────────────────────────────────────
interface CheckEventsConfig {
sources: string[]
news_impact_min: number
eco_z_threshold: number
technical_lookback_days: number
@@ -148,7 +171,6 @@ function presetDates(hours: number): { date_from: string; date_to: string } {
function makeDefaultConfig(): CheckEventsConfig {
return {
sources: ['news', 'eco', 'technical', 'reports'],
news_impact_min: 0.55,
eco_z_threshold: 1.5,
technical_lookback_days: 7,
@@ -160,30 +182,40 @@ function makeDefaultConfig(): CheckEventsConfig {
function CheckEventsPanel() {
const [config, setConfig] = useState<CheckEventsConfig>(makeDefaultConfig)
const [desks, setDesks] = useState<DeskRef[]>([])
const [running, setRunning] = useState(false)
const [result, setResult] = useState<ActionResult | null>(null)
const [error, setError] = useState<string | null>(null)
const [expanded, setExpanded] = useState(false)
const [showConfig, setShowConfig] = useState(false)
const toggleSource = (src: string) => {
setConfig(c => ({
...c,
sources: c.sources.includes(src)
? c.sources.filter(s => s !== src)
: [...c.sources, src],
}))
useEffect(() => {
fetch(`${API}/api/ai-desks`)
.then(r => r.json())
.then(d => setDesks(d))
.catch(() => {})
}, [])
const toggleDesk = async (desk: DeskRef) => {
const next = !desk.active
setDesks(prev => prev.map(d => d.id === desk.id ? { ...d, active: next } : d))
await fetch(`${API}/api/ai-desks/${desk.id}/toggle`, { method: 'PATCH' }).catch(() => {})
}
const run = async () => {
setRunning(true)
setResult(null)
setError(null)
const sources = [...new Set(
desks
.filter(d => d.active && DESK_TYPE_TO_SOURCE[d.type])
.map(d => DESK_TYPE_TO_SOURCE[d.type])
)]
try {
const res = await fetch(`${API}/api/actions/check-market-events`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(config),
body: JSON.stringify({ ...config, sources }),
})
if (!res.ok) {
const err = await res.json().catch(() => ({ detail: res.statusText }))
@@ -211,7 +243,7 @@ function CheckEventsPanel() {
<span className="w-2 h-2 rounded-full bg-emerald-400" />
<span className="text-slate-100 font-semibold text-sm">Check New Market Events</span>
<span className="text-xs text-slate-500 ml-1">
Scans news · FRED · MA crossovers · rapports institutionnels
Scans news · éco. calendar · MA crossovers · rapports institutionnels
</span>
</div>
<button
@@ -221,7 +253,7 @@ function CheckEventsPanel() {
{showConfig ? 'Masquer config' : 'Config'}
</button>
<button
disabled={running || config.sources.length === 0}
disabled={running}
onClick={run}
className="flex items-center gap-2 bg-emerald-600 hover:bg-emerald-500 disabled:bg-slate-700
disabled:text-slate-500 text-white text-sm font-medium rounded-lg px-4 py-2 transition-colors"
@@ -266,21 +298,25 @@ function CheckEventsPanel() {
</div>
</div>
{/* Sources */}
{/* Desks IA */}
<div>
<div className="text-slate-400 mb-2 text-xs uppercase tracking-wide">Sources actives</div>
<div className="text-slate-400 mb-2 text-xs uppercase tracking-wide">Desks IA</div>
<div className="flex flex-wrap gap-2">
{['news', 'eco', 'technical', 'reports'].map(s => (
<button key={s} onClick={() => toggleSource(s)}
className={`px-3 py-1 rounded-full text-xs border transition-colors ${
config.sources.includes(s)
? 'bg-emerald-600/30 border-emerald-500/50 text-emerald-300'
: 'border-slate-700 text-slate-500 hover:border-slate-500'
{desks.map(desk => (
<button key={desk.id} onClick={() => toggleDesk(desk)}
className={`flex items-center gap-1.5 px-3 py-1 rounded border text-xs transition-colors ${
desk.active
? (DESK_TYPE_COLORS[desk.type] ?? 'bg-slate-700/40 border-slate-600 text-slate-300')
: 'border-slate-700 text-slate-600 hover:border-slate-500 hover:text-slate-400'
}`}
>
{SOURCE_LABELS[s]}
<span className={`w-1.5 h-1.5 rounded-full flex-shrink-0 ${desk.active ? 'bg-current' : 'bg-slate-700'}`} />
{desk.name}
</button>
))}
{desks.length === 0 && (
<span className="text-xs text-slate-600 italic">Aucun desk configuré</span>
)}
</div>
</div>