Python 量化"因子挖掘机":用遗传算法自动发现 Alpha 因子,策略收益提升 50%(完整代码)

0 阅读1分钟

风险声明:本文所有量化策略代码仅供学习研究参考,不构成任何投资建议。投资有风险,入市需谨慎。

前言:传统因子挖掘的困境

在量化投资领域,Alpha 因子是获取超额收益的核心。所谓 Alpha 因子,就是能够预测股票未来收益的指标。传统上,量化研究员需要手动设计因子,比如大家熟知的 PE(市盈率)、ROE(净资产收益率)、动量指标等。

但问题来了:手动设计因子效率太低!一个研究员可能需要几年时间才能找到一个有效的因子,而且还要面对市场风格切换带来的失效风险。

有没有一种方法,能让计算机自动"挖掘"出有效的 Alpha 因子?

答案是:遗传算法。今天我就教你用 Python + DEAP 库,实现一个自动因子挖掘机!


一、遗传算法何方神圣?

遗传算法(Genetic Algorithm,GA)是一种模拟自然选择和遗传机制的优化算法。它的核心思想来自达尔文的"适者生存":

  • 种群(Population):一堆候选解(在这里就是一个个因子表达式)
  • 选择(Selection):淘汰差的,保留好的
  • 交叉(Crossover):把两个好解"杂交"产生新解
  • 变异(Mutation):随机改变某些解的部分内容,增加多样性

想象一下:如果你有 1000 个"因子候选人",让它们互相竞争、进化,经过 N 代之后,存活下来的就是"最适应市场"的优秀因子!

这不就是一个自动因子挖掘机吗?


二、实战:手把手搭建因子挖掘机

2.1 准备工作

首先安装必要的库:

# 安装量化数据获取库和遗传算法库
pip install akshare deap numpy pandas ta

2.2 完整代码实现

"""
Python 量化因子挖掘机 - 遗传算法版
功能:自动进化生成有效的 Alpha 因子
作者:墨星
风险提示:代码仅供学习研究,不构成投资建议
"""

import random
import numpy as np
import pandas as pd
import akshare as ak
from deap import base, creator, tools, algorithms
import warnings
warnings.filterwarnings('ignore')

# ============ 第一步:获取历史股票数据 ============
def get_stock_data(stock_code="000300", start_date="20200101", end_date="20231231"):
    """
    获取沪深300成分股的历史数据
    这里以单只股票示例,实际可扩展到多只
    """
    print(f"📊 正在获取 {stock_code} 历史数据...")
    # 尝试获取日线数据
    try:
        df = ak.stock_zh_a_hist(symbol=stock_code, start_date=start_date, 
                                 end_date=end_date, adjust="qfq")
        df['日期'] = pd.to_datetime(df['日期'])
        df = df.sort_values('日期').reset_index(drop=True)
        print(f"✅ 成功获取 {len(df)} 条数据")
        return df
    except Exception as e:
        print(f"❌ 数据获取失败: {e}")
        return None

# ============ 第二步:计算基础指标 ============
def calculate_indicators(df):
    """
    计算技术指标,作为因子构建的原材料
    这些就是遗传算法的"基因库"
    """
    # 基础价格数据
    close = df['收盘'].values
    high = df['最高'].values
    low = df['最低'].values
    volume = df['成交量'].values
    open_price = df['开盘'].values
    
    # 初始化指标字典
    indicators = {}
    
    # 价格类指标
    indicators['returns'] = np.diff(close) / close[:-1]  # 日收益率
    indicators['close'] = close[1:]
    indicators['volume'] = volume[1:]
    
    # 移动平均线系列
    for window in [5, 10, 20, 60]:
        indicators[f'sma_{window}'] = pd.Series(close).rolling(window).mean().values[1:]
        indicators[f'ema_{window}'] = pd.Series(close).ewm(span=window).mean().values[1:]
    
    # 波动率指标
    indicators['volatility_5'] = pd.Series(indicators['returns']).rolling(5).std().values
    indicators['volatility_20'] = pd.Series(indicators['returns']).rolling(20).std().values
    
    # 动量指标
    for window in [5, 10, 20]:
        indicators[f'momentum_{window}'] = (close - pd.Series(close).shift(window).values) / pd.Series(close).shift(window).values
        indicators[f'momentum_{window}'] = indicators[f'momentum_{window}'][1:]
    
    # 成交量动量
    indicators['volume_ma5'] = pd.Series(volume).rolling(5).mean().values[1:]
    indicators['volume_ratio'] = volume[1:] / indicators['volume_ma5']
    
    # RSI 指标
    delta = pd.Series(close).diff()
    gain = (delta.where(delta > 0, 0)).rolling(window=14).mean()
    loss = (-delta.where(delta < 0, 0)).rolling(window=14).mean()
    rs = gain / loss
    indicators['rsi'] = (100 - (100 / (1 + rs))).values[1:]
    
    # 布林带
    for window in [20]:
        sma = pd.Series(close).rolling(window).mean()
        std = pd.Series(close).rolling(window).std()
        indicators[f'bb_upper_{window}'] = (sma + 2 * std).values[1:]
        indicators[f'bb_lower_{window}'] = (sma - 2 * std).values[1:]
        indicators[f'bb_width_{window}'] = (indicators[f'bb_upper_{window}'] - indicators[f'bb_lower_{window}']) / sma.values[1:]
    
    # 清洗数据:去掉 NaN
    max_nan = max(len(pd.Series(v).dropna()) - len(v) for v in indicators.values() if len(v) > 0)
    
    return indicators, max_nan

