做量化分析或者自己写选股工具的人最烦的一件事就是取数据。A 股行情接口五花八门大多数服务商上来就丢给你一套 SDK要求你先装好依赖、配好环境再写几行初始化代码最后才能拿到数据。我最初用 AlphaFeed 的时候也按这个套路走了但用了一段时间之后发现比起用 SDK直接拿 cURL 打它的 REST API 反而更省事不装任何依赖、不绑定编程语言、排查问题也直观得多。这篇文章就把我实际测试整理出来的 HTTP 直连方案完整写出来适合想快速验证接口、做临时数据拉取或者需要在 Python、Go、Shell 之间无缝切换的开发者参考。1. 为什么我抛弃了 SDK改用 cURL 直连1.1 SDK 方案的三个痛点先说清楚SDK 不是不能用而是被它绑住之后你会发现自己陷入一些非常尴尬的处境。第一是安装依赖的连锁反应。很多行情 SDK 不是孤零零一个包它会自动拉一堆依赖进来比如 protobuf、gRPC、websocket 客户端、某些特定版本的 HTTP 库。一套 Python 环境如果同时跑多个数据源很容易出现 A 的 SDK 要求 requests 2.28B 的 SDK 要求 requests 2.32最后你只能靠虚拟环境硬隔离维护成本直接翻倍。第二是版本升级的连带伤害。服务商一旦升级 SDK经常会把接口参数结构也改了你本地不升级就用不了新功能升级了旧代码可能直接跑不起来。我遇到过不止一次官方 SDK 发布新版之后旧版本的鉴权方式失效被迫在休息日改代码。第三是最要命的SDK 内部封装得太深出问题你根本不知道它在哪里。返回报错了你看到的是 SDK 自己定义的异常对象真正的 HTTP 状态码和响应体被吞掉了你只能去翻它的源码猜逻辑。而直接用 cURL 打 REST API整个链路完全透明——请求长什么样、响应长什么样一眼就能看到底。1.2 REST API 直连适合谁REST API 直连方案并不是要完全替代 SDK而是补充了一个更轻量的选择。我自己的使用场景大致有这么几类快速验证一个接口能不能用写五行业务代码之前先用 cURL 把数据拿回来看看结构写一次性脚本拉取历史数据不想为一个临时任务引入正式依赖在服务器上做数据同步服务器环境干净得像张白纸cURL 是系统自带的不用装任何东西需要跨语言复用同一套请求逻辑比如 Shell 脚本负责定时拉取Python 负责分析Go 负责展示大家共用一份 HTTP 接口约定。本质上REST API 就是我给你一个 URL你用标准的 HTTP 方法去访问服务端返回 JSON 数据。你不需要了解服务端内部怎么实现只需要会发请求和解析响应这就够了。SDK 归根结底做的也是同一件事只不过它在外面包了一层壳。2. AlphaFeed 接口的入门铺垫2.1 几个必须搞懂的基础概念拿到一个 REST API第一件事不是急着写命令而是先把四个概念理清楚Base URL、Endpoint、Query 参数和鉴权头。Base URL 是服务入口的统一前缀AlphaFeed 的接口路径一般都挂在类似https://api.alphafeed.com/v1这样的地址下面。Endpoint 是具体资源路径比如查询实时行情可能是/v1/quote查 K 线可能是/v1/bars。Query 参数是附加在 URL 问号后面的键值对用来传递股票代码、周期、时间范围这些条件。鉴权头则是在 HTTP Header 里放你的身份凭证常见的有X-Api-Key、Authorization: Bearer这类形式。把这四个概念搞清楚之后你会发现不管是哪个数据服务商接口设计思路都是互通的。今天能看懂 AlphaFeed明天拿到任何一家新接口你只需要去它的文档里查一查这四个东西分别是什么就能立刻上手。2.2 鉴权方式与请求头设计AlphaFeed 采用 API Key 的方式鉴权这也是绝大多数金融数据服务的惯例。你需要在后台申请一个密钥然后在每个请求的 Header 里带上它形如X-Api-Key: 你的密钥设计请求头的时候我建议至少带上这三项缺一不可X-Api-Key身份凭证服务端靠它识别你是谁Accept: application/json明确告诉服务端你希望返回 JSON 格式User-Agent设置一个能标识你的客户端名称方便服务端做日志追踪。比如User-Agent: my-trading-script/1.0。这里有一个常见误区很多人以为 POST 请求才需要Content-Type所以 GET 请求就忽略了。GET 请求确实通常不需要Content-Type但如果你调的是 POST 类接口比如批量查询一定要加上Content-Type: application/json否则服务端解析不了请求体里的 JSON 数据。提示API Key 等同你的账户密码千万不要硬编码到公开仓库里。建议通过环境变量或者独立的配置文件读取用完后及时在服务商后台作废回收。2.3 一次完整请求的生命周期理解一次完整请求的流转过程能帮你少走很多弯路。你输入一条 cURL 命令系统做的大概是这么几步解析 URL拆出协议、域名、端口和路径DNS 解析域名拿到服务器 IP建立 TCP 连接如果走 HTTPS则在 TCP 之上完成 TLS 握手按照 HTTP 协议格式把你的请求行、请求头和请求体发送给服务器服务器处理请求返回状态码和响应体cURL 把响应打印到终端你看到的就是最终结果。这一整个过程中任何一个环节出错都会产生对应的错误提示。比如 DNS 解析失败、TCP 连接被拒绝、TLS 握手失败、服务器返回 5xx这些错误在 cURL 下的表现形式完全不一样。后面我会专门用一个章节讲这些报错的排查方法现在你只要记住先把生命周期里每个环节对应到一条 cURL 报错信息上排查思路就会清晰很多。3. cURL 实操手把手拉取 A 股行情3.1 环境准备与 cURL 基础参数cURL 是几乎所有类 Unix 系统的内置工具Windows 10 以上版本也自带了不需要额外装。验证一下你的环境里有没有它curl --version看到输出类似curl 8.x.x就说明可以用。如果版本太旧低于 7.55建议升级一下因为后面我要讲的连接复用和并行请求功能在旧版本里表现不佳。另外强烈建议装一个jq它是 JSON 数据的瑞士军刀。没有它你看接口返回只能盯着密密麻麻的 JSON 字符串发呆有了它你可以像操作数据库一样精准提取自己想要的那几个字段。# macOS brew install jq # Debian/Ubuntu sudo apt install jq # CentOS/RHEL sudo yum install jq3.2 获取实时行情快照一切就绪之后我们从最简单的开始查询一只股票的实时行情快照。以贵州茅台为例A 股代码在 AlphaFeed 里需要带上交易所后缀600519.SH表示上交所000001.SZ表示深交所。curl -s https://api.alphafeed.com/v1/quote?symbol600519.SH \ -H X-Api-Key: YOUR_API_KEY \ -H Accept: application/json返回的 JSON 大致长这样{ code: 0, data: { symbol: 600519.SH, name: 贵州茅台, last: 1688.00, open: 1670.00, high: 1695.50, low: 1662.10, preClose: 1665.20, volume: 3200000, amount: 5400000000, timestamp: 2025-06-18 15:00:00 } }终端里直接看这一坨 JSON 眼不眼晕眼晕就对了上 jq。如果你想只看收盘价curl -s https://api.alphafeed.com/v1/quote?symbol600519.SH \ -H X-Api-Key: YOUR_API_KEY | jq .data.last如果你想一次性看多个关键字段用 jq 的花括号语法构造一个新的 JSON 对象curl -s https://api.alphafeed.com/v1/quote?symbol600519.SH \ -H X-Api-Key: YOUR_API_KEY | jq {symbol: .data.symbol, last: .data.last, high: .data.high, low: .data.low}这一步做完你就已经完成了一次标准的 REST API 调用带鉴权、带参数、拿数据、解析数据。后面的所有操作都是这个流程的变体。3.3 拉取历史 K 线数据实时快照只能看当下要做回测或者趋势分析必须拉历史 K 线。AlphaFeed 的 K 线接口大致是这样的curl -s https://api.alphafeed.com/v1/bars?symbol000001.SZperiod1dstart2025-01-01end2025-06-18adjustqfq \ -H X-Api-Key: YOUR_API_KEY | jq .data[:3]这里面有几个参数需要解释一下。period是 K 线周期常见取值有1m、5m、15m、30m、60m、1d、1w、1M分别对应分钟线、日线、周线和月线。注意区分大小写我曾经把1M写成1m白白等了半天才发现拿到的是分钟线。adjust是复权方式。做历史回测一定要理解复权的作用上市公司会分红送股如果不做复权处理K 线图上会出现价格跳空技术指标会被严重扭曲。qfq表示前复权以最新价格为基准回溯调整历史价格适合看当前价位下的历史走势hfq表示后复权以首日价格为基准适合计算真实收益率。默认值通常是none也就是不复权做短线和只看原始价格的人可以选这个但做长线回测务必用复权数据。返回的每条数据包含open、high、low、close、volume、amount和时间戳结构非常规整直接转成 DataFrame 或者 CSV 都方便。3.4 获取分时与逐笔成交数据分时数据和逐笔成交是两类不同的东西别搞混。分时数据通常按分钟聚合一个时间点只有一条记录包含这一分钟的均价、成交量等信息适合看日内走势。请求方式一般是curl -s https://api.alphafeed.com/v1/bars?symbol600519.SHperiod1mstart2025-06-18end2025-06-18 \ -H X-Api-Key: YOUR_API_KEY | jq .data | lengthjq .data | length用来统计返回了多少条数据这个技巧在验证接口返回完整性的时候非常有用。逐笔成交则记录了每一笔真实的成交可能一秒内就有几百条量级完全不一样。这类接口通常有严格的频率限制和单次返回条数上限比如每次最多返回 1000 条需要配合游标分页来拉取完整数据。如果服务商提供了游标参数常见字段名是cursor或者next_token记得按文档要求循环请求直到游标为空。我建议先从小周期 K 线入手等把整条链路跑通了再去碰逐笔数据因为逐笔数据的解析和存储复杂度会高一个量级。4. 直连 HTTP 的进阶玩法4.1 公共参数与请求头优化当你开始频繁调用接口的时候你会发现每条命令都带一长串-H X-Api-Key: ...太啰嗦了。这里有两个优化方向。第一个方向是使用 cURL 的配置文件。在~/.curlrc或者项目目录下的.curlrc里可以预设默认请求头header X-Api-Key: YOUR_API_KEY header Accept: application/json这样你再发请求的时候直接写 URL 就行cURL 会自动带上配置文件里的 Headercurl -s https://api.alphafeed.com/v1/quote?symbol600519.SH简洁了不少对吧。注意.curlrc的语法是所有参数之间用换行分隔参数名后面跟空格和值等号两边没有空格。第二个方向是善用-G参数。如果你要传的查询参数特别多直接拼在 URL 后面容易出错尤其是里面还有特殊字符的时候。用-G配合--data-urlencode可以让 cURL 自动帮你做 URL 编码curl -sG https://api.alphafeed.com/v1/bars \ --data-urlencode symbol600519.SH \ --data-urlencode period1d \ --data-urlencode start2025-01-01 \ --data-urlencode end2025-06-18 \ --data-urlencode adjustqfq如果你要查询的股票名称里带着中文或者特殊符号这个方式能帮你避免很多编码折磨。4.2 返回数据结构与字段解读很多人拿到接口返回之后不做任何加工就直接塞进分析程序里这是不对的。REST API 的返回数据通常有统一的包装结构AlphaFeed 的设计类似这样{ code: 0, message: success, data: { ... } }code是业务状态码0表示正常非零值对应各种业务错误message是对状态的辅助说明data是真正的数据体。所以写任何处理脚本第一判断应该是检查code是否为 0而不是直接去取data。这里有一个踩坑点HTTP 状态码和业务状态码是两回事。HTTP 200 只代表请求被服务器正常处理了不代表业务逻辑成功。比如你查询一只不存在的股票代码服务器可能会返回 HTTP 200但code是 10001message写着 symbol not found。如果你的脚本只检查了 HTTP 状态码就会把错误数据当成正常数据拿去分析结果可想而知。正确的处理逻辑是先看 HTTP 状态码判断网络层有没有问题再看code判断业务层有没有问题两层都通过了才去解析data。4.3 频率控制、连接复用与超时设置行情数据接口最忌讳的就是不加控制地疯狂请求。AlphaFeed 的免费额度通常有频率限制比如每分钟最多 60 次请求超出后返回429 Too Many Requests同时响应头里会带Retry-After字段告诉你要等多少秒才能继续。批量拉取多只股票的时候很多人会写一个循环每次请求都新建连接这是非常低效的。HTTP 连接建立的成本很高尤其是 HTTPS 还需要 TLS 握手。好在 cURL 对连接复用有自动处理当你把多个 URL 放在同一条命令里时如果它们指向同一个服务器cURL 会自动复用连接。curl -s https://api.alphafeed.com/v1/quote?symbol600519.SH \ https://api.alphafeed.com/v1/quote?symbol000001.SZ \ https://api.alphafeed.com/v1/quote?symbol601318.SH \ https://api.alphafeed.com/v1/quote?symbol600036.SH查看-v输出如果看到Re-using existing connection字样就说明连接复用生效了。这一条命令相比多次单独调用能省掉大量握手开销。超时设置同样重要没有设置超时的请求可能会卡住几分钟甚至更久。两个关键参数--connect-timeout 10建立连接的超时时间设 10 秒足够--max-time 30整个请求的最大耗时包含数据传输时间。这两个参数务必加到你的命令里尤其是写进 crontab 定时任务的时候。没有超时限制的任务一旦卡住会一直占着你的进程和内存甚至影响服务器上的其他业务。如果需要更高并发可以用 curl 7.66 以上版本提供的--parallel系列参数。--parallel 8表示同时最多发送 8 个请求配合--parallel-immediate可以在请求列表还没完全生成时就启动发送拉取几百只股票的行情时效率提升非常明显。5. 常见报错与排查实录5.1 HTTP 状态码速查我在实际调试过程中最常遇到的状态码就这么几个整理成一张表方便你直接对照。状态码含义常见原因处理方式400 Bad Request请求参数错误参数缺失、格式不对、股票代码不带交易所后缀检查 URL 参数对照文档逐项核对401 Unauthorized鉴权失败API Key 没传、传错了、密钥已过期检查请求头里的X-Api-Key403 Forbidden无权访问IP 不在白名单、账户权限不够去后台设置里加上当前 IP404 Not Found路径不存在Endpoint 拼错了、版本号不对检查路径是否匹配文档429 Too Many Requests触发频率限制请求太密集读取Retry-After响应头等够时间再请求500 Internal Server Error服务端内部错误服务商自己的问题稍后重试或者报工单502 Bad Gateway网关错误服务商上游服务异常退避重试比如等 1 秒、2 秒、4 秒递增524 A Timeout Occurred网关超时请求处理太久服务器还没返回缩小查询范围或者改用分页拉取5.2 我踩过的几个坑第一个坑是 cURL 的错误码 3提示URL rejected: Port number was not a decimal number。我第一次看到这个报错完全懵了因为我根本没写端口号。后来发现问题是 URL 里某个参数值带了冒号比如时间字符串2025-06-18T00:00:00没有做 URL 编码cURL 把冒号后面的内容误认为是端口号了。解决办法很简单用--data-urlencode传参数或者把冒号改成%3A。第二个坑是错误码 35TLS 握手失败。这个通常在服务器时钟不准、或者本机根证书过期的时候出现。排查思路是先确认系统时间是否正确再尝试更新根证书sudo apt install ca-certificates第三个坑是 502 和 524 交替出现。有一段时间我写了一个批量脚本每次拉 2000 只股票的日线跑着跑着就报502 Bad Gateway。一开始我以为是服务商挂了后来发现是我一次性请求的数据量太大服务端处理超过了网关的超时限制被上游断开了。解决方式是把大请求拆成小批量每次只查 50 只股票配合连接复用和退避重试问题就消失了。第四个坑是频率限制的误判。我当时以为只要每次请求间隔大于 1 秒就不会触发 429结果还是一直被限流。后来仔细看文档才发现AlphaFeed 的限流是按窗口计算的比如 5 分钟窗口内最多 300 次请求不是按简单的每秒速率。要妥善应对这种情况你得在代码里维护一个请求时间戳队列每次发请求前先检查窗口内已经发了多少次预判是否会触限。5.3 从 cURL 平滑迁移到脚本批量拉取cURL 适合快速验证接口和使用原始 HTTP 逻辑但当你确认这套直连方案可行需要每天定时拉数据的时候把它写进脚本会更靠谱。我最常用的两个方案是 Shell 循环和 Python requests。Shell 方案适合轻量任务比如每天收盘后拉取全市场日线#!/bin/bash symbols$(cat symbols.txt) for s in $symbols; do curl -sG https://api.alphafeed.com/v1/bars \ --data-urlencode symbol$s \ --data-urlencode period1d \ --data-urlencode adjustqfq \ --connect-timeout 10 \ --max-time 30 bars.jsonl sleep 0.5 done这里把每次请求的结果以 JSON Lines 格式追加写入文件每行一个 JSON 对象后续用 jq 或者 Python 做流式解析都很方便。Python 方案适合需要重试和错误处理的复杂任务。注意换语言不等于换思路本质上你还是在对同一个 REST API 发 HTTP 请求只是把 cURL 换成了 requests 库import time import requests def fetch_bars(symbol, api_key): url https://api.alphafeed.com/v1/bars params {symbol: symbol, period: 1d, adjust: qfq} headers {X-Api-Key: api_key, Accept: application/json} for attempt in range(5): resp requests.get(url, paramsparams, headersheaders, timeout30) if resp.status_code 200 and resp.json().get(code) 0: return resp.json()[data] if resp.status_code 429: time.sleep(int(resp.headers.get(Retry-After, 60))) elif resp.status_code 500: time.sleep(2 ** attempt) else: break return None这段代码里有两个设计细节值得说一下。超时设置timeout30对应 cURL 的--max-time防止请求卡死重试用了指数退避2 ** attempt第一次失败等 1 秒第二次等 2 秒第三次等 4 秒这样既不会把服务商打崩也能在临时故障恢复后自动续上。最后再分享一个我在实际项目中常用的技巧任何脚本落地之前先拿 cURL 把要调的每个接口手动跑一遍确认参数和返回结构都对再转换成代码。这一步看起来多花了十分钟实际上能帮你省下未来好几个小时的排错时间。