xtraderClient/src/api/order.ts
2026-02-27 22:25:35 +08:00

231 lines
6.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { get, post } from './request'
/** 分页结果 */
export interface PageResult<T> {
list: T[]
page: number
pageSize: number
total: number
}
/**
* 订单项(与 doc.json definitions["model.ClobOrder"] 对齐)
* GET /clob/order/getOrderList 列表项
*/
export interface ClobOrderItem {
ID: number
assetID?: string
createdAt?: string
updatedAt?: string
expiration?: number
feeRateBps?: number
market?: string
orderType?: number
originalSize?: number
outcome?: string
price?: number
side?: number
sizeMatched?: number
status?: number
userID?: number
[key: string]: unknown
}
/** 订单列表响应 */
export interface OrderListResponse {
code: number
data: PageResult<ClobOrderItem>
msg: string
}
/** 订单状态1=未成交Live2=已成交0=已取消等 */
export const OrderStatus = {
Live: 1,
Matched: 2,
Cancelled: 0,
} as const
/**
* GET /clob/order/getOrderList 请求参数
*/
export interface GetOrderListParams {
page?: number
pageSize?: number
/** 订单状态筛选1=未成交 */
status?: number
startCreatedAt?: string
endCreatedAt?: string
marketID?: string
tokenID?: string
userID?: number
}
/**
* 分页获取订单列表
* GET /clob/order/getOrderList
* 需鉴权x-token、x-user-id
*/
export async function getOrderList(
params: GetOrderListParams = {},
config?: { headers?: Record<string, string> },
): Promise<OrderListResponse> {
const { page = 1, pageSize = 10, status, startCreatedAt, endCreatedAt, marketID, tokenID, userID } =
params
const query: Record<string, string | number | undefined> = { page, pageSize }
if (status != null && Number.isFinite(status)) query.status = status
if (startCreatedAt != null && startCreatedAt !== '') query.startCreatedAt = startCreatedAt
if (endCreatedAt != null && endCreatedAt !== '') query.endCreatedAt = endCreatedAt
if (marketID != null && marketID !== '') query.marketID = marketID
if (tokenID != null && tokenID !== '') query.tokenID = tokenID
if (userID != null && Number.isFinite(userID)) query.userID = userID
return get<OrderListResponse>('/clob/order/getOrderList', query, config)
}
/** 取消订单请求体request.CancelOrderReq */
export interface CancelOrderReq {
orderID: number
tokenID: string
userID: number
}
/** 通用 API 响应 */
export interface ApiResponse {
code: number
data?: unknown
msg: string
}
/**
* POST /clob/order/cancelOrder
* 撤销订单(撮合引擎),需鉴权 x-token
*/
export async function cancelOrder(
data: CancelOrderReq,
config?: { headers?: Record<string, string> },
): Promise<ApiResponse> {
return post<ApiResponse>('/clob/order/cancelOrder', data, config)
}
/** 钱包 History 展示项(与 Wallet.vue HistoryItem 一致) */
export interface HistoryDisplayItem {
id: string
market: string
side: 'Yes' | 'No'
activity: string
value: string
activityDetail?: string
profitLoss?: string
profitLossNegative?: boolean
timeAgo?: string
avgPrice?: string
shares?: string
iconChar?: string
iconClass?: string
}
/** Side: Buy=1, Sell=2 */
const Side = { Buy: 1, Sell: 2 } as const
function formatTimeAgo(createdAt: string | undefined): string {
if (!createdAt) return ''
const d = new Date(createdAt)
const now = Date.now()
const diff = now - d.getTime()
if (diff < 60000) return 'Just now'
if (diff < 3600000) return `${Math.floor(diff / 60000)} minutes ago`
if (diff < 86400000) return `${Math.floor(diff / 3600000)} hours ago`
if (diff < 604800000) return `${Math.floor(diff / 86400000)} days ago`
return d.toLocaleDateString()
}
/**
* 将 ClobOrderItem 映射为钱包 History 展示项
* price 为整数(已乘 10000sizeMatched 为已成交份额
*/
export function mapOrderToHistoryItem(order: ClobOrderItem): HistoryDisplayItem {
const id = String(order.ID ?? '')
const market = order.market ?? ''
const outcome = order.outcome ?? 'Yes'
const sideNum = order.side ?? Side.Buy
const sideLabel = sideNum === Side.Sell ? 'Sell' : 'Buy'
const activity = `${sideLabel} ${outcome}`
const priceBps = order.price ?? 0
const priceCents = Math.round(priceBps / 100)
const size = order.sizeMatched ?? order.originalSize ?? 0
const valueUsd = (priceBps / 10000) * size
const value = `$${valueUsd.toFixed(2)}`
const verb = sideNum === Side.Sell ? 'Sold' : 'Bought'
const activityDetail = `${verb} ${size} ${outcome} at ${priceCents}¢`
const avgPrice = `${priceCents}¢`
const timeAgo = formatTimeAgo(order.createdAt)
return {
id,
market,
side: outcome === 'No' ? 'No' : 'Yes',
activity,
value,
activityDetail,
profitLoss: value,
profitLossNegative: false,
timeAgo,
avgPrice,
shares: String(size),
}
}
/** 钱包 Open Orders 展示项(与 Wallet.vue OpenOrder 一致) */
export interface OpenOrderDisplayItem {
id: string
market: string
side: 'Yes' | 'No'
outcome: string
price: string
filled: string
total: string
expiration: string
actionLabel?: string
filledDisplay?: string
orderID?: number
tokenID?: string
}
/** OrderType GTC=0 表示 Until Cancelled */
const OrderType = { GTC: 0, GTD: 1 } as const
/**
* 将 ClobOrderItem 映射为钱包 Open Orders 展示项(未成交订单)
* price 为整数(已乘 10000
*/
export function mapOrderToOpenOrderItem(order: ClobOrderItem): OpenOrderDisplayItem {
const id = String(order.ID ?? '')
const market = order.market ?? ''
const sideNum = order.side ?? Side.Buy
const side = sideNum === Side.Sell ? 'No' : 'Yes'
const outcome = order.outcome || (side === 'Yes' ? 'Yes' : 'No')
const priceBps = order.price ?? 0
const priceCents = Math.round(priceBps / 100)
const price = `${priceCents}¢`
const originalSize = order.originalSize ?? 0
const sizeMatched = order.sizeMatched ?? 0
const filled = `${sizeMatched}/${originalSize}`
const totalUsd = (priceBps / 10000) * originalSize
const total = `$${totalUsd.toFixed(2)}`
const expiration =
order.orderType === OrderType.GTC ? 'Until Cancelled' : order.expiration?.toString() ?? ''
const actionLabel = sideNum === Side.Buy ? `Buy ${outcome}` : `Sell ${outcome}`
return {
id,
market,
side,
outcome,
price,
filled,
total,
expiration,
actionLabel,
filledDisplay: filled,
orderID: order.ID,
tokenID: order.assetID,
}
}