update
This commit is contained in:
parent
2fb2321399
commit
ee54e66dd7
@ -51,11 +51,19 @@ class BitgetTradingAPI:
|
|||||||
|
|
||||||
self.exchange = ccxt.bitget(config)
|
self.exchange = ccxt.bitget(config)
|
||||||
|
|
||||||
# 设置测试网(sandbox 模式)
|
# 设置测试网(手动配置 API 端点)
|
||||||
if use_testnet:
|
if use_testnet:
|
||||||
if hasattr(self.exchange, 'set_sandbox_mode'):
|
# Bitget 测试网端点
|
||||||
self.exchange.set_sandbox_mode(True)
|
self.exchange.urls['api']['swap'] = 'https://capi.bitget.com'
|
||||||
logger.info("✅ Bitget 测试网模式已启用")
|
self.exchange.urls['api']['swapPrivate'] = 'https://capi.bitget.com'
|
||||||
|
self.exchange.urls['api']['swapPublic'] = 'https://capi.bitget.com'
|
||||||
|
# 同时设置通用的 API URL
|
||||||
|
self.exchange.urls['api'] = {
|
||||||
|
'swap': 'https://capi.bitget.com',
|
||||||
|
'swapPrivate': 'https://capi.bitget.com',
|
||||||
|
'swapPublic': 'https://capi.bitget.com',
|
||||||
|
}
|
||||||
|
logger.info("✅ Bitget 测试网模式已启用 (capi.bitget.com)")
|
||||||
|
|
||||||
logger.info(f"Bitget 交易 API 初始化完成 ({'测试网' if use_testnet else '生产网'})")
|
logger.info(f"Bitget 交易 API 初始化完成 ({'测试网' if use_testnet else '生产网'})")
|
||||||
|
|
||||||
@ -84,11 +92,58 @@ class BitgetTradingAPI:
|
|||||||
# CCXT 标准化交易对格式
|
# CCXT 标准化交易对格式
|
||||||
ccxt_symbol = self._standardize_symbol(symbol)
|
ccxt_symbol = self._standardize_symbol(symbol)
|
||||||
|
|
||||||
|
# 手动获取合约规格(不依赖 CCXT 的 contractSize)
|
||||||
|
# Bitget 永续合约规格
|
||||||
|
if 'BTC' in symbol:
|
||||||
|
contract_size = 0.01 # BTC 每张 0.01 BTC
|
||||||
|
elif 'ETH' in symbol:
|
||||||
|
contract_size = 0.1 # ETH 每张 0.1 ETH
|
||||||
|
elif 'SOL' in symbol:
|
||||||
|
contract_size = 1 # SOL 每张 1 SOL
|
||||||
|
elif 'BNB' in symbol:
|
||||||
|
contract_size = 0.1 # BNB 每张 0.1 BNB
|
||||||
|
elif 'XRP' in symbol:
|
||||||
|
contract_size = 10 # XRP 每张 10 XRP
|
||||||
|
elif 'DOGE' in symbol:
|
||||||
|
contract_size = 100 # DOGE 每张 100 DOGE
|
||||||
|
elif 'MATIC' in symbol or 'POL' in symbol:
|
||||||
|
contract_size = 10 # MATIC 每张 10 MATIC
|
||||||
|
elif 'AVAX' in symbol:
|
||||||
|
contract_size = 1 # AVAX 每张 1 AVAX
|
||||||
|
elif 'LINK' in symbol:
|
||||||
|
contract_size = 1 # LINK 每张 1 LINK
|
||||||
|
elif 'UNI' in symbol:
|
||||||
|
contract_size = 1 # UNI 每张 1 UNI
|
||||||
|
elif 'ATOM' in symbol:
|
||||||
|
contract_size = 1 # ATOM 每张 1 ATOM
|
||||||
|
elif 'LTC' in symbol:
|
||||||
|
contract_size = 0.1 # LTC 每张 0.1 LTC
|
||||||
|
elif 'BCH' in symbol:
|
||||||
|
contract_size = 0.1 # BCH 每张 0.1 BCH
|
||||||
|
elif 'FIL' in symbol:
|
||||||
|
contract_size = 1 # FIL 每张 1 FIL
|
||||||
|
elif 'DOT' in symbol:
|
||||||
|
contract_size = 1 # DOT 每张 1 DOT
|
||||||
|
else:
|
||||||
|
# 默认尝试从市场信息获取
|
||||||
|
try:
|
||||||
|
market = self.exchange.market(ccxt_symbol)
|
||||||
|
contract_size = market.get('contractSize', 1)
|
||||||
|
logger.info(f"从市场信息获取合约规格: {contract_size}")
|
||||||
|
except Exception:
|
||||||
|
contract_size = 1
|
||||||
|
logger.warning(f"无法确定 {symbol} 的合约规格,使用默认值 1")
|
||||||
|
|
||||||
|
# 计算实际下单数量(张数 × 合约规格)
|
||||||
|
# Bitget 的 amount 参数是币的数量,不是张数
|
||||||
|
actual_amount = size * contract_size
|
||||||
|
|
||||||
# 构建订单参数
|
# 构建订单参数
|
||||||
# 单向持仓模式 + 联合保证金模式
|
# 单向持仓模式 + 联合保证金模式
|
||||||
params = {
|
params = {
|
||||||
'tdMode': 'cross', # 联合保证金模式(全仓)
|
'tdMode': 'cross', # 联合保证金模式(全仓)
|
||||||
'marginCoin': 'USDT', # 保证金币种
|
'marginCoin': 'USDT', # 保证金币种
|
||||||
|
'holdMode': 'oneWay', # 单向持仓模式
|
||||||
}
|
}
|
||||||
|
|
||||||
if client_order_id:
|
if client_order_id:
|
||||||
@ -100,12 +155,19 @@ class BitgetTradingAPI:
|
|||||||
if take_profit:
|
if take_profit:
|
||||||
params['takeProfit'] = str(take_profit)
|
params['takeProfit'] = str(take_profit)
|
||||||
|
|
||||||
|
# Bitget 合约交易特殊参数
|
||||||
|
params['holdMode'] = 'oneWay' # 单向持仓模式
|
||||||
|
|
||||||
|
# 调试:打印下单参数
|
||||||
|
logger.info(f"下单参数: symbol={ccxt_symbol}, type={order_type}, side={side}, 张数={size}, 实际amount={actual_amount}")
|
||||||
|
logger.debug(f"完整参数: {params}")
|
||||||
|
|
||||||
# 下单 - 直接使用通用 create_order 方法
|
# 下单 - 直接使用通用 create_order 方法
|
||||||
order = self.exchange.create_order(
|
order = self.exchange.create_order(
|
||||||
symbol=ccxt_symbol,
|
symbol=ccxt_symbol,
|
||||||
type=order_type,
|
type=order_type,
|
||||||
side=side,
|
side=side,
|
||||||
amount=size,
|
amount=actual_amount, # 使用实际币数量(张数 × 合约规格)
|
||||||
price=price,
|
price=price,
|
||||||
params=params
|
params=params
|
||||||
)
|
)
|
||||||
@ -363,6 +425,27 @@ class BitgetTradingAPI:
|
|||||||
# 使用独立的止损/止盈计划订单
|
# 使用独立的止损/止盈计划订单
|
||||||
# 注意:这种方式需要在平仓时也取消这些计划订单
|
# 注意:这种方式需要在平仓时也取消这些计划订单
|
||||||
|
|
||||||
|
# 获取合约规格(用于转换张数为币数量)
|
||||||
|
if 'BTC' in symbol:
|
||||||
|
contract_size = 0.01
|
||||||
|
elif 'ETH' in symbol:
|
||||||
|
contract_size = 0.1
|
||||||
|
elif 'SOL' in symbol:
|
||||||
|
contract_size = 1
|
||||||
|
elif 'BNB' in symbol:
|
||||||
|
contract_size = 0.1
|
||||||
|
elif 'XRP' in symbol:
|
||||||
|
contract_size = 10
|
||||||
|
elif 'DOGE' in symbol:
|
||||||
|
contract_size = 100
|
||||||
|
elif 'MATIC' in symbol or 'POL' in symbol:
|
||||||
|
contract_size = 10
|
||||||
|
else:
|
||||||
|
contract_size = 1
|
||||||
|
|
||||||
|
# 将张数转换为币数量
|
||||||
|
contracts_amount = contracts * contract_size
|
||||||
|
|
||||||
orders_created = []
|
orders_created = []
|
||||||
|
|
||||||
# 止损单
|
# 止损单
|
||||||
@ -374,7 +457,7 @@ class BitgetTradingAPI:
|
|||||||
symbol=ccxt_symbol,
|
symbol=ccxt_symbol,
|
||||||
type='stop_market',
|
type='stop_market',
|
||||||
side=sl_side,
|
side=sl_side,
|
||||||
amount=contracts,
|
amount=contracts_amount, # 使用币数量,不是张数
|
||||||
price=None,
|
price=None,
|
||||||
params={
|
params={
|
||||||
'stopPrice': stop_loss,
|
'stopPrice': stop_loss,
|
||||||
@ -385,7 +468,7 @@ class BitgetTradingAPI:
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
orders_created.append(('止损', sl_order))
|
orders_created.append(('止损', sl_order))
|
||||||
logger.info(f"✅ 止损单已下: {sl_side} {contracts}张 @ ${stop_loss}")
|
logger.info(f"✅ 止损单已下: {sl_side} {contracts}张 ({contracts_amount}币) @ ${stop_loss}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning(f"下止损单失败: {e}")
|
logger.warning(f"下止损单失败: {e}")
|
||||||
|
|
||||||
@ -398,7 +481,7 @@ class BitgetTradingAPI:
|
|||||||
symbol=ccxt_symbol,
|
symbol=ccxt_symbol,
|
||||||
type='limit',
|
type='limit',
|
||||||
side=tp_side,
|
side=tp_side,
|
||||||
amount=contracts,
|
amount=contracts_amount, # 使用币数量,不是张数
|
||||||
price=take_profit,
|
price=take_profit,
|
||||||
params={
|
params={
|
||||||
'tdMode': 'cross',
|
'tdMode': 'cross',
|
||||||
@ -407,7 +490,7 @@ class BitgetTradingAPI:
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
orders_created.append(('止盈', tp_order))
|
orders_created.append(('止盈', tp_order))
|
||||||
logger.info(f"✅ 止盈单已下: {tp_side} {contracts}张 @ ${take_profit}")
|
logger.info(f"✅ 止盈单已下: {tp_side} {contracts}张 ({contracts_amount}币) @ ${take_profit}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning(f"下止盈单失败: {e}")
|
logger.warning(f"下止盈单失败: {e}")
|
||||||
|
|
||||||
|
|||||||
@ -126,6 +126,7 @@ class SignalFormatter:
|
|||||||
signal_type_key = 'timeframe' if 'timeframe' in signal else 'type'
|
signal_type_key = 'timeframe' if 'timeframe' in signal else 'type'
|
||||||
signal_type = type_map.get(signal.get(signal_type_key), signal.get(signal_type_key))
|
signal_type = type_map.get(signal.get(signal_type_key), signal.get(signal_type_key))
|
||||||
action = action_map.get(signal['action'], signal['action'])
|
action = action_map.get(signal['action'], signal['action'])
|
||||||
|
action_icon = '🟢' if signal['action'] == 'buy' else '🔴'
|
||||||
grade = signal.get('grade', 'C')
|
grade = signal.get('grade', 'C')
|
||||||
confidence = signal.get('confidence', 0)
|
confidence = signal.get('confidence', 0)
|
||||||
entry_type = signal.get('entry_type', 'market')
|
entry_type = signal.get('entry_type', 'market')
|
||||||
|
|||||||
472
backend/test_bitget_trading.py
Normal file
472
backend/test_bitget_trading.py
Normal file
@ -0,0 +1,472 @@
|
|||||||
|
"""
|
||||||
|
测试 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()
|
||||||
@ -1,518 +0,0 @@
|
|||||||
# 黄金交易智能体 (Gold Agent) 技术调研与系统设计
|
|
||||||
|
|
||||||
## 一、项目概述
|
|
||||||
|
|
||||||
### 1.1 目标
|
|
||||||
构建一个基于 LLM 驱动的黄金 (XAUUSD) 行情分析智能体,对接 MetaTrader 5 (MT5) 进行实盘交易。
|
|
||||||
|
|
||||||
### 1.2 核心功能
|
|
||||||
- 实时获取黄金行情数据(通过 MT5)
|
|
||||||
- LLM 驱动的市场分析与信号生成
|
|
||||||
- 自动化交易执行(MT5 实盘)
|
|
||||||
- 风险管理与仓位控制
|
|
||||||
- 飞书/Telegram 通知推送
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 二、技术调研
|
|
||||||
|
|
||||||
### 2.1 MetaTrader 5 (MT5) API 调研
|
|
||||||
|
|
||||||
#### MT5 Python 库
|
|
||||||
```python
|
|
||||||
import MetaTrader5 as mt5
|
|
||||||
|
|
||||||
# 初始化
|
|
||||||
mt5.initialize()
|
|
||||||
|
|
||||||
# 获取行情数据
|
|
||||||
rates = mt5.copy_rates_from_pos("XAUUSD", mt5.TIMEFRAME_M15, 0, 100)
|
|
||||||
|
|
||||||
# 获取当前价格
|
|
||||||
tick = mt5.symbol_info_tick("XAUUSD")
|
|
||||||
bid = tick.bid
|
|
||||||
ask = tick.ask
|
|
||||||
|
|
||||||
# 下单
|
|
||||||
request = {
|
|
||||||
"action": mt5.TRADE_ACTION_DEAL,
|
|
||||||
"symbol": "XAUUSD",
|
|
||||||
"volume": 0.01, # 手数
|
|
||||||
"type": mt5.ORDER_TYPE_BUY,
|
|
||||||
"price": ask,
|
|
||||||
"deviation": 20,
|
|
||||||
"magic": 234000,
|
|
||||||
"comment": "Gold Agent",
|
|
||||||
"type_time": mt5.ORDER_TIME_GTC,
|
|
||||||
"type_filling": mt5.ORDER_FILLING_IOC,
|
|
||||||
}
|
|
||||||
mt5.order_send(request)
|
|
||||||
```
|
|
||||||
|
|
||||||
#### MT5 核心概念
|
|
||||||
| 概念 | 说明 |
|
|
||||||
|------|------|
|
|
||||||
| **手数 (Lot)** | 黄金最小 0.01 手,1 手 = 100 盎司 |
|
|
||||||
| **点值 (Point)** | 0.01 美元/点,1 点 = 0.01 USD |
|
|
||||||
| **杠杆** | 通常 1:100 - 1:500,由经纪商设定 |
|
|
||||||
| **交易时间** | 周一 00:00 - 周六 00:00 (服务器时间) |
|
|
||||||
| **点差** | 通常 20-50 点 (0.2-0.5 USD) |
|
|
||||||
|
|
||||||
### 2.2 黄金交易特点
|
|
||||||
|
|
||||||
#### XAUUSD 特性
|
|
||||||
| 特性 | 说明 | 交易策略影响 |
|
|
||||||
|------|------|-------------|
|
|
||||||
| **波动性高** | 日波动 50-200 点 | 需要宽止损 (30-50 点) |
|
|
||||||
| **流动性强** | 24 小时交易 | 可设置夜间交易 |
|
|
||||||
| **美元相关** | 与美元指数负相关 | 需关注 USD 数据 |
|
|
||||||
| **避险属性** | 市场恐慌时上涨 | 需关注 VIX 指数 |
|
|
||||||
| **交易时段** | 伦敦盘 (15:00-24:00) 和 纽约盘 (21:00-04:00) 最活跃 | 重点交易时段 |
|
|
||||||
| **周末停盘** | 周末不交易 | 周五收盘前需要平仓或宽止损 |
|
|
||||||
|
|
||||||
#### 技术指标适用性
|
|
||||||
- **趋势类**: MA, EMA, MACD 效果较好
|
|
||||||
- **震荡类**: RSI, KDJ 在盘整市有效
|
|
||||||
- **波动率类**: ATR 对止损设置重要
|
|
||||||
- **支撑阻力**: 黄金的关键整数位 ($2000, $2050, $2100) 效果明显
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 三、系统架构设计
|
|
||||||
|
|
||||||
### 3.1 整体架构
|
|
||||||
|
|
||||||
```
|
|
||||||
┌─────────────────────────────────────────────────────────────┐
|
|
||||||
│ Gold Agent 主控制器 │
|
|
||||||
│ (gold_agent/gold_agent.py) │
|
|
||||||
└─────────────────────────────────────────────────────────────┘
|
|
||||||
│
|
|
||||||
┌───────────────┼───────────────┐
|
|
||||||
▼ ▼ ▼
|
|
||||||
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
|
||||||
│ MT5 服务 │ │ 市场信号分析器 │ │ 交易决策器 │
|
|
||||||
│ (mt5_service) │ │ (market_signal_ │ │ (trading_ │
|
|
||||||
│ │ │ analyzer) │ │ decision) │
|
|
||||||
└─────────────────┘ └─────────────────┘ └─────────────────┘
|
|
||||||
│
|
|
||||||
┌───────────────┼───────────────┐
|
|
||||||
▼ ▼ ▼
|
|
||||||
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
|
||||||
│ 飞书/Telegram │ │ 模拟交易 │ │ 实盘交易 │
|
|
||||||
│ 通知 │ │ (paper_ │ │ (mt5_ │
|
|
||||||
│ │ │ trading) │ │ trading) │
|
|
||||||
└─────────────────┘ └─────────────────┘ └─────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3.2 目录结构
|
|
||||||
|
|
||||||
```
|
|
||||||
backend/app/
|
|
||||||
├── gold_agent/ # 黄金智能体模块
|
|
||||||
│ ├── __init__.py
|
|
||||||
│ ├── gold_agent.py # 主控制器
|
|
||||||
│ ├── market_signal_analyzer.py # 市场信号分析器
|
|
||||||
│ ├── trading_decision_maker.py # 交易决策器
|
|
||||||
│ └── strategy.py # 交易策略定义
|
|
||||||
│
|
|
||||||
├── services/ # 服务层
|
|
||||||
│ ├── mt5_service.py # MT5 数据服务
|
|
||||||
│ ├── mt5_trading_service.py # MT5 实盘交易服务
|
|
||||||
│ └── gold_paper_trading.py # 黄金模拟交易服务
|
|
||||||
│
|
|
||||||
├── models/ # 数据模型
|
|
||||||
│ └── gold_order.py # 黄金订单模型
|
|
||||||
│
|
|
||||||
└── api/ # API 接口
|
|
||||||
└── gold.py # 黄金智能体 API
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3.3 数据流设计
|
|
||||||
|
|
||||||
```
|
|
||||||
MT5 行情数据
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
┌─────────────────────────┐
|
|
||||||
│ 技术指标计算 │
|
|
||||||
│ (MA, EMA, RSI, MACD...) │
|
|
||||||
└─────────────────────────┘
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
┌─────────────────────────┐
|
|
||||||
│ 市场信号分析 (LLM) │
|
|
||||||
│ - 趋势判断 │
|
|
||||||
│ - 关键价位 │
|
|
||||||
│ - 信号生成 │
|
|
||||||
└─────────────────────────┘
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
┌─────────────────────────┐
|
|
||||||
│ 交易决策 (LLM) │
|
|
||||||
│ - 开仓/平仓/观望 │
|
|
||||||
│ - 仓位大小 │
|
|
||||||
│ - 止损止盈 │
|
|
||||||
└─────────────────────────┘
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
┌─────────────────────────┐
|
|
||||||
│ 风险检查 │
|
|
||||||
│ - 仓位限制 │
|
|
||||||
│ - 价格合理性验证 │
|
|
||||||
│ - 交易时段检查 │
|
|
||||||
└─────────────────────────┘
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
┌─────────────────────────┐
|
|
||||||
│ 执行交易 │
|
|
||||||
│ - 模拟交易 (回测) │
|
|
||||||
│ - 实盘交易 (MT5) │
|
|
||||||
└─────────────────────────┘
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
┌─────────────────────────┐
|
|
||||||
│ 通知推送 │
|
|
||||||
│ - 飞书卡片 │
|
|
||||||
│ - Telegram 消息 │
|
|
||||||
└─────────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 四、核心模块设计
|
|
||||||
|
|
||||||
### 4.1 MT5 服务 (mt5_service.py)
|
|
||||||
|
|
||||||
```python
|
|
||||||
class MT5Service:
|
|
||||||
"""MT5 数据服务 - 获取行情数据"""
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.connected = False
|
|
||||||
self.symbol = "XAUUSD"
|
|
||||||
|
|
||||||
def initialize(self, account: int, password: str, server: str) -> bool
|
|
||||||
def get_rates(self, timeframe: str, count: int) -> pd.DataFrame
|
|
||||||
def get_current_price(self) -> Tuple[float, float] # (bid, ask)
|
|
||||||
def get_tick(self) -> Dict
|
|
||||||
def get_positions(self) -> List[Dict]
|
|
||||||
def get_orders(self) -> List[Dict]
|
|
||||||
def get_account_info(self) -> Dict
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 时间周期映射
|
|
||||||
| 代码 | MT5 常量 | 用途 |
|
|
||||||
|------|----------|------|
|
|
||||||
| `M1` | `TIMEFRAME_M1` | 短线交易 |
|
|
||||||
| `M5` | `TIMEFRAME_M5` | 主要分析周期 |
|
|
||||||
| `M15` | `TIMEFRAME_M15` | 中线交易 |
|
|
||||||
| `H1` | `TIMEFRAME_H1` | 趋势确认 |
|
|
||||||
| `H4` | `TIMEFRAME_H4` | 日内趋势 |
|
|
||||||
| `D1` | `TIMEFRAME_D1` | 长期趋势 |
|
|
||||||
|
|
||||||
### 4.2 市场信号分析器 (market_signal_analyzer.py)
|
|
||||||
|
|
||||||
```python
|
|
||||||
class GoldMarketSignalAnalyzer:
|
|
||||||
"""黄金市场信号分析器"""
|
|
||||||
|
|
||||||
MARKET_ANALYSIS_PROMPT = """你是一位专业的黄金交易员...
|
|
||||||
|
|
||||||
## 黄金交易特点
|
|
||||||
- XAUUSD 极其活跃,日波动 50-200 点
|
|
||||||
- 关键整数位: $2000, $2050, $2100, $2150, $2200
|
|
||||||
- 伦敦盘 (15:00-24:00) 和 纽约盘 (21:00-04:00) 最活跃
|
|
||||||
- 周五收盘前需要谨慎持仓
|
|
||||||
|
|
||||||
## 输出格式
|
|
||||||
{
|
|
||||||
"trend_direction": "uptrend/downtrend/neutral",
|
|
||||||
"trend_strength": "strong/medium/weak",
|
|
||||||
"signals": [
|
|
||||||
{
|
|
||||||
"action": "buy/sell",
|
|
||||||
"entry_zone": 2050.50,
|
|
||||||
"stop_loss": 2045.00,
|
|
||||||
"take_profit": 2060.00,
|
|
||||||
"confidence": 85,
|
|
||||||
"grade": "A",
|
|
||||||
"reasoning": "..."
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"key_levels": {
|
|
||||||
"support": [2045.00, 2040.00],
|
|
||||||
"resistance": [2060.00, 2065.00]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
|
|
||||||
async def analyze(self, symbol: str, data: Dict) -> Dict
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4.3 交易决策器 (trading_decision_maker.py)
|
|
||||||
|
|
||||||
```python
|
|
||||||
class GoldTradingDecisionMaker:
|
|
||||||
"""黄金交易决策器"""
|
|
||||||
|
|
||||||
TRADING_DECISION_PROMPT = """你是黄金交易执行者...
|
|
||||||
|
|
||||||
## 账户信息
|
|
||||||
- 余额: {balance}
|
|
||||||
- 持仓: {positions}
|
|
||||||
- 杠杆: 1:100
|
|
||||||
|
|
||||||
## 黄金交易规则
|
|
||||||
- 最小手数: 0.01 手
|
|
||||||
- 1 手 = 100 盎司
|
|
||||||
- 点值: 0.01 USD/点
|
|
||||||
- 建议止损: 30-50 点
|
|
||||||
- 建议止盈: 1:2 或 1:3
|
|
||||||
|
|
||||||
## 输出格式
|
|
||||||
{
|
|
||||||
"decision": "OPEN/CLOSE/HOLD",
|
|
||||||
"action": "buy/sell",
|
|
||||||
"quantity": 0.01, # 手数
|
|
||||||
"entry_zone": 2050.50,
|
|
||||||
"stop_loss": 2045.00,
|
|
||||||
"take_profit": 2060.00,
|
|
||||||
"reasoning": "..."
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
|
|
||||||
async def make_decision(self, market_signal, positions, account) -> Dict
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4.4 MT5 实盘交易服务 (mt5_trading_service.py)
|
|
||||||
|
|
||||||
```python
|
|
||||||
class MT5TradingService:
|
|
||||||
"""MT5 实盘交易服务"""
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.mt5 = mt5
|
|
||||||
self.magic_number = 234000 # 识别 Gold Agent 的订单
|
|
||||||
|
|
||||||
def connect(self, account: int, password: str, server: str) -> bool
|
|
||||||
def open_order(self, action: str, volume: float, price: float,
|
|
||||||
sl: float, tp: float) -> Dict
|
|
||||||
def close_order(self, order_id: int) -> Dict
|
|
||||||
def close_all_positions(self) -> Dict
|
|
||||||
def get_positions(self) -> List[Dict]
|
|
||||||
def modify_position(self, ticket: int, sl: float, tp: float) -> Dict
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 订单类型说明
|
|
||||||
| 类型 | MT5 常量 | 说明 |
|
|
||||||
|------|----------|------|
|
|
||||||
| 市价买单 | `ORDER_TYPE_BUY` | 以 Ask 价格成交 |
|
|
||||||
| 市价卖单 | `ORDER_TYPE_SELL` | 以 Bid 价格成交 |
|
|
||||||
| 限价买单 | `ORDER_TYPE_BUY_LIMIT` | 低于当前价挂单 |
|
|
||||||
| 限价卖单 | `ORDER_TYPE_SELL_LIMIT` | 高于当前价挂单 |
|
|
||||||
|
|
||||||
### 4.5 风险控制规则
|
|
||||||
|
|
||||||
```python
|
|
||||||
# 仓位管理
|
|
||||||
MAX_POSITIONS = 3 # 最大同时持仓数
|
|
||||||
MAX_LOTS_PER_ORDER = 0.1 # 单笔最大手数
|
|
||||||
MAX_TOTAL_LOTS = 0.3 # 总持仓上限
|
|
||||||
|
|
||||||
# 止损止盈规则
|
|
||||||
MIN_STOP_LOSS_POINTS = 30 # 最小止损 30 点 (0.30 USD)
|
|
||||||
TAKE_PROFIT_RATIO = 2.0 # 止盈/止损比 1:2
|
|
||||||
|
|
||||||
# 价格合理性验证
|
|
||||||
MAX_PRICE_DEVIATION = 0.20 # 价格偏离不超过 20%
|
|
||||||
|
|
||||||
# 交易时段
|
|
||||||
WEEKEND_CLOSE_HOUR = 20 # 周五 20:00 后不开新仓
|
|
||||||
NEWS_FILTER_WINDOW = 30 # 重大新闻前后 30 分钟不开仓
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 五、实现计划
|
|
||||||
|
|
||||||
### Phase 1: 基础设施 (第 1-2 周)
|
|
||||||
- [ ] MT5 服务封装
|
|
||||||
- [ ] 连接管理
|
|
||||||
- [ ] 行情数据获取
|
|
||||||
- [ ] 账户信息查询
|
|
||||||
- [ ] 数据模型定义
|
|
||||||
- [ ] 黄金订单模型
|
|
||||||
- [ ] 持仓记录模型
|
|
||||||
- [ ] 配置项添加
|
|
||||||
- [ ] MT5 账户配置
|
|
||||||
- [ ] 飞书 webhook 配置
|
|
||||||
|
|
||||||
### Phase 2: 分析与决策 (第 3-4 周)
|
|
||||||
- [ ] 市场信号分析器
|
|
||||||
- [ ] 技术指标计算
|
|
||||||
- [ ] LLM 提示词设计
|
|
||||||
- [ ] 信号生成逻辑
|
|
||||||
- [ ] 交易决策器
|
|
||||||
- [ ] 持仓状态判断
|
|
||||||
- [ ] 开平仓决策
|
|
||||||
- [ ] 仓位大小计算
|
|
||||||
|
|
||||||
### Phase 3: 交易执行 (第 5-6 周)
|
|
||||||
- [ ] 模拟交易服务
|
|
||||||
- [ ] 订单管理
|
|
||||||
- [ ] 止损止盈执行
|
|
||||||
- [ ] 移动止损逻辑
|
|
||||||
- [ ] MT5 实盘交易服务
|
|
||||||
- [ ] 订单发送
|
|
||||||
- [ ] 持仓查询
|
|
||||||
- [ ] 订单修改/平仓
|
|
||||||
|
|
||||||
### Phase 4: 风险管理与通知 (第 7 周)
|
|
||||||
- [ ] 风险控制模块
|
|
||||||
- [ ] 仓位限制
|
|
||||||
- [ ] 价格验证
|
|
||||||
- [ ] 交易时段检查
|
|
||||||
- [ ] 通知推送
|
|
||||||
- [ ] 飞书卡片格式
|
|
||||||
- [ ] Telegram 消息推送
|
|
||||||
|
|
||||||
### Phase 5: 测试与优化 (第 8 周)
|
|
||||||
- [ ] 模拟盘测试
|
|
||||||
- [ ] 实盘小资金测试
|
|
||||||
- [ ] 性能优化
|
|
||||||
- [ ] 文档完善
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 六、风险考虑
|
|
||||||
|
|
||||||
### 6.1 技术风险
|
|
||||||
|
|
||||||
| 风险 | 应对措施 |
|
|
||||||
|------|----------|
|
|
||||||
| MT5 连接断开 | 自动重连机制,连接状态监控 |
|
|
||||||
| 订单执行失败 | 超时重试,失败告警 |
|
|
||||||
| 数据延迟 | 多时间周期数据验证 |
|
|
||||||
| 系统崩溃 | 持久化状态,重启恢复 |
|
|
||||||
|
|
||||||
### 6.2 交易风险
|
|
||||||
|
|
||||||
| 风险 | 应对措施 |
|
|
||||||
|------|----------|
|
|
||||||
| 市场剧烈波动 | 宽止损 + 小仓位 |
|
|
||||||
| 流动性枯竭 | 避开非交易时段 |
|
|
||||||
| 滑点风险 | 使用限价单,设置最大滑点 |
|
|
||||||
| 重大新闻事件 | 新闻过滤窗口,避免开仓 |
|
|
||||||
|
|
||||||
### 6.3 LLM 相关风险
|
|
||||||
|
|
||||||
| 风险 | 应对措施 |
|
|
||||||
|------|----------|
|
|
||||||
| 幻觉导致错误价格 | 价格合理性验证 |
|
|
||||||
| 信号质量不稳定 | 多信号确认,降低单次权重 |
|
|
||||||
| 延迟影响执行 | 缓存机制,异步处理 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 七、配置项设计
|
|
||||||
|
|
||||||
### 7.1 config.py 新增配置
|
|
||||||
|
|
||||||
```python
|
|
||||||
# ==================== 黄金交易智能体配置 ====================
|
|
||||||
# MT5 连接配置
|
|
||||||
mt5_account: int = 0 # MT5 账号
|
|
||||||
mt5_password: str = "" # MT5 密码
|
|
||||||
mt5_server: str = "" # MT5 服务器地址
|
|
||||||
|
|
||||||
# 黄金交易配置
|
|
||||||
gold_symbol: str = "XAUUSD" # 黄金交易品种
|
|
||||||
gold_analysis_interval: int = 300 # 分析间隔(秒)
|
|
||||||
gold_enabled: bool = False # 是否启用黄金智能体
|
|
||||||
|
|
||||||
# 仓位管理
|
|
||||||
gold_max_positions: int = 3 # 最大持仓数
|
|
||||||
gold_max_lots_per_order: float = 0.1 # 单笔最大手数
|
|
||||||
gold_max_total_lots: float = 0.3 # 总持仓上限
|
|
||||||
gold_default_lots: float = 0.01 # 默认手数
|
|
||||||
|
|
||||||
# 止损止盈
|
|
||||||
gold_min_stop_loss_points: float = 30 # 最小止损(点)
|
|
||||||
gold_take_profit_ratio: float = 2.0 # 止盈/止损比
|
|
||||||
|
|
||||||
# 交易时段
|
|
||||||
gold_weekend_close_hour: int = 20 # 周五收盘时间
|
|
||||||
gold_news_filter_minutes: int = 30 # 新闻过滤窗口(分钟)
|
|
||||||
|
|
||||||
# 通知配置
|
|
||||||
feishu_gold_webhook_url: str = "" # 黄金智能体飞书通知
|
|
||||||
```
|
|
||||||
|
|
||||||
### 7.2 环境变量 (.env)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# MT5 配置
|
|
||||||
MT5_ACCOUNT=12345678
|
|
||||||
MT5_PASSWORD=your_password
|
|
||||||
MT5_SERVER=your_broker_server
|
|
||||||
|
|
||||||
# 黄金智能体
|
|
||||||
GOLD_ENABLED=True
|
|
||||||
GOLD_SYMBOL=XAUUSD
|
|
||||||
|
|
||||||
# 飞书通知
|
|
||||||
FEISHU_GOLD_WEBHOOK_URL=https://open.feishu.cn/open-apis/bot/v2/hook/...
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 八、监控指标
|
|
||||||
|
|
||||||
### 8.1 系统监控
|
|
||||||
- MT5 连接状态
|
|
||||||
- 分析执行频率
|
|
||||||
- LLM 调用延迟
|
|
||||||
- 订单执行成功率
|
|
||||||
|
|
||||||
### 8.2 交易监控
|
|
||||||
- 当前持仓数
|
|
||||||
- 总盈亏 (USD)
|
|
||||||
- 胜率
|
|
||||||
- 最大回撤
|
|
||||||
- 平均持仓时间
|
|
||||||
|
|
||||||
### 8.3 信号监控
|
|
||||||
- 信号生成频率
|
|
||||||
- A/B/C/D 级信号分布
|
|
||||||
- 信号执行率
|
|
||||||
- 信号盈亏比
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 九、后续优化方向
|
|
||||||
|
|
||||||
1. **多品种支持** - 扩展到 XAGUSD (白银)、其他贵金属
|
|
||||||
2. **智能参数调优** - 基于历史数据自动优化参数
|
|
||||||
3. **策略回测** - 使用 MT5 历史数据进行策略回测
|
|
||||||
4. **风险模型升级** - VaR 计算、凯利公式仓位管理
|
|
||||||
5. **机器学习增强** - 使用 ML 模型辅助 LLM 决策
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 十、总结
|
|
||||||
|
|
||||||
本设计方案基于现有 Crypto Agent 架构,复用了:
|
|
||||||
- LLM 驱动的市场分析框架
|
|
||||||
- 交易决策框架
|
|
||||||
- 风险验证逻辑
|
|
||||||
- 通知推送机制
|
|
||||||
|
|
||||||
主要新增:
|
|
||||||
- MT5 数据与交易接口
|
|
||||||
- 黄金特定的交易规则
|
|
||||||
- 适配黄金特点的提示词
|
|
||||||
|
|
||||||
预计开发周期:**8 周**
|
|
||||||
风险等级:**中等** (实盘交易需谨慎)
|
|
||||||
投入建议:**先模拟盘充分测试后再小资金实盘**
|
|
||||||
Loading…
Reference in New Issue
Block a user