Align strategy UI prices with exchange tick sizes.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
/** 价格按交易所常见精度:永续/指数 0.01;期权权利金 0.1 */
|
||||
|
||||
export type PxKind = "perp" | "option" | "index";
|
||||
|
||||
export function fmtExPx(
|
||||
kind: PxKind,
|
||||
px: number | null | undefined,
|
||||
): string {
|
||||
if (px == null || Number.isNaN(Number(px))) return "—";
|
||||
const n = Number(px);
|
||||
if (kind === "option") {
|
||||
return (Math.round(n * 10) / 10).toFixed(1);
|
||||
}
|
||||
return (Math.round(n * 100) / 100).toFixed(2);
|
||||
}
|
||||
|
||||
export function fmtTopEx(
|
||||
kind: PxKind,
|
||||
px: number | null | undefined,
|
||||
sz: number | null | undefined,
|
||||
): string {
|
||||
if (px == null || Number.isNaN(Number(px))) return "—";
|
||||
const p = fmtExPx(kind, px);
|
||||
if (sz == null || Number.isNaN(Number(sz))) return p;
|
||||
const n = Number(sz);
|
||||
const s =
|
||||
n >= 100 ? n.toFixed(0) : n >= 10 ? n.toFixed(1) : n.toFixed(2);
|
||||
return `${p}(${s})`;
|
||||
}
|
||||
|
||||
export function fmtBidLiqEx(
|
||||
kind: PxKind,
|
||||
px: number | null | undefined,
|
||||
sz: number | null | undefined,
|
||||
): string {
|
||||
if (px == null || Number.isNaN(Number(px))) return "—";
|
||||
const p = fmtExPx(kind, px);
|
||||
if (sz == null || Number.isNaN(Number(sz))) return p;
|
||||
const n = Number(sz);
|
||||
const s =
|
||||
n >= 100 ? n.toFixed(0) : n >= 10 ? n.toFixed(1) : n.toFixed(2);
|
||||
return `${p}/${s}`;
|
||||
}
|
||||
+35
-28
@@ -1,27 +1,12 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { apiFetch, MarketSnapshot, PlanState } from "../api/client";
|
||||
import { fmtBidLiqEx, fmtExPx, fmtTopEx } from "../format";
|
||||
|
||||
function fmt(n: number | null | undefined, d = 2) {
|
||||
if (n == null || Number.isNaN(n)) return "—";
|
||||
return n.toFixed(d);
|
||||
}
|
||||
|
||||
/** 卖一/买一 + 对应档量:8.80(12.5) / 7.20(8.0) */
|
||||
function fmtTop(px: number | null | undefined, sz: number | null | undefined) {
|
||||
if (px == null || Number.isNaN(px)) return "—";
|
||||
if (sz == null || Number.isNaN(sz)) return fmt(px);
|
||||
const s = sz >= 100 ? sz.toFixed(0) : sz >= 10 ? sz.toFixed(1) : sz.toFixed(2);
|
||||
return `${fmt(px)}(${s})`;
|
||||
}
|
||||
|
||||
/** 持仓买一:价格/流动性张数,如 17.00/4000 */
|
||||
function fmtBidLiq(px: number | null | undefined, sz: number | null | undefined) {
|
||||
if (px == null || Number.isNaN(px)) return "—";
|
||||
if (sz == null || Number.isNaN(sz)) return fmt(px);
|
||||
const s = sz >= 100 ? sz.toFixed(0) : sz >= 10 ? sz.toFixed(1) : sz.toFixed(2);
|
||||
return `${fmt(px)}/${s}`;
|
||||
}
|
||||
|
||||
/** 期权杠杆 = 标的 ÷ 权利金 */
|
||||
function optionLevLabel(
|
||||
underlying: number | null | undefined,
|
||||
@@ -607,11 +592,15 @@ export default function PlanPage() {
|
||||
<div className="pos-grid">
|
||||
<div className="pos-cell">
|
||||
<span className="pos-label">开仓价</span>
|
||||
<span className="pos-value mono">{fmt(pos?.perp_entry_px)}</span>
|
||||
<span className="pos-value mono">
|
||||
{fmtExPx("perp", pos?.perp_entry_px)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="pos-cell">
|
||||
<span className="pos-label">可平价</span>
|
||||
<span className="pos-value mono">{fmt(pos?.perp_mark_px)}</span>
|
||||
<span className="pos-value mono">
|
||||
{fmtExPx("perp", pos?.perp_mark_px)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="pos-cell">
|
||||
<span className="pos-label">浮盈亏</span>
|
||||
@@ -666,7 +655,12 @@ export default function PlanPage() {
|
||||
<div className="pos-meta">
|
||||
<span className="pos-meta-item">期权持仓</span>
|
||||
<span className="pos-meta-item">到期 {pos?.expiry_ymd || "—"}</span>
|
||||
<span className="pos-meta-item">行权 {fmt(pos?.strike, 0)}</span>
|
||||
<span className="pos-meta-item">
|
||||
行权{" "}
|
||||
{pos?.strike != null
|
||||
? Math.round(Number(pos.strike))
|
||||
: "—"}
|
||||
</span>
|
||||
<span className="pos-meta-item mono">
|
||||
{fmt(pos?.option_qty_eth, 2)} ETH · {fmt(pos?.option_qty_contracts, 0)} 张
|
||||
</span>
|
||||
@@ -677,12 +671,18 @@ export default function PlanPage() {
|
||||
<div className="pos-grid">
|
||||
<div className="pos-cell">
|
||||
<span className="pos-label">开仓均价</span>
|
||||
<span className="pos-value mono">{fmt(pos?.option_entry_px)}</span>
|
||||
<span className="pos-value mono">
|
||||
{fmtExPx("option", pos?.option_entry_px)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="pos-cell">
|
||||
<span className="pos-label">买一</span>
|
||||
<span className="pos-value mono">
|
||||
{fmtBidLiq(pos?.option_mark_px, pos?.option_bid_sz)}
|
||||
{fmtBidLiqEx(
|
||||
"option",
|
||||
pos?.option_mark_px,
|
||||
pos?.option_bid_sz,
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="pos-cell">
|
||||
@@ -722,16 +722,17 @@ export default function PlanPage() {
|
||||
<h3 className="plan-panel-title">永续行情</h3>
|
||||
<div className="kv">
|
||||
<span>买一</span>
|
||||
<span className="mono">{fmt(snap?.perp?.bid)}</span>
|
||||
<span className="mono">{fmtExPx("perp", snap?.perp?.bid)}</span>
|
||||
</div>
|
||||
<div className="kv">
|
||||
<span>卖一</span>
|
||||
<span className="mono">{fmt(snap?.perp?.ask)}</span>
|
||||
<span className="mono">{fmtExPx("perp", snap?.perp?.ask)}</span>
|
||||
</div>
|
||||
<div className="kv">
|
||||
<span>市价</span>
|
||||
<span className="mono">
|
||||
{fmt(
|
||||
{fmtExPx(
|
||||
"perp",
|
||||
snap?.perp?.mark_px ??
|
||||
(snap?.perp?.bid != null && snap?.perp?.ask != null
|
||||
? (snap.perp.bid + snap.perp.ask) / 2
|
||||
@@ -741,13 +742,19 @@ export default function PlanPage() {
|
||||
</div>
|
||||
<div className="kv">
|
||||
<span>指数</span>
|
||||
<span className="mono">{fmt(snap?.index_px)}</span>
|
||||
<span className="mono">{fmtExPx("index", snap?.index_px)}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="card">
|
||||
<h3 className="plan-panel-title">
|
||||
{open ? "持仓期权" : "期权 ATM"}
|
||||
{snap?.pair ? ` @ ${snap.pair.strike}` : ""}
|
||||
{snap?.pair
|
||||
? ` @ ${
|
||||
snap.pair.strike != null
|
||||
? Math.round(Number(snap.pair.strike))
|
||||
: "—"
|
||||
}`
|
||||
: ""}
|
||||
</h3>
|
||||
{(() => {
|
||||
const under =
|
||||
@@ -776,14 +783,14 @@ export default function PlanPage() {
|
||||
<div className="kv">
|
||||
<span>Call {sideLabel}</span>
|
||||
<span className="mono">
|
||||
{fmtTop(callPx, callSz)}
|
||||
{fmtTopEx("option", callPx, callSz)}
|
||||
{!open ? ` · 杠杆 ${callLev}` : ""}
|
||||
</span>
|
||||
</div>
|
||||
<div className="kv">
|
||||
<span>Put {sideLabel}</span>
|
||||
<span className="mono">
|
||||
{fmtTop(putPx, putSz)}
|
||||
{fmtTopEx("option", putPx, putSz)}
|
||||
{!open ? ` · 杠杆 ${putLev}` : ""}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useEffect, useId, useMemo, useState } from "react";
|
||||
import { apiFetch } from "../api/client";
|
||||
import { fmtExPx } from "../format";
|
||||
import {
|
||||
closeReasonZh,
|
||||
fillDescZh,
|
||||
@@ -495,7 +496,7 @@ export default function TradesPage() {
|
||||
{fmtMovePoints(selectedGroup.move_points)}
|
||||
{selectedGroup.entry_index_px != null &&
|
||||
selectedGroup.close_index_px != null
|
||||
? `(${fmt(selectedGroup.entry_index_px, 1)}→${fmt(selectedGroup.close_index_px, 1)})`
|
||||
? `(${fmtExPx("index", selectedGroup.entry_index_px)}→${fmtExPx("index", selectedGroup.close_index_px)})`
|
||||
: ""}
|
||||
</span>
|
||||
</div>
|
||||
@@ -512,7 +513,10 @@ export default function TradesPage() {
|
||||
<div className="kv">
|
||||
<span>结算指数</span>
|
||||
<span className="mono">
|
||||
{fmt(selectedGroup.expiry_settle.settle_index_px)}
|
||||
{fmtExPx(
|
||||
"index",
|
||||
selectedGroup.expiry_settle.settle_index_px,
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="kv">
|
||||
@@ -554,7 +558,9 @@ export default function TradesPage() {
|
||||
) : null}
|
||||
</span>
|
||||
<span className="mono">
|
||||
{pxLabel} {f.fill_px.toFixed(4)} · 数量 {f.qty_eth}
|
||||
{pxLabel}{" "}
|
||||
{fmtExPx(isOpt ? "option" : "perp", f.fill_px)} · 数量{" "}
|
||||
{f.qty_eth}
|
||||
{notional != null && Number.isFinite(notional)
|
||||
? ` · 合计 ${notional.toFixed(2)}`
|
||||
: ""}{" "}
|
||||
|
||||
Reference in New Issue
Block a user