重要提示本篇是「股票异动」实战落地篇全程围绕某券商20种异动数据展开——从SQL表设计、实体类定义到爬取代码、解析逻辑再到枚举封装所有代码均为可运行版本复制粘贴即可嵌入你的量化项目新手也能快速打通异动数据抓取全流程量化交易的核心的是「数据闭环」前47篇我们搞定了六大核心股票池的抓取与整合解决了「选标的」的问题第48篇讲解了股票异动的使用而今天第49篇我们聚焦「抓时机」的核心——某券商股票异动数据手把手教你从0到1实现「数据爬取→解析→存储」全落地。很多量化新手卡在异动数据这一步要么不知道怎么设计数据库存储要么爬取接口报错、解析逻辑混乱要么枚举与数据不匹配导致异动信号无法正常使用。今天我将把实战中验证过的完整方案分享给大家包含SQL表设计直接执行、Do实体类适配MyBatis-Plus、异动枚举20种全覆盖、爬取代码避反爬、解析代码适配接口返回格式全程无冗余所有代码可直接复用帮你彻底搞定某券商异动数据注全文无敏感词所有代码均基于Java主流量化框架开发适配某券商最新接口兼顾实战落地与可扩展性建议收藏转发避免后续找不到一、核心前提先搞懂3个关键组件异动落地的基础想要实现某券商异动数据的爬取与落地必须先搞定「存储→实体→枚举」三大组件这是数据闭环的基础——SQL表负责存储异动历史数据Do类映射数据库表枚举类统一管理20种异动类型三者联动才能确保数据抓取、解析、存储无缝衔接。以下所有代码均为实战可运行版本无需修改直接复制使用即可。1. SQL表设计stock_yd_history股票异动历史表核心作用存储所有某券商股票异动的历史数据包含异动日期、股票编码、名称、异动类型、价格、涨跌比例等核心字段适配后续策略回测、实时分析索引优化到位查询效率拉满。直接复制执行即可创建数据表MySQL版本适配所有量化项目CREATE TABLE stock_yd_history ( id int NOT NULL AUTO_INCREMENT COMMENT 主键编号, curr_date timestamp NULL DEFAULT NULL COMMENT 当时日期, code varchar(6) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL COMMENT 股票编码, name varchar(100) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL COMMENT 股票名称, type int DEFAULT NULL COMMENT 类型对应异动枚举code, type_message varchar(100) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci DEFAULT NULL COMMENT 类型信息中文对应异动枚举desc, price decimal(18,8) DEFAULT NULL COMMENT 当时价格, price_proportion decimal(10,4) DEFAULT NULL COMMENT 当时涨跌比例, close_price decimal(10,4) DEFAULT NULL COMMENT 收盘价格, close_price_proportion decimal(10,4) DEFAULT NULL COMMENT 收盘涨跌比例, PRIMARY KEY (id) USING BTREE, KEY idx_stock_yd_history_1 (curr_date) USING BTREE, -- 按日期查询适配策略回测 KEY idx_stock_yd_history_2 (code) USING BTREE -- 按股票编码查询适配标的联动 ) ENGINEInnoDB AUTO_INCREMENT1 DEFAULT CHARSETutf8mb3 ROW_FORMATDYNAMIC COMMENT股票异动历史表;关键说明① 字段类型适配量化数据需求price用decimal(18,8)避免价格精度丢失② 新增close_price和close_price_proportion用于对比异动时价格与收盘价判断异动后续走势③ 双索引优化兼顾日期查询回测和股票编码查询联动标的。2. Do实体类StockYdHistoryDo适配MyBatis-Plus核心作用映射stock_yd_history表适配MyBatis-Plus实现数据的CRUD操作无需手动编写SQL直接调用BaseMapper即可完成异动数据的存储与查询节省开发时间。Data EqualsAndHashCode(callSuper false) TableName(stock_yd_history) public class StockYdHistoryDo implements Serializable { /** * 主键编号 */ TableId(value id, type IdType.AUTO) private Integer id; /** * 当时日期异动发生时间 */ TableField(curr_date) private Date currDate; /** * 股票编码6位如600000 */ TableField(code) private String code; /** * 股票名称 */ TableField(name) private String name; /** * 异动类型对应YiDongType枚举的code */ TableField(type) private Integer type; /** * 异动类型中文描述对应YiDongType枚举的desc */ TableField(type_message) private String typeMessage; /** * 异动发生时的价格 */ TableField(price) private BigDecimal price; /** * 异动发生时的涨跌比例 */ TableField(price_proportion) private BigDecimal priceProportion; /** * 当日收盘价 */ TableField(close_price) private BigDecimal closePrice; /** * 当日收盘涨跌比例 */ TableField(close_price_proportion) private BigDecimal closePriceProportion; }✅ 使用说明直接复制到项目entity包下确保MyBatis-Plus依赖配置正确即可通过StockYdHistoryMapper调用insert、selectList等方法实现异动数据的存储与查询。3. 异动枚举YiDongType20种异动全覆盖核心作用统一管理某券商20种异动类型封装异动code和中文描述提供代码映射、信号判断等工具方法避免硬编码减少解析错误同时适配后续策略的信号筛选。枚举类包含「20种异动定义代码映射信号判断工具方法」实战性拉满/** * 股票异动类型枚举某券商20种异动全覆盖 * author 金亥跃江聊量化 * date 2022/6/5 7:47 **/ public enum YiDongType { HUO_JIAN(8201, 火箭发射), JIA_DIE(8204, 加速下跌), FAN_TAN(8202, 快速反弹), GAO_TIAO(8203, 高台跳水), BIG_BUY(8193, 大笔买入), BIG_SELL(8194, 大笔卖出), ZHANG_TING(4, 封涨停板), DIE_TING(8, 封跌停板), OPEN_DIE(32, 打开跌停板), OPEN_ZHANG(16, 打开涨停板), HAVE_BIG_BUY(64, 有大买盘), HAVE_BIG_SELL(128, 有大卖盘), JING_ZHANG(8207, 竞价上涨), JING_DIE(8208, 竞价下跌), HIGH_WU(8209, 高开5日线), LOW_WU(8210, 低开5日线), UP_QUE(8211, 向上缺口), DOWN_QUE(8212, 向下缺口), HIGH_60(8213, 60日新高), LOW_60(8214, 60日新低), ZHANG_60(8215, 60日大幅上涨), DIE_60(8216, 60日大幅下跌); private Integer code; // 异动代码某券商官方定义 private String desc; // 异动中文描述 /** * 根据异动代码获取枚举对象核心方法用于解析接口数据 */ public static YiDongType getByCode(Integer code) { for (YiDongType yiDongType : YiDongType.values()) { if (yiDongType.getCode().equals(code)) { return yiDongType; } } return null; } /** * 判断异动列表是否包含「看空信号」大笔卖出、高台跳水、有大卖盘 */ public static boolean containsKong(ListInteger ydTextList) { if (CollUtil.isEmpty(ydTextList)) { return false; } return ydTextList.contains(YiDongType.BIG_SELL.getCode()) || ydTextList.contains(YiDongType.GAO_TIAO.getCode()) || ydTextList.contains(YiDongType.HAVE_BIG_SELL.getCode()); } /** * 判断异动列表是否包含「看多信号」大笔买入、快速反弹、有大买盘 */ public static boolean containsDuo(ListInteger ydTextList) { if (CollUtil.isEmpty(ydTextList)) { return false; } return ydTextList.contains(YiDongType.BIG_BUY.getCode()) || ydTextList.contains(YiDongType.FAN_TAN.getCode()) || ydTextList.contains(YiDongType.HAVE_BIG_BUY.getCode()); } /** * 判断该异动是否需要发送提醒实战策略用 */ public boolean isSendMessage () { switch(this) { case HUO_JIAN: // 火箭发射 case ZHANG_TING: // 封涨停板 case OPEN_DIE: // 打开跌停板 case JING_ZHANG: // 竞价上涨 case BIG_BUY: // 大笔买入 return true; // 这些异动需发送提醒捕捉机会 default: return false; } } // 构造方法getter YiDongType(Integer code, String desc) { this.code code; this.desc desc; } public Integer getCode() { return code; } public String getDesc() { return desc; } }✅ 核心亮点① 全覆盖某券商20种异动code与官方接口完全一致避免解析错误② 提供3个实用工具方法直接用于策略的信号判断看多/看空和提醒逻辑③ 无需修改直接复制到项目enum包下即可使用。二、核心落地某券商异动数据爬取代码避反爬分页抓取搞定了基础组件接下来就是核心的「异动数据爬取」——基于某券商专属接口实现分页抓取、反爬优化、异常处理确保数据抓取稳定、不中断适配高频抓取需求复用前47篇的反爬逻辑避免IP封禁。以下是完整的爬取方法Service层实现直接复制到项目中注入相关依赖即可运行/** * 某券商股票异动数据爬取实现类 * 核心功能分页抓取某券商异动数据处理反爬异常兜底确保数据完整性 */ Service public class StockYiDongServiceImpl implements StockYiDongService { Autowired private StockInfoParser stockInfoParser; // 解析器下文会提供 Autowired private HttpClientConfig httpClientConfig; // HttpClient配置反爬用 Override public ListStrategyStockInfo findYiDong(String type) { try { boolean stopSearch false; // 终止抓取标识 String CB_POINT_INDEX yueshushu; // 回调函数固定避免硬编码 int page 0; // 分页索引从0开始 ListStrategyStockInfo allResultList new ArrayList(); // 存储所有异动数据 // 分页抓取直到获取不到数据为止 do { // 1. 拼接异动抓取接口动态拼接分页参数、类型参数 String url MessageFormat.format( http://push2ex.eastmoney.com/getAllStockChanges?cb{0}type{1} ut7eea3edcaed734bea9cbfc24409ed989pageindex{2}pagesize50dptwzchanges_, CB_POINT_INDEX, type, page ); try { // 2. 发送请求复用反爬配置避免IP封禁 String content HttpUtil.sendGet( httpClientConfig.proxyNoUseCloseableHttpClient(), url MyDateUtil.getTimezone() // 拼接时间戳增强反爬 ); // 3. 处理JSONP格式去掉回调函数包裹转换为纯JSON content content.substring(CB_POINT_INDEX.length() 1); // 去掉前缀cb( content content.substring(0, content.length() - 2); // 去掉后缀) // 4. 解析异动数据调用解析器下文提供 ListStrategyStockInfo tempList stockInfoParser.parseYiDong(content); // 5. 分页逻辑有数据则继续抓取无数据则终止 if (!CollUtil.isEmpty(tempList)) { allResultList.addAll(tempList); page; // 页码自增 } else { stopSearch true; // 无数据终止抓取 } } catch (Exception e) { log.error(获取异动数据findYiDong出错类型{}, type, e); throw e; // 抛出异常便于监控告警 } // 6. 反爬优化休眠50ms避免高频抓取触发反爬 ThreadUtil.safeSleep(50); } while (!stopSearch); return allResultList; } catch (Exception e) { log.error(异动数据抓取整体出错IP{}, ThreadLocalUtils.getIp(), e); return Collections.emptyList(); // 异常兜底返回空列表避免策略崩溃 } } }反爬重点① 固定回调函数CB_POINT_INDEX避免动态变化导致抓取失败② 拼接时间戳MyDateUtil.getTimezone()模拟真实请求③ 休眠50ms降低抓取频率④ 复用HttpClient代理配置可根据需求添加多IP代理进一步规避IP封禁。http://push2ex.eastmoney.com/getAllStockChanges?cbyueshushutype8201ut7eea3edcaed734bea9cbfc24409ed989pageindex1pagesize50dptwzchanges_返回的信息是:yueshushu({rc:0,rt:105,svr:183632170,lt:2,full:0,data:{tc:1290,allstock:[{tm:102449,c:301268,m:0,n:铭利达,t:8201,i:0.102862,24.66000,0.102862},{tm:102443,c:300891,m:0,n:惠云钛业,t:8201,i:0.062261,11.09000,0.062261},{tm:102440,c:600708,m:1,n:光明地产,t:8201,i:0.026829,4.21000,0.026829},{tm:102355,c:300565,m:0,n:科信技术,t:8201,i:0.075269,14.00000,0.075269},{tm:102344,c:920748,m:0,n:路桥信息,t:8201,i:0.105968,45.40000,0.105968},{tm:102324,c:603685,m:1,n:晨丰科技,t:8201,i:0.028512,29.58000,0.028512}]}});三、核心落地异动数据解析代码适配接口格式校验爬取到接口返回的JSONP数据后需要进行解析——提取股票编码、名称、异动类型、价格、涨跌比例等核心字段适配StrategyStockInfo实体类同时处理异常数据确保解析后的数据可直接用于存储和策略使用。以下是完整的解析方法Parser层实现与爬取代码联动直接复用/** * 股票异动数据解析器 * 核心功能解析某券商异动接口返回的JSON数据转换为StrategyStockInfo列表 */ Component public class StockInfoParser { /** * 解析异动数据核心方法 * param content 处理后的纯JSON内容已去掉JSONP回调包裹 * return 解析后的异动标的列表 */ public ListStrategyStockInfo parseYiDong(String content) { // 1. 解析JSON对象 JSONObject jsonObject JSONObject.parseObject(content); JSONObject data jsonObject.getJSONObject(data); // 2. 空值校验无data字段则返回空列表 if (ObjectUtils.isEmpty(data)) { return Collections.emptyList(); } // 3. 获取异动数据列表接口返回的allstock字段 JSONArray jsonArray data.getJSONArray(allstock); if (jsonArray.size() 0) { return Collections.emptyList(); } // 4. 解析每一条异动数据 ListStrategyStockInfo result new ArrayList(32); jsonArray.forEach(n - { String singleContent n.toString(); JSONObject singleJsonObject JSONObject.parseObject(singleContent); // 5. 封装到StrategyStockInfo实体类 StrategyStockInfo strategyStockInfo new StrategyStockInfo(); strategyStockInfo.setCode(singleJsonObject.getString(c)); // 股票编码 strategyStockInfo.setName(singleJsonObject.getString(n)); // 股票名称 strategyStockInfo.setType(singleJsonObject.getInteger(t)); // 异动类型code // 6. 关联异动枚举获取中文描述避免空指针 YiDongType yiDongType YiDongType.getByCode(strategyStockInfo.getType()); strategyStockInfo.setTypeMessage(Objects.requireNonNull(yiDongType).getDesc()); // 7. 处理异动时间格式HH:mm:ss String time singleJsonObject.getString(tm); if (time.length() 6) { time 0 time; // 补零避免格式错误如1234→01234→00:12:34 } strategyStockInfo.setCurrDate( time.substring(0, 2) : time.substring(2, 4) : time.substring(4) ); // 8. 解析价格、涨跌比例接口返回i字段按逗号分割 String[] ydInfoArr singleJsonObject.getString(i).split(Const.JOB_PARAM_SPLIT); // 9. 处理不同长度的i字段接口返回格式有2种兼容处理 if (ydInfoArr.length 2) { try { // 格式1[价格, 涨跌比例] String formattedNumber String.valueOf(Double.parseDouble(ydInfoArr[0])); // 价格格式化保留4位小数 strategyStockInfo.setPrice(BigDecimalUtil.toShowString4(new BigDecimal(formattedNumber))); // 涨跌比例转换乘以100保留4位小数 strategyStockInfo.setPriceProportion( BigDecimalUtil.toDouble(BigDecimalUtil.mul100B(new BigDecimal(ydInfoArr[1])), 4) ); result.add(strategyStockInfo); } catch (Exception e) { log.error(异动数据解析异常长度2数据{}, 价格{}, ydInfoArr, formattedNumber, e); } } else if (ydInfoArr.length 3) { try { // 格式2[其他, 价格, 涨跌比例] String formattedNumber String.valueOf(Double.parseDouble(ydInfoArr[1])); strategyStockInfo.setPrice(BigDecimalUtil.toShowString4(new BigDecimal(formattedNumber))); strategyStockInfo.setPriceProportion( BigDecimalUtil.toDouble(BigDecimalUtil.mul100B(new BigDecimal(ydInfoArr[2])), 4) ); result.add(strategyStockInfo); } catch (Exception e) { log.error(异动数据解析异常长度3数据{}, 价格{}, ydInfoArr, formattedNumber, e); } } // 其他长度不处理避免异常数据影响整体列表 }); return result; } }✅ 解析重点① 兼容接口返回的2种格式i字段长度2和3避免解析报错② 对异动时间进行补零处理确保格式统一HH:mm:ss③ 关联YiDongType枚举自动获取异动中文描述避免硬编码④ 异常捕获跳过错误数据确保解析列表正常返回。补充StrategyStockInfo实体类解析结果封装用于封装解析后的异动数据适配爬取、解析、策略调用全流程与Do类对应可根据需求转换为StockYdHistoryDo存储到数据库Data EqualsAndHashCode(of {code}) // 按股票编码去重 public class StrategyStockInfo implements Serializable { private static final long serialVersionUID 1L; private String currDate; // 异动时间HH:mm:ss private String fullCode; // 股票完整编码可扩展如sh600000 private String code; // 股票6位编码 private String name; // 股票名称 private String price; // 异动时价格字符串格式便于展示 private Integer type; // 异动类型code对应YiDongType private Integer topNum; // 预留字段如异动排名 private String typeMessage; // 异动类型中文描述 private Double priceProportion; // 异动时涨跌比例百分比保留4位小数 }四、实战联动异动数据存储策略使用闭环落地爬取、解析完成后需要将数据存储到数据库stock_yd_history表同时联动前47篇的六大股票池实现「标的筛选异动时机捕捉」的策略闭环以下是核心落地步骤可直接复用。1. 异动数据存储Service层补充将解析后的StrategyStockInfo列表转换为StockYdHistoryDo存储到数据库实现异动历史数据的持久化用于后续策略回测和分析Autowired private StockYdHistoryMapper stockYdHistoryMapper; // MyBatis-Plus Mapper /** * 存储异动数据到数据库 * param strategyStockList 解析后的异动列表 * param currDate 异动日期当日交易日 */ public void saveYiDongData(ListStrategyStockInfo strategyStockList, Date currDate) { if (CollUtil.isEmpty(strategyStockList)) { return; } // 转换为StockYdHistoryDo批量存储 ListStockYdHistoryDo ydHistoryDoList strategyStockList.stream().map(strategy - { StockYdHistoryDo ydHistoryDo new StockYdHistoryDo(); ydHistoryDo.setCurrDate(currDate); // 异动日期完整时间戳 ydHistoryDo.setCode(strategy.getCode()); ydHistoryDo.setName(strategy.getName()); ydHistoryDo.setType(strategy.getType()); ydHistoryDo.setTypeMessage(strategy.getTypeMessage()); // 价格转换String→BigDecimal ydHistoryDo.setPrice(new BigDecimal(strategy.getPrice())); // 涨跌比例转换Double→BigDecimal ydHistoryDo.setPriceProportion(BigDecimal.valueOf(strategy.getPriceProportion())); // 收盘价、收盘涨跌比例可后续补充关联行情数据 ydHistoryDo.setClosePrice(null); ydHistoryDo.setClosePriceProportion(null); return ydHistoryDo; }).collect(Collectors.toList()); // 批量插入提升效率 stockYdHistoryMapper.insertBatchSomeColumn(ydHistoryDoList); }2. 与六大股票池联动策略核心结合前47篇的六大股票池ZT/DT/YES_ZT/QS/CX/ZB用异动数据捕捉最佳时机示例如下高胜率策略片段/** * 涨停股火箭发射大笔买入 组合策略短线高胜率 * param ztList 涨停股池列表复用前47篇方法 * param ydList 异动数据列表 * return 筛选后的优质标的 */ public ListStrategyStockInfo ztHuoJianStrategy(ListStockPoolInfo ztList, ListStrategyStockInfo ydList) { if (CollUtil.isEmpty(ztList) || CollUtil.isEmpty(ydList)) { return Collections.emptyList(); } // 1. 提取涨停股编码用于联动异动数据 SetString ztCodeSet ztList.stream().map(StockPoolInfo::getCode).collect(Collectors.toSet()); // 2. 筛选涨停股 火箭发射 或 大笔买入 return ydList.stream() .filter(yd - ztCodeSet.contains(yd.getCode())) // 属于涨停股池 .filter(yd - { // 异动类型为火箭发射8201或大笔买入8193 Integer type yd.getType(); return YiDongType.HUO_JIAN.getCode().equals(type) || YiDongType.BIG_BUY.getCode().equals(type); }) .filter(yd - yd.getPriceProportion() 3.0) // 涨跌比例3%确保异动力度 .collect(Collectors.toList()); }五、异动爬取解析10大避坑指南⚠️实战必看少走弯路结合实战经验整理了异动数据爬取、解析、存储全流程的10大高频坑每一个都附解决方案帮你避免数据抓取失败、解析报错、存储异常等问题确保异动数据正常落地。爬取层面4大坑1. 坑1回调函数cb硬编码接口返回格式错误✅ 解决固定cb为「yueshushu」与爬取代码一致避免动态变化导致JSONP解析失败2. 坑2未加反爬休眠高频抓取导致IP封禁✅ 解决严格保留ThreadUtil.safeSleep(50)非交易时间可调整为100ms必要时添加多IP代理3. 坑3未拼接时间戳被接口识别为机器人请求✅ 解决接口URL必须拼接MyDateUtil.getTimezone()模拟真实用户请求提升爬取成功率4. 坑4未处理分页只抓取第一页数据导致数据不全✅ 解决沿用分页逻辑page自增直到获取不到数据为止确保所有异动数据都能抓取到。解析层面3大坑5. 坑5未处理i字段长度差异导致解析报错✅ 解决严格兼容i字段长度2和3的两种格式添加try-catch跳过错误数据6. 坑6异动时间未补零格式混乱如1234→1:2:34✅ 解决对time字段进行补零处理确保格式统一为HH:mm:ss便于后续时间判断7. 坑7未做枚举空值校验导致空指针异常✅ 解决使用Objects.requireNonNull(yiDongType)或提前判断yiDongType是否为null避免空指针。存储使用层面3大坑8. 坑8价格格式转换错误导致存储失败✅ 解决StrategyStockInfo的price是String类型存储时转换为BigDecimal避免格式错误9. 坑9未批量存储单条插入效率低✅ 解决使用MyBatis-Plus的insertBatchSomeColumn方法批量插入异动数据提升存储效率10. 坑10异动与股票池未联动单独使用异动信号✅ 解决异动信号必须结合六大股票池使用比如涨停股火箭发射避免单一异动信号误判假机会。六、福利领取异动全流程完整代码包免费送为了帮大家快速落地异动数据的爬取、解析、存储与策略联动整理了本次第48篇的完整可运行代码包包含所有代码片段直接导入项目即可运行无需二次开发1. ① SQL表脚本stock_yd_history直接执行2. ② 所有实体类/枚举类StockYdHistoryDo、StrategyStockInfo、YiDongType3. ③ 爬取解析完整代码ServiceParser含反爬优化4. ④ 数据存储股票池联动策略代码直接嵌入量化项目5. ⑤ 避坑手册图文版含常见问题解决方案。私信回复【异动爬取】即可免费领取所有代码均注释完善适配最新某券商接口兼容前47篇的量化框架新手也能快速上手打通异动数据全流程。下期预告✨第49篇我们搞定了某券商异动数据的全落地实现了「六大股票池异动信号」的联动基础下一篇第50篇我们将聚焦 爬取股票资金数据, 敬请期待结尾互动你在爬取某券商异动数据时有没有踩过IP封禁、解析报错的坑需要我提前补充「多IP代理配置代码」帮你彻底规避反爬风险吗欢迎在评论区留言讨论#金亥跃江聊量化 #股票量化 #金亥跃江 #Java量化如果觉得这篇文章对你有帮助别忘了点赞在看转发让更多量化开发者少走弯路一起完善自己的量化交易体系 关注我持续解锁Java量化实战干货