feat: strategy builder
This commit is contained in:
@@ -1534,6 +1534,7 @@ export type StrategyScenario = {
|
||||
manual_grid?: ManualGridCell[]
|
||||
rate?: number
|
||||
n_expiries?: number
|
||||
contract_size?: number
|
||||
}
|
||||
|
||||
export type StrategyLeg = {
|
||||
|
||||
@@ -584,6 +584,7 @@ export default function StrategyBuilder() {
|
||||
const [horizonDays, setHorizonDays] = useState(8)
|
||||
const [scenario, setScenario] = useState<StrategyScenario>({
|
||||
symbol: '', horizon_days: 8, spot_shock_pct: 0, iv_level_shift: 0, skew_tilt: 0, term_shift: 0, manual_grid: [],
|
||||
contract_size: 100_000,
|
||||
})
|
||||
|
||||
// Chain lookup only commits on blur/Enter/datalist-pick, never mid-keystroke — typing
|
||||
@@ -662,11 +663,11 @@ export default function StrategyBuilder() {
|
||||
const handleLoadScenario = (s: SavedScenario) => {
|
||||
setSymbol(s.symbol)
|
||||
setHorizonDays(s.horizon_days)
|
||||
setScenario({
|
||||
setScenario(prev => ({
|
||||
symbol: s.symbol, horizon_days: s.horizon_days, spot_shock_pct: s.spot_shock_pct,
|
||||
iv_level_shift: s.iv_level_shift, skew_tilt: s.skew_tilt, term_shift: s.term_shift,
|
||||
manual_grid: s.manual_grid,
|
||||
})
|
||||
manual_grid: s.manual_grid, contract_size: prev.contract_size,
|
||||
}))
|
||||
}
|
||||
|
||||
const handleSaveStrategy = () => {
|
||||
@@ -721,13 +722,24 @@ export default function StrategyBuilder() {
|
||||
<div className="card space-y-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="stat-label">Jambes (1-4) — Spot {chain.spot}</div>
|
||||
<button
|
||||
onClick={addLeg}
|
||||
disabled={legs.length >= 4}
|
||||
className="flex items-center gap-1 text-xs bg-blue-600 hover:bg-blue-500 disabled:opacity-40 text-white px-2.5 py-1 rounded"
|
||||
>
|
||||
<Plus className="w-3.5 h-3.5" /> Ajouter une jambe
|
||||
</button>
|
||||
<div className="flex items-center gap-3">
|
||||
<label className="flex items-center gap-1.5 text-xs text-slate-400" title="Notionnel par contrat (ex. 100 000 = 1 lot standard EURUSD). S'applique à chaque jambe, multiplié par sa quantité.">
|
||||
Nominal (USD)
|
||||
<input
|
||||
type="number" step={1000} min={1}
|
||||
value={scenario.contract_size ?? 100_000}
|
||||
onChange={(e) => setScenario(s => ({ ...s, contract_size: parseFloat(e.target.value) || 100_000 }))}
|
||||
className="w-28 bg-dark-700 border border-slate-700/50 rounded px-2 py-1 text-slate-200"
|
||||
/>
|
||||
</label>
|
||||
<button
|
||||
onClick={addLeg}
|
||||
disabled={legs.length >= 4}
|
||||
className="flex items-center gap-1 text-xs bg-blue-600 hover:bg-blue-500 disabled:opacity-40 text-white px-2.5 py-1 rounded"
|
||||
>
|
||||
<Plus className="w-3.5 h-3.5" /> Ajouter une jambe
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
{legs.map((leg, i) => (
|
||||
|
||||
Reference in New Issue
Block a user