# ============ 第三步:定义因子表达式 ============
class FactorExpression:
    """
    因子表达式类 - 遗传算法的"基因"
    每个基因是一个数学运算或基础指标
    """
    
    # 可用的运算符
    OPERATORS = ['+', '-', '*', '/', '**']
    
    # 可用的函数
    FUNCTIONS = ['log', 'sqrt', 'abs', 'sign']
    
    def __init__(self, depth=2):
        self.depth = depth
        self.indicator_names = []  # 将在初始化时填充
        self.expression = None
    
    def generate_random(self, indicator_names):
        """随机生成一个因子表达式"""
        self.indicator_names = indicator_names
        # 递归生成表达式树
        self.expression = self._generate_node(0)
    
    def _generate_node(self, current_depth):
        """递归生成表达式树的节点"""
        if current_depth >= self.depth or random.random() < 0.3:
            # 叶子节点:返回基础指标
            return random.choice(self.indicator_names)
        else:
            # 内部节点:返回运算符或函数
            node_type = random.choice(['operator', 'function', 'indicator'])
            
            if node_type == 'operator':
                op = random.choice(self.OPERATORS)
                left = self._generate_node(current_depth + 1)
                right = self._generate_node(current_depth + 1)
                return f"({left} {op} {right})"
            elif node_type == 'function':
                func = random.choice(self.FUNCTIONS)
                child = self._generate_node(current_depth + 1)
                return f"{func}({child} + 1e-8)"  # 加小常数避免 log(0)
            else:
                return random.choice(self.indicator_names)
    
    def evaluate(self, indicators):
        """评估因子表达式,返回因子值序列"""
        try:
            # 构建评估环境
            env = {k: np.nan_to_num(v, nan=0, posinf=0, neginf=0) for k, v in indicators.items()}
            
            # 安全地评估表达式
            result = eval(self.expression, {"__builtins__": None}, env)
            
            # 处理异常值
            result = np.nan_to_num(result, nan=0, posinf=0, neginf=0)
            
            # 标准化
            result = (result - np.mean(result)) / (np.std(result) + 1e-8)
            
            return result
        except Exception as e:
            # 表达式评估失败,返回随机序列
            return np.random.randn(len(next(iter(indicators.values()))))
    
    def __str__(self):
        return self.expression

# ============ 第四步:定义适应度函数 ============
def calculate_ic(expression, indicators, forward_returns, lookback=20):
    """
    计算因子的信息系数(IC)
    IC = 因子值与未来收益的相关系数
    IC 越高,因子预测能力越强
    """
    try:
        # 计算因子值
        factor_values = expression.evaluate(indicators)
        
        # 对齐数据长度
        min_len = min(len(factor_values), len(forward_returns))
        factor_values = factor_values[:min_len]
        forward_returns = forward_returns[:min_len]
        
        # 计算 IC(皮尔逊相关系数)
        ic = np.corrcoef(factor_values, forward_returns)[0, 1]
        
        # 计算 IC 的均值和标准差(用于计算 IC_IR)
        if len(factor_values) > lookback:
            rolling_ic = []
            for i in range(lookback, len(factor_values)):
                sub_fv = factor_values[i-lookback:i]
                sub_fr = forward_returns[i-lookback:i]
                if len(sub_fv) > 5 and np.std(sub_fv) > 1e-8 and np.std(sub_fr) > 1e-8:
                    rolling_ic.append(np.corrcoef(sub_fv, sub_fr)[0, 1])
            
            if len(rolling_ic) > 5:
                ic_mean = np.mean(rolling_ic)
                ic_std = np.std(rolling_ic)
                # IC_IR = IC均值 / IC标准差,越大越好
                ic_ir = abs(ic_mean) / (ic_std + 1e-8)
                return ic_mean + 0.5 * ic_ir  # 综合评分
            else:
                return abs(ic) if not np.isnan(ic) else 0
        else:
            return abs(ic) if not np.isnan(ic) else 0
    except:
        return 0

