优化:订单簿显示优化
This commit is contained in:
parent
0069ae8357
commit
ff7bc3b685
@ -4,7 +4,7 @@
|
||||
<meta charset="UTF-8">
|
||||
<link rel="icon" href="/favicon.ico">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Vite App</title>
|
||||
<title>TestMarket</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
||||
@ -43,9 +43,9 @@
|
||||
:trackStyle="{ backgroundColor: 'transparent' }"
|
||||
/>
|
||||
</div>
|
||||
<div class="order-price asks-price">{{ ask.price }}¢</div>
|
||||
<div class="order-shares">{{ ask.shares.toFixed(2) }}</div>
|
||||
<div class="order-total">{{ ask.cumulativeTotal.toFixed(2) }}</div>
|
||||
<div class="order-price asks-price">{{ formatOrderBookPrice(ask) }}¢</div>
|
||||
<div class="order-shares">{{ trimTrailingZeros(ask.shares.toFixed(2)) }}</div>
|
||||
<div class="order-total">{{ trimTrailingZeros(ask.cumulativeTotal.toFixed(4)) }}</div>
|
||||
</div>
|
||||
<!-- Bids Orders -->
|
||||
<div class="asks-label">{{ t('trade.orderBookAsks') }}</div>
|
||||
@ -63,9 +63,9 @@
|
||||
:trackStyle="{ backgroundColor: 'transparent' }"
|
||||
/>
|
||||
</div>
|
||||
<div class="order-price bids-price">{{ bid.price }}¢</div>
|
||||
<div class="order-shares">{{ bid.shares.toFixed(2) }}</div>
|
||||
<div class="order-total">{{ bid.cumulativeTotal.toFixed(2) }}</div>
|
||||
<div class="order-price bids-price">{{ formatOrderBookPrice(bid) }}¢</div>
|
||||
<div class="order-shares">{{ trimTrailingZeros(bid.shares.toFixed(2)) }}</div>
|
||||
<div class="order-total">{{ trimTrailingZeros(bid.cumulativeTotal.toFixed(4)) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -93,8 +93,12 @@ import { USE_MOCK_ORDER_BOOK } from '../config/mock'
|
||||
const { t } = useI18n()
|
||||
|
||||
export interface OrderBookRow {
|
||||
/** 价格(美分),用于展示 */
|
||||
price: number
|
||||
/** 份额数量 */
|
||||
shares: number
|
||||
/** 原始精度单价(美元/份),用于计算总价,避免舍入误差 */
|
||||
priceRaw?: number
|
||||
}
|
||||
|
||||
const props = withDefaults(
|
||||
@ -223,6 +227,21 @@ watch(
|
||||
{ immediate: true },
|
||||
)
|
||||
|
||||
/** 去掉小数点后多余的 0(如 1.00 → 1,0.10 → 0.1) */
|
||||
function trimTrailingZeros(s: string): string {
|
||||
return s.replace(/\.?0+$/, '')
|
||||
}
|
||||
|
||||
/** 订单簿价格展示:小于 1 美分时最多显示两位小数,使用原始精度 priceRaw;小数点后全为 0 则不显示 */
|
||||
function formatOrderBookPrice(row: OrderBookRow): string {
|
||||
const cents =
|
||||
row.priceRaw != null && Number.isFinite(row.priceRaw) ? row.priceRaw * 100 : row.price
|
||||
if (!Number.isFinite(cents)) return '0'
|
||||
if (cents < 1) return trimTrailingZeros(cents.toFixed(2))
|
||||
if (cents < 100) return trimTrailingZeros(cents.toFixed(1))
|
||||
return String(Math.round(cents))
|
||||
}
|
||||
|
||||
// Calculate cumulative total for asks
|
||||
const asksWithCumulativeTotal = computed(() => {
|
||||
let cumulativeTotal = 0
|
||||
@ -231,8 +250,10 @@ const asksWithCumulativeTotal = computed(() => {
|
||||
|
||||
return sortedAsks
|
||||
.map((ask) => {
|
||||
// Calculate current ask's value
|
||||
const askValue = (ask.price * ask.shares) / 100 // Convert cents to dollars
|
||||
// 总价用原始精度单价(美元)计算,无则回退为 price/100
|
||||
const priceDollars =
|
||||
ask.priceRaw != null && Number.isFinite(ask.priceRaw) ? ask.priceRaw : ask.price / 100
|
||||
const askValue = priceDollars * ask.shares
|
||||
cumulativeTotal += askValue
|
||||
return {
|
||||
...ask,
|
||||
@ -249,9 +270,10 @@ const bidsWithCumulativeTotal = computed(() => {
|
||||
const sortedBids = [...bids.value].sort((a, b) => b.price - a.price)
|
||||
|
||||
return sortedBids.map((bid) => {
|
||||
// Calculate current bid's value
|
||||
console.log('bid.price', bid.price)
|
||||
const bidValue = (bid.price * bid.shares) / 100.0 // Convert cents to dollars
|
||||
// 总价用原始精度单价(美元)计算,无则回退为 price/100
|
||||
const priceDollars =
|
||||
bid.priceRaw != null && Number.isFinite(bid.priceRaw) ? bid.priceRaw : bid.price / 100
|
||||
const bidValue = priceDollars * bid.shares
|
||||
cumulativeTotal += bidValue
|
||||
return {
|
||||
...bid,
|
||||
|
||||
@ -1680,27 +1680,35 @@ const emit = defineEmits<{
|
||||
|
||||
// Computed properties
|
||||
/** Limit Price 显示值(美分),与 Yes/No 按钮单位一致 */
|
||||
/** 去掉小数点后多余的 0(如 1.00 → 1,0.10 → 0.1) */
|
||||
function trimTrailingZeros(s: string): string {
|
||||
return s.replace(/\.?0+$/, '')
|
||||
}
|
||||
|
||||
const limitPriceCentsDisplay = computed(() => Math.round(limitPrice.value * 10000) / 100)
|
||||
const currentPrice = computed(() => {
|
||||
return `${(limitPrice.value * 100).toFixed(0)}¢`
|
||||
return `${trimTrailingZeros((limitPrice.value * 100).toFixed(0))}¢`
|
||||
})
|
||||
|
||||
const totalPrice = computed(() => {
|
||||
let raw: number
|
||||
if (activeTab.value === 'sell' && limitType.value === 'Market') {
|
||||
const bestBidCents =
|
||||
selectedOption.value === 'yes'
|
||||
? (props.market?.bestBidYesCents ?? 0)
|
||||
: (props.market?.bestBidNoCents ?? 0)
|
||||
const price = bestBidCents / 100
|
||||
return (price * shares.value).toFixed(2)
|
||||
raw = (bestBidCents / 100) * shares.value
|
||||
} else {
|
||||
raw = limitPrice.value * shares.value
|
||||
}
|
||||
return (limitPrice.value * shares.value).toFixed(2)
|
||||
if (!Number.isFinite(raw)) return '0'
|
||||
return trimTrailingZeros(raw.toFixed(4))
|
||||
})
|
||||
|
||||
/** Sell 模式下的平均单价(¢) */
|
||||
const avgPriceCents = computed(() => {
|
||||
const p = limitPrice.value
|
||||
return (p * 100).toFixed(1)
|
||||
return trimTrailingZeros((p * 100).toFixed(1))
|
||||
})
|
||||
|
||||
/** To win = 份数 × 1U = shares × 1 USDC */
|
||||
@ -1711,9 +1719,9 @@ const toWinValue = computed(() => {
|
||||
? (props.market?.yesPrice ?? 0.5)
|
||||
: (props.market?.noPrice ?? 0.5)
|
||||
const sharesFromAmount = price > 0 ? amount.value / price : 0
|
||||
return sharesFromAmount.toFixed(2)
|
||||
return trimTrailingZeros(sharesFromAmount.toFixed(2))
|
||||
}
|
||||
return (shares.value * 1).toFixed(2)
|
||||
return trimTrailingZeros((shares.value * 1).toFixed(2))
|
||||
})
|
||||
|
||||
const limitTypeDisplay = computed(() =>
|
||||
|
||||
@ -510,7 +510,7 @@ const currentMarket = computed(() => {
|
||||
|
||||
// --- CLOB WebSocket 订单簿与成交 ---
|
||||
// 按 token 索引区分:0 = Yes,1 = No
|
||||
type OrderBookRows = { price: number; shares: number }[]
|
||||
type OrderBookRows = { price: number; shares: number; priceRaw?: number }[]
|
||||
const clobSdkRef = ref<ClobSdk | null>(null)
|
||||
const orderBookByToken = ref<Record<number, { asks: OrderBookRows; bids: OrderBookRows }>>({
|
||||
0: { asks: [], bids: [] },
|
||||
@ -560,14 +560,21 @@ const clobSpreadNo = computed(() => clobSpreadByToken.value[1])
|
||||
/** 订单簿份额接口按 6 位小数传(1_000_000 = 1 share),需除以该系数转为展示值 */
|
||||
const ORDER_BOOK_SIZE_SCALE = 1_000_000
|
||||
|
||||
/** 接口 key p 为价格(与 price 美分同源,除以 100 为美分);priceRaw 为美元单价保留原始精度供订单簿算总价 */
|
||||
function priceSizeToRows(record: Record<string, number> | undefined): OrderBookRows {
|
||||
if (!record) return []
|
||||
return Object.entries(record)
|
||||
.filter(([, rawShares]) => rawShares > 0)
|
||||
.map(([p, rawShares]) => ({
|
||||
price: Math.round(parseFloat(p) / 100),
|
||||
shares: rawShares / ORDER_BOOK_SIZE_SCALE,
|
||||
}))
|
||||
.map(([p, rawShares]) => {
|
||||
const numP = parseFloat(p)
|
||||
const priceCents = Math.round(numP / 100)
|
||||
const priceRawDollars = Number.isFinite(numP) ? numP / 10000 : priceCents / 100
|
||||
return {
|
||||
price: priceCents,
|
||||
shares: rawShares / ORDER_BOOK_SIZE_SCALE,
|
||||
priceRaw: priceRawDollars,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function getTokenIndex(msg: PriceSizePolyMsg): number {
|
||||
@ -599,17 +606,24 @@ function applyPriceSizeDelta(msg: PriceSizePolyMsg) {
|
||||
delta: Record<string, number> | undefined,
|
||||
asc: boolean,
|
||||
) => {
|
||||
const map = new Map(current.map((r) => [r.price, r.shares]))
|
||||
const map = new Map<string, { price: number; shares: number; priceRaw: number }>()
|
||||
const keyOf = (price: number, priceRaw: number) => priceRaw.toFixed(8)
|
||||
current.forEach((r) => {
|
||||
const pr = r.priceRaw ?? r.price / 100
|
||||
map.set(keyOf(r.price, pr), { price: r.price, shares: r.shares, priceRaw: pr })
|
||||
})
|
||||
if (delta) {
|
||||
Object.entries(delta).forEach(([p, rawShares]) => {
|
||||
const price = Math.round(parseFloat(p) / 100)
|
||||
const numP = parseFloat(p)
|
||||
const price = Math.round(numP / 100)
|
||||
const priceRaw = Number.isFinite(numP) ? numP / 10000 : price / 100
|
||||
const shares = rawShares / ORDER_BOOK_SIZE_SCALE
|
||||
if (shares <= 0) map.delete(price)
|
||||
else map.set(price, shares)
|
||||
const key = keyOf(price, priceRaw)
|
||||
if (shares <= 0) map.delete(key)
|
||||
else map.set(key, { price, shares, priceRaw })
|
||||
})
|
||||
}
|
||||
return Array.from(map.entries())
|
||||
.map(([price, shares]) => ({ price, shares }))
|
||||
return Array.from(map.values())
|
||||
.sort((a, b) => (asc ? a.price - b.price : b.price - a.price))
|
||||
}
|
||||
const prev = orderBookByToken.value[idx] ?? { asks: [], bids: [] }
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user