拓冰建站拓冰建站
首页 / 资讯中心 / 正文

多周期信号融合:用 K-1 后移消灭未来函数,让周线给日线当过滤器

【量化工程实战进阶 】多周期信号融合用 K-1 后移消灭未来函数让周线给日线当过滤器上一篇我们把成本算到了基点级结论是「回测里固定佣金会排错策略名次」。这一篇聊另一个更隐蔽、却更致命的回测杀手多周期信号融合里的未来函数。先看一个用真实行情魔码纯 HTTP 接口、演示证书拉取跑出来的事实日线金叉明细 周线闸门判定: 日线2025-10-22 收盘11.520 MA511.462 MA2011.444 - 引用已完成周2025-10-19 周MA2011.960 价周MAFalse 周MA上行False 融合NO(被过滤) 日线2025-11-06 收盘11.510 MA511.474 MA2011.472 - 引用已完成周2025-11-02 周MA2011.941 价周MAFalse 周MA上行False 融合NO(被过滤)同一段样本里日线出现了 2 次 MA5/MA20 金叉但两次都被周线过滤器挡掉了——因为价格11.52、11.51还压在周线 MA20约 11.96下面周线本身也没抬头。这不是策略「没信号」是过滤器在帮你挡噪音。而如果你用错了引用方式后果更严重。下面这行是同一个日线金叉点2025-10-22下「偷懒写法」和「正确写法」引用的周线[未来函数反例] 日线金叉处completed(K-1后移) vs naive(最新周线) 日线2025-10-22 completed周2025-10-19 naive周2025-11-30 判定未来函数! completed周MA2011.960 naive周MA2011.793注意naive周2025-11-30在 2025-10-22 这一天你的代码「看到了」11 月 30 日那一周的周线——那一周在 10 月 22 日根本还没发生。这就是多周期策略里最经典的「开天眼」作弊回测虚高、实盘归零。本文把这件事一次说透什么是多周期融合、为什么它会引入未来函数、怎么用一行K-1 后移彻底消灭它、以及怎么用魔码统一数据源让「日线 周线」来自同一条管线从根上杜绝跨源拼接偏差。本文你将得到什么一个对「多周期共振」的正经定义它不是玄学是多尺度信号滤波共振失效的本质是信号延迟累积 同一数据源重复采样导致的伪阳性。一套零 SDK、纯 HTTP的取数方式日线和周线用同一个端点、只换一个周期参数就拿到了数据天然同源同口径。一个可运行的K-1 后移实现把「未完成周线」从信号计算里剔除附真实输出证明它和偷懒写法差出整整 6 周。一个「日线触发器 周线过滤器」融合框架给出完整代码和过滤结果。四个高频坑跨源拼接、naive 引用最新周线、小周期过度投票、频率限制误算。全部零 SDK、纯 HTTP GET代码先跑通再成文。一、为什么「单周期信号」是隐形杀手做择时的人几乎都从单周期起步日线金叉买、死叉卖回测一跑夏普诱人挂上模拟盘就被「虚假信号」淹没。问题不在因子在周期的颗粒度。小周期分钟/日线高噪声、低信息量。日线上一根放量长阳就触发买入但那可能只是日内情绪几小时后就阴跌回去。让分钟级形态参与投票策略稳定性会断崖式下跌。大周期周/月线反应慢。周线 MA20 的平滑特性意味着它对价格变动极不敏感——等周线发出「金叉」趋势往往已经走完大半此时的「共振」常常不是入场点而是趋势末端的确认。自相关性伪阳性。在日线和周线同时找相似形态本质是对同一段价格序列做重复采样。震荡市里这会制造极高的伪阳性率——「买入即套」。所以工程上正确的姿势不是「在不同周期各写一个策略」而是分工大周期定方向趋势过滤器小周期定时机触发器。只有两者同向才出信号。这一篇聚焦的就是这套分工里最容易被写错的那一环——时间对齐。二、多周期融合的本质信号滤波不是玄学把「周线与日线共振」还原成信号处理问题它等价于用一个低频滤波器周线去给一个高频触发器日线做门控。周线趋势过滤器(低频) ──┐ ├─→ AND ──→ 融合信号(只在两者同向时出) 日线触发器(高频) ──┘关键点有三个务必刻进脑子里大周期只做「允许/禁止」的门控不做精确点位。周线的职责是回答「现在能不能做多」不是「现在精确买在哪」。触发器必须引用「已收盘」的大周期值。这是整篇文章的命门下一节展开。融合是「与」关系不是「或」。任何一周期说 NO信号就废。这天然降低了交易次数、优化了盈亏比和最大回撤胜率不一定升但不会被冲动交易拖死。三、取数据同一端点切周期杜绝跨源拼接多周期策略最隐蔽的坑不在算法在数据。很多人的日线来自 Tushare、周线来自 AKShare、分钟线来自爬虫三个源复权基准不同、缺失补齐规则不同、字段口径不同比如成交量一个是「手」一个是「股×100」拼到一起直接污染信号。魔码的做法简单得多日线、周线、60 分钟线都是同一个端点只换一个周期参数。数据源完全一致复权口径一致字段名一致从根上消灭跨源偏差。importrequests BASEhttps://api.momaapi.comLICENCE你的魔码证书# 换成你自己的正式证书CODE600519# 日线dailyrequests.get(f{BASE}/hsstock/history/{CODE}/d/n/{LICENCE}?st20250101et20260830,timeout20).json()# 周线只把周期参数 d 改成 wweeklyrequests.get(f{BASE}/hsstock/history/{CODE}/w/n/{LICENCE}?st20250101et20260830,timeout20).json()# 60 分钟线改成 60min60requests.get(f{BASE}/hsstock/history/{CODE}/60/n/{LICENCE}?st20250101et20260830,timeout20).json()print(日线条数 ,len(daily), 窗口 ,daily[0][t][:10],-,daily[-1][t][:10])print(周线条数 ,len(weekly), 窗口 ,weekly[0][t][:10],-,weekly[-1][t][:10])print(60分条数 ,len(min60), 窗口 ,min60[0][t][:10],-,min60[-1][t][:10])真实输出演示证书日线条数 50 窗口 2025-09-15 - 2025-12-01 周线条数 48 窗口 2025-01-05 - 2025-11-30 60分条数 50 窗口 2025-11-13 14:00:00 - 2025-12-01 15:00:00字段坑历史 K 线的收盘价字段是c、成交量字段是v单位「手」1 手 100 股。成交额字段是a不是实时接口的cje。写历史计算时务必用b[c]/b[v]/b[a]照抄实时字段名会直接KeyError。魔码行情接口的一个约定周线 bar 的t固定为该周周日交易所周期结束日标签即使那天不是交易日。这意味着「周线最后一条的日期在未来」是设计行为不是 bug——下一节的 K-1 后移正是利用这个约定来判定的。三个周期同一份数据、同一套字段后面无论怎么切都不会出现「日线复权前、周线复权后」之类的错位。四、K-1 后移消灭未来函数本文核心现在进入最关键的部分。问题当策略跑到历史某一天比如周二需要读「周线指标」来决定能不能交易。如果代码直接取「当前最新的周线 bar」而这一周还没走完你就等于提前知道了这周周五的收盘——这就是未来函数回测虚高。标准解法K-1 后移。在第 T 日只允许引用「周日 T」的最后一个已完成周线 bar。换句话说周一到大周五的任何时刻周线因子最高只用到「上周日收盘」那一根。实现上就是一行查找在周线日期列表里找最后一个周日 当日的位置。importrequestsimportnumpyasnp BASEhttps://api.momaapi.comLICENCE你的魔码证书CODE600519defget_bars(period):urlf{BASE}/hsstock/history/{CODE}/{period}/n/{LICENCE}?st20250101et20260830returnrequests.get(url,timeout20).json()defsma(arr,n):outnp.full(len(arr),np.nan)foriinrange(n-1,len(arr)):out[i]float(np.mean(arr[i-n1:i1]))returnout dailyget_bars(d)weeklyget_bars(w)dcnp.array([float(b[c])forbindaily])dd[b[t][:10]forbindaily]wcnp.array([float(b[c])forbinweekly])wd[b[t][:10]forbinweekly]# 周线 t 该周周日wma20sma(wc,20)dma5sma(dc,5)dma20sma(dc,20)# K-1 后移找「周日 当日」的最后一个已完成周线位置defcompleted_weekly_idx(daily_date):best-1fori,tinenumerate(wd):iftdaily_date:bestielse:breakreturnbest# 反例naive 每根日线都引用「最新可用周线」可能含未完成周defnaive_weekly_idx(_daily_date):returnlen(weekly)-1print([未来函数反例] 日线金叉处completed(K-1后移) vs naive(最新周线))foriinrange(1,len(dc)):ifany(np.isnan(x)forxin(dma5[i],dma20[i],dma5[i-1],dma20[i-1])):continuecross_up(dma5[i-1]dma20[i-1])and(dma5[i]dma20[i-1])ifnotcross_up:continuewicompleted_weekly_idx(dd[i])ninaive_weekly_idx(dd[i])naive_futurewd[ni]dd[i]print(f 日线{dd[i]}completed周{wd[wi]}naive周{wd[ni]}f判定{未来函数!ifnaive_futureelse一致}fcompleted周MA20{wma20[wi]:.3f}naive周MA20{wma20[ni]:.3f})真实输出演示证书[未来函数反例] 日线金叉处completed(K-1后移) vs naive(最新周线) 日线2025-10-22 completed周2025-10-19 naive周2025-11-30 判定未来函数! completed周MA2011.960 naive周MA2011.793 日线2025-11-06 completed周2025-11-02 naive周2025-11-30 判定未来函数! completed周MA2011.941 naive周MA2011.793看出问题了吗在 2025-10-22 这一天naive 写法引用的周线t2025-11-30——那是 6 周之后的周线。如果你在回测里用了这个值等于在 10 月 22 日就「知道」了 11 月 30 日的周线收盘策略当然看起来稳如老狗但实盘里那一天根本不存在这个信息。一行if t daily_date: best i就把这个坑填死了。这就是 K-1 后移的全部代码量但它决定了你的回测是「能复现」还是「自欺欺人」。提示演示证书的周线窗口是固定样本2025-01-05 起换成你的正式证书后返回真实全区间。K-1 后移逻辑与数据内容无关直接套用即可。五、融合框架日线触发器 周线过滤器把上面的构件拼起来就是一个最小可用的融合信号生成器。日线触发器MA5 上穿 MA20金叉。周线过滤器引用「已完成周线」的 MA20要求a价格在周线 MA20 之上多头排列、b周线 MA20 上行趋势向上。融合触发器 AND 过滤器两者同向才出信号。importrequestsimportnumpyasnp BASEhttps://api.momaapi.comLICENCE你的魔码证书CODE600519defget_bars(period):urlf{BASE}/hsstock/history/{CODE}/{period}/n/{LICENCE}?st20250101et20260830returnrequests.get(url,timeout20).json()defsma(arr,n):outnp.full(len(arr),np.nan)foriinrange(n-1,len(arr)):out[i]float(np.mean(arr[i-n1:i1]))returnout dailyget_bars(d)weeklyget_bars(w)dcnp.array([float(b[c])forbindaily])dd[b[t][:10]forbindaily]wcnp.array([float(b[c])forbinweekly])wd[b[t][:10]forbinweekly]wma20sma(wc,20)dma5sma(dc,5)dma20sma(dc,20)defcompleted_weekly_idx(daily_date):best-1fori,tinenumerate(wd):iftdaily_date:bestielse:breakreturnbest fused[]foriinrange(1,len(dc)):ifany(np.isnan(x)forxin(dma5[i],dma20[i],dma5[i-1],dma20[i-1])):continuecross_up(dma5[i-1]dma20[i-1])and(dma5[i]dma20[i-1])ifnotcross_up:continuewicompleted_weekly_idx(dd[i])ifwi1:continuepos_updc[i]wma20[wi]# 价格在周线MA20之上slope_upwma20[wi]wma20[wi-1]# 周线MA20上行ifpos_upandslope_up:fused.append((dd[i],float(dma5[i]),float(dma20[i]),wd[wi]))else:print(f 日线{dd[i]}收盘{dc[i]:.3f}- 周线闸门: 价周MA{pos_up}周MA上行{slope_up} 融合NO(被过滤))print(f\n融合信号日线金叉 ∩ 周线向上{len(fused)}个)forsinfused:print(f 日期{s[0]}MA5{s[1]:.3f}MA20{s[2]:.3f}引用周线{s[3]})真实输出演示证书日线2025-10-22 收盘11.520 MA511.462 MA2011.444 - 周线闸门: 价周MAFalse 周MA上行False 融合NO(被过滤) 日线2025-11-06 收盘11.510 MA511.474 MA2011.472 - 周线闸门: 价周MAFalse 周MA上行False 融合NO(被过滤) 融合信号日线金叉 ∩ 周线向上 0 个注意这里的诚实结论在这段演示样本里日线出现 2 次金叉但周线过滤器把 2 次全挡了——因为价格11.52 / 11.51还压在周线 MA20约 11.96下面周线本身也没抬头。这正是过滤器的价值它宁可少给信号也不让你逆着大趋势抄底。换成正式证书拉一段真实上行窗口融合信号就会自然出现框架本身不挑数据。六、四个高频坑坑 1跨源拼接。日线一个源、周线一个源复权基准和字段口径不一致。解决同一端点切周期见第三节。坑 2naive 引用最新周线未来函数。每根日线都取weekly[-1]会把未完成周的信息泄漏给过去。解决K-1 后移completed_weekly_idx只用「周日 当日」的周线。坑 3小周期过度投票。把分钟线形态也拿去「投票」噪声会淹没信号。解决小周期只做执行点触发器趋势方向交给大周期大周期不每根小 K 线重算用状态变量缓存「最近一次已收盘的大周期方向」即可。坑 4频率限制误算。魔码套餐带「次/分钟」频率限制多周期 多标的批量拉取时很容易触发限频。解决先拉大周期周/月缓存到本地盘中只增量拉小周期。批量请求加退避重试读响应里的限频字段。历史数据落地后复用不要把回测每次都打向线上接口。importtime,requestsdefget_with_retry(url,tries3):forkinrange(tries):rrequests.get(url,timeout20)ifr.status_code429:# 触发限频time.sleep(2**k)# 指数退避continuer.raise_for_status()returnr.json()raiseRuntimeError(限频重试耗尽请降低拉取频率或升级套餐)小结多周期信号融合不是「共振即买」的玄学而是「大周期过滤 小周期触发」的信号滤波工程。它最致命的坑是未来函数——错把未完成的周线泄漏给过去回测虚高、实盘归零。用一行K-1 后移只引用「周日 当日」的已完成周线就能彻底填坑而用魔码同一端点切周期取数又从根上消灭了跨源拼接的口径偏差。下一篇我们接着聊另一个和时间轴有关的坑交易日历与停牌对齐——多标的回测里一只股票停牌会让concat直接错位、净值曲线凭空断崖这篇的 K-1 后移思想在日历对齐上还有更深的应用。免责声明本文所有代码与运行结果均来自魔码行情接口实测仅用于量化工程方法演示不构成任何投资建议。市场有风险投资需谨慎。历史回测不代表未来收益实盘前请务必用正式证书在真实数据上充分验证。—— 魔码云服想用真实全量数据跑通上面的融合框架领取魔码量化 Pro 包证书含日/周/分钟多周期历史与实时行情前往www.momaapi.com/certificate-apply.html免费版也可直接体验。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门