This commit is contained in:
aaron 2026-02-26 23:31:29 +08:00
parent 2fb2321399
commit ee54e66dd7
4 changed files with 565 additions and 527 deletions

View File

@ -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}")

View File

@ -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')

View 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()

View File

@ -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 周**
风险等级:**中等** (实盘交易需谨慎)
投入建议:**先模拟盘充分测试后再小资金实盘**