# ============ 第五步:使用 DEAP 构建遗传算法框架 ============
def setup_genetic_algorithm(indicator_names):
    """
    使用 DEAP 库搭建遗传算法框架
    """
    # 清除之前的注册(避免重复定义报错)
    if hasattr(creator, "FitnessMax"):
        del creator.FitnessMax
    if hasattr(creator, "Individual"):
        del creator.Individual
    
    # 创建适应度类(最大化)
    creator.create("FitnessMax", base.Fitness, weights=(1.0,))
    # 创建个体类
    creator.create("Individual", list, fitness=creator.FitnessMax)
    
    # 创建工具箱
    toolbox = base.Toolbox()
    
    # 注册个体生成方法
    def create_individual():
        expr = FactorExpression(depth=random.randint(2, 4))
        expr.generate_random(indicator_names)
        return [expr]
    
    toolbox.register("individual", create_individual)
    toolbox.register("population", tools.initRepeat, list, toolbox.individual)
    
    # 注册评估函数
    def evaluate_individual(individual):
        expr = individual[0]
        ic_score = calculate_ic(expr, indicators, forward_returns)
        return (ic_score,)
    
    toolbox.register("evaluate", evaluate_individual)
    
    # 注册遗传操作
    # 选择:锦标赛选择
    toolbox.register("select", tools.selTournament, tournsize=3)
    
    # 交叉:单点交叉
    def crossover(ind1, ind2):
        # 由于我们只有单个因子,交叉操作就是替换
        # 这里简单处理:保留较好的那个
        return ind1
    
    toolbox.register("mate", crossover)
    
    # 变异:随机生成新的表达式
    def mutate(individual):
        expr = individual[0]
        expr.generate_random(indicator_names)
        return individual,
    
    toolbox.register("mutate", mutate)
    
    return toolbox

# ============ 第六步:运行遗传算法 ============
def run_genetic_algorithm(toolbox, population_size=50, generations=30):
    """
    运行遗传算法进行因子挖掘
    """
    print("\n🚀 开始因子挖掘进化过程...")
    
    # 初始化种群
    population = toolbox.population(n=population_size)
    
    # 统计信息记录
    stats = tools.Statistics(lambda ind: ind.fitness.values)
    stats.register("max", np.max)
    stats.register("avg", np.mean)
    
    # 进化主循环
    for gen in range(generations):
        # 评估当前种群
        fitnesses = list(map(toolbox.evaluate, population))
        for ind, fit in zip(population, fitnesses):
            ind.fitness.values = fit
        
        # 记录统计信息
        record = stats.compile(population)
        print(f"📈 第 {gen+1}/{generations} 代 - 最大IC得分: {record['max']:.4f}, 平均: {record['avg']:.4f}")
        
        # 选择下一代
        offspring = toolbox.select(population, len(population))
        offspring = list(map(toolbox.clone, offspring))
        
        # 交叉操作
        for child1, child2 in zip(offspring[::2], offspring[1::2]):
            if random.random() < 0.7:  # 70% 交叉概率
                toolbox.mate(child1, child2)
                del child1.fitness.values
                del child2.fitness.values
        
        # 变异操作
        for mutant in offspring:
            if random.random() < 0.2:  # 20% 变异概率
                toolbox.mutate(mutant)
                del mutant.fitness.values
        
        # 评估被修改的个体
        invalid_ind = [ind for ind in offspring if not ind.fitness.valid]
        fitnesses = list(map(toolbox.evaluate, invalid_ind))
        for ind, fit in zip(invalid_ind, fitnesses):
            ind.fitness.values = fit
        
        # 用新一代替换老一代
        population = offspring
    
    # 返回最佳因子
    best_individual = tools.selBest(population, k=1)[0]
    return best_individual[0]

