feat: archive trades by close time and show open time on live positions
Sort inner-archive daily trades by closed_at_ms; add open time and live hold duration to instance and hub position cards, with exchange margin on hub footer. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -6757,6 +6757,9 @@ def api_price_snapshot():
|
||||
symbol=r["symbol"],
|
||||
)
|
||||
apply_time_close_to_payload(payload, r)
|
||||
payload["opened_at"] = r["opened_at"] if "opened_at" in r.keys() else None
|
||||
open_ms = r["opened_at_ms"] if "opened_at_ms" in r.keys() else None
|
||||
payload["opened_at_ms"] = int(open_ms) if open_ms not in (None, "") else None
|
||||
new_sl, new_tp, changed = order_monitor_tpsl_needs_sync(
|
||||
r["stop_loss"], r["take_profit"], exchange_tpsl
|
||||
)
|
||||
|
||||
@@ -476,6 +476,8 @@
|
||||
<span>计划基数: {{ funds_fmt(o.margin_capital) if o.margin_capital is not none else '-' }}U</span>
|
||||
<span>杠杆: {{ o.leverage or '-' }}x</span>
|
||||
<span>仓位占比: {{ o.position_ratio if o.position_ratio is not none else '-' }}%</span>
|
||||
<span>开仓时间: {{ (o.opened_at or '-')[:16] }}</span>
|
||||
<span>持仓时长: <span class="order-hold-duration" id="order-hold-duration-{{ o.id }}" data-order-opened-ms="{{ o.opened_at_ms or '' }}">—</span></span>
|
||||
</div>
|
||||
<div class="pos-ex-orders">
|
||||
<div class="pos-ex-orders-title">交易所止盈止损</div>
|
||||
@@ -2227,10 +2229,41 @@ function refreshPriceSnapshotConditional(){
|
||||
paintExchangeTpslRow(o.id, o.exchange_tpsl || {});
|
||||
paintPlanTpslDisplay(o.id, o);
|
||||
if(window.TimeCloseUI) TimeCloseUI.paintOrderTimeClose(o);
|
||||
const holdEl = document.getElementById(`order-hold-duration-${o.id}`);
|
||||
if(holdEl && o.opened_at_ms != null && o.opened_at_ms !== ""){
|
||||
holdEl.setAttribute("data-order-opened-ms", String(o.opened_at_ms));
|
||||
}
|
||||
});
|
||||
tickOrderHoldDurations();
|
||||
}
|
||||
}).catch(()=>{});
|
||||
}
|
||||
function formatLiveHoldDurationFromMs(openedMs, nowMs){
|
||||
if(openedMs == null || openedMs === "" || !Number.isFinite(Number(openedMs))) return "—";
|
||||
const ms = Number(openedMs);
|
||||
const now = (nowMs != null) ? nowMs : Date.now();
|
||||
let sec = Math.floor((now - ms) / 1000);
|
||||
if(sec < 0) sec = 0;
|
||||
if(sec <= 0) return "0分钟";
|
||||
const d = Math.floor(sec / 86400); sec %= 86400;
|
||||
const h = Math.floor(sec / 3600); sec %= 3600;
|
||||
const m = Math.floor(sec / 60);
|
||||
const parts = [];
|
||||
if(d) parts.push(`${d}天`);
|
||||
if(h) parts.push(`${h}小时`);
|
||||
if(m || !parts.length) parts.push(`${m}分钟`);
|
||||
return parts.join("");
|
||||
}
|
||||
function tickOrderHoldDurations(){
|
||||
const now = Date.now();
|
||||
document.querySelectorAll(".order-hold-duration[data-order-opened-ms]").forEach(el=>{
|
||||
const ms = Number(el.getAttribute("data-order-opened-ms"));
|
||||
if(!Number.isFinite(ms) || ms <= 0) return;
|
||||
el.textContent = formatLiveHoldDurationFromMs(ms, now);
|
||||
});
|
||||
}
|
||||
setInterval(tickOrderHoldDurations, 1000);
|
||||
tickOrderHoldDurations();
|
||||
setInterval(refreshPriceSnapshotConditional, {{ price_refresh_seconds * 1000 }});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
Reference in New Issue
Block a user