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

资讯详情

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

全量A股实时行情快照采集实战:从选型到落库

全量A股实时行情快照采集实战:从选型到落库 做了一段时间个股研究之后我遇到一个特别别扭的问题手头的研究对象永远是那么几十只自选股所有复盘和策略验证都建立在一个很小且带有强烈主观偏好的样本上。市场真正发生了什么资金在往哪些板块走哪些票已经出现了异动我完全没有概念。于是我想搞一套全量股票实时交易数据的采集系统把全市场几千只股票的快照按固定节奏全部抓下来存进数据库后面无论是做复盘、做预警还是套策略回测都有了原料。这篇文章记录了这套系统从选型到上线跑起来的整个过程也把我在实际操作中踩到的坑一并列出来给想做同样事情的朋友一个能直接参考的路线图。文章内容围绕一个主题全量股票实时交易数据的获取。适用对象是个人量化爱好者、数据分析师以及任何不想再被自选股限制视野的投资者。我不讨论怎么选股只讨论一个基础问题——如何稳定、低成本地拿到全市场行情快照。1. 为什么要做一套全量行情快照来自复盘的痛点1.1 只看自选股看不到市场全貌复盘的时候大家通常打开行情软件翻一翻自选股看看大盘指数然后开始找自己关心的个股新闻。这个流程的问题在于自选股本身就是一种选择偏差——你只会关注自己已经认可的标的而市场的异动、风格的切换、资金的腾挪往往发生在你根本不看的那些股票里。我举个例子。某天我在自选股里发现白酒板块集体走强以为当天市场主线就是消费打开全市场涨幅榜才发现真正暴涨的板块其实是某类小市值题材股白酒连涨幅中位数都没排上。这种认知偏差只有当你把全市场几千只股票的实时数据放在一起看的时候才能纠正。所以全量快照的第一个价值是让你拥有一个不带偏见的观察视角。涨跌分布、热点扩散、连板高度、成交额异动这些事情全部建立在全量数据之上。只盯自选股等于带着滤镜看市场。1.2 全量场景要面对的三个难点单只股票的数据获取很简单随便一个免费接口都能拿到实时报价。但换到“全量”这个词之后问题就不一样了数量维度沪深京三个市场合计有5000只左右的股票不是几十只而是一次性要覆盖几千个代码。频率维度快照数据需要按固定节奏重复抓取白天交易时段内每5秒到10秒一轮持续数小时。这不是拉一次就结束的任务而是一个长时间运行的采集服务。稳定性维度接口可能超时、返回空数据、字段错位数据库可能连接数耗尽脚本可能半夜崩溃。任何一个环节出问题都会造成数据断档。我一开始天真地以为写个for循环遍历所有股票代码一只一只取数据就行。实际算一下就知道5000只股票每只请求一次如果每只耗时0.3秒一轮就需要25分钟。这个频率连做日内的分钟级监控都不够更不用说实时了。这也是很多人做全量行情采集时第一个被劝退的地方。正确思路不是逐只取数而是利用行情源的批量快照接口一次请求拿一页分几次把全市场拿完。下文讲的方案就是围绕这个思路展开的。2. 数据源选型免费实时接口的可行性分析2.1 常见数据源横向对比在动手写代码之前我花了一天时间调研可用的实时行情数据源。市面上的选择不少但真正适合个人全量采集的并不多我筛完之后列了这样一张表数据源费用实时性全量覆盖使用体验AkShare免费约3秒级快照支持一次返回全市场接口简单文档更新快Tushare Pro免费积分制高权限需积分积分低时延迟较高需要较高积分字段规范但门槛偏高Baostock免费日线为主实时较弱历史数据全面适合回测不适合实时券商Level-2行情收费逐笔成交全量成本高个人接入麻烦行情软件导出免费手动仅当前页面无法自动化弃用对比之后结论很清晰如果目标是免费拿到全市场A股的实时快照AkShare是目前最省事的路径。这个库本身不生产数据它只是把公开网页接口打包成了好用的Python函数。真正干活的是背后的行情服务所以速度和稳定性能满足日常研究需要。2.2 为什么我最后选择了东方财富通道AkShare里实时全市场快照的函数是stock_zh_a_spot_em背后用的是东方财富的行情接口。选择它有几个实际考量。第一请求成本低。它支持分页批量返回一页最多可以取数百条记录不分页也能一次拉完整市场。这意味着我可以用很低的请求频率拿到全量数据降低被封的风险。第二字段丰富。一次返回的字段不止是价格和涨跌幅还包括量比、换手率、市盈率、市净率、总市值、流通市值等这些字段在做板块轮动分析和风格因子研究时非常有用省去了我再去找财务数据拼接的麻烦。第三社区活跃。AkShare的更新频率很快即便接口结构发生变化通常几天内就会有修复版本。这对一个需要长期运行的数据采集系统来说很重要。我当时也考虑过自己直接写requests请求东方财富的接口不走AkShare。好处是少一层依赖坏处是需要自己维护加密参数和接口变化。后来我选择了折中方案先用AkShare快速跑通流程后续如果接口频繁变动再封装自己的请求函数。实际跑下来AkShare的稳定性足够这个折中方案完全够用。3. 快照数据的字段体系拿到数据后先看懂这些3.1 一条快照记录包含什么stock_zh_a_spot_em返回的数据结构是一个DataFrame每一行对应一只股票每一列是一个行情字段。核心字段大致如下代码、名称股票的唯一标识和中文简称。最新价、涨跌额、涨跌幅当前成交价格和相对于昨日收盘的变化。成交量、成交额当日累计的成交手数和成交金额。今开、最高、最低、昨收当日价格区间和基准价。振幅、量比、换手率衡量波动和活跃度的指标。市盈率、市净率估值类指标。总市值、流通市值市值规模用于风格分类。涨速、5分钟涨跌、60日涨跌幅、年初至今涨跌幅辅助判断短期动能和中期趋势。这些字段不是简单的“看一下”而已它们是后续所有分析和筛选的原料。比如我常做的涨幅中位数统计用pct_chg这一列就能算出来板块热度分析可以把amount按行业分类汇总风格切换研究则依赖total_mv排序后的分组统计。很多人把精力花在选股策略上却忽略了底层数据的字段质量。如果拿到的数据本身有误后面所有分析都是白做。所以我专门花时间把每个字段的含义、单位、可能的异常值都过了一遍这一点在后面校验部分还会细说。3.2 字段里的单位与空值陷阱我以为字段名看明白了就能直接用结果第一批数据入库后做统计时发现结果明显不对。排查了一圈问题出在单位上。成交量单位是“手”1手等于100股。成交额单位是“元”不是“万元”。总市值和流通市值单位是“元”不是“亿元”。从接口拿到的原始值都是整数形式比如某只股票总市值显示为28500000000这实际上是285亿元。如果直接把这个数当亿元用做出来的排行榜会完全失真。另一个容易踩的坑是空值。新股上市首日可能没有市盈率停牌股票没有最新价某些长期横盘的股票量比可能为0。用pandas读进来后这些位置会变成NaN或者None如果不做处理就写入数据库会导致字段错位或者类型转换报错。我的处理方式是在入库前统一做一次清洗df df.fillna({ pe_ttm: 0, volume: 0, amount: 0, turnover_rate: 0, })但这里有个细节不能盲目把所有NaN都填成0。比如price字段如果为NaN说明这只股票可能停牌被我标记为异常记录单独处理而不是直接填0否则后续做价格筛选时会把它当成一只超跌股挑出来。填0还是填Null取决于这个字段后续怎么用。还有一个容易忽略的字段代码。A股市场有60开头的沪市主板、00开头的深市主板和中小板、30开头的创业板、68开头的科创板、8和4开头的北交所股票。如果只做沪深主板研究代码前缀是天然的过滤条件不需要额外维护股票池。4. 全市场采集的架构设计与代码实现4.1 大体思路分页并发固定节奏核心思路很简单用分页参数把全市场5000多只股票切分成若干页每页几十到几百条然后用有限的并发去拉取这些页面最后把所有页面合并成一个完整的快照集。这里有两个需要权衡的地方。一个是单次请求的页大小。页大小设得越大总请求次数越少但单次响应时间可能变长首次拿到数据的时间变慢。我实测下来每页200条对免费接口来说是一个比较稳定的值。全市场5000只股票差不多25页就能拿完。另一个是并发线程数。开太多的线程可以缩短一轮采集的总耗时但会显著增加被封风险。我最初用20个线程并发跑了半小时就出现请求被拒的情况。后来降到了6个并发配合每页请求之间0.3到0.5秒的间隔连跑几周都没再触发风控。4.2 交易时段判断与调度策略全量快照不是全天都要高频采集。A股的交易时段是周一至周五的9:15到11:30、13:00到15:00。其中9:15到9:25是集合竞价阶段数据变化很剧烈9:30到11:30和13:00到15:00是连续竞价阶段11:30到13:00午间休市其余时间休市。如果不管时段一直抓不仅浪费资源还会污染数据。晚上拿到的快照其实仍然是当天最后一笔数据长时间下来会积累大量重复记录。所以在采集调度里必须加入交易时段判断def is_trading_time(dt): 判断某个时间点是否处于A股交易时段 if dt.weekday() 5: return False hm dt.hour * 60 dt.minute morning 9 * 60 15 hm 11 * 60 30 afternoon 13 * 60 hm 15 * 60 return morning or afternoon我采用的策略是交易时段内每5秒触发一轮全量采集午休时段不采集晚间做一次收盘数据的全量快照用于归档。5秒这个间隔不是拍脑袋定的而是综合了接口频率限制和日内分析所需的最低粒度。如果需要更高的频率Level-2逐笔行情才是正确选择但那需要付费数据源成本完全不同。4.3 核心代码结构整个采集流程可以拆成三层单页数据获取、全量数据汇总、定时调度写入。以下是我实际运行的简化版本我加上了必要的错误处理。第一层单页获取。这里用requests直接请求东方财富的批量行情接口没有走AkShare主要是为了控制分页参数import requests import pandas as pd import time HEADERS { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64), Referer: https://quote.eastmoney.com/, } def fetch_spot_page(page: int, page_size: int 200): url https://82.push2.eastmoney.com/api/qt/clist/get params { pn: page, pz: page_size, po: 1, np: 1, fltt: 2, invt: 2, fid: f3, fs: m:0t:6,m:0t:80,m:1t:2,m:1t:23, fields: f2,f3,f4,f5,f6,f7,f8,f9,f10,f12,f14,f15,f16,f17,f18,f20,f21,f23,f24,f25, } resp requests.get(url, paramsparams, headersHEADERS, timeout10) data resp.json().get(data) or {} total data.get(total, 0) diff data.get(diff, []) if not diff: return pd.DataFrame(), total df pd.DataFrame(diff) return df, total其实用pandas.read_json也能处理但直接请求能拿到total这个总数方便控制分页循环。而且字段映射关系是自己维护的不会因为库版本升级而突然变动。第二层合并全量快照。拿到首页后解析出总数算出总页数然后并发拉取剩余所有页from concurrent.futures import ThreadPoolExecutor, as_completed def fetch_all_spot(): first_df, total fetch_spot_page(1, 200) if first_df.empty or total 0: raise RuntimeError(empty response from spot api) page_size 200 total_pages (total page_size - 1) // page_size frames [first_df] with ThreadPoolExecutor(max_workers6) as executor: future_map { executor.submit(fetch_spot_page, page, page_size): page for page in range(2, total_pages 1) } for future in as_completed(future_map): df, _ future.result() if not df.empty: frames.append(df) time.sleep(0.3) return pd.concat(frames, ignore_indexTrue)这个函数返回的DataFrame就是某一时刻的全市场快照。我在实际工程里还给每行加了一个统一的快照时间戳字段用于区分是哪一轮抓到的数据。第三层调度。这里用APScheduler的BlockingScheduler固定间隔5秒触发一次from apscheduler.schedulers.blocking import BlockingScheduler import datetime def snapshot_job(): ts datetime.datetime.now() if not is_trading_time(ts): return try: df fetch_all_spot() save_snapshot(df, ts) except Exception as exc: log_error(ts, exc) scheduler BlockingScheduler(timezoneAsia/Shanghai) scheduler.add_job(snapshot_job, interval, seconds5, next_run_timedatetime.datetime.now()) scheduler.start()这里的save_snapshot是后面要讲的落库函数。把这套脚本挂在服务器上就能在交易时段不断累积快照数据。5. 数据落库为高频写入而设计的存储方案5.1 MySQL方案字段与索引设计采集的数据最终要落到数据库里才能做历史分析和后续的应用开发。实时快照数据有“高频写入、按时间聚合、单行数据不太大”的特点我用MySQL来做第一版的存储完全够用。表结构设计的关键点在于把快照时间和股票代码作为检索维度。我用的建表语句大致如下CREATE TABLE stock_spot_snapshot ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, ts_code VARCHAR(10) NOT NULL COMMENT 股票代码, name VARCHAR(20) COMMENT 股票名称, price DECIMAL(10,3) COMMENT 最新价, pct_chg DECIMAL(10,3) COMMENT 涨跌幅, change_amt DECIMAL(10,3) COMMENT 涨跌额, volume BIGINT COMMENT 成交量(手), amount BIGINT COMMENT 成交额(元), high DECIMAL(10,3) COMMENT 最高, low DECIMAL(10,3) COMMENT 最低, open DECIMAL(10,3) COMMENT 今开, pre_close DECIMAL(10,3) COMMENT 昨收, turnover_rate DECIMAL(10,4) COMMENT 换手率, pe_ttm DECIMAL(12,4) COMMENT 市盈率TTM, pb DECIMAL(10,4) COMMENT 市净率, total_mv DECIMAL(20,2) COMMENT 总市值, circ_mv DECIMAL(20,2) COMMENT 流通市值, snap_time DATETIME NOT NULL COMMENT 快照时间, KEY idx_ts_time (ts_code, snap_time), KEY idx_snap_time (snap_time) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT全市场股票实时快照;注意这里我没有用UNIQUE KEY约束“同一个股票同一时间只能有一条记录”因为涉及到并发写的顺序和数据重试强制唯一约束反而容易造成插入失败。我在应用层保证同一轮快照的snap_time统一查询时按snap_time分组即可。索引方面(ts_code, snap_time)的组合索引覆盖了“查某只股票的一段时间走势”的场景snap_time单列索引覆盖了“按时间点截取全市场快照”的场景。两个索引对数据量在百万级以内的表完全够用不需要额外的分区策略。5.2 写入性能优化与批量插入如果每只股票一条INSERT语句一轮5000条就需要5000次交互每秒都要执行一轮数据库压力会非常大。正确做法是使用executemany批量写入。我用的是pymysqlimport pymysql def save_snapshot(df, snap_time): conn pymysql.connect( host127.0.0.1, userquant, password******, databasestock, charsetutf8mb4, ) sql INSERT INTO stock_spot_snapshot (ts_code, name, price, pct_chg, change_amt, volume, amount, high, low, open, pre_close, turnover_rate, pe_ttm, pb, total_mv, circ_mv, snap_time) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s) rows [ ( row.ts_code, row.name, row.price, row.pct_chg, row.change_amt, row.volume, row.amount, row.high, row.low, row.open, row.pre_close, row.turnover_rate, row.pe_ttm, row.pb, row.total_mv, row.circ_mv, snap_time, ) for row in df.itertuples(indexFalse) ] try: with conn.cursor() as cursor: cursor.executemany(sql, rows) conn.commit() finally: conn.close()实际测试下来用executemany一次性写入5000行耗时在300毫秒左右。放在5秒一个采集周期的任务里开销完全可以接受。真正拖慢写入速度的往往是连接复用问题。我最初在循环里每次新建连接、写完关闭这个模式没有错但要注意连接必须放在finally里关闭否则进程长时间运行后数据库会积压大量连接最后连接数满导致写入失败。如果后续数据量进一步膨胀比如跑了几个月之后表里积累了上亿条记录MySQL的聚合查询会开始变慢。这时有两个升级路径一是把快照表做成按天分区的表只保留近30天的高频快照更早的数据导出到冷存储二是迁移到ClickHouse这类列式数据库按时间和股票代码排序聚合性能会提升一个数量级。我目前还在MySQL阶段因为个人研究场景下数百万行的数据量配合合适的索引查询延时仍在可接受范围内。如果你确定要长期高频采集建议直接上ClickHouse省得后面迁移数据。5.3 数据量变大之后的升级路径说到数据规模不妨做个粗略估算。5000只股票每5秒一轮每天交易4小时快照记录数大概是5000 × (4 × 3600 / 5) ≈ 1440万这个数字意味着跑一个月就有4亿条记录。虽然看起来吓人但实际存储量没有想象中那么大。每条记录约200字节加上索引开销一个月的原始快照大约在10GB到20GB之间。普通服务器硬盘完全扛得住真正的瓶颈在查询效率不在存储容量。这也是我反复调整索引的原因。否则全表扫描一个数亿行的表任何查询都是灾难。6. 让采集进程稳定运行重试、防封与监控6.1 接口频率限制和防封经验免费接口最怕的就是被封。我的实际经验是东方财富的这个批量接口对频率并不算苛刻但仍然有明显的风控阈值。我早期测试时并发开20、间隔压到0.1秒大约半小时后开始出现偶发的空响应或者{code: -1}之类的错误返回。后面我总结了一套稳妥的频率策略并发数控制在6以内。每页请求之间至少间隔0.3秒。单轮全量采集失败后不立即重试而是等下一轮调度自然触发。连续3轮都失败的异常情况发送告警通知。按这个节奏一轮全量采集的耗时大概在5到10秒之间和5秒调度周期刚好匹配。如果抓取耗时超过周期调度器会跳过重叠的任务避免任务堆积。为了让请求看起来更像正常浏览器行为我在每次请求里都带上完整的User-Agent和Referer。这个做法不是为了绕过什么限制而是单纯为了让请求头完整减少被风控误判的可能。6.2 异常处理与进程守护采集脚本挂在服务器上最怕的是半夜崩溃而没有任何感知。我做了两层防护。第一层代码内的容错和日志。每个环节都用try-except包起来异常信息写入日志文件import logging logging.basicConfig( filename/var/log/stock_spot.log, levellogging.INFO, format%(asctime)s %(levelname)s %(message)s, ) def snapshot_job(): ts datetime.datetime.now() if not is_trading_time(ts): return try: df fetch_all_spot() save_snapshot(df, ts) logging.info(fsnapshot ok: {ts} {len(df)} rows) except Exception as exc: logging.error(fsnapshot failed: {ts} {exc})这里有个容易被忽略的点日志里要记录每一轮抓到的数据条数。条数突然从5000掉到2000说明分页逻辑出了问题或者接口返回异常仅凭日志级别就能快速定位。第二层进程守护。我用systemd管理采集脚本配置里加上自动重启规则脚本异常退出后能自动拉起。如果你的服务器上没有systemd用supervisord效果也一样。关键是让进程“死不了”。[Unit] DescriptionStock Spot Collector Afternetwork.target [Service] ExecStart/usr/bin/python3 /opt/stock_spot/collector.py Restartalways RestartSec10 [Install] WantedBymulti-user.target7. 数据校验怎么证明我抓的数据是对的7.1 条数与覆盖度检查数据采集系统跑起来之后不能只看日志里一片“ok”就放心。行情源偶尔会抽风接口可能返回不全网络抖动可能导致部分页面丢失。所以我每天收盘后都会跑一遍校验任务从几个维度验证当天数据的完整性。第一项是条数校验。正常交易日全市场A股快照应该稳定在5000只左右。如果收盘归档的条数与这个基准相差超过2%就说明分页漏拉或者接口返回异常。为了核对准确我维护了一个股票列表快照表每周从交易所网站同步一次全部上市股票代码然后对比快照表里出现的代码是否覆盖了这个列表。刚上线的几天我发现每天的条数都有轻微波动。查了一下才知道新股上市和退市整理期的股票会让总数小幅变化单纯的条数阈值判断会有误差。后来我改成“覆盖度”判断只检查上市股票列表里的代码是否都出现在快照中不纠结具体总数。覆盖度的计算方法很简单用上市列表的代码集合减去快照的代码集合差值就是缺失的股票。每天跑一次把缺失代码写进报告def check_coverage(snapshot_df, listed_df): snapshot_codes set(snapshot_df[ts_code]) listed_codes set(listed_df[ts_code]) missing listed_codes - snapshot_codes return missing7.2 字段逻辑一致性检查条数对不代表数据对。还要检查关键字段之间的逻辑一致性。最核心的是涨跌幅与价格的关系。涨跌幅应该等于最新价相对于昨收的百分比变化也就是pct_chg (price / pre_close - 1) * 100。如果某只股票算出来的理论涨跌幅和接口返回的涨跌幅偏差超过0.1个百分点这条数据基本可以判定有问题我会标记出来人工复核。另一个检查是价格区间合理性。每只股票都有涨跌停限制主板的涨停价是昨收的110%跌停价是昨收的90%创业板和科创板是120%和80%。如果一只创业板股票的pct_chg超过了20%那肯定不是正常交易数据需要立刻排查是接口问题还是数据源本身的问题。我还会拿全市场涨幅分布和指数做交叉验证。比如上证指数涨了1%但全市场涨幅中位数却是-2%这种背离虽然不一定是数据错误但至少值得多看一眼。很多时候行情软件的指数涨幅和全市场个股涨幅中位数确实会背离所以这个指标只作为辅助信号不单独触发告警。数据校验这件事很容易被人忽略但它恰恰是全量数据系统里最体现工程素养的部分。数据错了后面所有分析都是建在沙滩上。我宁可每天多花5分钟跑校验也不想等策略跑完回测才发现底层数据有洞。8. 踩坑实录连续运行几周的教训8.1 最坑的五个问题这套系统从写第一版到稳定运行中间踩了不少坑。我把最典型的几个列出来给后来者省点时间。坑一Python依赖版本不匹配。AkShare依赖的pandas版本如果太旧会在内部解析JSON时报AttributeError。这不是代码逻辑问题纯粹是环境问题。解决方法是把pandas升级到当前稳定版并且用虚拟环境管理依赖避免和系统其他项目的依赖打架。坑二数据快照时间不统一。早期我在每行记录里单独生成一个时间戳结果发现同一轮采集的不同股票时间戳可能相差好几秒。做日内分析时不同股票的“同一时刻”其实不是同一个时刻。修正方法是只在循环开始时生成一个时间戳整轮所有行共用这一个时间。坑三午间休市数据污染。有几天我发现数据库里11:30到13:00之间也有大量快照记录数据一模一样。排查后发现是我的交易时段判断写错了边界条件午休判定的区间开闭有问题。修正之后非交易时段的调度直接return不再产生新数据。坑四MySQL连接数耗尽。连续运行三天后数据库报Too many connections。原因是某次网络抖动导致脚本进程内的连接没有释放而我的连接管理代码恰好没有走finally关闭。把小概率异常变成必然故障的往往是资源清理代码写得不到位。坑五接口字段对应关系变化。某个周一早上我发现入库的turnover_rate列全是0字段排查后确认是接口字段的f8返回值从百分比变为了小数。这类变化很难提前预防只能靠每日校验任务尽早发现。8.2 我的处理办法上面这些坑的处理方式总结下来就是三条原则数据清洗前置、资源释放兜底、校验每日必跑。数据清洗前置是指在拿到接口原始数据之后不如库之前就完成字段重命名、单位统一、空值填充、类型转换。不要指望在SQL查询时再处理到了那个阶段脏数据已经在表里生了根回头清洗成本极高。资源释放兜底指所有连接、会话、文件句柄都必须放在try-finally或with语句里管理。这不是代码规范问题而是长期运行服务的生存问题。校验每日必跑则是我坚持到现在的一个习惯。哪怕数据已经连续一周完全正常我每天收盘后还是会过一遍校验脚本顺手扫一眼日志里的异常行。这个习惯让我在接口字段变更后的一个小时内就发现并修复了问题避免了整周数据全部作废的后果。另外一个很有用的经验是在设计表结构时给每行数据留下一个可追溯的标记。我的快照表没有加额外的采集来源字段但如果以后接入多路数据源做交叉验证建议加上source和batch_id两个字段否则数据出了问题你连是哪个数据源、哪一轮采集导致的都查不清楚。全量股票实时交易数据这个事儿看起来只是“拿数据”三个字真正落地的过程中数据源选型、字段理解、采集架构、存储设计、稳定性保障、数据校验每一环都有它自己的问题。把这套系统跑通之后后面的研究和策略开发才算是真正站在一个完整、可靠的数据地基上。接下来我会继续写这个系列的其他部分包括行情数据的清洗归档、分钟级K线重建以及基于全量快照的复盘点位推导。如果你也在做类似的事情希望这篇实操记录能帮你少走几步弯路。
返回列表