473 lines
15 KiB
Python
473 lines
15 KiB
Python
"""
|
||
测试 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()
|