feat(hub): exchange price precision, entry price, and trend DCA display
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -22,9 +22,16 @@ from __future__ import annotations
|
||||
|
||||
import math
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
_REPO_ROOT = Path(__file__).resolve().parents[1]
|
||||
if str(_REPO_ROOT) not in sys.path:
|
||||
sys.path.insert(0, str(_REPO_ROOT))
|
||||
from hub_ohlcv_lib import format_price_by_tick, price_tick_from_market
|
||||
|
||||
import ccxt
|
||||
from fastapi import FastAPI, Header, HTTPException, Request
|
||||
from fastapi.responses import JSONResponse
|
||||
@@ -363,6 +370,44 @@ def _finite_or_none(x: Any) -> float | None:
|
||||
return None
|
||||
|
||||
|
||||
def _position_price_fmt(ex: Any, symbol: str, price: float | None) -> tuple[float | None, str | None, float | None]:
|
||||
"""返回 (原价, 交易所精度字符串, price_tick)。"""
|
||||
if price is None or price <= 0 or not symbol:
|
||||
return None, None, None
|
||||
tick: float | None = None
|
||||
try:
|
||||
ex.load_markets()
|
||||
unified = ex.market(symbol)["symbol"]
|
||||
tick = price_tick_from_market(ex, unified)
|
||||
px_str = str(ex.price_to_precision(unified, price))
|
||||
return _finite_or_none(float(px_str)), px_str, tick
|
||||
except Exception:
|
||||
return price, format_price_by_tick(price, tick), tick
|
||||
|
||||
|
||||
def _position_entry_price(p: dict[str, Any]) -> float | None:
|
||||
"""四所 ccxt 持仓统一解析开仓均价(Binance/OKX/Gate 字段名不一致)。"""
|
||||
info = p.get("info") or {}
|
||||
if not isinstance(info, dict):
|
||||
info = {}
|
||||
for key in (
|
||||
p.get("entryPrice"),
|
||||
p.get("entry_price"),
|
||||
p.get("average"),
|
||||
info.get("entryPrice"),
|
||||
info.get("entry_price"),
|
||||
info.get("avgPx"),
|
||||
info.get("avgEntryPrice"),
|
||||
info.get("avg_entry_price"),
|
||||
info.get("avgPrice"),
|
||||
info.get("openAvgPx"),
|
||||
):
|
||||
px = _finite_or_none(key)
|
||||
if px is not None and px > 0:
|
||||
return px
|
||||
return None
|
||||
|
||||
|
||||
def _extract_usdt_total(balance: dict[str, Any]) -> float | None:
|
||||
"""从 ccxt balance 结构中尽量取出 USDT 总额(与 crypto_monitor_binance 一致)。"""
|
||||
usdt_info = balance.get("USDT") or {}
|
||||
@@ -536,11 +581,8 @@ def _status_inner(x_control_token: str | None) -> Any:
|
||||
notional_f = float(notional) if notional is not None else None
|
||||
except (TypeError, ValueError):
|
||||
notional_f = None
|
||||
entry = p.get("entryPrice")
|
||||
try:
|
||||
entry_f = float(entry) if entry is not None else None
|
||||
except (TypeError, ValueError):
|
||||
entry_f = None
|
||||
entry_f = _position_entry_price(p)
|
||||
_, entry_fmt, price_tick = _position_price_fmt(ex, sym, entry_f)
|
||||
positions_out.append(
|
||||
{
|
||||
"symbol": sym,
|
||||
@@ -549,7 +591,9 @@ def _status_inner(x_control_token: str | None) -> Any:
|
||||
"contracts_signed": c,
|
||||
"notional_usdt": _finite_or_none(notional_f) if notional_f is not None else None,
|
||||
"unrealized_pnl": _finite_or_none(upnl_f),
|
||||
"entry_price": _finite_or_none(entry_f) if entry_f is not None else None,
|
||||
"entry_price": entry_f,
|
||||
"entry_price_fmt": entry_fmt,
|
||||
"price_tick": _finite_or_none(price_tick) if price_tick is not None else None,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user