封面图:Python代码与股市K线图结合,科技感程序员工作场景
Python量化入门:用布林带策略抓住A股强势股
代码+实战,从小白到能跑策略
开篇:为什么你应该学Python量化
2024年以来,A股市场结构性行情愈发明显。科技股动不动就拉出涨停板,新能源板块轮流拉升,半导体国产替代的故事讲了一轮又一轮。但真正能在这类行情里赚到钱的人,往往不是靠感觉,而是靠一套可以重复执行的量化策略。
很多人觉得量化很高深,是专业人士的专属。但其实,对于普通股民来说,学会用Python写一个简单的技术指标策略,并不需要多高的数学门槛。你只需要懂最基本的语法,会装几个库,就能搭建起一套属于自己的选股系统。门槛真的没有想象中那么高,关键是愿意动手去做。
我自己也是从完全不懂量化开始的。2019年接触Python,最早只是想爬一爬数据,后来慢慢学会了用akshare抓行情数据,用pandas处理数据,用matplotlib画图,再后来开始研究一些经典的技术指标策略,比如MACD、布林带、RSI等等。回头看,那段从零开始的学习过程虽然磕磕绊绊,但每一步都算数。
今天这篇文章,我用最通俗的方式,手把手教你用Python实现一个布林带量化策略。这个策略的核心逻辑非常清晰:当股价突破布林带上轨时,可能是强势信号;当股价跌破布林带下轨时,可能是弱势信号。配合成交量验证,能在一定程度上过滤假信号。整个过程不需要复杂的数学知识,只需要懂最基础的Python语法。
核心观点:布林带策略不是圣杯,它有适用场景也有失效场景。学会策略逻辑、知道策略的边界,比盲目迷信技术指标更重要。技术指标是工具,不是保证,敬畏市场永远是对的。
我知道很多散户看到"量化"两个字就本能地想关掉页面,觉得那是机构和专业人士才能玩的东西。但我想告诉你,如果你能看懂这篇文章并且跟着动手跑一遍代码,你就已经超过了市场上90%以上的散户。因为大多数人只是看看,从来不动手。而市场奖励的,永远是那些愿意动手的人。
01 布林带策略原理:附Python代码
布林带(Bollinger Bands)由约翰·布林格在1980年代提出,是技术分析中最经典的趋势跟踪指标之一。它的计算方法其实相当简单:先计算N日收盘价的移动平均线(MA),然后计算这N日收盘价的标准差(STD),最后在MA上下各加减K倍的标准差,就得到了上轨和下轨。
经典参数设置是N=20,K=2。也就是说,中轨是20日均线,上轨是中轨加上2倍标准差,下轨是中轨减去2倍标准差。从统计学角度,这个范围大约能覆盖85%的价格波动,所以当价格突破上轨或跌破下轨时,往往意味着比较极端的行情信号。这个数字不是凭空拍脑袋的,而是基于正态分布的概率计算。
在实际应用中,布林带的用法主要有三种。第一种是趋势跟踪,股价在上轨附近运行说明处于强势,可以考虑顺势做多。第二种是均值回归,股价触及下轨后反弹是经典的抄底信号。第三种是突破确认,当布林带开口收窄后再度放大,往往意味着趋势即将展开。不同的市场环境应该使用不同的策略,不能机械套用。
对于A股强势股筛选,我们的策略逻辑是:找到那些股价处于上升通道且屡创新高的个股,然后观察它们什么时候出现短暂回调到布林带中轨附近的机会。这类股票有一个特征:即使短期调整,也不会轻易跌破布林带下轨,因为市场承接力强。强势股之所以强势,是因为有资金愿意在回调时接盘,这是核心逻辑。
K线图与布林带示意图,金融图表风格展示股价与布林带轨道的关系
Python实现布林带的计算其实只需要几行代码。我们用pandas计算均线和标准差,用numpy处理数组运算,用akshare获取真实的A股数据。整个流程是:获取数据、计算指标、绘制图表、筛选信号。听起来复杂,实际上每个步骤都有成熟的库可以调用,不需要自己造轮子。
import pandas as pd
import numpy as np
import akshare as ak
# 获取股票数据
def get_stock_data(stock_code, start_date, end_date):
df = ak.stock_zh_a_hist(symbol=stock_code, start_date=start_date, end_date=end_date)
df = df[['日期', '开盘', '收盘', '最高', '最低', '成交量']]
df.columns = ['date', 'open', 'close', 'high', 'low', 'volume']
return df
# 计算布林带
def calculate_bollinger_bands(df, window=20, num_std=2):
df['ma'] = df['close'].rolling(window=window).mean()
df['std'] = df['close'].rolling(window=window).std()
df['upper'] = df['ma'] + num_std * df['std']
df['lower'] = df['ma'] - num_std * df['std']
return df
# 筛选强势股信号
def find_strong_stocks(df):
df = calculate_bollinger_bands(df)
# 当收盘价突破上轨时触发买入信号
df['signal'] = np.where(df['close'] > df['upper'], 1, 0)
return df[df['signal'] == 1]
补充说明:布林带的参数不是固定的,短线交易者可以用10日均线,长线投资者可以用30日甚至60日均线。标准差倍数通常取2,但波动性较大的品种可以适当放大到2.5或3。参数的调整需要根据具体股票特性和市场环境来定,不是死板的数字。
上面这段代码是一个最小可用版本,实际使用中还需要加入很多边界条件判断。比如,数据获取失败怎么办,股票停牌了怎么处理,成交量异常时怎么过滤。这些细节决定了策略在真实环境中的稳定性,也区分了业余爱好者和专业玩家的差距。
02 完整Python代码实现:可直接复制使用
上面展示的是核心逻辑,接下来我们给出一个完整可运行的Python脚本。这个脚本实现了从数据获取、指标计算、信号筛选到结果输出的全流程。你可以把这个代码复制到本地,直接运行看效果。重要的是先跑起来,不要追求一开始就完美。
第一步是导入所有需要的库。如果你的电脑上还没有安装akshare、pandas、matplotlib,可以通过pip install命令安装。需要提醒的是,akshare的数据源来自东方财富网,有时会因为网络原因出现获取失败的情况,建议加上异常处理和重试机制。网络不稳定是常态,而不是例外。
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
布林带量化选股策略
功能:获取A股数据,计算布林带,筛选强势股信号
"""
import pandas as pd
import numpy as np
import akshare as ak
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
from datetime import datetime, timedelta
import warnings
warnings.filterwarnings('ignore')
# 设置中文显示
plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei']
plt.rcParams['axes.unicode_minus'] = False
def get_realtime_data(stock_code):
"""获取股票实时数据"""
try:
df = ak.stock_zh_a_hist(
symbol=stock_code,
start_date=(datetime.now() - timedelta(days=120)).strftime('%Y%m%d'),
end_date=datetime.now().strftime('%Y%m%d'),
adjust='qfq'
)
df.columns = ['date', 'open', 'close', 'high', 'low', 'volume', 'amount', 'amplitude']
df['date'] = pd.to_datetime(df['date'])
return df
except Exception as e:
print(f"获取数据失败: {e}")
return None
def calculate_bollinger(df, window=20, num_std=2):
"""计算布林带指标"""
df = df.copy()
df['ma'] = df['close'].rolling(window=window).mean()
df['std'] = df['close'].rolling(window=window).std()
df['upper'] = df['ma'] + num_std * df['std']
df['middle'] = df['ma']
df['lower'] = df['ma'] - num_std * df['std']
# 计算布林带宽度(开口程度)
df['bandwidth'] = (df['upper'] - df['lower']) / df['ma'] * 100
return df
def detect_signals(df):
"""检测买入卖出信号"""
df = df.copy()
# 买入信号:收盘价从下轨上方回升,或者突破上轨
df['buy_signal'] = (
(df['close'] > df['lower']) &
(df['close'].shift(1) <= df['lower'].shift(1))
) | (
(df['close'] > df['upper']) &
(df['close'].shift(1) <= df['upper'].shift(1))
)
# 卖出信号:收盘价从上轨回落,或者跌破中轨
df['sell_signal'] = (
(df['close'] < df['upper']) &
(df['close'].shift(1) >= df['upper'].shift(1))
) | (
(df['close'] < df['ma']) &
(df['close'].shift(1) >= df['ma'].shift(1))
)
return df
def plot_bollinger(df, stock_code, save_path=None):
"""绘制布林带图表"""
fig, ax = plt.subplots(figsize=(14, 7))
# 绘制K线
ax.plot(df['date'], df['close'], label='收盘价', color='#333333', linewidth=1.5)
# 绘制布林带
ax.plot(df['date'], df['upper'], label='上轨', color='#e74c3c', linestyle='--', linewidth=1.2)
ax.plot(df['date'], df['middle'], label='中轨(MA20)', color='#3498db', linestyle='-', linewidth=1.2)
ax.plot(df['date'], df['lower'], label='下轨', color='#27ae60', linestyle='--', linewidth=1.2)
ax.fill_between(df['date'], df['upper'], df['lower'], alpha=0.1, color='#3498db')
# 标记买入信号
buy_points = df[df['buy_signal']]
ax.scatter(buy_points['date'], buy_points['close'], marker='^', color='#e74c3c', s=100, label='买入信号', zorder=5)
# 标记卖出信号
sell_points = df[df['sell_signal']]
ax.scatter(sell_points['date'], sell_points['close'], marker='v', color='#27ae60', s=100, label='卖出信号', zorder=5)
ax.set_title(f'{stock_code} 布林带策略图', fontsize=16, fontweight='bold')
ax.set_xlabel('日期', fontsize=12)
ax.set_ylabel('价格', fontsize=12)
ax.legend(loc='upper left')
ax.grid(True, alpha=0.3)
ax.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m'))
plt.xticks(rotation=45)
plt.tight_layout()
if save_path:
plt.savefig(save_path, dpi=150)
print(f"图表已保存到: {save_path}")
plt.close()
# 批量扫描多只股票
def scan_stocks(stock_list):
"""扫描多个股票寻找信号"""
results = []
for code in stock_list:
print(f"正在分析 {code}...")
df = get_realtime_data(code)
if df is None or len(df) < 30:
continue
df = calculate_bollinger(df)
df = detect_signals(df)
# 获取最新信号
latest = df.iloc[-1]
if latest['buy_signal']:
results.append({
'code': code,
'close': latest['close'],
'upper': latest['upper'],
'lower': latest['lower'],
'ma': latest['ma'],
'bandwidth': latest['bandwidth']
})
print(f" 发现买入信号: {code}")
return results
if __name__ == '__main__':
# 测试单只股票
test_code = '000001' # 平安银行
df = get_realtime_data(test_code)
if df is not None:
df = calculate_bollinger(df)
df = detect_signals(df)
plot_bollinger(df, test_code, save_path='bollinger_demo.png')
# 打印最新数据
print("\n最新布林带数据:")
print(df[['date', 'close', 'upper', 'middle', 'lower']].tail(10))代码编辑器界面,深色主题,Python代码展示
这段代码有几个地方值得特别注意。第一是异常处理,akshare在网络波动时可能会抛出异常,我们在get_realtime_data函数里加了try-except,这样即使某只股票数据获取失败,也不会影响整个程序的运行。第二是布林带开口的判断,bandwidth这个指标可以帮助我们识别即将突破的品种,当布林带收窄到极限时,往往意味着大幅波动即将到来。这个指标在实战中非常有用。
重点提醒:这段代码只是演示策略逻辑,实盘使用前必须加入仓位管理、止损逻辑和风险控制模块。没有任何策略是100%赚钱的,敬畏市场永远是对的。一套没有止损逻辑的策略,在极端行情里可能一次亏光所有利润。
我见过太多人拿着一个看起来不错的策略就冲进市场,结果遇到一次极端行情就爆仓。量化策略的核心不是找出圣杯,而是建立一套完整的交易系统,包括选股逻辑、买入条件、卖出条件、仓位管理、风险控制等等。单独一个技术指标什么都不是,只有放在完整的系统里才有意义。
03 今日实战:用策略分析当前市场
理论讲完了,现在我们用真实数据来检验一下这个策略的实战效果。我选取了几个当前市场关注度较高的板块代表性股票,用布林带策略进行回测分析。通过实际数据检验,你才能真正理解策略的有效性和局限性。
以半导体板块为例,近期受国产替代政策催化,多只个股走出强势行情。我们用Python脚本抓取相关股票近三个月的数据,计算布林带,观察哪些股票出现了突破信号。从回测结果来看,某些强势股确实在突破布林带上轨后继续上涨,收益率可观。但这不代表所有突破上轨的股票都能涨,还是要看板块和市场整体环境。
但这里要特别强调一个关键点:A股市场存在明显的板块轮动效应。某一策略在某个阶段可能非常有效,但到了另一个阶段可能连续失效。布林带策略尤其容易在震荡行情里吃亏,因为股价会反复穿越上下轨,产生大量假信号。这个问题没有完美的解决方案,只能通过组合其他指标来过滤。
如何解决这个问题?我的经验是配合成交量一起使用。当股价突破布林带上轨的同时,成交量也明显放大,这个信号的可靠性就会大大提高。因为有资金主动买入,才能推动股价持续上涨,而不是昙花一现的假突破。量价配合是技术分析中最基本但也最有效的原则之一。
量化回测收益曲线图,绿色上涨趋势展示策略效果
下面展示一个简单的回测框架,帮助你评估策略在历史数据上的表现。回测的核心思路是:设定初始资金,模拟买入卖出操作,计算收益率和最大回撤。这里我们用一个简化版本,演示如何用Python实现回测逻辑。完整的回测系统要复杂得多,但这个最小版本足够让你理解核心原理。
def backtest_bollinger_strategy(df, initial_capital=100000, position_size=0.1):
"""
布林带策略回测
买入条件:收盘价从下轨反弹且成交量放大1.5倍
卖出条件:收盘价从上轨回落或跌幅超过5%
"""
cash = initial_capital
position = 0
trades = []
for i in range(len(df)):
if i < 20:
continue
row = df.iloc[i]
prev_row = df.iloc[i-1]
# 买入信号
if position == 0:
if (row['close'] > row['lower'] and prev_row['close'] <= prev_row['lower']
and row['volume'] > prev_row['volume'] * 1.5):
shares = int(cash * position_size / row['close'])
if shares > 0:
cost = shares * row['close']
cash -= cost
position = shares
trades.append({
'date': row['date'],
'action': 'BUY',
'price': row['close'],
'shares': shares,
'cash': cash
})
# 卖出信号
elif position > 0:
if (row['close'] < row['upper'] and prev_row['close'] >= prev_row['upper']
or row['close'] < df.iloc[i-1]['close'] * 0.95):
revenue = position * row['close']
cash += revenue
trades.append({
'date': row['date'],
'action': 'SELL',
'price': row['close'],
'shares': position,
'cash': cash
})
position = 0
# 计算收益率
final_capital = cash + position * df.iloc[-1]['close']
total_return = (final_capital - initial_capital) / initial_capital * 100
return {
'initial_capital': initial_capital,
'final_capital': final_capital,
'total_return': total_return,
'total_trades': len(trades),
'trades': trades
}
# 使用示例
result = backtest_bollinger_strategy(df)
print(f"总收益率: {result['total_return']:.2f}%")
print(f"总交易次数: {result['total_trades']}")
print(f"最终资金: {result['final_capital']:.2f}")
实盘经验:回测结果仅供参考,不代表未来收益。A股市场有涨跌幅限制、印花税、交易佣金等成本因素,这些在回测中往往被忽略。建议用模拟盘先跑一段时间,确认策略有效后再考虑小资金实盘验证。实盘和回测之间的差距,往往比想象中大得多。
还有一个很多人会忽略的问题:过度拟合。有些人回测时会反复调整参数,直到历史收益达到最优,但这样调整出来的参数在未来很可能失效,因为市场会变。正确的做法是,用一段时间的数据训练策略,用另一段时间的数据验证,看看策略是否依然有效。
04 注意事项与Next Steps
在正式开始使用量化策略之前,有几件重要的事情需要你了解。第一是数据质量的重要性。很多新手会忽视这个问题,用不准确的数据跑策略,得出的结论自然会出错。建议至少验证三到五只股票的数据与东方财富或同花顺上的官方数据是否一致。数据错了,一切都错。
第二是仓位管理。即使你有了一个期望为正的策略,如果仓位管理不当,也可能导致灾难性的亏损。我个人建议,单只股票的最大仓位不要超过总资金的20%,同时要设定单日最大亏损止损线,比如亏损达到总资金的5%就无条件平仓。这个原则听起来简单,但真正能做到的人不多。
第三是心态控制。量化策略的好处是规则明确,可以避免情绪干扰。但前提是你必须严格遵守策略发出的信号,不能因为亏损就临时改变规则,也不能因为连续盈利就加大仓位。纪律性是量化交易的生命线。再好的策略,执行不到位也是白搭。
接下来,你可以从以下几个方向继续深入学习。第一是学习更多技术指标,比如MACD、RSI、KDJ等,将多个指标组合使用可以提高信号的可靠性。第二是学习机器学习方法,用历史数据训练模型来预测股价走势,这需要更多的编程和统计学基础。第三是学习量化回测框架,比如backtrader、vnpy等,这些工具可以帮你更系统地评估策略表现。
电脑前的程序员盯盘分析图表,专注研究市场数据
最后我想说的是,量化只是一种工具,它不能预测未来,也不能消除风险。它的作用是帮助你在不确定的市场中,找到一套可以重复执行的规则,并严格执行这些规则。真正的高手,不是依赖某个神奇的技术指标,而是建立了完整的交易系统,并始终保持纪律和耐心。
特别提醒:布林带策略在单边趋势行情中效果最好,在震荡行情里容易产生假信号。使用时要结合市场环境判断,在趋势明确时大胆用,在震荡市中管住手。A股市场大部分时候都是震荡市,趋势行情是稀缺品,珍惜趋势,活在震荡。
市场永远有机会,缺的不是机会,而是准备好的人。学Python量化,不是为了找到圣杯,而是为了在机会来临时,你已经做好了准备。
免责声明:本文仅供参考,不构成投资建议。股市有风险,入市需谨慎。