This commit is contained in:
aaron 2026-02-25 20:47:20 +08:00
parent 75ad00770d
commit 64c5ce606e
4 changed files with 188 additions and 18 deletions

View File

@ -84,7 +84,7 @@ class CryptoAgent:
})
logger.info(f"加密货币智能体初始化完成LLM 驱动),监控交易对: {self.symbols}")
logger.info(f"模拟交易: 始终启用")
logger.info(f"📊 交易: 始终启用")
if self.real_trading:
auto_status = "启用" if self.real_trading.get_auto_trading_status() else "禁用"
@ -977,7 +977,7 @@ class CryptoAgent:
decision_text = decision_map.get(decision_type, decision_type)
# 账户类型标识
account_type = "📊 模拟" if is_paper else "💰 实盘"
account_type = "📊" if is_paper else "💰"
# 方向图标
if 'long' in action.lower() or 'buy' in action.lower():
@ -1484,7 +1484,7 @@ class CryptoAgent:
"""发送有信号但未执行交易的通知"""
try:
symbol = market_signal.get('symbol')
account_type = "📊 模拟" if is_paper else "💰 实盘"
account_type = "📊" if is_paper else "💰"
# 获取最佳信号
best_signal = self._get_best_signal_from_market(market_signal)

View File

@ -54,18 +54,32 @@ async def price_monitor_loop():
side_icon = "🟢" if result.get('side') == 'long' else "🔴"
grade = result.get('signal_grade', 'N/A')
title = f"✅ 挂单成交 - {result.get('symbol')}"
symbol = result.get('symbol', '')
entry_price = result.get('entry_price', 0)
filled_price = result.get('filled_price', 0)
stop_loss = result.get('stop_loss', 0)
take_profit = result.get('take_profit', 0)
# 根据交易对精度格式化价格
try:
from app.services.bitget_service import bitget_service
precision = bitget_service.get_precision(symbol)
price_fmt = f"{{:,.{precision['pricePrecision']}f}}"
except:
price_fmt = "{:,.2f}"
title = f"✅ 挂单成交 - {symbol}"
content_parts = [
f"{side_icon} **方向**: {side_text}",
f"⭐ **信号等级**: {grade}",
f"💰 **挂单价**: ${result.get('entry_price', 0):,.2f}",
f"🎯 **成交价**: ${result.get('filled_price', 0):,.2f}",
f"💰 **挂单价**: ${price_fmt.format(entry_price)}",
f"🎯 **成交价**: ${price_fmt.format(filled_price)}",
f"💵 **仓位**: ${result.get('quantity', 0):,.0f}",
]
if result.get('stop_loss'):
content_parts.append(f"🛑 **止损**: ${result.get('stop_loss', 0):,.2f}")
if result.get('take_profit'):
content_parts.append(f"🎯 **止盈**: ${result.get('take_profit', 0):,.2f}")
if stop_loss:
content_parts.append(f"🛑 **止损**: ${price_fmt.format(stop_loss)}")
if take_profit:
content_parts.append(f"🎯 **止盈**: ${price_fmt.format(take_profit)}")
content = "\n".join(content_parts)
@ -152,6 +166,15 @@ async def price_monitor_loop():
side_icon = '🟢' if result.get('side') == 'long' else '🔴'
pnl = result.get('current_pnl_percent', 0)
symbol_display = result.get('symbol', '')
new_stop_loss = result.get('new_stop_loss', 0)
# 根据交易对精度格式化价格
try:
from app.services.bitget_service import bitget_service
precision = bitget_service.get_precision(symbol_display)
price_fmt = f"{{:,.{precision['pricePrecision']}f}}"
except:
price_fmt = "{:,.2f}"
if move_type == 'trailing_first':
title = f"📈 移动止损已激活 - {symbol_display}"
@ -159,7 +182,7 @@ async def price_monitor_loop():
f"{side_icon} **方向**: {side_text}",
f"",
f"📈 **当前盈利**: {pnl:+.2f}%",
f"🛑 **新止损价**: ${result.get('new_stop_loss', 0):,.2f}",
f"🛑 **新止损价**: ${price_fmt.format(new_stop_loss)}",
f"",
f"💰 锁定利润,让利润奔跑"
]
@ -170,7 +193,7 @@ async def price_monitor_loop():
f"{side_icon} **方向**: {side_text}",
f"",
f"📈 **当前盈利**: {pnl:+.2f}%",
f"🛑 **新止损价**: ${result.get('new_stop_loss', 0):,.2f}",
f"🛑 **新止损价**: ${price_fmt.format(new_stop_loss)}",
f"",
f"🎯 继续锁定更多利润"
]
@ -181,7 +204,7 @@ async def price_monitor_loop():
f"{side_icon} **方向**: {side_text}",
f"",
f"📈 **当前盈利**: {pnl:+.2f}%",
f"🛑 **新止损价**: ${result.get('new_stop_loss', 0):,.2f}",
f"🛑 **新止损价**: ${price_fmt.format(new_stop_loss)}",
f"",
f"💰 锁定利润,让利润奔跑"
]
@ -205,19 +228,33 @@ async def price_monitor_loop():
side_icon = '🟢' if result.get('side') == 'long' else '🔴'
grade = result.get('signal_grade', 'N/A')
title = f"✅ 挂单成交 - {result.get('symbol')}"
symbol = result.get('symbol', '')
entry_price = result.get('entry_price', 0)
filled_price = result.get('filled_price', 0)
stop_loss = result.get('stop_loss', 0)
take_profit = result.get('take_profit', 0)
# 根据交易对精度格式化价格
try:
from app.services.bitget_service import bitget_service
precision = bitget_service.get_precision(symbol)
price_fmt = f"{{:,.{precision['pricePrecision']}f}}"
except:
price_fmt = "{:,.2f}"
title = f"✅ 挂单成交 - {symbol}"
content_parts = [
f"{side_icon} **方向**: {side_text}",
f"",
f"⭐ **信号等级**: {grade}",
f"",
f"💰 **挂单价**: ${result.get('entry_price', 0):,.2f}",
f"🎯 **成交价**: ${result.get('filled_price', 0):,.2f}",
f"💰 **挂单价**: ${price_fmt.format(entry_price)}",
f"🎯 **成交价**: ${price_fmt.format(filled_price)}",
f"📊 **持仓价值**: ${result.get('quantity', 0):,.0f}",
f"",
f"🛑 **止损价**: ${result.get('stop_loss', 0):,.2f}",
f"🎯 **止盈价**: ${result.get('take_profit', 0):,.2f}"
f"🛑 **止损价**: ${price_fmt.format(stop_loss)}",
f"🎯 **止盈价**: ${price_fmt.format(take_profit)}"
]
content = "\n".join(content_parts)

