news 2026/10/14 9:45:34

多Agent量化交易流水线:LGBM市场状态分类与周度复盘

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
多Agent量化交易流水线:LGBM市场状态分类与周度复盘

简介:这是一套面向量化入门者与Python爱好者的轻量级AI炒股系统源码,聚焦实战场景下的择时分析与周度复盘能力,解决初学者难以落地多模型协同与Agent模块化设计的痛点。资源共11个文件,含4个核心Python脚本(如app.py、main.py、train.py)、2个预训练模型pkl文件(分类与回归双模型各一)、2个示例数据csv(data.csv、trades.csv)及requirements.txt等,总大小仅598KB,普通笔记本即可快速运行调试。已有440人学习下载,适合用于课程设计、个人量化项目孵化或Agent架构学习实践。读者可直接运行Streamlit交互界面完成单票择时推演,调用多Agent模块解耦的选股、风控、择时与复盘流程,并自动生成结构化周报;代码结构清晰,utils.py封装通用工具,__pycache__已包含编译缓存,便于二次开发与模型替换。

1. 这不是“AI选股App”,而是一套可审计、可回溯、可周度迭代的交易决策流水线:用多Agent解耦信号生成、仓位管理与复盘归因,LGBM不直接预测涨跌,而是建模“当前市场状态是否适合执行某类择时策略”

你见过太多“AI炒股”项目——模型一跑,准确率92%,回测曲线光滑如镜,但实盘第一天就爆仓。问题不在算法,而在系统设计:把价格预测、仓位计算、止损逻辑、复盘归因全塞进一个黑匣子,出问题时连哪一行代码在哪个市场环境下失效都查不到。本方案彻底放弃“端到端预测股价”的玄学路径,转而构建一条可拆解、可验证、可周度快修的决策流水线:用多Agent架构将“什么时候该看什么信号”(择时分析)和“看到信号后怎么下注、怎么盯盘、怎么收手”(仓位执行)物理隔离;LGBM不预测明天涨还是跌,而是训练成一个市场状态分类器——它判断的是“当前波动率+量能+板块轮动强度是否处于历史前15%的高风险区”,这个输出直接驱动Agent的决策开关;所有中间结果(信号触发时间、仓位变化轨迹、盈亏归因到具体因子)自动存入本地SQLite,支撑后续周度复盘。适合有Python工程能力、熟悉pandas和lightgbm、手头已有日频行情数据(A股/港股/美股均可)的量化实践者,目标不是替代人做决策,而是让每次交易都有据可查、每次复盘都有迹可循。


2. 多Agent架构设计:为什么不用单模型硬编码?三个Agent的职责边界与通信契约

2.1 择时Agent:只负责“发信号”,不碰仓位,不关心盈亏

择时Agent是整个系统的哨兵。它的唯一输入是过去60个交易日的OHLCV、行业资金流、北向持仓变化(可选),输出是结构化信号:{'signal_type': 'breakout', 'confidence': 0.82, 'valid_until': '2024-06-28', 'target_stocks': ['600519.SH', '000858.SZ']}。关键设计点在于信号必须带有效期和置信度——避免“过期信号”被误执行。我们不用强化学习动态调整阈值,而是用LGBM输出的原始分数(model.predict_proba(X)[:, 1])直接作为置信度,因为LGBM的输出概率在跨周期校准后具备统计意义(后文详述校准方法)。信号类型严格限定为预定义枚举:['breakout', 'mean_reversion', 'trend_follow', 'volatility_squeeze'],禁止自由文本,确保下游Agent能无歧义解析。

2.2 执行Agent:只响应信号,按规则算仓位,不修改信号

执行Agent收到择时Agent的信号后,启动三步确定性流程:

  1. 过滤:剔除ST、停牌、流通市值<50亿的股票(参数可配置);
  2. 仓位分配:对剩余标的按signal_confidence * (1 / rank_in_signal)加权,例如3只股票置信度分别为0.82/0.75/0.68,则权重为0.82/1 : 0.75/2 : 0.68/3 ≈ 0.41 : 0.375 : 0.227;
  3. 风控截断:单票仓位上限15%,单日总开仓不超过可用资金的30%。

