Skip to content

Commit 90e26c2

Browse files
authored
Merge pull request #177 from Travisun/fix/chart-cyq-align-cross-market-summary
fix: watchlist quote reliability, portfolio InstrumentRef, and ETF cache
2 parents 8101605 + 27c6219 commit 90e26c2

69 files changed

Lines changed: 4422 additions & 549 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

apps/server/src/index.ts

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -733,6 +733,11 @@ app.post<{ Body: Record<string, unknown> }>('/api/instruments/quotes', async (re
733733
return { success: r.success, data: r.data, message: r.message }
734734
})
735735

736+
app.post<{ Body: Record<string, unknown> }>('/api/instruments/quote', async (req) => {
737+
const r = await hub.dispatch('instrument_quote', req.body ?? {})
738+
return { success: r.success, data: r.data, message: r.message }
739+
})
740+
736741
app.post<{ Body: Record<string, unknown> }>('/api/instruments/chart', async (req) => {
737742
const r = await hub.dispatch('instrument_chart', req.body ?? {})
738743
return { success: r.success, data: r.data, message: r.message }
@@ -1992,16 +1997,39 @@ app.put<{ Body: { groups?: Array<{ id: string; title: string; sortOrder: number;
19921997
},
19931998
)
19941999

1995-
app.post<{ Body: { code: string; shares: number; price: number; side?: string; date?: string; market?: string } }>(
2000+
app.post<{ Body: {
2001+
code: string
2002+
shares: number
2003+
price: number
2004+
side?: string
2005+
date?: string
2006+
market?: string
2007+
assetClass?: string
2008+
instrument?: { market: string; assetClass: string; symbol: string; exchange?: string }
2009+
} }>(
19962010
'/api/portfolio/trade',
19972011
async (req, reply) => {
1998-
const { code, shares, price, side = 'buy', date, market } = req.body ?? {}
2012+
const { code, shares, price, side = 'buy', date, market, assetClass, instrument } = req.body ?? {}
19992013
if (!code || !shares || !price) return reply.code(400).send({ error: 'code, shares, price required' })
20002014
const pm = hub.de.portfolio
20012015
const m = market?.trim() || undefined
2002-
const result = side === 'sell'
2003-
? await pm.sell(code, shares, price, date, '', m as import('@opptrix/shared').Market | undefined)
2004-
: await pm.buy(code, shares, price, date, '', m as import('@opptrix/shared').Market | undefined)
2016+
const ac = assetClass?.trim() || undefined
2017+
const inst = instrument && typeof instrument === 'object'
2018+
? instrument as import('@opptrix/shared').InstrumentRef
2019+
: undefined
2020+
const result = await pm.recordTrade(
2021+
side === 'sell' ? 'sell' : 'buy',
2022+
code,
2023+
shares,
2024+
price,
2025+
{
2026+
date,
2027+
name: '',
2028+
market: m as import('@opptrix/shared').Market | undefined,
2029+
assetClass: ac as import('@opptrix/shared').AssetClass | undefined,
2030+
instrument: inst,
2031+
},
2032+
)
20052033
return { success: true, trade: result }
20062034
},
20072035
)

client-ui/src/api/client.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -598,6 +598,17 @@ export const research = {
598598
signal,
599599
),
600600

601+
/** 单标的最新价 — 关注添加后立即拉价;默认 fresh 跳过覆盖层缓存 */
602+
instrumentQuote: (
603+
instrument: InstrumentRef,
604+
opts?: { fresh?: boolean },
605+
signal?: AbortSignal,
606+
) => postInstrument<{ quote: UnifiedInstrumentQuote; failed?: { code: string; reason: string } }>(
607+
'/instruments/quote',
608+
{ instrument, fresh: opts?.fresh ?? true },
609+
signal,
610+
),
611+
601612
instrumentChart: async (
602613
instrument: InstrumentRef,
603614
period: ChartPeriod | 'daily' | 'weekly' | 'monthly' | 'intraday' = 'daily',
@@ -1276,7 +1287,14 @@ export async function reloadInstalledProvider(providerId: string) {
12761287
}
12771288

12781289
export async function portfolioTrade(payload: {
1279-
code: string; shares: number; price: number; side?: 'buy' | 'sell'; date?: string; market?: string
1290+
code: string
1291+
shares: number
1292+
price: number
1293+
side?: 'buy' | 'sell'
1294+
date?: string
1295+
market?: string
1296+
assetClass?: string
1297+
instrument?: { market: string; assetClass: string; symbol: string; exchange?: string }
12801298
}) {
12811299
const resp = await fetchWithTimeout(`${API_BASE}/portfolio/trade`, {
12821300
method: 'POST',

client-ui/src/chat/ComposerStockMentionList.tsx

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
import { ProgressBar, Text } from '@fluentui/react-components'
22
import type { WatchlistItem } from '../types/market'
33
import {
4-
displayCodeFromInstrument,
4+
formatInstrumentSearchHitSubtitle,
55
normalizeWatchlistItem,
6-
tryResolveWatchlistInstrument,
76
watchlistItemKey,
87
} from '../market/instrument'
98
import { UNIVERSE_PREP_COPY, type UniversePrepUi } from '../market/useInstrumentSearchWithUniversePrep'
109
import ComposerTooltipMenu, {
1110
COMPOSER_MENU_WIDTH,
1211
ComposerTooltipMenuItem,
1312
} from './ComposerTooltipMenu'
13+
import HoverMarqueeText from './HoverMarqueeText'
1414

1515
interface Props {
1616
open: boolean
@@ -79,21 +79,20 @@ export default function ComposerStockMentionList({
7979
) : null}
8080
{items.map((item, index) => {
8181
const row = normalizeWatchlistItem(item)
82-
const ref = tryResolveWatchlistInstrument(row)
83-
const codeLabel = ref ? displayCodeFromInstrument(ref) : row.code
8482
const active = index === activeIndex
83+
const subtitle = formatInstrumentSearchHitSubtitle(row)
8584
return (
8685
<ComposerTooltipMenuItem
8786
key={watchlistItemKey(row)}
8887
active={active}
88+
title={subtitle}
89+
className="opptrix-hover-marquee-host"
8990
onMouseEnter={() => onHover(index)}
9091
onClick={() => onSelect(row)}
9192
>
9293
<span className="opptrix-composer-tooltip-menu__item-main">
93-
<span className="opptrix-composer-tooltip-menu__item-title">{row.name}</span>
94-
</span>
95-
<span className="opptrix-composer-tooltip-menu__item-code">
96-
{codeLabel}
94+
<HoverMarqueeText text={row.name} className="opptrix-composer-tooltip-menu__item-title" />
95+
<span className="opptrix-composer-tooltip-menu__item-meta">{subtitle}</span>
9796
</span>
9897
</ComposerTooltipMenuItem>
9998
)

client-ui/src/chat/ComposerTooltipMenu.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,16 +194,19 @@ export function ComposerTooltipMenuItem({
194194
onMouseEnter,
195195
children,
196196
className,
197+
title,
197198
}: {
198199
active?: boolean
199200
onClick: () => void
200201
onMouseEnter?: () => void
201202
children: ReactNode
202203
className?: string
204+
title?: string
203205
}) {
204206
return (
205207
<button
206208
type="button"
209+
title={title}
207210
className={mergeClasses(
208211
'opptrix-composer-tooltip-menu__item',
209212
active && 'opptrix-composer-tooltip-menu__item--active',

client-ui/src/market/CrossMarketDetailPlaceholder.tsx

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -73,14 +73,12 @@ interface Props {
7373
stock: WatchlistItem
7474
marketLabel?: string
7575
loading?: boolean
76-
localIndexed?: boolean | null
7776
}
7877

7978
export default function CrossMarketDetailPlaceholder({
8079
stock,
8180
marketLabel,
8281
loading = false,
83-
localIndexed = null,
8482
}: Props) {
8583
const s = useStyles()
8684
const ref = resolveWatchlistInstrument(stock)
@@ -122,11 +120,7 @@ export default function CrossMarketDetailPlaceholder({
122120
) : (
123121
<div className={s.card}>
124122
<Text className={s.muted}>
125-
{localIndexed === true
126-
? '已匹配在线名录。实时行情、K 线与深度分析将在对应数据源接入后在此展示。'
127-
: localIndexed === false
128-
? '在线名录暂未匹配该代码,请核对代码或在对话中直接 @ 引用。'
129-
: '跨市场详情页已就绪;接入行情源后将在此展示概况、K 线与分析模块。'}
123+
跨市场详情页已就绪;接入行情源后将在此展示概况、K 线与分析模块。
130124
</Text>
131125
</div>
132126
)}

client-ui/src/market/CrossMarketDetailTab.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import { resolveWatchlistInstrument } from './instrument'
55

66
interface Props {
77
stock: WatchlistItem
8-
localIndexed?: boolean | null
98
loading?: boolean
109
onManage?: () => void
1110
onSelectPeer?: (item: WatchlistItem) => void
@@ -19,7 +18,6 @@ export default function CrossMarketDetailTab(props: Props) {
1918
<CrossMarketDetailPlaceholder
2019
stock={props.stock}
2120
loading={props.loading}
22-
localIndexed={props.localIndexed}
2321
/>
2422
)
2523
}

0 commit comments

Comments
 (0)