feat: strategy builder
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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} />}
|
||||
|
||||
Reference in New Issue
Block a user