一句话回答: 调试策略时反复跑同一段取数代码,会重复请求同样的 K 线——既慢又耗额度。给 AlphaFeed 的取数包一层本地缓存:先按「代码+周期+复权+日期」查本地 Parquet,命中就直接读盘、未命中再请求并落盘。几十行就能把重复请求降到零,离线也能复用历史数据。
写策略/回测时,同一只股票的历史日线你可能一天跑几十遍。每次都真请求:① 慢(网络往返);② 费额度(免费额度有限);③ 断网就没法调试。解决办法是加一个「读穿透」缓存层:有缓存用缓存、没有才请求,历史日线这种「已收盘就不再变」的数据尤其适合缓存。
from alphafeed import AlphaFeed
from pathlib import Path
import pandas as pd
af = AlphaFeed() # 读取 ALPHAFEED_API_KEY
CACHE_DIR = Path("./af_cache")
CACHE_DIR.mkdir(exist_ok=True)
def cache_path(symbol, period, adjust, count):
# 用参数拼出唯一文件名,参数不同就是不同缓存
safe = symbol.replace(".", "_")
return CACHE_DIR / f"{safe}__{period}__{adjust}__{count}.parquet"缓存键要素 | 为什么要纳入 |
|---|---|
| 不同标的不同数据 |
| 日线/分钟线不同 |
| 复权方式不同,价格不同 |
| 取的根数不同 |
def get_klines_cached(symbol, period="1d", adjust="forward", count=250,
max_age_hours=12):
p = cache_path(symbol, period, adjust, count)
# 缓存存在且未过期 → 直接读盘
if p.exists():
age_h = (pd.Timestamp.now().timestamp() - p.stat().st_mtime) / 3600
if age_h <= max_age_hours:
return pd.read_parquet(p)
# 未命中/过期 → 真请求并落盘
df = af.klines.get(symbol, period=period, adjust=adjust,
count=count, to_dataframe=True)
df.to_parquet(p, engine="pyarrow")
return df参数 | 含义 | 建议 |
|---|---|---|
| 缓存有效期 | 盘后历史数据可设大(如 12/24h);盘中实时勿缓存 |
import time
t0 = time.perf_counter(); a = get_klines_cached("600519.SH"); t1 = time.perf_counter()
b = get_klines_cached("600519.SH"); t2 = time.perf_counter()
print(f"首次(请求+落盘) {t1-t0:.3f}s | 二次(命中缓存) {t2-t1:.3f}s")
print("两次数据一致:", a.equals(b))首次请求并落盘,第二次直接读 Parquet——通常快一个数量级,且不消耗 API 额度。
def get_many_cached(codes, **kw):
out, missing = {}, []
for c in codes:
p = cache_path(c, kw.get("period", "1d"), kw.get("adjust", "forward"),
kw.get("count", 250))
if p.exists():
out[c] = pd.read_parquet(p)
else:
missing.append(c)
# 只对未缓存的批量请求,命中的直接复用
if missing:
fetched = af.klines.batch(missing, period=kw.get("period", "1d"),
adjust=kw.get("adjust", "forward"),
count=kw.get("count", 250), to_dataframe=True)
for c, df in fetched.items():
df.to_parquet(cache_path(c, kw.get("period", "1d"),
kw.get("adjust", "forward"), kw.get("count", 250)))
out[c] = df
return out
data = get_many_cached(["600519.SH", "000001.SZ", "601318.SH"])
print("拿到:", list(data.keys()))symbol/period/adjust/count 缺一不可,否则会串数据。max_age_hours 设 12/24h 较稳;跨交易日应失效重取当日新数据。forward 前复权价在发生新除权后历史值会调整,长缓存要在除权后主动失效或缩短有效期。quotes/depth 是实时的,缓存意义不大且易误用。Q:缓存用 Parquet 还是 pickle?
A:推荐 Parquet(跨语言、压缩、保留 dtype);pickle 简单但不通用、有安全与版本兼容问题。
Q:怎么强制刷新缓存?
A:把 max_age_hours 设 0,或删除对应缓存文件,下次调用就会重新请求并落盘。
Q:SDK 自己有缓存吗?为什么还要我做?
A:本地缓存是「你的调试/回测复用层」,把重复请求降到零、支持离线;与 SDK 内置的重试/限流处理是两回事,互补。
Q:会不会读到过期的当日数据?
A:会——所以盘中/当日未收线别用长缓存。历史已收盘数据才适合缓存;跨交易日让缓存失效。
Q:需要付费吗?
A:缓存是本地能力、不涉及付费;它恰恰帮你省 API 额度。取数本身的额度见定价页。
给取数加本地缓存,是策略调试与回测的性价比之选:按「代码+周期+复权+根数」做缓存键,命中读 Parquet、未命中再请求并落盘,批量时只请求缺失标的。记住「只缓存已收盘的历史数据、设好过期、除权后失效」,几十行就能把重复请求降到零,既省 AlphaFeed 额度又提速。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。