Qionk/a-share-quant
0
1"""2波段斐波那契分析(替代短期斐波那契)3使用 scipy.signal.find_peaks 自动检测完整波段高低点4基于波段起点/终点计算标准黄金分割线5"""6 7import numpy as np8import pandas as pd9from scipy.signal import find_peaks10 11# 斐波那契比率12FIB_LEVELS = [0.0, 0.191, 0.236, 0.382, 0.5, 0.618, 0.786, 0.809, 1.0, 1.272, 1.618, 2.0]13FIB_NAMES = {14 0.0: "波段起点", 0.191: "19.1%", 0.236: "23.6%", 0.382: "38.2%",15 0.5: "50.0%(黄金)", 0.618: "61.8%", 0.786: "78.6%", 0.809: "80.9%",16 1.0: "波段终点", 1.272: "127.2%", 1.618: "161.8%", 2.0: "200.0%",17}18FIB_COLORS = {19 0.0: "blue", 0.191: "lightgreen", 0.236: "orange", 0.382: "green",20 0.5: "gold", 0.618: "darkgreen", 0.786: "darkgreen", 0.809: "green",21 1.0: "red", 1.272: "orange", 1.618: "darkorange", 2.0: "red",22}23 24 25def detect_wave_levels(df: pd.DataFrame, wave_window: int = 60,26 min_wave_return: float = 0.05) -> dict:27 """28 自动检测最近完整波段的高低点。29 30 参数:31 wave_window: 检测窗口(交易日数)32 min_wave_return: 最小波段涨跌幅(默认5%,小于此幅度视为震荡)33 34 返回:35 trend: 趋势方向(上涨波段/下跌波段/震荡趋势)36 wave_start/wave_end: 波段起/终点价格37 start_date/end_date: 起/终点日期38 wave_return: 波段累计涨跌幅39 is_valid_wave: 是否为有效波段40 """41 recent_df = df.tail(wave_window).copy()42 close_prices = recent_df['close'].values43 44 if len(close_prices) < 30:45 recent_high = float(close_prices.max())46 recent_low = float(close_prices.min())47 return {48 'trend': '震荡趋势',49 'wave_start': recent_low,50 'wave_end': recent_high,51 'start_date': recent_df.index[recent_df['close'].idxmin()],52 'end_date': recent_df.index[recent_df['close'].idxmax()],53 'wave_return': (recent_high - recent_low) / recent_low if recent_low > 0 else 0,54 'is_valid_wave': False,55 }56 57 # 检测局部高低点(距离至少10天)58 peaks, _ = find_peaks(close_prices, distance=10)59 troughs, _ = find_peaks(-close_prices, distance=10)60 61 if len(peaks) == 0 or len(troughs) == 0:62 recent_high = float(close_prices.max())63 recent_low = float(close_prices.min())64 high_idx = close_prices.argmax()65 low_idx = close_prices.argmin()66 return {67 'trend': '震荡趋势',68 'wave_start': recent_low,69 'wave_end': recent_high,70 'start_date': recent_df.index[low_idx],71 'end_date': recent_df.index[high_idx],72 'wave_return': (recent_high - recent_low) / recent_low if recent_low > 0 else 0,73 'is_valid_wave': False,74 }75 76 last_peak_idx = peaks[-1]77 last_trough_idx = troughs[-1]78 last_peak_price = float(close_prices[last_peak_idx])79 last_trough_price = float(close_prices[last_trough_idx])80 last_peak_date = recent_df.index[last_peak_idx]81 last_trough_date = recent_df.index[last_trough_idx]82 83 if last_peak_date > last_trough_date:84 # 高点在低点之后 → 上涨波段85 wave_return = (last_peak_price - last_trough_price) / last_trough_price86 is_valid = wave_return >= min_wave_return87 return {88 'trend': '上涨波段' if is_valid else '震荡趋势',89 'wave_start': last_trough_price,90 'wave_end': last_peak_price,91 'start_date': last_trough_date,92 'end_date': last_peak_date,93 'wave_return': wave_return,94 'is_valid_wave': is_valid,95 }96 else:97 # 低点在高点之后 → 下跌波段98 wave_return = (last_peak_price - last_trough_price) / last_peak_price99 is_valid = abs(wave_return) >= min_wave_return100 return {101 'trend': '下跌波段' if is_valid else '震荡趋势',102 'wave_start': last_peak_price,103 'wave_end': last_trough_price,104 'start_date': last_peak_date,105 'end_date': last_trough_date,106 'wave_return': wave_return,107 'is_valid_wave': is_valid,108 }109 110 111def calculate_wave_fibonacci(wave_info: dict) -> list:112 """113 基于波段信息计算所有标准黄金分割价位。114 115 返回 list[dict]:116 name: 价位名称, price: 价格, type: 类型(支撑/阻力/目标), color: 显示颜色117 """118 trend = wave_info['trend']119 wave_start = wave_info['wave_start']120 wave_end = wave_info['wave_end']121 wave_range = abs(wave_end - wave_start)122 if wave_range < 0.01 * wave_start:123 wave_range = 0.01 * wave_start124 125 levels = []126 127 if trend == "上涨波段":128 # 回撤支撑位(上涨后回调)129 levels.append({'name': '0.191支撑', 'price': round(wave_end - wave_range * 0.191, 2),130 'type': '弱支撑', 'color': 'lightgreen', 'level': 0.191})131 levels.append({'name': '0.382支撑', 'price': round(wave_end - wave_range * 0.382, 2),132 'type': '强支撑', 'color': 'green', 'level': 0.382})133 levels.append({'name': '0.5支撑', 'price': round(wave_end - wave_range * 0.5, 2),134 'type': '黄金支撑', 'color': 'gold', 'level': 0.5})135 levels.append({'name': '0.618支撑', 'price': round(wave_end - wave_range * 0.618, 2),136 'type': '极强支撑', 'color': 'darkgreen', 'level': 0.618})137 levels.append({'name': '0.809支撑', 'price': round(wave_end - wave_range * 0.809, 2),138 'type': '强支撑', 'color': 'green', 'level': 0.809})139 # 扩展目标位140 levels.append({'name': '1.272目标', 'price': round(wave_end + wave_range * 0.272, 2),141 'type': '第一目标', 'color': 'orange', 'level': 1.272})142 levels.append({'name': '1.618目标', 'price': round(wave_end + wave_range * 0.618, 2),143 'type': '第二目标', 'color': 'darkorange', 'level': 1.618})144 levels.append({'name': '2.0目标', 'price': round(wave_end + wave_range * 1.0, 2),145 'type': '第三目标', 'color': 'red', 'level': 2.0})146 # 波段高低点147 levels.append({'name': '波段高点', 'price': round(wave_end, 2),148 'type': '阻力位', 'color': 'red', 'level': 1.0})149 levels.append({'name': '波段低点', 'price': round(wave_start, 2),150 'type': '强支撑', 'color': 'darkgreen', 'level': 0.0})151 152 elif trend == "下跌波段":153 # 反弹阻力位(下跌后反弹)154 levels.append({'name': '0.191阻力', 'price': round(wave_end + wave_range * 0.191, 2),155 'type': '弱阻力', 'color': 'lightcoral', 'level': 0.191})156 levels.append({'name': '0.382阻力', 'price': round(wave_end + wave_range * 0.382, 2),157 'type': '强阻力', 'color': 'red', 'level': 0.382})158 levels.append({'name': '0.5阻力', 'price': round(wave_end + wave_range * 0.5, 2),159 'type': '黄金阻力', 'color': 'gold', 'level': 0.5})160 levels.append({'name': '0.618阻力', 'price': round(wave_end + wave_range * 0.618, 2),161 'type': '极强阻力', 'color': 'darkred', 'level': 0.618})162 levels.append({'name': '0.809阻力', 'price': round(wave_end + wave_range * 0.809, 2),163 'type': '强阻力', 'color': 'red', 'level': 0.809})164 # 扩展目标位(下跌延续)165 levels.append({'name': '1.272目标', 'price': round(wave_end - wave_range * 0.272, 2),166 'type': '第一目标', 'color': 'orange', 'level': 1.272})167 levels.append({'name': '1.618目标', 'price': round(wave_end - wave_range * 0.618, 2),168 'type': '第二目标', 'color': 'darkorange', 'level': 1.618})169 levels.append({'name': '2.0目标', 'price': round(wave_end - wave_range * 1.0, 2),170 'type': '第三目标', 'color': 'green', 'level': 2.0})171 # 波段高低点172 levels.append({'name': '波段低点', 'price': round(wave_end, 2),173 'type': '支撑位', 'color': 'green', 'level': 0.0})174 levels.append({'name': '波段高点', 'price': round(wave_start, 2),175 'type': '强阻力', 'color': 'darkred', 'level': 1.0})176 177 else:178 # 震荡趋势179 low = min(wave_start, wave_end)180 high = max(wave_start, wave_end)181 levels.append({'name': '震荡下轨', 'price': round(low, 2),182 'type': '支撑位', 'color': 'green', 'level': 0.0})183 levels.append({'name': '震荡中轨', 'price': round((low + high) / 2, 2),184 'type': '中性位', 'color': 'gray', 'level': 0.5})185 levels.append({'name': '震荡上轨', 'price': round(high, 2),186 'type': '阻力位', 'color': 'red', 'level': 1.0})187 188 levels.sort(key=lambda x: x['price'])189 return levels190 191 192def generate_wave_fib_signals(current_price: float, fib_levels: list,193 wave_info: dict, model_prediction: float = 0,194 relative_volume: float = 1.0,195 sensitivity: float = 0.015) -> list:196 """197 基于波段斐波那契价位生成买卖信号。198 199 参数:200 current_price: 当前价格201 fib_levels: calculate_wave_fibonacci 返回的价位列表202 wave_info: detect_wave_levels 返回的波段信息203 model_prediction: 模型预测的收益率%(正=看涨,负=看跌)204 relative_volume: 相对成交量(与20日均值比)205 sensitivity: 信号灵敏度(默认1.5%,比短期分析更宽松)206 207 返回 list[dict]: 信号列表208 """209 trend = wave_info['trend']210 is_valid_wave = wave_info['is_valid_wave']211 signals = []212 213 if not is_valid_wave:214 signals.append({215 'type': '观望',216 'level': '无',217 'price': current_price,218 'reason': '未检测到有效的上涨/下跌波段,建议观望',219 'confidence': 1,220 })221 return signals222 223 for level_info in fib_levels:224 price_diff = abs(current_price - level_info['price']) / current_price if current_price > 0 else float('inf')225 226 if price_diff <= sensitivity:227 level_name = level_info['name']228 level_type = level_info['type']229 confidence = 3230 231 # 结合模型预测调整置信度232 if (trend == "上涨波段" and model_prediction > 0) or \233 (trend == "下跌波段" and model_prediction < 0):234 confidence += 1235 elif (trend == "上涨波段" and model_prediction < 0) or \236 (trend == "下跌波段" and model_prediction > 0):237 confidence -= 1238 239 # 结合成交量调整240 if relative_volume > 1.5:241 confidence += 1242 elif relative_volume < 0.7:243 confidence -= 1244 245 confidence = max(1, min(5, confidence))246 247 if trend == "上涨波段":248 if "支撑" in level_type:249 if model_prediction > 0:250 if "黄金支撑" in level_type or "极强支撑" in level_type:251 signal_type = '强买入'252 else:253 signal_type = '买入'254 signals.append({255 'type': signal_type,256 'level': level_name,257 'price': level_info['price'],258 'reason': f'上涨波段回调至{level_name},量价配合,模型看涨',259 'confidence': confidence,260 })261 else:262 signals.append({263 'type': '观望',264 'level': level_name,265 'price': level_info['price'],266 'reason': f'上涨波段回调至{level_name},但模型看跌,建议观望',267 'confidence': confidence,268 })269 elif "目标" in level_type:270 signals.append({271 'type': '止盈',272 'level': level_name,273 'price': level_info['price'],274 'reason': f'价格到达上涨波段{level_name},建议分批止盈',275 'confidence': confidence,276 })277 elif level_name == "波段高点":278 if model_prediction > 0:279 signals.append({280 'type': '持有/加仓',281 'level': level_name,282 'price': level_info['price'],283 'reason': '价格突破波段高点,趋势延续,目标看1.272扩展位',284 'confidence': confidence,285 })286 else:287 signals.append({288 'type': '止盈',289 'level': level_name,290 'price': level_info['price'],291 'reason': '价格触及波段高点阻力,模型看跌,建议止盈',292 'confidence': confidence,293 })294 295 elif trend == "下跌波段":296 if "阻力" in level_type:297 if model_prediction < 0:298 if "黄金阻力" in level_type or "极强阻力" in level_type:299 signal_type = '强卖出'300 else:301 signal_type = '卖出'302 signals.append({303 'type': signal_type,304 'level': level_name,305 'price': level_info['price'],306 'reason': f'下跌波段反弹至{level_name},量价配合,模型看跌',307 'confidence': confidence,308 })309 else:310 signals.append({311 'type': '观望',312 'level': level_name,313 'price': level_info['price'],314 'reason': f'下跌波段反弹至{level_name},但模型看涨,建议观望',315 'confidence': confidence,316 })317 elif "目标" in level_type:318 signals.append({319 'type': '轻仓抄底',320 'level': level_name,321 'price': level_info['price'],322 'reason': f'价格到达下跌波段{level_name},可轻仓尝试抄底',323 'confidence': confidence,324 })325 elif level_name == "波段低点":326 if model_prediction < 0:327 signals.append({328 'type': '止损/清仓',329 'level': level_name,330 'price': level_info['price'],331 'reason': '价格跌穿波段低点,趋势延续,目标看1.272扩展位',332 'confidence': confidence,333 })334 else:335 signals.append({336 'type': '轻仓抄底',337 'level': level_name,338 'price': level_info['price'],339 'reason': '价格触及波段低点支撑,模型看涨,可轻仓抄底',340 'confidence': confidence,341 })342 343 # 如果没有匹配信号,给出默认建议344 if not signals:345 signals.append({346 'type': '持有/观望',347 'level': '无',348 'price': current_price,349 'reason': '当前价格未接近任何关键黄金分割位,继续持有或观望',350 'confidence': 2,351 })352 353 return signals