# 资金监控模块 import pandas as pd import numpy as np from typing import Dict, Optional, List from qihuo_analyzer.core.models import StrategyConfig class FundFlowMonitor: """资金流向监控器""" def __init__(self, config: Optional[StrategyConfig] = None): self.config = config or StrategyConfig() def analyze_fund_flow(self, data: pd.DataFrame) -> Dict: """分析资金流向""" result = {} # 分析持仓量变化 oi_analysis = self._analyze_open_interest(data) result.update(oi_analysis) # 分析量价关系 volume_price_analysis = self._analyze_volume_price_relationship(data) result.update(volume_price_analysis) # 分析资金流向强度 fund_flow_strength = self._calculate_fund_flow_strength(data) result['fund_flow_strength'] = fund_flow_strength # 分析资金集中度 fund_concentration = self._analyze_fund_concentration(data) result.update(fund_concentration) # 综合资金面信号 fund_signal = self._generate_fund_signal(result) result['fund_signal'] = fund_signal return result def _analyze_open_interest(self, data: pd.DataFrame) -> Dict: """分析持仓量变化""" # 计算持仓量变化 data['oi_change'] = data['open_interest'].diff() data['oi_change_pct'] = data['oi_change'] / data['open_interest'].shift(1) * 100 # 最近N天持仓量变化 recent_oi_change = data['oi_change'].tail(5).sum() recent_oi_change_pct = data['oi_change_pct'].tail(5).mean() # 持仓量趋势 oi_trend = self._judge_oi_trend(data['open_interest']) # 持仓量与价格关系 oi_price_relationship = self._judge_oi_price_relationship(data) return { 'recent_oi_change': recent_oi_change, 'recent_oi_change_pct': recent_oi_change_pct, 'oi_trend': oi_trend, 'oi_price_relationship': oi_price_relationship } def _analyze_volume_price_relationship(self, data: pd.DataFrame) -> Dict: """分析量价关系""" # 计算价格变化 data['price_change'] = data['close'].diff() data['price_change_pct'] = data['price_change'] / data['close'].shift(1) * 100 # 计算成交量变化 data['volume_change'] = data['volume'].diff() data['volume_change_pct'] = data['volume_change'] / data['volume'].shift(1) * 100 # 量价配合度 volume_price_fit = self._calculate_volume_price_fit(data) # 量价背离检测 divergence = self._detect_volume_price_divergence(data) # 成交量趋势 volume_trend = self._judge_volume_trend(data['volume']) return { 'volume_price_fit': volume_price_fit, 'divergence': divergence, 'volume_trend': volume_trend } def _calculate_fund_flow_strength(self, data: pd.DataFrame) -> float: """计算资金流向强度""" # 计算资金流向 # 简化计算:(收盘价 - 开盘价) * 成交量 fund_flow = ((data['close'] - data['open']) * data['volume']).tail(20).sum() # 归一化到-100到100 if fund_flow > 0: strength = min(100, (fund_flow / data['volume'].tail(20).sum()) * 1000) else: strength = max(-100, (fund_flow / data['volume'].tail(20).sum()) * 1000) return strength def _analyze_fund_concentration(self, data: pd.DataFrame) -> Dict: """分析资金集中度""" # 计算成交量集中度(前5天成交量占比) recent_volume = data['volume'].tail(5).sum() total_volume = data['volume'].tail(30).sum() volume_concentration = recent_volume / total_volume if total_volume > 0 else 0 # 计算持仓量集中度(最近持仓量变化占比) recent_oi_change = abs(data['oi_change'].tail(5).sum()) total_oi = data['open_interest'].iloc[-1] oi_concentration = recent_oi_change / total_oi if total_oi > 0 else 0 return { 'volume_concentration': volume_concentration, 'oi_concentration': oi_concentration } def _judge_oi_trend(self, oi_series: pd.Series) -> str: """判断持仓量趋势""" # 使用简单移动平均线判断趋势 ma5 = oi_series.rolling(window=5).mean().iloc[-1] ma20 = oi_series.rolling(window=20).mean().iloc[-1] if ma5 > ma20 * 1.02: return 'strong_increasing' elif ma5 > ma20: return 'increasing' elif ma5 < ma20 * 0.98: return 'strong_decreasing' elif ma5 < ma20: return 'decreasing' else: return 'stable' def _judge_oi_price_relationship(self, data: pd.DataFrame) -> Dict: """判断持仓量与价格关系""" recent_data = data.tail(10) # 计算价格变化 if 'price_change' not in recent_data.columns: recent_data['price_change'] = recent_data['close'].diff() # 计算持仓量变化 if 'oi_change' not in recent_data.columns: recent_data['oi_change'] = recent_data['open_interest'].diff() # 计算平均价格变化和平均持仓量变化 avg_price_change = recent_data['price_change'].mean() avg_oi_change = recent_data['oi_change'].mean() if avg_price_change > 0 and avg_oi_change > 0: return 'price_up_oi_up' # 价涨量增 elif avg_price_change > 0 and avg_oi_change < 0: return 'price_up_oi_down' # 价涨量减 elif avg_price_change < 0 and avg_oi_change > 0: return 'price_down_oi_up' # 价跌量增 elif avg_price_change < 0 and avg_oi_change < 0: return 'price_down_oi_down' # 价跌量减 else: return 'stable' def _calculate_volume_price_fit(self, data: pd.DataFrame) -> float: """计算量价配合度""" recent_data = data.tail(20) # 计算量价配合的次数 fit_count = 0 total_count = len(recent_data) - 1 for i in range(1, len(recent_data)): price_change = recent_data['price_change'].iloc[i] volume_change = recent_data['volume_change'].iloc[i] # 量价配合:价格上涨成交量增加,价格下跌成交量减少 if (price_change > 0 and volume_change > 0) or (price_change < 0 and volume_change < 0): fit_count += 1 fit_ratio = fit_count / total_count if total_count > 0 else 0 return fit_ratio * 100 def _detect_volume_price_divergence(self, data: pd.DataFrame) -> str: """检测量价背离""" recent_data = data.tail(10) # 计算价格趋势(斜率) price_slope = np.polyfit(range(len(recent_data)), recent_data['close'], 1)[0] # 计算成交量趋势(斜率) volume_slope = np.polyfit(range(len(recent_data)), recent_data['volume'], 1)[0] # 判断背离 if price_slope > 0 and volume_slope < 0: return 'bearish_divergence' # 价格上涨,成交量下降,看跌背离 elif price_slope < 0 and volume_slope > 0: return 'bullish_divergence' # 价格下降,成交量上升,看涨背离 else: return 'no_divergence' def _judge_volume_trend(self, volume_series: pd.Series) -> str: """判断成交量趋势""" ma5 = volume_series.rolling(window=5).mean().iloc[-1] ma20 = volume_series.rolling(window=20).mean().iloc[-1] if ma5 > ma20 * 1.1: return 'strong_increasing' elif ma5 > ma20: return 'increasing' elif ma5 < ma20 * 0.9: return 'strong_decreasing' elif ma5 < ma20: return 'decreasing' else: return 'stable' def _generate_fund_signal(self, fund_analysis: Dict) -> str: """生成资金面信号""" signals = [] # 持仓量信号 if fund_analysis.get('oi_trend') in ['strong_increasing', 'increasing']: if fund_analysis.get('oi_price_relationship') == 'price_up_oi_up': signals.append('bullish') elif fund_analysis.get('oi_price_relationship') == 'price_down_oi_up': signals.append('bearish') # 量价关系信号 if fund_analysis.get('volume_price_fit') > 60: if fund_analysis.get('volume_trend') in ['strong_increasing', 'increasing']: signals.append('bullish') # 量价背离信号 if fund_analysis.get('divergence') == 'bullish_divergence': signals.append('bullish') elif fund_analysis.get('divergence') == 'bearish_divergence': signals.append('bearish') # 资金流向强度信号 fund_flow_strength = fund_analysis.get('fund_flow_strength', 0) if fund_flow_strength > 30: signals.append('bullish') elif fund_flow_strength < -30: signals.append('bearish') # 综合信号 if signals.count('bullish') > signals.count('bearish'): return 'bullish' elif signals.count('bearish') > signals.count('bullish'): return 'bearish' else: return 'neutral' def detect_volume_spikes(self, data: pd.DataFrame, threshold: float = 2.0) -> List[int]: """检测成交量异动""" # 计算成交量移动平均线和标准差 data['volume_ma'] = data['volume'].rolling(window=20).mean() data['volume_std'] = data['volume'].rolling(window=20).std() # 计算成交量偏离度 data['volume_zscore'] = (data['volume'] - data['volume_ma']) / data['volume_std'] # 找出成交量异动的位置 spikes = data[data['volume_zscore'] > threshold].index return list(spikes) def analyze_institutional_activity(self, data: pd.DataFrame) -> Dict: """分析机构活动""" # 基于持仓量和成交量的变化分析机构活动 # 机构通常会引起较大的持仓量变化 # 计算大资金活动指标 data['institutional_activity'] = data['oi_change'] * abs(data['price_change']) # 最近机构活动强度 recent_institutional_activity = data['institutional_activity'].tail(5).sum() # 机构活动趋势 institutional_trend = 'increasing' if recent_institutional_activity > 0 else 'decreasing' return { 'recent_institutional_activity': recent_institutional_activity, 'institutional_trend': institutional_trend }