Match options branding: title prefix and ETH/BTC header quotes.
Use 「加密货币 | OKX 对冲」 and show live top-right ETHUSDT/BTCUSDT quotes like the options instance. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -7134,6 +7134,36 @@ def options_main_page():
|
||||
return render_main_page("options")
|
||||
|
||||
|
||||
@app.route("/api/header-quotes")
|
||||
@login_required
|
||||
def api_header_quotes():
|
||||
"""顶栏 ETH/BTC 报价(公共 ticker last + 24h 涨跌)."""
|
||||
from lib.exchange.okx_public_http_lib import fetch_swap_ticker
|
||||
|
||||
quotes = []
|
||||
for base in ("ETH", "BTC"):
|
||||
item: dict = {"base": base, "symbol": f"{base}USDT"}
|
||||
try:
|
||||
row = fetch_swap_ticker(base)
|
||||
last = float(row.get("last") or row.get("markPx") or 0)
|
||||
open24 = float(row.get("open24h") or row.get("sodUtc0") or 0)
|
||||
change = (last - open24) if open24 > 0 else 0.0
|
||||
pct = (change / open24 * 100.0) if open24 > 0 else 0.0
|
||||
item.update(
|
||||
{
|
||||
"last": last,
|
||||
"open24h": open24,
|
||||
"change": change,
|
||||
"pct": pct,
|
||||
"mark": float(row.get("markPx") or last or 0) or None,
|
||||
}
|
||||
)
|
||||
except Exception as e:
|
||||
item["error"] = str(e)[:160]
|
||||
quotes.append(item)
|
||||
return jsonify({"ok": True, "quotes": quotes})
|
||||
|
||||
|
||||
@app.route("/api/account_snapshot")
|
||||
@login_required
|
||||
def api_account_snapshot():
|
||||
@@ -9408,6 +9438,7 @@ _OPTIONS_PANEL_JS = os.path.join(_REPO_STATIC_DIR, "options_panel.js")
|
||||
_OPTIONS_EXPIRY_COUNTDOWN_JS = os.path.join(_REPO_STATIC_DIR, "options_expiry_countdown.js")
|
||||
_OPTIONS_SETTINGS_JS = os.path.join(_REPO_STATIC_DIR, "options_settings.js")
|
||||
_HEDGE_PLAN_JS = os.path.join(_REPO_STATIC_DIR, "hedge_plan.js")
|
||||
_HEADER_LIVE_QUOTES_JS = os.path.join(_REPO_STATIC_DIR, "header_live_quotes.js")
|
||||
|
||||
|
||||
@app.route("/static/ai_review_render.js")
|
||||
@@ -9417,6 +9448,13 @@ def static_ai_review_render_js():
|
||||
return send_file(_AI_REVIEW_RENDER_JS, mimetype="application/javascript; charset=utf-8")
|
||||
|
||||
|
||||
@app.route("/static/header_live_quotes.js")
|
||||
def static_header_live_quotes_js():
|
||||
if not os.path.isfile(_HEADER_LIVE_QUOTES_JS):
|
||||
return Response("not found", status=404, mimetype="text/plain; charset=utf-8")
|
||||
return send_file(_HEADER_LIVE_QUOTES_JS, mimetype="application/javascript; charset=utf-8")
|
||||
|
||||
|
||||
@app.route("/static/form_submit_guard.js")
|
||||
def static_form_submit_guard_js():
|
||||
if not os.path.isfile(_FORM_SUBMIT_GUARD_JS):
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
/**
|
||||
* 顶栏右上角 ETH/BTC 实时报价(公共 ticker).
|
||||
*/
|
||||
(function (global) {
|
||||
const BASES = ["ETH", "BTC"];
|
||||
|
||||
function $(id) {
|
||||
return document.getElementById(id);
|
||||
}
|
||||
|
||||
function refreshMs() {
|
||||
const raw = document.body && document.body.getAttribute("data-price-refresh-ms");
|
||||
const n = Number(raw);
|
||||
return n > 2000 ? n : 5000;
|
||||
}
|
||||
|
||||
function fmtLast(base, v) {
|
||||
if (v == null || Number.isNaN(Number(v))) return "—";
|
||||
const n = Number(v);
|
||||
if (base === "BTC") {
|
||||
return n.toLocaleString("en-US", { minimumFractionDigits: 1, maximumFractionDigits: 1 });
|
||||
}
|
||||
return n.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 });
|
||||
}
|
||||
|
||||
function fmtChg(base, v) {
|
||||
if (v == null || Number.isNaN(Number(v))) return "—";
|
||||
const n = Number(v);
|
||||
const abs = Math.abs(n);
|
||||
const body =
|
||||
base === "BTC"
|
||||
? abs.toLocaleString("en-US", { minimumFractionDigits: 1, maximumFractionDigits: 1 })
|
||||
: abs.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 });
|
||||
if (n > 0) return "+" + body;
|
||||
if (n < 0) return "-" + body;
|
||||
return body;
|
||||
}
|
||||
|
||||
function fmtPct(v) {
|
||||
if (v == null || Number.isNaN(Number(v))) return "—";
|
||||
const n = Number(v);
|
||||
const body = Math.abs(n).toFixed(2) + "%";
|
||||
if (n > 0) return "+" + body;
|
||||
if (n < 0) return "-" + body;
|
||||
return body;
|
||||
}
|
||||
|
||||
function paintRow(row, q) {
|
||||
if (!row || !q) return;
|
||||
const last = row.querySelector('[data-hlq="last"]');
|
||||
const chg = row.querySelector('[data-hlq="chg"]');
|
||||
const pct = row.querySelector('[data-hlq="pct"]');
|
||||
const base = row.getAttribute("data-base") || "";
|
||||
if (last) last.textContent = fmtLast(base, q.last);
|
||||
if (chg) chg.textContent = fmtChg(base, q.change);
|
||||
if (pct) pct.textContent = fmtPct(q.pct);
|
||||
const up = Number(q.change) > 0 || Number(q.pct) > 0;
|
||||
const down = Number(q.change) < 0 || Number(q.pct) < 0;
|
||||
row.classList.toggle("is-up", up);
|
||||
row.classList.toggle("is-down", down);
|
||||
}
|
||||
|
||||
async function fetchQuotes() {
|
||||
const r = await fetch("/api/header-quotes", {
|
||||
credentials: "same-origin",
|
||||
cache: "no-store",
|
||||
});
|
||||
const d = await r.json();
|
||||
if (!d || !d.ok) throw new Error((d && d.msg) || "行情失败");
|
||||
return d.quotes || [];
|
||||
}
|
||||
|
||||
async function tick() {
|
||||
const host = $("header-live-quotes");
|
||||
if (!host) return;
|
||||
try {
|
||||
const quotes = await fetchQuotes();
|
||||
BASES.forEach(function (base) {
|
||||
const row = host.querySelector('.hlq-row[data-base="' + base + '"]');
|
||||
const q = quotes.find(function (x) {
|
||||
return String((x && x.base) || "").toUpperCase() === base;
|
||||
});
|
||||
if (row && q && !q.error) paintRow(row, q);
|
||||
});
|
||||
} catch (_) {
|
||||
/* 静默:保持上次有效行情 */
|
||||
}
|
||||
}
|
||||
|
||||
let timer = null;
|
||||
function boot() {
|
||||
if (!$("header-live-quotes")) return;
|
||||
void tick();
|
||||
if (timer) clearInterval(timer);
|
||||
timer = setInterval(function () {
|
||||
void tick();
|
||||
}, refreshMs());
|
||||
}
|
||||
|
||||
if (document.readyState === "loading") {
|
||||
document.addEventListener("DOMContentLoaded", boot);
|
||||
} else {
|
||||
boot();
|
||||
}
|
||||
|
||||
global.HeaderLiveQuotes = { refresh: tick, boot: boot };
|
||||
})(window);
|
||||
@@ -561,6 +561,129 @@ html[data-theme="light"] .theme-toggle-btn.is-active {
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
/* 顶栏品牌:加密货币 | OKX 对冲 + 右上角 ETH/BTC 报价 */
|
||||
.header.header--brand {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 52px;
|
||||
margin-bottom: 10px;
|
||||
padding: 0 168px 0 12px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.header.header--brand .brand-title {
|
||||
margin: 0;
|
||||
font-size: 1.7rem;
|
||||
font-weight: 700;
|
||||
color: #e8ecff;
|
||||
text-align: center;
|
||||
line-height: 1.25;
|
||||
letter-spacing: 0.02em;
|
||||
}
|
||||
.header.header--brand .brand-prefix {
|
||||
color: #e8ecff;
|
||||
}
|
||||
.header.header--brand .brand-sep {
|
||||
display: inline-block;
|
||||
margin: 0 0.45em;
|
||||
color: #9aa3bf;
|
||||
font-weight: 500;
|
||||
}
|
||||
.header.header--brand .brand-product {
|
||||
color: #e8ecff;
|
||||
}
|
||||
.header-live-quotes {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
gap: 3px;
|
||||
z-index: 2;
|
||||
pointer-events: none;
|
||||
}
|
||||
.header-live-quotes .hlq-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
font-size: 0.72rem;
|
||||
line-height: 1.2;
|
||||
color: #c5cce0;
|
||||
white-space: nowrap;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.header-live-quotes .hlq-icon {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
border-radius: 50%;
|
||||
flex: 0 0 auto;
|
||||
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.18);
|
||||
}
|
||||
.header-live-quotes .hlq-icon--eth {
|
||||
background: radial-gradient(circle at 35% 30%, #8ad0ff, #3c7cff 55%, #1a3a8a);
|
||||
}
|
||||
.header-live-quotes .hlq-icon--btc {
|
||||
background: radial-gradient(circle at 35% 30%, #ffd27a, #f0a028 55%, #a65a00);
|
||||
}
|
||||
.header-live-quotes .hlq-sym {
|
||||
color: #9aa3bf;
|
||||
min-width: 58px;
|
||||
}
|
||||
.header-live-quotes .hlq-last {
|
||||
color: #e8ecff;
|
||||
font-weight: 600;
|
||||
min-width: 64px;
|
||||
text-align: right;
|
||||
}
|
||||
.header-live-quotes .hlq-chg,
|
||||
.header-live-quotes .hlq-pct {
|
||||
min-width: 48px;
|
||||
text-align: right;
|
||||
color: #9aa3bf;
|
||||
}
|
||||
.header-live-quotes .hlq-row.is-up .hlq-chg,
|
||||
.header-live-quotes .hlq-row.is-up .hlq-pct {
|
||||
color: #3dd68c;
|
||||
}
|
||||
.header-live-quotes .hlq-row.is-down .hlq-chg,
|
||||
.header-live-quotes .hlq-row.is-down .hlq-pct {
|
||||
color: #ff8a8a;
|
||||
}
|
||||
html[data-theme="light"] .header.header--brand .brand-title,
|
||||
html[data-theme="light"] .header.header--brand .brand-prefix,
|
||||
html[data-theme="light"] .header.header--brand .brand-product {
|
||||
color: #142232;
|
||||
}
|
||||
html[data-theme="light"] .header.header--brand .brand-sep,
|
||||
html[data-theme="light"] .header-live-quotes .hlq-sym {
|
||||
color: #5a6f85;
|
||||
}
|
||||
html[data-theme="light"] .header-live-quotes .hlq-last {
|
||||
color: #142232;
|
||||
}
|
||||
@media (max-width: 720px) {
|
||||
.header.header--brand {
|
||||
padding: 0 8px;
|
||||
min-height: 0;
|
||||
align-items: flex-start;
|
||||
}
|
||||
.header.header--brand .brand-title {
|
||||
font-size: 1.05rem !important;
|
||||
text-align: left;
|
||||
}
|
||||
.header-live-quotes {
|
||||
position: static;
|
||||
transform: none;
|
||||
margin-top: 6px;
|
||||
align-items: flex-start;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
/* ── 统一顶栏面板:状态/筛选 + 统计条 ── */
|
||||
.instance-header-panel {
|
||||
margin-top: 18px;
|
||||
|
||||
@@ -79,6 +79,8 @@ def install_instance_theme_static(app) -> None:
|
||||
"account_ledger.js": "application/javascript; charset=utf-8",
|
||||
"options_expiry_countdown.js": "application/javascript; charset=utf-8",
|
||||
"options_panel.js": "application/javascript; charset=utf-8",
|
||||
"header_live_quotes.js": "application/javascript; charset=utf-8",
|
||||
"hedge_plan.js": "application/javascript; charset=utf-8",
|
||||
"order_entry_model.js": "application/javascript; charset=utf-8",
|
||||
"focus_chart_page.js": "application/javascript; charset=utf-8",
|
||||
"focus_chart_page.css": "text/css; charset=utf-8",
|
||||
|
||||
@@ -215,7 +215,7 @@ def pwa_app_name(exchange_key: str) -> str:
|
||||
ex = (exchange_key or "").strip().lower()
|
||||
return {
|
||||
"binance": "Binance 交易系统",
|
||||
"okx": "okx对冲",
|
||||
"okx": "加密货币 | OKX 对冲",
|
||||
"gate": "Gate 交易系统",
|
||||
}.get(ex, "交易系统")
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<link rel="stylesheet" href="/static/instance_theme_early.css?v=4">
|
||||
<link rel="stylesheet" href="/static/account_risk_badge.css?v=4">
|
||||
<link rel="stylesheet" href="/static/instance_page.css?v=13">
|
||||
<link rel="stylesheet" href="/static/instance_theme.css?v=121">
|
||||
<link rel="stylesheet" href="/static/instance_theme.css?v=122">
|
||||
<script src="/static/account_risk_badge.js?v=4"></script>
|
||||
<script src="/static/open_submit_gate.js?v=1"></script>
|
||||
<meta name="theme-color" content="#0b0d14">
|
||||
@@ -27,9 +27,7 @@
|
||||
data-price-refresh-ms="{{ price_refresh_seconds * 1000 }}"
|
||||
>
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<h1>okx对冲</h1>
|
||||
</div>
|
||||
{% include 'instance_brand_header.html' %}
|
||||
<nav class="top-nav embed-top-nav" aria-label="实例导航">
|
||||
{% if hedge_plan_nav_visible and display.show_nav_hedge_plan %}
|
||||
<a href="/hedge-plan" data-embed-tab="hedge_plan" class="{% if initial_tab == 'hedge_plan' %}active{% endif %}">{% if hedge_plan_show_options_options %}期期对冲{% else %}永期对冲{% endif %}</a>
|
||||
@@ -140,5 +138,6 @@ window.__INSTANCE_DISPLAY__ = {{ display | tojson }};
|
||||
<script src="/static/instance_live.js?v=6"></script>
|
||||
<script src="/static/instance_embed.js?v=32"></script>
|
||||
<script src="/static/instance_mobile_nav.js?v=3"></script>
|
||||
<script src="/static/header_live_quotes.js?v=1"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
<link rel="manifest" href="/static/icons/manifest.webmanifest">
|
||||
<title>{{ pwa_app_name }}</title>
|
||||
<link rel="stylesheet" href="/static/instance_page.css?v=13">
|
||||
<link rel="stylesheet" href="/static/instance_theme.css?v=121">
|
||||
<link rel="stylesheet" href="/static/instance_theme.css?v=122">
|
||||
|
||||
</head>
|
||||
<body
|
||||
@@ -150,9 +150,7 @@
|
||||
</div>
|
||||
{% endmacro %}
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<h1>okx对冲</h1>
|
||||
</div>
|
||||
{% include 'instance_brand_header.html' %}
|
||||
<div class="top-nav">
|
||||
{% if hedge_plan_nav_visible and display.show_nav_hedge_plan %}
|
||||
<a href="/hedge-plan" class="{% if page == 'hedge_plan' %}active{% endif %}">{% if hedge_plan_show_options_options %}期期对冲{% else %}永期对冲{% endif %}</a>
|
||||
@@ -2091,5 +2089,6 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
{% endif %}
|
||||
</script>
|
||||
<script src="/static/instance_settings_prefs.js?v=22"></script>
|
||||
<script src="/static/header_live_quotes.js?v=1"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,24 @@
|
||||
{# 顶栏品牌标题 + 右上角 ETH/BTC 报价(与期权仓同款) #}
|
||||
<div class="header header--brand">
|
||||
<h1 class="brand-title">
|
||||
<span class="brand-prefix">加密货币</span>
|
||||
<span class="brand-sep" aria-hidden="true">|</span>
|
||||
<span class="brand-product">OKX 对冲</span>
|
||||
</h1>
|
||||
<div class="header-live-quotes" id="header-live-quotes" aria-label="ETH/BTC 行情">
|
||||
<div class="hlq-row" data-base="ETH">
|
||||
<span class="hlq-icon hlq-icon--eth" aria-hidden="true"></span>
|
||||
<span class="hlq-sym">ETHUSDT</span>
|
||||
<span class="hlq-last" data-hlq="last">—</span>
|
||||
<span class="hlq-chg" data-hlq="chg">—</span>
|
||||
<span class="hlq-pct" data-hlq="pct">—</span>
|
||||
</div>
|
||||
<div class="hlq-row" data-base="BTC">
|
||||
<span class="hlq-icon hlq-icon--btc" aria-hidden="true"></span>
|
||||
<span class="hlq-sym">BTCUSDT</span>
|
||||
<span class="hlq-last" data-hlq="last">—</span>
|
||||
<span class="hlq-chg" data-hlq="chg">—</span>
|
||||
<span class="hlq-pct" data-hlq="pct">—</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user