""" 测试 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()