diff --git a/web/src/views/SimpleView.tsx b/web/src/views/SimpleView.tsx index 6dc0da6..d8a9e0a 100644 --- a/web/src/views/SimpleView.tsx +++ b/web/src/views/SimpleView.tsx @@ -50,6 +50,8 @@ type SimpleRuntimeSnapshot = { orderId?: string; }; +type MarketPriceSource = 'live' | 'latest_close' | 'reference_price' | null; + const SIMPLE_AUTO_PROFILE_NAME = 'Simple Auto Profile'; const SIMPLE_AUTO_PROFILE_KEY = SIMPLE_AUTO_PROFILE_NAME.toLowerCase(); const SIMPLE_SYMBOL_DATALIST_ID = 'simple-supported-symbols'; @@ -313,6 +315,10 @@ function buildDraftFromEntry(entry: ManualEntryPayload): SimpleSetupDraft { }; } +function inferMarketPriceSourceFromEntry(entry: ManualEntryPayload): MarketPriceSource { + return entry.reference_price ? 'reference_price' : null; +} + function formatSetupStatus(status?: string | null): string { const normalized = String(status || '').trim().toLowerCase(); switch (normalized) { @@ -442,6 +448,7 @@ export function SimpleView() { const [draft, setDraft] = useState(DEFAULT_DRAFT); const [submitting, setSubmitting] = useState(false); const [loadingPrice, setLoadingPrice] = useState(false); + const [marketPriceSource, setMarketPriceSource] = useState(null); const [message, setMessage] = useState(null); const [error, setError] = useState(null); const marketPriceRequestSymbolRef = useRef(''); @@ -526,6 +533,7 @@ export function SimpleView() { useEffect(() => { if (!livePrice) return; + setMarketPriceSource('live'); setDraft((prev) => ( prev.currentMarketPrice === livePrice.toFixed(4) ? prev @@ -547,7 +555,7 @@ export function SimpleView() { } const timer = window.setTimeout(() => { - void handleLoadMarketPrice(); + void handleLoadMarketPrice({ silent: true }); }, 250); return () => { @@ -573,18 +581,25 @@ export function SimpleView() { setSavedSetups(normalizeSimpleEntries(entryRows)); } - async function handleLoadMarketPrice() { + function setMarketPriceValue(value: string, source: MarketPriceSource) { + setMarketPriceSource(source); + updateDraft('currentMarketPrice', value); + } + + async function handleLoadMarketPrice(options?: { silent?: boolean }) { if (!normalizedSymbol) return; const requestSymbol = normalizedSymbol; marketPriceRequestSymbolRef.current = requestSymbol; setLoadingPrice(true); - setError(null); - setMessage(null); + if (!options?.silent) { + setError(null); + setMessage(null); + } try { if (livePrice > 0) { if (marketPriceRequestSymbolRef.current === requestSymbol) { - updateDraft('currentMarketPrice', livePrice.toFixed(4)); + setMarketPriceValue(livePrice.toFixed(4), 'live'); } return; } @@ -593,19 +608,19 @@ export function SimpleView() { const lastClose = Number(bars?.[bars.length - 1]?.close || 0); if (Number.isFinite(lastClose) && lastClose > 0) { if (marketPriceRequestSymbolRef.current === requestSymbol) { - updateDraft('currentMarketPrice', lastClose.toFixed(4)); + setMarketPriceValue(lastClose.toFixed(4), 'latest_close'); } } else { const researchProfile = await fetchResearchProfile(requestSymbol).catch(() => null); const fallbackReferencePrice = extractReferencePriceFromResearchProfile(researchProfile); if (fallbackReferencePrice && marketPriceRequestSymbolRef.current === requestSymbol) { - updateDraft('currentMarketPrice', fallbackReferencePrice.toFixed(4)); + setMarketPriceValue(fallbackReferencePrice.toFixed(4), 'reference_price'); return; } throw new Error('No live price or latest close is available for this symbol right now.'); } } catch (err: any) { - if (marketPriceRequestSymbolRef.current === requestSymbol) { + if (!options?.silent && marketPriceRequestSymbolRef.current === requestSymbol) { setError(err?.message ?? 'Failed to load market data'); } } finally { @@ -641,6 +656,7 @@ export function SimpleView() { await refreshSetupList(); setEditingSetupId(null); + setMarketPriceSource(draft.currentMarketPrice ? marketPriceSource : null); setDraft({ ...DEFAULT_DRAFT, currentMarketPrice: draft.currentMarketPrice, @@ -654,6 +670,7 @@ export function SimpleView() { function handleEdit(entry: ManualEntryPayload) { setEditingSetupId(String(entry.stock_instance_id || '')); + setMarketPriceSource(inferMarketPriceSourceFromEntry(entry)); setDraft(buildDraftFromEntry(entry)); setMessage(null); setError(null); @@ -665,6 +682,7 @@ export function SimpleView() { await deleteManualEntry(entryId); if (editingSetupId === entryId) { setEditingSetupId(null); + setMarketPriceSource(null); setDraft(DEFAULT_DRAFT); } await refreshSetupList(); @@ -694,6 +712,7 @@ export function SimpleView() { type="button" onClick={() => { setEditingSetupId(null); + setMarketPriceSource(draft.currentMarketPrice ? marketPriceSource : null); setDraft({ ...DEFAULT_DRAFT, currentMarketPrice: draft.currentMarketPrice, @@ -716,11 +735,14 @@ export function SimpleView() { Symbol setDraft((prev) => ({ - ...prev, - symbol: e.target.value.toUpperCase(), - currentMarketPrice: '', - }))} + onChange={(e) => { + setMarketPriceSource(null); + setDraft((prev) => ({ + ...prev, + symbol: e.target.value.toUpperCase(), + currentMarketPrice: '', + })); + }} list={SIMPLE_SYMBOL_DATALIST_ID} placeholder="AAPL" /> @@ -743,11 +765,14 @@ export function SimpleView() {