Files
ccxt_with_mt5/test/atest_mt5_integration.py
lz_db 0fab423a18 add
2025-11-16 12:31:03 +08:00

529 lines
18 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.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import asyncio
import logging
import sys
import os
from datetime import datetime
# 添加 ccxt 路径
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from ccxt import mt5 as mt5_sync
from ccxt.async_support import mt5 as mt5_async
from ccxt.pro import mt5 as mt5_pro
from ccxt.base.errors import ExchangeError, AuthenticationError, InvalidOrder
# 配置日志
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
handlers=[
logging.StreamHandler(),
logging.FileHandler('mt5_test.log', encoding='utf-8')
]
)
logger = logging.getLogger('MT5-Test')
# 测试配置
TEST_CONFIG = {
'apiKey': '76888962',
'secret': 'LZ-trade666888',
'verbose': False, # 启用详细日志
'hostname': '43.167.188.220:5000',
'host': '18.163.85.196',
'port': 443,
}
class MT5SyncTest:
"""同步版本测试"""
def __init__(self):
self.exchange = mt5_sync(TEST_CONFIG)
self.test_results = {}
def run_all_tests(self):
"""运行所有同步测试"""
logger.info("🚀 开始同步版本测试")
tests = [
self.test_connection,
self.test_token_management,
self.test_markets,
self.test_balance,
self.test_ticker,
self.test_account_details,
self.test_orders,
self.test_order_operations,
]
for test in tests:
try:
test_name = test.__name__
logger.info(f"\n{'='*50}")
logger.info(f"执行测试: {test_name}")
logger.info(f"{'='*50}")
result = test()
self.test_results[test_name] = result
status = "✅ 通过" if result else "❌ 失败"
logger.info(f"{status} - {test_name}")
except Exception as e:
logger.error(f"❌ 测试失败 {test.__name__}: {e}")
self.test_results[test.__name__] = False
self.print_test_summary()
return self.test_results
def test_connection(self):
"""测试连接"""
try:
logger.info("测试服务器连接...")
result = self.exchange.ping_server()
logger.info(f"Ping 结果: {result}")
return result is True
except Exception as e:
logger.error(f"连接测试失败: {e}")
return False
def test_token_management(self):
"""测试 Token 管理"""
try:
logger.info("测试 Token 获取...")
token = self.exchange.get_token()
logger.info(f"获取到 Token: {token}")
# logger.info("测试连接检查...")
# check_result = self.exchange.check_connect()
# logger.info(f"连接检查: {check_result}")
logger.info("测试服务器时区...")
timezone = self.exchange.server_timezone()
logger.info(f"服务器时区: {timezone}")
return token is not None and timezone is not None
except Exception as e:
logger.error(f"Token 管理测试失败: {e}")
return False
def test_markets(self):
"""测试市场数据"""
try:
logger.info("获取交易对列表...")
markets = self.exchange.fetch_markets()
logger.info(f"获取到 {len(markets)} 个交易对")
# 显示前5个交易对
for i, market in enumerate(markets[:5]):
logger.info(f" {i+1}. {market['symbol']} - {market['base']}/{market['quote']}")
if len(markets) > 5:
logger.info(f" ... 还有 {len(markets) - 5} 个交易对")
return len(markets) > 0
except Exception as e:
logger.error(f"市场数据测试失败: {e}")
return False
def test_balance(self):
"""测试余额查询"""
try:
logger.info("获取账户余额...")
balance = self.exchange.fetch_balance()
logger.info(f"余额信息: {balance}")
if 'USDT' in balance['total']:
total = balance['total']['USDT']
free = balance['free']['USDT']
used = balance['used']['USDT']
logger.info(f"USDT 余额 - 总额: {total}, 可用: {free}, 占用: {used}")
return balance is not None
except Exception as e:
logger.error(f"余额查询测试失败: {e}")
return False
def test_ticker(self):
"""测试行情数据"""
try:
symbols_to_test = ['EUR/USD', 'GBP/USD', 'USD/JPY']
for symbol in symbols_to_test:
try:
logger.info(f"获取 {symbol} 行情...")
ticker = self.exchange.fetch_ticker(symbol)
if ticker:
logger.info(f" {symbol}: 买={ticker['bid']}, 卖={ticker['ask']}, 最后={ticker['last']}")
else:
logger.warning(f" 无法获取 {symbol} 行情")
except Exception as e:
logger.warning(f" 获取 {symbol} 行情失败: {e}")
# 测试批量获取行情
logger.info("测试批量获取行情...")
tickers = self.exchange.fetch_tickers(['EUR/USD', 'GBP/USD'])
logger.info(f"批量获取到 {len(tickers)} 个行情")
return True
except Exception as e:
logger.error(f"行情数据测试失败: {e}")
return False
def test_account_details(self):
"""测试账户详情"""
try:
logger.info("获取账户详情...")
account_details = self.exchange.fetch_account_details()
logger.info(f"账户详情: {account_details}")
required_fields = ['serverName', 'user', 'currency', 'accountLeverage']
for field in required_fields:
if field in account_details:
logger.info(f" {field}: {account_details[field]}")
return account_details is not None
except Exception as e:
logger.error(f"账户详情测试失败: {e}")
return False
def test_orders(self):
"""测试订单查询"""
try:
logger.info("获取未平仓订单...")
open_orders = self.exchange.fetch_open_orders()
logger.info(f"未平仓订单数量: {len(open_orders)}")
for order in open_orders[:3]: # 显示前3个订单
logger.info(f" 订单 {order['id']}: {order['symbol']} {order['side']} {order['type']} {order['status']}")
logger.info("获取已平仓订单...")
closed_orders = self.exchange.fetch_closed_orders()
logger.info(f"已平仓订单数量: {len(closed_orders)}")
return True
except Exception as e:
logger.error(f"订单查询测试失败: {e}")
return False
def test_order_operations(self):
"""测试订单操作(只测试不实际下单)"""
try:
logger.info("测试订单创建参数验证...")
# 测试市价单参数
market_order_params = {
'symbol': 'EUR/USD',
'type': 'market',
'side': 'buy',
'amount': 0.01,
}
# 测试限价单参数
limit_order_params = {
'symbol': 'EUR/USD',
'type': 'limit',
'side': 'buy',
'amount': 0.01,
'price': 1.0800,
}
logger.info("✅ 订单参数验证通过")
logger.info("注意: 实际下单测试需要在真实环境中进行")
return True
except Exception as e:
logger.error(f"订单操作测试失败: {e}")
return False
def print_test_summary(self):
"""打印测试总结"""
logger.info("\n" + "="*60)
logger.info("📊 同步版本测试总结")
logger.info("="*60)
passed = sum(1 for result in self.test_results.values() if result)
total = len(self.test_results)
for test_name, result in self.test_results.items():
status = "✅ 通过" if result else "❌ 失败"
logger.info(f" {test_name}: {status}")
logger.info(f"\n总体结果: {passed}/{total} 通过")
if passed == total:
logger.info("🎉 所有测试通过!")
else:
logger.info("⚠️ 部分测试失败,请检查日志")
class MT5AsyncTest:
"""异步版本测试"""
def __init__(self):
self.exchange = None
self.test_results = {}
async def initialize(self):
"""初始化异步交易所"""
self.exchange = mt5_async(TEST_CONFIG)
async def run_all_tests(self):
"""运行所有异步测试"""
logger.info("\n🚀 开始异步版本测试")
await self.initialize()
tests = [
self.test_connection_async,
self.test_markets_async,
self.test_balance_async,
self.test_ticker_async,
self.test_orders_async,
]
for test in tests:
try:
test_name = test.__name__
logger.info(f"\n{'='*50}")
logger.info(f"执行测试: {test_name}")
logger.info(f"{'='*50}")
result = await test()
self.test_results[test_name] = result
status = "✅ 通过" if result else "❌ 失败"
logger.info(f"{status} - {test_name}")
except Exception as e:
logger.error(f"❌ 测试失败 {test.__name__}: {e}")
self.test_results[test.__name__] = False
await self.exchange.close()
self.print_test_summary()
return self.test_results
async def test_connection_async(self):
"""测试异步连接"""
try:
logger.info("测试异步 Token 获取...")
token = await self.exchange.get_token()
logger.info(f"获取到 Token: {token}")
return token is not None
except Exception as e:
logger.error(f"异步连接测试失败: {e}")
return False
async def test_markets_async(self):
"""测试异步市场数据"""
try:
logger.info("获取异步交易对列表...")
markets = await self.exchange.fetch_markets()
logger.info(f"获取到 {len(markets)} 个交易对")
return len(markets) > 0
except Exception as e:
logger.error(f"异步市场数据测试失败: {e}")
return False
async def test_balance_async(self):
"""测试异步余额查询"""
try:
logger.info("获取异步账户余额...")
balance = await self.exchange.fetch_balance()
logger.info(f"余额信息: {balance}")
return balance is not None
except Exception as e:
logger.error(f"异步余额查询测试失败: {e}")
return False
async def test_ticker_async(self):
"""测试异步行情数据"""
try:
logger.info("获取异步行情...")
ticker = await self.exchange.fetch_ticker('EUR/USD')
logger.info(f"EUR/USD: 买={ticker['bid']}, 卖={ticker['ask']}")
return ticker is not None
except Exception as e:
logger.error(f"异步行情数据测试失败: {e}")
return False
async def test_orders_async(self):
"""测试异步订单查询"""
try:
logger.info("获取异步未平仓订单...")
open_orders = await self.exchange.fetch_open_orders()
logger.info(f"未平仓订单数量: {len(open_orders)}")
return True
except Exception as e:
logger.error(f"异步订单查询测试失败: {e}")
return False
def print_test_summary(self):
"""打印测试总结"""
logger.info("\n" + "="*60)
logger.info("📊 异步版本测试总结")
logger.info("="*60)
passed = sum(1 for result in self.test_results.values() if result)
total = len(self.test_results)
for test_name, result in self.test_results.items():
status = "✅ 通过" if result else "❌ 失败"
logger.info(f" {test_name}: {status}")
logger.info(f"\n总体结果: {passed}/{total} 通过")
class MT5WebSocketTest:
"""WebSocket 测试"""
def __init__(self):
self.exchange = None
self.received_messages = []
async def initialize(self):
"""初始化 WebSocket 交易所"""
self.exchange = mt5_pro(TEST_CONFIG)
async def test_websocket_orders(self):
"""测试 WebSocket 订单监听"""
try:
await self.initialize()
logger.info("\n🔌 开始 WebSocket 订单监听测试")
logger.info("监听订单更新...")
# 设置超时
timeout = 30 # 30秒后停止监听
async def order_listener():
try:
orders = await self.exchange.watch_orders()
logger.info(f"📦 收到订单更新: {len(orders)} 个订单")
for order in orders:
logger.info(f" 订单 {order['id']}: {order['symbol']} {order['side']} {order['status']}")
self.received_messages.append({
'type': 'order',
'data': order,
'timestamp': datetime.now()
})
except Exception as e:
logger.error(f"订单监听错误: {e}")
# 运行监听器
await asyncio.wait_for(order_listener(), timeout=timeout)
return len(self.received_messages) > 0
except asyncio.TimeoutError:
logger.info("⏰ WebSocket 监听超时(正常结束)")
return len(self.received_messages) > 0
except Exception as e:
logger.error(f"WebSocket 测试失败: {e}")
return False
finally:
if self.exchange:
await self.exchange.close()
async def test_websocket_balance(self):
"""测试 WebSocket 余额监听"""
try:
await self.initialize()
logger.info("\n💰 开始 WebSocket 余额监听测试")
logger.info("监听余额更新...")
timeout = 20 # 20秒后停止监听
async def balance_listener():
try:
balance = await self.exchange.watch_balance()
logger.info(f"💰 收到余额更新")
logger.info(f" 余额信息: {balance}")
self.received_messages.append({
'type': 'balance',
'data': balance,
'timestamp': datetime.now()
})
except Exception as e:
logger.error(f"余额监听错误: {e}")
await asyncio.wait_for(balance_listener(), timeout=timeout)
return len([msg for msg in self.received_messages if msg['type'] == 'balance']) > 0
except asyncio.TimeoutError:
logger.info("⏰ WebSocket 余额监听超时(正常结束)")
return len([msg for msg in self.received_messages if msg['type'] == 'balance']) > 0
except Exception as e:
logger.error(f"WebSocket 余额测试失败: {e}")
return False
finally:
if self.exchange:
await self.exchange.close()
async def main():
"""主测试函数"""
print("="*70)
print("MT5 集成测试套件")
print("="*70)
# 同步测试
sync_test = MT5SyncTest()
sync_results = sync_test.run_all_tests()
# 异步测试
async_test = MT5AsyncTest()
async_results = await async_test.run_all_tests()
# WebSocket 测试
ws_test = MT5WebSocketTest()
print("\n" + "="*70)
print("🔌 WebSocket 测试(需要实际交易活动)")
print("="*70)
ws_order_result = await ws_test.test_websocket_orders()
ws_balance_result = await ws_test.test_websocket_balance()
# 最终总结
print("\n" + "="*70)
print("🎯 最终测试总结")
print("="*70)
sync_passed = sum(1 for result in sync_results.values() if result)
async_passed = sum(1 for result in async_results.values() if result)
print(f"同步版本: {sync_passed}/{len(sync_results)} 通过")
print(f"异步版本: {async_passed}/{len(async_results)} 通过")
print(f"WebSocket 订单: {'✅ 通过' if ws_order_result else '❌ 失败'}")
print(f"WebSocket 余额: {'✅ 通过' if ws_balance_result else '❌ 失败'}")
total_tests = len(sync_results) + len(async_results) + 2
total_passed = sync_passed + async_passed + ws_order_result + ws_balance_result
print(f"\n总体结果: {total_passed}/{total_tests} 通过")
if total_passed == total_tests:
print("🎉 所有测试通过MT5 集成工作正常")
else:
print("⚠️ 部分测试失败,请检查日志文件 'mt5_test.log'")
if __name__ == "__main__":
try:
asyncio.run(main())
except KeyboardInterrupt:
print("\n👋 用户中断测试")
except Exception as e:
print(f"❌ 测试执行错误: {e}")
import traceback
traceback.print_exc()