View File

@ -370,6 +370,119 @@ class BitgetService:
logger.error(f"获取 {symbol} ticker 失败: {e}")
return None
def get_symbol_info(self, symbol: str, category: str = None) -> Optional[Dict[str, Any]]:
"""
获取交易对信息包含精度配置
Args:
symbol: 交易对
category: 产品类型默认 USDT-FUTURES
Returns:
交易对信息包含 pricePrecision quantityPrecision
"""
try:
if category is None:
category = self.CATEGORY_USDT_FUTURES
url = f"{self._base_url}/api/v3/market/symbols"
params = {
'category': category,
'symbol': symbol
}
response = self._session.get(url, params=params, timeout=10)
response.raise_for_status()
result = response.json()
if result.get('code') != '00000':
logger.error(f"Bitget API 错误: {result.get('msg')}")
return None
data = result.get('data', [])
if not data:
return None
symbol_data = data[0]
# 返回精度信息
return {
'symbol': symbol_data.get('symbol'),
'status': symbol_data.get('status'),
'pricePrecision': int(symbol_data.get('pricePrecision', 2)),
'quantityPrecision': int(symbol_data.get('quantityPrecision', 2)),
'minTradeAmount': float(symbol_data.get('minTradeAmount', 0)),
'maxTradeAmount': float(symbol_data.get('maxTradeAmount', 0)),
'takerFeeRate': float(symbol_data.get('takerFeeRate', 0.001)),
'makerFeeRate': float(symbol_data.get('makerFeeRate', 0.001)),
}
except Exception as e:
logger.error(f"获取 {symbol} 交易对信息失败: {e}")
return None
# 缓存交易对精度信息
_symbol_precision_cache: Dict[str, Dict[str, int]] = {}
def get_precision(self, symbol: str, category: str = None) -> Dict[str, int]:
"""
获取交易对价格和数量精度带缓存
Args:
symbol: 交易对
category: 产品类型默认 USDT-FUTURES
Returns:
{'pricePrecision': 2, 'quantityPrecision': 2}
"""
# 检查缓存
if symbol in self._symbol_precision_cache:
return self._symbol_precision_cache[symbol]
# 获取交易对信息
info = self.get_symbol_info(symbol, category)
if info:
precision = {
'pricePrecision': info['pricePrecision'],
'quantityPrecision': info['quantityPrecision']
}
# 缓存结果
self._symbol_precision_cache[symbol] = precision
return precision
# 默认精度
return {'pricePrecision': 2, 'quantityPrecision': 2}
def round_price(self, symbol: str, price: float, category: str = None) -> float:
"""
根据交易对精度四舍五入价格
Args:
symbol: 交易对
price: 原始价格
category: 产品类型
Returns:
四舍五入后的价格
"""
precision = self.get_precision(symbol, category)
return round(price, precision['pricePrecision'])
def round_quantity(self, symbol: str, quantity: float, category: str = None) -> float:
"""
根据交易对精度四舍五入数量
Args:
symbol: 交易对
quantity: 原始数量
category: 产品类型
Returns:
四舍五入后的数量
"""
precision = self.get_precision(symbol, category)
return round(quantity, precision['quantityPrecision'])
def get_funding_rate(self, symbol: str) -> Optional[Dict[str, Any]]:
"""
获取资金费率包含标记价格和指数价格

View File

@ -116,6 +116,26 @@ class PaperTradingService:
finally:
db.close()
def _apply_price_precision(self, symbol: str, price: float) -> float:
"""
应用交易对价格精度
Args:
symbol: 交易对
price: 原始价格
Returns:
四舍五入后的价格
"""
if price is None or price == 0:
return price
try:
from app.services.bitget_service import bitget_service
return bitget_service.round_price(symbol, price)
except Exception as e:
logger.debug(f"价格精度调整失败 {symbol}: {e},使用原始价格")
return price
def create_order_from_signal(self, signal: Dict[str, Any], current_price: float = None) -> Dict[str, Any]:
"""
从交易信号创建模拟订单