959593cdab
Program monitors open positions and market-closes at deadline; UI shows label and countdown on instance and hub boards. Co-authored-by: Cursor <cursoragent@cursor.com>
94 lines
3.2 KiB
JavaScript
94 lines
3.2 KiB
JavaScript
/**
|
|
* 时间平仓:表单开关 + 持仓倒计时。
|
|
*/
|
|
(function (global) {
|
|
"use strict";
|
|
|
|
function pad2(n) {
|
|
return n < 10 ? "0" + n : String(n);
|
|
}
|
|
|
|
function formatCountdown(sec) {
|
|
const s = Math.max(0, parseInt(sec, 10) || 0);
|
|
const h = Math.floor(s / 3600);
|
|
const m = Math.floor((s % 3600) / 60);
|
|
const r = s % 60;
|
|
return pad2(h) + ":" + pad2(m) + ":" + pad2(r);
|
|
}
|
|
|
|
function bindTimeCloseForm(checkboxId, selectId, wrapId) {
|
|
const cb = document.getElementById(checkboxId);
|
|
const sel = document.getElementById(selectId);
|
|
const wrap = wrapId ? document.getElementById(wrapId) : null;
|
|
if (!cb || !sel) return;
|
|
function sync() {
|
|
const on = !!cb.checked;
|
|
sel.disabled = !on;
|
|
if (wrap) wrap.classList.toggle("is-disabled", !on);
|
|
}
|
|
cb.addEventListener("change", sync);
|
|
sync();
|
|
}
|
|
|
|
function paintOrderTimeClose(order) {
|
|
if (!order || order.id == null) return;
|
|
const wrap = document.getElementById("order-time-close-wrap-" + order.id);
|
|
const cd = document.getElementById("order-time-close-cd-" + order.id);
|
|
if (!wrap || !cd) return;
|
|
const enabled = !!(order.time_close_enabled || order.time_close_at_ms);
|
|
if (!enabled) {
|
|
wrap.style.display = "none";
|
|
return;
|
|
}
|
|
wrap.style.display = "";
|
|
const hours = order.time_close_hours;
|
|
const label = order.time_close_label || (hours ? "时间平仓 " + hours + "h" : "时间平仓");
|
|
const labelEl = wrap.querySelector(".pos-time-close-label");
|
|
if (labelEl) labelEl.textContent = label;
|
|
let rem =
|
|
order.time_close_remaining_sec != null
|
|
? Number(order.time_close_remaining_sec)
|
|
: null;
|
|
if ((rem == null || !Number.isFinite(rem)) && order.time_close_at_ms) {
|
|
rem = Math.max(0, Math.floor((Number(order.time_close_at_ms) - Date.now()) / 1000));
|
|
}
|
|
cd.textContent = Number.isFinite(rem) ? formatCountdown(rem) : "--:--:--";
|
|
wrap.dataset.closeAtMs = order.time_close_at_ms ? String(order.time_close_at_ms) : "";
|
|
}
|
|
|
|
function tickLocalCountdowns() {
|
|
document.querySelectorAll("[data-close-at-ms]").forEach(function (wrap) {
|
|
const closeAtRaw = wrap.dataset.closeAtMs || wrap.getAttribute("data-close-at-ms") || "";
|
|
const cd = wrap.querySelector(".pos-time-close-cd");
|
|
if (!cd) return;
|
|
const closeAt = Number(closeAtRaw);
|
|
if (!closeAt) return;
|
|
const rem = Math.max(0, Math.floor((closeAt - Date.now()) / 1000));
|
|
cd.textContent = formatCountdown(rem);
|
|
});
|
|
}
|
|
|
|
function paintOrders(orders) {
|
|
(orders || []).forEach(paintOrderTimeClose);
|
|
}
|
|
|
|
function syncKeyTimeCloseVisibility(show) {
|
|
const wrap = document.getElementById("key-time-close-wrap");
|
|
if (!wrap) return;
|
|
wrap.style.display = show ? "inline-flex" : "none";
|
|
}
|
|
|
|
global.TimeCloseUI = {
|
|
bindTimeCloseForm: bindTimeCloseForm,
|
|
paintOrderTimeClose: paintOrderTimeClose,
|
|
paintOrders: paintOrders,
|
|
tickLocalCountdowns: tickLocalCountdowns,
|
|
syncKeyTimeCloseVisibility: syncKeyTimeCloseVisibility,
|
|
formatCountdown: formatCountdown,
|
|
};
|
|
|
|
if (!global.__timeCloseCountdownTimer) {
|
|
global.__timeCloseCountdownTimer = setInterval(tickLocalCountdowns, 1000);
|
|
}
|
|
})(typeof window !== "undefined" ? window : globalThis);
|