feat: saxo connector
This commit is contained in:
@@ -474,7 +474,7 @@ function SaxoConnectionCard() {
|
||||
<div className="card">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<div className="section-title mb-0 flex items-center gap-1">
|
||||
<Link2 className="w-3 h-3" /> Saxo OpenAPI (SIM)
|
||||
<Link2 className="w-3 h-3" /> Saxo OpenAPI ({(status?.environment ?? 'sim').toUpperCase()})
|
||||
</div>
|
||||
<span className={clsx('badge', status?.connected ? 'badge-green' : 'badge-red')}>
|
||||
{status?.connected ? 'Connecté' : 'Déconnecté'}
|
||||
|
||||
@@ -8,10 +8,13 @@ import {
|
||||
useOptionChainSlice, usePriceStrategy, useOptimizeStrategy,
|
||||
useScenarios, useSaveScenario, useDeleteScenario,
|
||||
useSavedStrategies, useSaveStrategyRecord, useDeleteSavedStrategy,
|
||||
useWatchlistTickers,
|
||||
type StrategyLeg, type StrategyScenario, type PriceCombo, type StrategyCandidate,
|
||||
type OptimizeConstraints, type SavedScenario,
|
||||
} from '../hooks/useApi'
|
||||
|
||||
type WatchlistEntry = { ticker: string; is_active: number | boolean; added_by: string }
|
||||
|
||||
const STRIKE_PCTS = [80, 85, 90, 95, 100, 105, 110, 115, 120]
|
||||
const DELTA_NEUTRAL_THRESHOLD = 0.15
|
||||
|
||||
@@ -94,11 +97,12 @@ function GreeksTile({ label, now, scenario }: { label: string; now: number; scen
|
||||
// ── Scenario panel ────────────────────────────────────────────────────────────
|
||||
|
||||
function ScenarioPanel({
|
||||
symbol, setSymbol, horizonDays, setHorizonDays, scenario, setScenario,
|
||||
symbol, setSymbol, horizonDays, setHorizonDays, scenario, setScenario, watchlistTickers,
|
||||
}: {
|
||||
symbol: string; setSymbol: (v: string) => void
|
||||
horizonDays: number; setHorizonDays: (v: number) => void
|
||||
scenario: StrategyScenario; setScenario: (v: StrategyScenario) => void
|
||||
watchlistTickers: string[]
|
||||
}) {
|
||||
const slider = (
|
||||
key: 'spot_shock_pct' | 'iv_level_shift' | 'skew_tilt' | 'term_shift',
|
||||
@@ -127,7 +131,11 @@ function ScenarioPanel({
|
||||
onChange={(e) => setSymbol(e.target.value.toUpperCase())}
|
||||
className="w-full bg-dark-700 border border-slate-700/50 rounded px-2 py-1.5 text-sm text-white"
|
||||
placeholder="SPY, QQQ, GLD…"
|
||||
list="strategy-builder-watchlist"
|
||||
/>
|
||||
<datalist id="strategy-builder-watchlist">
|
||||
{watchlistTickers.map(t => <option key={t} value={t} />)}
|
||||
</datalist>
|
||||
</div>
|
||||
<div className="w-28">
|
||||
<label className="stat-label block mb-1">Horizon (j)</label>
|
||||
@@ -483,6 +491,11 @@ export default function StrategyBuilder() {
|
||||
})
|
||||
const [activeTemplate, setActiveTemplate] = useState<string | null>(null)
|
||||
|
||||
const { data: watchlistData } = useWatchlistTickers()
|
||||
const watchlistTickers = ((watchlistData?.tickers ?? []) as WatchlistEntry[])
|
||||
.filter(t => t.is_active)
|
||||
.map(t => t.ticker)
|
||||
|
||||
const { data: chain, isLoading: chainLoading, isError: chainError, refetch: refetchChain, isFetching } =
|
||||
useOptionChainSlice(symbol, horizonDays, 3)
|
||||
|
||||
@@ -584,7 +597,7 @@ export default function StrategyBuilder() {
|
||||
)}
|
||||
|
||||
<ScenarioPanel symbol={symbol} setSymbol={setSymbol} horizonDays={horizonDays} setHorizonDays={setHorizonDays}
|
||||
scenario={scenario} setScenario={setScenario} />
|
||||
scenario={scenario} setScenario={setScenario} watchlistTickers={watchlistTickers} />
|
||||
|
||||
<ScenarioLibrary symbol={symbol} scenario={scenario} onLoad={handleLoadScenario} />
|
||||
<SavedStrategiesLibrary symbol={symbol} onLoad={(legs, templateName) => { setActiveTemplate(templateName); setLegs(legs) }} />
|
||||
|
||||
Reference in New Issue
Block a user