a
This commit is contained in:
parent
71c9a7fb02
commit
ed9256899a
|
|
@ -640,13 +640,25 @@ class BinanceClient:
|
||||||
"""
|
"""
|
||||||
# 1. 先检查内存缓存
|
# 1. 先检查内存缓存
|
||||||
if symbol in self._symbol_info_cache:
|
if symbol in self._symbol_info_cache:
|
||||||
return self._symbol_info_cache[symbol]
|
cached_mem = self._symbol_info_cache[symbol]
|
||||||
|
# 兼容旧缓存:早期版本没有 tickSize/pricePrecision,容易触发 -4014/-1111
|
||||||
|
if isinstance(cached_mem, dict) and ("tickSize" not in cached_mem or "pricePrecision" not in cached_mem):
|
||||||
|
try:
|
||||||
|
self._symbol_info_cache.pop(symbol, None)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
return cached_mem
|
||||||
|
|
||||||
# 2. 从 Redis 缓存读取
|
# 2. 从 Redis 缓存读取
|
||||||
cache_key = f"symbol_info:{symbol}"
|
cache_key = f"symbol_info:{symbol}"
|
||||||
cached = await self.redis_cache.get(cache_key)
|
cached = await self.redis_cache.get(cache_key)
|
||||||
if cached:
|
if cached:
|
||||||
logger.debug(f"从Redis缓存获取 {symbol} 交易对信息")
|
logger.debug(f"从Redis缓存获取 {symbol} 交易对信息")
|
||||||
|
# 兼容旧缓存:早期版本没有 tickSize/pricePrecision,容易触发 -4014/-1111
|
||||||
|
if isinstance(cached, dict) and ("tickSize" not in cached or "pricePrecision" not in cached):
|
||||||
|
logger.info(f"{symbol} symbol_info 缓存缺少 tickSize/pricePrecision,自动刷新一次")
|
||||||
|
else:
|
||||||
# 同时更新内存缓存
|
# 同时更新内存缓存
|
||||||
self._symbol_info_cache[symbol] = cached
|
self._symbol_info_cache[symbol] = cached
|
||||||
return cached
|
return cached
|
||||||
|
|
@ -760,6 +772,92 @@ class BinanceClient:
|
||||||
|
|
||||||
return adjusted
|
return adjusted
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _format_decimal_str(d) -> str:
|
||||||
|
try:
|
||||||
|
s = format(d, 'f')
|
||||||
|
except Exception:
|
||||||
|
s = str(d)
|
||||||
|
if '.' in s:
|
||||||
|
s = s.rstrip('0').rstrip('.')
|
||||||
|
return s
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _format_quantity_str(quantity: float, symbol_info: Optional[Dict]) -> str:
|
||||||
|
"""
|
||||||
|
把数量格式化为币安可接受的字符串,避免 quantityPrecision=0 时发送 "197.0" 导致 -1111。
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
from decimal import Decimal, ROUND_DOWN
|
||||||
|
except Exception:
|
||||||
|
# fallback:尽量去掉浮点尾巴
|
||||||
|
q = float(quantity or 0)
|
||||||
|
if not symbol_info:
|
||||||
|
return str(q)
|
||||||
|
qp = int(symbol_info.get("quantityPrecision", 8) or 8)
|
||||||
|
if qp <= 0:
|
||||||
|
return str(int(q))
|
||||||
|
return str(round(q, qp))
|
||||||
|
|
||||||
|
qp = 8
|
||||||
|
try:
|
||||||
|
qp = int(symbol_info.get("quantityPrecision", 8) or 8) if symbol_info else 8
|
||||||
|
except Exception:
|
||||||
|
qp = 8
|
||||||
|
|
||||||
|
qd = Decimal(str(quantity))
|
||||||
|
if qp <= 0:
|
||||||
|
q2 = qd.to_integral_value(rounding=ROUND_DOWN)
|
||||||
|
return BinanceClient._format_decimal_str(q2)
|
||||||
|
q2 = qd.quantize(Decimal(f"1e-{qp}"), rounding=ROUND_DOWN)
|
||||||
|
return BinanceClient._format_decimal_str(q2)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _format_limit_price_str(price: float, symbol_info: Optional[Dict], side: str) -> str:
|
||||||
|
"""
|
||||||
|
把 LIMIT 价格格式化为币安可接受的字符串(tickSize/pricePrecision 对齐),避免:
|
||||||
|
-4014 Price not increased by tick size
|
||||||
|
-1111 Precision is over the maximum defined for this asset
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
from decimal import Decimal, ROUND_DOWN, ROUND_UP
|
||||||
|
except Exception:
|
||||||
|
return str(round(float(price), 8))
|
||||||
|
|
||||||
|
tick = 0.0
|
||||||
|
pp = 8
|
||||||
|
try:
|
||||||
|
tick = float(symbol_info.get("tickSize", 0) or 0) if symbol_info else 0.0
|
||||||
|
except Exception:
|
||||||
|
tick = 0.0
|
||||||
|
try:
|
||||||
|
pp = int(symbol_info.get("pricePrecision", 8) or 8) if symbol_info else 8
|
||||||
|
except Exception:
|
||||||
|
pp = 8
|
||||||
|
|
||||||
|
p = Decimal(str(price))
|
||||||
|
rounding = ROUND_DOWN if (side or "").upper() == "BUY" else ROUND_UP
|
||||||
|
|
||||||
|
# 1) tickSize 优先(最严格)
|
||||||
|
try:
|
||||||
|
t = Decimal(str(tick))
|
||||||
|
if t > 0:
|
||||||
|
q = p / t
|
||||||
|
q2 = q.to_integral_value(rounding=rounding)
|
||||||
|
p2 = q2 * t
|
||||||
|
return BinanceClient._format_decimal_str(p2)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# 2) 没有 tickSize 时,用 pricePrecision 兜底
|
||||||
|
try:
|
||||||
|
if pp <= 0:
|
||||||
|
return BinanceClient._format_decimal_str(p.to_integral_value(rounding=rounding))
|
||||||
|
p2 = p.quantize(Decimal(f"1e-{pp}"), rounding=rounding)
|
||||||
|
return BinanceClient._format_decimal_str(p2)
|
||||||
|
except Exception:
|
||||||
|
return BinanceClient._format_decimal_str(p)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _adjust_price_to_tick(price: float, tick_size: float, side: str) -> float:
|
def _adjust_price_to_tick(price: float, tick_size: float, side: str) -> float:
|
||||||
"""
|
"""
|
||||||
|
|
@ -1006,7 +1104,8 @@ class BinanceClient:
|
||||||
'symbol': symbol,
|
'symbol': symbol,
|
||||||
'side': side,
|
'side': side,
|
||||||
'type': order_type,
|
'type': order_type,
|
||||||
'quantity': adjusted_quantity
|
# 关键:quantityPrecision=0 时必须是 "197" 而不是 "197.0",否则会触发 -1111
|
||||||
|
'quantity': self._format_quantity_str(adjusted_quantity, symbol_info)
|
||||||
}
|
}
|
||||||
|
|
||||||
# 处理持仓模式(解决 -4061:position side 与账户设置不匹配)
|
# 处理持仓模式(解决 -4061:position side 与账户设置不匹配)
|
||||||
|
|
@ -1049,13 +1148,8 @@ class BinanceClient:
|
||||||
raise ValueError("限价单必须指定价格")
|
raise ValueError("限价单必须指定价格")
|
||||||
params = dict(params)
|
params = dict(params)
|
||||||
params['timeInForce'] = 'GTC'
|
params['timeInForce'] = 'GTC'
|
||||||
# LIMIT 价格按 tickSize 修正(避免 -4014 / -1111)
|
# LIMIT 价格按 tickSize/pricePrecision 修正(避免 -4014 / -1111)
|
||||||
tick = float(symbol_info.get("tickSize", 0) or 0) if symbol_info else 0.0
|
params['price'] = self._format_limit_price_str(float(price), symbol_info, side)
|
||||||
p2 = self._adjust_price_to_tick(float(price), tick, side)
|
|
||||||
# 用字符串提交,避免浮点造成的精度问题
|
|
||||||
params['price'] = str(p2)
|
|
||||||
# quantity 也转成字符串提交(同样避免浮点精度)
|
|
||||||
params['quantity'] = str(params.get('quantity'))
|
|
||||||
return await self.client.futures_create_order(**params)
|
return await self.client.futures_create_order(**params)
|
||||||
|
|
||||||
# 提交订单;若遇到:
|
# 提交订单;若遇到:
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user