优化:订单簿信息的完善
This commit is contained in:
parent
6b12938c89
commit
8c455ba00a
@ -249,11 +249,11 @@ export class ClobSdk {
|
||||
// 2. 基于 'e' 字段的事件
|
||||
switch (msg.e) {
|
||||
case 'price_size_all':
|
||||
console.log('[ClobSdk] 回调: price_size_all', { m: msg.m, t: msg.t, bids: msg.b, asks: msg.s });
|
||||
console.log('[ClobSdk] 回调: price_size_all', { i: msg.i, m: msg.m, t: msg.t, bids: msg.b, asks: msg.s });
|
||||
this.listeners.priceSizeAll.forEach(cb => cb(msg as PriceSizePolyMsg));
|
||||
break;
|
||||
case 'price_size_delta':
|
||||
console.log('[ClobSdk] 回调: price_size_delta', { m: msg.m, t: msg.t, bids: msg.b, asks: msg.s });
|
||||
console.log('[ClobSdk] 回调: price_size_delta', { i: msg.i, m: msg.m, t: msg.t, bids: msg.b, asks: msg.s });
|
||||
this.listeners.priceSizeDelta.forEach(cb => cb(msg as PriceSizePolyMsg));
|
||||
break;
|
||||
case 'trade':
|
||||
|
||||
@ -99,18 +99,35 @@ export interface OrderBookRow {
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
/** Yes token 订单簿(index 0) */
|
||||
asksYes?: OrderBookRow[]
|
||||
bidsYes?: OrderBookRow[]
|
||||
/** No token 订单簿(index 1) */
|
||||
asksNo?: OrderBookRow[]
|
||||
bidsNo?: OrderBookRow[]
|
||||
lastPriceYes?: number
|
||||
lastPriceNo?: number
|
||||
spreadYes?: number
|
||||
spreadNo?: number
|
||||
/** @deprecated 兼容旧用法,优先使用 asksYes/asksNo */
|
||||
asks?: OrderBookRow[]
|
||||
bids?: OrderBookRow[]
|
||||
lastPrice?: number
|
||||
spread?: number
|
||||
loading?: boolean
|
||||
connected?: boolean
|
||||
/** 市场 Yes 选项文案,来自 market.outcomes[0] */
|
||||
yesLabel?: string
|
||||
/** 市场 No 选项文案,来自 market.outcomes[1] */
|
||||
noLabel?: string
|
||||
}>(),
|
||||
{
|
||||
asksYes: () => [],
|
||||
bidsYes: () => [],
|
||||
asksNo: () => [],
|
||||
bidsNo: () => [],
|
||||
lastPriceYes: undefined,
|
||||
lastPriceNo: undefined,
|
||||
spreadYes: undefined,
|
||||
spreadNo: undefined,
|
||||
asks: () => [],
|
||||
bids: () => [],
|
||||
lastPrice: undefined,
|
||||
@ -122,33 +139,60 @@ const props = withDefaults(
|
||||
},
|
||||
)
|
||||
|
||||
// State
|
||||
// State:up = Yes 交易,down = No 交易
|
||||
const activeTrade = ref('up')
|
||||
|
||||
// 使用 props 或回退到 mock 数据(来自 mockData 统一封装)
|
||||
const internalAsks = ref<OrderBookRow[]>([...MOCK_ORDER_BOOK_ASKS])
|
||||
const internalBids = ref<OrderBookRow[]>([...MOCK_ORDER_BOOK_BIDS])
|
||||
const internalLastPrice = ref(MOCK_ORDER_BOOK_LAST_PRICE)
|
||||
const internalSpread = ref(MOCK_ORDER_BOOK_SPREAD)
|
||||
|
||||
// 当有外部数据时使用 props,否则在 USE_MOCK_ORDER_BOOK 时用 mock
|
||||
const asks = computed(() =>
|
||||
props.asks?.length ? props.asks : USE_MOCK_ORDER_BOOK ? internalAsks.value : [],
|
||||
)
|
||||
const bids = computed(() =>
|
||||
props.bids?.length ? props.bids : USE_MOCK_ORDER_BOOK ? internalBids.value : [],
|
||||
)
|
||||
const displayLastPrice = computed(() =>
|
||||
props.lastPrice ?? (USE_MOCK_ORDER_BOOK ? internalLastPrice.value : 0),
|
||||
)
|
||||
const displaySpread = computed(() =>
|
||||
props.spread ?? (USE_MOCK_ORDER_BOOK ? internalSpread.value : 0),
|
||||
)
|
||||
// 根据 activeTrade 选择当前 tab 对应的数据
|
||||
const asks = computed(() => {
|
||||
const isYes = activeTrade.value === 'up'
|
||||
const fromYes = isYes ? props.asksYes : props.asksNo
|
||||
const fromLegacy = props.asks
|
||||
const hasYesNo = (fromYes?.length ?? 0) > 0
|
||||
const hasLegacy = (fromLegacy?.length ?? 0) > 0
|
||||
if (hasYesNo) return fromYes ?? []
|
||||
if (hasLegacy) return fromLegacy ?? []
|
||||
return USE_MOCK_ORDER_BOOK ? internalAsks.value : []
|
||||
})
|
||||
const bids = computed(() => {
|
||||
const isYes = activeTrade.value === 'up'
|
||||
const fromYes = isYes ? props.bidsYes : props.bidsNo
|
||||
const fromLegacy = props.bids
|
||||
const hasYesNo = (fromYes?.length ?? 0) > 0
|
||||
const hasLegacy = (fromLegacy?.length ?? 0) > 0
|
||||
if (hasYesNo) return fromYes ?? []
|
||||
if (hasLegacy) return fromLegacy ?? []
|
||||
return USE_MOCK_ORDER_BOOK ? internalBids.value : []
|
||||
})
|
||||
const displayLastPrice = computed(() => {
|
||||
const isYes = activeTrade.value === 'up'
|
||||
const fromYesNo = isYes ? props.lastPriceYes : props.lastPriceNo
|
||||
if (fromYesNo != null) return fromYesNo
|
||||
if (props.lastPrice != null) return props.lastPrice
|
||||
return USE_MOCK_ORDER_BOOK ? internalLastPrice.value : 0
|
||||
})
|
||||
const displaySpread = computed(() => {
|
||||
const isYes = activeTrade.value === 'up'
|
||||
const fromYesNo = isYes ? props.spreadYes : props.spreadNo
|
||||
if (fromYesNo != null) return fromYesNo
|
||||
if (props.spread != null) return props.spread
|
||||
return USE_MOCK_ORDER_BOOK ? internalSpread.value : 0
|
||||
})
|
||||
|
||||
// 仅在没有外部数据且开启 mock 时运行 mock 更新
|
||||
let mockInterval: ReturnType<typeof setInterval> | undefined
|
||||
watch(
|
||||
() => props.connected || (props.asks?.length ?? 0) > 0,
|
||||
() =>
|
||||
props.connected ||
|
||||
(props.asksYes?.length ?? 0) > 0 ||
|
||||
(props.bidsYes?.length ?? 0) > 0 ||
|
||||
(props.asksNo?.length ?? 0) > 0 ||
|
||||
(props.bidsNo?.length ?? 0) > 0 ||
|
||||
(props.asks?.length ?? 0) > 0,
|
||||
(hasRealData) => {
|
||||
if (hasRealData && mockInterval) {
|
||||
clearInterval(mockInterval)
|
||||
|
||||
@ -47,10 +47,14 @@
|
||||
<!-- Order Book Section -->
|
||||
<v-card class="order-book-card" elevation="0" rounded="lg">
|
||||
<OrderBook
|
||||
:asks="orderBookAsks"
|
||||
:bids="orderBookBids"
|
||||
:last-price="clobLastPrice"
|
||||
:spread="clobSpread"
|
||||
:asks-yes="orderBookAsksYes"
|
||||
:bids-yes="orderBookBidsYes"
|
||||
:asks-no="orderBookAsksNo"
|
||||
:bids-no="orderBookBidsNo"
|
||||
:last-price-yes="clobLastPriceYes"
|
||||
:last-price-no="clobLastPriceNo"
|
||||
:spread-yes="clobSpreadYes"
|
||||
:spread-no="clobSpreadNo"
|
||||
:loading="clobLoading"
|
||||
:connected="clobConnected"
|
||||
:yes-label="yesLabel"
|
||||
@ -357,15 +361,30 @@ const currentMarket = computed(() => {
|
||||
})
|
||||
|
||||
// --- CLOB WebSocket 订单簿与成交 ---
|
||||
// 按 token 索引区分:0 = Yes,1 = No
|
||||
type OrderBookRows = { price: number; shares: number }[]
|
||||
const clobSdkRef = ref<ClobSdk | null>(null)
|
||||
const orderBookAsks = ref<{ price: number; shares: number }[]>([])
|
||||
const orderBookBids = ref<{ price: number; shares: number }[]>([])
|
||||
const clobLastPrice = ref<number | undefined>(undefined)
|
||||
const clobSpread = ref<number | undefined>(undefined)
|
||||
const orderBookByToken = ref<Record<number, { asks: OrderBookRows; bids: OrderBookRows }>>({
|
||||
0: { asks: [], bids: [] },
|
||||
1: { asks: [], bids: [] },
|
||||
})
|
||||
const clobLastPriceByToken = ref<Record<number, number | undefined>>({ 0: undefined, 1: undefined })
|
||||
const clobSpreadByToken = ref<Record<number, number | undefined>>({ 0: undefined, 1: undefined })
|
||||
const clobConnected = ref(false)
|
||||
const clobLoading = ref(false)
|
||||
/** 当前订阅的 tokenIds,用于根据 msg.m 匹配 Yes(0)/No(1) */
|
||||
const clobTokenIdsRef = ref<string[]>([])
|
||||
|
||||
function priceSizeToRows(record: Record<string, number> | undefined): { price: number; shares: number }[] {
|
||||
const orderBookAsksYes = computed(() => orderBookByToken.value[0]?.asks ?? [])
|
||||
const orderBookBidsYes = computed(() => orderBookByToken.value[0]?.bids ?? [])
|
||||
const orderBookAsksNo = computed(() => orderBookByToken.value[1]?.asks ?? [])
|
||||
const orderBookBidsNo = computed(() => orderBookByToken.value[1]?.bids ?? [])
|
||||
const clobLastPriceYes = computed(() => clobLastPriceByToken.value[0])
|
||||
const clobLastPriceNo = computed(() => clobLastPriceByToken.value[1])
|
||||
const clobSpreadYes = computed(() => clobSpreadByToken.value[0])
|
||||
const clobSpreadNo = computed(() => clobSpreadByToken.value[1])
|
||||
|
||||
function priceSizeToRows(record: Record<string, number> | undefined): OrderBookRows {
|
||||
if (!record) return []
|
||||
return Object.entries(record)
|
||||
.filter(([, shares]) => shares > 0)
|
||||
@ -375,15 +394,32 @@ function priceSizeToRows(record: Record<string, number> | undefined): { price: n
|
||||
}))
|
||||
}
|
||||
|
||||
function getTokenIndex(msg: PriceSizePolyMsg): number {
|
||||
const ids = clobTokenIdsRef.value
|
||||
const m = msg.m
|
||||
if (m != null && ids.length >= 2) {
|
||||
const idx = ids.findIndex((id) => String(id) === String(m))
|
||||
if (idx === 0 || idx === 1) return idx
|
||||
}
|
||||
if (typeof msg.i === 'number' && (msg.i === 0 || msg.i === 1)) return msg.i
|
||||
return 0
|
||||
}
|
||||
|
||||
function applyPriceSizeAll(msg: PriceSizePolyMsg) {
|
||||
orderBookAsks.value = priceSizeToRows(msg.s).sort((a, b) => a.price - b.price)
|
||||
orderBookBids.value = priceSizeToRows(msg.b).sort((a, b) => b.price - a.price)
|
||||
updateSpreadFromBook()
|
||||
const idx = getTokenIndex(msg)
|
||||
const asks = priceSizeToRows(msg.s).sort((a, b) => a.price - b.price)
|
||||
const bids = priceSizeToRows(msg.b).sort((a, b) => b.price - a.price)
|
||||
orderBookByToken.value = {
|
||||
...orderBookByToken.value,
|
||||
[idx]: { asks, bids },
|
||||
}
|
||||
updateSpreadForToken(idx)
|
||||
}
|
||||
|
||||
function applyPriceSizeDelta(msg: PriceSizePolyMsg) {
|
||||
const idx = getTokenIndex(msg)
|
||||
const mergeDelta = (
|
||||
current: { price: number; shares: number }[],
|
||||
current: OrderBookRows,
|
||||
delta: Record<string, number> | undefined,
|
||||
asc: boolean,
|
||||
) => {
|
||||
@ -399,16 +435,23 @@ function applyPriceSizeDelta(msg: PriceSizePolyMsg) {
|
||||
.map(([price, shares]) => ({ price, shares }))
|
||||
.sort((a, b) => (asc ? a.price - b.price : b.price - a.price))
|
||||
}
|
||||
orderBookAsks.value = mergeDelta(orderBookAsks.value, msg.s, true)
|
||||
orderBookBids.value = mergeDelta(orderBookBids.value, msg.b, false)
|
||||
updateSpreadFromBook()
|
||||
const prev = orderBookByToken.value[idx] ?? { asks: [], bids: [] }
|
||||
const asks = mergeDelta(prev.asks, msg.s, true)
|
||||
const bids = mergeDelta(prev.bids, msg.b, false)
|
||||
orderBookByToken.value = {
|
||||
...orderBookByToken.value,
|
||||
[idx]: { asks, bids },
|
||||
}
|
||||
updateSpreadForToken(idx)
|
||||
}
|
||||
|
||||
function updateSpreadFromBook() {
|
||||
const bestAsk = orderBookAsks.value[0]?.price
|
||||
const bestBid = orderBookBids.value[0]?.price
|
||||
function updateSpreadForToken(idx: number) {
|
||||
const book = orderBookByToken.value[idx]
|
||||
if (!book) return
|
||||
const bestAsk = book.asks[0]?.price
|
||||
const bestBid = book.bids[0]?.price
|
||||
if (bestAsk != null && bestBid != null) {
|
||||
clobSpread.value = bestAsk - bestBid
|
||||
clobSpreadByToken.value = { ...clobSpreadByToken.value, [idx]: bestAsk - bestBid }
|
||||
}
|
||||
}
|
||||
|
||||
@ -417,8 +460,10 @@ function connectClob(tokenIds: string[]) {
|
||||
clobSdkRef.value = null
|
||||
clobLoading.value = true
|
||||
clobConnected.value = false
|
||||
orderBookAsks.value = []
|
||||
orderBookBids.value = []
|
||||
clobTokenIdsRef.value = tokenIds
|
||||
orderBookByToken.value = { 0: { asks: [], bids: [] }, 1: { asks: [], bids: [] } }
|
||||
clobLastPriceByToken.value = { 0: undefined, 1: undefined }
|
||||
clobSpreadByToken.value = { 0: undefined, 1: undefined }
|
||||
|
||||
const options = {
|
||||
url: getClobWsUrl(),
|
||||
@ -442,7 +487,10 @@ function connectClob(tokenIds: string[]) {
|
||||
sdk.onTrade((msg: TradePolyMsg) => {
|
||||
const priceNum = parseFloat(msg.p)
|
||||
if (Number.isFinite(priceNum)) {
|
||||
clobLastPrice.value = Math.round(priceNum * 100)
|
||||
const priceCents = Math.round(priceNum * 100)
|
||||
const side = msg.side?.toLowerCase()
|
||||
const tokenIdx = side === 'buy' ? 0 : 1
|
||||
clobLastPriceByToken.value = { ...clobLastPriceByToken.value, [tokenIdx]: priceCents }
|
||||
}
|
||||
// 追加到 Activity 列表
|
||||
const side = msg.side?.toLowerCase() === 'buy' ? 'Yes' : 'No'
|
||||
@ -919,10 +967,12 @@ watch(
|
||||
clobTokenIds,
|
||||
(tokenIds) => {
|
||||
if (tokenIds.length > 0) {
|
||||
// 用接口返回的 Yes 价格作为初始 lastPrice
|
||||
const payload = tradeMarketPayload.value
|
||||
if (payload?.yesPrice != null) {
|
||||
clobLastPrice.value = Math.round(payload.yesPrice * 100)
|
||||
clobLastPriceByToken.value = {
|
||||
...clobLastPriceByToken.value,
|
||||
0: Math.round(payload.yesPrice * 100),
|
||||
}
|
||||
}
|
||||
connectClob(tokenIds)
|
||||
} else {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user