注意:执行Agent从不调用任何预测模型,所有计算基于信号字段和本地风控参数。它的输出是标准订单字典:{'order_list': [{'code': '600519.SH', 'action': 'buy', 'amount': 1200, 'price_limit': 185.2}, ...], 'total_risk_exposure': 0.28}。这份订单会被写入orders_pending.csv,等待模拟/实盘引擎读取。

2.3 复盘Agent:不参与实时决策,只做周度归因与策略衰减诊断

复盘Agent在每周日晚22:00自动触发,读取本周所有已成交订单、对应持仓盈亏、市场状态快照(波动率分位数、行业轮动强度指数),生成三份报告:

  • weekly_signal_effectiveness.csv:统计每类信号的成功率(盈利订单占比)、平均持有天数、最大回撤;
  • factor_contribution.xlsx:用SHAP值分解本周盈亏中各因子(如“北向增持强度”“小盘股溢价”)的贡献度;
  • strategy_decay_report.md:对比当前信号触发频率与过去12周均值,若breakout信号本周触发次数下降40%,则标记“趋势跟踪类策略可能进入低效期”。
    这个Agent的存在,让“周度复盘”不再是人工翻Excel,而是自动生成可执行的策略调整建议。

3. LGBM双模型设计:一个判状态,一个校准置信度,拒绝“预测即真理”

3.1 状态分类模型(StateClassifier):把市场切成4个可操作象限

我们不训练模型预测“明天涨1.5%”,而是定义四个市场状态:

状态ID名称判定逻辑(示例)
S1高波动+高流动性20日ATR > 历史90%分位 & 日均成交额 > 500亿
S2低波动+趋势初现ATR < 历史30%分位 & MACD柱状图连续3日扩大
S3高波动+流动性枯竭ATR > 90%分位 & 成交额 < 200亿
S4低波动+横盘震荡ATR < 30%分位 & BOLL带宽 < 5%

StateClassifier的训练目标是:给定当日特征(ATR、成交额、MACD、RSI、行业轮动强度等12维),输出四分类概率。关键技巧在于特征工程必须包含跨周期统计量:例如'atr_20d_ratio_to_1y_mean'(当前ATR除以一年均值),而非原始ATR值——这能让模型理解“当前波动是常态还是异动”。模型用lightgbm.LGBMClassifier,num_class=4,损失函数选multiclass,重点调参项:

params = { 'objective': 'multiclass', 'num_class': 4, 'learning_rate': 0.05, 'num_leaves': 31, 'min_data_in_leaf': 20, 'feature_fraction': 0.8, 'bagging_fraction': 0.9, 'bagging_freq': 5, 'lambda_l1': 0.1, 'lambda_l2': 0.1, 'verbose': -1 }

逻辑说明:min_data_in_leaf=20防止过拟合小样本状态(如S3状态全年仅出现12次);bagging_freq=5引入随机性,避免模型对特定极端行情过拟合;lambda_l1/l2双正则控制树结构复杂度。训练集必须覆盖至少3个完整牛熊周期(建议2019-2024年数据)。

3.2 置信度校准模型(ConfidenceCalibrator):让LGBM输出真正可信

StateClassifier输出的概率(如[0.1, 0.65, 0.2, 0.05])不能直接当置信度用——原始LGBM概率常呈“尖峰厚尾”,在低概率区间严重失真。我们训练第二个轻量级LGBM,输入是StateClassifier的4维输出概率+原始特征,输出是标量calibrated_confidence(0~1)。训练目标是让calibrated_confidence与实际胜率高度相关:

# 构造校准标签:以未来5日收益率>2%为正样本 y_calibrate = (future_returns > 0.02).astype(int) # 特征:state_probs + 原始12维特征 X_calibrate = np.hstack([state_probs, raw_features]) # 训练二分类模型 calibrator = lgb.LGBMClassifier( objective='binary', learning_rate=0.1, num_leaves=15, min_data_in_leaf=10 ) calibrator.fit(X_calibrate, y_calibrate)

参数说明:num_leaves=15限制模型复杂度,避免校准过拟合;min_data_in_leaf=10保证每个叶子节点有足够样本支撑概率估计。校准后,当模型输出calibrated_confidence=0.78,意味着历史上同类信号后续5日盈利超2%的概率真实值为76.3%~79.1%(用Bootstrap法验证)。


