1
This commit is contained in:
parent
02647e92d2
commit
64d4e5acdb
@ -359,6 +359,7 @@ class TradingDecisionMaker:
|
||||
{
|
||||
"decision": "OPEN/CLOSE/ADD/REDUCE/CANCEL_PENDING/HOLD",
|
||||
"action": "buy/sell",
|
||||
"entry_type": "market/limit", // market=现价入场, limit=挂单等待
|
||||
"quantity": 保证金金额(USDT),
|
||||
"entry_price": 入场价格,
|
||||
"stop_loss": 止损价格,
|
||||
|
||||
@ -161,16 +161,23 @@ class HyperliquidTradingService:
|
||||
self.check_risk_limits()
|
||||
|
||||
try:
|
||||
result = self.exchange.market_open(symbol, is_buy, size, reduce_only=reduce_only)
|
||||
if reduce_only:
|
||||
# 平仓使用 market_close(不需要指定 is_buy,自动判断)
|
||||
result = self.exchange.market_close(symbol, sz=size)
|
||||
else:
|
||||
# 开仓使用 market_open
|
||||
result = self.exchange.market_open(symbol, is_buy, size)
|
||||
|
||||
side = "买入" if is_buy else "卖出"
|
||||
logger.info(f"✅ Hyperliquid 市价单: {side} {symbol} {size}")
|
||||
order_type = "平仓" if reduce_only else "开仓"
|
||||
logger.info(f"✅ Hyperliquid 市价单: {order_type} {side} {symbol} {size}")
|
||||
|
||||
return {
|
||||
"success": True,
|
||||
"symbol": symbol,
|
||||
"side": "buy" if is_buy else "sell",
|
||||
"size": size,
|
||||
"reduce_only": reduce_only,
|
||||
"result": result
|
||||
}
|
||||
except Exception as e:
|
||||
@ -225,25 +232,35 @@ class HyperliquidTradingService:
|
||||
挂单列表
|
||||
"""
|
||||
try:
|
||||
# Hyperliquid 没有直接的获取挂单 API,需要通过 user_state
|
||||
# 注意:这个方法可能需要根据实际 API 调整
|
||||
state = self.info.user_state(self.wallet_address)
|
||||
open_orders = state.get("openOrders", [])
|
||||
# 使用 open_orders API 获取挂单
|
||||
orders_data = self.info.open_orders(self.wallet_address)
|
||||
|
||||
orders = []
|
||||
for order in open_orders:
|
||||
for order in orders_data or []:
|
||||
coin = order.get("coin")
|
||||
if symbol and coin != symbol:
|
||||
continue
|
||||
|
||||
# side: "A" = ask (sell/做空), "B" = bid (buy/做多)
|
||||
side = order.get("side")
|
||||
is_buy = (side == "B")
|
||||
|
||||
# Hyperliquid API 不直接返回 reduce_only 标记
|
||||
# 但我们可以根据其他信息判断
|
||||
# 暂时将所有订单都标记为非 reduce_only
|
||||
is_reduce_only = order.get("reduce_only", False)
|
||||
|
||||
orders.append({
|
||||
"order_id": order.get("oid"),
|
||||
"symbol": coin,
|
||||
"side": order.get("side"),
|
||||
"size": float(order.get("totalSz", 0)),
|
||||
"side": "buy" if is_buy else "sell",
|
||||
"size": float(order.get("sz", 0)),
|
||||
"price": float(order.get("limitPx", 0)),
|
||||
"is_reduce_only": order.get("reduceOnly", False),
|
||||
"order_type": order.get("orderType", {})
|
||||
"is_reduce_only": is_reduce_only,
|
||||
"order_type": order.get("orderType", {}),
|
||||
"timestamp": order.get("timestamp"),
|
||||
"original_size": float(order.get("origSz", 0)),
|
||||
"raw_side": side
|
||||
})
|
||||
|
||||
return orders
|
||||
@ -317,6 +334,9 @@ class HyperliquidTradingService:
|
||||
|
||||
# 设置止盈(限价单)
|
||||
if tp_price:
|
||||
# 四舍五入价格到合适精度(避免 float_to_wire rounding 错误)
|
||||
tp_price = round(float(tp_price), 5)
|
||||
|
||||
tp_result = self.exchange.order(
|
||||
symbol, close_is_buy, size, tp_price,
|
||||
{"limit": {"tif": "Gtc"}},
|
||||
@ -330,6 +350,11 @@ class HyperliquidTradingService:
|
||||
# 触发价格需要稍微偏离(避免滑点问题)
|
||||
exec_px = sl_price * 0.999 if close_is_buy else sl_price * 1.001
|
||||
|
||||
# 四舍五入价格到合适精度(避免 float_to_wire rounding 错误)
|
||||
# Hyperliquid 要求价格最多 5 位小数
|
||||
sl_price = round(float(sl_price), 5)
|
||||
exec_px = round(float(exec_px), 5)
|
||||
|
||||
sl_result = self.exchange.order(
|
||||
symbol, close_is_buy, size, exec_px,
|
||||
{"trigger": {"triggerPx": sl_price, "isMarket": True, "tpsl": "sl"}},
|
||||
@ -397,9 +422,21 @@ class HyperliquidTradingService:
|
||||
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}
|
||||
# Hyperliquid SDK 没有 cancel_all_orders 方法,需要先查询再逐个取消
|
||||
orders = self.get_open_orders(symbol)
|
||||
|
||||
results = []
|
||||
for order in orders:
|
||||
order_symbol = order.get('symbol')
|
||||
order_id = order.get('order_id')
|
||||
try:
|
||||
result = self.exchange.cancel(order_symbol, order_id)
|
||||
results.append(result)
|
||||
except Exception as e:
|
||||
logger.warning(f"取消订单失败: {order_symbol} #{order_id} - {e}")
|
||||
|
||||
logger.info(f"取消所有订单: {symbol or '全部'} ({len(results)} 个)")
|
||||
return {"success": True, "result": results, "cancelled_count": len(results)}
|
||||
except Exception as e:
|
||||
logger.error(f"取消所有订单失败: {e}")
|
||||
return {"success": False, "error": str(e)}
|
||||
|
||||
@ -4,7 +4,7 @@ langchain==0.1.0
|
||||
langchain-community==0.0.20
|
||||
zhipuai==2.0.1
|
||||
openai>=1.0.0
|
||||
tushare==1.3.8
|
||||
tushare>=1.4.0 # 升级到1.4+以支持更新的websocket-client(解决hyperliquid依赖冲突)
|
||||
sqlalchemy==2.0.25
|
||||
pydantic==2.5.3
|
||||
pydantic-settings==2.1.0
|
||||
|
||||
355
backend/test_hyperliquid_operations.py
Normal file
355
backend/test_hyperliquid_operations.py
Normal file
@ -0,0 +1,355 @@
|
||||
"""
|
||||
Hyperliquid 小资金测试脚本
|
||||
|
||||
测试内容:
|
||||
1. 获取账户状态
|
||||
2. 市价单测试(小金额)
|
||||
3. 限价单测试(小金额)
|
||||
4. 止盈止损设置
|
||||
5. 查询持仓和挂单
|
||||
6. 清理测试(平仓、取消挂单)
|
||||
|
||||
运行方式:
|
||||
python3 test_hyperliquid_operations.py
|
||||
|
||||
⚠️ 请确保 .env 中已配置:
|
||||
- CLAWFI_WALLET_ADDRESS
|
||||
- CLAWFI_PRIVATE_KEY
|
||||
- hyperliquid_trading_enabled=true
|
||||
"""
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
|
||||
# 添加项目路径
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
|
||||
|
||||
from app.services.hyperliquid_trading_service import HyperliquidTradingService
|
||||
from app.utils.logger import logger
|
||||
|
||||
|
||||
class HyperliquidTester:
|
||||
"""Hyperliquid 测试器"""
|
||||
|
||||
def __init__(self):
|
||||
"""初始化测试器"""
|
||||
try:
|
||||
self.service = HyperliquidTradingService()
|
||||
logger.info("✅ Hyperliquid 服务初始化成功")
|
||||
except Exception as e:
|
||||
logger.error(f"❌ 初始化失败: {e}")
|
||||
raise
|
||||
|
||||
def print_section(self, title: str):
|
||||
"""打印分隔线"""
|
||||
print(f"\n{'='*60}")
|
||||
print(f" {title}")
|
||||
print(f"{'='*60}\n")
|
||||
|
||||
def test_1_get_account_state(self):
|
||||
"""测试1:获取账户状态"""
|
||||
self.print_section("测试1: 获取账户状态")
|
||||
|
||||
state = self.service.get_account_state()
|
||||
|
||||
print(f"💰 账户价值: ${state['account_value']:,.2f}")
|
||||
print(f"📊 已用保证金: ${state['total_margin_used']:,.2f}")
|
||||
print(f"💵 可用余额: ${state['available_balance']:,.2f}")
|
||||
print(f"📈 持仓数量: {len(state['positions'])}")
|
||||
|
||||
positions = self.service.get_open_positions()
|
||||
if positions:
|
||||
print(f"\n当前持仓:")
|
||||
for pos in positions:
|
||||
coin = pos['coin']
|
||||
size = pos['size']
|
||||
entry = pos['entry_price']
|
||||
pnl = pos['unrealized_pnl']
|
||||
print(f" - {coin}: {size:.4f} @ ${entry:,.2f} | PnL: ${pnl:,.2f}")
|
||||
else:
|
||||
print(f" 无持仓")
|
||||
|
||||
return state
|
||||
|
||||
def test_2_market_order(self, symbol: str = "BTC", is_buy: bool = True, size: float = 0.001):
|
||||
"""测试2:市价单(小金额测试)"""
|
||||
self.print_section(f"测试2: 市价单 - {'买入' if is_buy else '卖出'} {symbol}")
|
||||
|
||||
# 检查风险限制
|
||||
risk = self.service.check_risk_limits()
|
||||
print(f"🔍 风险检查:")
|
||||
print(f" 初始余额: ${risk['initial_balance']:,.2f}")
|
||||
print(f" 当前价值: ${risk['current_value']:,.2f}")
|
||||
print(f" 回撤: {risk['drawdown_percent']:.2f}%")
|
||||
print(f" 安全交易: {'✅' if risk['safe_to_trade'] else '❌'}")
|
||||
|
||||
if not risk['safe_to_trade']:
|
||||
print("⚠️ 风险检查未通过,跳过测试")
|
||||
return None
|
||||
|
||||
# 更新杠杆
|
||||
print(f"\n⚙️ 设置杠杆为 2x...")
|
||||
self.service.update_leverage(symbol, 2)
|
||||
|
||||
# 下市价单
|
||||
print(f"\n📝 下市价单: {'买入' if is_buy else '卖出'} {size} {symbol}")
|
||||
result = self.service.place_market_order(
|
||||
symbol=symbol,
|
||||
is_buy=is_buy,
|
||||
size=size
|
||||
)
|
||||
|
||||
if result.get('success'):
|
||||
print(f"✅ 市价单成功")
|
||||
print(f" 方向: {'买入' if is_buy else '卖出'}")
|
||||
print(f" 数量: {size}")
|
||||
print(f" 结果: {result.get('result')}")
|
||||
|
||||
# 设置止盈止损
|
||||
print(f"\n🎯 设置止盈止损...")
|
||||
current_positions = self.service.get_open_positions()
|
||||
if current_positions:
|
||||
pos = current_positions[0]
|
||||
entry_price = pos['entry_price']
|
||||
is_long = pos['size'] > 0
|
||||
|
||||
# 计算止盈止损价格(小范围测试)
|
||||
if is_long:
|
||||
tp_price = entry_price * 1.01 # +1%
|
||||
sl_price = entry_price * 0.995 # -0.5%
|
||||
else:
|
||||
tp_price = entry_price * 0.99 # -1%
|
||||
sl_price = entry_price * 1.005 # +0.5%
|
||||
|
||||
print(f" 入场价: ${entry_price:,.2f}")
|
||||
print(f" 止盈价: ${tp_price:,.2f}")
|
||||
print(f" 止损价: ${sl_price:,.2f}")
|
||||
|
||||
tp_sl_result = self.service.set_tp_sl(
|
||||
symbol=symbol,
|
||||
is_long=is_long,
|
||||
size=abs(size),
|
||||
tp_price=tp_price,
|
||||
sl_price=sl_price
|
||||
)
|
||||
|
||||
if tp_sl_result.get('success'):
|
||||
print(f"✅ 止盈止损设置成功")
|
||||
else:
|
||||
print(f"❌ 止盈止损设置失败: {tp_sl_result.get('error')}")
|
||||
else:
|
||||
print(f"❌ 市价单失败: {result.get('error')}")
|
||||
|
||||
# 等待一下查看结果
|
||||
print(f"\n⏳ 等待2秒查看持仓状态...")
|
||||
time.sleep(2)
|
||||
|
||||
# 查询挂单
|
||||
orders = self.service.get_open_orders(symbol)
|
||||
if orders:
|
||||
print(f"\n📋 当前挂单:")
|
||||
for order in orders:
|
||||
print(f" - ID: {order['order_id']}")
|
||||
print(f" 方向: {order['side']}")
|
||||
print(f" 价格: ${order['price']:,.2f}")
|
||||
print(f" 数量: {order['size']}")
|
||||
else:
|
||||
print(f"\n📋 无挂单")
|
||||
|
||||
return result
|
||||
|
||||
def test_3_limit_order(self, symbol: str = "BTC", is_buy: bool = True, size: float = 0.001):
|
||||
"""测试3:限价单(挂单测试)"""
|
||||
self.print_section(f"测试3: 限价单 - {'买入' if is_buy else '卖出'} {symbol}")
|
||||
|
||||
# 获取当前价格
|
||||
import requests
|
||||
try:
|
||||
response = requests.get("https://api.hyperliquid.xyz/info", json={
|
||||
"type": "meta",
|
||||
"coin": symbol
|
||||
}, timeout=5)
|
||||
data = response.json()
|
||||
# 从响应中获取当前价格(简化处理,使用一个估算值)
|
||||
current_price = 95000 # 假设 BTC 当前价格
|
||||
except:
|
||||
current_price = 95000
|
||||
|
||||
# 设置挂单价格(距离当前价格 1%)
|
||||
if is_buy:
|
||||
limit_price = current_price * 0.99 # 低于市价 1%
|
||||
else:
|
||||
limit_price = current_price * 1.01 # 高于市价 1%
|
||||
|
||||
print(f"💡 当前价格估算: ${current_price:,.2f}")
|
||||
print(f"📝 挂单价格: ${limit_price:,.2f} ({'低于' if is_buy else '高于'}市价1%)")
|
||||
|
||||
# 下限价单
|
||||
result = self.service.place_limit_order(
|
||||
symbol=symbol,
|
||||
is_buy=is_buy,
|
||||
size=size,
|
||||
price=limit_price
|
||||
)
|
||||
|
||||
if result.get('success'):
|
||||
print(f"✅ 限价单成功")
|
||||
print(f" 方向: {'买入' if is_buy else '卖出'}")
|
||||
print(f" 数量: {size}")
|
||||
print(f" 价格: ${limit_price:,.2f}")
|
||||
|
||||
# 查询挂单
|
||||
orders = self.service.get_open_orders(symbol)
|
||||
if orders:
|
||||
print(f"\n📋 当前挂单:")
|
||||
for order in orders:
|
||||
print(f" - ID: {order['order_id']}")
|
||||
print(f" 方向: {order['side']}")
|
||||
print(f" 价格: ${order['price']:,.2f}")
|
||||
print(f" 数量: {order['size']}")
|
||||
|
||||
return result
|
||||
else:
|
||||
print(f"❌ 限价单失败: {result.get('error')}")
|
||||
return None
|
||||
|
||||
def test_4_check_tp_sl(self, symbol: str = "BTC"):
|
||||
"""测试4:查询止盈止损"""
|
||||
self.print_section(f"测试4: 查询止盈止损 - {symbol}")
|
||||
|
||||
tp_sl = self.service.get_tp_sl_prices(symbol)
|
||||
|
||||
print(f"📊 止盈止损状态:")
|
||||
if tp_sl['take_profit']:
|
||||
print(f" ✅ 止盈: ${tp_sl['take_profit']:,.2f}")
|
||||
else:
|
||||
print(f" ➖ 止盈: 未设置")
|
||||
|
||||
if tp_sl['stop_loss']:
|
||||
print(f" ✅ 止损: ${tp_sl['stop_loss']:,.2f}")
|
||||
else:
|
||||
print(f" ➖ 止损: 未设置")
|
||||
|
||||
return tp_sl
|
||||
|
||||
def test_5_cleanup(self, symbol: str = "BTC"):
|
||||
"""测试5:清理(平仓、取消挂单)"""
|
||||
self.print_section(f"测试5: 清理测试 - {symbol}")
|
||||
|
||||
# 取消所有挂单
|
||||
print(f"🗑️ 取消所有挂单...")
|
||||
cancel_result = self.service.cancel_all_orders(symbol)
|
||||
if cancel_result.get('success'):
|
||||
print(f"✅ 取消挂单成功")
|
||||
else:
|
||||
print(f"❌ 取消挂单失败: {cancel_result.get('error')}")
|
||||
|
||||
# 平仓
|
||||
positions = self.service.get_open_positions()
|
||||
symbol_positions = [p for p in positions if p['coin'] == symbol]
|
||||
|
||||
if symbol_positions:
|
||||
print(f"\n📤 平仓 {symbol}...")
|
||||
for pos in symbol_positions:
|
||||
size = abs(pos['size'])
|
||||
is_long = pos['size'] > 0
|
||||
|
||||
result = self.service.place_market_order(
|
||||
symbol=symbol,
|
||||
is_buy=not is_long, # 平仓方向相反
|
||||
size=size,
|
||||
reduce_only=True
|
||||
)
|
||||
|
||||
if result.get('success'):
|
||||
print(f"✅ 平仓成功: {size} {symbol}")
|
||||
else:
|
||||
print(f"❌ 平仓失败: {result.get('error')}")
|
||||
else:
|
||||
print(f"📊 无持仓需要平仓")
|
||||
|
||||
# 最终状态
|
||||
print(f"\n📊 最终状态:")
|
||||
final_positions = self.service.get_open_positions()
|
||||
final_orders = self.service.get_open_orders(symbol)
|
||||
|
||||
print(f" 持仓: {len(final_positions)} 个")
|
||||
print(f" 挂单: {len(final_orders)} 个")
|
||||
|
||||
if len(final_positions) == 0 and len(final_orders) == 0:
|
||||
print(f"✅ 清理完成,无持仓和挂单")
|
||||
else:
|
||||
print(f"⚠️ 清理未完全,请检查")
|
||||
|
||||
def run_all_tests(self, auto_mode: bool = True):
|
||||
"""运行所有测试
|
||||
|
||||
Args:
|
||||
auto_mode: 自动模式,不等待用户确认(默认True)
|
||||
"""
|
||||
print("\n" + "="*60)
|
||||
print(" Hyperliquid 小资金测试")
|
||||
if auto_mode:
|
||||
print(" 自动模式: 将直接执行所有测试")
|
||||
else:
|
||||
print(" 交互模式: 每步需要确认")
|
||||
print("="*60)
|
||||
|
||||
def wait_for_confirmation(msg: str):
|
||||
"""等待用户确认(仅在非自动模式下)"""
|
||||
if not auto_mode:
|
||||
try:
|
||||
input(f"\n⚠️ {msg}\n按 Enter 继续,Ctrl+C 取消...")
|
||||
except EOFError:
|
||||
print("⚠️ 检测到非交互环境,切换到自动模式")
|
||||
return True
|
||||
return True
|
||||
|
||||
try:
|
||||
# 测试1:账户状态
|
||||
self.test_1_get_account_state()
|
||||
|
||||
# 测试2:市价单
|
||||
wait_for_confirmation("即将测试市价单(小金额)")
|
||||
self.test_2_market_order(symbol="BTC", is_buy=True, size=0.001)
|
||||
|
||||
# 测试3:限价单
|
||||
wait_for_confirmation("即将测试限价单(小金额)")
|
||||
self.test_3_limit_order(symbol="BTC", is_buy=False, size=0.001)
|
||||
|
||||
# 测试4:查询止盈止损
|
||||
self.test_4_check_tp_sl(symbol="BTC")
|
||||
|
||||
# 测试5:清理
|
||||
wait_for_confirmation("即将清理测试(平仓、取消挂单)")
|
||||
self.test_5_cleanup(symbol="BTC")
|
||||
|
||||
print("\n" + "="*60)
|
||||
print(" ✅ 所有测试完成")
|
||||
print("="*60)
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print("\n\n⚠️ 测试被用户中断")
|
||||
print("🧹 执行清理...")
|
||||
|
||||
# 尝试清理
|
||||
self.test_5_cleanup(symbol="BTC")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"❌ 测试出错: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
# 尝试清理
|
||||
print("\n🧹 执行清理...")
|
||||
self.test_5_cleanup(symbol="BTC")
|
||||
|
||||
|
||||
def main():
|
||||
"""主函数"""
|
||||
tester = HyperliquidTester()
|
||||
tester.run_all_tests()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Loading…
Reference in New Issue
Block a user