stock-ai-agent/backend/test_bitget_trading.py
2026-02-26 23:31:29 +08:00

473 lines
15 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
测试 Bitget 实盘交易 API
运行前请确保已在 .env 中配置:
- bitget_api_key
- bitget_api_secret
- bitget_passphrase
- bitget_use_testnet = true (建议先在测试网测试)
"""
import sys
import os
import asyncio
# 添加项目路径
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
from app.services.bitget_trading_api_sdk import BitgetTradingAPI
from app.config import get_settings
def print_section(title: str):
"""打印分节标题"""
print("\n" + "=" * 60)
print(f" {title}")
print("=" * 60)
def test_connection(api: BitgetTradingAPI):
"""测试 API 连接"""
print_section("1. 测试 API 连接")
if api.test_connection():
print("✅ API 连接成功")
return True
else:
print("❌ API 连接失败")
return False
def test_balance(api: BitgetTradingAPI):
"""测试查询余额"""
print_section("2. 查询账户余额")
balance = api.get_balance()
if balance:
print(f"✅ 查询余额成功")
for currency, info in balance.items():
available = info.get('available', '0')
frozen = info.get('frozen', '0')
print(f" {currency}: 可用={available}, 冻结={frozen}")
return True
else:
print("❌ 查询余额失败")
return False
def test_position(api: BitgetTradingAPI, symbol: str = "BTCUSDT"):
"""测试查询持仓"""
print_section(f"3. 查询持仓 ({symbol})")
positions = api.get_position(symbol)
if positions is not None:
if positions:
print(f"✅ 查询到 {len(positions)} 个持仓")
for pos in positions:
pos_side = pos.get('side', 'N/A')
contracts = pos.get('contracts', 0)
unrealized_pnl = pos.get('unrealizedPnl', 0)
print(f" {pos_side}: {contracts}张, 未实现盈亏={unrealized_pnl}")
else:
print("✅ 当前无持仓")
return True
else:
print("❌ 查询持仓失败")
return False
def test_leverage(api: BitgetTradingAPI, symbol: str = "BTCUSDT", leverage: int = 10):
"""测试设置杠杆"""
print_section(f"4. 设置杠杆 ({symbol} {leverage}x)")
if api.set_leverage(symbol, leverage):
print(f"✅ 设置杠杆成功: {leverage}x")
return True
else:
print(f"❌ 设置杠杆失败")
return False
def test_open_orders(api: BitgetTradingAPI):
"""测试查询挂单"""
print_section("5. 查询当前挂单")
orders = api.get_open_orders()
if orders is not None:
if orders:
print(f"✅ 查询到 {len(orders)} 个挂单")
for order in orders[:5]: # 只显示前5个
order_id = order.get('id', 'N/A')
symbol = order.get('symbol', 'N/A')
side = order.get('side', 'N/A')
amount = order.get('amount', 'N/A')
price = order.get('price', 'N/A')
print(f" {order_id}: {symbol} {side} {amount} @ {price}")
else:
print("✅ 当前无挂单")
return True
else:
print("❌ 查询挂单失败")
return False
def test_place_order(api: BitgetTradingAPI, symbol: str = "BTCUSDT",
side: str = "buy", usdt_value: float = 100,
order_type: str = "market", leverage: int = 20, price: float = None):
"""
测试下单(按 USDT 价值)
Args:
api: Bitget API 实例
symbol: 交易对
side: 买卖方向
usdt_value: 下单的 USDT 价值
order_type: 订单类型
leverage: 杠杆倍数
price: 限价单价格(可选)
"""
print_section(f"6. 下单测试 ({symbol} {side} 价值${usdt_value}, {leverage}x杠杆)")
settings = get_settings()
# 先设置杠杆
print(f"设置杠杆: {leverage}x")
api.set_leverage(symbol, leverage)
# 获取当前价格
import ccxt
ccxt_symbol = api._standardize_symbol(symbol)
ticker = api.exchange.fetch_ticker(ccxt_symbol)
current_price = ticker['last'] if ticker else None
if not current_price:
print("❌ 无法获取当前价格")
return None
print(f"当前价格: ${current_price:.2f}")
# 选择订单类型
print("\n选择订单类型:")
print(" 1. 市价单 (立即成交)")
print(" 2. 限价单 (挂单,可测试撤单)")
order_type_choice = input("请选择 (1-2, 默认2): ").strip()
if order_type_choice == '1':
order_type = 'market'
limit_price = None
else:
order_type = 'limit'
# 限价单需要输入价格
price_input = input("挂单价格 (留空使用默认远离市价的价格): ").strip()
if price_input:
limit_price = float(price_input)
else:
# 默认设置一个远离当前价的价格,确保不会立即成交
if side == 'buy':
limit_price = current_price * 0.95 # 买单价格低于市价5%
print(f"默认买单价格: ${limit_price:.2f} (低于市价5%)")
else:
limit_price = current_price * 1.05 # 卖单价格高于市价5%
print(f"默认卖单价格: ${limit_price:.2f} (高于市价5%)")
# 计算合约数量
# Bitget 合约规格: BTC/USDT 每张 0.01 BTC, ETH/USDT 每张 0.1 ETH
if 'BTC' in symbol:
contract_size = 0.01 # 每张 0.01 BTC
min_size = 1 # Bitget 最小 1 张
coin_price = current_price
elif 'ETH' in symbol:
contract_size = 0.1 # 每张 0.1 ETH
min_size = 1 # Bitget 最小 1 张
coin_price = current_price
else:
contract_size = 1
min_size = 1
coin_price = current_price
# 计算需要的数量 (USDT价值 / (币价 * 合约规格))
size = usdt_value / (coin_price * contract_size)
# 确保满足最小下单量
size = max(size, min_size)
# 计算实际仓位价值和所需保证金
position_value = size * contract_size * coin_price
required_margin = position_value / leverage
print(f"合约规格: {contract_size} 币/张")
print(f"下单数量: {size:.3f}张 (最小{min_size}张)")
print(f"仓位价值: ${position_value:.2f}")
print(f"所需保证金: ${required_margin:.2f} ({leverage}x杠杆)")
print(f"模式: {'测试网' if settings.bitget_use_testnet else '生产网'}")
# 检查余额
balance = api.get_balance()
available = float(balance.get('USDT', {}).get('available', 0))
print(f"账户可用余额: ${available:.2f}")
if required_margin > available:
print(f"\n❌ 余额不足!")
print(f" 需要: ${required_margin:.2f}")
print(f" 可用: ${available:.2f}")
print(f" 缺口: ${required_margin - available:.2f}")
# 建议更高的杠杆
suggested_leverage = int(required_margin / available * leverage) + 1
if suggested_leverage <= 125: # Bitget 最大杠杆
print(f"\n建议: 使用 {suggested_leverage}x 杠杆或减少下单金额")
return None
order_type_text = "限价单" if order_type == "limit" else "市价单"
print(f"订单类型: {order_type_text}")
if price:
print(f"挂单价格: ${price:.2f}")
confirm = input(f"\n⚠️ 即将下单: {symbol} {side} {size:.3f}张 ({order_type_text}, 仓位价值${position_value:.2f}, 保证金${required_margin:.2f}),是否继续?(y/n): ")
if confirm.lower() != 'y':
print("已取消下单")
return None
order = api.place_order(
symbol=symbol,
side=side,
order_type=order_type,
size=size,
price=price
)
if order:
print(f"✅ 下单成功")
print(f" 订单ID: {order.get('id', 'N/A')}")
print(f" 状态: {order.get('status', 'N/A')}")
print(f" 价格: {order.get('price', 'N/A')}")
print(f" 数量: {order.get('amount', 'N/A')}")
fee = order.get('fee')
if fee and isinstance(fee, dict):
print(f" 手续费: {fee.get('cost', 'N/A')}")
else:
print(f" 手续费: N/A")
return order
else:
print("❌ 下单失败")
return None
def test_cancel_order(api: BitgetTradingAPI, symbol: str = "BTCUSDT", order_id: str = None):
"""测试撤单"""
print_section("7. 撤单测试")
if not order_id:
# 如果没有提供订单ID先查询挂单
orders = api.get_open_orders(symbol)
if not orders:
print("⚠️ 无可撤订单")
return None
order_id = orders[0].get('id')
print(f"订单ID: {order_id}")
if api.cancel_order(symbol, order_id=order_id):
print("✅ 撤单成功")
return True
else:
print("❌ 撤单失败")
return False
def test_close_position(api: BitgetTradingAPI, symbol: str = "BTCUSDT"):
"""测试平仓"""
print_section(f"8. 平仓测试 ({symbol})")
positions = api.get_position(symbol)
if not positions:
print("⚠️ 无持仓可平")
return None
print(f"持仓数量: {len(positions)}")
for pos in positions:
pos_side = pos.get('side', 'N/A')
contracts = pos.get('contracts', 0)
print(f" {pos_side}: {contracts}")
confirm = input("\n⚠️ 即将平仓,是否继续?(y/n): ")
if confirm.lower() != 'y':
print("已取消平仓")
return None
result = api.close_position(symbol)
if result:
print("✅ 平仓成功")
return True
else:
print("❌ 平仓失败")
return False
def test_modify_sl_tp(api: BitgetTradingAPI, symbol: str = "BTCUSDT"):
"""测试修改止损止盈"""
print_section(f"9. 修改止损止盈 ({symbol})")
positions = api.get_position(symbol)
if not positions:
print("⚠️ 无持仓,跳过止损止盈测试")
return None
pos = positions[0]
pos_side = pos.get('side')
mark_price = float(pos.get('markPrice', 0))
print(f"当前持仓: {pos_side}, 标记价: {mark_price}")
# 根据持仓方向计算测试用的止损止盈价格
if pos_side == 'long':
test_sl = mark_price * 0.98 # 止损设为标记价的 98%
test_tp = mark_price * 1.02 # 止盈设为标记价的 102%
else:
test_sl = mark_price * 1.02 # 做空止损设为标记价的 102%
test_tp = mark_price * 0.98 # 做空止盈设为标记价的 98%
print(f"测试参数: 止损={test_sl:.2f}, 止盈={test_tp:.2f}")
confirm = input("\n⚠️ 即将设置止损止盈,是否继续?(y/n): ")
if confirm.lower() != 'y':
print("已取消设置")
return None
if api.modify_sl_tp(symbol, stop_loss=test_sl, take_profit=test_tp):
print("✅ 设置止损止盈成功")
return True
else:
print("❌ 设置止损止盈失败")
return False
def test_cancel_all_orders(api: BitgetTradingAPI, symbol: str = "BTCUSDT"):
"""测试撤销所有挂单"""
print_section(f"10. 撤销所有挂单 ({symbol})")
# 先查询挂单
orders = api.get_open_orders(symbol)
if not orders:
print("⚠️ 无挂单可撤")
return None
print(f"当前挂单数: {len(orders)}")
confirm = input("\n⚠️ 即将撤销所有挂单,是否继续?(y/n): ")
if confirm.lower() != 'y':
print("已取消")
return None
if api.cancel_all_orders(symbol):
print("✅ 撤销所有挂单成功")
return True
else:
print("❌ 撤销所有挂单失败")
return False
def main():
"""主测试流程"""
print("\n" + "=" * 60)
print(" Bitget 实盘交易 API 测试")
print("=" * 60)
# 加载配置
settings = get_settings()
# 检查配置
if not settings.bitget_api_key or not settings.bitget_api_secret:
print("❌ 未配置 Bitget API 密钥")
print("请在 .env 文件中设置:")
print(" bitget_api_key=your_api_key")
print(" bitget_api_secret=your_api_secret")
print(" bitget_passphrase=your_passphrase (可选)")
print(" bitget_use_testnet=true/false (测试网/生产网)")
return
# 创建 API 实例
api = BitgetTradingAPI(
api_key=settings.bitget_api_key,
api_secret=settings.bitget_api_secret,
passphrase=settings.bitget_passphrase or "",
use_testnet=settings.bitget_use_testnet
)
mode = '测试网' if settings.bitget_use_testnet else '生产网'
print(f"\n模式: {mode}")
print(f"API Key: {settings.bitget_api_key[:8]}...{settings.bitget_api_key[-4:]}")
try:
# 基础测试(不涉及交易)
if not test_connection(api):
return
test_balance(api)
test_position(api)
test_leverage(api)
test_open_orders(api)
# 交易相关测试(需要确认)
print("\n" + "=" * 60)
print(" 交易功能测试(需要手动确认)")
print("=" * 60)
# 检查用户是否要测试交易功能
test_trading = input("\n是否测试交易功能(下单、平仓等)?(y/n): ")
if test_trading.lower() == 'y':
# 选择交易对
print("\n选择交易对:")
print(" 1. BTCUSDT")
print(" 2. ETHUSDT")
print(" 3. 自定义")
choice = input("请选择 (1-3): ").strip()
if choice == '1':
symbol = 'BTCUSDT'
elif choice == '2':
symbol = 'ETHUSDT'
else:
symbol = input("请输入交易对 (如 BTCUSDT): ").strip().upper()
# 选择买卖方向
direction = input("方向 (buy/sell, 默认buy): ").strip().lower()
side = direction if direction in ['buy', 'sell'] else 'buy'
# 输入 USDT 价值
usdt_input = input("下单价值 (USDT, 默认100): ").strip()
usdt_value = float(usdt_input) if usdt_input else 100
# 输入杠杆倍数
leverage_input = input("杠杆倍数 (默认20, 最大125): ").strip()
leverage = int(leverage_input) if leverage_input else 20
leverage = min(max(leverage, 1), 125) # 限制在 1-125 之间
# 下单测试(函数内部会询问订单类型)
order = test_place_order(api, symbol=symbol, side=side, usdt_value=usdt_value, leverage=leverage)
if order:
import time
print("\n等待 3 秒后撤单...")
time.sleep(3)
# 撤单测试
test_cancel_order(api, symbol, order_id=order.get('id'))
# 平仓测试
test_close_position(api, symbol)
# 止损止盈测试
test_modify_sl_tp(api, symbol)
# 撤销所有挂单
test_cancel_all_orders(api, symbol)
else:
print("跳过交易功能测试")
finally:
# 关闭连接
api.close()
print("\n" + "=" * 60)
print(" 测试完成")
print("=" * 60)
if __name__ == "__main__":
main()