4. 从数据到信号:本地化部署全流程与关键参数配置

4.1 数据准备:三类数据源的清洗与对齐规范

系统依赖三类数据,必须按统一时间戳对齐:

  • 行情数据:日频OHLCV,要求open/high/low/close/volume字段齐全,缺失用前值填充(ffill),但需记录缺失标记列volume_is_estimated;
  • 资金流数据:北向资金日净流入(单位:亿元),来源可选聚源/东方财富,要求与行情日期严格对齐;
  • 行业数据:申万一级行业指数收盘价,用于计算行业轮动强度(abs(行业涨跌幅 - 全市场涨跌幅)的均值)。

所有数据存入data/raw/下按日期分区(如data/raw/20240621/),清洗脚本核心逻辑:

# clean_data.py import pandas as pd import numpy as np def align_and_clean(date_str): # 读取当日行情 df_price = pd.read_parquet(f'data/raw/{date_str}/price.parquet') # 读取资金流 df_fund = pd.read_csv(f'data/raw/{date_str}/fund_flow.csv') # 合并,按股票代码索引对齐 merged = df_price.join(df_fund.set_index('code'), on='code', rsuffix='_fund') # 填充缺失值并标记 merged['volume'] = merged['volume'].fillna(method='ffill') merged['volume_is_estimated'] = merged['volume'].isna().astype(int) # 保存清洗后数据 merged.to_parquet(f'data/cleaned/{date_str}.parquet') return merged if __name__ == '__main__': for date in get_trading_days('20240101', '20240621'): align_and_clean(date)

逻辑说明:join操作确保股票维度对齐;volume_is_estimated标记列后续会作为特征输入StateClassifier,让模型知道“当前成交量是否可靠”;get_trading_days函数需自行实现(推荐用akshare获取A股交易日历)。

4.2 信号生成:择时Agent的最小可运行命令

假设已清洗好2024年6月全部数据,运行择时Agent只需一条命令:

python agent_timing.py --start_date 20240617 --end_date 20240621 --model_path models/state_classifier.txt

agent_timing.py核心逻辑:

# agent_timing.py import lightgbm as lgb import pandas as pd from utils.feature_engineer import build_features def generate_signals(start_date, end_date, model_path): model = lgb.Booster(model_file=model_path) signals = [] for date in pd.date_range(start_date, end_date, freq='D'): if not is_trading_day(date): continue # 构建当日特征(含跨周期统计量) X = build_features(date) # 返回12维numpy数组 # 预测市场状态 state_probs = model.predict(X.reshape(1, -1))[0] # 用校准模型转换置信度 calibrator = lgb.Booster(model_file='models/confidence_calibrator.txt') calibrated_conf = calibrator.predict( np.hstack([state_probs, X]).reshape(1, -1) )[0] # 根据状态映射择时信号 signal_type = map_state_to_signal(np.argmax(state_probs)) if calibrated_conf > 0.7: # 置信度阈值 signals.append({ 'date': date.strftime('%Y%m%d'), 'signal_type': signal_type, 'confidence': float(calibrated_conf), 'valid_until': (date + pd.Timedelta(days=5)).strftime('%Y%m%d'), 'target_stocks': select_stocks_by_signal(signal_type, date) }) pd.DataFrame(signals).to_csv('output/timing_signals.csv', index=False) if __name__ == '__main__': import argparse parser = argparse.ArgumentParser() parser.add_argument('--start_date') parser.add_argument('--end_date') parser.add_argument('--model_path') args = parser.parse_args() generate_signals(args.start_date, args.end_date, args.model_path)

参数说明:--start_date/--end_date指定信号生成区间;--model_path指向训练好的StateClassifier模型;map_state_to_signal函数需按业务规则实现(如S1状态触发volatility_squeeze信号);select_stocks_by_signal根据信号类型筛选标的(breakout选近期放量突破年线个股)。


5. 避坑指南:血泪经验总结的5个高频翻车点与根治方案

5.1 现象:择时信号频繁触发但实盘亏损,回溯发现90%信号在S3状态(高波动+流动性枯竭)下发出

