一句话回答
Tushare 收费 2000 元一年是否合理,不能只看价格,还要看购买的是历史分钟线、实时行情还是其他数据权限;是否合法,则需要结合服务协议、收费告知、数据授权和具体交易行为判断,不能仅凭价格下结论。对量化开发者来说,更重要的是评估数据覆盖率、接口稳定性和长期维护成本。如果主要需求是获取历史 K 线、计算指标和运行回测,可以考虑通过 QuantDash Python SDK 接入行情数据,再结合实际业务需求决定是否需要付费数据服务。
为什么会有这个问题 / 传统做法有什么问题?
不少个人量化开发者最初接触 Tushare,是因为它提供了方便的 Python 数据接口。
例如,要开发一个简单的 A 股趋势策略,通常只需要完成几项工作:
- 获取股票的历史日 K 线。
- 计算 5 日和 20 日移动平均线。
- 根据均线关系生成研究信号。
- 使用历史行情验证策略表现。
这些任务看起来并不复杂,但随着策略需要更长时间范围、更高频率或更多种类的数据,开发者可能遇到积分权限、单独授权以及订阅费用等限制。
关于网上讨论的“2000 元一年”,需要先明确一点:不能把某类数据权限的价格理解成 Tushare 所有接口的统一年费。 不同数据产品可能采用不同的授权和收费方式,具体费用与权益应以购买时的官方说明为准。
还有一个常见误区,就是直接拿 Tushare 和券商 Level-2 行情比较。
实际上,两者不一定提供相同的数据产品。
| 比较维度 | 历史行情数据服务 | 券商 Level-2 行情 |
|---|---|---|
| 主要用途 | 历史研究、指标计算、策略回测 | 盘中观察、盘口分析 |
| 数据重点 | 长周期 K 线、历史序列 | 实时盘口及相关行情 |
| 历史深度 | 取决于具体订阅权限 | 取决于券商提供的服务 |
| Python 接入 | 取决于 API 能力 | 取决于券商接口权限 |
| 使用授权 | 需要确认数据使用范围 | 需要确认账户和行情授权 |
因此,某项历史数据服务比券商 Level-2 价格更高,并不能直接说明它不合理。
真正值得量化开发者计算的是总成本。
假设某项数据服务年费为 2000 元,同时开发者每月需要投入 4 小时维护接口,按每小时 100 元估算:
年总成本 = 2000 + 4 × 12 × 100 = 6800 元。
这里的维护时间和人力单价只是用于说明成本模型的假设,并非实际统计结果。
如果替代数据源能够满足研究需求,减少不必要的订阅项目,开发者就有机会调整预算。
不过,不能因为一个 SDK 提供了历史 K 线接口,就认为所有数据都可以免费获取。可访问的数据范围、调用限制及收费情况,仍然需要根据实际服务权益确认。
分步骤解决:使用 QuantDash 进行实践
下面以单只股票的日线研究为例,看看如何建立不依赖某一家数据提供商的基础行情处理流程。
第 1 步:安装 SDK 并初始化客户端
安装 QuantDash:
# 在 Python 环境或终端中执行
# pip install quantdash
实际安装命令为 pip install quantdash。
初始化客户端:
import os
from quantdash import QuantDash
api_key = os.getenv("QUANTDASH_API_KEY")
if not api_key:
raise RuntimeError(
"请先设置 QUANTDASH_API_KEY 环境变量"
)
try:
qd = QuantDash(api_key=api_key)
except Exception as exc:
raise RuntimeError(
f"QuantDash 客户端初始化失败: {exc}"
) from exc
API Key 使用环境变量管理,避免直接写入代码仓库。
初始化成功只表示客户端对象已经创建,并不代表账户一定具备所有市场和数据类型的访问权限。
第 2 步:获取与主题相关的数据
对于只需要研究日频策略的开发者,可以先获取单只股票的历史日 K 线。
import pandas as pd
symbol = "600519.SH"
try:
df = qd.klines.get(
symbol,
period="1d",
count=100,
adjust="forward",
to_dataframe=True,
)
except Exception as exc:
raise RuntimeError(
f"{symbol} 历史行情获取失败: {exc}"
) from exc
if not isinstance(df, pd.DataFrame):
raise TypeError("行情返回结果不是 DataFrame")
if df.empty:
raise ValueError("行情数据为空,请检查权限或参数")
print("返回字段:", df.columns.tolist())
print("数据行数:", len(df))
print(df.head())
这里使用的几个参数:
- period=“1d”:获取日线数据。
- count=100:请求最近 100 根 K 线。
- adjust=“forward”:使用前复权价格。
- to_dataframe=True:将返回结果转换为 Pandas DataFrame。
需要注意,前复权价格适合部分连续价格序列研究,但不是历史真实成交价格。
代码中的 close 等字段必须通过实际返回结果确认,不能默认不同数据提供商的字段完全一致。
第 3 步:处理数据并展示核心逻辑
获取行情以后,可以计算一个简单的均线研究指标。
以下代码承接第二步的 df。示例假设返回结果包含 close 字段;如果实际名称不同,需要先完成字段映射。
import numpy as np
import pandas as pd
if "close" not in df.columns:
raise KeyError(
f"缺少 close 字段,实际字段: {df.columns.tolist()}"
)
close = pd.to_numeric(
df["close"],
errors="coerce"
)
if close.isna().any():
raise ValueError("收盘价包含缺失值或非数值")
if not np.isfinite(close.to_numpy()).all():
raise ValueError("收盘价包含无穷大")
if (close <= 0).any():
raise ValueError("收盘价存在非正数")
# 均线计算需要保证时间顺序
# 以下代码要求 df 已按交易时间升序排列
# 排序字段需根据实际返回结构确认
if len(df) < 20:
raise ValueError("计算20日均线至少需要20条记录")
result = df.copy()
result["close"] = close
result["ma5"] = close.rolling(
window=5,
min_periods=5
).mean()
result["ma20"] = close.rolling(
window=20,
min_periods=20
).mean()
result["above_ma20"] = (
result["ma5"] > result["ma20"]
)
print(result[[
"close",
"ma5",
"ma20",
"above_ma20"
]].tail())
这里有一个重要前提:K 线必须先按照真实交易时间升序排列。
由于不同 SDK 版本和数据接口可能使用不同的日期字段,实际运行时应先检查返回结构,再使用正确的日期字段排序。
如果没有确认时间顺序,计算出来的均线可能看起来正常,但实际上毫无研究意义。
第 4 步:展示完整业务示例或进阶处理
选择行情数据源时,不能只检查能否成功请求,还应验证价格序列的基本质量。
下面构造一个可复用的数据质量检查函数。
import numpy as np
import pandas as pd
def check_daily_prices(data):
required = ["open", "high", "low", "close"]
missing = [
col for col in required
if col not in data.columns
]
if missing:
raise ValueError(
f"缺少必要字段: {missing}"
)
if data.empty:
raise ValueError("输入行情为空")
prices = data[required].apply(
pd.to_numeric,
errors="coerce"
)
invalid_number = ~np.isfinite(
prices.to_numpy()
)
non_positive = (
prices.to_numpy() <= 0
)
invalid_high = (
prices["high"] <
prices[["open", "low", "close"]].max(axis=1)
)
invalid_low = (
prices["low"] >
prices[["open", "high", "close"]].min(axis=1)
)
return {
"total_rows": len(data),
"invalid_number_rows": int(
invalid_number.any(axis=1).sum()
),
"non_positive_rows": int(
non_positive.any(axis=1).sum()
),
"invalid_high_rows": int(
invalid_high.sum()
),
"invalid_low_rows": int(
invalid_low.sum()
),
}
report = check_daily_prices(df)
print("行情质量检查结果:")
for key, value in report.items():
print(key, value)
这段代码由业务层自行执行,并不是 SDK 自动提供的数据审计功能。
此外,OHLC 价格关系检查属于基础结构检查,不能代替复权一致性检查。若不同价格字段使用了不同调整口径,还需要单独排查。
实际部署时,还需要增加交易日期重复检查、缺失交易日检查和数据版本记录。
关键坑与注意事项
- 不要把 2000 元理解为所有接口的统一价格。 不同数据类型可能对应不同权限,实际购买前应核对权益、期限和退款条款。
- 历史行情不能直接替代 Level-2。 日线和分钟 K 线不能提供所有逐笔成交、逐笔委托或订单簿信息。
- 复权方式必须明确。 前复权、后复权和不复权的价格序列不同,混合计算容易产生错误指标。
- 返回成功不等于数据完整。 即使接口没有报错,也需要检查交易日期和预期数量。
- 不要直接信任价格字段。 数值转换、非正价格、缺失数据和极端波动都需要验证。
- 注意数据授权边界。 个人研究、企业使用、对外展示和数据再分发可能具有不同的许可要求。
- 回测需要保存数据版本。 如果历史行情后来发生修订,缺少原始数据快照就可能无法复现当时的研究结果。
常见问题(FAQ)
Q:Tushare 收费 2000 元一年合法吗?
A:收费本身不当然违法。是否合法,需要结合服务协议、收费信息披露、用户权益、合同变更、数据授权等具体事实判断。如果涉及原有服务条款变更或争议收费,应保留购买页面、协议版本和交易记录,必要时咨询专业法律人士。
Q:2000 元的历史行情服务和券商 Level-2 谁更划算?
A:无法仅凭价格判断。历史策略研究更关注数据跨度、复权和历史完整性;盘口策略更关注实时深度、延迟和逐笔数据。
Q:QuantDash 可以直接替代 Tushare 吗?
A:对于已确认支持的历史 K 线获取场景,可以作为候选数据源。但接口字段、覆盖市场、历史范围、更新速度及实际权限需要逐项验证,不能假定全部兼容。
Q:为什么不同数据源计算的均线会不一样?
A:常见原因包括复权方式不同、停牌处理不同、缺少交易日、数据修订以及时间顺序不一致。
Q:只做个人日线回测,有必要购买高价数据吗?
A:应先明确标的数量、历史跨度、数据种类和使用频率。如果现有合法授权的数据已经满足需求,就不一定需要额外购买更高等级的数据服务。
小结
Tushare 收费 2000 元一年的争议,表面上是价格问题,实际上涉及数据产品差异、使用授权和开发者的长期成本。单纯拿它与券商 Level-2 比价,并不能得出完整结论。对于量化研究,数据是否符合实际策略需求,比名义价格更值得关注。
QuantDash Python SDK 可以作为历史行情数据接入的一种实现方式,帮助开发者获取 K 线并开展后续指标计算。但数据质量检查、字段适配、复权验证、授权确认和历史版本管理仍然需要由工程系统完成。只有建立可检查、可替换的数据接入流程,才能降低对单一供应商的依赖,提高研究结果的可靠性。
参考
QuantDash Python SDK 文档:https://docs.quantdash.net/zh-Hans/sdk/python-quickstart
282



被折叠的 条评论
为什么被折叠?



