1. 量化选股的基础逻辑与单因子策略我第一次接触量化选股是在2015年当时市场剧烈波动让我意识到传统主观投资的风险。量化选股的核心思想很简单用数据说话用规则替代情绪。就像超市选水果我们会有意识地挑选新鲜、饱满的而量化选股就是把这个挑选过程标准化、自动化。单因子策略是最基础的量化选股方法相当于只用新鲜度一个标准来选水果。在股票市场常见的单因子包括市值因子小市值效应是A股市场最显著的特征之一。就像小树比大树长得快小公司往往成长性更好估值因子PE、PB等指标相当于用价格/重量来判断水果是否划算动量因子近期涨幅好的股票往往有惯性就像滚动的雪球我在2016年测试过一个简单的小市值策略每月初买入沪深300成分股中市值最小的20只股票持有一个月后调仓。回测显示这个策略年化收益能达到18%远超指数。但2017年蓝筹股行情中这个策略却大幅跑输市场这让我意识到单一因子的局限性。# 小市值策略核心代码示例 def handle(context): # 获取股票池中所有股票的市值数据 df get_fundamentals(query(valuation).filter( valuation.code.in_(get_index_stocks(000300.XSHG)) ))[[code, market_cap]] # 按市值排序并选取最小的20只 df df.sort_values(market_cap).iloc[:20] # 调仓逻辑 rebalance_portfolio(context, df[code].tolist())这个简单策略有几个关键点需要注意调仓频率不宜过高每月一次比较合适避免过多交易成本最好限定在某个指数成分股内选股比如沪深300降低风险小市值股票流动性较差实际交易时要考虑冲击成本2. 从单因子到多因子的策略演进单因子策略就像只用成绩选拔学生而多因子策略则是综合成绩、体育、品德等多方面评估。我在2018年开始尝试将市值因子与ROE因子结合发现效果明显提升。为什么要用多因子主要有三个原因分散风险单一因子会周期性失效多因子可以平滑收益提高收益好的因子组合能产生112的效果降低波动不同因子间的相关性较低时组合波动会减小构建多因子模型的关键步骤因子选取选择逻辑清晰、历史有效的因子。我常用市值质量的组合市值自然对数处理后的总市值质量ROE、毛利率等盈利能力指标数据标准化就像把身高和体重统一到0-100分才能相加。常用方法有Min-Max归一化Z-score标准化# 多因子标准化示例 def normalize_factors(df): # 市值越小越好所以用1减去归一化值 df[size_score] 1 - (df[market_cap] - df[market_cap].min()) / (df[market_cap].max() - df[market_cap].min()) # ROE越大越好直接归一化 df[quality_score] (df[roe] - df[roe].min()) / (df[roe].max() - df[roe].min()) # 综合评分(各因子权重可调整) df[total_score] 0.6*df[quality_score] 0.4*df[size_score] return df权重分配这是最考验经验的部分。我通常这样确定权重回测各因子单独表现计算因子间相关性用ICIR(信息比率)评估因子稳定性初始等权配置再逐步优化3. 多因子策略的完整实现下面以市值ROE双因子策略为例展示在聚宽平台的完整实现。这个策略我实盘运行过2年年化收益约22%最大回撤15%。3.1 策略框架搭建首先构建策略的基本框架包括初始化函数设置基准、手续费等定时调仓函数每月第一个交易日运行风险控制模块单只股票最大仓位限制def initialize(context): # 设置基准为沪深300 set_benchmark(000300.XSHG) set_option(use_real_price, True) # 设置手续费(券商实际费率) set_order_cost(OrderCost( open_commission0.0003, close_commission0.0003, close_tax0.001 ), typestock) # 策略参数 g.stock_num 30 # 持仓股票数 g.factors [market_cap, roe] # 选用因子 # 定时每月第一个交易日调仓 run_monthly(rebalance, 1) def rebalance(context): # 获取因子数据 df get_factor_data(context) # 因子标准化与综合评分 df calculate_scores(df) # 执行调仓 adjust_portfolio(context, df)3.2 因子数据处理获取基本面数据时要注意使用最新财报数据避免未来函数处理缺失值剔除或填充行业均值极端值处理Winsorize缩尾处理def get_factor_data(context): # 获取沪深300成分股 stock_list get_index_stocks(000300.XSHG) # 构建查询(市值ROE) q query( valuation.code, valuation.market_cap, indicator.roe ).filter( valuation.code.in_(stock_list) ) # 获取数据并转换为DataFrame df get_fundamentals(q) # 处理缺失值(用行业中位数填充) industry_median df.groupby(industry)[roe].median() df[roe] df.apply( lambda x: industry_median[x[industry]] if np.isnan(x[roe]) else x[roe], axis1 ) return df3.3 组合优化与调仓实际交易时要考虑分批下单减少冲击成本保留部分现金应对赎回设置单只股票权重上下限def adjust_portfolio(context, df): # 按综合评分选前30只股票 buy_list df.sort_values(total_score, ascendingFalse).head(g.stock_num) # 计算目标权重(等权) target_weights {code: 0.98/g.stock_num for code in buy_list[code]} # 当前持仓 current_weights {} for stock in context.portfolio.positions: current_weights[stock] context.portfolio.positions[stock].value / context.portfolio.total_value # 调仓逻辑 for stock in set(current_weights.keys()) | set(target_weights.keys()): if stock in target_weights: # 需要买入或调整 order_target_percent(stock, target_weights[stock]) else: # 需要卖出 order_target(stock, 0)4. 策略回测与优化要点回测时最容易犯的三个错误未来函数使用了当时不可得的数据幸存者偏差忽略了已退市股票过度拟合在噪音中寻找模式4.1 回测关键指标解读我主要看这几个指标年化收益15%以上为合格最大回撤控制在20%以内夏普比率大于1说明风险收益比好胜率超过55%算不错下表是一个典型的多因子策略回测结果指标单因子(市值)多因子(市值ROE)年化收益18.2%22.7%最大回撤25.3%14.8%夏普比率0.891.32月胜率58%63%4.2 因子组合优化方法我常用的优化流程单因子测试逐个验证因子有效性因子相关性分析选择相关性低的因子组合权重优化用网格搜索法寻找最优权重样本外测试用不同时间段验证稳健性# 因子IC分析示例 def factor_analysis(df): # 计算因子值与下月收益的相关系数 ic_results {} for factor in [market_cap, roe]: ic df.groupby(date).apply( lambda x: x[factor].corr(x[next_return]) ) ic_results[factor] ic.mean() return ic_results4.3 实盘注意事项从回测到实盘要注意交易成本包括佣金、印花税、冲击成本停牌处理要有替代方案资金规模小市值策略容量有限风控规则设置止损线我在实盘中会额外添加这些规则单日最大回撤超过3%暂停交易组合波动率超过阈值时降低仓位每月定期检查因子有效性多因子策略不是一劳永逸的需要持续维护。我每周会花2小时检查因子表现每季度会做一次全面评估。记住市场在不断变化策略也需要与时俱进。