agent.py 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. """
  2. 动量冲浪者智能体
  3. 基于价格突破和成交量确认生成短期动量信号
  4. 最佳生态:夏季繁荣
  5. 持有期:不超过5日
  6. """
  7. from datetime import datetime, timedelta
  8. from typing import Dict, Any, Optional
  9. import numpy as np
  10. import pandas as pd
  11. from agents.base import AgentBase, AgentSignal, SignalDirection, SignalStrength
  12. from core.ecosystem import MacroRegime, UnifiedEcosystem
  13. class MomentumSurferAgent(AgentBase):
  14. """
  15. 动量冲浪者智能体
  16. 策略逻辑:
  17. 1. 价格突破20周期高点 + 成交量>1.5倍均量 = 买入
  18. 2. 快速止盈(3-5日)
  19. 3. 严格止损(-2%)
  20. 4. 避开有毒订单流时段
  21. """
  22. def __init__(
  23. self,
  24. config: Optional[Dict[str, Any]] = None,
  25. breakout_period: int = 20,
  26. volume_ratio: float = 1.5,
  27. max_hold_days: int = 5,
  28. stop_loss: float = 0.02
  29. ):
  30. super().__init__(
  31. name="momentum_surfer",
  32. config=config,
  33. max_position=0.6,
  34. min_confidence=0.65
  35. )
  36. self.breakout_period = breakout_period
  37. self.volume_ratio = volume_ratio
  38. self.max_hold_days = max_hold_days
  39. self.stop_loss = stop_loss
  40. self.preferred_regimes = [MacroRegime.SUMMER]
  41. def generate_signal(
  42. self,
  43. price_data: pd.DataFrame,
  44. ecosystem: Optional[UnifiedEcosystem] = None
  45. ) -> Optional[AgentSignal]:
  46. """生成交易信号"""
  47. if len(price_data) < self.breakout_period + 5:
  48. return None
  49. # 有毒订单流检查
  50. if ecosystem and hasattr(ecosystem, 'micro'):
  51. if ecosystem.micro.flow_toxicity.value in ["high", "medium"]:
  52. return None
  53. # 突破检测
  54. breakout_signal = self._detect_breakout(price_data)
  55. if breakout_signal == 0:
  56. return None
  57. direction = SignalDirection.LONG if breakout_signal > 0 else SignalDirection.SHORT
  58. # 成交量确认
  59. volume_confirmed = self._check_volume_confirmation(price_data)
  60. if not volume_confirmed:
  61. return None
  62. # 计算置信度
  63. confidence = abs(breakout_signal)
  64. # 动能强度
  65. momentum_strength = self._calculate_momentum_strength(price_data)
  66. confidence *= (1 + momentum_strength)
  67. confidence = min(1.0, confidence)
  68. if not self._validate_signal(direction, confidence, ecosystem):
  69. return None
  70. position_size = self._calculate_position_size(ecosystem, confidence)
  71. strength = (
  72. SignalStrength.STRONG if confidence > 0.8
  73. else SignalStrength.MEDIUM if confidence > 0.70
  74. else SignalStrength.WEAK
  75. )
  76. signal = AgentSignal(
  77. agent_name=self.name,
  78. direction=direction,
  79. strength=strength,
  80. confidence=confidence,
  81. suggested_position=position_size,
  82. expected_return=self.get_expected_return(price_data, ecosystem),
  83. win_probability=self.get_win_probability(price_data, ecosystem),
  84. timestamp=datetime.now(),
  85. valid_until=datetime.now() + timedelta(days=self.max_hold_days),
  86. metadata={
  87. "breakout_strength": breakout_signal,
  88. "momentum_strength": momentum_strength,
  89. "max_hold_days": self.max_hold_days,
  90. "stop_loss": self.stop_loss
  91. }
  92. )
  93. self.current_signal = signal
  94. self.signal_history.append(signal)
  95. return signal
  96. def get_expected_return(
  97. self,
  98. price_data: pd.DataFrame,
  99. ecosystem: Optional[UnifiedEcosystem] = None
  100. ) -> float:
  101. """计算预期收益(短期高收益率)"""
  102. base_return = 0.20 # 20%年化
  103. # 短期爆发预期更高
  104. daily_return = base_return / 252
  105. expected_5day = (1 + daily_return) ** 5 - 1
  106. return expected_5day * 10 # 换算回年化展示
  107. def get_win_probability(
  108. self,
  109. price_data: pd.DataFrame,
  110. ecosystem: Optional[UnifiedEcosystem] = None
  111. ) -> float:
  112. """计算胜率"""
  113. base_prob = 0.50
  114. # 趋势强度加成
  115. momentum = self._calculate_momentum_strength(price_data)
  116. base_prob += momentum * 0.15
  117. # 生态加成
  118. if ecosystem and ecosystem.macro.regime == MacroRegime.SUMMER:
  119. base_prob += 0.12
  120. elif ecosystem and ecosystem.macro.regime == MacroRegime.SPRING:
  121. base_prob += 0.05
  122. return min(0.70, base_prob)
  123. def _detect_breakout(self, data: pd.DataFrame) -> float:
  124. """检测突破信号强度 (-1 到 1)"""
  125. current_price = data['close'].iloc[-1]
  126. high_20 = data['high'].iloc[-self.breakout_period:].max()
  127. low_20 = data['low'].iloc[-self.breakout_period:].min()
  128. prev_close = data['close'].iloc[-2]
  129. # 向上突破
  130. if current_price > high_20 and current_price > prev_close * 1.01:
  131. strength = (current_price - high_20) / high_20 * 50
  132. return min(1.0, 0.6 + strength)
  133. # 向下突破
  134. if current_price < low_20 and current_price < prev_close * 0.99:
  135. strength = (low_20 - current_price) / low_20 * 50
  136. return -min(1.0, 0.6 + strength)
  137. return 0.0
  138. def _check_volume_confirmation(self, data: pd.DataFrame) -> bool:
  139. """检查成交量确认"""
  140. current_volume = data['volume'].iloc[-1]
  141. avg_volume = data['volume'].iloc[-20:].mean()
  142. if avg_volume == 0:
  143. return False
  144. return current_volume > avg_volume * self.volume_ratio
  145. def _calculate_momentum_strength(self, data: pd.DataFrame) -> float:
  146. """计算动能强度"""
  147. if len(data) < 10:
  148. return 0.0
  149. # 使用ROC(变动率)
  150. roc = (data['close'].iloc[-1] - data['close'].iloc[-10]) / data['close'].iloc[-10]
  151. # 使用RSI
  152. delta = data['close'].diff()
  153. gain = (delta.where(delta > 0, 0)).rolling(14).mean()
  154. loss = (-delta.where(delta < 0, 0)).rolling(14).mean()
  155. rs = gain / loss
  156. rsi = 100 - (100 / (1 + rs))
  157. current_rsi = rsi.iloc[-1] if not pd.isna(rsi.iloc[-1]) else 50
  158. # 综合动能
  159. momentum = (roc * 10 + (current_rsi - 50) / 50) / 2
  160. return max(-1, min(1, momentum))
  161. def _calculate_position_size(
  162. self,
  163. ecosystem: Optional[UnifiedEcosystem],
  164. confidence: float
  165. ) -> float:
  166. """计算建议仓位(动量策略仓位较小)"""
  167. base_size = self.max_position * confidence * 0.8
  168. if ecosystem:
  169. if ecosystem.macro.regime == MacroRegime.SUMMER:
  170. base_size *= 1.0
  171. else:
  172. base_size *= 0.5
  173. health_factor = ecosystem.meso.health_score / 100
  174. base_size *= health_factor
  175. return min(self.max_position, base_size)
  176. def _check_regime_match(self, ecosystem: Any) -> float:
  177. """检查生态适配度"""
  178. if not ecosystem or not hasattr(ecosystem, 'macro'):
  179. return 0.7
  180. if ecosystem.macro.regime == MacroRegime.SUMMER:
  181. return 1.2
  182. elif ecosystem.macro.regime in [MacroRegime.SPRING]:
  183. return 0.9
  184. else:
  185. return 0.5