Align header title and ETH/BTC quotes with crypto_okx.

Match fullwidth title separator, page spacing, and move live tickers into the nav right column using the same spot-ticker UI/API.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-10-14 19:27:33 +08:00
parent 22de1fd40c
commit 1d71414c56
13 changed files with 511 additions and 286 deletions
+56 -31
View File
@@ -7134,34 +7134,59 @@ def options_main_page():
return render_main_page("options")
@app.route("/api/header-quotes")
@app.route("/api/nav_spot_tickers")
@login_required
def api_header_quotes():
"""顶栏 ETH/BTC 报价(公共 ticker last + 24h 涨跌)."""
from lib.exchange.okx_public_http_lib import fetch_swap_ticker
def api_nav_spot_tickers():
"""BTC/ETH 最新价,相对北京时间 08:00(OKX sodUtc0)涨跌 — 与 crypto_okx 同款."""
try:
from lib.market.nav_spot_tickers_lib import build_nav_spot_tickers
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})
tickers = build_nav_spot_tickers(
exchange,
reset_hour=TRADING_DAY_RESET_HOUR,
now_fn=lambda: datetime.now(APP_TZ),
)
# 公共 HTTP 回退:ccxt/代理异常时仍能出顶栏价
if not any((t or {}).get("last") is not None for t in tickers):
from lib.exchange.okx_public_http_lib import fetch_swap_ticker
from lib.market.nav_spot_tickers_lib import session_day_key
day_key = session_day_key(datetime.now(APP_TZ), reset_hour=TRADING_DAY_RESET_HOUR)
rebuilt = []
for base in ("ETH", "BTC"):
try:
row = fetch_swap_ticker(base)
last = float(row.get("last") or row.get("markPx") or 0) or None
open_px = float(row.get("sodUtc0") or row.get("open24h") or 0) or None
change = (last - open_px) if last is not None and open_px else None
pct = (change / open_px * 100.0) if change is not None and open_px else None
rebuilt.append(
{
"base": base,
"inst_id": f"{base}-USDT-SWAP",
"symbol": f"{base}USDT",
"last": round(last, 8) if last is not None else None,
"open": round(open_px, 8) if open_px is not None else None,
"change": round(change, 8) if change is not None else None,
"change_pct": round(pct, 4) if pct is not None else None,
"session_day": day_key,
"anchor": "BJ 08:00",
"anchor_field": "sodUtc0",
}
)
except Exception:
rebuilt.append({"base": base, "symbol": f"{base}USDT", "last": None})
if any((t or {}).get("last") is not None for t in rebuilt):
tickers = rebuilt
return jsonify(
{
"ok": True,
"updated_at": app_now_str(),
"tickers": tickers,
}
)
except Exception as e:
return jsonify({"ok": False, "msg": str(e) or "行情读取失败", "tickers": []}), 502
@app.route("/api/account_snapshot")
@@ -9438,7 +9463,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")
_NAV_SPOT_TICKERS_JS = os.path.join(_REPO_STATIC_DIR, "nav_spot_tickers.js")
@app.route("/static/ai_review_render.js")
@@ -9448,11 +9473,11 @@ 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):
@app.route("/static/nav_spot_tickers.js")
def static_nav_spot_tickers_js():
if not os.path.isfile(_NAV_SPOT_TICKERS_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")
return send_file(_NAV_SPOT_TICKERS_JS, mimetype="application/javascript; charset=utf-8")
@app.route("/static/form_submit_guard.js")