Fix options mark price float junk and target index input rollback.
Skip full card redraw while the monitor target field is focused, and format mark/avg without relying on dusty server strings. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -47,7 +47,11 @@
|
||||
if (v === null || v === undefined || Number.isNaN(Number(v))) return "—";
|
||||
const n = Number(v);
|
||||
const tick = Number(tickSz);
|
||||
if (!tickSz || Number.isNaN(tick) || tick <= 0) return String(n);
|
||||
if (!tickSz || Number.isNaN(tick) || tick <= 0) {
|
||||
// 无 tick 时裁掉浮点毛刺,勿 482.4881990066513
|
||||
let s = n.toFixed(4).replace(/\.?0+$/, "");
|
||||
return s || "0";
|
||||
}
|
||||
let decimals = 0;
|
||||
if (tick < 1) decimals = Math.max(0, -Math.round(Math.log10(tick)));
|
||||
else if (String(tick).indexOf(".") >= 0) decimals = String(tick).split(".")[1].length;
|
||||
@@ -1006,8 +1010,9 @@
|
||||
const closeSheets = p.avail_pos != null && Number(p.avail_pos) > 0 ? p.avail_pos : p.pos;
|
||||
const tickSz = p.tick_sz;
|
||||
const premTxt = fmtDisplay(p.premium_paid_fmt, p.premium_paid != null ? fmtUsdc(p.premium_paid) : null);
|
||||
const avgTxt = fmtDisplay(p.avg_px_fmt, fmtOptionPx(p.avg_px, tickSz));
|
||||
const markTxt = fmtDisplay(p.mark_px_fmt, fmtOptionPx(p.mark_px, tickSz));
|
||||
// 优先用数值+tick 现算,避免接口侧 mark_px_fmt 带着浮点毛刺直出
|
||||
const avgTxt = p.avg_px != null ? fmtOptionPx(p.avg_px, tickSz) : fmtDisplay(p.avg_px_fmt);
|
||||
const markTxt = p.mark_px != null ? fmtOptionPx(p.mark_px, tickSz) : fmtDisplay(p.mark_px_fmt);
|
||||
return (
|
||||
'<div class="pos-card-head">' +
|
||||
'<div class="pos-card-symbol"><strong>' + (p.inst_id || "") + '</strong>' +
|
||||
@@ -1430,12 +1435,22 @@
|
||||
const livePane = document.getElementById("opt-pos-live");
|
||||
if (!wrap) return;
|
||||
const active = document.activeElement;
|
||||
const draftFocusInst =
|
||||
active && active.classList && active.classList.contains("opt-pos-target-input")
|
||||
? active.getAttribute("data-inst")
|
||||
: null;
|
||||
const draftSelStart = draftFocusInst != null ? active.selectionStart : null;
|
||||
const draftSelEnd = draftFocusInst != null ? active.selectionEnd : null;
|
||||
// 正在输入目标指数:先落到草稿,本轮不重绘整卡,避免数字往回退
|
||||
if (active && active.classList && active.classList.contains("opt-pos-target-input")) {
|
||||
const focusInst = active.getAttribute("data-inst") || "";
|
||||
if (focusInst) {
|
||||
state.targetDraftByInst[focusInst] = String(active.value || "");
|
||||
}
|
||||
return;
|
||||
}
|
||||
// 未聚焦时也同步可见输入,防止漏掉 input 事件
|
||||
wrap.querySelectorAll(".opt-pos-target-input").forEach(function (inp) {
|
||||
const id = inp.getAttribute("data-inst") || "";
|
||||
if (!id) return;
|
||||
const v = String(inp.value || "");
|
||||
if (v.trim() === "") delete state.targetDraftByInst[id];
|
||||
else state.targetDraftByInst[id] = v;
|
||||
});
|
||||
wrap.innerHTML = "";
|
||||
if (!list.length) {
|
||||
if (empty) empty.style.display = "";
|
||||
@@ -1469,17 +1484,6 @@
|
||||
if (window.OptionsExpiryCountdown && OptionsExpiryCountdown.ensureTimer) {
|
||||
OptionsExpiryCountdown.ensureTimer();
|
||||
}
|
||||
if (draftFocusInst) {
|
||||
const reinp = wrap.querySelector('.opt-pos-target-input[data-inst="' + draftFocusInst + '"]');
|
||||
if (reinp) {
|
||||
reinp.focus();
|
||||
try {
|
||||
if (draftSelStart != null && draftSelEnd != null) {
|
||||
reinp.setSelectionRange(draftSelStart, draftSelEnd);
|
||||
}
|
||||
} catch (e) { /* ignore */ }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function refreshPositions() {
|
||||
|
||||
@@ -16,7 +16,10 @@
|
||||
if (v === null || v === undefined || Number.isNaN(Number(v))) return "—";
|
||||
const n = Number(v);
|
||||
const tick = Number(tickSz);
|
||||
if (!tickSz || Number.isNaN(tick) || tick <= 0) return String(n);
|
||||
if (!tickSz || Number.isNaN(tick) || tick <= 0) {
|
||||
let s = n.toFixed(4).replace(/\.?0+$/, "");
|
||||
return s || "0";
|
||||
}
|
||||
let decimals = 0;
|
||||
if (tick < 1) decimals = Math.max(0, -Math.round(Math.log10(tick)));
|
||||
else if (String(tick).indexOf(".") >= 0) decimals = String(tick).split(".")[1].length;
|
||||
@@ -131,8 +134,8 @@
|
||||
const closePreview = p.close_preview || {};
|
||||
const tickSz = p.tick_sz;
|
||||
const premTxt = fmtDisplay(p.premium_paid_fmt, p.premium_paid != null ? fmtUsdc(p.premium_paid) : null);
|
||||
const avgTxt = fmtDisplay(p.avg_px_fmt, fmtOptionPx(p.avg_px, tickSz));
|
||||
const markTxt = fmtDisplay(p.mark_px_fmt, fmtOptionPx(p.mark_px, tickSz));
|
||||
const avgTxt = p.avg_px != null ? fmtOptionPx(p.avg_px, tickSz) : fmtDisplay(p.avg_px_fmt);
|
||||
const markTxt = p.mark_px != null ? fmtOptionPx(p.mark_px, tickSz) : fmtDisplay(p.mark_px_fmt);
|
||||
let headActions = "";
|
||||
if (!readOnly) {
|
||||
const closeSheets = p.avail_pos != null && Number(p.avail_pos) > 0 ? p.avail_pos : p.pos;
|
||||
|
||||
@@ -115,7 +115,9 @@ def round_option_px(px: float, tick_sz: Any, side: str) -> float:
|
||||
def format_option_px(px: float, tick_sz: Any) -> str:
|
||||
tick = _safe_float(tick_sz)
|
||||
if tick is None or tick <= 0:
|
||||
return str(px)
|
||||
# 无 tick 时裁到 4 位并去尾零,避免 482.4881990066513 这类浮点毛刺
|
||||
s = f"{float(px):.4f}".rstrip("0").rstrip(".")
|
||||
return s or "0"
|
||||
if tick < 1:
|
||||
decimals = max(0, -int(round(math.log10(tick))))
|
||||
return f"{px:.{decimals}f}".rstrip("0").rstrip(".") or "0"
|
||||
|
||||
@@ -273,4 +273,4 @@
|
||||
</div>
|
||||
</div>
|
||||
<script src="/static/options_expiry_countdown.js?v=1"></script>
|
||||
<script src="/static/options_panel.js?v=32"></script>
|
||||
<script src="/static/options_panel.js?v=33"></script>
|
||||
|
||||
@@ -21,6 +21,8 @@ def test_round_option_px():
|
||||
assert format_option_px(1370, "5") == "1370"
|
||||
assert format_option_px(1160, 5) == "1160"
|
||||
assert format_option_px(1000, "5") == "1000"
|
||||
# 无 tick 时不得透出浮点毛刺
|
||||
assert format_option_px(482.4881990066513, None) == "482.4882"
|
||||
|
||||
|
||||
def test_premium_per_sheet():
|
||||
|
||||
Reference in New Issue
Block a user