行情区:K线全屏、可选技术指标与交易所价格精度对齐
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+52
-15
@@ -56,17 +56,17 @@ def chart_fetch_start_ms(timeframe: str, need: int, now_ms: int | None = None) -
|
||||
|
||||
|
||||
def price_tick_from_market(exchange, exchange_symbol: str) -> Optional[float]:
|
||||
"""最小价格变动单位(与交易所 tick / price_to_precision 一致)。"""
|
||||
try:
|
||||
markets = getattr(exchange, "markets", None) or {}
|
||||
m = markets.get(exchange_symbol) or {}
|
||||
prec = m.get("precision") or {}
|
||||
p = prec.get("price")
|
||||
if p is not None:
|
||||
p = float(p)
|
||||
if p > 0:
|
||||
return p
|
||||
info = m.get("info") or {}
|
||||
for key in ("tickSize", "price_increment", "order_price_round"):
|
||||
if not getattr(exchange, "markets", None):
|
||||
exchange.load_markets()
|
||||
market = exchange.market(exchange_symbol)
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
info = market.get("info") or {}
|
||||
if isinstance(info, dict):
|
||||
for key in ("tickSize", "tickSz", "price_increment", "order_price_round", "quote_increment"):
|
||||
if info.get(key) not in (None, ""):
|
||||
try:
|
||||
v = float(info[key])
|
||||
@@ -74,11 +74,52 @@ def price_tick_from_market(exchange, exchange_symbol: str) -> Optional[float]:
|
||||
return v
|
||||
except (TypeError, ValueError):
|
||||
pass
|
||||
|
||||
limits = market.get("limits") or {}
|
||||
price_limits = limits.get("price") or {}
|
||||
if price_limits.get("min") not in (None, ""):
|
||||
try:
|
||||
v = float(price_limits["min"])
|
||||
if v > 0:
|
||||
return v
|
||||
except (TypeError, ValueError):
|
||||
pass
|
||||
|
||||
try:
|
||||
sample = exchange.price_to_precision(exchange_symbol, 12345.678901234)
|
||||
s = str(sample).strip()
|
||||
if "." in s:
|
||||
frac = s.split(".", 1)[1]
|
||||
if frac:
|
||||
return 10 ** (-len(frac))
|
||||
return 1.0
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
prec = (market.get("precision") or {}).get("price")
|
||||
if prec is not None:
|
||||
try:
|
||||
p = float(prec)
|
||||
if p >= 1 and abs(p - round(p)) < 1e-9 and p <= 12:
|
||||
return 10 ** (-int(p))
|
||||
if 0 < p < 1:
|
||||
return p
|
||||
except (TypeError, ValueError):
|
||||
pass
|
||||
return None
|
||||
|
||||
|
||||
def _decimals_from_tick(tick: float) -> int:
|
||||
if tick >= 1:
|
||||
return 0
|
||||
s = f"{tick:.12f}".rstrip("0")
|
||||
if "." in s:
|
||||
frac = s.split(".", 1)[1]
|
||||
if frac:
|
||||
return min(12, len(frac))
|
||||
return max(0, min(12, int(round(-math.log10(tick)))))
|
||||
|
||||
|
||||
def format_price_by_tick(value: Any, tick: Optional[float]) -> str:
|
||||
if value in (None, ""):
|
||||
return "-"
|
||||
@@ -89,11 +130,7 @@ def format_price_by_tick(value: Any, tick: Optional[float]) -> str:
|
||||
if v == 0:
|
||||
return "0"
|
||||
if tick and tick > 0:
|
||||
decimals = max(0, min(12, int(round(-math.log10(tick))) if tick < 1 else 0))
|
||||
if tick >= 1:
|
||||
decimals = 0
|
||||
text = f"{v:.{decimals}f}"
|
||||
return text.rstrip("0").rstrip(".") if "." in text else text
|
||||
return f"{v:.{_decimals_from_tick(float(tick))}f}"
|
||||
av = abs(v)
|
||||
if av >= 10000:
|
||||
d = 2
|
||||
|
||||
Reference in New Issue
Block a user