feat: Add Hyperliquid trading integration with ClawFi

- Add hyperliquid_trading_service.py with position management and TP/SL
- Implement dual-track trading (paper trading + Hyperliquid)
- Add position size calculation based on available margin
- Support net position mode (Hyperliquid) vs order mode (paper)
- Add risk controls: 10% circuit breaker, 10x max leverage
- Add test script for Hyperliquid SDK validation
This commit is contained in:
aaron 2026-03-22 11:42:25 +08:00
parent ed6fcb50cd
commit a22dfe459c
6 changed files with 1347 additions and 86 deletions

View File

@ -0,0 +1,100 @@
# Hyperliquid 集成代码 Review
## 核心差异总结
### 1. 仓位模式
- **Hyperliquid**: 净持仓模式Position Netting- 同币种订单自动合并
- **模拟盘**: 订单模式Order-based- 每个订单独立
### 2. 止盈止损
- **Hyperliquid**: 独立订单开仓后单独设置reduce_only=True
- **模拟盘**: 订单属性(创建时设置)
### 3. 需要修正的问题
#### 问题 1: `_get_hyperliquid_trading_state()` 需要查询止盈止损订单
```python
def _get_hyperliquid_trading_state(self) -> tuple:
# 需要额外查询挂单,找出 reduce_only 的止盈止损订单
# 并关联到对应的持仓
```
#### 问题 2: `_execute_hyperliquid_trade()` 需要设置止盈止损
```python
async def _execute_hyperliquid_trade(...):
# 1. 开仓
result = self.hyperliquid.place_market_order(...)
# 2. 立即设置止盈止损(新增)
if result.get('success'):
await self._set_hyperliquid_tp_sl(decision)
```
#### 问题 3: 加仓需要重新计算止盈止损
```python
# 加仓时:
# 1. 取消旧的止盈止损订单
# 2. 执行加仓
# 3. 根据新的平均入场价重新设置止盈止损
```
#### 问题 4: 平仓需要先取消止盈止损订单
```python
async def _execute_hyperliquid_close(...):
# 1. 取消该币种的所有止盈止损订单(新增)
# 2. 市价平仓
```
#### 问题 5: 不支持同时多空
```python
# Hyperliquid 同一币种只能有一个方向的净持仓
# 如果决策是反向开仓,会自动平掉现有持仓并反向
# 需要在决策器中考虑这个限制
```
## 修正方案
### 新增方法到 `hyperliquid_trading_service.py`
```python
def get_open_orders(self, symbol: Optional[str] = None) -> List[Dict[str, Any]]:
"""获取挂单(包括止盈止损订单)"""
def get_tp_sl_orders(self, symbol: str) -> Dict[str, Optional[float]]:
"""获取指定币种的止盈止损价格"""
# 返回 {'take_profit': price, 'stop_loss': price}
def set_tp_sl(self, symbol: str, is_long: bool, size: float,
tp_price: Optional[float], sl_price: Optional[float]):
"""设置止盈止损"""
def cancel_tp_sl_orders(self, symbol: str):
"""取消指定币种的所有止盈止损订单"""
```
### 修改 `crypto_agent.py`
```python
async def _execute_hyperliquid_trade(...):
# 1. 检查是否有反向持仓Hyperliquid 会自动平仓)
# 2. 执行开仓
# 3. 设置止盈止损
# 4. 如果是加仓,需要重新计算止盈止损
```
## 决策器需要考虑的差异
1. **加仓决策**: Hyperliquid 会合并仓位,入场价变成加权平均
2. **反向开仓**: Hyperliquid 会自动平掉现有持仓
3. **止盈止损调整**: 加仓后需要重新设置止盈止损
## 建议
1. **先实现基础功能**: 开仓 + 止盈止损 + 平仓
2. **再实现高级功能**: 加仓、减仓、调整止盈止损
3. **测试验证**: 在测试网充分测试后再启用实盘
4. **风控优先**: 确保 10% 熔断和杠杆限制正确工作
## Sources
- [Bybit Copy Trading Settlement Guide](https://www.bybit.nl/en/help-center/article/A-Comprehensive-Guide-to-Copy-Trading-Settlement)
- [Hyperliquid Fees and Margin Guide](https://publish0x.com/toxi-trading-bot-short-review/how-to-trade-perpetuals-on-hyperliquid-fees-margin-liquidati-xrplyqn)

View File

@ -207,6 +207,19 @@ class Settings(BaseSettings):
pullback_select_time: str = "09:00" # 选股时间24小时制
pullback_sectors_to_check: int = 5 # 检查板块数量
# ========== Hyperliquid 交易配置ClawFi 集成)==========
# Hyperliquid 交易开关
hyperliquid_trading_enabled: bool = False # Hyperliquid 实盘交易开关(默认关闭)
# Hyperliquid 环境变量(由 clawfi-hyperliquid-skill 安装脚本注入)
# CLAWFI_WALLET_ADDRESS - 主钱包地址
# CLAWFI_PRIVATE_KEY - Agent 专用私钥
# Hyperliquid 风险控制
hyperliquid_max_total_leverage: float = 10.0 # 总杠杆上限≤10xClawFi 强制规则)
hyperliquid_circuit_breaker_drawdown: float = 0.10 # 10% 熔断阈值ClawFi 强制规则)
hyperliquid_max_single_position: float = 1000 # 单笔最大持仓金额 (USD)
class Config:
env_file = find_env_file()
case_sensitive = False

View File

@ -60,6 +60,15 @@ class CryptoAgent:
max_total_leverage=self.paper_trading.max_total_leverage
)
# Hyperliquid 实盘服务(可选)
from app.services.hyperliquid_trading_service import get_hyperliquid_service
self.hyperliquid = get_hyperliquid_service()
if self.hyperliquid:
logger.info(f"🔥 Hyperliquid 实盘交易: 已启用")
else:
logger.info(f"📊 Hyperliquid 实盘交易: 未启用(仅模拟盘)")
# 状态管理
self.last_signals: Dict[str, Dict[str, Any]] = {}
self.signal_cooldown: Dict[str, datetime] = {}
@ -81,6 +90,7 @@ class CryptoAgent:
monitor.update_config("crypto_agent", {
"symbols": self.symbols,
"auto_trading_enabled": True, # 模拟交易始终启用
"hyperliquid_enabled": self.hyperliquid is not None,
"analysis_interval": "每5分钟整点"
})
@ -595,34 +605,46 @@ class CryptoAgent:
await self._send_market_signal_notification(market_signal, current_price)
# ============================================================
# 第二阶段:交易决策(信号 + 仓位 + 账户状态
# 模拟交易和实盘交易分别进行独立决策
# 第二阶段:交易决策(双轨独立
# 模拟交易和 Hyperliquid 实盘分别进行独立决策
# ============================================================
logger.info(f"\n🤖 【第二阶段:交易决策】")
# 获取配置
paper_trading_enabled = self.settings.paper_trading_enabled
paper_decision = None
hyperliquid_decision = None
# 交易决策
if paper_trading_enabled:
logger.info(f"\n📊 【交易决策】")
positions, account, pending_orders = self._get_trading_state()
# 过滤只传递当前symbol的挂单给决策器避免LLM搞混
pending_orders_for_symbol = [o for o in pending_orders if o.get('symbol') == symbol]
decision = await self.decision_maker.make_decision(
market_signal, positions, account, current_price, pending_orders_for_symbol
# 2.1 模拟盘决策
if self.settings.paper_trading_enabled:
logger.info(f"\n📊 【模拟盘决策】")
paper_positions, paper_account, paper_pending = self._get_paper_trading_state()
paper_pending_for_symbol = [o for o in paper_pending if o.get('symbol') == symbol]
paper_decision = await self.decision_maker.make_decision(
market_signal, paper_positions, paper_account, current_price, paper_pending_for_symbol
)
self._log_trading_decision(decision)
# 发送交易决策通知
await self._send_trading_decision_notification(decision, market_signal, current_price)
logger.info(f" 模拟盘决策: {paper_decision.get('action')} - {paper_decision.get('reasoning', '')}")
await self._send_trading_decision_notification(paper_decision, market_signal, current_price, prefix="[模拟盘]")
else:
logger.info(f"⏸️ 交易未启用")
decision = None
logger.info(f"⏸️ 模拟盘交易未启用")
# 2.2 Hyperliquid 实盘决策(独立)
if self.hyperliquid:
logger.info(f"\n🔥 【Hyperliquid 决策】")
hl_positions, hl_account, hl_pending = self._get_hyperliquid_trading_state()
hl_pending_for_symbol = [o for o in hl_pending if o.get('symbol') == symbol]
hyperliquid_decision = await self.decision_maker.make_decision(
market_signal, hl_positions, hl_account, current_price, hl_pending_for_symbol
)
logger.info(f" Hyperliquid 决策: {hyperliquid_decision.get('action')} - {hyperliquid_decision.get('reasoning', '')}")
await self._send_trading_decision_notification(hyperliquid_decision, market_signal, current_price, prefix="[Hyperliquid]")
else:
logger.info(f"⏸️ Hyperliquid 实盘交易未启用")
# ============================================================
# 第三阶段:执行交易决策
# 第三阶段:执行交易决策(双轨独立)
# ============================================================
await self._execute_decisions(decision, market_signal, current_price)
await self._execute_decisions(paper_decision, hyperliquid_decision, market_signal, current_price)
except Exception as e:
logger.error(f"❌ 分析 {symbol} 出错: {e}")
@ -735,9 +757,9 @@ class CryptoAgent:
if risk:
logger.info(f" 风险: {risk}")
def _get_trading_state(self) -> tuple:
def _get_paper_trading_state(self) -> tuple:
"""
获取交易状态持仓和账户
获取模拟盘交易状态持仓和账户
Returns:
(positions, account, pending_orders) - 持仓列表账户状态挂单列表
@ -774,9 +796,83 @@ class CryptoAgent:
return position_list, account, pending_orders
async def _execute_decisions(self, decision: Dict[str, Any],
def _get_hyperliquid_trading_state(self) -> tuple:
"""
获取 Hyperliquid 实盘交易状态持仓和账户
Returns:
(positions, account, pending_orders) - 持仓列表账户状态挂单列表
"""
try:
# 获取账户状态
hl_state = self.hyperliquid.get_account_state()
# 转换持仓格式
position_list = []
for pos in hl_state["positions"]:
position_data = pos.get("position", {})
coin = position_data.get("coin")
size = float(position_data.get("szi", 0))
if size != 0:
entry_price = float(position_data.get("entryPx", 0))
unrealized_pnl = float(position_data.get("unrealizedPnl", 0))
# 获取止盈止损价格(从挂单中查询)
tp_sl_prices = self.hyperliquid.get_tp_sl_prices(coin)
position_list.append({
'symbol': f"{coin}USDT", # BTC → BTCUSDT
'side': 'buy' if size > 0 else 'sell',
'holding': abs(size),
'entry_price': entry_price,
'unrealized_pnl': unrealized_pnl,
'stop_loss': tp_sl_prices.get('stop_loss'),
'take_profit': tp_sl_prices.get('take_profit')
})
# 转换账户格式(匹配模拟盘格式)
account = {
'current_balance': hl_state["account_value"],
'initial_balance': self.hyperliquid.initial_balance,
'used_margin': hl_state["total_margin_used"],
'available_balance': hl_state["available_balance"],
'total_position_value': sum(abs(float(p.get("position", {}).get("szi", 0)) *
float(p.get("position", {}).get("entryPx", 0)))
for p in hl_state["positions"]),
'max_total_leverage': self.hyperliquid.max_total_leverage
}
# 计算当前总杠杆
if account['current_balance'] > 0:
account['current_total_leverage'] = account['total_position_value'] / account['current_balance']
else:
account['current_total_leverage'] = 0
# 获取挂单(包括止盈止损)
all_orders = self.hyperliquid.get_open_orders()
pending_orders = []
for order in all_orders:
pending_orders.append({
'order_id': order.get('order_id'),
'symbol': f"{order['symbol']}USDT", # 转换格式
'side': 'buy' if order.get('side') == 'B' else 'sell',
'entry_price': order.get('price'),
'quantity': order.get('size'),
'entry_type': 'limit',
'is_reduce_only': order.get('is_reduce_only', False)
})
return position_list, account, pending_orders
except Exception as e:
logger.error(f"获取 Hyperliquid 状态失败: {e}")
return [], {}, []
async def _execute_decisions(self, paper_decision: Dict[str, Any],
hyperliquid_decision: Dict[str, Any],
market_signal: Dict[str, Any], current_price: float):
"""执行交易决策"""
"""执行交易决策(双轨独立)"""
# 选择最佳信号用于保存
best_signal = self._get_best_signal_from_market(market_signal)
@ -788,67 +884,74 @@ class CryptoAgent:
signal_to_save['current_price'] = current_price
self.signal_db.add_signal(signal_to_save)
# 获取配置
paper_trading_enabled = self.settings.paper_trading_enabled
# ============================================================
# 执行模拟盘决策
# ============================================================
if paper_decision:
await self._execute_paper_decisions(paper_decision, market_signal, current_price)
# ============================================================
# 执行交易决策
# 执行 Hyperliquid 决策
# ============================================================
if paper_trading_enabled and decision:
decision_type = decision.get('decision', 'HOLD')
if hyperliquid_decision and self.hyperliquid:
await self._execute_hyperliquid_decisions(hyperliquid_decision, market_signal, current_price)
if decision_type == 'HOLD':
reasoning = decision.get('reasoning', '观望')
logger.info(f"\n📊 交易决策: {reasoning}")
# HOLD决策的理由已在交易决策通知中说明无需单独通知
else:
logger.info(f"\n📊 【执行交易】")
async def _execute_paper_decisions(self, decision: Dict[str, Any],
market_signal: Dict[str, Any],
current_price: float):
"""执行模拟盘决策"""
decision_type = decision.get('action', 'HOLD')
if decision_type in ['OPEN', 'ADD']:
# 先执行交易
logger.info(f" 准备执行交易...")
result = await self._execute_paper_trade(decision, market_signal, current_price)
# 检查是否成功执行
order = result.get('order') if result else None
logger.info(f" 订单创建检查: order={'存在' if order else '不存在'}, result_key={'order' in (result or {})}")
if order:
# 验证订单对象的有效性
if hasattr(order, 'order_id') and order.order_id:
logger.info(f" 订单验证通过: {order.order_id}")
# 只有成功创建订单后才发送通知
await self._send_signal_notification(market_signal, decision, current_price)
else:
logger.error(f" ❌ 订单对象无效: 缺少order_id属性")
# 订单创建失败,理由已在日志中记录,无需单独通知
else:
# 订单创建失败,理由已在日志中记录,无需单独通知
reason = result.get('message', '订单创建失败') if result else '订单创建失败'
logger.warning(f" ⚠️ 交易未执行: {reason}")
elif decision_type == 'CLOSE':
close_success = await self._execute_close(decision, current_price)
# CLOSE 操作也发送执行通知
if close_success:
await self._send_signal_notification(market_signal, decision, current_price)
else:
logger.warning(f" ⚠️ 平仓未成功执行,跳过通知")
elif decision_type == 'CANCEL_PENDING':
cancel_success = await self._execute_cancel_pending(decision)
# CANCEL_PENDING 操作也发送执行通知
if cancel_success:
await self._send_signal_notification(market_signal, decision, current_price)
else:
logger.warning(f" ⚠️ 取消挂单未成功执行,跳过通知")
elif decision_type == 'REDUCE':
reduce_success = await self._execute_reduce(decision)
# REDUCE 操作也发送执行通知
if reduce_success:
await self._send_signal_notification(market_signal, decision, current_price)
else:
logger.warning(f" ⚠️ 减仓未成功执行,跳过通知")
if decision_type == 'HOLD':
reasoning = decision.get('reasoning', '观望')
logger.info(f"\n📊 交易决策: {reasoning}")
# HOLD决策的理由已在交易决策通知中说明无需单独通知
else:
logger.info(f"\n⏸️ 交易未启用或决策为空")
logger.info(f"\n📊 【执行交易】")
if decision_type in ['OPEN', 'ADD']:
# 先执行交易
logger.info(f" 准备执行交易...")
result = await self._execute_paper_trade(decision, market_signal, current_price)
# 检查是否成功执行
order = result.get('order') if result else None
logger.info(f" 订单创建检查: order={'存在' if order else '不存在'}, result_key={'order' in (result or {})}")
if order:
# 验证订单对象的有效性
if hasattr(order, 'order_id') and order.order_id:
logger.info(f" 订单验证通过: {order.order_id}")
# 只有成功创建订单后才发送通知
await self._send_signal_notification(market_signal, decision, current_price)
else:
logger.error(f" ❌ 订单对象无效: 缺少order_id属性")
# 订单创建失败,理由已在日志中记录,无需单独通知
else:
# 订单创建失败,理由已在日志中记录,无需单独通知
reason = result.get('message', '订单创建失败') if result else '订单创建失败'
logger.warning(f" ⚠️ 交易未执行: {reason}")
elif decision_type == 'CLOSE':
close_success = await self._execute_close(decision, current_price)
# CLOSE 操作也发送执行通知
if close_success:
await self._send_signal_notification(market_signal, decision, current_price)
else:
logger.warning(f" ⚠️ 平仓未成功执行,跳过通知")
elif decision_type == 'CANCEL_PENDING':
cancel_success = await self._execute_cancel_pending(decision)
# CANCEL_PENDING 操作也发送执行通知
if cancel_success:
await self._send_signal_notification(market_signal, decision, current_price)
else:
logger.warning(f" ⚠️ 取消挂单未成功执行,跳过通知")
elif decision_type == 'REDUCE':
reduce_success = await self._execute_reduce(decision)
# REDUCE 操作也发送执行通知
if reduce_success:
await self._send_signal_notification(market_signal, decision, current_price)
else:
logger.warning(f" ⚠️ 减仓未成功执行,跳过通知")
def _get_best_signal_from_market(self, market_signal: Dict[str, Any]) -> Dict[str, Any]:
"""从市场信号中获取最佳信号"""
@ -1032,14 +1135,15 @@ class CryptoAgent:
async def _send_trading_decision_notification(self, decision: Dict[str, Any],
market_signal: Dict[str, Any],
current_price: float):
current_price: float,
prefix: str = ""):
"""发送交易决策通知(第二阶段)"""
try:
decision_type = decision.get('decision', 'HOLD')
symbol = market_signal.get('symbol')
# 账户类型标识
account_type = "📊 交易"
account_type = f"{prefix} 📊 交易" if prefix else "📊 交易"
# 决策类型映射
decision_map = {
@ -1165,7 +1269,8 @@ class CryptoAgent:
logger.debug(traceback.format_exc())
async def _send_signal_notification(self, market_signal: Dict[str, Any],
decision: Dict[str, Any], current_price: float):
decision: Dict[str, Any], current_price: float,
prefix: str = ""):
"""发送交易执行通知(第三阶段)"""
try:
decision_type = decision.get('decision', 'HOLD')
@ -1176,6 +1281,9 @@ class CryptoAgent:
# 构建消息 - 使用旧格式风格
symbol = market_signal.get('symbol')
# 添加前缀到标题
title_prefix = f"{prefix} " if prefix else ""
action = decision.get('action', '')
reasoning = decision.get('reasoning', '')
risk_analysis = decision.get('risk_analysis', '')
@ -1222,22 +1330,22 @@ class CryptoAgent:
# 挂单时标题显示"挂单",现价单时显示"开仓"/"平仓"等
if decision_type == 'OPEN':
decision_title = '挂单' if entry_type == 'limit' else '开仓'
title = f"[执行] {account_type} {symbol} {decision_title}"
title = f"{title_prefix}[执行] {account_type} {symbol} {decision_title}"
color = "green"
elif decision_type == 'CLOSE':
decision_title = '挂单' if entry_type == 'limit' else '平仓'
title = f"[执行] {account_type} {symbol} {decision_title}"
title = f"{title_prefix}[执行] {account_type} {symbol} {decision_title}"
color = "orange"
elif decision_type == 'ADD':
decision_title = '挂单' if entry_type == 'limit' else '加仓'
title = f"[执行] {account_type} {symbol} {decision_title}"
title = f"{title_prefix}[执行] {account_type} {symbol} {decision_title}"
color = "green"
elif decision_type == 'REDUCE':
decision_title = '挂单' if entry_type == 'limit' else '减仓'
title = f"[执行] {account_type} {symbol} {decision_title}"
title = f"{title_prefix}[执行] {account_type} {symbol} {decision_title}"
color = "orange"
else:
title = f"[执行] {account_type} {symbol} 交易执行"
title = f"{title_prefix}[执行] {account_type} {symbol} 交易执行"
color = "blue"
# 构建卡片内容
@ -1723,6 +1831,237 @@ class CryptoAgent:
logger.error(f"执行减仓失败: {e}")
return False
# ============================================================
# Hyperliquid 执行方法
# ============================================================
async def _execute_hyperliquid_decisions(self, decision: Dict[str, Any],
market_signal: Dict[str, Any],
current_price: float):
"""执行 Hyperliquid 决策"""
decision_type = decision.get('action', 'HOLD')
if decision_type == 'HOLD':
reasoning = decision.get('reasoning', '观望')
logger.info(f" Hyperliquid 决策: {reasoning}")
return
try:
if decision_type in ['OPEN', 'ADD']:
logger.info(f" 准备执行 Hyperliquid 交易...")
result = await self._execute_hyperliquid_trade(decision, market_signal, current_price)
if result.get('success'):
logger.info(f" ✅ Hyperliquid 交易成功")
await self._send_signal_notification(market_signal, decision, current_price, prefix="[Hyperliquid]")
else:
logger.error(f" ❌ Hyperliquid 交易失败: {result.get('error')}")
elif decision_type == 'CLOSE':
logger.info(f" 准备 Hyperliquid 平仓...")
result = await self._execute_hyperliquid_close(decision, current_price)
if result.get('success'):
logger.info(f" ✅ Hyperliquid 平仓成功")
await self._send_signal_notification(market_signal, decision, current_price, prefix="[Hyperliquid]")
else:
logger.error(f" ❌ Hyperliquid 平仓失败: {result.get('error')}")
elif decision_type == 'CANCEL_PENDING':
logger.info(f" 准备取消 Hyperliquid 挂单...")
result = await self._execute_hyperliquid_cancel(decision)
if result.get('success'):
logger.info(f" ✅ Hyperliquid 取消成功")
else:
logger.error(f" ❌ Hyperliquid 取消失败: {result.get('error')}")
except Exception as e:
logger.error(f" ❌ Hyperliquid 执行异常: {e}")
async def _execute_hyperliquid_trade(self, decision: Dict[str, Any],
market_signal: Dict[str, Any],
current_price: float) -> Dict[str, Any]:
"""执行 Hyperliquid 开仓/加仓"""
try:
symbol = decision.get('symbol', '').replace('USDT', '') # BTCUSDT → BTC
side = decision.get('side')
entry_type = decision.get('entry_type', 'market')
entry_price = decision.get('entry_price', current_price)
# 计算仓位大小(基于可用保证金和风控)
size = self._calculate_hyperliquid_position_size(decision, current_price)
# 检查保证金是否充足
if size <= 0:
return {"success": False, "error": "保证金不足,无法开仓"}
# 更新杠杆
leverage = min(decision.get('leverage', 10), 10)
self.hyperliquid.update_leverage(symbol, leverage)
# 如果是加仓,先取消旧的止盈止损
if decision.get('action') == 'ADD':
self.hyperliquid.cancel_tp_sl_orders(symbol)
logger.info(f" 取消旧的止盈止损订单")
# 执行交易
if entry_type == 'market':
result = self.hyperliquid.place_market_order(
symbol=symbol,
is_buy=(side == 'buy'),
size=size
)
else: # limit
result = self.hyperliquid.place_limit_order(
symbol=symbol,
is_buy=(side == 'buy'),
size=size,
price=entry_price
)
# 如果开仓成功,设置止盈止损
if result.get('success'):
tp_price = decision.get('take_profit')
sl_price = decision.get('stop_loss')
if tp_price or sl_price:
# 判断方向
is_long = (side == 'buy')
# 设置止盈止损
tp_sl_result = self.hyperliquid.set_tp_sl(
symbol=symbol,
is_long=is_long,
size=size,
tp_price=tp_price,
sl_price=sl_price
)
if not tp_sl_result.get('success'):
logger.warning(f" ⚠️ 设置止盈止损失败: {tp_sl_result.get('error')}")
return result
except Exception as e:
logger.error(f"Hyperliquid 交易执行失败: {e}")
return {"success": False, "error": str(e)}
async def _execute_hyperliquid_close(self, decision: Dict[str, Any],
current_price: float) -> Dict[str, Any]:
"""执行 Hyperliquid 平仓"""
try:
symbol = decision.get('symbol', '').replace('USDT', '')
# 先取消所有止盈止损订单
self.hyperliquid.cancel_tp_sl_orders(symbol)
logger.info(f" 取消止盈止损订单")
# 获取当前持仓
position = self.hyperliquid.get_position_for_symbol(symbol)
if not position:
return {"success": False, "error": "未找到持仓"}
size = abs(position["size"])
is_long = position["size"] > 0
# 平仓(方向相反)
result = self.hyperliquid.place_market_order(
symbol=symbol,
is_buy=not is_long,
size=size,
reduce_only=True
)
return result
except Exception as e:
logger.error(f"Hyperliquid 平仓失败: {e}")
return {"success": False, "error": str(e)}
async def _execute_hyperliquid_cancel(self, decision: Dict[str, Any]) -> Dict[str, Any]:
"""执行 Hyperliquid 取消挂单"""
try:
symbol = decision.get('symbol', '').replace('USDT', '')
result = self.hyperliquid.cancel_all_orders(symbol)
return result
except Exception as e:
logger.error(f"Hyperliquid 取消挂单失败: {e}")
return {"success": False, "error": str(e)}
def _calculate_hyperliquid_position_size(self, decision: Dict[str, Any], current_price: float) -> float:
"""
计算 Hyperliquid 仓位大小基于可用保证金和风控限制
Args:
decision: 交易决策
current_price: 当前价格
Returns:
可开仓数量币的数量 BTC = 0.01
"""
try:
# 获取账户状态
account_state = self.hyperliquid.get_account_state()
current_balance = account_state["account_value"]
used_margin = account_state["total_margin_used"]
available_balance = account_state["available_balance"]
# 获取当前所有持仓的总价值
total_position_value = 0
positions = self.hyperliquid.get_open_positions()
for pos in positions:
size = abs(pos["size"])
entry_price = pos["entry_price"]
total_position_value += size * entry_price
# 当前总杠杆
current_total_leverage = total_position_value / current_balance if current_balance > 0 else 0
# 获取杠杆配置
leverage = min(decision.get('leverage', 5), 10) # 最大 10x
# 计算最大可开仓金额(考虑多个限制)
max_by_config = self.hyperliquid.max_single_position # 配置的单笔限制
max_by_available = available_balance * leverage # 可用保证金 × 杠杆
max_by_total_leverage = (current_balance * self.hyperliquid.max_total_leverage - total_position_value) # 总杠杆限制
# 取最小值作为最大可开仓金额
max_position_usd = min(max_by_config, max_by_available, max_by_total_leverage)
# 风控检查:不能超过可用余额的 50%(保守策略)
max_position_usd = min(max_position_usd, current_balance * 0.5)
# 如果计算出的最大值 <= 0说明保证金不足
if max_position_usd <= 0:
logger.warning(f"⚠️ 可用保证金不足,无法开仓")
logger.warning(f" 账户价值: ${current_balance:.2f}")
logger.warning(f" 可用余额: ${available_balance:.2f}")
logger.warning(f" 总持仓价值: ${total_position_value:.2f}")
logger.warning(f" 当前总杠杆: {current_total_leverage:.2f}x")
return 0
# 根据当前价格计算数量
size = max_position_usd / current_price
logger.info(f"💰 仓位计算:")
logger.info(f" 账户价值: ${current_balance:.2f}")
logger.info(f" 可用余额: ${available_balance:.2f}")
logger.info(f" 总持仓价值: ${total_position_value:.2f}")
logger.info(f" 当前总杠杆: {current_total_leverage:.2f}x")
logger.info(f" 计划杠杆: {leverage}x")
logger.info(f" 最大可开仓金额: ${max_position_usd:.2f} (限制: min(配置${max_by_config:.0f}, 可用${max_by_available:.0f}, 杠杆${max_by_total_leverage:.0f}))")
logger.info(f" 计算数量: {size:.6f} @ ${current_price:.2f}")
# 四舍五入到合理精度
return round(size, 6)
except Exception as e:
logger.error(f"计算仓位大小失败: {e}")
# 发生错误时返回 0不开仓
return 0
def _convert_to_paper_signal(self, symbol: str, signal: Dict[str, Any],
current_price: float) -> Dict[str, Any]:
"""转换 LLM 信号格式为模拟交易格式"""

View File

@ -0,0 +1,498 @@
"""
Hyperliquid 交易服务 - ClawFi 集成
"""
import os
from typing import Dict, Any, Optional, List
from datetime import datetime
from app.config import get_settings
from app.utils.logger import logger
try:
from hyperliquid.info import Info
from hyperliquid.exchange import Exchange
from eth_account import Account
HYPERLIQUID_AVAILABLE = True
except ImportError:
HYPERLIQUID_AVAILABLE = False
logger.warning("Hyperliquid SDK 未安装,请运行: npx clawfi-hyperliquid-skill")
class HyperliquidTradingService:
"""Hyperliquid 交易服务ClawFi 集成)"""
def __init__(self):
"""初始化 Hyperliquid 交易服务"""
if not HYPERLIQUID_AVAILABLE:
raise ImportError("Hyperliquid SDK 未安装")
self.settings = get_settings()
# 从环境变量加载认证信息
self.wallet_address = os.getenv("CLAWFI_WALLET_ADDRESS")
self.private_key = os.getenv("CLAWFI_PRIVATE_KEY")
if not self.wallet_address or not self.private_key:
raise ValueError(
"缺少 Hyperliquid 认证信息。请运行: "
"npx clawfi-hyperliquid-skill --wallet=0x... --key=0x..."
)
# 风控配置
self.max_total_leverage = self.settings.hyperliquid_max_total_leverage
self.circuit_breaker_drawdown = self.settings.hyperliquid_circuit_breaker_drawdown
self.max_single_position = self.settings.hyperliquid_max_single_position
# 初始化 SDK
self.info = Info(base_url="https://api.hyperliquid.xyz")
account = Account.from_key(self.private_key)
self.exchange = Exchange(account, base_url="https://api.hyperliquid.xyz",
account_address=self.wallet_address)
# 初始账户价值(用于熔断检查)
self.initial_balance: Optional[float] = None
self._initialize_account()
logger.info(f"Hyperliquid 交易服务初始化完成")
logger.info(f" 钱包地址: {self.wallet_address}")
logger.info(f" 总杠杆上限: {self.max_total_leverage}x")
logger.info(f" 熔断阈值: {self.circuit_breaker_drawdown * 100}%")
def _initialize_account(self):
"""初始化账户信息"""
try:
state = self.get_account_state()
self.initial_balance = state["account_value"]
logger.info(f" 初始账户价值: ${self.initial_balance:,.2f}")
except Exception as e:
logger.error(f"初始化账户失败: {e}")
raise
def get_account_state(self) -> Dict[str, Any]:
"""获取账户状态"""
try:
state = self.info.user_state(self.wallet_address)
margin_summary = state.get("marginSummary", {})
account_value = float(margin_summary.get("accountValue", 0))
total_margin_used = float(margin_summary.get("totalMarginUsed", 0))
return {
"account_value": account_value,
"total_margin_used": total_margin_used,
"available_balance": account_value - total_margin_used,
"positions": state.get("assetPositions", []),
"margin_summary": margin_summary
}
except Exception as e:
logger.error(f"获取账户状态失败: {e}")
raise
def check_risk_limits(self) -> Dict[str, Any]:
"""
检查风险限制ClawFi 强制规则
Returns:
风险检查结果
"""
state = self.get_account_state()
current_value = state["account_value"]
# 计算回撤
if self.initial_balance is None:
self.initial_balance = current_value
drawdown = (self.initial_balance - current_value) / self.initial_balance if self.initial_balance > 0 else 0
# 10% 熔断检查
circuit_breaker_triggered = drawdown >= self.circuit_breaker_drawdown
if circuit_breaker_triggered:
logger.error(f"🚨 触发 10% 熔断!当前回撤: {drawdown * 100:.2f}%")
# 平掉所有持仓
self.market_close_all()
raise Exception(f"触发 10% 熔断 - 所有持仓已平仓(回撤: {drawdown * 100:.2f}%")
return {
"initial_balance": self.initial_balance,
"current_value": current_value,
"drawdown": drawdown,
"drawdown_percent": drawdown * 100,
"circuit_breaker_triggered": circuit_breaker_triggered,
"safe_to_trade": not circuit_breaker_triggered
}
def update_leverage(self, symbol: str, leverage: int):
"""
更新杠杆必须在开仓前调用
Args:
symbol: 交易对 "BTC"
leverage: 杠杆倍数10
"""
if leverage > 10:
raise ValueError(f"杠杆不能超过 10xClawFi 规则),当前: {leverage}x")
try:
result = self.exchange.update_leverage(leverage, symbol, is_cross=False)
logger.info(f"更新杠杆: {symbol}{leverage}x")
return result
except Exception as e:
logger.error(f"更新杠杆失败: {e}")
raise
def place_market_order(
self,
symbol: str,
is_buy: bool,
size: float,
reduce_only: bool = False
) -> Dict[str, Any]:
"""
下市价单
Args:
symbol: 交易对 "BTC"
is_buy: True=做多False=做空
size: 数量
reduce_only: 是否仅平仓
"""
# 风险检查
self.check_risk_limits()
try:
result = self.exchange.market_open(symbol, is_buy, size, reduce_only=reduce_only)
side = "买入" if is_buy else "卖出"
logger.info(f"✅ Hyperliquid 市价单: {side} {symbol} {size}")
return {
"success": True,
"symbol": symbol,
"side": "buy" if is_buy else "sell",
"size": size,
"result": result
}
except Exception as e:
logger.error(f"下单失败: {e}")
return {
"success": False,
"error": str(e)
}
def place_limit_order(
self,
symbol: str,
is_buy: bool,
size: float,
price: float,
reduce_only: bool = False
) -> Dict[str, Any]:
"""下限价单"""
self.check_risk_limits()
try:
result = self.exchange.order(symbol, is_buy, size, price,
{"limit": {"tif": "Gtc"}},
reduce_only=reduce_only)
side = "买入" if is_buy else "卖出"
logger.info(f"✅ Hyperliquid 限价单: {side} {symbol} {size} @ ${price}")
return {
"success": True,
"symbol": symbol,
"side": "buy" if is_buy else "sell",
"size": size,
"price": price,
"result": result
}
except Exception as e:
logger.error(f"下单失败: {e}")
return {
"success": False,
"error": str(e)
}
def get_open_orders(self, symbol: Optional[str] = None) -> List[Dict[str, Any]]:
"""
获取所有挂单包括止盈止损订单
Args:
symbol: 可选指定币种
Returns:
挂单列表
"""
try:
# Hyperliquid 没有直接的获取挂单 API需要通过 user_state
# 注意:这个方法可能需要根据实际 API 调整
state = self.info.user_state(self.wallet_address)
open_orders = state.get("openOrders", [])
orders = []
for order in open_orders:
coin = order.get("coin")
if symbol and coin != symbol:
continue
orders.append({
"order_id": order.get("oid"),
"symbol": coin,
"side": order.get("side"),
"size": float(order.get("totalSz", 0)),
"price": float(order.get("limitPx", 0)),
"is_reduce_only": order.get("reduceOnly", False),
"order_type": order.get("orderType", {})
})
return orders
except Exception as e:
logger.error(f"获取挂单失败: {e}")
return []
def get_tp_sl_prices(self, symbol: str) -> Dict[str, Optional[float]]:
"""
获取指定币种的止盈止损价格
Args:
symbol: 币种 "BTC"
Returns:
{'take_profit': price, 'stop_loss': price}
"""
try:
orders = self.get_open_orders(symbol)
tp_price = None
sl_price = None
for order in orders:
if not order.get("is_reduce_only"):
continue
order_type = order.get("order_type", {})
# 止盈:限价单
if "limit" in order_type and order["price"] > 0:
tp_price = order["price"]
# 止损:触发单
if "trigger" in order_type:
trigger_px = order_type.get("trigger", {}).get("triggerPx")
if trigger_px:
sl_price = float(trigger_px)
return {
"take_profit": tp_price,
"stop_loss": sl_price
}
except Exception as e:
logger.error(f"获取止盈止损价格失败: {e}")
return {"take_profit": None, "stop_loss": None}
def set_tp_sl(
self,
symbol: str,
is_long: bool,
size: float,
tp_price: Optional[float] = None,
sl_price: Optional[float] = None
) -> Dict[str, Any]:
"""
设置止盈止损开仓后调用
Args:
symbol: 币种 "BTC"
is_long: 是否多头
size: 数量
tp_price: 止盈价格可选
sl_price: 止损价格可选
Returns:
执行结果
"""
try:
results = []
close_is_buy = not is_long # 平多头=卖出,平空头=买入
# 设置止盈(限价单)
if tp_price:
tp_result = self.exchange.order(
symbol, close_is_buy, size, tp_price,
{"limit": {"tif": "Gtc"}},
reduce_only=True
)
results.append({"type": "take_profit", "result": tp_result})
logger.info(f"✅ 设置止盈: {symbol} @ ${tp_price}")
# 设置止损(触发单)
if sl_price:
# 触发价格需要稍微偏离(避免滑点问题)
exec_px = sl_price * 0.999 if close_is_buy else sl_price * 1.001
sl_result = self.exchange.order(
symbol, close_is_buy, size, exec_px,
{"trigger": {"triggerPx": sl_price, "isMarket": True, "tpsl": "sl"}},
reduce_only=True
)
results.append({"type": "stop_loss", "result": sl_result})
logger.info(f"✅ 设置止损: {symbol} @ ${sl_price}(触发)")
return {
"success": True,
"results": results
}
except Exception as e:
logger.error(f"设置止盈止损失败: {e}")
return {
"success": False,
"error": str(e)
}
def cancel_tp_sl_orders(self, symbol: str) -> Dict[str, Any]:
"""
取消指定币种的所有止盈止损订单
Args:
symbol: 币种 "BTC"
Returns:
取消结果
"""
try:
orders = self.get_open_orders(symbol)
cancelled_count = 0
for order in orders:
if order.get("is_reduce_only"):
result = self.exchange.cancel(symbol, order["order_id"])
if result.get("status") == "ok":
cancelled_count += 1
logger.info(f"✅ 取消 {symbol} 的止盈止损订单: {cancelled_count}")
return {
"success": True,
"cancelled_count": cancelled_count
}
except Exception as e:
logger.error(f"取消止盈止损订单失败: {e}")
return {
"success": False,
"error": str(e)
}
def cancel_order(self, symbol: str, order_id: int) -> Dict[str, Any]:
"""取消订单"""
try:
result = self.exchange.cancel(symbol, order_id)
logger.info(f"取消订单: {symbol} #{order_id}")
return {"success": True, "result": result}
except Exception as e:
logger.error(f"取消订单失败: {e}")
return {"success": False, "error": str(e)}
def cancel_all_orders(self, symbol: Optional[str] = None) -> Dict[str, Any]:
"""取消所有订单"""
try:
result = self.exchange.cancel_all_orders(symbol)
logger.info(f"取消所有订单: {symbol or '全部'}")
return {"success": True, "result": result}
except Exception as e:
logger.error(f"取消所有订单失败: {e}")
return {"success": False, "error": str(e)}
def market_close_all(self) -> Dict[str, Any]:
"""紧急平仓所有持仓(熔断时使用)"""
try:
state = self.get_account_state()
positions = state["positions"]
results = []
for pos in positions:
position_data = pos.get("position", {})
coin = position_data.get("coin")
size = float(position_data.get("szi", 0))
if size == 0:
continue
# 取消该币种的所有挂单(包括止盈止损)
self.cancel_all_orders(coin)
is_long = size > 0
result = self.place_market_order(
symbol=coin,
is_buy=not is_long, # 平多头=卖出,平空头=买入
size=abs(size),
reduce_only=True
)
results.append(result)
logger.info(f"🚨 紧急平仓完成,共平仓 {len(results)} 个持仓")
return {"success": True, "closed_positions": len(results), "results": results}
except Exception as e:
logger.error(f"紧急平仓失败: {e}")
return {"success": False, "error": str(e)}
def get_open_positions(self) -> List[Dict[str, Any]]:
"""获取所有持仓"""
try:
state = self.get_account_state()
positions = []
for pos in state["positions"]:
position_data = pos.get("position", {})
coin = position_data.get("coin")
size = float(position_data.get("szi", 0))
if size == 0:
continue
positions.append({
"coin": coin,
"size": size, # 正数=多头,负数=空头
"entry_price": float(position_data.get("entryPx", 0)),
"unrealized_pnl": float(position_data.get("unrealizedPnl", 0)),
"leverage": position_data.get("leverage", {}).get("value"),
"liquidation_price": position_data.get("liquidationPx"),
"position": position_data # 保留原始数据
})
return positions
except Exception as e:
logger.error(f"获取持仓失败: {e}")
return []
def get_position_for_symbol(self, symbol: str) -> Optional[Dict[str, Any]]:
"""获取指定币种的持仓"""
positions = self.get_open_positions()
for pos in positions:
if pos["coin"] == symbol:
return pos
return None
# 单例
_hyperliquid_service_instance = None
def get_hyperliquid_service() -> Optional[HyperliquidTradingService]:
"""获取 Hyperliquid 交易服务单例"""
global _hyperliquid_service_instance
settings = get_settings()
# 如果未启用,返回 None
if not settings.hyperliquid_trading_enabled:
return None
if _hyperliquid_service_instance is None:
try:
_hyperliquid_service_instance = HyperliquidTradingService()
except Exception as e:
logger.error(f"初始化 Hyperliquid 服务失败: {e}")
return None
return _hyperliquid_service_instance

View File

@ -35,3 +35,7 @@ lxml>=4.9.0
akshare>=1.12.0
apscheduler>=3.10.0 # 定时任务
# Hyperliquid 交易依赖ClawFi 集成)
hyperliquid-python-sdk>=0.22.0
eth-account>=0.10.0

307
backend/test_hyperliquid.py Normal file
View File

@ -0,0 +1,307 @@
#!/usr/bin/env python3
"""
Hyperliquid SDK 测试脚本
用于验证 Hyperliquid 集成是否正常工作
警告此脚本仅执行查询操作不会执行任何交易
"""
import os
import sys
from typing import Dict, Any
# 添加项目路径
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
def test_sdk_import():
"""测试 SDK 导入"""
print("\n" + "="*60)
print("📦 测试 1: SDK 导入")
print("="*60)
try:
from hyperliquid.info import Info
from hyperliquid.exchange import Exchange
from eth_account import Account
print("✅ SDK 导入成功")
return True
except ImportError as e:
print(f"❌ SDK 导入失败: {e}")
print("\n请运行以下命令安装 SDK:")
print(" npx clawfi-hyperliquid-skill --wallet=YOUR_WALLET --key=YOUR_KEY")
return False
def test_env_variables():
"""测试环境变量"""
print("\n" + "="*60)
print("🔑 测试 2: 环境变量")
print("="*60)
wallet = os.getenv("CLAWFI_WALLET_ADDRESS")
private_key = os.getenv("CLAWFI_PRIVATE_KEY")
if not wallet:
print("❌ CLAWFI_WALLET_ADDRESS 未设置")
print("\n请运行以下命令设置环境变量:")
print(" npx clawfi-hyperliquid-skill --wallet=YOUR_WALLET --key=YOUR_KEY")
return False
if not private_key:
print("❌ CLAWFI_PRIVATE_KEY 未设置")
print("\n请运行以下命令设置环境变量:")
print(" npx clawfi-hyperliquid-skill --wallet=YOUR_WALLET --key=YOUR_KEY")
return False
print(f"✅ CLAWFI_WALLET_ADDRESS: {wallet}")
print(f"✅ CLAWFI_PRIVATE_KEY: {private_key[:10]}...{private_key[-4:]}")
return True
def test_connection(wallet: str, private_key: str):
"""测试连接和基础查询"""
print("\n" + "="*60)
print("🔗 测试 3: 连接 Hyperliquid")
print("="*60)
try:
from hyperliquid.info import Info
from hyperliquid.exchange import Exchange
from eth_account import Account
# 初始化
account = Account.from_key(private_key)
info = Info(base_url="https://api.hyperliquid.xyz", skip_ws=True)
exchange = Exchange(account, base_url="https://api.hyperliquid.xyz",
account_address=wallet)
print(f"✅ Agent 地址: {account.address}")
print(f"✅ 目标钱包: {wallet}")
# 测试查询账户状态
print("\n📊 查询账户状态...")
user_state = info.user_state(wallet)
if not user_state:
print("❌ 无法获取账户状态")
return False
# 账户价值
margin_summary = user_state.get("marginSummary", {})
account_value = float(margin_summary.get("accountValue", 0))
withdrawable = float(margin_summary.get("withdrawable", 0))
total_margin_used = float(margin_summary.get("totalMarginUsed", 0))
print(f"✅ 账户价值: ${account_value:,.2f}")
print(f"✅ 可提取: ${withdrawable:,.2f}")
print(f"✅ 已用保证金: ${total_margin_used:,.2f}")
# 查询持仓
print("\n📈 查询持仓...")
positions = user_state.get("assetPositions", [])
if not positions:
print("✅ 无持仓")
else:
open_positions = []
for pos in positions:
p = pos.get("position", {})
size = float(p.get("szi", 0))
if size != 0:
open_positions.append({
"coin": p.get("coin"),
"size": size,
"entry_px": float(p.get("entryPx", 0)),
"unrealized_pnl": float(p.get("unrealizedPnl", 0))
})
if not open_positions:
print("✅ 无活跃持仓")
else:
print(f"✅ 活跃持仓数: {len(open_positions)}")
for pos in open_positions:
side = "做多" if pos["size"] > 0 else "做空"
print(f" {pos['coin']}: {side} {abs(pos['size'])} @ ${pos['entry_px']:,.2f} | "
f"PnL: ${pos['unrealized_pnl']:,.2f}")
# 查询挂单
print("\n📋 查询挂单...")
open_orders = user_state.get("openOrders", [])
if not open_orders:
print("✅ 无挂单")
else:
print(f"✅ 挂单数: {len(open_orders)}")
for order in open_orders:
coin = order.get("coin")
side = order.get("side")
size = float(order.get("totalSz", 0))
limit_px = float(order.get("limitPx", 0))
print(f" {coin}: {side} {size} @ ${limit_px:,.2f}")
return True
except Exception as e:
print(f"❌ 连接失败: {e}")
import traceback
traceback.print_exc()
return False
def test_tick_size():
"""测试获取 tick size"""
print("\n" + "="*60)
print("📏 测试 4: 获取 Tick Size")
print("="*60)
try:
from hyperliquid.info import Info
info = Info(base_url="https://api.hyperliquid.xyz", skip_ws=True)
# 获取元数据
meta = info.meta()
universe = meta.get("universe", [])
print(f"✅ 可交易币种数: {len(universe)}")
# 显示前几个币种的 tick size
for asset in universe[:5]:
name = asset.get("name")
tick_size = float(asset.get("tickSize", 1.0))
sz_decimals = asset.get("szDecimals", 5)
print(f" {name}: tick_size={tick_size}, sz_decimals={sz_decimals}")
return True
except Exception as e:
print(f"❌ 获取 tick size 失败: {e}")
return False
def test_service_initialization():
"""测试服务初始化"""
print("\n" + "="*60)
print("🔧 测试 5: 服务初始化")
print("="*60)
try:
from app.services.hyperliquid_trading_service import get_hyperliquid_service
# 尝试获取服务(如果未启用会返回 None
service = get_hyperliquid_service()
if service is None:
print("⚠️ Hyperliquid 服务未启用hyperliquid_trading_enabled=False")
print(" 要启用服务,请在 .env 文件中设置: hyperliquid_trading_enabled=true")
return False
print("✅ Hyperliquid 服务初始化成功")
print(f" 钱包地址: {service.wallet_address}")
print(f" 最大杠杆: {service.max_total_leverage}x")
print(f" 熔断阈值: {service.circuit_breaker_drawdown * 100}%")
print(f" 单笔最大持仓: ${service.max_single_position}")
# 测试获取账户状态
print("\n📊 测试获取账户状态...")
state = service.get_account_state()
print(f"✅ 账户价值: ${state['account_value']:,.2f}")
print(f"✅ 可用余额: ${state['available_balance']:,.2f}")
# 测试获取持仓
print("\n📈 测试获取持仓...")
positions = service.get_open_positions()
if positions:
print(f"✅ 活跃持仓: {len(positions)}")
for pos in positions:
side = "做多" if pos["size"] > 0 else "做空"
print(f" {pos['coin']}: {side} {abs(pos['size'])}")
else:
print("✅ 无活跃持仓")
# 测试获取止盈止损价格
print("\n🛡️ 测试获取止盈止损...")
if positions:
for pos in positions:
tp_sl = service.get_tp_sl_prices(pos["coin"])
print(f" {pos['coin']}: TP={tp_sl['take_profit']}, SL={tp_sl['stop_loss']}")
else:
# 测试 BTC 的止盈止损(即使没有持仓)
tp_sl = service.get_tp_sl_prices("BTC")
print(f" BTC: TP={tp_sl['take_profit']}, SL={tp_sl['stop_loss']}")
return True
except Exception as e:
print(f"❌ 服务初始化失败: {e}")
import traceback
traceback.print_exc()
return False
def main():
"""主测试流程"""
print("\n" + "🦅"*30)
print(" Hyperliquid 集成测试")
print("🦅"*30)
results = {}
# 测试 1: SDK 导入
results["sdk_import"] = test_sdk_import()
if not results["sdk_import"]:
print("\n❌ SDK 导入失败,无法继续测试")
return False
# 测试 2: 环境变量
results["env_vars"] = test_env_variables()
if not results["env_vars"]:
print("\n❌ 环境变量未设置,无法继续测试")
return False
# 获取环境变量
wallet = os.getenv("CLAWFI_WALLET_ADDRESS")
private_key = os.getenv("CLAWFI_PRIVATE_KEY")
# 测试 3: 连接
results["connection"] = test_connection(wallet, private_key)
if not results["connection"]:
print("\n⚠️ 连接测试失败,但继续测试其他功能")
# 测试 4: Tick size
results["tick_size"] = test_tick_size()
# 测试 5: 服务初始化
results["service"] = test_service_initialization()
# 总结
print("\n" + "="*60)
print("📊 测试结果总结")
print("="*60)
for test_name, result in results.items():
status = "✅ 通过" if result else "❌ 失败"
print(f" {test_name}: {status}")
all_passed = all(results.values())
if all_passed:
print("\n🎉 所有测试通过Hyperliquid 集成正常工作")
else:
print("\n⚠️ 部分测试失败,请检查上述错误信息")
return all_passed
if __name__ == "__main__":
try:
success = main()
sys.exit(0 if success else 1)
except KeyboardInterrupt:
print("\n\n⚠️ 测试被用户中断")
sys.exit(1)
except Exception as e:
print(f"\n\n❌ 测试过程中发生异常: {e}")
import traceback
traceback.print_exc()
sys.exit(1)