""" Bitget 真实 API 集成测试 ⚠️ 警告:此测试会使用真实 API 调用和真实订单! - 确保使用测试网或接受小额手续费 - 市价单会立即成交,产生实际盈亏 - 测试后检查是否有残留订单/持仓 运行方式: cd backend source venv/bin/activate python3 tests/test_bitget_live_integration.py """ import os import sys import time from datetime import datetime # 添加项目路径 sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from dotenv import load_dotenv load_dotenv('/Users/aaron/source_code/Stock_Agent/.env') from app.services.bitget_trading_api_sdk import BitgetTradingAPI from app.services.bitget_live_trading_service import BitgetLiveTradingService class BitgetIntegrationTest: """Bitget 真实 API 集成测试""" def __init__(self): self.api_key = os.getenv('BITGET_API_KEY') self.api_secret = os.getenv('BITGET_API_SECRET') self.passphrase = os.getenv('BITGET_PASSPHRASE') self.use_testnet = os.getenv('BITGET_USE_TESTNET', 'true').lower() == 'true' if not all([self.api_key, self.api_secret, self.passphrase]): raise ValueError("❌ 请在 .env 中配置 BITGET_API_KEY, BITGET_API_SECRET, BITGET_PASSPHRASE") print(f"\n{'='*60}") print(f"Bitget 真实 API 集成测试") print(f"{'='*60}") print(f"API Key: {self.api_key[:10]}...") print(f"测试网模式: {self.use_testnet}") print(f"时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}") print(f"{'='*60}\n") # 初始化 API self.api = BitgetTradingAPI( api_key=self.api_key, api_secret=self.api_secret, passphrase=self.passphrase, use_testnet=self.use_testnet ) # 初始化 Service self.service = BitgetLiveTradingService.__new__(BitgetLiveTradingService) self.service.trading_api = self.api self.service.max_single_position = 100 self.service.max_total_leverage = 5 self.service.circuit_breaker_drawdown = 0.10 self.service._initial_account_state = None self.service.initial_balance = None # 添加这个属性 # 测试状态 self.limit_order_id = None self.limit_order_symbol = None self.market_entry_price = 0.0 # ==================== 基础连接测试 ==================== def test_01_connection(self): """测试 1: API 连接""" print("\n📡 测试 1: API 连接") print("-" * 40) try: # 获取服务器时间 server_time = self.api.exchange.fetch_time() print(f"✅ 服务器时间: {datetime.fromtimestamp(server_time/1000)}") # 加载市场信息 markets = self.api.exchange.load_markets() btc_market = markets.get('BTC/USDT:USDT', {}) print(f"✅ BTC 市场ID: {btc_market.get('id', 'N/A')}") return True except Exception as e: print(f"❌ 连接失败: {e}") return False def test_02_get_balance(self): """测试 2: 获取账户余额""" print("\n💰 测试 2: 获取账户余额") print("-" * 40) try: balance = self.api.get_balance() if balance: usdt = balance.get('USDT', {}) print(f"✅ USDT 可用: {usdt.get('free', 0):.2f}") print(f"✅ USDT 冻结: {usdt.get('used', 0):.2f}") print(f"✅ USDT 总额: {usdt.get('total', 0):.2f}") return True else: print("❌ 余额为空") return False except Exception as e: print(f"❌ 获取余额失败: {e}") return False def test_03_get_positions(self): """测试 3: 获取当前持仓""" print("\n📊 测试 3: 获取当前持仓") print("-" * 40) try: positions = self.api.get_position() if positions: active_positions = [p for p in positions if float(p.get('contracts', 0)) > 0] print(f"✅ 当前持仓数: {len(active_positions)}") for pos in active_positions: symbol = pos.get('symbol', 'N/A') contracts = float(pos.get('contracts', 0)) side = pos.get('side', 'N/A') entry = float(pos.get('entryPrice', 0)) print(f" - {symbol}: {side} {contracts} 张 @ ${entry:,.2f}") else: print("✅ 当前无持仓") return True except Exception as e: print(f"❌ 获取持仓失败: {e}") return False def test_04_set_leverage(self): """测试 4: 设置杠杆""" print("\n⚙️ 测试 4: 设置杠杆") print("-" * 40) try: symbol = 'BTCUSDT' leverage = 5 self.api.set_leverage(symbol, leverage) print(f"✅ {symbol} 杠杆已设置为 {leverage}x") return True except Exception as e: print(f"❌ 设置杠杆失败: {e}") return False # ==================== 限价单测试 ==================== def test_05_place_limit_order(self): """测试 5: 下限价单(挂单,预期不成交)""" print("\n📝 测试 5: 下限价单") print("-" * 40) try: symbol = 'BTCUSDT' # 获取当前价格 ticker = self.api.exchange.fetch_ticker('BTC/USDT:USDT') current_price = ticker['last'] print(f" 当前 BTC 价格: ${current_price:,.2f}") # 计算挂单价格(当前价格 - 5%,预期不会成交) limit_price = current_price * 0.95 print(f" 挂单价格: ${limit_price:,.2f} (低 5%,预期不成交)") # 下 1 张多单(最小单位) order = self.api.place_order( symbol=symbol, side='buy', order_type='limit', size=1, # 1 张 = 0.01 BTC price=limit_price ) if order: self.limit_order_id = order.get('id') self.limit_order_symbol = symbol print(f"✅ 限价单已下: {self.limit_order_id}") print(f" 订单状态: {order.get('status')}") return True else: print("❌ 下单返回空") return False except Exception as e: print(f"❌ 下限价单失败: {e}") return False def test_06_get_open_orders(self): """测试 6: 查询挂单""" print("\n📋 测试 6: 查询挂单") print("-" * 40) try: symbol = self.limit_order_symbol or 'BTCUSDT' orders = self.api.get_open_orders(symbol) if orders: print(f"✅ 当前挂单数: {len(orders)}") for order in orders: order_id = order.get('id', 'N/A') side = order.get('side', 'N/A') price = order.get('price', 0) amount = order.get('amount', 0) print(f" - {order_id}: {side} {amount} @ ${price:,.2f}") else: print("⚠️ 无挂单") return True except Exception as e: print(f"❌ 查询挂单失败: {e}") return False def test_07_cancel_order(self): """测试 7: 撤销挂单""" print("\n❌ 测试 7: 撤销挂单") print("-" * 40) try: if not self.limit_order_id: print("⚠️ 没有需要撤销的订单") return True # 使用 cancel_all_orders 撤销该 symbol 所有挂单 success = self.api.cancel_all_orders(self.limit_order_symbol) if success: print(f"✅ 已撤销 {self.limit_order_symbol} 的所有挂单") self.limit_order_id = None return True else: print(f"❌ 撤单失败") return False except Exception as e: print(f"❌ 撤销订单失败: {e}") return False # ==================== 市价单测试(真实成交!)==================== def test_08_market_order_open(self): """测试 8: 市价开多单(真实成交!)""" print("\n⚡ 测试 8: 市价开多单") print("-" * 40) print("⚠️ 警告:此测试会产生真实订单和手续费!") try: symbol = 'BTCUSDT' # 获取当前价格 ticker = self.api.exchange.fetch_ticker('BTC/USDT:USDT') current_price = ticker['last'] print(f" 当前 BTC 价格: ${current_price:,.2f}") # 下 1 张市价多单(最小单位 = 0.01 BTC) print(f" 下市价多单: 1 张 (0.01 BTC)...") order = self.api.place_order( symbol=symbol, side='buy', order_type='market', size=1 # 1 张 = 0.01 BTC ) if order: order_id = order.get('id') status = order.get('status') # 安全获取 average 价格 avg_price = order.get('average') or order.get('price') or 0 if avg_price: avg_price = float(avg_price) print(f"✅ 市价单已成交: {order_id}") print(f" 订单状态: {status}") print(f" 成交均价: ${avg_price:,.2f}") print(f" 成交数量: {order.get('filled', 0)} 张") # 保存入场价 self.market_entry_price = avg_price return True else: print("❌ 下单返回空") return False except Exception as e: print(f"❌ 市价开仓失败: {e}") import traceback traceback.print_exc() return False def test_09_verify_position_opened(self): """测试 9: 验证持仓已开启""" print("\n🔍 测试 9: 验证持仓") print("-" * 40) try: positions = self.api.get_position('BTCUSDT') if positions: for pos in positions: contracts = float(pos.get('contracts', 0)) if contracts > 0: side = pos.get('side', 'N/A') entry_price = float(pos.get('entryPrice', 0)) unrealized_pnl = float(pos.get('unrealizedPnl', 0)) print(f"✅ 持仓已确认:") print(f" 方向: {side}") print(f" 数量: {contracts} 张") print(f" 开仓价: ${entry_price:,.2f}") print(f" 未实现盈亏: ${unrealized_pnl:,.2f}") return True print("❌ 未找到持仓") return False except Exception as e: print(f"❌ 验证持仓失败: {e}") return False def test_10_set_tp_sl(self): """测试 10: 设置止盈止损""" print("\n🎯 测试 10: 设置止盈止损") print("-" * 40) try: # 获取当前价格 ticker = self.api.exchange.fetch_ticker('BTC/USDT:USDT') current_price = ticker['last'] # 设置止盈(+1%)和止损(-0.5%) tp_price = current_price * 1.01 # +1% sl_price = current_price * 0.995 # -0.5% print(f" 当前价格: ${current_price:,.2f}") print(f" 设置止盈: ${tp_price:,.2f} (+1%)") print(f" 设置止损: ${sl_price:,.2f} (-0.5%)") result = self.api.modify_sl_tp( symbol='BTCUSDT', stop_loss=sl_price, take_profit=tp_price ) if result: print(f"✅ 止盈止损已设置") else: print(f"⚠️ 止盈止损设置返回 False") return True # 无论成功与否都继续测试 except Exception as e: print(f"⚠️ 设置止盈止损失败: {e}") return True # 不算失败,继续测试 def test_11_market_order_close(self): """测试 11: 市价平仓""" print("\n🔒 测试 11: 市价平仓") print("-" * 40) try: # 获取当前价格 ticker = self.api.exchange.fetch_ticker('BTC/USDT:USDT') current_price = ticker['last'] print(f" 当前 BTC 价格: ${current_price:,.2f}") # 先撤销止盈止损单 try: self.api.cancel_all_orders('BTCUSDT') print(" 已撤销止盈止损单") except: pass # 使用 close_position 方法平仓 print(f" 下市价平仓单...") order = self.api.close_position( symbol='BTCUSDT', side='sell', # 平多 size=0.01, # 0.01 张 ) if order: order_id = order.get('id') avg_price = order.get('average') or order.get('price') or 0 if avg_price: avg_price = float(avg_price) print(f"✅ 平仓成功: {order_id}") print(f" 平仓均价: ${avg_price:,.2f}") # 计算盈亏 if self.market_entry_price > 0 and avg_price > 0: pnl = (avg_price - self.market_entry_price) * 0.01 # 1张 = 0.01 BTC pnl_pct = (avg_price - self.market_entry_price) / self.market_entry_price * 100 print(f" 实现盈亏: ${pnl:,.2f} ({pnl_pct:+.2f}%)") return True else: print("❌ 平仓返回空") return False except Exception as e: print(f"❌ 市价平仓失败: {e}") import traceback traceback.print_exc() return False def test_12_verify_position_closed(self): """测试 12: 验证持仓已平""" print("\n✅ 测试 12: 验证持仓已平") print("-" * 40) try: positions = self.api.get_position('BTCUSDT') has_position = False if positions: for pos in positions: if float(pos.get('contracts', 0)) > 0: has_position = True print(f"⚠️ 仍有持仓: {pos.get('contracts')} 张") if not has_position: print("✅ 持仓已全部平仓") return True else: print("❌ 持仓未完全平仓") return False except Exception as e: print(f"❌ 验证平仓失败: {e}") return False # ==================== Service 层测试 ==================== def test_13_service_get_account_state(self): """测试 13: Service 层获取账户状态""" print("\n🏦 测试 13: Service 获取账户状态") print("-" * 40) try: 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}") return True except Exception as e: print(f"❌ 获取账户状态失败: {e}") import traceback traceback.print_exc() return False def test_14_service_get_positions(self): """测试 14: Service 层获取持仓""" print("\n📈 测试 14: Service 获取持仓") print("-" * 40) try: positions = self.service.get_open_positions() if positions: print(f"✅ 当前持仓数: {len(positions)}") for pos in positions: # 安全获取字段 symbol = pos.get('symbol', 'N/A') side = pos.get('side', 'N/A') size = pos.get('size', 0) entry = pos.get('entry_price', 0) print(f" - {symbol}: {side} {size} @ ${entry:,.2f}") else: print("✅ 当前无持仓") return True except Exception as e: print(f"❌ 获取持仓失败: {e}") import traceback traceback.print_exc() return False def test_15_service_contract_size(self): """测试 15: Service 合约面值""" print("\n📐 测试 15: 合约面值换算") print("-" * 40) try: # 测试已知币种 btc_size = self.service.get_contract_size('BTC') eth_size = self.service.get_contract_size('ETH') sol_size = self.service.get_contract_size('SOL') print(f"✅ BTC 合约面值: {btc_size} BTC/张") print(f"✅ ETH 合约面值: {eth_size} ETH/张") print(f"✅ SOL 合约面值: {sol_size} SOL/张") # 测试币数转合约数 contracts = self.service.coins_to_contracts('BTC', 0.05) # 0.05 BTC print(f"✅ 0.05 BTC = {contracts} 张") return True except Exception as e: print(f"❌ 合约面值测试失败: {e}") return False def test_16_risk_check(self): """测试 16: 风控检查""" print("\n⚠️ 测试 16: 风控检查") print("-" * 40) try: result = self.service.check_risk_limits() if result['allowed']: print(f"✅ 风控检查通过") else: print(f"⚠️ 风控检查未通过: {result['reason']}") return True except Exception as e: print(f"⚠️ 风控检查异常: {e}") return True # 不算失败 # ==================== 清理和运行 ==================== def cleanup(self): """清理测试产生的订单和持仓""" print("\n🧹 清理测试订单...") print("-" * 40) try: # 撤销所有 BTC 挂单 success = self.api.cancel_all_orders('BTCUSDT') if success: print("✅ 已撤销所有 BTC 挂单") else: print("⚠️ 撤销 BTC 挂单返回 False(可能无挂单)") # 检查是否有残留持仓 positions = self.api.get_position('BTCUSDT') if positions: for pos in positions: contracts = float(pos.get('contracts', 0)) if contracts > 0: symbol = pos.get('symbol', '') print(f"⚠️ 发现残留持仓: {symbol} {contracts} 张") print(f" 请手动平仓!") except Exception as e: print(f"⚠️ 清理失败: {e}") def run_all_tests(self): """运行所有测试""" tests = [ self.test_01_connection, self.test_02_get_balance, self.test_03_get_positions, self.test_04_set_leverage, self.test_05_place_limit_order, self.test_06_get_open_orders, self.test_07_cancel_order, self.test_08_market_order_open, self.test_09_verify_position_opened, self.test_10_set_tp_sl, self.test_11_market_order_close, self.test_12_verify_position_closed, self.test_13_service_get_account_state, self.test_14_service_get_positions, self.test_15_service_contract_size, self.test_16_risk_check, ] results = [] for test in tests: try: result = test() results.append((test.__name__, result)) time.sleep(0.5) # 避免触发频率限制 except Exception as e: print(f"\n❌ 测试异常: {e}") import traceback traceback.print_exc() results.append((test.__name__, False)) # 清理 self.cleanup() # 汇总 print("\n" + "=" * 60) print("测试结果汇总") print("=" * 60) passed = sum(1 for _, r in results if r) total = len(results) for name, result in results: status = "✅ PASS" if result else "❌ FAIL" print(f"{status}: {name}") print(f"\n总计: {passed}/{total} 通过") print("=" * 60) return passed == total if __name__ == '__main__': print("\n" + "!" * 60) print("⚠️ 警告:此测试将使用真实 API 调用!") print("!" * 60) print("请确认:") print("1. 你已了解这会产生真实 API 调用") print("2. 你使用的是测试网或接受小额手续费") print("3. 市价单会立即成交,产生实际盈亏") print("4. 测试后请检查是否有残留订单/持仓") print("!" * 60) confirm = input("\n是否继续?(yes/no): ") if confirm.lower() != 'yes': print("已取消测试") sys.exit(0) try: tester = BitgetIntegrationTest() success = tester.run_all_tests() sys.exit(0 if success else 1) except Exception as e: print(f"\n❌ 测试失败: {e}") import traceback traceback.print_exc() sys.exit(1)