优化顺势而为策略

This commit is contained in:
aaron 2026-02-26 00:02:20 +08:00
parent faa551e661
commit f6e15d4a48
5 changed files with 520 additions and 28 deletions

View File

@ -514,9 +514,9 @@ class CryptoAgent:
# 模拟交易决策 # 模拟交易决策
if paper_trading_enabled: if paper_trading_enabled:
logger.info(f"\n📊 【模拟交易决策】") logger.info(f"\n📊 【模拟交易决策】")
positions, account = self._get_trading_state(use_real_trading=False) positions, account, pending_orders = self._get_trading_state(use_real_trading=False)
paper_decision = await self.decision_maker.make_decision( paper_decision = await self.decision_maker.make_decision(
market_signal, positions, account, current_price market_signal, positions, account, current_price, pending_orders
) )
self._log_trading_decision(paper_decision) self._log_trading_decision(paper_decision)
else: else:
@ -527,9 +527,9 @@ class CryptoAgent:
logger.info(f"\n💰 【实盘交易决策】") logger.info(f"\n💰 【实盘交易决策】")
# 检查是否开启自动交易 # 检查是否开启自动交易
if self.real_trading and self.real_trading.get_auto_trading_status(): if self.real_trading and self.real_trading.get_auto_trading_status():
positions, account = self._get_trading_state(use_real_trading=True) positions, account, pending_orders = self._get_trading_state(use_real_trading=True)
real_decision = await self.decision_maker.make_decision( real_decision = await self.decision_maker.make_decision(
market_signal, positions, account, current_price market_signal, positions, account, current_price, pending_orders
) )
self._log_trading_decision(real_decision) self._log_trading_decision(real_decision)
else: else:
@ -657,6 +657,9 @@ class CryptoAgent:
Args: Args:
use_real_trading: True 获取实盘状态False 获取模拟交易状态 use_real_trading: True 获取实盘状态False 获取模拟交易状态
Returns:
(positions, account, pending_orders) - 持仓列表账户状态挂单列表
""" """
if use_real_trading and self.real_trading: if use_real_trading and self.real_trading:
# 实盘交易 # 实盘交易
@ -667,21 +670,33 @@ class CryptoAgent:
active_orders = self.paper_trading.get_active_orders() active_orders = self.paper_trading.get_active_orders()
account = self.paper_trading.get_account_status() account = self.paper_trading.get_account_status()
# 转换为标准格式(只返回已成交的订单作为持仓) # 分离持仓和挂单
position_list = [] position_list = []
pending_orders = []
for order in active_orders: for order in active_orders:
# 只返回已成交OPEN 状态且有 filled_price的订单
if order.get('status') == 'open' and order.get('filled_price'): if order.get('status') == 'open' and order.get('filled_price'):
# 已成交的订单作为持仓
position_list.append({ position_list.append({
'symbol': order.get('symbol'), 'symbol': order.get('symbol'),
'side': order.get('side'), 'side': order.get('side'),
'holding': order.get('quantity', 0), # 使用 quantity 作为持仓量 'holding': order.get('quantity', 0),
'entry_price': order.get('filled_price') or order.get('entry_price'), 'entry_price': order.get('filled_price') or order.get('entry_price'),
'stop_loss': order.get('stop_loss'), 'stop_loss': order.get('stop_loss'),
'take_profit': order.get('take_profit') 'take_profit': order.get('take_profit')
}) })
elif order.get('status') == 'pending':
# 未成交的订单作为挂单
pending_orders.append({
'order_id': order.get('order_id'),
'symbol': order.get('symbol'),
'side': order.get('side'),
'entry_price': order.get('entry_price'),
'quantity': order.get('quantity', 0),
'entry_type': order.get('entry_type', 'market'),
'confidence': order.get('confidence', 0)
})
return position_list, account return position_list, account, pending_orders
async def _execute_decisions(self, paper_decision: Dict[str, Any], async def _execute_decisions(self, paper_decision: Dict[str, Any],
real_decision: Dict[str, Any], real_decision: Dict[str, Any],
@ -721,22 +736,28 @@ class CryptoAgent:
logger.info(f"\n📊 【执行模拟交易】") logger.info(f"\n📊 【执行模拟交易】")
if decision_type in ['OPEN', 'ADD']: if decision_type in ['OPEN', 'ADD']:
# 先执行交易 # 先执行交易
result = await self._execute_paper_trade(paper_decision, market_signal, current_price) result = await self._execute_paper_trade(paper_decision, market_signal, current_price)
# 检查是否成功执行 # 检查是否成功执行
order = result.get('order') if result else None order = result.get('order') if result else None
if order: if order:
# 只有成功创建订单后才发送通知 # 只有成功创建订单后才发送通知
await self._send_signal_notification(market_signal, paper_decision, current_price, is_paper=True) await self._send_signal_notification(market_signal, paper_decision, current_price, is_paper=True)
paper_executed = True paper_executed = True
else: else:
# 有信号但订单创建失败,发送未执行通知 # 有信号但订单创建失败,发送未执行通知
reason = result.get('message', '订单创建失败') if result else '订单创建失败' reason = result.get('message', '订单创建失败') if result else '订单创建失败'
await self._notify_signal_not_executed(market_signal, paper_decision, current_price, is_paper=True, reason=reason) await self._notify_signal_not_executed(market_signal, paper_decision, current_price, is_paper=True, reason=reason)
logger.warning(f" ⚠️ 模拟交易未执行,已发送通知") logger.warning(f" ⚠️ 模拟交易未执行,已发送通知")
elif decision_type == 'CLOSE': elif decision_type == 'CLOSE':
await self._execute_close(paper_decision, paper_trading=True) await self._execute_close(paper_decision, paper_trading=True)
paper_executed = True paper_executed = True
elif decision_type == 'CANCEL_PENDING':
await self._execute_cancel_pending(paper_decision, paper_trading=True)
paper_executed = True
elif decision_type == 'REDUCE':
await self._execute_reduce(paper_decision, paper_trading=True)
paper_executed = True
# ============================================================ # ============================================================
# 执行实盘交易决策 # 执行实盘交易决策
@ -770,6 +791,12 @@ class CryptoAgent:
elif decision_type == 'CLOSE': elif decision_type == 'CLOSE':
await self._execute_close(real_decision, paper_trading=False) await self._execute_close(real_decision, paper_trading=False)
real_executed = True real_executed = True
elif decision_type == 'CANCEL_PENDING':
await self._execute_cancel_pending(real_decision, paper_trading=False)
real_executed = True
elif decision_type == 'REDUCE':
await self._execute_reduce(real_decision, paper_trading=False)
real_executed = True
# 如果都没有执行,给出提示 # 如果都没有执行,给出提示
if not paper_executed and not real_executed: if not paper_executed and not real_executed:
@ -1260,6 +1287,76 @@ class CryptoAgent:
except Exception as e: except Exception as e:
logger.error(f"执行平仓失败: {e}") logger.error(f"执行平仓失败: {e}")
async def _execute_cancel_pending(self, decision: Dict[str, Any], paper_trading: bool = True):
"""执行取消挂单
Args:
decision: 交易决策
paper_trading: True=模拟交易, False=实盘交易
"""
try:
symbol = decision.get('symbol')
orders_to_cancel = decision.get('orders_to_cancel', [])
if not orders_to_cancel:
logger.info(f" ⚠️ 没有需要取消的订单")
return
trading_service = self.paper_trading if paper_trading else self.real_trading
trading_type = "模拟" if paper_trading else "实盘"
if not trading_service:
logger.warning(f" {trading_type}交易服务未初始化")
return
logger.info(f" 🚫 {trading_type}取消挂单: {symbol}")
logger.info(f" 取消订单数量: {len(orders_to_cancel)}")
cancelled_count = 0
for order_id in orders_to_cancel:
try:
# 取消订单
result = trading_service.cancel_order(order_id)
if result.get('success'):
cancelled_count += 1
logger.info(f" ✅ 已取消订单: {order_id}")
else:
logger.warning(f" ⚠️ 取消订单失败: {order_id} | {result.get('message', '')}")
except Exception as e:
logger.error(f" ❌ 取消订单异常: {order_id} | {e}")
logger.info(f" 📊 成功取消 {cancelled_count}/{len(orders_to_cancel)} 个订单")
except Exception as e:
logger.error(f"执行取消挂单失败: {e}")
async def _execute_reduce(self, decision: Dict[str, Any], paper_trading: bool = True):
"""执行减仓
Args:
decision: 交易决策
paper_trading: True=模拟交易, False=实盘交易
"""
try:
symbol = decision.get('symbol')
trading_type = "模拟" if paper_trading else "实盘"
logger.info(f" 📤 {trading_type}减仓: {symbol}")
logger.info(f" 理由: {decision.get('reasoning', '')}")
trading_service = self.paper_trading if paper_trading else self.real_trading
if not trading_service:
logger.warning(f" {trading_type}交易服务未初始化")
return
# TODO: 实现减仓逻辑
# 减仓可以是部分平仓,需要根据决策中的参数执行
logger.info(f" ⚠️ 减仓功能待实现")
except Exception as e:
logger.error(f"执行减仓失败: {e}")
def _convert_to_paper_signal(self, symbol: str, signal: Dict[str, Any], def _convert_to_paper_signal(self, symbol: str, signal: Dict[str, Any],
current_price: float) -> Dict[str, Any]: current_price: float) -> Dict[str, Any]:
"""转换 LLM 信号格式为模拟交易格式""" """转换 LLM 信号格式为模拟交易格式"""

View File

@ -33,6 +33,22 @@ class TradingDecisionMaker:
3. 账户状态余额已用保证金杠杆等 3. 账户状态余额已用保证金杠杆等
## 决策类型 ## 决策类型
### 🚨 铁律(必须首先检查)
**趋势与信号方向一致性检查第一优先级**
| 当前趋势 | 信号方向 | 允许操作 | 条件 |
|---------|---------|---------|------|
| `uptrend` (上升) | buy (做多) | 允许 | 正常仓位 |
| `uptrend` (上升) | sell (做空) | 禁止 | **除非**有极强的多重反转信号背离+放量+关键形态+ confidence >= 85 |
| `downtrend` (下降) | sell (做空) | 允许 | 正常仓位 |
| `downtrend` (下降) | buy (做多) | 禁止 | **除非**有极强的多重反转信号背离+放量+关键形态+ confidence >= 85 |
| `neutral` (震荡) | buy/sell | 允许 | 轻仓操作 |
**趋势强度限制**
- `strong` 趋势**严禁逆势开仓**直接返回 HOLD
- `medium` 趋势逆势开仓需 confidence >= 85 + 多重反转信号
- `weak` `neutral`可双向交易但谨慎仓位
### 1. 开仓OPEN ### 1. 开仓OPEN
**时机**无持仓或可以加仓时 **时机**无持仓或可以加仓时
**要求** **要求**
@ -43,6 +59,34 @@ class TradingDecisionMaker:
- 账户有足够的可用杠杆空间 - 账户有足够的可用杠杆空间
- 风险可控止损明确 - 风险可控止损明确
**顺势 vs 逆势仓位规则重要**
| 情况 | 仓位限制 | 保证金倍率 |
|-----|---------|-----------|
| **顺势交易**信号与趋势同向 | 正常仓位 | 100% |
| **震荡市交易**neutral 趋势 | 降级处理 | 70% |
| **逆势交易**信号与趋势反向 | **大幅降级** | 30% |
**具体规则**
- 顺势 A heavy12% 保证金
- 顺势 B medium6% 保证金
- 顺势 C light3% 保证金
- 震荡 A medium6% × 70% 4%
- 震荡 B light3% × 70% 2%
- 震荡 C micro1.5% 保证金
- **逆势交易**仅允许在 medium/weak 趋势 + confidence >= 85
- 逆势 A light3% × 30% 1%
- 逆势 B/C **禁止**返回 HOLD
**示例**
- 账户余额 $10,000
- 顺势 heavy保证金 $1,200 持仓 $24,000
- 顺势 medium保证金 $600 持仓 $12,000
- 顺势 light保证金 $300 持仓 $6,000
- 逆势 light极少数情况保证金 $100 持仓 $2,000
### 2. 平仓CLOSE ### 2. 平仓CLOSE
**时机** **时机**
- 触发止损/止盈 - 触发止损/止盈
@ -67,7 +111,24 @@ class TradingDecisionMaker:
- 降低风险敞口 - 降低风险敞口
- 不确定增加 - 不确定增加
### 5. 观望HOLD ### 5. 取消挂单CANCEL_PENDING
**时机**
- **趋势反转时自动取消反向挂单**重要
- 当前是 `uptrend`上升趋势取消所有做空short挂单
- 当前是 `downtrend`下降趋势取消所有做多long挂单
- 趋势强度为 `strong` 必须立即取消反向挂单
- **信号方向与挂单方向相反**
- 新信号是 buy但存在 sell 挂单 取消 sell 挂单
- 新信号是 sell但存在 buy 挂单 取消 buy 挂单
- **价格偏离过大**
- 当前价格距离挂单价超过 3%建议取消重新挂单
**输出格式**
- `decision: "CANCEL_PENDING"`
- `orders_to_cancel`: ["order_id_1", "order_id_2"] - 要取消的订单ID列表
- `reasoning`: "取消原因"
### 6. 观望HOLD
**时机** **时机**
- 信号不明确 - 信号不明确
- 风险过大 - 风险过大
@ -99,6 +160,7 @@ class TradingDecisionMaker:
- **heavy**使用保证金 = 账户余额 × 12% - **heavy**使用保证金 = 账户余额 × 12%
- **medium**使用保证金 = 账户余额 × 6% - **medium**使用保证金 = 账户余额 × 6%
- **light**使用保证金 = 账户余额 × 3% - **light**使用保证金 = 账户余额 × 3%
- **micro**使用保证金 = 账户余额 × 1.5%极小仓位仅用于震荡市或特殊逆势情况
#### 4. 选择逻辑示例 #### 4. 选择逻辑示例
假设当前可用杠杆空间为 50% 假设当前可用杠杆空间为 50%
@ -135,21 +197,36 @@ class TradingDecisionMaker:
```json ```json
{ {
"decision": "OPEN/CLOSE/ADD/REDUCE/HOLD", "decision": "OPEN/CLOSE/ADD/REDUCE/CANCEL_PENDING/HOLD",
"symbol": "BTC/USDT", "symbol": "BTC/USDT",
"side": "buy/sell", "side": "buy/sell",
"action": "open_long/close_short/add_long/...", "action": "open_long/close_short/add_long/...",
"position_size": "heavy/medium/light", "position_size": "heavy/medium/light/micro",
"quantity": 1200, "quantity": 1200,
"position_multiplier": 1.0,
"confidence": 0-100, "confidence": 0-100,
"reasoning": "简洁的决策理由1句话15字以内", "reasoning": "简洁的决策理由1句话15字以内",
"risk_analysis": "核心风险点1句话15字以内", "risk_analysis": "核心风险点1句话15字以内",
"stop_loss": 65500, "stop_loss": 65500,
"take_profit": 67500, "take_profit": 67500,
"orders_to_cancel": ["order_id_1", "order_id_2"],
"notes": "其他说明" "notes": "其他说明"
} }
``` ```
**注意**
- `position_size` 可选值`heavy`/`medium`/`light`/`micro`
- `heavy`12% 保证金顺势A级
- `medium`6% 保证金顺势B级 震荡A级
- `light`3% 保证金顺势C级 震荡B级
- `micro`1.5% 保证金震荡C级 特殊逆势情况
- `position_multiplier`仓位倍率可选默认1.0
- 顺势交易1.0
- 震荡市0.7
- 逆势交易极少数情况0.3
- 如果 `decision` `CANCEL_PENDING`需要提供 `orders_to_cancel` 字段要取消的订单ID列表
- 如果 `decision` `OPEN/ADD/CLOSE/REDUCE`不需要 `orders_to_cancel` 字段
## 重要说明 ## 重要说明
- **所有价格必须是纯数字**不要加 $ 符号逗号或其他格式 - **所有价格必须是纯数字**不要加 $ 符号逗号或其他格式
- `stop_loss``take_profit` 必须是数字类型 - `stop_loss``take_profit` 必须是数字类型
@ -182,7 +259,8 @@ class TradingDecisionMaker:
market_signal: Dict[str, Any], market_signal: Dict[str, Any],
positions: List[Dict[str, Any]], positions: List[Dict[str, Any]],
account: Dict[str, Any], account: Dict[str, Any],
current_price: float = None) -> Dict[str, Any]: current_price: float = None,
pending_orders: List[Dict[str, Any]] = None) -> Dict[str, Any]:
""" """
做出交易决策 做出交易决策
@ -191,6 +269,7 @@ class TradingDecisionMaker:
positions: 当前持仓列表 positions: 当前持仓列表
account: 账户状态 account: 账户状态
current_price: 当前价格用于判断入场方式 current_price: 当前价格用于判断入场方式
pending_orders: 未成交的挂单列表
Returns: Returns:
交易决策字典 交易决策字典
@ -198,7 +277,7 @@ class TradingDecisionMaker:
try: try:
# 1. 准备决策上下文 # 1. 准备决策上下文
decision_context = self._prepare_decision_context( decision_context = self._prepare_decision_context(
market_signal, positions, account, current_price market_signal, positions, account, current_price, pending_orders or []
) )
# 2. 构建提示词 # 2. 构建提示词
@ -229,15 +308,19 @@ class TradingDecisionMaker:
market_signal: Dict[str, Any], market_signal: Dict[str, Any],
positions: List[Dict[str, Any]], positions: List[Dict[str, Any]],
account: Dict[str, Any], account: Dict[str, Any],
current_price: float = None) -> Dict[str, Any]: current_price: float = None,
pending_orders: List[Dict[str, Any]] = None) -> Dict[str, Any]:
"""准备决策上下文""" """准备决策上下文"""
context = { context = {
'symbol': market_signal.get('symbol'), 'symbol': market_signal.get('symbol'),
'market_state': market_signal.get('market_state'), 'market_state': market_signal.get('market_state'),
'trend': market_signal.get('trend'), 'trend': market_signal.get('trend'),
'trend_direction': market_signal.get('trend_direction'), # 新增:趋势方向
'trend_strength': market_signal.get('trend_strength'), # 新增:趋势强度
'signals': market_signal.get('signals', []), 'signals': market_signal.get('signals', []),
'key_levels': market_signal.get('key_levels', {}), 'key_levels': market_signal.get('key_levels', {}),
'positions': positions, 'positions': positions,
'pending_orders': pending_orders or [], # 新增:挂单列表
'account': account, 'account': account,
'current_price': current_price 'current_price': current_price
} }
@ -281,7 +364,13 @@ class TradingDecisionMaker:
prompt_parts.append(f"## 市场信号") prompt_parts.append(f"## 市场信号")
prompt_parts.append(f"交易对: {context['symbol']}") prompt_parts.append(f"交易对: {context['symbol']}")
prompt_parts.append(f"市场状态: {context.get('market_state')}") prompt_parts.append(f"市场状态: {context.get('market_state')}")
prompt_parts.append(f"趋势: {context.get('trend')}")
# 趋势信息(新增)
trend_direction = context.get('trend_direction', 'neutral')
trend_strength = context.get('trend_strength', 'weak')
direction_text = {'uptrend': '📈 上升趋势', 'downtrend': '📉 下降趋势', 'neutral': ' 震荡'}.get(trend_direction, trend_direction)
strength_text = {'strong': '强势', 'medium': '中等', 'weak': '弱势'}.get(trend_strength, trend_strength)
prompt_parts.append(f"趋势: {direction_text} ({strength_text})")
# 当前价格(如果有) # 当前价格(如果有)
current_price = context.get('current_price') current_price = context.get('current_price')
@ -306,6 +395,36 @@ class TradingDecisionMaker:
prompt_parts.append(f" 理由: {sig.get('reasoning', 'N/A')}") prompt_parts.append(f" 理由: {sig.get('reasoning', 'N/A')}")
# 趋势一致性检查(新增)
trend_direction = context.get('trend_direction', 'neutral')
trend_strength = context.get('trend_strength', 'weak')
prompt_parts.append(f"\n## 🚨 趋势一致性检查(第一优先级)")
for i, sig in enumerate(signals, 1):
action = sig.get('action', 'hold')
is_aligned = (trend_direction == 'uptrend' and action == 'buy') or \
(trend_direction == 'downtrend' and action == 'sell') or \
(trend_direction == 'neutral')
if is_aligned:
prompt_parts.append(f"✅ 信号#{i} ({action}) 与趋势 ({trend_direction}) 一致 → 可正常开仓")
else:
# 逆势信号
if trend_strength == 'strong':
prompt_parts.append(f"❌ 信号#{i} ({action}) 与强趋势 ({trend_direction}) 相反 → **严禁逆势,返回 HOLD**")
elif trend_strength == 'medium':
confidence = sig.get('confidence', 0)
if confidence >= 85:
prompt_parts.append(f"⚠️ 信号#{i} ({action}) 与中等趋势相反,但 confidence={confidence}>=85 → 可谨慎 micro 仓位")
else:
prompt_parts.append(f"❌ 信号#{i} ({action}) 与中等趋势相反confidence不足 → 返回 HOLD")
else: # weak or neutral
confidence = sig.get('confidence', 0)
if confidence >= 85:
prompt_parts.append(f"⚠️ 信号#{i} ({action}) 与弱趋势相反,但 confidence={confidence}>=85 → 可 micro 仓位")
else:
prompt_parts.append(f"❌ 信号#{i} ({action}) 与弱趋势相反confidence不足 → 返回 HOLD")
# 关键价位 # 关键价位
key_levels = context.get('key_levels', {}) key_levels = context.get('key_levels', {})
if key_levels: if key_levels:
@ -353,6 +472,19 @@ class TradingDecisionMaker:
else: else:
prompt_parts.append("无持仓") prompt_parts.append("无持仓")
# 当前挂单
pending_orders = context.get('pending_orders', [])
prompt_parts.append(f"\n## 当前挂单")
if pending_orders:
for order in pending_orders:
side_icon = "🟢" if order.get('side') == 'long' else "🔴"
entry_type = "现价单" if order.get('entry_type') == 'market' else "挂单"
prompt_parts.append(f"- {side_icon} {order.get('symbol')}: {order.get('side')} | {entry_type}")
prompt_parts.append(f" 挂单价: ${order.get('entry_price')} | 数量: {order.get('quantity')} USDT")
prompt_parts.append(f" 订单ID: {order.get('order_id')}")
else:
prompt_parts.append("无挂单")
# 账户状态 # 账户状态
account = context.get('account', {}) account = context.get('account', {})
lev_info = context.get('leverage_info', {}) lev_info = context.get('leverage_info', {})

