feat: strategy builder

This commit is contained in:
OpenSquared
2026-07-18 23:35:17 +02:00
parent ca30e37942
commit 2b6b32958d
2 changed files with 25 additions and 9 deletions

View File

@@ -149,6 +149,16 @@ def optimize(req: OptimizeRequest):
)
except ValueError as e:
raise HTTPException(status_code=404, detail=str(e))
except Exception as e:
import traceback
from services.database import log_system_event
tb = traceback.format_exc()
log_system_event(
level="ERROR", source="strategy_optimizer",
message=f"Optimize failed for {req.scenario.symbol}: {e}",
ticker=req.scenario.symbol, details={"error": str(e), "traceback": tb},
)
raise HTTPException(status_code=500, detail=f"Erreur optimiseur: {e} (détail complet dans System Logs)")
return results

View File

@@ -566,11 +566,17 @@ function SavedStrategiesLibrary({ symbol, onLoad }: { symbol: string; onLoad: (l
// ── Page ──────────────────────────────────────────────────────────────────────
export default function StrategyBuilder() {
const [symbol, setSymbol] = useState('SPY')
const [symbol, setSymbol] = useState('')
const [debouncedSymbol, setDebouncedSymbol] = useState('')
const [horizonDays, setHorizonDays] = useState(8)
const [scenario, setScenario] = useState<StrategyScenario>({
symbol: 'SPY', horizon_days: 8, spot_shock_pct: 0, iv_level_shift: 0, skew_tilt: 0, term_shift: 0, manual_grid: [],
symbol: '', horizon_days: 8, spot_shock_pct: 0, iv_level_shift: 0, skew_tilt: 0, term_shift: 0, manual_grid: [],
})
useEffect(() => {
const t = setTimeout(() => setDebouncedSymbol(symbol.trim()), 500)
return () => clearTimeout(t)
}, [symbol])
const [legs, setLegs] = useState<StrategyLeg[]>([])
const [constraints, setConstraints] = useState<OptimizeConstraints>({
max_legs: 4, delta_threshold: 0.15, max_loss_cap: null, objective: 'net_pnl', top_n: 20,
@@ -585,11 +591,11 @@ export default function StrategyBuilder() {
])).sort()
const { data: chain, isLoading: chainLoading, isError: chainError, error: chainErrorObj, refetch: refetchChain, isFetching } =
useOptionChainSlice(symbol, horizonDays, 3)
useOptionChainSlice(debouncedSymbol, horizonDays, 3)
useEffect(() => {
setScenario(s => ({ ...s, symbol, horizon_days: horizonDays }))
}, [symbol, horizonDays])
setScenario(s => ({ ...s, symbol: debouncedSymbol, horizon_days: horizonDays }))
}, [debouncedSymbol, horizonDays])
useEffect(() => {
if (chain && chain.expiries.length && legs.length === 0) {
@@ -681,17 +687,17 @@ export default function StrategyBuilder() {
{chainError && (
<div className="px-4 py-3 rounded border border-red-700/40 bg-red-900/10 text-xs text-red-300 flex items-center gap-2">
<AlertTriangle className="w-4 h-4" />
{(chainErrorObj as any)?.response?.data?.detail ?? `Chaîne d'options indisponible pour ${symbol}. Essayez un autre symbole.`}
{(chainErrorObj as any)?.response?.data?.detail ?? `Chaîne d'options indisponible pour ${debouncedSymbol}. Essayez un autre symbole.`}
</div>
)}
<ScenarioPanel symbol={symbol} setSymbol={setSymbol} horizonDays={horizonDays} setHorizonDays={setHorizonDays}
scenario={scenario} setScenario={setScenario} watchlistTickers={watchlistTickers} />
<ScenarioLibrary symbol={symbol} scenario={scenario} onLoad={handleLoadScenario} />
<SavedStrategiesLibrary symbol={symbol} onLoad={(legs, templateName) => { setActiveTemplate(templateName); setLegs(legs) }} />
<ScenarioLibrary symbol={debouncedSymbol} scenario={scenario} onLoad={handleLoadScenario} />
<SavedStrategiesLibrary symbol={debouncedSymbol} onLoad={(legs, templateName) => { setActiveTemplate(templateName); setLegs(legs) }} />
{chainLoading && <div className="card-sm text-xs text-slate-500">Chargement de la chaîne réelle ({symbol})…</div>}
{chainLoading && <div className="card-sm text-xs text-slate-500">Chargement de la chaîne réelle ({debouncedSymbol})…</div>}
{chain && <ScenarioGrid chain={chain} spot={chain.spot} scenario={scenario} setScenario={setScenario} />}
{chain && <VolSurfaceHeatmap chain={chain} spot={chain.spot} />}