量化交易实战:基于买卖压力多维度指标的Python选股策略

发布时间:2026/7/30 5:28:29
量化交易实战:基于买卖压力多维度指标的Python选股策略 1. 项目概述量化买卖压力选股的核心逻辑在A股市场里混迹了十几年我见过太多朋友沉迷于各种“神奇”的指标和公式试图找到一个能一劳永逸的“圣杯”。结果往往是今天追涨杀跌明天又去研究新的战法账户资金曲线却像心电图一样起伏不定。说到底很多传统技术分析的问题在于“滞后”和“主观”。一个金叉信号出来股价可能已经涨了一截不同的人看同一张K线图得出的结论可能截然相反。“量化买卖压力选股”这个思路其核心价值就在于尝试用客观、可计算的数据来替代主观、模糊的感觉。它不试图预测明天是涨是跌——那是神仙干的事——而是通过一套规则去衡量当前市场中“买方力量”和“卖方力量”的实时对比。你可以把它想象成一个战场上的“兵力探测器”。我们不去猜哪边会赢而是通过侦察兵数据反馈计算出哪一方在当下这个时刻投入的“兵力”资金更多阵型价格走势更稳固。这个策略适合两类人一是对编程和数据分析有兴趣不满足于黑盒指标想自己动手验证想法的交易者二是希望建立一套纪律性强的半自动或全自动观察清单筛选体系减少盘中情绪化决策的投资者。它不是一个“点石成金”的魔法而是一个帮你把复杂市场信息“翻译”成可操作信号的“翻译器”和“过滤器”。2. 策略核心买卖压力的量化定义与计算买卖压力听起来很玄乎其实拆解开来就是“钱往哪里流”的问题。股价的上涨本质上是主动买入的资金多于主动卖出的资金推动价格上移。我们的目标就是设计一些指标来量化这种资金流动的强度和方向。2.1 核心压力指标拆解在实践中我通常会结合多个维度来综合评估买卖压力单一指标很容易被操纵或失效。以下是几个经过实战检验的核心计算思路1. 价格-成交量关系压力这是最基础也最有效的维度。光看价格涨跌不够必须结合成交量来看“成色”。放量上涨 vs. 缩量上涨同样涨3%伴随成交量较前5日均量放大50%以上的上涨其买方压力的“质量”和持续性通常远高于成交量萎缩的上涨。后者可能是无量空涨随时可能回落。放量下跌 vs. 缩量下跌放量下跌说明抛压沉重且真实卖方力量占绝对主导缩量下跌则可能是洗盘或惜售卖方压力未必持续。计算方法可以计算“量价齐升系数”。例如定义当日涨幅1%且成交量放大超过20%为“有效买压信号”计1分当日跌幅1%且成交量放大超过20%为“有效卖压信号”计-1分。统计最近N日如5日的总得分可以反映短期多空力量的累积情况。2. 委托盘压力盘口压力盯过盘口的朋友都知道买一和卖一挂的单子委买委卖是战场的最前线。但这里陷阱很多需要更精细的计算。传统委比陷阱简单的委买量-委卖量/委买量委卖量*100% 这个委比指标极易被主力用“钓鱼单”欺骗。他们在买一挂上巨量买单营造支撑假象却在更高价位悄悄卖出。改进的盘口压力算法我更关注“真实委托密度”。不是看总量而是分析买盘和卖盘在价格区间上的分布。例如计算买盘前五档的加权平均价格距离现价有多远卖盘前五档的加权平均价格距离现价有多远。如果买盘堆积在离现价很近的位置比如1%以内而卖盘挂在较远的位置比如2%以外说明下方支撑相对坚实向上突破的卖压较轻。这比单纯的委买总量更有意义。3. 价格波动与资金流向压力这涉及到对每笔成交的归属进行推断虽然无法100%准确但能提供重要参考。主动性买卖资金通过比对每笔成交价与当时的买一卖一价可以推断该笔成交是主动买入吃卖单还是主动卖出砸买单。统计一段时间内如1分钟、5分钟主动买入和主动卖出的资金净额就是最直观的资金流向。大单净流入进一步聚焦“主力”行为。定义单笔成交金额大于某个阈值如50万元的为大单。分别统计大单主动买入和主动卖出的金额计算“大单净流入”。持续的大单净流入往往是买方压力占据优势的体现。但要注意也要结合股价位置看高位的大单净流入可能是对倒出货。4. 趋势强度压力买卖压力最终要体现在价格趋势上。一个健康的买方压力应该能推动价格沿着某个趋势通道稳定运行。ATR平均真实波幅标准化涨幅单日涨幅2%在大盘股和小盘股身上的意义完全不同。可以用当日涨幅除以近期ATR得到一个标准化的“趋势强度值”。比如当日涨幅达到1.5倍ATR可以认为买方压力强劲。布林带位置与带宽股价运行在布林带中轨上方且布林带带宽上下轨距离没有急剧收缩或扩张表明趋势性买压维持在一个稳定的状态。如果股价紧贴上轨运行同时带宽温和扩张这是很强的趋势买压信号。注意没有任何一个指标是完美的。量化买卖压力的关键在于多维度印证。当价格-成交量、盘口、资金流向、趋势强度等多个指标同时指向买方压力增强时这个信号的可靠性才会大大提升。我们的选股策略就是寻找这些指标出现“共振”的标的。2.2 指标合成与压力分数计算出上述一系列子指标后我们需要将它们合成一个或多个综合的“买卖压力分数”。这里切忌简单平均。标准化每个指标的量纲和范围不同比如涨幅是百分比资金流入是金额首先要进行标准化处理使其落在类似的范围如0-100分或-1到1之间。加权根据你的经验和对不同指标的信赖程度赋予不同的权重。例如在趋势行情中你可能给趋势强度指标更高的权重在震荡行情中可能更看重盘口和资金流向指标。初期可以等权开始。阈值设定最终的综合压力分数比如一个0-100的“买方压力指数”需要设定一个阈值来触发选股信号。例如当指数连续3日大于70且今日分数比昨日高则纳入观察。这个阈值需要通过历史数据回测来优化但不能过度优化避免“过拟合”。3. 策略实现从思路到可运行的Python代码有了清晰的理论框架接下来就是用代码将其实现。这里我提供一个基于Python的、模块化的实现示例使用pandas和talib库。你可以在此基础上修改和扩展。3.1 环境准备与数据获取首先确保你的Python环境安装了必要的库。数据源方面可以使用akshare免费、tushare部分免费或券商/专业数据服务商的API。# 导入必要库 import pandas as pd import numpy as np import talib import akshare as ak from datetime import datetime, timedelta # 假设我们有一个函数来获取股票历史数据这里用akshare示例需处理接口稳定性 def get_stock_data(stock_code, start_date, end_date): 获取单只股票的日线数据 try: df ak.stock_zh_a_hist(symbolstock_code, perioddaily, start_datestart_date, end_dateend_date, adjustqfq) df.rename(columns{ 日期: date, 开盘: open, 收盘: close, 最高: high, 最低: low, 成交量: volume, 成交额: amount, 振幅: amplitude, 涨跌幅: pct_chg }, inplaceTrue) df[date] pd.to_datetime(df[date]) df.set_index(date, inplaceTrue) df.sort_index(inplaceTrue) return df except Exception as e: print(f获取数据失败 {stock_code}: {e}) return pd.DataFrame() # 获取沪深300成分股列表作为我们的股票池示例 def get_hs300_components(): df ak.index_stock_cons_csindex(symbol000300) return df[成分券代码].tolist()3.2 核心压力指标计算函数我们将之前讨论的指标实现为独立的函数便于维护和组合。def calculate_price_volume_pressure(df, window5): 计算量价关系压力 规则当日涨幅1%且成交量5日均量*1.2得1分当日跌幅1%且成交量5日均量*1.2得-1分。 返回N日累计得分。 df df.copy() # 计算5日均量 df[volume_ma5] df[volume].rolling(window5).mean() # 初始化得分列 df[pv_score] 0 # 条件判断 cond_buy (df[pct_chg] 1.0) (df[volume] df[volume_ma5] * 1.2) cond_sell (df[pct_chg] -1.0) (df[volume] df[volume_ma5] * 1.2) df.loc[cond_buy, pv_score] 1 df.loc[cond_sell, pv_score] -1 # 计算累计得分 df[pv_cum_score] df[pv_score].rolling(windowwindow).sum() return df[[pv_cum_score]] def calculate_trend_strength_pressure(df, atr_period14): 计算趋势强度压力 使用ATR标准化后的涨幅以及股价相对于布林带的位置。 df df.copy() # 计算ATR df[atr] talib.ATR(df[high], df[low], df[close], timeperiodatr_period) # 计算ATR标准化涨幅 (避免除零) df[norm_change] df[pct_chg] / (df[atr] / df[close].shift(1) * 100 1e-8) # 计算布林带 df[bb_upper], df[bb_middle], df[bb_lower] talib.BBANDS(df[close], timeperiod20, nbdevup2, nbdevdn2) # 计算股价在布林带中的相对位置 (0到1之间) df[bb_position] (df[close] - df[bb_lower]) / (df[bb_upper] - df[bb_lower] 1e-8) # 趋势强度得分标准化涨幅 布林带位置例如在中轨上方且位置高于0.5给予加分 df[trend_score] df[norm_change] * 10 # 放大系数便于观察 df.loc[df[close] df[bb_middle], trend_score] 5 df.loc[df[bb_position] 0.5, trend_score] 5 return df[[norm_change, bb_position, trend_score]] # 注意资金流向和盘口压力需要高频tick或Level2数据计算复杂且数据源要求高。 # 此处仅作示意假设我们有一个函数能返回每日的“主力资金净流入率”基于大单统计的估算 def calculate_money_flow_pressure(df, money_flow_series): 计算资金流压力。money_flow_series是一个与df索引对齐的Series表示每日主力净流入率单位%。 df df.copy() df[money_flow] money_flow_series # 可以计算资金流的N日平滑或趋势 df[money_flow_ma5] df[money_flow].rolling(5).mean() # 资金流得分当日流入为正得分且趋势向上加分 df[mf_score] 0 df.loc[df[money_flow] 0, mf_score] df[money_flow] df.loc[df[money_flow_ma5] df[money_flow_ma5].shift(1), mf_score] 2 return df[[money_flow, mf_score]]3.3 综合评分与选股逻辑将各个指标得分整合并实施选股规则。def calculate_composite_pressure_score(df): 计算综合买卖压力分数。 这里假设我们已经有了pv_cum_score, trend_score, mf_score这几个子分数列。 df df.copy() # 1. 标准化各子分数使用排名分位数标准化避免极端值影响 for score_col in [pv_cum_score, trend_score, mf_score]: if score_col in df.columns: # 将分数转换为在过去60个交易日中的排名分位数0-1 df[f{score_col}_norm] df[score_col].rolling(window60).apply( lambda x: (x.rank(pctTrue).iloc[-1]) if len(x.dropna()) 30 else np.nan ) # 2. 加权求和权重可调 weight_map {pv_cum_score_norm: 0.3, trend_score_norm: 0.4, mf_score_norm: 0.3} norm_cols [col for col in df.columns if col.endswith(_norm)] df[composite_score] 0 for col in norm_cols: base_col col.replace(_norm, ) weight weight_map.get(base_col, 0) df[composite_score] df[col].fillna(0.5) * weight * 100 # 放大到0-100分范围 return df def select_stocks(stock_pool, start_date, end_date): 执行选股流程 selected [] trade_date end_date # 假设我们在end_date这天进行选股 for code in stock_pool[:50]: # 示例只跑前50只实际应全量或分批 df get_stock_data(code, start_date, end_date) if df.empty or len(df) 60: # 数据不足 continue # 计算各项压力指标 df_pv calculate_price_volume_pressure(df) df_trend calculate_trend_strength_pressure(df) # 假设我们有一个获取资金流数据的函数这里用随机数模拟 np.random.seed(hash(code) % 10000) mock_money_flow pd.Series(np.random.randn(len(df)) * 0.5, indexdf.index) df_mf calculate_money_flow_pressure(df, mock_money_flow) # 合并结果 df pd.concat([df, df_pv, df_trend, df_mf], axis1) # 计算综合分数 df calculate_composite_pressure_score(df) # 获取最新一日的信号 latest df.iloc[-1] # 定义选股条件示例 # 1. 综合分数 65 # 2. 趋势分数 0 (处于上涨趋势) # 3. 量价累计分数 0 (近期量价配合良好) # 4. 股价在20日均线之上 df[ma20] df[close].rolling(20).mean() condition ( latest.get(composite_score, 0) 65 and latest.get(trend_score, 0) 0 and latest.get(pv_cum_score, 0) 0 and latest[close] df[ma20].iloc[-2] # 昨日收盘价在20日线上 ) if condition: selected.append({ code: code, name: 需从其他接口获取, # 实际应填充名称 composite_score: round(latest.get(composite_score, 0), 2), close: latest[close], pct_chg: latest[pct_chg] }) # 按综合分数排序 result_df pd.DataFrame(selected).sort_values(bycomposite_score, ascendingFalse) return result_df # 执行示例 if __name__ __main__: end datetime.now().strftime(%Y%m%d) start (datetime.now() - timedelta(days180)).strftime(%Y%m%d) # 过去180天数据 pool get_hs300_components() selected_stocks select_stocks(pool, start, end) print(f选股日期{end}) print(selected_stocks.head(10))4. 策略回测、评估与迭代代码能跑出结果只是第一步更重要的是评估这个策略在历史上是否真的有效以及如何优化。4.1 回测框架搭建要点不要急于用真金白银去试。一个简单的回测框架应该包含以下要素数据准备准备足够长时间的历史数据至少涵盖一轮牛熊如5-10年。信号生成在历史数据的每一天运行你的选股函数得到当日的“候选股”列表。交易规则买入在信号出现日的收盘价或次日开盘价假设买入。仓位管理是等权重买入所有候选股还是只买前几名是满仓还是固定比例仓位这些都需要明确。卖出这是策略成败的关键。可以设定目标止盈如盈利20%卖出。移动止损如从最高点回撤8%卖出。信号失效卖出当综合压力分数跌破某个阈值如50时卖出。固定持有期如买入后持有5个交易日无论盈亏都卖出。绩效计算计算策略的整体收益率、年化收益、最大回撤、夏普比率、胜率盈利交易次数/总交易次数、盈亏比平均盈利/平均亏损等关键指标。实操心得回测中最容易犯的错是“未来函数”和“幸存者偏差”。确保你的计算在每一天只用当天及之前的数据绝对不能用到未来的数据比如用明天的收盘价算今天的指标。另外回测时要使用“后复权”价格并考虑是否包含已经退市的股票这能避免只看到存活下来的好公司而高估策略。4.2 策略评估与常见问题回测结果出来后不要只看总收益。一个收益很高但回撤巨大的策略实盘中你很可能拿不住。最大回撤这是衡量策略风险最重要的指标之一。它告诉你历史上最坏的情况下账户会亏多少钱。如果最大回撤超过30%对于大多数投资者来说心理压力会非常大。胜率与盈亏比高胜率低盈亏比的策略赚小钱次数多亏一次大的和低胜率高盈亏比的策略经常小亏偶尔大赚是两种不同的风格。后者通常对心理素质要求更高。策略容量你的策略能管理多少资金如果一买就冲击成本很高或者只对小盘股有效那么这个策略的实用价值就有限。常见问题与排查回测结果完美实盘就亏检查点是否忽略了交易成本佣金、印花税、滑点实盘下单能否以回测假设的价格成交数据质量是否有问题是否用了有错误或经过调整的数据回测对策在回测中加上每笔交易0.1%-0.2%的成本并设置一个滑点比如买入价按收盘价上浮0.1%卖出价下浮0.1%让回测更接近现实。选出的股票太多或太少检查点评分阈值是否合理市场整体是牛市还是熊市在牛市中可能大部分股票分数都高熊市中则相反。对策可以使用动态阈值。例如只选择综合分数排名前10%的股票。或者引入“市场状态”判断在不同市况下使用不同的阈值权重。信号闪烁频繁交易检查点指标是否过于敏感是否使用了太短的时间周期对策对原始信号进行平滑处理。例如不是分数一上70就买而是要求“连续两日站在70以上”或“分数上穿70均线”才触发。或者加入“最小持仓期”限制买入后至少持有N日才能卖出。4.3 策略迭代与风控没有一个策略能永远有效。市场风格在变因子的效力也会衰减。你需要建立一个迭代机制。定期回顾每季度或每半年用最新的数据重新跑一遍回测看看策略的各项指标是否稳定。如果发现夏普比率持续下降最大回撤扩大就要警惕了。因子挖掘与轮动不要死守最初的几个压力指标。可以尝试引入新的维度如分析师预期变化、产业链新闻情绪、市场广度指标等。也可以让策略自动在几套不同的参数或因子组合间轮动选择近期表现最好的那一套。严格风控这是量化交易的生死线。必须在策略层就设定好单票仓位上限防止黑天鹅。整体仓位控制根据市场整体风险如沪深300指数的波动率动态调整总仓位。每日最大亏损止损当账户单日亏损超过一定比例如2%强制平仓所有头寸停止交易冷静复盘。我个人在实际使用这类压力选股模型时最大的体会是它最好的用途不是提供一个“买入”按钮而是帮你高效地过滤掉市场中绝大多数噪音将注意力聚焦在少数“多空力量正在发生有利变化”的标的上。最终的交易决策还需要结合基本面逻辑、板块效应、市场整体环境来做出。这个模型是我的“侦察兵系统”它告诉我哪里可能有战机但何时开火、投入多少兵力依然需要我这个“指挥官”结合更全面的情报来定夺。代码和策略是冰冷的但如何理解和使用它却充满了艺术性。