View File

@ -1000,6 +1000,44 @@ class PaperTradingService:
finally: finally:
db.close() db.close()
def cancel_order(self, order_id: str) -> Dict[str, Any]:
"""
取消挂单
Args:
order_id: 订单ID
Returns:
取消结果字典
"""
if order_id not in self.active_orders:
return {
'success': False,
'message': f'订单不存在: {order_id}'
}
order = self.active_orders[order_id]
# 只能取消挂单状态的订单
if order.status != OrderStatus.PENDING:
return {
'success': False,
'message': f'只能取消挂单状态的订单,当前状态: {order.status.value}'
}
result = self._cancel_pending_order(order)
if result:
return {
'success': True,
'order_id': order_id,
'message': '挂单已取消'
}
else:
return {
'success': False,
'message': '取消挂单失败'
}
def _cancel_pending_order(self, order: PaperOrder) -> Dict[str, Any]: def _cancel_pending_order(self, order: PaperOrder) -> Dict[str, Any]:
"""取消挂单""" """取消挂单"""
db = db_service.get_session() db = db_service.get_session()

View File

@ -553,6 +553,89 @@ class RealTradingService:
return order.to_dict() return order.to_dict()
return None return None
def cancel_order(self, order_id: str) -> Dict[str, Any]:
"""
取消挂单
Args:
order_id: 订单ID
Returns:
取消结果字典
"""
if order_id not in self.active_orders:
return {
'success': False,
'message': f'订单不存在: {order_id}'
}
order = self.active_orders[order_id]
# 只能取消挂单状态的订单
if order.status != OrderStatus.PENDING:
return {
'success': False,
'message': f'只能取消挂单状态的订单,当前状态: {order.status.value}'
}
# 调用交易所 API 取消订单
if not self.trading_api:
return {
'success': False,
'message': '交易 API 未初始化'
}
try:
# 获取原始订单ID如果有
original_order_id = order.original_order_id or order_id
# 调用交易所取消订单API
success = self.trading_api.cancel_order(symbol=order.symbol, order_id=original_order_id)
if success:
# 更新本地订单状态
order.status = OrderStatus.CANCELLED
order.closed_at = datetime.utcnow()
# 保存到数据库
db = db_service.get_session()
try:
db_order = db.query(RealOrder).filter(RealOrder.order_id == order_id).first()
if db_order:
db_order.status = OrderStatus.CANCELLED
db_order.closed_at = datetime.utcnow()
db.merge(db_order)
db.commit()
except Exception as e:
logger.error(f"更新数据库订单状态失败: {e}")
db.rollback()
finally:
db.close()
# 从活跃订单缓存中移除
if order_id in self.active_orders:
del self.active_orders[order_id]
logger.info(f"实盘挂单已取消: {order_id} | {order.symbol}")
return {
'success': True,
'order_id': order_id,
'message': '挂单已取消'
}
else:
return {
'success': False,
'message': '交易所取消订单失败'
}
except Exception as e:
logger.error(f"取消实盘挂单失败: {e}")
return {
'success': False,
'message': f'取消订单异常: {e}'
}
def sync_positions_from_exchange(self) -> List[Dict]: def sync_positions_from_exchange(self) -> List[Dict]:
""" """
从交易所同步持仓状态 从交易所同步持仓状态

