4. 布林带多周期突破信号
任务目标
本案例实现一个基于日、周、月三个周期布林带(Bollinger Bands)的突破信号生成器。当收盘价同时突破日线与周线的布林带上轨时产生“强突破买入”信号;当收盘价同时跌破日线与周线的下轨时产生“强突破卖出”信号。月线布林带作为趋势过滤条件——仅当价格位于月线中轨上方时允许买入信号,位于月线中轨下方时允许卖出信号,从而过滤逆大势的假突破。
实现方案
使用Python与pandas实现。布林带参数采用20日/20周/20月周期,标准差倍数取2。数据源使用yfinance获取股票日线行情,通过resample将日线聚合为周线与月线。核心逻辑为:计算三个周期的布林带上下轨及中轨,将周/月布林带值通过reindex与ffill对齐到日线,最后按上述规则生成信号。
完整代码
import pandas as pd
import numpy as np
import yfinance as yf
def add_bollinger(df, window=20, num_std=2):
"""计算布林带,返回带中轨/上轨/下轨的DataFrame"""
df = df.copy()
df['mid'] = df['Close'].rolling(window).mean()
std = df['Close'].rolling(window).std()
df['upper'] = df['mid'] + num_std * std
df['lower'] = df['mid'] - num_std * std
return df
def resample_weekly_monthly(df):
"""将日线聚合为周线与月线(取每周/每月最后一个交易日)"""
df.index = pd.to_datetime(df.index)
weekly = df['Close'].resample('W').last().dropna()
monthly = df['Close'].resample('ME').last().dropna()
return weekly, monthly
def generate_signals(ticker, start='2023-01-01', end='2024-12-31'):
# 1. 获取日线数据
raw = yf.download(ticker, start=start, end=end, progress=False)
if raw.empty:
raise ValueError(f"未获取到 {ticker} 的数据,请检查代码或日期范围")
daily = raw[['Close']].copy()
daily.columns = ['Close']
# 2. 计算日线布林带
daily_boll = add_bollinger(daily)
# 3. 聚合周线与月线,并计算各自布林带
weekly_series, monthly_series = resample_weekly_monthly(daily)
weekly_df = add_bollinger(weekly_series.to_frame('Close'))
monthly_df = add_bollinger(monthly_series.to_frame('Close'))
# 4. 将周/月布林带对齐到日线(向前填充)
weekly_boll = weekly_df[['mid', 'upper', 'lower']].reindex(
daily.index, method='ffill')
monthly_boll = monthly_df[['mid', 'upper', 'lower']].reindex(
daily.index, method='ffill')
# 5. 合并所有布林带值
result = pd.DataFrame(index=daily.index)
result['Close'] = daily['Close']
result['D_mid'] = daily_boll['mid']
result['D_upper'] = daily_boll['upper']
result['D_lower'] = daily_boll['lower']
result['W_upper'] = weekly_boll['upper']
result['W_lower'] = weekly_boll['lower']
result['M_mid'] = monthly_boll['mid']
# 6. 生成信号
# 买入:收盘价突破日上轨 AND 突破周上轨 AND 收盘价 > 月中轨
buy_cond = (result['Close'] > result['D_upper']) & \
(result['Close'] > result['W_upper']) & \
(result['Close'] > result['M_mid'])
# 卖出:收盘价跌破日下轨 AND 跌破周下轨 AND 收盘价 < 月中轨
sell_cond = (result['Close'] < result['D_lower']) & \
(result['Close'] < result['W_lower']) & \
(result['Close'] < result['M_mid'])
result['Signal'] = 0
result.loc[buy_cond, 'Signal'] = 1
result.loc[sell_cond, 'Signal'] = -1
# 7. 仅保留有信号的日期
signals = result[result['Signal'] != 0]
return result, signals
if __name__ == '__main__':
# 示例:以苹果股票为例
full_data, signal_days = generate_signals('AAPL')
print(f"总交易日数: {len(full_data)}")
print(f"产生信号天数: {len(signal_days)}")
if not signal_days.empty:
print("\n信号明细(前10条):")
print(signal_days[['Close', 'D_upper', 'W_upper', 'M_mid', 'Signal']].head(10))
else:
print("所选时间范围内无突破信号")
运行方式
- 安装依赖:
pip install pandas numpy yfinance - 将上述代码保存为
bollinger_signal.py - 运行:
python bollinger_signal.py
验证方法
程序运行后应输出总交易日数(通常为数百天)和信号天数。若信号天数大于0,则打印前10条信号明细。判定通过的准则:
- 程序无异常退出,且输出的信号明细中每条记录的
Close均大于对应D_upper和W_upper(买入信号)或小于D_lower和W_lower(卖出信号); - 买入信号的
Close均大于M_mid,卖出信号的Close均小于M_mid; - 若信号数为0,说明该股票在所选区间内未出现同时满足多周期条件的极端突破,可更换股票代码(如
'MSFT'、'600519.SS')或扩大日期范围重新验证。
常见问题
- 周线/月线数据不足:若起始日期距今不足20周或20个月,对应布林带会出现NaN,导致无法产生信号。建议将start参数至少提前6个月。
- 数据源限制:yfinance对部分A股代码支持有限,若下载失败可改用美股代码或使用本地CSV数据替换下载部分。
- 信号延迟:周/月布林带使用ffill填充,意味着当周/月布林带尚未收盘更新时,使用的是上一周期的值,这是避免未来函数的必要处理。