当前位置: 首页 > news >正文

程序员量化交易实战 08:把原始 K 线清洗成可信行情

第 7 篇得到了一批干净的股票。接下来要处理行情。

行情数据最容易让人掉以轻心。看起来就是日期、开高低收、成交量、成交额几列,但供应商格式、单位、缺失值和异常行只要有一点没处理好,回测就会悄悄偏掉。

原始 K 线不能直接入库

同样是日线,不同来源可能长这样:

日期, 开盘, 最高, 最低, 收盘, 成交量 2026-06-15, 10.0, 10.8, 9.9, 10.5, 120

也可能长这样:

{"date": "20260615", "open": "10.0", "high": "10.8", "low": "9.9", "close": "10.5", "volume": "12000"}

成交量的单位还可能是“手”,也可能是“股”。如果不统一,成交量参与率、流动性过滤和回测成交约束都会错。

先定义清洗后的对象

第 8 章新增app/market_data.py。清洗后的行情对象叫CleanMarketBar

@dataclass(frozen=True) class CleanMarketBar: symbol: str trade_date: date open: float high: float low: float close: float volume: float amount: float source: str payload: dict[str, Any] = field(default_factory=dict)

它还不是 ORM 对象。它位于“供应商原始数据”和“数据库写入”之间,职责很清楚:把一批原始行变成统一口径。

日期和数字解析要宽容

供应商字段很少完全一致。日期可能是2026-06-16202606162026/06/16。数字里可能有逗号、百分号、空值和--

所以先写两个很小的解析函数:

def parse_trade_date(value: Any) -> date | None: if isinstance(value, date): return value text = str(value or "").strip().replace("/", "-") for fmt in ("%Y-%m-%d", "%Y%m%d"): try: return datetime.strptime(text[:10] if fmt == "%Y-%m-%d" else text, fmt).date() except ValueError: continue return None
def parse_number(value: Any) -> float | None: text = str(value).replace(",", "").replace("%", "").strip() if not text or text in {"-", "--", "nan", "None"}: return None return float(text)

工程上要注意:解析可以宽容,但后续校验要严格。

OHLC 必须自洽

第 8 章的clean_market_bars()会检查几类问题:

  • 没有交易日:missing_trade_date
  • 同一天重复:duplicate_trade_date
  • 收盘价缺失或小于等于 0:invalid_close
  • 最高价、最低价、开盘价、收盘价关系不成立:invalid_ohlc_range

这里最关键的是不要静默丢弃。函数返回两个结果:清洗后的bars和被拒绝的rejected

bars, rejected = clean_market_bars("600519.SH", rows, source="eastmoney", volume_unit="lot")

被拒绝行会带上原因和原始行:

{"reason": "invalid_ohlc_range", "row": row}

这对排查供应商字段变化很有用。真实系统里,rejected后续可以进入审计日志或数据任务结果。

成交量单位统一成股

A 股里常见的“手”是 100 股。如果供应商返回的是手,系统内部要统一转成股:

volume_multiplier = 100 if volume_unit == "lot" else 1 normalized_volume = round(volume * volume_multiplier, 4)

这一步会写入payload

payload={"raw": row, "volume_unit": "shares"}

也就是说,内部对象明确表达“成交量已经是股”。原始行仍然保留在 payload,后面发现问题可以追溯。

覆盖率报告

清洗完以后,还要有一个最小覆盖率摘要:

def coverage_report(bars: Iterable[CleanMarketBar]) -> dict[str, object]: rows = list(bars) dates = [row.trade_date for row in rows] symbols = sorted({row.symbol for row in rows}) return { "rows": len(rows), "symbols": len(symbols), "first_date": min(dates).isoformat() if dates else None, "latest_date": max(dates).isoformat() if dates else None, "sources": sorted({row.source for row in rows}), }

这是后续/api/data/quality的小版本。写策略之前,先知道自己有多少行、覆盖多少股票、最早和最新日期是什么。

可运行基础校验

行情清洗的校验要同时看到覆盖率和拒绝原因。当前统一用这条命令复现第 01-08 篇的基础能力:

uv run python -m scripts.chapter_examples foundation-check

本章对应输出如下:

截图里的覆盖率说明清洗后剩下 2 行、1 只股票、日期从2026-01-022026-01-05。被拒绝的那行保留invalid_ohlc_range,说明最高价低于开盘价时不会被悄悄写入后续回测。

