You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

285 lines
11 KiB

# 资金监控模块
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
}