算法交易数据获取实战:从Python入门到实盘可用
1. 什么是算法交易别被术语吓住它本质就是“用程序代替人盯盘下单”很多人一听到“算法交易”脑子里立刻浮现出高频闪动的K线图、满屏跳动的代码、还有穿着西装在华尔街交易大厅里对着电话狂吼的场景。其实大可不必。在我过去八年实操股票、期货、期权策略开发的经历里算法交易最朴素的定义就是把你的交易逻辑用计算机能理解的语言写下来让它自动执行买卖动作。它不是魔法也不是只有顶级量化基金才能玩的游戏——就像你用Excel做自动求和、用手机设置闹钟提醒自己吃药一样算法交易只是把“规则执行”这件事从人脑和手动操作搬到了电脑上。核心关键词是“Finance”——但请注意这里说的Finance不是教科书里抽象的CAPM模型或Black-Scholes公式推导而是每天真实发生在交易所里的价格跳动、订单簿变化、成交确认延迟、滑点损耗这些带着温度的细节。我见过太多新手一上来就埋头调参LSTM预测明天收盘价结果回测曲线漂亮得像油画实盘第一天就因为一笔市价单吃掉3个点的滑点直接爆仓。所以这篇文章不讲虚的我们从最底层、最实在的环节开始搞懂算法交易到底在做什么以及怎么拿到真正能用、够干净、带时间戳的原始交易数据。适合三类人刚毕业想入行量化岗的学生、有实盘经验但想摆脱手动盯盘的个人交易者、以及对技术好奇、想亲手跑通第一个自动下单脚本的程序员。不需要你精通金融工程但得会写Python基础循环不需要你背熟所有希腊字母但得知道“买一价”和“最新成交价”差0.5分意味着什么。这事儿说白了就是一场“人机协作”的分工重构。人负责思考“什么时候该买/卖”机器负责解决“怎么在毫秒级响应、不手抖、不犹豫、不情绪化地执行”。而所有这一切的前提是你手里握着的数据得是真实的、及时的、结构清晰的。否则再精妙的模型输入的是垃圾输出的只能是更精致的垃圾。所以Part-1不谈模型、不谈回测框架、不谈风控模块——我们就死磕一个最基础、也最容易被忽视的环节数据获取链路的搭建与验证。它就像盖楼的地基地基没打牢上面建再高的摩天大楼风一吹就晃。2. 算法交易的整体设计思路为什么必须从数据源头开始拆解2.1 别急着写策略先画一张“数据流地图”很多初学者一上来就想实现“双均线金叉买入、死叉卖出”代码写了两百行运行起来发现回测收益曲线像心电图波动剧烈得没法看。我帮他们排查八成问题出在数据上要么用的是日线收盘价却去模拟分钟级的高频开平仓要么数据源本身就有缺失某天下午两点到三点的行情完全空白策略却默认那里有连续报价硬生生算出一个不存在的“金叉”。所以我的第一课永远是带他们画一张“数据流地图”。这张地图只包含四个节点数据源 → 数据获取接口 → 数据清洗与存储 → 策略引擎输入。每个节点之间都必须标清楚“数据格式是什么”、“时间精度是多少”、“延迟大概多久”、“有没有断点或异常值”。比如你选了某家券商的免费行情API它的文档写着“T1提供前日分钟线”那你就得清醒认识到这个数据根本不能用于日内策略连做隔夜持仓的简单趋势跟踪都可能因开盘跳空而失效。再比如你爬取某财经网站的实时报价页面渲染用的是JavaScript动态加载你用requests直接GET返回的HTML里压根没有最新价格全是占位符——这种“假实时”数据拿来训练模型等于教AI认错字。我自己的习惯是拿到任何新数据源先不做策略花半天时间写一个极简脚本只做三件事——连接、拉取、存成CSV。然后打开CSV用Excel或pandas的describe()函数盯着看三样东西时间列是否连续检查是否有整分钟/整小时的空白价格列是否有明显异常值比如某秒内价格从10元跳到1000元成交量列是否为0的时段占比超过5%就要警惕。这三步做完你才真正摸清了这个数据源的“脾气”。它不是技术活是态度活。我见过最离谱的案例是某团队用某第三方平台的“实时”数据做期货套利结果发现对方服务器时钟比交易所快87毫秒导致所有信号都提前发出实盘永远追在价格后面跑。2.2 为什么Python是首选不是因为它多酷而是它足够“糙”且“快”有人问C不是更快吗FPGA不是延迟更低吗没错但那是给年化百亿美金管理规模的对冲基金准备的。对我们绝大多数人来说Python的“慢”恰恰是优势。它的慢体现在执行速度上但它的快体现在开发速度、调试速度、试错成本上。举个例子你想验证一个简单的布林带突破策略。用C你得先搭编译环境、写Makefile、处理内存指针、调试段错误……一周可能还在跑通Hello World。用Pythonpandas一行read_csv读数据ta-lib一行计算布林带matplotlib三行画图从想法到看到结果半小时搞定。这半小时里你不是在和编译器斗气而是在和市场逻辑对话“如果我把标准差倍数从2改成2.5信号会不会变少但胜率提高”更重要的是Python生态里几乎所有你能想到的金融数据接口都有现成的、维护良好的封装库。yfinance抓雅虎财经免费数据akshare对接国内全市场开源数据baostock连A股日线都不用注册vnpy的gateway模块甚至能直连国内主流期货公司的CTP柜台。这些不是玩具是经过成千上万实盘用户踩过坑、修过bug的生产级工具。它们的共同特点是文档清晰、报错明确、社区活跃。当你在深夜调试一个连接超时错误时Google搜错误信息第一页基本就是Stack Overflow上某个和你一样倒霉的同行贴出的解决方案。这种“有人垫过脚”的安全感是任何小众语言给不了的。当然Python也有硬伤GIL全局解释器锁让它无法真正并行纯Python循环处理亿级tick数据确实吃力。但我们的应对策略很务实核心计算用NumPy向量化耗时IO用asyncio异步瓶颈模块用Cython重写。你看这不是在回避缺点而是在承认局限的前提下用最省力的方式绕过去。这恰恰是成熟从业者和学院派最大的区别——前者永远在找“够用就好”的解法后者总在追求“理论上最优”的解法。2.3 机器学习在这里扮演什么角色先撕掉“预测神器”的标签提到“Algorithmic Trading with Python and Machine Learning”很多人下意识觉得哦这是要用深度学习预测股价。这个理解偏差太大而且非常危险。在我经手的上百个实盘策略中真正依赖“预测未来价格”这一目标的不到5%。为什么因为价格本身是高度随机、受无数不可观测变量影响的混沌系统。你让模型学“明天涨还是跌”本质上是在教它猜硬币正反面——长期来看准确率不会显著高于50%而交易成本会吃掉所有微弱的优势。机器学习在算法交易里更常见的、也更靠谱的角色是模式识别、异常检测、特征工程、执行优化。比如用聚类算法K-Means把历史波动率分成高/中/低三档让策略在不同波动环境下自动切换参数用孤立森林Isolation Forest实时扫描Level 2订单簿快速识别大单撤单引发的流动性枯竭信号用XGBoost给每笔潜在交易打一个“执行难度分”指导智能订单路由Smart Order Routing选择最优的交易所和委托类型。这些任务不预测价格而是理解市场微观结构、捕捉行为模式、提升执行效率。它们的目标不是“赚多少钱”而是“少亏多少钱”、“多抓住几次确定性机会”。所以Part-1我们不碰任何ML模型。我们要做的是确保你拿到的数据已经包含了足够丰富的原始特征逐笔成交的时间、价格、成交量、买卖方向五档行情的挂单量、挂单价格、挂单变化速率甚至交易所的系统时间戳与本地时间戳的差值。这些才是机器学习真正能“嚼得动”的肉。没有这些给你再大的GPU集群也训练不出一个能赚钱的模型。记住数据质量决定策略上限模型复杂度只影响你逼近上限的速度。3. 核心细节解析如何获取真实、可用、合规的交易数据3.1 免费数据源够用但必须知道它的“保质期”对于起步阶段免费数据源是唯一合理的选择。我把它比作学开车时的驾校教练车——性能一般仪表盘可能偶尔失灵但足够让你练熟离合、油门、方向盘的配合。关键是要清楚它的边界在哪里。yfinance是我给新手推荐的第一站。它封装了Yahoo Finance的公开API支持全球主要交易所的股票、指数、ETF、加密货币的分钟级、日线、周线数据。优点是零配置、无注册、无调用频率限制相对宽松、数据字段齐全Open/High/Low/Close/Volume/Adj Close。我常用它来快速验证一个宏观策略的逻辑比如“标普500指数跌破200日均线后买入VIX期货对冲”。但它的致命短板是延迟高通常滞后15-20分钟、不提供逐笔成交和Level 2行情、部分小盘股数据缺失严重。所以它绝对不能用于任何需要实时性的策略哪怕是日内的简单突破也容易因延迟错过最佳入场点。akshare是国内开发者维护的宝藏库专为中国市场优化。它能抓取上交所、深交所、中金所、上海黄金交易所等官方渠道的免费、准实时数据。我特别喜欢它的几个模块stock_zh_a_hist可以拿到A股全市场日线含复权因子futures_main_sina能实时获取主力合约的最新报价和持仓量index_zh_a_hist提供宽基指数的分钟线。它的数据源更“近水楼台”延迟通常控制在1-3分钟内且字段设计非常符合国内交易员的习惯比如直接返回“涨跌幅”、“换手率”、“市盈率TTM”。但要注意它依赖网页爬虫一旦目标网站改版接口就会失效。我去年就遇到过一次上交所官网结构调整akshare的stock_zh_a_spot接口停摆三天好在我提前做了本地缓存没耽误实盘。提示用免费数据源务必建立“双源校验”机制。比如同时用yfinance和akshare拉取同一支股票的日线对比收盘价和成交量。如果差异超过0.5%说明至少有一个源出了问题此时应暂停策略运行人工核查。这看似麻烦却是避免“数据污染”导致策略失效的最有效防火墙。3.2 付费数据源为实盘而生贵得有道理当你从“玩票”进入“真金白银”付费数据源就成了必选项。这里没有“最好”只有“最适合”。我按使用场景分三类推荐第一类专业行情终端如Wind、同花顺iFinD。这是国内机构的标配。它们的优势在于数据权威、字段全面、更新及时、支持定制化推送。Wind的“万得股票”数据库能提供从1990年至今的A股全量财务数据、股东结构、研报摘要、甚至高管薪酬它的Level 2行情能精确到每一笔委托的挂单时间、撤单原因。但代价是年费数万元起且API接入复杂需要专门的客户端授权。对我个人而言它更像是一个“数据图书馆”我用它查证宏观指标、下载历史财务报表而不是作为实盘策略的实时数据源——太重了。第二类量化数据服务商如聚宽JoinQuant、掘金MyQuant。这是个人和小团队的主力。它们提供标准化的Python SDK一行代码就能订阅实时行情、获取历史数据、提交订单。聚宽的“分钟线”数据延迟稳定在500毫秒内支持A股、期货、期权掘金的“Tick数据”服务能提供完整的逐笔成交和五档行情延迟可压到100毫秒。最关键的是它们的SDK和回测引擎深度集成你在回测里写的策略几乎不用改代码就能一键部署到实盘。我自己的日内短线策略就跑在掘金的Tick数据上。它的定价也很透明基础版每月几百元对个人交易者完全可承受。第三类交易所直连如中金所CTP、上期所FAST。这是终极形态也是门槛最高的。你需要成为交易所的会员或通过期货公司代理申请交易编码部署专用服务器通过C API直连柜台。好处是延迟最低微秒级、数据最原始裸协议、完全自主可控。坏处是开发成本极高运维压力巨大一个小bug可能导致穿仓。我只在管理客户资金、策略逻辑极其敏感如做市商价差套利时才会启用这套方案。对99%的个人开发者它属于“屠龙刀”好看但日常切菜用不上。注意无论选择哪种付费源签约前必须仔细阅读《数据使用协议》。里面会明确规定数据能否用于实盘交易、能否二次分发、能否用于训练商业模型。我曾见过有团队把聚宽的数据喂给自己的大模型结果被律师函警告——因为协议里白纸黑字写着“仅限策略研究禁止用于AI训练”。合规不是束缚是保护。3.3 自建数据管道当标准方案不够用时你得学会“拧螺丝”有时候标准数据源就是无法满足你的特殊需求。比如你想监控某只股票在雪球、东方财富股吧里的舆情热度变化作为辅助信号或者你想把天气预报API台风路径影响港口装卸和某航运期货的价格联动起来。这时候“自建数据管道”就成了必备技能。我的做法是用Python的schedule库写一个轻量级调度器搭配requests和BeautifulSoup或Selenium处理JS渲染做数据采集用pandas做清洗最后存入本地SQLite数据库小数据量或PostgreSQL大数据量。整个流程我称之为“三明治架构”上层是调度和监控保证它不死中间是采集和清洗保证它干净底层是存储和索引保证它快。举个真实案例我有个做农产品期货的策略需要实时跟踪主产区的降雨量。国家气象局官网有公开数据但格式是PDF扫描件。我用pdfplumber库解析PDF提取表格再用geopy库把乡镇名称转成经纬度最后和期货合约的交割仓库坐标做距离计算生成一个“降雨影响指数”。这个指数每周更新一次虽然原始但它捕捉到了一个纯价格数据无法反映的驱动因素。整个管道从零开始搭建用了不到两天成本是0元。自建管道的核心心得是永远假设上游会挂掉。所以我在每个采集脚本里都强制加入三重保险1超时设置requests.get(timeout30)2失败重试tenacity库最多重试3次3断点续传记录最后成功采集的日期下次从那里开始。这看起来繁琐但比半夜被报警短信叫醒、发现数据断了三天要强得多。4. 实操过程详解手把手搭建你的第一个数据获取脚本4.1 环境准备三步到位拒绝“配置地狱”别被网上那些动辄几十行的环境配置教程吓到。我的原则是最小依赖、最大兼容、一次配好。以下步骤在Windows、macOS、Linux上均实测通过。第一步安装Python 3.9去python.org下载最新稳定版安装时务必勾选“Add Python to PATH”。验证打开命令行输入python --version看到Python 3.9.x或更高版本即可。为什么是3.9因为它是目前NumPy、pandas等科学计算库支持最完善、bug最少的版本。低于3.8某些新语法如类型提示会报错高于3.11部分老库还没适配。第二步创建虚拟环境别再用全局Python了执行python -m venv algo_trading_env algo_trading_env\Scripts\activate # Windows # 或 source algo_trading_env/bin/activate # macOS/Linux这会在当前目录下创建一个完全隔离的Python环境。所有后续安装的包都只在这个环境里生效彻底避免“A项目需要pandas 1.4B项目需要pandas 2.0”的冲突。第三步安装核心依赖在激活的虚拟环境中执行pip install --upgrade pip pip install pandas numpy matplotlib yfinance akshare requests beautifulsoup4 schedule tenacity注意yfinance和akshare是重点它们是我们获取数据的“两条腿”。tenacity是那个负责失败重试的库别漏掉。全部装完输入pip list你会看到一个干净的、只属于这个项目的包列表。实操心得我建议你把这三步写成一个setup.shmacOS/Linux或setup.batWindows脚本。以后换新电脑双击运行30秒搞定环境。这比每次手动敲命令、记错版本、查报错要高效十倍。4.2 获取A股日线数据用akshare跑通第一个完整流程现在让我们写第一个真正有用的脚本。目标获取贵州茅台600519.SH过去一年的日线数据并保存为CSV文件同时画出收盘价走势图。# get_stock_data.py import akshare as ak import pandas as pd import matplotlib.pyplot as plt from datetime import datetime, timedelta def fetch_moutai_data(): 获取贵州茅台日线数据 try: # akshare的股票日线接口symbol参数是股票代码adjust参数指定复权方式 # qfq是前复权hfq是后复权none是不复权。个人推荐qfq df ak.stock_zh_a_hist( symbol600519, perioddaily, start_date20230501, # 格式YYYYMMDD end_date20240501, adjustqfq ) # akshare返回的DataFrame列名是中文我们统一转成英文方便后续处理 df.columns [ date, open, close, high, low, volume, turnover, change_pct, change_amt, amplitude, swing, trading_status ] # 将date列转为datetime类型并设为索引 df[date] pd.to_datetime(df[date]) df.set_index(date, inplaceTrue) # 检查数据质量打印基本信息 print(✅ 数据获取成功) print(f 数据范围{df.index.min()} 至 {df.index.max()}) print(f 总交易日{len(df)} 天) print(f 缺失值检查\n{df.isnull().sum()}) return df except Exception as e: print(f❌ 数据获取失败{e}) return None def save_and_plot(df): 保存数据并绘图 if df is None: return # 保存为CSV注意indexTrue保留日期索引 filename fmoutai_daily_{datetime.now().strftime(%Y%m%d_%H%M%S)}.csv df.to_csv(filename, encodingutf-8-sig) # utf-8-sig兼容Excel中文 print(f 数据已保存至{filename}) # 绘制收盘价走势图 plt.figure(figsize(12, 6)) plt.plot(df.index, df[close], label贵州茅台收盘价, linewidth2) plt.title(贵州茅台600519.SH日线收盘价走势, fontsize14) plt.xlabel(日期) plt.ylabel(价格元) plt.grid(True, alpha0.3) plt.legend() plt.tight_layout() plt.savefig(fmoutai_price_chart_{datetime.now().strftime(%Y%m%d_%H%M%S)}.png) print(️ 价格走势图已保存) if __name__ __main__: data fetch_moutai_data() save_and_plot(data)把这个代码保存为get_stock_data.py在命令行里运行python get_stock_data.py几秒钟后你会看到类似这样的输出✅ 数据获取成功 数据范围2023-05-04 00:00:00 至 2024-04-30 00:00:00 总交易日245 天 缺失值检查 date 0 open 0 close 0 ... 数据已保存至moutai_daily_20240525_103022.csv ️ 价格走势图已保存打开生成的CSV文件你会看到整齐的表格打开PNG图片一条清晰的K线图跃然眼前。这就是你的第一个“数据管道”——它虽小但五脏俱全有错误处理、有数据清洗、有质量检查、有结果输出。实操心得这个脚本里ak.stock_zh_a_hist的start_date和end_date参数格式必须是YYYYMMDD字符串不能是datetime对象也不能是2023-05-01这种带横杠的。我第一次用的时候就因为传了datetime.today().strftime(%Y-%m-%d)结果akshare直接返回空DataFramedebug了半小时才发现是格式问题。这种细节文档里往往不强调但实操中处处是坑。4.3 获取期货主力合约实时行情用yfinance和自定义逻辑结合A股日线是“静态”的而期货市场是“活”的。主力合约会切换价格跳动频繁我们需要一个能“守着它”的脚本。这里我们用yfinance获取一个近似实时的“快照”再用一个简单的逻辑判断当前主力合约。# get_futures_main.py import yfinance as yf import pandas as pd from datetime import datetime def get_main_contract(symbol_base): 根据基础符号获取当前主力合约代码简化版 # 这是一个极简逻辑假设主力合约是距离当前日期最近的、未到期的合约 # 真实场景中你需要查询中金所/上期所的官方主力合约列表 months [03, 06, 09, 12] current_year datetime.now().year current_month datetime.now().month # 找到下一个到期月份 for m in months: if int(m) current_month: next_month m break else: # 如果今年没有了取明年3月 next_month 03 current_year 1 # 构造合约代码例如 IF2406 表示沪深300股指期货2024年6月合约 # yfinance的期货代码格式是IFF (IF期货)但需要具体合约所以用 IF2406F contract_code f{symbol_base}{current_year % 100}{next_month}F return contract_code def fetch_futures_quote(contract_code): 获取期货合约实时报价 try: # yfinance的Ticker对象 ticker yf.Ticker(contract_code) # 获取实时数据.info返回字典.history返回DataFrame # 对于实时报价.info更轻量 info ticker.info # 提取关键字段 quote { symbol: contract_code, last_price: info.get(regularMarketPrice, 0), open_price: info.get(regularMarketOpen, 0), high_price: info.get(regularMarketDayHigh, 0), low_price: info.get(regularMarketDayLow, 0), volume: info.get(regularMarketVolume, 0), timestamp: datetime.now().strftime(%Y-%m-%d %H:%M:%S) } print(f {contract_code} 实时报价{quote[last_price]:.2f} | f涨跌幅{(quote[last_price] - quote[open_price])/quote[open_price]*100:.2f}% | f时间{quote[timestamp]}) return quote except Exception as e: print(f❌ 获取{contract_code}报价失败{e}) return None if __name__ __main__: # 假设我们要跟踪沪深300股指期货主力合约 base_symbol IF # IF代表沪深300 main_contract get_main_contract(base_symbol) quote fetch_futures_quote(main_contract) # 可以把quote存入列表后续扩展为循环刷新 if quote: quotes_list [quote] df pd.DataFrame(quotes_list) df.to_csv(ffutures_quote_{datetime.now().strftime(%Y%m%d_%H%M%S)}.csv, indexFalse) print( 期货报价已保存)运行这个脚本你会看到类似 IF2406F 实时报价3625.20 | 涨跌幅0.45% | 时间2024-05-25 10:45:22 期货报价已保存这个脚本的价值不在于它多精准yfinance的期货数据延迟可能达几分钟而在于它展示了如何把一个模糊的需求“我要看主力合约”转化为可执行的代码逻辑。get_main_contract函数里的逻辑你可以随时替换成从交易所官网爬取的权威列表或者接入聚宽的主力合约API。框架搭好了血肉随时可以填充。5. 常见问题与排查技巧实录那些没人告诉你的“坑”5.1 “Connection refused” 和 “Timeout”网络问题的真相这是新手遇到最多的报错。你以为是网络不好其实是数据源在“防你”。yfinance的“Connection refused”大概率是你的IP被Yahoo临时封禁了。原因短时间内请求太频繁比如在一个循环里没加sleep。解决方案在每次yf.Ticker(...).info调用后加time.sleep(1)。更优雅的做法是用tenacity库做指数退避重试。akshare的“Timeout”国内网站常有反爬机制。akshare默认的requests超时是5秒有时不够。解决方案修改akshare的源码或者更简单——在调用前全局设置import requests from akshare import stock_zh_a_hist # 设置全局超时 requests.adapters.DEFAULT_TIMEOUT 30所有HTTP请求的通用法则永远在headers里加上User-Agent。很多网站会直接拒绝没有UA的请求。一行代码搞定headers {User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36} requests.get(url, headersheaders)5.2 数据“看着对其实错”时间戳陷阱这是最隐蔽、杀伤力最大的坑。我亲眼见过一个团队因为没处理好时区把UTC时间当成北京时间导致所有信号晚了8小时一个月亏掉本金的15%。akshare的stock_zh_a_hist返回的时间是“字符串”不是datetime。如果你直接df.index.max()得到的是字符串比较结果不是真实时间。必须显式转换pd.to_datetime(df[date])。yfinance的Ticker.history()返回的索引是DatetimeIndex但它的时区是None。这意味着它不区分UTC还是北京时间。解决方案在转换后强制指定时区df.index df.index.tz_localize(Asia/Shanghai)最狠的坑交易所的“系统时间” vs “行情时间”。有些数据源尤其是Level 2会提供两个时间戳一个是交易所服务器打的时间System Time一个是行情数据生成的时间Exchange Time。两者可能相差几毫秒。做高频策略时必须用Exchange Time做排序否则K线合成会错乱。5.3 CSV打开乱码、Excel显示“#####”字符编码与数字格式乱码问题Python默认用UTF-8保存但Windows Excel默认用GBK打开。解决方案保存时用encodingutf-8-sig这个-sig会在文件开头加BOM标记Excel就能正确识别。Excel显示“#####”不是数据错了是列宽不够。但更深层的原因是你的价格列是float64Excel试图用科学计数法显示超长小数。解决方案在保存前对价格列做格式化df[close] df[close].round(2) # 保留两位小数日期在Excel里变成数字因为pandas的datetime保存为CSV时变成了时间戳从1970年开始的秒数。解决方案保存前把日期列转成字符串df[date] df[date].dt.strftime(%Y-%m-%d)5.4 回测曲线完美实盘一塌糊涂数据“幸存者偏差”这是所有策略死亡的终极原因。你用akshare下载了2000只A股过去10年的数据回测一个选股策略选出的都是牛股。但你忘了这2000只股票是今天还存在的、没退市的。那些2015年就ST、2018年就破产的股票数据早就被akshare过滤掉了。你的回测是在一个“幸存者”的样本池里做的天然就高估了胜率。解决方案只有一个构建“当时当地”的数据集。比如你要回测2015年的策略就必须找到2015年实际可交易的股票列表可以从当年的季报里扒然后只用这些股票的历史数据。这很麻烦但这是职业和业余的分水岭。我自己的做法是每年12月31日用akshare.stock_info_a_code_name()获取当天全市场股票代码存成一个stocks_20151231.csv作为那一年的“可投资 universe”。回测时强制只从这个列表里选股。最后分享一个小技巧在你的数据获取脚本里加一个“健康检查”函数。每次拉完数据自动运行def health_check(df): # 检查时间连续性 expected_days len(pd.date_range(df.index.min(), df.index.max(), freqD)) actual_trading_days len(df) continuity_ratio actual_trading_days / expected_days if continuity_ratio 0.95: print(f⚠️ 警告数据连续性不足理论交易日{expected_days}实际{actual_trading_days}比例{continuity_ratio:.2f}) # 检查价格合理性用IQR方法 Q1 df[close].quantile(0.25) Q3 df[close].quantile(0.75) IQR Q3 - Q1 outliers df[(df[close] Q1 - 1.5*IQR) | (df[close] Q3 1.5*IQR)] if len(outliers) 0: print(f⚠️ 警告发现{len(outliers)}个价格异常值)这个函数能在数据入库前帮你揪出90%以上的“脏数据”。它不炫技但管用。