资讯动态

遗传编程与遗传算法在算法交易中的分工与实践

发布时间:2026/10/3 7:09:51 来源:尧图企业网站定制
简介一款以苹果股票价格预测为研究对象的算法交易程序采用Python实现面向对量化交易与进化计算感兴趣的开发者。项目内置两种独立策略遗传编程模块通过进化树形种群最小化预测价格与实际价格的误差融合纳斯达克、苹果及标普市场信号遗传算法模块则进化字符模型用于判断次日股价涨跌。压缩包共40个文件其中含21个py脚本、8个csv历史行情样本、7个txt设计说明及2个md文档整体大小约1.21MB目录按两种算法分模块组织并配有算法设计说明和运行指南。目前已有330人学习下载。读者可对照代码梳理数据预处理、信号构造、进化迭代与回测评估的完整流程也可在此框架上替换股票代码开展二次实验是掌握算法交易与进化计算的实用参考。1. 算法交易程序里的遗传编程和遗传算法这不是 AI 荐股是让程序自己长出因子表达式拿到这个标题的读者多半不是在找“稳赚策略”而是想搞清楚遗传编程和遗传算法在同一套算法交易程序里到底怎么分工、代码怎么写、跑出来靠不靠谱。先给结论用遗传编程GP预测股票价格本质是从历史行情里演化出一组数学表达式比如mul(add(mom20, vol20), log_abs(ret60))这样的因子公式遗传算法GA则在表达式定下来之后专门去搜参数——止损放多少、持仓拿几天、开仓阈值设多高。GP解决“长什么样”GA解决“具体调成什么值”。它不是黑匣子每一步演化出来的结果都能打印成公式、写进文档、放进回测。适合有 Python 基础、想自己搭一套可解释量化策略框架的从业者也适合想验证“进化计算在因子挖掘上到底值不值得投入”的团队。它不是荐股工具不承诺收益率它是一套你能控制每一步的预测与回测流水线。2. 遗传编程和遗传算法各自解决哪一环GP 找表达式结构GA 找参数2.1 遗传算法原理从一个参数空间搜索的例子说起遗传算法原理说起来不复杂把一组待优化参数编码成一个“个体”比如[持仓天数, 止损线, 开仓阈值]给每个个体算一个适应度分数然后用选择、交叉、变异迭代出下一代。它不依赖梯度所以适应度函数哪怕是个有大量毛刺的回测结果搜索照样进行。在算法交易里最常见的 GA 用法就是做参数寻优。举个例子你写了一个双均线策略快线周期、慢线周期、止损比例三个参数。手调只能试几十组穷举三万个组合又太慢。GA 的做法是随机生成 40 组参数作为初代种群每组参数跑一遍历史回测把夏普比率当适应度然后锦标赛选择留下表现好的对参数做算术交叉和高斯扰动再迭代 30 代。整个过程一晚上能跑完最后拿到的参数组合往往比手调稳定得多。需要特别注意的是GA 把参数空间搜索做得很溜但它不会告诉你“双均线这个结构本身行不行”。如果策略结构是错的GA 搜到的最优参数也只是在错误结构里相对没那么差。这个边界感必须建立起来否则很容易出现“最优参数回测漂亮、实盘一塌糊涂”的翻车事故。在遗传算法原理的三个操作子里选择压力是最容易被忽略的。锦标赛大小设成 3 和设成 6搜索结果差异很大前者多样性更好但收敛慢后者收敛快却容易在局部最优里出不来。量化场景的特征维度高、噪声大我一般优先偏探索锦标赛大小取 3 或 4宁可多跑几代也不要让它早早锁定一个次优解。2.2 遗传编程一棵公式树是怎么长出来的遗传编程和 GA 最大的区别在个体的编码方式。GA 是定长的参数向量GP 是一棵树。这棵树可以由函数节点和终端节点组成比如mul(add(mom20, vol20), log_abs(ret60))其中add、mul是函数节点mom20、vol20、ret60是终端节点也就是你提前构造好的特征。演化过程跟 GA 类似初代用genHalfAndHalf生成一堆深浅不一的随机树评估时把每棵树编译成 Python 函数逐行套到历史数据上算出预测值然后按预测值和未来收益的秩相关系数IC打分接着锦标赛选择、子树交叉、子树变异迭代几十代。树交叉不是交换两个参数而是交换两个子树所以后代可能比父代更深也可能更浅。树的最大深度是一个硬约束我一般限制在 6 层以内太深的树在样本外几乎没有生存能力只有拟合噪声的本事。GP 做股票价格预测和深度学习比最大的优势是可解释性。LSTM 给你一个无法读懂的浮点输出GP 给你一棵公式树你可以gp.stringify打印出来扔给投研同事看哪里有问题改哪里——某个特征量级太大、某个常数范围不合理一眼就能发现。相比“黑匣子”GP 更像一个结构搜索工具。代价是计算量更大、随机性更强同一个种子和不同种子跑出来的结构差异可能非常大这一点后面专门讲怎么处理。2.3 GP 与 GA 的流水线分工先找结构再定参数GP 和 GA 在同一套算法交易程序里不是并列关系而是接力关系。常见做法是 GP 先跑给定一组原始特征演化出若干候选预测表达式。注意这一步的目标不是直接给出买卖信号而是生成一个“预测未来 N 日收益排序”的因子表达式。拿到这个表达式后再进入 GA 阶段把表达式里的常数、开仓阈值、持仓窗口、止损止盈交给 GA 去优化——一个进化算法管结构另一个管参数两件事互不干扰各自在自己的空间里搜。很多人误以为 GA 能直接替代 GP把表达式结构编码成一个字符串丢给 GA 去变异实践里这样做收敛极慢因为结构空间是离散的、非连续的字符串交叉很难产生有意义的子树组合。反过来用 GP 去调参数也不合适树结构里的常数通常靠 ephemeral constant 随机生成精度远不如 GA 的浮点编码。把结构搜索和参数搜索分开是这条技术路线上性价比最高的分工方式。3. 用 Python 从零跑通最小方案数据准备、GP 演化、GA 调参、回测一条线3.1 数据准备用 yfinance 拉沪深300收盘价并切成对数收益环境配置上只要装好 Python 3.8 以上的环境配齐pandas、numpy、scipy、deap、yfinance这几件套就能开跑。建议单独建一个虚拟环境避免把系统 Python 搞乱。数据获取直接用 yfinance 拉沪深300指数日线作为演示标的它流动性好、成分稳定适合当基准。import yfinance as yf import pandas as pd import numpy as np df yf.download(000300.SS, start2015-01-01, end2023-12-31, auto_adjustTrue) close df[Close].dropna() ret np.log(close / close.shift(1)).dropna() print(close.head()) print(ret.describe())这里有两个细节值得说明。第一用对数收益而不是原始价格训练预测表达式是因为价格的绝对值随时间漂移直接用价格做回归会学到“价格越来越高”这种没意义的趋势而对数收益是平稳的。第二dropna()必须做close.shift(1)会让第一行变成 NaN特征矩阵和标签对不齐是后续所有莫名其妙的 IC 异常的第一个嫌疑点。如果你的 yfinance 返回的是多列 MultiIndex只保留单标的的Close列即可。3.2 GP 演化预测表达式DEAP 里的算子集合与适应度设计DEAP 是做这项实验最顺手的库。下面这段代码演示从算子集合到个体评估的完整骨架。特征先手工构造三个动量类因子20 日动量、60 日动量、20 日波动率作为树的终端节点。import math import random import operator import numpy as np from deap import base, creator, tools, gp, algorithms from scipy.stats import spearmanr def log_abs(x): return math.log(abs(x) 1e-8) def protected_div(x, y): return x / y if abs(y) 1e-8 else 1.0 mom20 close / close.shift(20) - 1.0 mom60 close / close.shift(60) - 1.0 vol20 ret.rolling(20).std() X pd.DataFrame({mom20: mom20, mom60: mom60, vol20: vol20}).dropna() y close.shift(-5) / close - 1.0 y y.reindex(X.index).dropna() X X.loc[y.index] pset gp.PrimitiveSet(MAIN, 3) pset.addPrimitive(operator.add, 2) pset.addPrimitive(operator.sub, 2) pset.addPrimitive(operator.mul, 2) pset.addPrimitive(protected_div, 2) pset.addPrimitive(log_abs, 1) pset.addEphemeralConstant(rand_const, lambda: random.uniform(0.1, 10)) pset.renameArguments(ARG0mom20, ARG1mom60, ARG2vol20) creator.create(FitnessIC, base.Fitness, weights(1.0,)) creator.create(Individual, gp.PrimitiveTree, fitnesscreator.FitnessIC) toolbox base.Toolbox() toolbox.register(expr, gp.genHalfAndHalf, psetpset, min_1, max_2) toolbox.register(individual, tools.initIterate, creator.Individual, toolbox.expr) toolbox.register(population, tools.initRepeat, list, toolbox.individual) toolbox.register(compile, gp.compile, psetpset) toolbox.register(select, tools.selTournament, tournsize3) toolbox.register(mate, gp.cxOnePoint) toolbox.register(expr_mut, gp.genFull, min_0, max_2) toolbox.register(mutate, gp.mutUniform, exprtoolbox.expr_mut, psetpset) def eval_ic(individual): func toolbox.compile(exprindividual) pred np.array([func(*row) for row in X.values], dtypefloat) valid np.isfinite(pred) np.isfinite(y.values) if valid.sum() 100: return (-1.0,) ic spearmanr(pred[valid], y.values[valid])[0] depth_penalty 0.05 * max(0, len(individual) - 24) return (ic - depth_penalty,) toolbox.register(evaluate, eval_ic) stats tools.Statistics(lambda ind: ind.fitness.values[0]) stats.register(avg, np.mean) stats.register(max, np.max) hof tools.HallOfFame(1) pop toolbox.population(n120) pop, log algorithms.eaSimple(pop, toolbox, cxpb0.85, mutpb0.15, ngen20, statsstats, halloffamehof, verboseTrue) print(gp.stringify(hof[0]))这段代码有几个关键参数直接影响结果。种群大小 120、代数 20 是通用起步值如果数据量大可以适当缩到 80 和 15优先保证一次实验能在几分钟内跑完。算子集合里我刻意加入了log_abs和protected_div因为金融数据里经常出现负数和零值原生的log和除法会在评估时抛异常这一层保护必须做在算子层而不是事后清洗。适应度用秩相关系数 IC 而不是 MSE是因为股票预测更关心排序而不是精确价格MSE 会被极端值带偏而 Spearman 相关性对单调变换不敏感。深度惩罚0.05 * max(0, len(individual) - 24)是个实用技巧表达式越大适应度扣分越多防止演化出一棵包含 50 个节点的复杂树去硬记噪声。3.3 GA 接力寻参把表达式参数和交易参数一起优化GP 演化出的表达式只输出一个连续预测值离可交易还有距离。下一步交给 GA优化三个交易参数持仓窗口缩放系数、止损线、开仓阈值。这段遗传算法 Python 代码沿用 DEAP但适应度类换一个名字避免与 GP 的FitnessIC冲突。creator.create(FitnessSharpe, base.Fitness, weights(1.0,)) creator.create(Individual, list, fitnesscreator.FitnessSharpe) LOW [0.02, 0.005, 0.0] HIGH [1.0, 0.05, 1.0] def uniform_init(): return [random.uniform(lo, hi) for lo, hi in zip(LOW, HIGH)] def eval_sharpe(individual): hold_scale, stop_loss, threshold individual hold_days max(1, int(round(hold_scale * 30))) pred pd.Series(gp_pred, indexy.index) raw_weight np.where(pred threshold, 1.0, -1.0) position pd.Series(raw_weight, indexy.index).rolling( hold_days, min_periods1).mean().fillna(0.0) strat_ret position.shift(1).values * ret.reindex(y.index).values strat_ret strat_ret[np.isfinite(strat_ret)] if len(strat_ret) 30 or np.std(strat_ret) 1e-12: return (-10.0,) sharpe np.sqrt(252) * np.mean(strat_ret) / np.std(strat_ret) return (sharpe,) toolbox2 base.Toolbox() toolbox2.register(individual, tools.initIterate, creator.Individual, uniform_init) toolbox2.register(population, tools.initRepeat, list, toolbox2.individual) toolbox2.register(evaluate, eval_sharpe) toolbox2.register(mate, tools.cxSimulatedBinaryBounded, lowLOW, upHIGH, eta20.0) toolbox2.register(mutate, tools.mutPolynomialBounded, lowLOW, upHIGH, eta20.0, indpb0.2) toolbox2.register(select, tools.selTournament, tournsize4) pop2 toolbox2.population(n40) algorithms.eaMuPlusLambda(pop2, toolbox2, mu40, lambda_40, cxpb0.7, mutpb0.3, ngen30, verboseTrue)这里最值得讲的参数是cxSimulatedBinaryBounded的eta20.0。模拟二进制交叉的eta控制子代与父代的相似程度eta越大子代越贴近父代搜索越精细eta越小子代跳跃越大越容易探索新区域。在量化参数寻优里我一般先粗搜用eta10找到高适应度区域后再细搜用eta30一次性跑完的实验通常取 20 作折中。另外position.shift(1)这行是未来函数防火墙——当天的信号只能次日生效跳过这步的人会在回测里看到一条几乎完美的净值曲线实盘第一周就被市场教训。止损线在这个向量化版本里没有直接参与回测实际落地时建议单独用逐日循环模块叠加避免向量化代码里写复杂的状态逻辑。3.4 最小回测先算清你赚的是不是手续费框架可以不上回测必须自己写一遍。一个 20 行的最小回测足够回答“这个策略到底行不行”position position.shift(1).fillna(0.0) daily_ret position * ret.reindex(y.index).fillna(0.0) cum (1 daily_ret).cumprod() max_drawdown (cum / cum.cummax() - 1.0).min() sharpe np.sqrt(252) * daily_ret.mean() / (daily_ret.std() 1e-12) cost_rate 0.00025 0.0005 turnover (position.diff().abs().sum() / len(position)) * 252 net_ret daily_ret - position.diff().abs() * cost_rate net_sharpe np.sqrt(252) * net_ret.mean() / (net_ret.std() 1e-12) print(f毛夏普: {sharpe:.2f}, 最大回撤: {max_drawdown:.2%}, 年化换手: {turnover:.1f}) print(f扣成本后夏普: {net_sharpe:.2f})这么短的回测已经覆盖了三个核心问题毛夏普能不能看、回撤能不能忍、换手率会不会把收益吃掉。position.diff().abs()算的是每期仓位变化乘以双边成本率再乘 252 就是年化磨损。做这一步的核心目的是在投入复杂回测框架之前先淘汰掉一批依赖超高换手才能盈利的伪策略。完整工程落地时再替换成支持逐日撮合、手续费滑点分项统计的回测框架但最小回测永远是最快的那把尺子。4. 关键参数怎么调才不翻车种群、代数、树深度与成本敏感性4.1 GP 与 GA 关键参数速查表参数设置有时候是玄学但有这张表兜底至少不会在起步阶段翻得很惨。下面是我在类似项目里常用的范围新手可以直接照抄起步再根据自己的数据量和机器性能调整。参数常见范围影响翻车特征GP 种群大小80–200太小早熟太大每代评估太慢适应度前几代猛涨后彻底卡住GP 代数15–30越久越容易过拟合后期适应度微涨但样本外崩掉树最大深度5–7深度直接决定表达式复杂度表达式超过 30 个节点且很难读懂锦标赛大小3–5选择压力取 6 以上多样性快速丢失交叉率0.8–0.9结构重组频率小于 0.6 时收敛极慢变异率0.1–0.2引入新结构高于 0.3 时最优个体反复丢失ephemeral 常数范围0.1–10表达式里的尺度因子范围太窄导致预测值量级过小GA 种群大小30–50参数空间维度不高时可小小于 20 容易陷入局部最优GA 代数20–40参数寻优收敛速度超过 50 代适应度不再变化多项式变异 eta10–30变异步长控制eta 太大接近无变异太小到处乱跳这里最容易被忽略的是 GP 的代数其实不宜太长。很多人误以为进化代数越多越好实际上 GP 在 15 代左右就已经把训练期里的有效结构挖得差不多了后面每一代都在往噪声上贴合。判断依据很简单把每一代的最优 IC 和验证集 IC 同时打点训练 IC 还在涨而验证 IC 开始掉的时候就是该停的时候。没有后悔药提前做好早停观察比事后调参重要得多。4.2 标签与数据切分预测未来几个 bar 决定问题性质预测股票价格具体化之后的监督信号是“未来 N 日收益”。N 选 1 天策略变成高频择时选 20 天变成波段因子选 60 天变成偏中低频的截面排序。N 的选择决定了整个问题的性质也决定了换手率的天花板。用 5 天预测做演示最合适信号有一定持续性换手又不会高到离谱。forward close.shift(-1) / close - 1.0 forward forward.rolling(5).mean().shift(-5) y forward.reindex(X.index).dropna() X X.loc[y.index] split_train int(len(X) * 0.6) split_valid int(len(X) * 0.8) X_train, y_train X.iloc[:split_train], y.iloc[:split_train] X_valid, y_valid X.iloc[split_train:split_valid], y.iloc[split_train:split_valid] X_test, y_test X.iloc[split_valid:], y.iloc[split_valid:]这段切分代码里藏着一个很容易犯的错窗口函数和shift的组合要想清楚时序对齐。close.shift(-1)已经是未来数据再做rolling(5).mean()会继续向前扩展窗口最后的shift(-5)是为了把标签中心对齐到预测日。任何一步方向错了都会在训练集里混入未来信息导致 IC 虚高。我的一线习惯是标签构造完立刻打印y.tail(5)和close.tail(6)肉眼确认最后一行标签是 NaN、倒数第二行对应的是未来第五天的收益确认无误再往下走。时序切分和机器学习的随机切分是两回事不能用train_test_split打乱数据。金融时间序列一旦打乱未来信息直接泄漏进训练集回测再漂亮也是自欺欺人。按时间顺序切成训练、验证、测试三段测试段必须是最后 20% 的时间区间不能混进训练。4.3 成本模型换手率一上来收益全是假的所有进化类策略都有同一个毛病为了提升适应度会演化出频繁改变预测符号的表达式表现在持仓上就是每天都换仓。GP 和 GA 都不会主动控制换手率如果你的适应度函数里没有交易成本它们就会把交易成本这个约束丢到一边疯狂追求信号拟合精度。我在上一节最小回测里已经给出成本计算的骨架。实际调参时建议把成本直接写进 GP 的评估函数而不是只放在回测阶段。做法是对种群里的每个个体先算预测值再换算成平均持仓变化估算年化换手最后在 IC 适应度里减去一个换手惩罚项。改造方案是在eval_ic里加这么一段逻辑pred_series pd.Series(pred, indexy.index) weight np.where(pred_series pred_series.median(), 1.0, -1.0) pos_series pd.Series(weight, indexy.index).rolling( max(1, int(round(hold_days_est))), min_periods1).mean().fillna(0.0) annual_turnover pos_series.diff().abs().mean() * 252 ic spearmanr(pred[valid], y.values[valid])[0] score ic - 0.02 * max(0.0, annual_turnover - 20.0)这里的0.02是换手惩罚系数表示年化换手每超过 20 倍IC 就扣 0.02。系数取值因策略而异但思路是固定的让进化过程自己去权衡“预测准”和“少交易”这两件事而不是等回测阶段才发现问题。血泪经验是不加这个惩罚的 GP 策略样本内 IC 能到 0.08看起来很不错扣完手续费之后夏普直奔负数——赚的全是手续费。5. 五个高频踩坑记录从 DEAP 报错到样本外失效5.1 DEAP 同一进程重复调用 creator 直接报错现象在同一脚本里先跑完 GP再跑 GA第二次执行creator.create(FitnessSharpe, ...)时抛出类似TypeError: FitnessSharpe already exists的异常。原因DEAP 的creator.create是一旦执行就永久注册在模块命名空间里的同一进程重复创建同名类是设计上的问题不是你的代码逻辑错误。解决给每个流程不同命名比如 GP 用FitnessICGA 用FitnessSharpe。如果同一个个体类型需要反复重建用if not hasattr(creator, FitnessSharpe)包裹创建语句保证只创建一次。这是 DEAP 工程化里最常见的第一道坎踩过一次之后就会习惯性起名加上前缀。5.2 适应度卡住不涨早熟收敛的特征和解法现象演化跑到第 5 代种群平均 IC 就不再变化最优个体也没更新后续 15 代全部白跑。原因锦标赛选择压力太大加上cxOnePoint交叉后优秀个体的子树在种群内快速扩散多样性在几代之内耗尽所有个体长得越来越像近亲繁殖导致搜索停滞。解决先把锦标赛大小从 5 降到 3再把变异率从 0.1 提到 0.2。如果还是卡就在每代结束后随机往种群里注入 10% 的新个体用genHalfAndHalf重新生成给演化池补充新鲜结构。另一个有效做法是引入共享适应度机制计算个体与最优个体的树结构相似度相似度越高适应度折扣越大强制种群保持分布。这个操作虽然会让最优解搜索变慢但能明显提高最终结果的稳定性。5.3 样本内 IC 高、样本外崩溃过拟合和未来函数的锅现象训练集上最优 IC 达到 0.1验证集掉到 0.02测试集直接变成 -0.03。最气人的是换一个随机种子跑一遍结果又不一样。原因两个可能性叠加。一是 GP 表达式过深过大在训练集上记住了噪声二是标签构造用了未来窗口训练时看似完美实盘里根本没有那个信息。解决先从数据层排查未来函数把shift方向和窗口对齐逐行核验。确认没有泄漏后在适应度里同时加深度惩罚和换手惩罚把表达式压到 20 个节点以内。最后做滚动验证把训练区间每次向后滑 20 个交易日反复演化并记录验证 IC 的分布区间。如果验证 IC 的上下四分位跨越 0 太多说明结构本身不稳定再换一组原始特征或调整预测周期不要硬撑。5.4 高换手策略把收益全赔给手续费现象扣成本前夏普 1.5扣成本后夏普 -0.2。查看逐日仓位发现信号几乎每天在 1 和 -1 之间反复横跳。原因GP 演化出的表达式可能在某个特征穿过阈值时输出剧烈跳变比如protected_div(mom20, vol20)在波动率很小时会产生极端值导致预测符号频繁翻转。GA 在优化持仓窗口时也会把窗口缩到最短以追求适应度提升。解决在 GP 评估里加入换手惩罚把年化换手上限写进适应度函数同时把 GA 的持仓窗口参数下限从 1 天抬到 3 天。表达式输出层再做一层平滑预测值取过去 3 日的加权平均再和阈值比较能显著降低信号的毛刺。量化交易策略代码里最容易低估的就是这个环节进化算法不会替你考虑交易成本你不写进去它永远不会自己学会。5.5 两次运行结果差异巨大随机种子和一致性检查现象同一份数据、同一套代码昨天跑出来的最优表达式是mul(mom20, vol20)今天再跑变成add(mom60, log_abs(vol20))完全对不上。原因初代种群是随机生成的GP 的搜索结果高度依赖初代结构分布固定了随机种子并不能解决问题它只是让两次运行完全一样而不是让结果更可靠。解决第一每次运行前固定random.seed(seed)和np.random.seed(seed)并把 seed 记录进实验日志保证任何一次结果可追溯。第二每个种子独立跑 5 次取验证 IC 的中位数作为该种子的评价再比较不同种子之间的结果一致性。最终是否采用某个表达式不看单次最优而是看它在 80% 的随机种子下都有正 IC。这是对抗进化随机性的唯一可靠方式没有捷径。6. 进阶验证技巧滚动重演化比单次演化可靠得多单跑一次 GP、再跑一次 GA、回测通过这只是入门。真正让这套算法交易程序具备实盘研究价值的是把“单次演化”升级成“滚动重演化”让因子表达式跟随市场风格变化持续更新。做法是按步长滚动切片每次拿最近 250 个交易日的窗口去演化和寻参表达式只在接下来 20 个交易日内使用到期后滑到下一个窗口重新演化。这样保证预测表达式永远基于“最近的市场模式”而不是一次性用一个五年前的数据演化出的公式去打明天的仗。代码骨架如下window 250 step 20 factor pd.Series(indexclose.index, dtypefloat) for start in range(0, len(close) - window - step, step): train_win close.iloc[start:start window] best_expr run_gp_on(train_win) future_win close.iloc[start window:start window step] factor.loc[future_win.index] apply_expr(best_expr, future_win)滚动重演化还有个副产品表达式存活监测。每个表达式投入使用后按 30 天滚动窗口重新计算它与未来收益的 IC并记录这条 IC 时间序列。当滚动 IC 连续 10 个交易日低于零就把它标记为退化提前切换到下一个候选表达式而不是等回撤扩大了才反应过来。我最早做的项目就是吃了全样本做特征选择的亏一次演化定终身半年后市场风格切换策略直接进入漫长回撤那段时间每天看着净值曲线往下掉才真正明白滚动重演化不是进阶技巧而是生存必需。最后补一句实盘习惯任何进化出来的表达式上实盘前必须经过一次人工审查打印出公式、理解每个特征的经济学含义、确认没有用到未来信息。进化算法给你的是候选方案不是免检结论。这一条希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