Options: default nearest expiry and env ask-liquidity chain filter.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-07-20 08:37:39 +08:00
parent 7eb098def6
commit ed1ec6f14a
7 changed files with 96 additions and 15 deletions
+67 -11
View File
@@ -14,6 +14,8 @@
moneyFilter: "all",
chainView: "list",
strikeExpandAll: false,
/** 环境 OKX_OPTIONS_CHAIN_ASK_LIQ_FILTER_ENABLED;链接口可热更新 */
askLiqFilter: root.dataset.askLiqFilter !== "0",
chain: panelCache.chain || null,
selectedInst: null,
orderQuote: null,
@@ -361,21 +363,41 @@
return "";
}
function askLiqFilterOn() {
return !!state.askLiqFilter;
}
function hasAskLiquidity(c) {
if (!c) return false;
if (c.ask_estimated) return false;
const a = Number(c.ask);
const s = Number(c.ask_sz);
return Number.isFinite(a) && a > 0 && Number.isFinite(s) && s >= 1;
}
function syncAskLiqFilterFromChain(d) {
if (!d || d.ask_liq_filter_enabled == null) return;
state.askLiqFilter = !!d.ask_liq_filter_enabled;
root.dataset.askLiqFilter = state.askLiqFilter ? "1" : "0";
}
function countContractsForType(contracts) {
if (state.chainView === "t") {
return countStraddleStrikes(contracts);
}
return (contracts || []).filter(function (c) {
return c.opt_type === state.optType;
if (c.opt_type !== state.optType) return false;
if (askLiqFilterOn() && !hasAskLiquidity(c)) return false;
return true;
}).length;
}
function countStraddleStrikes(contracts) {
const strikes = new Set();
(contracts || []).forEach(function (c) {
if (c.strike != null) strikes.add(String(c.strike));
const rows = buildStraddleRows(contracts).filter(function (row) {
if (!askLiqFilterOn()) return true;
return hasAskLiquidity(row.call) || hasAskLiquidity(row.put);
});
return strikes.size;
return rows.length;
}
function buildStraddleRows(contracts) {
@@ -418,7 +440,11 @@
function filterStraddleRows(rows, indexPx) {
const atmStrike = findAtmStrike(rows, indexPx);
return rows.filter(function (row) {
return matchesStrikeRowFilter(row.strike, indexPx, atmStrike);
if (!matchesStrikeRowFilter(row.strike, indexPx, atmStrike)) return false;
if (askLiqFilterOn() && !hasAskLiquidity(row.call) && !hasAskLiquidity(row.put)) {
return false;
}
return true;
});
}
@@ -479,7 +505,10 @@
function filterChainContracts(contracts) {
return (contracts || []).filter(function (c) {
return c.opt_type === state.optType && matchesMoneyFilter(c.moneyness);
if (c.opt_type !== state.optType) return false;
if (!matchesMoneyFilter(c.moneyness)) return false;
if (askLiqFilterOn() && !hasAskLiquidity(c)) return false;
return true;
});
}
@@ -536,11 +565,32 @@
}
const line = document.getElementById("opt-index-line");
if (line) {
const liqHint = askLiqFilterOn() ? "仅显示卖一深度≥1张" : "显示全部卖一(含估算~)";
line.textContent =
"指数 " + state.underlying + " ≈ " + fmt(idx, 2) + " · 默认显示全部 · 实值含平值 · 虚值=价外";
"指数 " + state.underlying + " ≈ " + fmt(idx, 2) +
" · 默认最近一期 · " + liqHint + " · 实值含平值 · 虚值=价外";
}
}
function pickNearestExpiry(exps) {
if (!exps || !exps.length) return "";
const now = Date.now();
let best = null;
let bestDelta = Infinity;
exps.forEach(function (e) {
const t = Number(e.exp_time);
if (!Number.isFinite(t)) return;
const delta = t - now;
if (delta < -60000) return;
if (delta < bestDelta) {
bestDelta = delta;
best = e;
}
});
if (best) return String(best.exp_time);
return String(exps[0].exp_time);
}
function renderExpiryOptions(preserveSelection) {
const sel = document.getElementById("opt-exp-select");
if (!sel) return;
@@ -555,6 +605,8 @@
});
if (prev && exps.some(function (e) { return String(e.exp_time) === String(prev); })) {
sel.value = prev;
} else if (exps.length) {
sel.value = pickNearestExpiry(exps);
}
}
@@ -852,7 +904,8 @@
if (!list.length) {
const label = moneyFilterLabel();
const suffix = label ? label : optTypeLabel(state.optType);
tbody.innerHTML = '<tr><td colspan="' + cols + '" class="muted">该到期日暂无' + suffix + "合约</td></tr>";
const liqTip = askLiqFilterOn() ? "(卖一深度≥1 时才显示,可在环境配置关闭筛选)" : "";
tbody.innerHTML = '<tr><td colspan="' + cols + '" class="muted">该到期日暂无' + suffix + "合约" + liqTip + "</td></tr>";
state.selectedInst = null;
return;
}
@@ -911,8 +964,10 @@
const atmStrike = findAtmStrike(rows, indexPx);
let matchedSelected = false;
rows.forEach(function (row) {
const call = row.call;
const put = row.put;
const callRaw = row.call;
const putRaw = row.put;
const call = callRaw && (!askLiqFilterOn() || hasAskLiquidity(callRaw)) ? callRaw : null;
const put = putRaw && (!askLiqFilterOn() || hasAskLiquidity(putRaw)) ? putRaw : null;
const combined = straddleAskPerUnit(call && call.ask, put && put.ask);
const tr = document.createElement("tr");
tr.className = "opt-strike-row opt-strike-row-t";
@@ -1090,6 +1145,7 @@
panelCache.chain = d;
panelCache.underlying = uly;
panelCache.optType = state.optType;
syncAskLiqFilterFromChain(d);
if (!soft) {
state.selectedInst = null;
resetMoneyFilterToAll();