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 "—";
|
if (v === null || v === undefined || Number.isNaN(Number(v))) return "—";
|
||||||
const n = Number(v);
|
const n = Number(v);
|
||||||
const tick = Number(tickSz);
|
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;
|
let decimals = 0;
|
||||||
if (tick < 1) decimals = Math.max(0, -Math.round(Math.log10(tick)));
|
if (tick < 1) decimals = Math.max(0, -Math.round(Math.log10(tick)));
|
||||||
else if (String(tick).indexOf(".") >= 0) decimals = String(tick).split(".")[1].length;
|
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 closeSheets = p.avail_pos != null && Number(p.avail_pos) > 0 ? p.avail_pos : p.pos;
|
||||||
const tickSz = p.tick_sz;
|
const tickSz = p.tick_sz;
|
||||||
const premTxt = fmtDisplay(p.premium_paid_fmt, p.premium_paid != null ? fmtUsdc(p.premium_paid) : null);
|
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));
|
// 优先用数值+tick 现算,避免接口侧 mark_px_fmt 带着浮点毛刺直出
|
||||||
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);
|
||||||
return (
|
return (
|
||||||
'<div class="pos-card-head">' +
|
'<div class="pos-card-head">' +
|
||||||
'<div class="pos-card-symbol"><strong>' + (p.inst_id || "") + '</strong>' +
|
'<div class="pos-card-symbol"><strong>' + (p.inst_id || "") + '</strong>' +
|
||||||
@@ -1430,12 +1435,22 @@
|
|||||||
const livePane = document.getElementById("opt-pos-live");
|
const livePane = document.getElementById("opt-pos-live");
|
||||||
if (!wrap) return;
|
if (!wrap) return;
|
||||||
const active = document.activeElement;
|
const active = document.activeElement;
|
||||||
const draftFocusInst =
|
// 正在输入目标指数:先落到草稿,本轮不重绘整卡,避免数字往回退
|
||||||
active && active.classList && active.classList.contains("opt-pos-target-input")
|
if (active && active.classList && active.classList.contains("opt-pos-target-input")) {
|
||||||
? active.getAttribute("data-inst")
|
const focusInst = active.getAttribute("data-inst") || "";
|
||||||
: null;
|
if (focusInst) {
|
||||||
const draftSelStart = draftFocusInst != null ? active.selectionStart : null;
|
state.targetDraftByInst[focusInst] = String(active.value || "");
|
||||||
const draftSelEnd = draftFocusInst != null ? active.selectionEnd : null;
|
}
|
||||||
|
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 = "";
|
wrap.innerHTML = "";
|
||||||
if (!list.length) {
|
if (!list.length) {
|
||||||
if (empty) empty.style.display = "";
|
if (empty) empty.style.display = "";
|
||||||
@@ -1469,17 +1484,6 @@
|
|||||||
if (window.OptionsExpiryCountdown && OptionsExpiryCountdown.ensureTimer) {
|
if (window.OptionsExpiryCountdown && OptionsExpiryCountdown.ensureTimer) {
|
||||||
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() {
|
async function refreshPositions() {
|
||||||
|
|||||||
@@ -16,7 +16,10 @@
|
|||||||
if (v === null || v === undefined || Number.isNaN(Number(v))) return "—";
|
if (v === null || v === undefined || Number.isNaN(Number(v))) return "—";
|
||||||
const n = Number(v);
|
const n = Number(v);
|
||||||
const tick = Number(tickSz);
|
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;
|
let decimals = 0;
|
||||||
if (tick < 1) decimals = Math.max(0, -Math.round(Math.log10(tick)));
|
if (tick < 1) decimals = Math.max(0, -Math.round(Math.log10(tick)));
|
||||||
else if (String(tick).indexOf(".") >= 0) decimals = String(tick).split(".")[1].length;
|
else if (String(tick).indexOf(".") >= 0) decimals = String(tick).split(".")[1].length;
|
||||||
@@ -131,8 +134,8 @@
|
|||||||
const closePreview = p.close_preview || {};
|
const closePreview = p.close_preview || {};
|
||||||
const tickSz = p.tick_sz;
|
const tickSz = p.tick_sz;
|
||||||
const premTxt = fmtDisplay(p.premium_paid_fmt, p.premium_paid != null ? fmtUsdc(p.premium_paid) : null);
|
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 avgTxt = p.avg_px != null ? fmtOptionPx(p.avg_px, tickSz) : fmtDisplay(p.avg_px_fmt);
|
||||||
const markTxt = fmtDisplay(p.mark_px_fmt, fmtOptionPx(p.mark_px, tickSz));
|
const markTxt = p.mark_px != null ? fmtOptionPx(p.mark_px, tickSz) : fmtDisplay(p.mark_px_fmt);
|
||||||
let headActions = "";
|
let headActions = "";
|
||||||
if (!readOnly) {
|
if (!readOnly) {
|
||||||
const closeSheets = p.avail_pos != null && Number(p.avail_pos) > 0 ? p.avail_pos : p.pos;
|
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:
|
def format_option_px(px: float, tick_sz: Any) -> str:
|
||||||
tick = _safe_float(tick_sz)
|
tick = _safe_float(tick_sz)
|
||||||
if tick is None or tick <= 0:
|
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:
|
if tick < 1:
|
||||||
decimals = max(0, -int(round(math.log10(tick))))
|
decimals = max(0, -int(round(math.log10(tick))))
|
||||||
return f"{px:.{decimals}f}".rstrip("0").rstrip(".") or "0"
|
return f"{px:.{decimals}f}".rstrip("0").rstrip(".") or "0"
|
||||||
|
|||||||
@@ -273,4 +273,4 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script src="/static/options_expiry_countdown.js?v=1"></script>
|
<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(1370, "5") == "1370"
|
||||||
assert format_option_px(1160, 5) == "1160"
|
assert format_option_px(1160, 5) == "1160"
|
||||||
assert format_option_px(1000, "5") == "1000"
|
assert format_option_px(1000, "5") == "1000"
|
||||||
|
# 无 tick 时不得透出浮点毛刺
|
||||||
|
assert format_option_px(482.4881990066513, None) == "482.4882"
|
||||||
|
|
||||||
|
|
||||||
def test_premium_per_sheet():
|
def test_premium_per_sheet():
|
||||||
|
|||||||
Reference in New Issue
Block a user