View File

@ -0,0 +1,142 @@
"""
测试市场信号分析 - 验证趋势判断功能
"""
import asyncio
import sys
import os
import json
# 添加项目路径
sys.path.insert(0, os.path.dirname(__file__))
from app.crypto_agent.market_signal_analyzer import MarketSignalAnalyzer
from app.utils.logger import logger
from app.services.bitget_service import BitgetService
async def test_trend_analysis(symbol: str = "BTCUSDT"):
"""测试趋势分析"""
logger.info("=" * 60)
logger.info(f"开始测试市场信号分析: {symbol}")
logger.info("=" * 60)
# 1. 获取 K 线数据
logger.info(f"\n[1/3] 获取 {symbol} 的 K 线数据...")
bitget_service = BitgetService()
data = bitget_service.get_multi_timeframe_data(symbol)
if not data:
logger.error(f"❌ 无法获取 {symbol} 的 K 线数据")
return
logger.info(f"✅ K 线数据获取成功")
for tf, df in data.items():
if df is not None and len(df) > 0:
latest_price = float(df.iloc[-1]['close'])
logger.info(f" {tf}: 最新价格 ${latest_price:,.2f}, 数据量 {len(df)}")
# 2. 分析市场信号
logger.info(f"\n[2/3] 分析市场信号...")
analyzer = MarketSignalAnalyzer()
result = await analyzer.analyze(symbol, data)
# 3. 打印分析结果
logger.info(f"\n[3/3] 分析结果:")
logger.info("-" * 60)
# 打印趋势判断(新功能)
logger.info("\n📊 趋势判断(新增):")
logger.info(f" 趋势方向: {result.get('trend_direction', 'unknown')}")
logger.info(f" 趋势强度: {result.get('trend_strength', 'unknown')}")
# 打印市场状态
logger.info(f"\n📈 市场状态:")
logger.info(f" {result.get('market_state', 'unknown')}")
logger.info(f" 分析摘要: {result.get('analysis_summary', 'unknown')}")
logger.info(f" 量价分析: {result.get('volume_analysis', 'unknown')}")
# 打印关键价位
if result.get('key_levels'):
logger.info(f"\n💰 关键价位:")
levels = result['key_levels']
if levels.get('support'):
logger.info(f" 支撑位: {[f'${s:,.0f}' for s in levels['support'] if s]}")
if levels.get('resistance'):
logger.info(f" 阻力位: {[f'${r:,.0f}' for r in levels['resistance'] if r]}")
# 打印信号
logger.info(f"\n🎯 交易信号:")
signals = result.get('signals', [])
if not signals:
logger.warning(" ⚠️ 无交易信号(观望)")
else:
for i, sig in enumerate(signals, 1):
action_emoji = "🟢" if sig.get('action') == 'buy' else "🔴" if sig.get('action') == 'sell' else ""
grade_icon = {'A': '⭐⭐⭐', 'B': '⭐⭐', 'C': '', 'D': ''}.get(sig.get('grade', ''), '')
entry_type_emoji = "⚡现价" if sig.get('entry_type') == 'market' else "⏳挂单"
logger.info(f"\n 信号 #{i} {action_emoji} {grade_icon}")
logger.info(f" ├─ 类型: {sig.get('timeframe', 'unknown')} | {entry_type_emoji}")
logger.info(f" ├─ 操作: {sig.get('action', 'unknown').upper()}")
logger.info(f" ├─ 信心度: {sig.get('confidence', 0)}% | 等级: {sig.get('grade', 'unknown')}")
logger.info(f" ├─ 入场价: ${sig.get('entry_zone', 0):,.2f}")
logger.info(f" ├─ 止损: ${sig.get('stop_loss', 0):,.2f} | 止盈: ${sig.get('take_profit', 0):,.2f}")
logger.info(f" └─ 理由: {sig.get('reasoning', 'unknown')}")
# 检查是否顺势
trend_dir = result.get('trend_direction', 'neutral')
action = sig.get('action', '')
if trend_dir == 'uptrend' and action == 'sell':
logger.warning(f" ⚠️ 警告: 上升趋势中做空(逆势)")
elif trend_dir == 'downtrend' and action == 'buy':
logger.warning(f" ⚠️ 警告: 下降趋势中做多(逆势)")
elif trend_dir == 'uptrend' and action == 'buy':
logger.info(f" ✅ 顺势交易")
elif trend_dir == 'downtrend' and action == 'sell':
logger.info(f" ✅ 顺势交易")
logger.info("\n" + "=" * 60)
logger.info("测试完成")
logger.info("=" * 60)
return result
async def test_multiple_symbols():
"""测试多个交易对"""
symbols = ['BTCUSDT', 'ETHUSDT', 'SOLUSDT']
for symbol in symbols:
logger.info(f"\n\n{'#' * 60}")
logger.info(f"# 测试 {symbol}")
logger.info(f"{'#' * 60}")
try:
await test_trend_analysis(symbol)
except Exception as e:
logger.error(f"{symbol} 测试失败: {e}")
import traceback
traceback.print_exc()
# 等待一段时间,避免 API 限流
await asyncio.sleep(2)
def main():
import argparse
parser = argparse.ArgumentParser(description='测试市场信号分析')
parser.add_argument('--symbol', default='BTCUSDT', help='交易对(默认: BTCUSDT')
parser.add_argument('--multi', action='store_true', help='测试多个交易对')
args = parser.parse_args()
if args.multi:
asyncio.run(test_multiple_symbols())
else:
asyncio.run(test_trend_analysis(args.symbol))
if __name__ == '__main__':
main()