处理重复开仓的问题
This commit is contained in:
parent
7cdb24bf47
commit
91ec4ec091
@ -55,6 +55,12 @@ class TradingDecisionMaker:
|
|||||||
- 同向新信号
|
- 同向新信号
|
||||||
- 趋势加强
|
- 趋势加强
|
||||||
|
|
||||||
|
**价格距离限制(重要)**:
|
||||||
|
- 如果已有持仓/挂单的价格与新价格距离 < 1%,**不加仓也不开新仓**
|
||||||
|
- 例如:现有 BTC 做多持仓 @ $95,000,新信号 @ $95,500(差距 0.52% < 1%),拒绝开仓
|
||||||
|
- 这是为了避免在同一价格区域重复建仓,导致风险过度集中
|
||||||
|
- **例外**:如果信号是 A 级(confidence >= 90)且趋势非常强劲,可以考虑放宽到 0.5%
|
||||||
|
|
||||||
### 4. 减仓(REDUCE)
|
### 4. 减仓(REDUCE)
|
||||||
**时机**:
|
**时机**:
|
||||||
- 部分止盈
|
- 部分止盈
|
||||||
@ -258,6 +264,13 @@ class TradingDecisionMaker:
|
|||||||
'max_leverage': max_leverage
|
'max_leverage': max_leverage
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# 价格距离检查信息(用于 LLM 判断)
|
||||||
|
context['price_distance_check'] = {
|
||||||
|
'enabled': True,
|
||||||
|
'min_distance_percent': 1.0, # 最小价格距离 1%
|
||||||
|
'exception_threshold': 90 # A 级信号且 confidence >= 90 时可放宽到 0.5%
|
||||||
|
}
|
||||||
|
|
||||||
return context
|
return context
|
||||||
|
|
||||||
def _build_decision_prompt(self, context: Dict[str, Any]) -> str:
|
def _build_decision_prompt(self, context: Dict[str, Any]) -> str:
|
||||||
@ -355,6 +368,14 @@ class TradingDecisionMaker:
|
|||||||
prompt_parts.append(f"可用杠杆空间: {lev_info.get('available_leverage_percent', 0):.1f}%")
|
prompt_parts.append(f"可用杠杆空间: {lev_info.get('available_leverage_percent', 0):.1f}%")
|
||||||
prompt_parts.append(f"最大杠杆限制: {lev_info.get('max_leverage', 20)}x")
|
prompt_parts.append(f"最大杠杆限制: {lev_info.get('max_leverage', 20)}x")
|
||||||
|
|
||||||
|
# 价格距离检查规则
|
||||||
|
price_check = context.get('price_distance_check', {})
|
||||||
|
if price_check.get('enabled'):
|
||||||
|
prompt_parts.append(f"\n## 价格距离限制")
|
||||||
|
prompt_parts.append(f"⚠️ 重要:如果有相同方向的持仓/挂单,价格距离必须 >= {price_check.get('min_distance_percent', 1)}%")
|
||||||
|
prompt_parts.append(f"- 低于此距离不开新仓,避免风险过度集中")
|
||||||
|
prompt_parts.append(f"- A级信号(confidence >= {price_check.get('exception_threshold', 90)})可考虑放宽到 0.5%")
|
||||||
|
|
||||||
prompt_parts.append(f"\n请根据以上信息,做出交易决策。")
|
prompt_parts.append(f"\n请根据以上信息,做出交易决策。")
|
||||||
|
|
||||||
return "\n".join(prompt_parts)
|
return "\n".join(prompt_parts)
|
||||||
|
|||||||
@ -186,19 +186,38 @@ class PaperTradingService:
|
|||||||
result['message'] = msg
|
result['message'] = msg
|
||||||
return result
|
return result
|
||||||
|
|
||||||
# 2. 检查是否有接近的挂单(价格差距 < 1%)
|
# 2. 检查是否有接近的订单(包括挂单和持仓)
|
||||||
|
# 安全网阈值(LLM 已在提示词中告知此规则,这是最后的安全检查)
|
||||||
|
# 设置一个更宽松的阈值,主要判断由 LLM 负责
|
||||||
|
price_distance_threshold = 0.003 # 0.3% 作为安全网
|
||||||
|
|
||||||
same_direction_orders = [
|
same_direction_orders = [
|
||||||
order for order in self.active_orders.values()
|
order for order in self.active_orders.values()
|
||||||
if order.symbol == symbol and order.side == side
|
if order.symbol == symbol and order.side == side
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# 检查挂单距离
|
||||||
pending_orders = [
|
pending_orders = [
|
||||||
order for order in same_direction_orders
|
order for order in same_direction_orders
|
||||||
if order.status == OrderStatus.PENDING
|
if order.status == OrderStatus.PENDING
|
||||||
]
|
]
|
||||||
for pending in pending_orders:
|
for pending in pending_orders:
|
||||||
price_diff = abs(pending.entry_price - entry_price) / pending.entry_price
|
price_diff = abs(pending.entry_price - entry_price) / pending.entry_price
|
||||||
if price_diff < 0.01: # 价格差距小于 1%
|
if price_diff < price_distance_threshold:
|
||||||
msg = f"已有接近的挂单 @ ${pending.entry_price:,.2f}(价格差距 {price_diff*100:.2f}%)"
|
msg = f"已有接近的挂单 @ ${pending.entry_price:,.2f}(价格差距 {price_diff*100:.2f}% < {price_distance_threshold*100:.0f}%)"
|
||||||
|
logger.info(f"订单限制: {symbol} {msg},新信号 @ ${entry_price:,.2f},跳过")
|
||||||
|
result['message'] = msg
|
||||||
|
return result
|
||||||
|
|
||||||
|
# 检查持仓距离(避免在已有持仓价格附近重复开仓)
|
||||||
|
open_positions = [
|
||||||
|
order for order in same_direction_orders
|
||||||
|
if order.status == OrderStatus.OPEN
|
||||||
|
]
|
||||||
|
for position in open_positions:
|
||||||
|
price_diff = abs(position.entry_price - entry_price) / position.entry_price
|
||||||
|
if price_diff < price_distance_threshold:
|
||||||
|
msg = f"已有接近的持仓 @ ${position.entry_price:,.2f}(价格差距 {price_diff*100:.2f}% < {price_distance_threshold*100:.0f}%)"
|
||||||
logger.info(f"订单限制: {symbol} {msg},新信号 @ ${entry_price:,.2f},跳过")
|
logger.info(f"订单限制: {symbol} {msg},新信号 @ ${entry_price:,.2f},跳过")
|
||||||
result['message'] = msg
|
result['message'] = msg
|
||||||
return result
|
return result
|
||||||
@ -866,6 +885,58 @@ class PaperTradingService:
|
|||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def _calculate_dynamic_price_threshold(self, symbol: str, current_price: float) -> float:
|
||||||
|
"""
|
||||||
|
根据市场波动率动态计算价格距离阈值
|
||||||
|
|
||||||
|
使用 ATR (Average True Range) 来衡量波动率:
|
||||||
|
- 高波动率币种(如山寨币)→ 更大的阈值
|
||||||
|
- 低波动率币种(如 BTC)→ 更小的阈值
|
||||||
|
|
||||||
|
Args:
|
||||||
|
symbol: 交易对
|
||||||
|
current_price: 当前价格
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
动态价格阈值(例如 0.01 = 1%)
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# 获取市场数据服务
|
||||||
|
from app.services.bitget_service import bitget_service
|
||||||
|
|
||||||
|
# 获取 1 小时 K 线数据来计算 ATR
|
||||||
|
df = bitget_service.get_klines(symbol, interval='1h', limit=50)
|
||||||
|
|
||||||
|
if df is None or len(df) < 14 or 'atr' not in df.columns:
|
||||||
|
# 无法获取 ATR,使用默认值 1%
|
||||||
|
logger.debug(f"{symbol} 无法获取 ATR,使用默认阈值 1%")
|
||||||
|
return 0.01
|
||||||
|
|
||||||
|
# 获取最新的 ATR 值
|
||||||
|
current_atr = float(df['atr'].iloc[-1])
|
||||||
|
|
||||||
|
# 计算 ATR 占价格的百分比
|
||||||
|
atr_percent = (current_atr / current_price) * 100
|
||||||
|
|
||||||
|
# 动态阈值:ATR% 的 50% 作为价格距离阈值
|
||||||
|
# 例如:ATR = 3% → 阈值 = 1.5%
|
||||||
|
# ATR = 1% → 阈值 = 0.5%
|
||||||
|
dynamic_threshold = (atr_percent / 100) * 0.5
|
||||||
|
|
||||||
|
# 设置最小和最大阈值限制
|
||||||
|
min_threshold = 0.005 # 最小 0.5%
|
||||||
|
max_threshold = 0.02 # 最大 2%
|
||||||
|
|
||||||
|
dynamic_threshold = max(min_threshold, min(dynamic_threshold, max_threshold))
|
||||||
|
|
||||||
|
logger.debug(f"{symbol} ATR: {atr_percent:.2f}%, 动态阈值: {dynamic_threshold*100:.2f}%")
|
||||||
|
|
||||||
|
return dynamic_threshold
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"{symbol} 计算动态阈值失败: {e},使用默认值 1%")
|
||||||
|
return 0.01
|
||||||
|
|
||||||
def close_order_manual(self, order_id: str, exit_price: float) -> Optional[Dict[str, Any]]:
|
def close_order_manual(self, order_id: str, exit_price: float) -> Optional[Dict[str, Any]]:
|
||||||
"""手动平仓或取消挂单"""
|
"""手动平仓或取消挂单"""
|
||||||
if order_id not in self.active_orders:
|
if order_id not in self.active_orders:
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user