本篇实战任务

拉取第 8 章代码:

git clone https://github.com/ax2/zi-quant-platform.git cd zi-quant-platform git checkout chapter-08 uv sync --extra dev uv run pytest

只跑行情清洗测试:

uv run pytest tests/test_market_data.py

第 8 章全量测试通过:159 passed,仍只有既有 FastAPI deprecation warning。

本章更新与代码仓库

本章更新内容:

  • 新增app/market_data.py
  • 实现交易日解析、数字解析、OHLC 校验、重复行拒绝、成交量单位统一和覆盖率摘要。
  • 新增tests/test_market_data.py,验证清洗结果和拒绝原因。

代码仓库:

https://github.com/ax2/zi-quant-platform

本章代码:

git clone https://github.com/ax2/zi-quant-platform.git cd zi-quant-platform git checkout chapter-08 uv sync --extra dev uv run pytest tests/test_market_data.py

本篇小结

行情数据的重点不是“下载到了”,而是“清洗后能不能解释”。

这一篇把日期解析、数字解析、OHLC 校验、重复行处理、成交量单位和覆盖率摘要做成纯函数。下一篇开始在这些干净 K 线上计算因子。

http://www.jsqmd.com/news/1062684/

相关文章:

  • 对话信息增益:量化公共讨论质量的核心算法与实践
  • 接口自动化测试进阶:从pytest框架到CI/CD集成的工程化实践
  • QEMU-KVM虚拟化架构深度解析与macOS虚拟机实战指南
  • 2026年徐州装修公司深度解析与排行:谁在引领品质家装新浪潮? - 博客万
  • 广州杨森药业:敏感肌修护源头厂家深耕院线敏感肌贴牌,专利技术领航问题肌修护新赛道 - 速递信息
  • OBS Studio终极教程:免费开源直播录制软件的完整使用指南
  • 2026在天津卖钻石,90% 的人都卖亏了 - 名奢变现站
  • 2026年 律师/公司法务律师/离婚纠纷/经济纠纷/企业顾问/民间借贷/债务追讨/劳动纠纷/合同纠纷/婚姻家庭律师推荐:专业维权与精准服务实力之选 - 企业推荐官【官方】
  • 2026 年 6 月合肥 LV 回收攻略,出手多赚两千 - 奢品小当家
  • 在哪里可以测标准情商量表?高信效度无广告测评入口汇总 - 秒达资讯
  • LS2088A安全引擎CCB寄存器配置实战:从硬件加速原理到嵌入式驱动开发
  • 本地大模型部署的三大真相:硬件、CUDA与API资源调度
  • Wand-Enhancer终极指南:免费解锁Wand专业版功能与远程控制体验
  • # 西安莲湖区家电维修清洗安装公司,西安土拨鼠家庭维修值得信赖 - 十大品牌榜
  • React SaaS主题定制完整方案:5个关键策略打造品牌化界面
  • 2026年国产调制式叶绿素荧光成像仪厂家推荐:杭州绿色思维智能科技实力解读 - 品牌推荐大师1
  • Codex模型原理与合法接入方式:从API调用到本地代码助手搭建
  • 3步搞定虚拟显示器驱动:从安装到彻底清理的完整指南
  • Go 语言 Agent 框架双雄:TRPC Agent Go vs Agentic Go Kit 深度对比
  • 金华渗漏维修靠谱机构盘点 2026、全屋防水堵漏正规企业实力排名一览 - 宅安选房屋修缮
  • 2026 四川区域变压器回收服务商综合实力梳理 适配厂房工地事业单位物资处置参考 - 深度智识库
  • GroupDPO:内存高效的组级直接偏好优化方法解析与实践
  • SuperSlicer完整指南:从零开始掌握专业3D打印切片技术
  • 艾尔登法环存档编辑器:PC与PlayStation跨平台存档修改完全指南
  • 跨设备游戏串流终极指南:用Sunshine打造你的私人云游戏服务器
  • CNAS认证的过滤性能检测机构有哪些 - 品牌排行榜
  • Kazumi追番神器:3分钟打造专属动漫资源库,跨平台免费追番指南
  • 2026清远甲醛检测品牌推荐:本地8家靠谱机构实测 - 环保除醛知识库
  • 2026年铜绞线:解读行业三大核心发展趋势 - 速递信息
  • 3步搞定:163MusicLyrics音乐歌词下载工具快速上手教程