diff --git a/index.html b/index.html
index 9e5fc8f..df3f429 100644
--- a/index.html
+++ b/index.html
@@ -4,7 +4,7 @@
-
Vite App
+ TestMarket
diff --git a/src/components/OrderBook.vue b/src/components/OrderBook.vue
index 7bad62e..240a528 100644
--- a/src/components/OrderBook.vue
+++ b/src/components/OrderBook.vue
@@ -43,9 +43,9 @@
:trackStyle="{ backgroundColor: 'transparent' }"
/>
- {{ ask.price }}¢
- {{ ask.shares.toFixed(2) }}
- {{ ask.cumulativeTotal.toFixed(2) }}
+ {{ formatOrderBookPrice(ask) }}¢
+ {{ trimTrailingZeros(ask.shares.toFixed(2)) }}
+ {{ trimTrailingZeros(ask.cumulativeTotal.toFixed(4)) }}
{{ t('trade.orderBookAsks') }}
@@ -63,9 +63,9 @@
:trackStyle="{ backgroundColor: 'transparent' }"
/>
- {{ bid.price }}¢
- {{ bid.shares.toFixed(2) }}
- {{ bid.cumulativeTotal.toFixed(2) }}
+ {{ formatOrderBookPrice(bid) }}¢
+ {{ trimTrailingZeros(bid.shares.toFixed(2)) }}
+ {{ trimTrailingZeros(bid.cumulativeTotal.toFixed(4)) }}
@@ -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,
diff --git a/src/components/TradeComponent.vue b/src/components/TradeComponent.vue
index 21dadc3..736b465 100644
--- a/src/components/TradeComponent.vue
+++ b/src/components/TradeComponent.vue
@@ -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(() =>
diff --git a/src/views/TradeDetail.vue b/src/views/TradeDetail.vue
index 4bb36be..c53d31e 100644
--- a/src/views/TradeDetail.vue
+++ b/src/views/TradeDetail.vue
@@ -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(null)
const orderBookByToken = ref>({
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 | 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 | undefined,
asc: boolean,
) => {
- const map = new Map(current.map((r) => [r.price, r.shares]))
+ const map = new Map()
+ 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: [] }