feat: vertical 3-column timeline + MA regime bootstrap engine
- TimelineVertical: replace horizontal frise with Y=time vertical layout, 3 columns (Long/Medium/Short), auto-scroll to selected date, sub-columns for overlapping events, today/selected-date horizontal lines - ma_analyzer.py: detect MA50/MA200 crossovers + MA100 slope changes + MA20 direction swings on EUR/USD, Brent, Gold, S&P500, US10Y (5y history) with 5-bar confirmation, dedup, GPT-4o-mini enrichment, idempotent DB save - POST /api/timeline/bootstrap-ma endpoint to trigger analysis - Bootstrap MA button in Timeline page with loading state + result count Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
import { useState, useEffect, useCallback } from 'react'
|
||||
import { useSearchParams } from 'react-router-dom'
|
||||
import { ChevronLeft, ChevronRight, Sparkles, Calendar, Clock, Layers, RefreshCw, List } from 'lucide-react'
|
||||
import { ChevronLeft, ChevronRight, Sparkles, Calendar, Clock, Layers, RefreshCw, List, Cpu } from 'lucide-react'
|
||||
import axios from 'axios'
|
||||
import clsx from 'clsx'
|
||||
import TimelineFrise from '../components/TimelineFrise'
|
||||
import TimelineVertical from '../components/TimelineVertical'
|
||||
import EventManager from '../components/EventManager'
|
||||
|
||||
const api = axios.create({ baseURL: '/api' })
|
||||
@@ -191,6 +191,8 @@ export default function Timeline() {
|
||||
const [generating, setGenerating] = useState(false)
|
||||
const [bootstrapped, setBootstrapped] = useState(false)
|
||||
const [showManager, setShowManager] = useState(false)
|
||||
const [bootstrappingMA, setBootstrappingMA] = useState(false)
|
||||
const [maResult, setMaResult] = useState<{ detected: number; saved: number } | null>(null)
|
||||
|
||||
const fetchDay = useCallback(async (d: string) => {
|
||||
setLoading(true)
|
||||
@@ -250,6 +252,18 @@ export default function Timeline() {
|
||||
}
|
||||
}, [allEvents.length, bootstrapped, bootstrap])
|
||||
|
||||
const bootstrapMA = useCallback(async () => {
|
||||
setBootstrappingMA(true)
|
||||
setMaResult(null)
|
||||
try {
|
||||
const res = await api.post('/timeline/bootstrap-ma')
|
||||
setMaResult({ detected: res.data.detected, saved: res.data.saved })
|
||||
await fetchEvents()
|
||||
} catch { /* ignore */ } finally {
|
||||
setBootstrappingMA(false)
|
||||
}
|
||||
}, [fetchEvents])
|
||||
|
||||
const navigate = (days: number) => setRefDate(prev => offsetDate(prev, days))
|
||||
|
||||
return (
|
||||
@@ -278,6 +292,20 @@ export default function Timeline() {
|
||||
<List className="w-3 h-3" />
|
||||
Gérer événements
|
||||
</button>
|
||||
<button
|
||||
onClick={bootstrapMA}
|
||||
disabled={bootstrappingMA}
|
||||
className="flex items-center gap-1.5 px-3 py-1.5 text-xs text-emerald-400 border border-emerald-700/40 rounded-lg hover:bg-emerald-900/20 transition-colors disabled:opacity-40"
|
||||
title="Analyser EUR/USD, Brent, Gold, S&P500, US10Y via MAs → générer les périodes via GPT"
|
||||
>
|
||||
<Cpu className="w-3 h-3" />
|
||||
{bootstrappingMA ? 'Analyse MA...' : 'Bootstrap MA'}
|
||||
</button>
|
||||
{maResult && (
|
||||
<span className="text-xs text-emerald-400/70">
|
||||
{maResult.saved} périodes ajoutées
|
||||
</span>
|
||||
)}
|
||||
<button
|
||||
onClick={() => bootstrap()}
|
||||
className="flex items-center gap-1.5 px-3 py-1.5 text-xs text-slate-400 border border-slate-700/40 rounded-lg hover:bg-dark-700 transition-colors"
|
||||
@@ -342,9 +370,9 @@ export default function Timeline() {
|
||||
|
||||
</div>
|
||||
|
||||
{/* Frise chronologique */}
|
||||
{/* Vertical timeline */}
|
||||
{allEvents.length > 0 && (
|
||||
<TimelineFrise events={allEvents} refDate={refDate} onDateChange={setRefDate} />
|
||||
<TimelineVertical events={allEvents} refDate={refDate} onDateChange={setRefDate} />
|
||||
)}
|
||||
|
||||
{/* 3 context panels */}
|
||||
|
||||
Reference in New Issue
Block a user