fix(hub): align Binance chart ticks and improve monitor mark price
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -15,6 +15,8 @@ from hub_ohlcv_lib import (
|
||||
format_price_by_tick,
|
||||
last_closed_bar_open_ms,
|
||||
normalize_chart_timeframe,
|
||||
normalize_price_tick,
|
||||
round_ohlcv_bars_to_tick,
|
||||
window_start_ms,
|
||||
)
|
||||
|
||||
@@ -73,6 +75,72 @@ def init_db(db_path: Path | None = None) -> None:
|
||||
ON ohlcv_bars (exchange_key, symbol, timeframe, open_time_ms)
|
||||
"""
|
||||
)
|
||||
conn.execute(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS ohlcv_symbol_meta (
|
||||
exchange_key TEXT NOT NULL,
|
||||
symbol TEXT NOT NULL,
|
||||
price_tick REAL,
|
||||
updated_at INTEGER NOT NULL,
|
||||
PRIMARY KEY (exchange_key, symbol)
|
||||
)
|
||||
"""
|
||||
)
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
def save_symbol_price_tick(
|
||||
exchange_key: str,
|
||||
symbol: str,
|
||||
price_tick: float | None,
|
||||
db_path: Path | None = None,
|
||||
) -> None:
|
||||
tick = price_tick
|
||||
if tick is None:
|
||||
return
|
||||
try:
|
||||
t = float(tick)
|
||||
except (TypeError, ValueError):
|
||||
return
|
||||
if t <= 0:
|
||||
return
|
||||
ex_k = (exchange_key or "").strip().lower()
|
||||
sym = (symbol or "").strip().upper()
|
||||
conn = _connect(db_path)
|
||||
try:
|
||||
conn.execute(
|
||||
"""
|
||||
INSERT INTO ohlcv_symbol_meta (exchange_key, symbol, price_tick, updated_at)
|
||||
VALUES (?,?,?,?)
|
||||
ON CONFLICT(exchange_key, symbol) DO UPDATE SET
|
||||
price_tick=excluded.price_tick,
|
||||
updated_at=excluded.updated_at
|
||||
""",
|
||||
(ex_k, sym, t, int(time.time())),
|
||||
)
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
def load_symbol_price_tick(
|
||||
exchange_key: str,
|
||||
symbol: str,
|
||||
db_path: Path | None = None,
|
||||
) -> float | None:
|
||||
ex_k = (exchange_key or "").strip().lower()
|
||||
sym = (symbol or "").strip().upper()
|
||||
conn = _connect(db_path)
|
||||
try:
|
||||
row = conn.execute(
|
||||
"SELECT price_tick FROM ohlcv_symbol_meta WHERE exchange_key=? AND symbol=?",
|
||||
(ex_k, sym),
|
||||
).fetchone()
|
||||
if not row or row["price_tick"] is None:
|
||||
return None
|
||||
return float(row["price_tick"])
|
||||
except (TypeError, ValueError):
|
||||
return None
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
@@ -273,6 +341,8 @@ def resolve_chart_bars(
|
||||
if remote.get("ok") and remote.get("bars"):
|
||||
fetched = upsert_bars(ex_k, sym, tf, remote["bars"], db_path)
|
||||
price_tick = remote.get("price_tick")
|
||||
if price_tick is not None:
|
||||
save_symbol_price_tick(ex_k, sym, price_tick, db_path)
|
||||
db_rows = load_bars_range(ex_k, sym, tf, fetch_start_ms, now_ms, db_path)
|
||||
else:
|
||||
remote_err = remote.get("msg") or remote.get("error") or "实例拉取 K 线失败"
|
||||
@@ -282,6 +352,8 @@ def resolve_chart_bars(
|
||||
if len(db_rows) > need:
|
||||
db_rows = db_rows[-need:]
|
||||
|
||||
if price_tick is None:
|
||||
price_tick = load_symbol_price_tick(ex_k, sym, db_path)
|
||||
if price_tick is None:
|
||||
try:
|
||||
tick_probe = remote_fetch(
|
||||
@@ -292,9 +364,15 @@ def resolve_chart_bars(
|
||||
)
|
||||
if tick_probe.get("ok"):
|
||||
price_tick = tick_probe.get("price_tick")
|
||||
if price_tick is not None:
|
||||
save_symbol_price_tick(ex_k, sym, price_tick, db_path)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
price_tick = normalize_price_tick(price_tick)
|
||||
if db_rows and price_tick is not None:
|
||||
round_ohlcv_bars_to_tick(db_rows, price_tick)
|
||||
|
||||
candles = _to_chart_candles(db_rows)
|
||||
if not candles:
|
||||
return {"ok": False, "msg": remote_err or "无 K 线数据", "purged": purged}
|
||||
|
||||
Reference in New Issue
Block a user