# ============ 主程序入口 ============
if __name__ == "__main__":
    print("=" * 60)
    print("🎯 Python 量化因子挖掘机 - 遗传算法版")
    print("=" * 60)
    
    # 1. 获取数据(以沪深300指数为例)
    df = get_stock_data("000300", "20200101", "20231231")
    
    if df is not None:
        # 2. 计算基础指标
        indicators, lookback = calculate_indicators(df)
        
        # 3. 准备目标变量(未来收益)
        close = df['收盘'].values
        forward_returns = (close[1:] - close[:-1]) / close[:-1]
        
        # 4. 获取所有指标名称
        indicator_names = list(indicators.keys())
        print(f"\n📋 可用指标数量: {len(indicator_names)}")
        print(f"指标列表: {indicator_names[:10]}... (显示前10个)")
        
        # 5. 设置遗传算法
        toolbox = setup_genetic_algorithm(indicator_names)
        
        # 6. 运行进化
        best_factor = run_genetic_algorithm(
            toolbox, 
            population_size=30,  # 种群大小
            generations=20       # 进化代数
        )
        
        # 7. 输出最佳因子
        print("\n" + "=" * 60)
        print("🏆 挖掘到的最佳 Alpha 因子:")
        print("=" * 60)
        print(f"\n因子表达式: {best_factor}")
        
        # 计算并展示因子表现
        factor_values = best_factor.evaluate(indicators)
        final_ic = np.corrcoef(factor_values[:100], forward_returns[:100])[0, 1]
        print(f"信息系数(IC): {final_ic:.4f}")
        
        # 生成简单的交易信号
        signal = np.sign(factor_values)
        strategy_returns = signal[:-1] * forward_returns
        cumulative_return = np.cumprod(1 + strategy_returns[-252:])
        total_return = cumulative_return[-1] - 1 if len(cumulative_return) > 0 else 0
        
        print(f"假设策略收益(近252日): {total_return*100:.2f}%")
        print("\n⚠️ 风险提示:以上回测结果仅供学习研究,不构成投资建议!")

三、代码核心解析

3.1 因子表达式生成(Gene)

代码中的 FactorExpression 类是因子的"基因":

  • 它可以生成数学表达式树
  • 叶子节点是基础指标(SMA、RSI、动量等)
  • 内部节点是运算符(+、-、*、/)和函数(log、sqrt、abs)

3.2 适应度函数(Fitness)

因子好不好,用 IC(Information Coefficient) 来衡量:

  • IC = 因子值与未来收益的相关系数
  • IC 越高,说明因子的预测能力越强
  • 遗传算法会不断淘汰 IC 低的因子,保留 IC 高的因子

3.3 遗传操作

  • 选择:用锦标赛算法,从种群中挑选优秀的个体
  • 交叉:把两个好因子"混合"(代码中简化处理)
  • 变异:随机改变因子表达式,增加多样性,避免过早收敛

四、运行效果与实操建议

4.1 预期效果

运行上面的代码,你会看到类似这样的输出:

📊 正在获取 000300 历史数据...
✅ 成功获取 730 条数据

📋 可用指标数量: 25

🚀 开始因子挖掘进化过程...
📈 第 1/20 代 - 最大IC得分: 0.0523, 平均: 0.0215
📈 第 2/20 代 - 最大IC得分: 0.0687, 平均: 0.0342
...
📈 第 20/20 代 - 最大IC得分: 0.0891, 平均: 0.0512

🏆 挖掘到的最佳 Alpha 因子:
因子表达式: (momentum_10 * (sma_5 - sma_20))
信息系数(IC): 0.0891
假设策略收益(近252日): 15.32%

从结果可以看到:

  • 进化 20 代后,最大 IC 从 0.05 提升到 0.09
  • 挖掘到的因子是动量与均线的组合,有一定预测能力
  • 假设策略收益约 15%(注意:这是理想情况下的回测)

4.2 进阶优化方向

  1. 增加更多原始指标:加入基本面因子(PE、PB、ROE)、行业因子等
  2. 多目标优化:同时优化 IC、IC_IR(信息比率)、回测收益
  3. 时间序列交叉验证:用滚动窗口验证因子稳定性
  4. 加入交易成本:更真实的回测环境

五、注意事项与风险提示

⚠️ 重要提醒

  1. 过拟合风险:历史数据挖掘的因子可能在未来失效,需要样本外验证
  2. 市场风格切换:因子有效性会随市场环境变化,没有"圣杯"
  3. 交易成本:实际执行时,手续费、滑点会侵蚀收益
  4. 本文代码仅供学习研究,不构成任何投资建议

总结

今天我们用 Python + DEAP 库实现了一个自动因子挖掘机

  • 核心思想是遗传算法模拟自然选择
  • 用 IC(信息系数)作为适应度函数
  • 通过选择、交叉、变异不断进化
  • 最终可以自动发现有效的 Alpha 因子

当然,真正的量化策略需要更多的工作:因子组合、风险控制、组合优化等。但有了这个"因子挖掘机",至少你不再需要手动一个个尝试了!

如果你对量化感兴趣,欢迎在评论区讨论交流~


声明:本文部分链接为联盟推广链接,不影响价格。