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 { useEffect, useState } from "react";
|
||||||
import { apiFetch, MarketSnapshot, PlanState } from "../api/client";
|
import { apiFetch, MarketSnapshot, PlanState } from "../api/client";
|
||||||
|
import { fmtBidLiqEx, fmtExPx, fmtTopEx } from "../format";
|
||||||
|
|
||||||
function fmt(n: number | null | undefined, d = 2) {
|
function fmt(n: number | null | undefined, d = 2) {
|
||||||
if (n == null || Number.isNaN(n)) return "—";
|
if (n == null || Number.isNaN(n)) return "—";
|
||||||
return n.toFixed(d);
|
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(
|
function optionLevLabel(
|
||||||
underlying: number | null | undefined,
|
underlying: number | null | undefined,
|
||||||
@@ -607,11 +592,15 @@ export default function PlanPage() {
|
|||||||
<div className="pos-grid">
|
<div className="pos-grid">
|
||||||
<div className="pos-cell">
|
<div className="pos-cell">
|
||||||
<span className="pos-label">开仓价</span>
|
<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>
|
||||||
<div className="pos-cell">
|
<div className="pos-cell">
|
||||||
<span className="pos-label">可平价</span>
|
<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>
|
||||||
<div className="pos-cell">
|
<div className="pos-cell">
|
||||||
<span className="pos-label">浮盈亏</span>
|
<span className="pos-label">浮盈亏</span>
|
||||||
@@ -666,7 +655,12 @@ export default function PlanPage() {
|
|||||||
<div className="pos-meta">
|
<div className="pos-meta">
|
||||||
<span className="pos-meta-item">期权持仓</span>
|
<span className="pos-meta-item">期权持仓</span>
|
||||||
<span className="pos-meta-item">到期 {pos?.expiry_ymd || "—"}</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">
|
<span className="pos-meta-item mono">
|
||||||
{fmt(pos?.option_qty_eth, 2)} ETH · {fmt(pos?.option_qty_contracts, 0)} 张
|
{fmt(pos?.option_qty_eth, 2)} ETH · {fmt(pos?.option_qty_contracts, 0)} 张
|
||||||
</span>
|
</span>
|
||||||
@@ -677,12 +671,18 @@ export default function PlanPage() {
|
|||||||
<div className="pos-grid">
|
<div className="pos-grid">
|
||||||
<div className="pos-cell">
|
<div className="pos-cell">
|
||||||
<span className="pos-label">开仓均价</span>
|
<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>
|
||||||
<div className="pos-cell">
|
<div className="pos-cell">
|
||||||
<span className="pos-label">买一</span>
|
<span className="pos-label">买一</span>
|
||||||
<span className="pos-value mono">
|
<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>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="pos-cell">
|
<div className="pos-cell">
|
||||||
@@ -722,16 +722,17 @@ export default function PlanPage() {
|
|||||||
<h3 className="plan-panel-title">永续行情</h3>
|
<h3 className="plan-panel-title">永续行情</h3>
|
||||||
<div className="kv">
|
<div className="kv">
|
||||||
<span>买一</span>
|
<span>买一</span>
|
||||||
<span className="mono">{fmt(snap?.perp?.bid)}</span>
|
<span className="mono">{fmtExPx("perp", snap?.perp?.bid)}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="kv">
|
<div className="kv">
|
||||||
<span>卖一</span>
|
<span>卖一</span>
|
||||||
<span className="mono">{fmt(snap?.perp?.ask)}</span>
|
<span className="mono">{fmtExPx("perp", snap?.perp?.ask)}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="kv">
|
<div className="kv">
|
||||||
<span>市价</span>
|
<span>市价</span>
|
||||||
<span className="mono">
|
<span className="mono">
|
||||||
{fmt(
|
{fmtExPx(
|
||||||
|
"perp",
|
||||||
snap?.perp?.mark_px ??
|
snap?.perp?.mark_px ??
|
||||||
(snap?.perp?.bid != null && snap?.perp?.ask != null
|
(snap?.perp?.bid != null && snap?.perp?.ask != null
|
||||||
? (snap.perp.bid + snap.perp.ask) / 2
|
? (snap.perp.bid + snap.perp.ask) / 2
|
||||||
@@ -741,13 +742,19 @@ export default function PlanPage() {
|
|||||||
</div>
|
</div>
|
||||||
<div className="kv">
|
<div className="kv">
|
||||||
<span>指数</span>
|
<span>指数</span>
|
||||||
<span className="mono">{fmt(snap?.index_px)}</span>
|
<span className="mono">{fmtExPx("index", snap?.index_px)}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="card">
|
<div className="card">
|
||||||
<h3 className="plan-panel-title">
|
<h3 className="plan-panel-title">
|
||||||
{open ? "持仓期权" : "期权 ATM"}
|
{open ? "持仓期权" : "期权 ATM"}
|
||||||
{snap?.pair ? ` @ ${snap.pair.strike}` : ""}
|
{snap?.pair
|
||||||
|
? ` @ ${
|
||||||
|
snap.pair.strike != null
|
||||||
|
? Math.round(Number(snap.pair.strike))
|
||||||
|
: "—"
|
||||||
|
}`
|
||||||
|
: ""}
|
||||||
</h3>
|
</h3>
|
||||||
{(() => {
|
{(() => {
|
||||||
const under =
|
const under =
|
||||||
@@ -776,14 +783,14 @@ export default function PlanPage() {
|
|||||||
<div className="kv">
|
<div className="kv">
|
||||||
<span>Call {sideLabel}</span>
|
<span>Call {sideLabel}</span>
|
||||||
<span className="mono">
|
<span className="mono">
|
||||||
{fmtTop(callPx, callSz)}
|
{fmtTopEx("option", callPx, callSz)}
|
||||||
{!open ? ` · 杠杆 ${callLev}` : ""}
|
{!open ? ` · 杠杆 ${callLev}` : ""}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="kv">
|
<div className="kv">
|
||||||
<span>Put {sideLabel}</span>
|
<span>Put {sideLabel}</span>
|
||||||
<span className="mono">
|
<span className="mono">
|
||||||
{fmtTop(putPx, putSz)}
|
{fmtTopEx("option", putPx, putSz)}
|
||||||
{!open ? ` · 杠杆 ${putLev}` : ""}
|
{!open ? ` · 杠杆 ${putLev}` : ""}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { useEffect, useId, useMemo, useState } from "react";
|
import { useEffect, useId, useMemo, useState } from "react";
|
||||||
import { apiFetch } from "../api/client";
|
import { apiFetch } from "../api/client";
|
||||||
|
import { fmtExPx } from "../format";
|
||||||
import {
|
import {
|
||||||
closeReasonZh,
|
closeReasonZh,
|
||||||
fillDescZh,
|
fillDescZh,
|
||||||
@@ -495,7 +496,7 @@ export default function TradesPage() {
|
|||||||
{fmtMovePoints(selectedGroup.move_points)}
|
{fmtMovePoints(selectedGroup.move_points)}
|
||||||
{selectedGroup.entry_index_px != null &&
|
{selectedGroup.entry_index_px != null &&
|
||||||
selectedGroup.close_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>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -512,7 +513,10 @@ export default function TradesPage() {
|
|||||||
<div className="kv">
|
<div className="kv">
|
||||||
<span>结算指数</span>
|
<span>结算指数</span>
|
||||||
<span className="mono">
|
<span className="mono">
|
||||||
{fmt(selectedGroup.expiry_settle.settle_index_px)}
|
{fmtExPx(
|
||||||
|
"index",
|
||||||
|
selectedGroup.expiry_settle.settle_index_px,
|
||||||
|
)}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="kv">
|
<div className="kv">
|
||||||
@@ -554,7 +558,9 @@ export default function TradesPage() {
|
|||||||
) : null}
|
) : null}
|
||||||
</span>
|
</span>
|
||||||
<span className="mono">
|
<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 != null && Number.isFinite(notional)
|
||||||
? ` · 合计 ${notional.toFixed(2)}`
|
? ` · 合计 ${notional.toFixed(2)}`
|
||||||
: ""}{" "}
|
: ""}{" "}
|
||||||
|
|||||||
Reference in New Issue
Block a user