Options: default nearest expiry and env ask-liquidity chain filter.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -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();
|
||||
|
||||
Vendored
+1
@@ -85,6 +85,7 @@ HOT_RELOAD_EXACT = frozenset({
|
||||
"HEDGE_PLAN_ENABLED",
|
||||
"HEDGE_PLAN_SHOW_PERP_OPTIONS",
|
||||
"HEDGE_PLAN_SHOW_OPTIONS_OPTIONS",
|
||||
"OKX_OPTIONS_CHAIN_ASK_LIQ_FILTER_ENABLED",
|
||||
"HEDGE_PLAN_LIVE_ORDER",
|
||||
"HEDGE_PLAN_OPEN_ORDER",
|
||||
"HEDGE_PLAN_ON_PERP_SL_CLOSE_OPTIONS",
|
||||
|
||||
Vendored
+6
@@ -126,6 +126,11 @@ _OPTIONS_SECTION: dict[str, Any] = {
|
||||
("OKX_OPTIONS_TRADE_BUDGET_USDC", "单笔预算(USDC)", ""),
|
||||
("OKX_OPTIONS_BUDGET_BUFFER", "预算缓冲比例", "如 0.95"),
|
||||
("OKX_OPTIONS_DEFAULT_UNDERLY", "默认标的", "如 ETH"),
|
||||
(
|
||||
"OKX_OPTIONS_CHAIN_ASK_LIQ_FILTER_ENABLED",
|
||||
"链上仅显示有卖一",
|
||||
"默认 true;开启后隐藏无卖一深度或深度不足1张的合约(含标记价估算行)",
|
||||
),
|
||||
],
|
||||
}
|
||||
|
||||
@@ -186,6 +191,7 @@ _RUNTIME_ENV_DEFAULTS: dict[str, str] = {
|
||||
"RISK_MOOD_ISSUES_DAILY_FREEZE": "true",
|
||||
"HEDGE_PLAN_SHOW_PERP_OPTIONS": "true",
|
||||
"HEDGE_PLAN_SHOW_OPTIONS_OPTIONS": "true",
|
||||
"OKX_OPTIONS_CHAIN_ASK_LIQ_FILTER_ENABLED": "true",
|
||||
"HEDGE_PLAN_OO_CLOSE_MODE_ENABLED": "true",
|
||||
"HEDGE_PLAN_OO_BIAS_SPLIT_BY": "budget",
|
||||
"HEDGE_PLAN_OO_BIAS_RATIO": "0.7",
|
||||
|
||||
@@ -108,6 +108,7 @@ def _build_cfg(app_module: Any) -> dict[str, Any]:
|
||||
"default_underly": (os.getenv("OKX_OPTIONS_DEFAULT_UNDERLY") or "ETH").strip().upper(),
|
||||
"max_dte_days": _env_float("OKX_OPTIONS_MAX_DTE_DAYS", 2.0),
|
||||
"chain_max_dte_days": _env_float("OKX_OPTIONS_CHAIN_MAX_DTE_DAYS", 14.0),
|
||||
"chain_ask_liq_filter": _env_bool("OKX_OPTIONS_CHAIN_ASK_LIQ_FILTER_ENABLED", True),
|
||||
"itm_max_dist": _env_float("OKX_OPTIONS_ITM_MAX_DIST_USD", 30.0),
|
||||
"td_mode": (os.getenv("OKX_OPTIONS_TD_MODE") or "isolated").strip(),
|
||||
# 市价平仓已硬关闭(忽略 env),仅买一限价
|
||||
@@ -370,6 +371,8 @@ def register_options_routes(app: Flask, cfg: dict[str, Any]) -> None:
|
||||
return jsonify({"ok": False, "msg": f"加载期权链失败: {e}"})
|
||||
expiries = chain.get("expiries") or []
|
||||
chain_err = chain.get("chain_error")
|
||||
# 热更新:每次读 env,保存配置后刷新链即可生效
|
||||
ask_liq_filter = _env_bool("OKX_OPTIONS_CHAIN_ASK_LIQ_FILTER_ENABLED", True)
|
||||
if not expiries:
|
||||
return jsonify(
|
||||
{
|
||||
@@ -377,9 +380,17 @@ def register_options_routes(app: Flask, cfg: dict[str, Any]) -> None:
|
||||
"msg": chain_err or "暂无到期日,请稍后点「刷新链」",
|
||||
**chain,
|
||||
"chain_max_dte_days": cfg["chain_max_dte_days"],
|
||||
"ask_liq_filter_enabled": ask_liq_filter,
|
||||
}
|
||||
)
|
||||
return jsonify({"ok": True, **chain, "chain_max_dte_days": cfg["chain_max_dte_days"]})
|
||||
return jsonify(
|
||||
{
|
||||
"ok": True,
|
||||
**chain,
|
||||
"chain_max_dte_days": cfg["chain_max_dte_days"],
|
||||
"ask_liq_filter_enabled": ask_liq_filter,
|
||||
}
|
||||
)
|
||||
|
||||
@app.route("/api/options/quote")
|
||||
@lr
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<div class="options-page-wrap" style="grid-column:1/-1" id="options-root"
|
||||
data-default-underly="{{ options_default_underly | default('ETH') }}">
|
||||
data-default-underly="{{ options_default_underly | default('ETH') }}"
|
||||
data-ask-liq-filter="{% if options_chain_ask_liq_filter is defined %}{{ '1' if options_chain_ask_liq_filter else '0' }}{% else %}1{% endif %}">
|
||||
{% if not options_enabled %}
|
||||
<div class="flash" style="margin-bottom:12px">期权 API 未启用:请在 <code>crypto_monitor_okx/.env</code> 设置 <code>OKX_OPTIONS_ENABLED=true</code> 及主账户 <code>OKX_OPTIONS_API_*</code>,然后 <code>pm2 restart crypto_okx --update-env</code>.</div>
|
||||
{% endif %}
|
||||
@@ -7,7 +8,7 @@
|
||||
<div class="options-dual-grid">
|
||||
<div class="card options-order-card">
|
||||
<h2>期权下单 <a class="muted" href="/options/guide" target="_blank" rel="noopener" style="font-size:13px;font-weight:500;margin-left:8px">开平仓与监控说明</a></h2>
|
||||
<p class="muted options-hint">报价单位为每 1 ETH/BTC;1 张 = 0.01.<strong>列表</strong>含卖一/买一;<strong>T 型</strong>仅卖一(买方开仓),中间为跨式双买测算.链上无卖一挂单时以标记价/内在价值估算并标 <strong>~</strong>(仅参考).<strong>开仓只认真实卖一价且卖一深度>0</strong>;无深度时面板显示参考标记价并禁用买入.链展示近 <span id="opt-chain-dte">14</span> 日到期.<strong>T 型</strong>默认 ATM ±5 档,可展开全部.平仓仅买一限价,见说明.</p>
|
||||
<p class="muted options-hint">报价单位为每 1 ETH/BTC;1 张 = 0.01.默认选中<strong>最近一期</strong>到期,可手动改.<strong>列表</strong>含卖一/买一;<strong>T 型</strong>仅卖一(买方开仓),中间为跨式双买测算.环境配置「链上仅显示有卖一」开启时,隐藏无真实卖一或深度不足 1 张的合约(估算价 <strong>~</strong> 亦不显示).<strong>开仓只认真实卖一价且卖一深度≥1</strong>.链展示近 <span id="opt-chain-dte">14</span> 日到期.<strong>T 型</strong>默认 ATM ±5 档,可展开全部.平仓仅买一限价,见说明.</p>
|
||||
<div class="form-row options-chain-toolbar">
|
||||
<button type="button" class="btn-secondary opt-uly-btn active" data-uly="ETH">ETH</button>
|
||||
<button type="button" class="btn-secondary opt-uly-btn" data-uly="BTC">BTC</button>
|
||||
@@ -299,4 +300,4 @@
|
||||
</div>
|
||||
</div>
|
||||
<script src="/static/options_expiry_countdown.js?v=1"></script>
|
||||
<script src="/static/options_panel.js?v=45"></script>
|
||||
<script src="/static/options_panel.js?v=46"></script>
|
||||
|
||||
Reference in New Issue
Block a user