Fix intermittent loss of options positions and realtime PnL on refresh.
Use stale-while-revalidate for positions API and UI, throttle sync calls, and avoid overwriting displayed PnL with null on transient failures. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -17,6 +17,12 @@
|
||||
expandedPosInst: null,
|
||||
};
|
||||
|
||||
let lastGoodPositions = null;
|
||||
let lastGoodPositionsAt = 0;
|
||||
let positionsRefreshSeq = 0;
|
||||
let refreshAllTimer = null;
|
||||
const POSITIONS_STALE_MS = 45000;
|
||||
|
||||
function fmt(v, d) {
|
||||
if (v === null || v === undefined || Number.isNaN(Number(v))) return "—";
|
||||
return Number(v).toFixed(d == null ? 2 : d);
|
||||
@@ -570,20 +576,38 @@
|
||||
}
|
||||
}
|
||||
|
||||
async function refreshPositions() {
|
||||
const d = await apiJson("/api/options/positions");
|
||||
function resolvePositionsList(d) {
|
||||
const now = Date.now();
|
||||
const list = (d && d.ok && d.positions) ? d.positions : [];
|
||||
if (d && d.ok) {
|
||||
if (list.length) {
|
||||
lastGoodPositions = list;
|
||||
lastGoodPositionsAt = now;
|
||||
return list;
|
||||
}
|
||||
lastGoodPositions = null;
|
||||
lastGoodPositionsAt = 0;
|
||||
return list;
|
||||
}
|
||||
if (lastGoodPositions && lastGoodPositions.length && now - lastGoodPositionsAt < POSITIONS_STALE_MS) {
|
||||
return lastGoodPositions;
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
function paintPositions(list) {
|
||||
const wrap = document.getElementById("opt-pos-cards");
|
||||
const empty = document.getElementById("opt-pos-empty");
|
||||
const livePane = document.getElementById("opt-pos-live");
|
||||
const list = (d.ok && d.positions) || [];
|
||||
if (!wrap) return;
|
||||
wrap.innerHTML = "";
|
||||
if (!list.length) {
|
||||
empty.style.display = "";
|
||||
if (empty) empty.style.display = "";
|
||||
state.expandedPosInst = null;
|
||||
if (livePane) livePane.classList.remove("options-pos-live-pane--accordion");
|
||||
return;
|
||||
}
|
||||
empty.style.display = "none";
|
||||
if (empty) empty.style.display = "none";
|
||||
const multi = list.length >= 2;
|
||||
wrap.classList.toggle("opt-pos-cards--accordion", multi);
|
||||
if (livePane) livePane.classList.toggle("options-pos-live-pane--accordion", multi);
|
||||
@@ -611,6 +635,13 @@
|
||||
}
|
||||
}
|
||||
|
||||
async function refreshPositions() {
|
||||
const seq = ++positionsRefreshSeq;
|
||||
const d = await apiJson("/api/options/positions");
|
||||
if (seq !== positionsRefreshSeq) return;
|
||||
paintPositions(resolvePositionsList(d));
|
||||
}
|
||||
|
||||
async function refreshStats() {
|
||||
const d = await apiJson("/api/options/stats");
|
||||
const winEl = document.getElementById("opt-stats-winrate");
|
||||
@@ -704,9 +735,13 @@
|
||||
}
|
||||
|
||||
function refreshAllPositions() {
|
||||
refreshPositions();
|
||||
refreshStats();
|
||||
refreshHistory();
|
||||
if (refreshAllTimer) clearTimeout(refreshAllTimer);
|
||||
refreshAllTimer = setTimeout(function () {
|
||||
refreshAllTimer = null;
|
||||
refreshPositions();
|
||||
refreshStats();
|
||||
refreshHistory();
|
||||
}, 120);
|
||||
}
|
||||
|
||||
function bootOptionsPanel() {
|
||||
|
||||
Reference in New Issue
Block a user