ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

六因子选股与双指标择时:量化策略回测实战拆解

六因子选股与双指标择时:量化策略回测实战拆解 简介这份文档资料面向具备一定量化基础的投资者与学习者围绕布林通道指标与KD指标系统讲解股票池量化策略的完整研究流程。内容从动态市盈率、每股收益、净利润环比增长率、净资产收益率、流动资产与流动负债差额、经营活动现金净流量六大基本面指标出发筛选出江山股份、天赐材料、盛新锂能、中矿资源四只股票组建股票池并逐一展开基本面分析随后结合布林指标与KD指标制定买卖择时规则最后对回测参数设置及收益、成交、风险三方面结果进行解读涵盖夏普比率、索提诺比率、贝塔、波动率与最大回撤等指标。资源包为1个docx文档压缩包约391KB结构完整、逻辑清晰便于对照学习选股逻辑、择时依据与回测分析方法。目前已有326人学习适合希望将基本面与技术面结合、搭建量化策略框架的读者参考。1. 从六因子选股到双指标择时一份能跑通回测的量化策略拆解很多人做量化卡在第一步股票池怎么选。要么凭感觉挑几只热门股要么全市场撒网跑因子结果回测一跑就翻车。这份资源给了一条相对完整的路径——先用动态市盈率、每股收益、净利润环比增长率、净资产收益率、流动资产与流动负债差额、经营活动现金净流量六个基本面指标筛出四只股票再用布林通道和 KD 指标做买卖择时最后用聚宽框架跑完三年半的回测。策略收益率 256.66%年化 48.28%夏普 1.097最大回撤 38.56%。适合有一定 Python 基础、想搞明白「选股逻辑怎么落地成代码、择时信号怎么组合、回测参数怎么设」的从业者。下面按资源本身的结构拆开讲每一步都落到能复现的细节上。2. 六因子选股从财务指标到股票池的代码实现2.1 为什么是这六个指标而不是更多选股逻辑的核心不是指标越多越好而是每个指标对应一个明确的维度且指标之间不能高度共线。这份资源选了六个动态市盈率pe_ratio对应估值维度每股收益eps对应获利能力净利润环比增长率inc_net_profit_annual对应成长性净资产收益率roe对应质量流动资产与流动负债差额对应偿债能力经营活动现金净流量对应现金流状况。六个维度覆盖了价值、质量、成长、获利、偿债、现金流基本把基本面分析的主要切面都占住了。常见做法是堆十几个因子然后跑 IC 分析筛但对于股票池规模不大的策略因子太多反而容易过拟合。六个指标的好处是每个都有明确的经济含义筛选条件也好设定阈值。资源里给的阈值是pe_ratio 在 10 到 40 之间eps 大于 0.3inc_net_profit_annual 大于 0.3即 30%roe 在 15% 到 40% 之间流动资产大于流动负债经营现金流入大于流出。这些阈值不是拍脑袋来的——pe_ratio 上限 40 过滤掉高估值泡沫下限 10 排除掉可能有问题的小盘股roe 上限 40% 是因为过高往往不可持续净利润环比 30% 以上才算真正的高成长。2.2 聚宽财务数据查询的写法与参数说明资源里的选股代码用的是聚宽get_fundamentals接口直接查财务数据表。代码结构如下stocks_data get_fundamentals( query( valuation.code, valuation.pe_ratio, valuation.market_cap, indicator.eps, indicator.roe, indicator.inc_net_profit_annual, income.net_profit, cash_flow.subtotal_operate_cash_inflow, cash_flow.subtotal_operate_cash_outflow, indicator.inc_net_profit_annual ).filter( # 条件筛选股票 valuation.pe_ratio 40, valuation.pe_ratio 10, # 动态市盈率价值类指标 indicator.eps 0.3, # 每股收益获利能力指标 indicator.inc_net_profit_annual 0.3, # 净利润环比增长率成长类指标 indicator.roe 15, indicator.roe 40, # 净资产收益率质量类指标 balance.total_current_assets balance.total_current_liability, # 流动资产大于流动负债偿债能力 cash_flow.subtotal_operate_cash_inflow cash_flow.subtotal_operate_cash_outflow # 经营活动现金流入大于流出现金流量 ), dateNone )这段代码的逻辑分两层query()里声明要取哪些字段filter()里写筛选条件。dateNone表示取最新一期的财务数据。注意balance.total_current_assets和balance.total_current_liability来自资产负债表cash_flow来自现金流量表indicator是聚宽整合好的衍生指标表。实际跑的时候如果报字段不存在先确认聚宽的财务数据表名和字段名有没有更新——不同版本的 jqdatasdk 字段命名可能有差异。筛选结果就是四只股票江山股份600389.XSHG、天赐材料002709.XSHE、盛新锂能002240.XSHE、中矿资源002738.XSHE。这四只集中在农化制品、电池化学品、能源金属、小金属四个细分行业行业分散度一般但资源里对每只都做了基本面分析来支撑选择逻辑。比如江山股份的归母净利润从 2019 年 3 亿涨到 2021 年 8.165 亿天赐材料是电解液龙头受益于碳中和盛新锂能 2021 年净利润同比增长 3030%中矿资源在海外矿产布局上有先发优势。2.3 投资组合的预期收益与相关性测算选完股票不是直接跑策略资源里先做了组合层面的测算。用近三年对数年化收益率算出来江山股份 38.93%、天赐材料 70.81%、盛新锂能 56.18%、中矿资源 55.25%组合期望收益 55.29%波动率 57%。协方差矩阵显示四只股票之间相关性在 0.85 到 0.96 之间属于高度正相关。这意味着组合的分散化效果有限——涨的时候一起涨跌的时候一起跌。资源里也承认了这一点所以设置了 80% 资金交易、20% 资金做风险管理。这里有个值得注意的细节预期收益率 55.29% 和后来回测的年化 48.28% 比较接近说明策略在样本内的表现和预期基本吻合。但波动率从预期的 57% 降到了 40.4%说明择时信号确实起到了一定的降低波动作用。相关性高的问题在实际操作中需要警惕——如果四只股票同时触发卖出条件回撤会非常集中。3. 布林通道与 KD 指标的择时逻辑及代码落地3.1 两个指标的分工与组合条件布林通道和 KD 指标在策略里的分工很明确。布林通道基于价格的标准差计算上中下轨中轨是 20 日均线上下轨是中轨加减两倍标准差。价格突破中轨视为趋势确认触及上轨视为压力位。KD 指标基于最高价、最低价和收盘价计算 K 值和 D 值K 值在 20 附近向上交叉 D 值是金叉买入信号K 值在 80 附近向下交叉 D 值是死叉卖出信号。资源里的买入条件是股价大于布林中轨且 K 值大于等于 15且 K 值大于 D 值金叉三个条件同时满足才用 80% 资金买入。卖出条件是股价大于布林上轨且 K 值小于等于 80且 K 值小于 D 值死叉三个条件同时满足才全仓平仓。注意这里的逻辑和教科书上的经典用法有差异——经典用法是价格触及下轨买入、触及上轨卖出但资源里用的是突破中轨买入、突破上轨卖出更偏向趋势跟踪而非均值回归。KD 指标的参数是 N18、M13、M23比常见的 9、3、3 要慢一些。慢速 KD 的好处是信号更少、假信号更少适合低频交易。布林通道参数是 timeperiod20、nbdevup2、nbdevdn2这是标准参数没有做额外优化。3.2 聚宽策略代码的逐段拆解初始化函数里做了几件事定义股票池、设置基准为沪深 300、开启动态复权、设置交易手续费、注册每日运行函数。from jqdata import * from jqlib.technical_analysis import * def initialize(context): # 定义股票池 g.stocks [002709.XSHE, 002240.XSHE, 002738.XSHE, 600389.XSHG] # 设置基准参数沪深300指数 set_benchmark(000300.XSHG) # 设置动态复权使用真实价格 set_option(use_real_price, True) # 设置股票交易手续费 set_order_cost(OrderCost( close_tax0.001, # 印花税千分之一卖出时收取 open_commission0.0003, # 买入佣金万分之三 close_commission0.0003, # 卖出佣金万分之三 min_commission5 # 最低佣金5元 ), typestock) # 运行函数 run_daily(trade, every_bar)set_option(use_real_price, True)这一行很关键。如果不开回测用的是前复权价格历史价格会被后来的除权除息影响导致回测结果失真。开了之后用真实价格但需要在分红除权时做相应处理。手续费设置里印花税是单边千分之一卖出时收佣金是双边万分之三最低 5 元。这个费率设置和资源里回测参数表一致。交易函数是核心逻辑def trade(context): # 循环股票池 for security in g.stocks: # 获取股票收盘价取最近30天 close_data attribute_history(security, 30, 1d, [close]) # 计算布林通道指标值返回上轨线、中轨线、下轨线 upperband, middleband, lowerband Bollinger_Bands( security, check_datecontext.current_dt, timeperiod20, nbdevup2, nbdevdn2 ) # 取得上一时间点价格 current_price close_data[close][-1] # 计算KD指标值返回K值、D值 K1, D1 KD(security, check_datecontext.current_dt, N18, M13, M23) # 计算账户现金 cash context.portfolio.available_cash # 买入条件股价突破中轨线K值15K线向上穿过D线且有可用现金 if current_price middleband[security] and K1[security] 15 and K1[security] D1[security] and (cash 0): log.info(买入 %s % (security)) order_value(security, cash * 0.8) # 用80%资金买入 # 卖出条件股价突破上轨线K值80K线向下跌破D线且有可卖持仓 elif current_price upperband[security] and K1[security] 80 and K1[security] D1[security] and context.portfolio.positions[security].closeable_amount 0: log.info(卖出%s % (security)) order_target(security, 0) # 全仓平仓 log.info(一天交易完成################################)几个容易忽略的点attribute_history取 30 天数据是为了给布林通道的 20 周期留足计算空间Bollinger_Bands和KD都是聚宽技术分析库的函数返回的是字典需要用[security]取值order_value是按金额下单order_target是按目标持仓数量下单设为 0 就是清仓。买入用 80% 资金而不是全仓是为了留 20% 应对回撤和补仓机会。3.3 回测参数设置与结果解读回测参数在资源里写得很清楚初始资金 20 万回测区间 2019-01-01 到 2022-05-01基准沪深 300印花税千分之一单边佣金万分之三双边最低 5 元。这些参数在聚宽回测界面里对应设置即可。回测结果的核心数据策略收益率 256.66%年化 48.28%基准收益 33.40%策略波动率 40.4%夏普比率 1.097索提诺比率 1.693贝塔 0.934最大回撤 38.56%。最大回撤区间在 2021 年 9 月到 2022 年 1 月对应的是大盘高位回调阶段。成交情况方面三年半里交易频率很低主要操作集中在 2019 年整个回测期只出现过两笔卖单2019 年 3 月 8 日全仓卖出威华股份盛新锂能曾用名盈利 9213 元2019 年 12 月 16 日平仓中矿资源亏损 9435 元。这说明策略的信号触发频率不高大部分时间在持有交易成本对收益的侵蚀很小。4. 避坑与排查回测跑通前最容易翻车的五个地方4.1 财务数据字段对不上导致选股为空现象跑get_fundamentals返回空 DataFrame或者报字段不存在。原因通常是聚宽财务数据表的字段名在不同版本间有调整比如indicator.inc_net_profit_annual在某些版本里叫indicator.inc_net_profit_year_on_year。解决方法是先用get_fundamentals(query(valuation.code).limit(5))确认接口能通再逐个字段试或者查聚宽最新的财务数据字典。另外dateNone取的是最新一期如果当天不是财报发布期数据可能是上一季度的这会影响选股结果的时效性。4.2 复权设置不当导致价格信号失真现象回测收益异常高或异常低买卖点明显不对。原因多半是set_option(use_real_price, True)没开或者开了但没处理分红除权。不开真实价格的话前复权价格在除权日会出现跳空布林通道和 KD 指标的计算会受干扰。开了之后持仓期间的分红会以现金形式返还到账户但股价会除权需要确认回测框架是否自动处理了这部分。常见做法是在回测设置里勾选「真实价格」并同时开启「分红再投资」或手动处理。4.3 KD 指标的金叉死叉判断逻辑写反现象买入信号频繁触发但收益很差或者卖出信号从不触发。原因是 KD 的金叉判断写成了K1 D1但没加「上一时刻 K1 D1」的条件导致只要 K 大于 D 就持续触发买入。正确的金叉判断应该是当前 K 大于 D 且上一时刻 K 小于等于 D。资源里的代码用的是K1[security] D1[security]严格来说只判断了当前状态没有判断交叉动作。实际跑的时候如果发现买入过于频繁需要补上交叉判断。4.4 布林通道返回值类型搞错现象middleband[security]报 KeyError 或 TypeError。原因是Bollinger_Bands返回的是字典key 是股票代码但有些版本返回的是 DataFrame 或 Series。先用print(type(middleband))确认返回类型如果是 DataFrame 就用middleband[security].iloc[-1]取值。另外check_datecontext.current_dt传的是回测当前时间确保取的是当天数据而不是未来数据。4.5 回测区间与财报数据的时间对齐问题现象回测前半段选不出股票或者选出的股票和资源里列的四只不一致。原因是财务数据有发布延迟2019 年初回测时2018 年年报可能还没发布get_fundamentals取到的是 2018 年三季报的数据。解决方法是把选股逻辑放在回测开始后一段时间再执行或者用date参数指定取哪个报告期的数据。资源里的选股结果是基于 2022 年 5 月之前的财务数据回测时如果严格按时间点取数早期选出的股票可能不同。5. 进阶调优从参数敏感性到信号过滤的实操技巧策略跑通之后下一步是看参数稳不稳。布林通道的timeperiod和nbdev、KD 的N、M1、M2这几个参数稍微动一下回测结果可能差很多。我一般会做一轮参数扫描布林周期从 15 到 30标准差倍数从 1.5 到 2.5KD 的 N 从 9 到 24每组跑一遍回测看年化收益和最大回撤的变化。如果某个参数在很窄的范围内才表现好说明过拟合了实盘大概率翻车。信号过滤是另一个调优方向。资源里的买入条件是三个同时满足但实际跑的时候会发现有些信号出现在大盘暴跌期间个股基本面没变但系统性风险把价格打下去了。常见做法是加一层大盘过滤沪深 300 在 20 日均线以下时不开新仓或者用波动率指标做仓位控制。这样会牺牲一部分收益但能显著降低回撤。还有一个容易被忽略的点是持仓集中度。四只股票如果同时触发买入80% 资金会平均分配到四只上每只 20%。但如果只有一只触发80% 资金全砸一只风险集中度很高。改进方法是用order_target_percent按目标权重下单每只股票设定上限比如单只不超过总资金的 30%。这样即使信号集中也不会出现单票重仓的情况。验证策略是否靠谱我习惯看三个东西一是分年度收益如果某一年特别高、某一年特别低说明策略对市场环境依赖大二是滚动夏普看不同时间窗口的夏普比率是否稳定三是最大回撤的恢复时间如果回撤后半年都爬不出来实盘很难拿住。这份资源的回测区间覆盖了 2019 到 2022 年包含了上涨、震荡、下跌三种行情分年度看的话 2021 年贡献了主要收益2022 年初回撤较大这个特征在实盘前要有心理准备。从那以后我每次跑完回测都会强制走一遍参数敏感性检查和分年度收益拆解确认策略不是靠某一段行情撑起来的才敢往下走。希望帮到你。本文还有配套的精品资源点击获取
返回列表