原因:StateClassifier在S3状态样本极少(2024年前5个月仅出现7次),模型将其误判为S1(高波动+高流动性),导致在流动性枯竭时仍按“高流动性”逻辑执行突破策略。
解决:在训练StateClassifier时,对S3状态样本做SMOTE过采样,并在损失函数中为S3类别设置3倍权重:class_weight={0:1, 1:1, 2:3, 3:1}。同时,在择时Agent中增加硬性熔断:当volume_is_estimated==1且atr_20d_ratio_to_1y_mean>1.5时,直接屏蔽所有信号。

5.2 现象:LGBM输出的置信度为0.85,但后续5日盈利概率仅52%

原因:未对StateClassifier输出做概率校准,原始LGBM概率在0.7~0.9区间存在系统性高估。
解决:必须部署ConfidenceCalibrator模型,且校准标签y_calibrate不能简单用“涨跌”,而要用多空双阈值:正样本定义为future_return > 0.02,负样本定义为future_return < -0.015,中间区域(-1.5%~2%)不参与训练,避免模型学习模糊地带。

5.3 现象:周度复盘报告中“信号成功率”指标忽高忽低,无法归因

原因:复盘Agent计算成功率时,未排除“信号有效期内停牌”或“信号发出后次日跌停无法买入”的订单,导致分母失真。
解决:在订单生成阶段(执行Agent)就标记order_status:'pending'(待执行)、'filled'(已成交)、'canceled'(因停牌/跌停取消)。复盘时只统计order_status=='filled'的订单,分母=已成交订单数,分子=已成交且盈利订单数。

5.4 现象:多Agent间信号传递延迟,导致执行Agent处理的是3天前的信号

原因:择时Agent输出CSV文件,执行Agent定时扫描文件修改时间,但Linux系统文件时间戳精度为秒级,且网络存储可能存在缓存。
解决:改用SQLite数据库作为Agent间通信媒介。择时Agent插入记录到signals表,执行Agent查询WHERE status='pending' AND valid_until >= ?,处理完后更新status='processed'。SQL语句自带事务保证,杜绝竞态。

5.5 现象:更换股票池(如从沪深300切换到中证1000)后,所有信号失效

原因:特征工程中使用了全局统计量(如“全市场成交额均值”),当股票池变更,这些统计量分布偏移,导致特征失真。
解决:所有跨周期统计量必须按股票池重新计算。在build_features函数中,传入当前股票池列表,atr_20d_ratio_to_1y_mean改为“该股票池内个股ATR均值的1年分位数”,而非全市场均值。


6. 周度复盘的进阶技巧:用SHAP值定位策略失效根源与动态调整信号阈值

6.1 为什么不用简单的“胜率排序”,而要用SHAP做归因?

胜率只能告诉你“breakout信号最近不好用”,但无法回答“是因为突破位置选错了?还是因为突破后量能跟不上?或是因为大盘处于S3状态却强行执行?”。SHAP值能分解每个特征对单笔交易盈亏的贡献:

# 在复盘Agent中计算SHAP import shap explainer = shap.TreeExplainer(state_classifier_model) shap_values = explainer.shap_values(X_weekly) # X_weekly: 本周所有成交订单的特征矩阵 # 按特征聚合SHAP绝对值均值 feature_importance = np.abs(shap_values).mean(axis=0).mean(axis=0) # shape=(12,)

输出示例:{'atr_20d_ratio_to_1y_mean': 0.42, 'rsi_14d': 0.31, 'industry_rotation_strength': 0.18, ...}。若发现atr_20d_ratio_to_1y_mean贡献度骤降(从0.42→0.15),说明近期信号有效性不再由波动率主导,需检查是否进入低波动周期。

6.2 动态阈值调整:让系统自己学会“什么时候该保守”

固定置信度阈值(如0.7)在不同市场状态下效果差异巨大。我们设计动态阈值机制:

当前市场状态推荐置信度阈值调整逻辑
S1(高波动+高流动性)0.65流动性充足,允许更高容错
S2(低波动+趋势初现)0.75趋势脆弱,需更高确定性
S3(高波动+流动性枯竭)0.0(熔断)直接禁用所有信号
S4(低波动+横盘震荡)0.70震荡市需耐心,避免频繁交易
该阈值表存于config/dynamic_thresholds.json,复盘Agent每周根据state_distribution_last_4w(过去4周各状态出现频率)微调阈值:若S2状态占比连续2周<10%,则将其阈值下调0.05。

