agent.py 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. """
  2. 波动率卖家智能体
  3. 基于IV Rank分析和卖方策略生成信号
  4. 最佳生态:夏季繁荣、春季复苏(避开秋季)
  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 VolatilitySellerAgent(AgentBase):
  14. """
  15. 波动率卖家智能体
  16. 策略逻辑:
  17. 1. 当隐含波动率(IV Rank)> 80%时,做空波动率
  18. 2. 使用卖方策略:卖出宽跨式(Strangle)或铁鹰(Iron Condor)
  19. 3. 避开秋季(高波动不确定期)
  20. 4. 收取权利金,利用波动率均值回归
  21. 注意:此策略需要期权数据支持
  22. """
  23. def __init__(
  24. self,
  25. config: Optional[Dict[str, Any]] = None,
  26. iv_rank_high: float = 80,
  27. iv_rank_low: float = 20,
  28. strategy_type: str = "strangle", # "straddle", "strangle", "iron_condor"
  29. delta_target: float = 0.30,
  30. dte_target: int = 30 # 到期日目标(天)
  31. ):
  32. super().__init__(
  33. name="volatility_seller",
  34. config=config,
  35. max_position=0.4,
  36. min_confidence=0.70
  37. )
  38. self.iv_rank_high = iv_rank_high
  39. self.iv_rank_low = iv_rank_low
  40. self.strategy_type = strategy_type
  41. self.delta_target = delta_target
  42. self.dte_target = dte_target
  43. self.preferred_regimes = [MacroRegime.SUMMER, MacroRegime.SPRING]
  44. self.avoid_regimes = [MacroRegime.AUTUMN]
  45. # 检查是否有期权数据
  46. self.has_option_data = False
  47. def generate_signal(
  48. self,
  49. price_data: pd.DataFrame,
  50. ecosystem: Optional[UnifiedEcosystem] = None,
  51. option_data: Optional[Dict] = None
  52. ) -> Optional[AgentSignal]:
  53. """生成卖方信号"""
  54. if len(price_data) < 60:
  55. return None
  56. # 检查是否在避免的生态
  57. if ecosystem and hasattr(ecosystem, 'macro'):
  58. if ecosystem.macro.regime in self.avoid_regimes:
  59. return None
  60. # 计算IV Rank(使用历史波动率作为代理)
  61. iv_rank = self._calculate_iv_rank(price_data)
  62. # 检查是否满足做空波动率条件
  63. if iv_rank < self.iv_rank_high:
  64. return None
  65. # 检查趋势(避免在强趋势市场做卖方)
  66. trend_strength = self._calculate_trend_strength(price_data)
  67. if trend_strength > 0.7: # 强趋势市场风险高
  68. return None
  69. direction = SignalDirection.NEUTRAL # 卖方策略方向中性
  70. confidence = min(1.0, (iv_rank - self.iv_rank_high) / 20 + 0.5)
  71. if not self._validate_signal(direction, confidence, ecosystem):
  72. return None
  73. position_size = self._calculate_position_size(ecosystem, confidence, iv_rank)
  74. strength = (
  75. SignalStrength.STRONG if confidence > 0.85
  76. else SignalStrength.MEDIUM if confidence > 0.75
  77. else SignalStrength.WEAK
  78. )
  79. signal = AgentSignal(
  80. agent_name=self.name,
  81. direction=direction,
  82. strength=strength,
  83. confidence=confidence,
  84. suggested_position=position_size,
  85. expected_return=self.get_expected_return(price_data, ecosystem),
  86. win_probability=self.get_win_probability(price_data, ecosystem),
  87. timestamp=datetime.now(),
  88. valid_until=datetime.now() + timedelta(days=self.dte_target),
  89. metadata={
  90. "iv_rank": iv_rank,
  91. "strategy_type": self.strategy_type,
  92. "delta_target": self.delta_target,
  93. "dte_target": self.dte_target,
  94. "trend_strength": trend_strength,
  95. "note": "Option strategy - requires options data"
  96. }
  97. )
  98. self.current_signal = signal
  99. self.signal_history.append(signal)
  100. return signal
  101. def get_expected_return(
  102. self,
  103. price_data: pd.DataFrame,
  104. ecosystem: Optional[UnifiedEcosystem] = None
  105. ) -> float:
  106. """计算预期收益(卖方策略收益稳定但有限)"""
  107. # 卖方策略通常每月收取1-3%权利金
  108. monthly_premium = 0.02
  109. annual_return = (1 + monthly_premium) ** 12 - 1
  110. return annual_return
  111. def get_win_probability(
  112. self,
  113. price_data: pd.DataFrame,
  114. ecosystem: Optional[UnifiedEcosystem] = None
  115. ) -> float:
  116. """计算胜率(卖方策略胜率通常较高,70-80%)"""
  117. base_prob = 0.70
  118. iv_rank = self._calculate_iv_rank(price_data)
  119. if iv_rank > 85:
  120. base_prob += 0.10
  121. elif iv_rank > 80:
  122. base_prob += 0.05
  123. # 避开秋季降低胜率预期
  124. if ecosystem and ecosystem.macro.regime == MacroRegime.AUTUMN:
  125. base_prob -= 0.15
  126. return min(0.85, base_prob)
  127. def _calculate_iv_rank(self, data: pd.DataFrame) -> float:
  128. """
  129. 计算IV Rank
  130. 使用历史波动率作为隐含波动率的代理
  131. IV Rank = (当前IV - 52周最低IV) / (52周最高IV - 52周最低IV)
  132. """
  133. if len(data) < 252:
  134. lookback = len(data) // 2
  135. else:
  136. lookback = 252
  137. # 计算历史波动率
  138. returns = data['close'].pct_change().dropna()
  139. current_vol = returns.iloc[-20:].std() * np.sqrt(252)
  140. # 滚动计算历史波动率
  141. rolling_vol = returns.rolling(20).std() * np.sqrt(252)
  142. historical_vol = rolling_vol.iloc[-lookback:]
  143. vol_min = historical_vol.min()
  144. vol_max = historical_vol.max()
  145. if vol_max == vol_min:
  146. return 50.0
  147. iv_rank = (current_vol - vol_min) / (vol_max - vol_min) * 100
  148. return iv_rank
  149. def _calculate_trend_strength(self, data: pd.DataFrame) -> float:
  150. """计算趋势强度(用于避开关强趋势市场)"""
  151. if len(data) < 20:
  152. return 0.0
  153. # 使用ADX作为趋势强度代理
  154. high = data['high']
  155. low = data['low']
  156. close = data['close']
  157. plus_dm = high.diff().clip(lower=0)
  158. minus_dm = (-low.diff()).clip(lower=0)
  159. tr = pd.concat([high - low, (high - close.shift(1)).abs(), (low - close.shift(1)).abs()], axis=1).max(axis=1)
  160. atr = tr.rolling(14).mean()
  161. plus_di = 100 * (plus_dm.rolling(14).mean() / atr)
  162. minus_di = 100 * (minus_dm.rolling(14).mean() / atr)
  163. dx = 100 * (plus_di - minus_di).abs() / (plus_di + minus_di)
  164. adx = dx.rolling(14).mean()
  165. current_adx = adx.iloc[-1] if not pd.isna(adx.iloc[-1]) else 20
  166. return min(1.0, current_adx / 50)
  167. def _calculate_position_size(
  168. self,
  169. ecosystem: Optional[UnifiedEcosystem],
  170. confidence: float,
  171. iv_rank: float
  172. ) -> float:
  173. """计算建议仓位(卖方策略仓位保守)"""
  174. base_size = self.max_position * confidence * 0.5
  175. # IV越高,仓位越小(风险控制)
  176. if iv_rank > 90:
  177. base_size *= 0.7
  178. elif iv_rank > 85:
  179. base_size *= 0.85
  180. if ecosystem:
  181. if ecosystem.macro.regime in self.preferred_regimes:
  182. base_size *= 1.0
  183. else:
  184. base_size *= 0.6
  185. return min(self.max_position, base_size)
  186. def _check_regime_match(self, ecosystem: Any) -> float:
  187. """检查生态适配度"""
  188. if not ecosystem or not hasattr(ecosystem, 'macro'):
  189. return 0.7
  190. if ecosystem.macro.regime in self.avoid_regimes:
  191. return 0.3
  192. elif ecosystem.macro.regime in self.preferred_regimes:
  193. return 1.1
  194. else:
  195. return 0.8