feat条件单新建按标的过滤匹配币种的客户端。
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -72,6 +72,23 @@ def _candidates_from_snaps(rows: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
||||
transfer_block_msg = str(snap.get("transfer_block_msg") or "").strip()
|
||||
if has_pos and not transfer_block_msg:
|
||||
transfer_block_msg = "期权持仓中禁止划转"
|
||||
allowed_raw = snap.get("options_allowed_underlys")
|
||||
if not isinstance(allowed_raw, list):
|
||||
allowed_raw = inst.get("allowed_underlys")
|
||||
allowed_underlys: list[str] = []
|
||||
if isinstance(allowed_raw, list):
|
||||
for x in allowed_raw:
|
||||
u = str(x or "").strip().upper()
|
||||
if u in ("ETH", "BTC") and u not in allowed_underlys:
|
||||
allowed_underlys.append(u)
|
||||
if not allowed_underlys:
|
||||
# 旧实例未上报白名单:仍显示在 ETH/BTC 下(兼容)
|
||||
allowed_underlys = ["ETH", "BTC"]
|
||||
default_underly = str(
|
||||
snap.get("options_underly") or inst.get("underly") or allowed_underlys[0]
|
||||
).strip().upper()
|
||||
if default_underly not in allowed_underlys:
|
||||
default_underly = allowed_underlys[0]
|
||||
out.append(
|
||||
{
|
||||
"id": row.get("id"),
|
||||
@@ -86,6 +103,8 @@ def _candidates_from_snaps(rows: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
||||
"funding_usdt": funding_usdt,
|
||||
"transfer_blocked": transfer_blocked,
|
||||
"transfer_block_msg": transfer_block_msg,
|
||||
"allowed_underlys": allowed_underlys,
|
||||
"default_underly": default_underly,
|
||||
}
|
||||
)
|
||||
return out
|
||||
|
||||
+95
-25
@@ -203,6 +203,31 @@
|
||||
}
|
||||
|
||||
var candidatesById = {};
|
||||
var allCandidates = [];
|
||||
|
||||
function selectedCreateUnderlying() {
|
||||
const el = document.getElementById("cf-underlying");
|
||||
const u = el ? String(el.value || "ETH").trim().toUpperCase() : "ETH";
|
||||
return u === "BTC" ? "BTC" : "ETH";
|
||||
}
|
||||
|
||||
function candidateMatchesUnderlying(it, uly) {
|
||||
const u = String(uly || "ETH").toUpperCase();
|
||||
const raw = it && it.allowed_underlys;
|
||||
const allow = Array.isArray(raw)
|
||||
? raw
|
||||
.map(function (x) {
|
||||
return String(x || "")
|
||||
.trim()
|
||||
.toUpperCase();
|
||||
})
|
||||
.filter(function (x) {
|
||||
return x === "ETH" || x === "BTC";
|
||||
})
|
||||
: [];
|
||||
if (!allow.length) return true;
|
||||
return allow.indexOf(u) >= 0;
|
||||
}
|
||||
|
||||
function fmtTradingU(v) {
|
||||
if (v == null || v === "") return "—";
|
||||
@@ -412,6 +437,60 @@
|
||||
);
|
||||
}
|
||||
|
||||
function paintCandidates(checkedIds) {
|
||||
const box = document.getElementById("cf-instances");
|
||||
if (!box) return;
|
||||
const uly = selectedCreateUnderlying();
|
||||
const list = (allCandidates || []).filter(function (it) {
|
||||
return candidateMatchesUnderlying(it, uly);
|
||||
});
|
||||
if (!(allCandidates || []).length) {
|
||||
box.innerHTML = '<span class="muted">暂无启用实例</span>';
|
||||
return;
|
||||
}
|
||||
if (!list.length) {
|
||||
box.innerHTML =
|
||||
'<span class="muted">当前标的 ' +
|
||||
escapeHtml(uly) +
|
||||
" 下暂无匹配客户端(请确认实例 OKX_OPTIONS_UNDERLYINGS)</span>";
|
||||
return;
|
||||
}
|
||||
const keep = {};
|
||||
(checkedIds || []).forEach(function (id) {
|
||||
keep[String(id)] = true;
|
||||
});
|
||||
const filteredChecked = list
|
||||
.map(function (it) {
|
||||
return String(it.id);
|
||||
})
|
||||
.filter(function (id) {
|
||||
return keep[id];
|
||||
});
|
||||
const sim = [];
|
||||
const live = [];
|
||||
const other = [];
|
||||
list.forEach(function (it) {
|
||||
const m = instanceMode(it);
|
||||
if (m === "sim") sim.push(it);
|
||||
else if (m === "live") live.push(it);
|
||||
else other.push(it);
|
||||
});
|
||||
box.innerHTML =
|
||||
renderCandidateGroup("sim", sim, filteredChecked) +
|
||||
renderCandidateGroup("live", live, filteredChecked) +
|
||||
renderCandidateGroup("", other, filteredChecked);
|
||||
box.querySelectorAll(".cond-inst-xfer").forEach(function (btn) {
|
||||
btn.addEventListener("click", function (ev) {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
transferInternal(
|
||||
btn.getAttribute("data-inst-id") || "",
|
||||
btn.getAttribute("data-dir") || "to_trading"
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function loadCandidates() {
|
||||
const box = document.getElementById("cf-instances");
|
||||
if (!box) return;
|
||||
@@ -423,35 +502,12 @@
|
||||
const r = await fetch("/api/cond_orders/candidates");
|
||||
const d = await r.json();
|
||||
const list = d.instances || [];
|
||||
if (!list.length) {
|
||||
box.innerHTML = '<span class="muted">暂无启用实例</span>';
|
||||
return;
|
||||
}
|
||||
const sim = [];
|
||||
const live = [];
|
||||
const other = [];
|
||||
allCandidates = list;
|
||||
candidatesById = {};
|
||||
list.forEach(function (it) {
|
||||
const m = instanceMode(it);
|
||||
candidatesById[String(it.id)] = it;
|
||||
if (m === "sim") sim.push(it);
|
||||
else if (m === "live") live.push(it);
|
||||
else other.push(it);
|
||||
});
|
||||
box.innerHTML =
|
||||
renderCandidateGroup("sim", sim, checkedIds) +
|
||||
renderCandidateGroup("live", live, checkedIds) +
|
||||
renderCandidateGroup("", other, checkedIds);
|
||||
box.querySelectorAll(".cond-inst-xfer").forEach(function (btn) {
|
||||
btn.addEventListener("click", function (ev) {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
transferInternal(
|
||||
btn.getAttribute("data-inst-id") || "",
|
||||
btn.getAttribute("data-dir") || "to_trading"
|
||||
);
|
||||
});
|
||||
});
|
||||
paintCandidates(checkedIds);
|
||||
}
|
||||
|
||||
function renderDesktopRows(list) {
|
||||
@@ -729,6 +785,20 @@
|
||||
const pxEl = document.getElementById("cf-trigger-px");
|
||||
if (typeEl) typeEl.addEventListener("change", syncTriggerHint);
|
||||
if (pxEl) pxEl.addEventListener("input", syncTriggerHint);
|
||||
const ulyEl = document.getElementById("cf-underlying");
|
||||
if (ulyEl) {
|
||||
ulyEl.addEventListener("change", function () {
|
||||
const box = document.getElementById("cf-instances");
|
||||
const checkedIds = box
|
||||
? Array.prototype.slice
|
||||
.call(box.querySelectorAll('input[name="cf-inst"]:checked'))
|
||||
.map(function (el) {
|
||||
return decodeURIComponent(el.value || "");
|
||||
})
|
||||
: [];
|
||||
paintCandidates(checkedIds);
|
||||
});
|
||||
}
|
||||
syncTriggerHint();
|
||||
// 读缓存秒开 + SSE;不再前端定时现场拉实例
|
||||
refresh();
|
||||
|
||||
@@ -68,7 +68,7 @@
|
||||
|
||||
<div class="cond-section">
|
||||
<div class="cond-section-title">客户端</div>
|
||||
<p class="muted" style="margin:0 0 8px;font-size:.76rem">展示交易/资金余额;「划入交易 / 划入资金」本页双向划转,表单不丢。</p>
|
||||
<p class="muted" style="margin:0 0 8px;font-size:.76rem">按上方标的过滤客户端(读实例标的白名单)。展示交易/资金余额;「划入交易 / 划入资金」本页双向划转,表单不丢。</p>
|
||||
<div id="cf-instances"></div>
|
||||
</div>
|
||||
|
||||
@@ -95,6 +95,6 @@
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block scripts %}
|
||||
<script src="{{ url_for('static', filename='cond_orders.js') }}?v=13"></script>
|
||||
<script src="{{ url_for('static', filename='cond_orders.js') }}?v=14"></script>
|
||||
<script>HubCondOrders.boot();</script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -46,6 +46,30 @@ class TestCondCandidatesFunds(unittest.TestCase):
|
||||
self.assertFalse(out[1]["selectable"])
|
||||
self.assertIsNone(out[1]["trading_usdt"])
|
||||
self.assertTrue(out[1]["transfer_blocked"])
|
||||
self.assertEqual(out[0]["allowed_underlys"], ["ETH", "BTC"])
|
||||
|
||||
def test_candidates_carry_allowed_underlys(self):
|
||||
from lib.cond_orders_cache_lib import _candidates_from_snaps
|
||||
|
||||
rows = [
|
||||
{
|
||||
"id": 3,
|
||||
"name": "eth-only",
|
||||
"online": True,
|
||||
"error": "",
|
||||
"snapshot": {
|
||||
"positions_brief": [],
|
||||
"cond_order_locked": False,
|
||||
"instance": {"mode": "sim", "underly": "ETH", "allowed_underlys": ["ETH"]},
|
||||
"options_allowed_underlys": ["ETH"],
|
||||
"options_underly": "ETH",
|
||||
"funds": {"trading_usdt": 1.0, "funding_usdt": 2.0},
|
||||
},
|
||||
}
|
||||
]
|
||||
out = _candidates_from_snaps(rows)
|
||||
self.assertEqual(out[0]["allowed_underlys"], ["ETH"])
|
||||
self.assertEqual(out[0]["default_underly"], "ETH")
|
||||
|
||||
def test_open_url_tab_appended(self):
|
||||
from lib.sso_lib import build_embed_open_url
|
||||
|
||||
Reference in New Issue
Block a user