6.3 复盘报告的落地动作清单:把分析转化为下周操作

一份合格的周度复盘报告,必须包含可执行动作,而非仅描述现象。我们在strategy_decay_report.md中强制包含:

  • ✅已确认问题:breakout信号在S2状态下胜率下降至41%(历史均值58%)
  • 🛠️根因定位:SHAP显示'rsi_14d'贡献度从0.31→0.12,说明突破时RSI超买程度不足
  • 📝下周动作:调整breakout信号触发条件:要求RSI(14) > 65(原为>55),并在信号字典中新增字段'rsi_requirement': 65
  • 📊验证指标:下周监控:满足新RSI条件的breakout信号数量、其中S2状态下信号占比、平均持有天数

这套机制让复盘不再是“总结过去”,而是“设计下周”。我坚持了18个月,最大的教训是:不要相信任何未经周度复盘验证的信号逻辑,哪怕它在回测中表现完美——市场状态永远在变,而你的系统必须比它变得更快。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/14 9:45:34

Agent技能封装:把散装API变成大模型真正会用的工具

1. 为什么Agent总在"差一步执行"的地方翻车 做AI Agent也有一段时间了&#xff0c;我发现一个特别普遍的现象&#xff1a;大家把精力都花在模型选型、Prompt调优、上下文管理这些"看得见"的地方&#xff0c;结果一到真实任务执行&#xff0c;Agent就露出马…

作者头像 李华
网站建设 2026/10/14 9:45:34

SpringBoot集成DBUnit:彻底解决单元测试数据库隔离与数据污染问题

如果你们团队的 SpringBoot 项目也在跑单元测试&#xff0c;我猜你迟早会遇到一个让人抓狂的场景&#xff1a;单个测试类单独执行&#xff0c;绿得发亮&#xff1b;整个测试套件一跑&#xff0c;红得发黑。更崩溃的是&#xff0c;报错信息通常还不是固定的&#xff0c;今天挂在…

作者头像 李华
网站建设 2026/10/14 9:45:12

AnyPS5跨平台串流与输入映射:架构解析与实操避坑指南

1. 项目缘起与核心定位AnyPS5 这个标题第一次出现在我视野里的时候&#xff0c;我下意识地把它拆成了两个部分&#xff1a;Any 和 PS5。Any 代表通用、跨平台、不受限&#xff1b;PS5 则指向一个具体的硬件平台。合在一起&#xff0c;直觉告诉我这是一个关于“让某个东西在 PS5…

作者头像 李华
网站建设 2026/10/14 9:44:46

Docker封装KubeSpray部署高可用K8s集群实战

简介&#xff1a;本资源是一套面向Linux运维工程师与云原生初学者的Kubernetes高可用集群部署实战包&#xff0c;聚焦国内网络环境下的离线部署痛点&#xff0c;基于Docker容器化方式集成Kubespray工具链&#xff0c;解决企业级K8S集群快速搭建、组件版本兼容与网络插件&#x…

作者头像 李华
网站建设 2026/10/14 9:43:52

Bootstrap树形表格插件实战:原理、选型与高频踩坑

简介&#xff1a;Bootstrap TreeGrid 插件基于 Bootstrap 框架&#xff0c;面向 Web 前端开发者&#xff0c;主要解决表格内层级数据的展示与交互问题&#xff0c;常用于权限管理、分类目录等需要嵌套结构的页面&#xff0c;也适用于后台管理系统或内容组织场景。压缩包共 7 个…

作者头像 李华
网站建设 2026/10/14 9:43:32

TV模型与哈里斯鹰算法在图像修复中的优化应用

1. 项目概述&#xff1a;当TV模型遇上哈里斯鹰算法 作为一名长期从事图像处理研究的工程师&#xff0c;我经常遇到各种图像修复的挑战。传统TV&#xff08;Total Variation&#xff09;模型虽然在某些场景下表现不错&#xff0c;但面对复杂图像时往往力不从心。直到我发现哈里斯…

作者头像 李华