尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

从数据到决策:10个Skill打造量化投研四级闭环

从数据到决策:10个Skill打造量化投研四级闭环 做量化投研的人几乎都会经历一个幻觉破灭的过程。刚入行时你以为瓶颈是策略觉得只要找到一个足够好的因子剩下的事就是躺赢。真做上两三年你会发现卡脖子的从来不是模型本身而是把数据、信号、策略、决策这四层真正串成一条不断线的流水线。我在自营和几家私募朋友那里见过太多团队因子库堆了几百个研报读了无数可真到要复现一个三个月前的结论时还是要人肉打开十几个脚本、手动对齐交易日、把 Excel 在群里传来传去。这次想聊的是我最近用 WorkBuddy 的 Skill 机制把一条完整的金融量化投研链路拆成 10 个 Skill、串成四级闭环的做法。所谓四级是数据层、信号层、策略层、决策层所谓闭环是决策层产生的结论能反过来约束信号和数据的采集范围而不是一条单向跑到黑的管道。整套东西不涉及任何投资建议它解决的只是研究流程的自动化与可复现——把重复劳动从人手里拿掉让每一次结论都能追溯到具体的输入和版本。如果你正在做量化研究、因子挖掘、组合管理或者只是想让 AI 助手真正参与到你的投研流程里而不只是陪你聊天这篇文章里的分层思路和接口约定都能直接抄。哪怕你不用 WorkBuddy这套10 个 Skill 分四层的拆法放到别的 Agent 工作台上同样成立。1. 为什么一条龙比单点神器更重要四级投研闭环到底解决什么1.1 单点 Skill 的幻觉每个都能用合起来跑不动Skill 这个概念刚火起来的时候大家的反应都是太爽了。写一个抓行情的 Skill能用写一个算因子的 Skill也能用再写一个生成研报的 Skill输出还挺唬人。于是很多人就以为自己的工作流已经自动化的实际上并没有。问题出在接口。每个 Skill 单独看都很聪明但它们彼此之间没有约定。抓行情那个 Skill 输出的日期是字符串2024-03-01算因子那个 Skill 期望的是datetime对象前一个用了后复权价格后一个默认拿的是不复权价前一个把缺失值填成 0后一个用 0 去算收益率直接把整条因子的分布带偏。单点能跑的 Skill串起来就是灾难。我见过最典型的场景一个团队把九个 Skill 都写好了每个都能跑出漂亮的结果可当他们想把流程自动化定时执行时发现根本接不上最后只能写一个巨大的胶水脚本把所有逻辑重新实现一遍。Skill 白写了。所以第一件要想清楚的事是Skill 的价值不在单点有多强而在于它能不能在一条链路上稳定地交出符合约定的输出。这也是我坚持按四级闭环来设计整套 Skill 的原因——不是先写 Skill 再想怎么串而是先定层、再定每层的输入输出契约最后才落每个 Skill 的实现。1.2 四级闭环的分层逻辑数据、信号、策略、决策我把整条量化投研链路切成四层每一层只解决一类问题不越界层级解决的问题核心产出对应 Skill 数第一级 数据层把散落的数据变成干净、对齐、可追溯的宽表标准化行情表、基本面表3第二级 信号层把宽表变成有统计意义的因子与信号因子值、信号分数3第三级 策略层把信号翻译成仓位与组合目标权重、回测曲线2第四级 决策层把组合变成可解释的结论与风控动作风险预警、投研报告2分层最直接的好处是故障定位快。某天回测结果突然变差你不需要从头翻代码。先看数据层有没有缺数据、复权参数有没有变再抽一两个因子看 IC 是否异常最后才怀疑策略本身。没有分层的时候你连问题出在哪一层都不知道。第二个好处是可替换。数据源换了你只需要改数据层的 Skill信号层以上完全不动。我最近就把一个行情源从 A 换成 B因为数据层的输出契约没变上面七个 Skill 一行代码都没改就平滑切换了。这在传统大脚本结构里几乎不可能。闭环体现在哪在决策层。投研报告 Skill 会记录这一期结论依赖了哪些因子、哪些数据区间当风控 Skill 发现某类风险暴露超标时它可以反向要求信号层缩小某个因子的权重区间甚至要求数据层补充采集特定风格的数据。链路不是单向的这是我把它叫闭环而不是流水线的原因。1.3 为什么是 10 个 Skill而不是 3 个或 30 个这是个被问过很多次的问题。直觉上 Skill 越少越简单或者越多越灵活实际上两端都是坑。**Skill 太少比如 3 个**会怎么样你会得到一个数据 Skill、策略 Skill、报告 Skill的粗颗粒结构。这种 Skill 内部逻辑极其臃肿一个数据 Skill 里塞了抓取、清洗、复权、对齐、缓存五件事。结果是调试困难出问题不知道是哪一步、复用困难只想用复权逻辑却要拖上抓取、版本管理困难改一个字整个 Skill 都要重新验证。粗颗粒的 Skill 本质上是把单体应用换了个名字。**Skill 太多比如 30 个**又会怎样管理成本爆炸。每个 Skill 都要有输入输出约定、都要有测试用例、都要维护依赖关系最后你大部分时间花在我这个改动会不会影响下游第七个 Skill上。而且 Skill 之间的调用链太长出错时日志散在十几个地方排查链路长得让人崩溃。我的经验值是每层 2 到 4 个、总数控制在 10 个上下最舒服。这个数量下每个 Skill 的职责足够单一大概一到两个函数级别的逻辑调用链长度适中最长也就四跳整体又足够覆盖从原始数据到结论的全流程。10 个不是拍脑袋定的是职责单一和链路可控这两个约束求交的结果。2. 数据层三个 Skill先把脏活从人手里拿掉2.1 行情落库 Skill接口选型与断点续补数据层最基础的一环是行情数据的采集与落库。这个 Skill 听起来简单做不做好差别极大因为 90% 的量化事故都跟某天数据没抓到或者抓到一半断了有关。我在设计这个 Skill 时给自己定了三条硬规则。第一条是幂等写入同一支股票同一天的数据不管跑多少次落库结果必须一致。实现方式是用 (代码, 日期) 作为唯一键做 upsert而不是无脑 append。这一条能救你无数次——网络抖动导致重试、脚本被误触发两次、定时任务重复启动都不会污染数据。第二条是断点续补。采集任务经常因为限流、超时、进程被杀而中断。Skill 必须能回答我已经抓到哪一天了这个问题然后从断点继续而不是从头再来。我的做法是在数据库里维护一张采集进度表记录每个标的的成功采集区间和最后尝试时间。重启时先读进度表算出缺口区间只补缺口。这个逻辑不复杂但少了它你的采集任务在几十个标的上跑到一半崩掉就真的想砸键盘。第三条是原始数据留底。清洗后的数据当然要存但采集回来的原始响应也建议原样存一份可以压缩至少保留一段时间。原因很现实你永远不知道哪天会发现某个字段的清洗逻辑写错了这时候如果没有原始数据就只能重新去拉——而历史分笔、历史快照这类数据往往是拉不回来的。# 行情采集 Skill 的关键配置示例 universe: [000001.SZ, 600000.SH, 300750.SZ] frequency: 1d adjust: qfq # 前复权需与下游因子层严格一致 retry: max_attempts: 5 backoff_seconds: [1, 3, 9, 27, 60] gap_fill: true # 启动时自动识别并补缺口 raw_snapshot: true # 原始响应留底提示adjust这个参数一定要在数据层就定死并且写进输出的元数据里。我踩过的最深的坑就是数据层用不复权因子层以为是前复权算出来的动量因子全是错的而且错得很合理肉眼根本看不出来。2.2 基本面解析 Skill从公告和财报里抠出结构化字段行情数据是规整的时序数据处理起来相对机械。真正折磨人的是基本面数据——财报 PDF、公告正文、各种披露文件格式五花八门同一个指标在不同年份的表述可能都不一样。这个 Skill 的核心任务是把非结构化文本变成结构化字段。我把它的实现拆成三级兜底优先走字段映射表其次走规则抽取最后才交给模型理解。为什么是这个顺序因为确定性和成本。字段映射表覆盖的是那些格式稳定的标准科目命中率可能就有七成而且零成本零风险规则抽取覆盖那些略有变化但仍有明显模式的数据只有前面都没命中的疑难杂症才调用模型去理解。这里有个关键设计模型抽取的结果必须带置信度和原文位置。一个字段如果模型只给了 0.5 的置信度它就不应该直接进入下游的因子计算而应该被标记成待人工确认。我见过太多团队直接信任模型输出结果某个季度把营业成本识别成了营业总成本因为两者数值刚好接近一路到回测都没被发现最后对着一个漂亮但错误的净值曲线高兴了半个月。另一个容易被忽略的点是单位。中文财报里金额单位可能是元、千元、万元有时候同一份文件里前后都不统一。这个 Skill 应该在解析完就把所有金额统一到元并在输出里显式标注单位换算系数。听起来是小事但- 2300 万元和- 2300 元混在一起照样能让你的估值模型飞上天。2.3 对齐与校准 Skill交易日历、复权、缺失值前两个 Skill 负责把数据弄进来第三个负责把它们对齐。这个 Skill 我称之为数据层的守门人因为下游所有计算都假设它输出了一个干净的宽表。它要处理三件事。第一件是交易日历对齐。不同市场、不同品种的交易日不一样节假日、临时休市也要考虑。对齐的时候要区分该有数据但没有和本来就不该有数据——前者是缺口要报警后者是正常休市要放过。很多新手把这两种情况混为一谈结果要么漏报了真实的数据缺失要么在节假日疯狂误报。第二件是复权处理。前复权、后复权、不复权各有适用场景回测通常用后复权避免历史价格被未来分红除权影响展示最近走势用前复权更直观。关键是全流程统一并在宽表元数据里写清楚用的是哪种。第三件也是最容易被低估的是缺失值策略。缺失值到底该填 0、前值填充、还是标记为 NaN取决于字段语义。价格类的缺失前值填充通常比填 0 合理得多成交量类如果当天确实停牌填 0 是符合事实的而财务指标如果缺失很多情况下应该保持 NaN 让下游因子层自己做决策硬填会引入虚假信号。我的做法是在这个 Skill 里对每类字段维护一套默认策略同时允许下游覆盖并把填了什么、填了多少记进日志。# 缺失值策略的示意按字段类型分派 MISSING_POLICY { close: ffill, # 价格前值填充 volume: zero, # 成交量停牌即 0 pe_ttm: nan, # 估值缺失保留 NaN revenue: nan, # 财务缺失保留 NaN } def fill_missing(df, policyMISSING_POLICY): for col, rule in policy.items(): if col not in df.columns: continue if rule ffill: df[col] df[col].ffill() elif rule zero: df[col] df[col].fillna(0) # nan 规则即保持原样交给下游判断 return df提示这个 Skill 一定要输出一份数据质量报告包含每个字段的缺失率、异常跳变次数、缺口日期列表。这份报告是后面决策层判断这次结论可信不可信的重要依据。一份基于 30% 缺失数据算出来的因子就算 IC 再高也不该被信任。3. 信号层三个 Skill从原始数据到能上桌的因子3.1 因子计算 Skill全量与增量怎么选数据层给了你干净的宽表信号层的第一个 Skill 负责把宽表变成因子。这里第一个要拍板的是全量重算还是增量更新。全量重算的逻辑很简单每次都从头把整个历史区间的因子算一遍。好处是永远不会因为状态残留导致数值不一致坏处是慢。如果你的股票池有几千个标的、历史有十几年、因子有几百个全量重算一次可能要几十分钟甚至更久。增量更新只算新增的那一天好处是快坏处是一旦某天算错错误会永久留存在历史里而且很难发现。我做增量的方式是增量为主、定期全量校验日常跑增量每周或每月做一次全量重算把结果和增量累积的结果对比出现偏差就报警。这样既有增量速度又有全量的可信度兜底。因子计算本身还有几个工程上的讲究。向量化优先能整表算的不要写循环尤其在滚动窗口计算上rolling、groupby这类向量化操作的性能比逐行循环高出几个数量级。中间结果缓存像移动平均、波动率这类被多个因子共用的中间量算一次缓存起来避免重复计算。参数外置窗口长度、阈值这些参数不要硬编码在逻辑里放到配置里方便后面做参数敏感性测试。# 滚动动量因子中间量复用 参数外置 def momentum(df, windows(20, 60, 120)): close df[close_adj] ret close.pct_change() out {} for w in windows: # 同一窗口的均值和波动率被多个因子共享 vol ret.rolling(w).std() out[fmom_{w}d] close / close.shift(w) - 1 out[fsharpe_{w}d] ret.rolling(w).mean() / vol return pd.DataFrame(out, indexdf.index)3.2 因子检验 SkillIC、分层、换手率三件套因子算出来不等于能用。第二个 Skill 的任务是给每个因子做体检不合格的直接拦在门外别流到策略层去。体检主要看三个指标。IC信息系数衡量因子值和未来收益的相关性是我最先看的。IC 均值要稳定为正或为负取决于因子方向ICIRIC 均值除以 IC 标准差要足够高一般我的经验线是 ICIR 低于 0.3 基本就没什么可说。更重要的是 IC 的稳定性一个因子如果三年里两年 IC 都很高、某一年突然掉到负值那它在实盘中大概率会在你最不希望的时候失效。分层测试是把股票按因子值分成若干组看各组的未来收益是否单调。理想情况下从高分组到低分组收益应该平稳递减。如果出现两头高中间低或者跳来跳去的形状说明这个因子跟收益的关系是非线性的或者不稳定的直接线性使用会出问题。换手率是最容易被忽略但最致命的。一个 IC 很高的因子如果每天换手率是 200%那点超额收益全被交易成本吃掉了。我在这个 Skill 里会把换手率和 IC 放在一起看算一个扣成本后的有效 IC只有这个值仍然为正的因子才保留。检验项关注点我的经验阈值不合格怎么办IC 均值方向是否稳定绝对值 0.02剔除或重新定义ICIR稳定性 0.3拉长窗口或降频使用分层单调性线性可用性首尾组收益单调做非线性变换或分组使用换手率成本侵蚀日换手 30%加平滑或降频3.3 信号合成 Skill多因子打分与阈值确定单因子体检过关后第三个 Skill 负责把它们合成一个综合信号。合成方法从简单到复杂有很长的光谱等权打分、IC 加权、最大化 ICIR 的最优组合、再到各种机器学习模型。我的建议是从等权开始不要一上来就上复杂模型。原因很实在等权打分的可解释性极强出了问题你知道是哪个因子拖后腿复杂模型虽然样本内表现好但样本外失效的概率也高而且失效的时候你很难说清到底哪一步出了问题。先用等权跑通整条链路、确认每个环节都没问题再逐步引入加权是我比较稳的节奏。合成的关键细节是标准化。不同因子的量纲天差地别动量的数量级是 0.1 左右成交额的数量级可能是 1e8。直接相加等于让成交额因子独占权重。我的做法是截面 Z-Score 标准化在每个交易日期内对全体标的的因子值做标准化消除量纲。这里要注意处理极端值先做 winsorize缩尾再标准化否则几个异常值会把均值和标准差带偏。阈值方面我倾向于分位数而非绝对值。比如信号前 10% 做多、后 10% 做空用分位数能自适应市场分布的变化。如果你写死因子值大于 2 就买入那在市场整体抬升的时候你会瞬间满仓在市场整体下压的时候你会一笔都不成交。分位数天然规避了这个问题。4. 策略层两个 Skill把信号翻译成仓位4.1 回测 Skill未来函数和幸存者偏差怎么防策略层的第一个 Skill 是回测。回测本身不难写难的是不骗自己。回测里最常见的两个谎言一个是未来函数一个是幸存者偏差我在这个 Skill 里对它们做了针对性的防护。未来函数指的是在计算 t 时刻的决策时用到了 t 时刻之后才有的信息。最隐蔽的一种是财务数据财报有披露日2024 年一季报的披露截止是 4 月底但很多人的数据表里直接把一季报的营收接到了 3 月 31 日。这等于你 3 月 31 日就知道了 4 月底才公布的数据回测当然一路飘红。我的做法是所有财务字段都按披露日实际公告日入库而不是会计截止日在回测里按披露日对齐。另一种未来函数出现在标准化环节如果你用整个回测区间的均值方差去做 Z-Score 标准化就等于用了未来的分布信息。正确做法是滚动窗口标准化每个时点只用该时点之前的数据算均值和方差。幸存者偏差指的是你的股票池只包含那些活到今天的标的退市的、被并购的都被剔除了导致回测结果偏乐观。防护方法是使用历史成分股每个时点应该用当时真实的成分构成而不是用现在的成分列表回溯。这一点在指数增强类策略里尤其关键A 股这些年的成分调整幅度不小不处理的话回测虚高是常态。# 回测时间对齐的核心原则任何信号只能用 t 的信息 def build_features(df, financials, asof_date): price_part df.loc[:asof_date] # 行情按自然日截断 fin_part financials[ financials[announce_date] asof_date # 财务按披露日截断 ].groupby(code).last() return price_part.join(fin_part, oncode)提示回测里我习惯加一个延迟一天的开关默认开启。也就是 t 日产生的信号t1 日才执行。这既符合现实收盘后算信号次日开盘才能下单又能顺带过滤掉一批隐蔽的未来函数。很多回测只要加上这一天延迟收益就腰斩这本身就是个好信号——说明原来的收益很大程度上来自不现实的信息优势。4.2 组合优化 Skill约束怎么写才落地信号变成权重中间要过组合优化这一关。这个 Skill 是最容易纸上很美、实盘很丑的地方因为教科书里的优化目标和现实中的约束条件差得远。我见过太多人一上来就写最大化预期收益、最小化组合方差跑出来一堆空头权重和 90% 的单一持仓。现实里的组合优化约束比目标重要得多。我在这个 Skill 里固定了几条约束单票权重上限通常 3% 到 5%防止赌单一标的、行业中性各行业权重偏离基准控制在 1% 到 2% 以内防止行业暴露失控、换手约束单期换手上限控制交易成本、持仓数量下限比如至少 50 只保证分散。约束的写法有个坑约束太紧会无解。如果你同时要求行业中性、单票不超过 2%、换手不超过 10%又想在某个极端行情下达到目标收益优化器很可能直接报无可行解。这时候程序不能崩而应该有降级策略——比如按优先级逐步放松约束先放松换手、再放松单票上限并把每次放松记进日志。我发现很多团队的优化 Skill 在无解时直接抛异常导致整个流程卡死其实只需要几行降级逻辑就能让流程继续。另一个实践心得是先做简单规则、再上优化器。等权、按信号分位数加权这些简单方法没有优化器那么多幺蛾子可解释性强作为基准线足够用。等你的流程跑顺了、确实发现简单方法满足不了需求再引入均值方差或者风险平价这类优化。顺序反了你会在调试优化器上花掉大量本可以用于打磨数据的时间。5. 决策层两个 Skill风控与投研报告5.1 风险监控 Skill预警不是越多越好决策层的第一个 Skill 是风险监控。它的定位不是帮你赚钱而是帮你在出事之前醒过来。很多团队的风控 Skill 设计得很全几十条预警规则一网打尽结果每天收到几百条告警最后所有人都把告警当背景噪音真正重要的那一条反而被淹没了。风控的第一原则是少而准。我一般只保留三类预警第一类流程性预警。数据缺失率超过阈值、采集任务连续两天失败、因子分布突然发生大偏移——这类预警指向的是你的流程可能有问题通常需要立刻处理因为流程坏了后面所有结论都不可信。第二类暴露度预警。组合在某个风格因子上的暴露超限、行业集中度超限、单一标的权重因为涨跌自然漂移超过上限。这类预警指向的是你可能不知不觉承担了不该承担的风险。第三类一致性预警。实际持仓和模型目标持仓偏离过大、实际成交和回测假设的成本差异过大。这类预警指向的是你的模型和现实脱节了。三类之外的我基本不加。宁可少一条预警漏掉一次机会也不愿意多十条预警让所有人都麻木。阈值的设计上我用的是分级而不是一刀切轻微超标只记日志中度超标发通知严重超标才要求人工介入。分级的好处是每一档的紧迫性不同处理方式的预期也不同。5.2 投研报告 Skill可追溯比好看重要决策层的第二个 Skill 负责把整条链路的结果汇总成一份投研报告。这里我想强调一个反直觉的观点报告的可追溯性比它好看重要一百倍。一份漂亮的报告图表精美、结论清晰但如果三个月后你看到它却完全想不起来当时用的什么数据、什么参数、什么版本那它就是废纸。我在设计这个 Skill 时强制它输出一份血缘记录这次报告用了哪个数据快照、跑了哪些因子、每个因子的参数是什么、组合优化的约束是什么、结果对应的代码版本是多少。有了血缘记录三件事就变得可能。第一复现任何时候你能把三个月前的结论一模一样地重跑出来。第二归因当结论出错时你能快速定位是数据错了、因子错了还是策略错了。第三对比这次的结论和上次的差异能精确到因为某个因子权重变了这个粒度而不是笼统的市场变了。报告的结构我倾向于固定成四段结论页这一期做了什么调整、为什么、依据页支撑结论的数据和因子表现、风险页当前的主要风险点、预警状态、附录页血缘记录、参数表、代码版本。固定结构的好处是习惯之后阅读效率极高团队成员一眼就能找到自己关心的部分。配合 WorkBuddy 的 Skill 机制这份报告可以做到全自动生成。每次流程跑完报告 Skill 自动拉取上游三个层级的输出拼装成结构化文档同时把血缘记录写进元数据。我实际用下来这个环节节省的时间是最直观的——以前写一份周报要小半天现在跑完流程报告就躺在那里了人只需要做最后一步的判断和标注。6. 让 10 个 Skill 真正一条龙接口约定与调度6.1 Skill 之间的契约输入输出统一前面九节讲了 10 个 Skill 各自做什么但让它们真正串起来的是契约。这是我整篇文章最想强调的一点也是大多数团队做失败的地方。契约包含四个要素格式、命名、时间语义、元数据。格式上统一用表格或者 JSON不要一个 Skill 输出 CSV、下一个输出 pickle格式转换本身就是 bug 温床。命名上字段名全局唯一且含义固定close_adj在所有 Skill 里都指前复权收盘价不许有的地方指后复权。时间语义上明确每个字段是交易日还是自然日、披露日还是截止日这是最容易出分歧的地方。元数据上每个 Skill 的输出都要带上数据的版本、生成时间、参数快照。{ data: ..., meta: { skill: factor_momentum, version: 1.3.0, params: {windows: [20, 60, 120]}, input_snapshot: 2024-06-30T09:00:00, adjust: qfq, generated_at: 2024-07-01T02:15:33 } }我踩过的最贵的一个坑就是两个 Skill 对日期的理解不一致。上游用交易日索引下游以为用的是自然日结果周末的因子值全部错位一个月后才发现。自从强制要求每个输出带元数据、并且元数据里明确写清楚时间语义这类问题基本绝迹。提示契约要写下来不要靠口头约定。我建议在每个 Skill 的目录里放一个contract.md写明输入字段、输出字段、单位、时间语义、异常情况下的行为。新人接手时看这份文件就够了不用读代码。6.2 三种触发方式定时、事件、手动串起来之后还要解决什么时候跑的问题。我一般配置三种触发方式覆盖不同场景。定时触发是最常用的按交易日历在收盘后自动跑整条链路。这里要注意的是依赖顺序数据层跑完才能跑信号层信号层跑完才能跑策略层不能并发乱跑。WorkBuddy 的编排能力可以直接表达这种依赖或者简单点用状态标记加轮询也能实现。事件触发用于应对突发情况。比如数据质量报告发现缺失率超标就自动触发一次数据补采某个预警被触发就自动跑一次专项分析。这类触发不需要每天发生但发生时必须及时。手动触发用于研究和调试。做新因子的时候我不会每次都跑全链路而是手动指定从信号层开始跑用缓存好的数据做快速迭代。这就要求每个 Skill 都支持单独运行模式而不是只能作为链条的一环。三种触发方式共存的时候最容易出问题的是并发冲突。定时任务和手动任务同时跑两个进程同时写同一张表数据就乱了。我的做法是在数据层的写入上加锁或者用一个简单的任务队列串行化保证同一时刻只有一个写操作。6.3 我踩过的三个坑状态污染、数据版本错乱、日志缺失第一个坑是状态污染。有些 Skill 会在内部维护缓存或者全局状态跑一次增量之后状态里残留了上一次的结果下次跑全量时受影响。解决方法是把 Skill 设计成无状态的所有输入显式传入所有输出显式返回需要缓存的东西放到外部存储并带上版本号。第二个坑是数据版本错乱。数据层更新了但信号层的缓存还是旧的算出来的因子和最新数据对不上。解决方法是给每次数据更新打一个快照 ID下游 Skill 必须在输入里声明自己基于哪个快照对不上就强制重算。这个机制加上之后我基本没再遇到过结果对不上但说不清哪里不对的情况。第三个坑是日志缺失。链路上任何一个 Skill 出错你都需要能快速知道是哪一步、什么参数、输入是什么。所以每个 Skill 的入口和出口都要打结构化日志入参、出参摘要、耗时、是否成功。日志不需要多详细但必须能回答这一步发生了什么。我现在的习惯是写 Skill 先写日志再写逻辑这样调试效率高很多。7. 落地节奏别一次上十个7.1 先做哪三个再往哪扩如果你看完觉得这套东西值得试我的建议是不要一次上十个 Skill那几乎必然半途而废。正确的节奏是先做三个跑通一条最短链路尝到甜头再扩。哪三个行情落库 因子计算 投研报告。这三个构成了最小闭环有数据、有信号、有结论。它们的实现难度都不高串起来之后你能立刻感受到数据自动更新、因子自动算、报告自动生成的便利。这个正反馈很重要它会支撑你熬过后面做基本面解析、组合优化这些硬骨头的过程。跑通最小闭环之后按这个顺序扩对齐校准因为它能显著提升数据质量、风险监控因为它能防止你在错误的路上跑太远、因子检验因为它能过滤掉大量无效因子、组合优化因为它最复杂放最后。基本面解析、回测、信号合成这三个可以根据你的策略类型灵活插入。我自己的实践是分三批上线每批之间隔两周左右让每个 Skill 都有时间经历真实的日常运行、暴露问题、被修稳。一次性全上再统一调试问题会堆在一起根本无从下手。7.2 常见问题与排查对照下面这张表是我实际遇到并解决过的问题按出现频率排序供你对照排查现象大概率原因排查方向全链路结果突然整体偏移数据层复权参数被改检查数据层输出的adjust元数据因子值大面积 NaN数据对齐时用了错误的交易日历对比对齐前后行数、检查缺口列表回测收益异常高存在未来函数按披露日重新对齐财务数据加一天延迟增量结果与全量不一致因子计算有状态残留检查缓存与全局变量做全量重算对比报告数据和持仓对不上数据版本错乱核对快照 ID确认下游基于最新快照优化器频繁报无解约束过紧按优先级逐级放松约束并记录预警被忽略阈值设置过宽或规则过多精简规则改为分级告警这张表本身就是我踩坑的浓缩。你如果刚开始搭大概率会在这七条里中招至少三次提前知道的话能省下不少熬夜的时间。真正把 10 个 Skill 串成一条稳定的链路之后我最大的感受不是效率提升而是心里的确定性。以前每次出一个结论我都隐隐担心这个数到底对不对、是不是哪里又错了。现在每一环都有契约、有元数据、有血缘、有校验我对结论的信任度完全不同。哪怕结论是错的我也能迅速知道错在哪、为什么错。这种东西比某一次多赚了多少值钱得多。最后提醒一句整套流程只做研究自动化与结论可追溯不构成任何投资建议。市场里的判断永远需要人来做Skill 能做的只是把那些本该由机器完成的脏活累活接过去让你有精力思考真正重要的那部分。
返回列表