背景
无论是做量化交易还是行情分析,数据是少不了的。数据来源一般有三种
- 券商自己的数据源
这方面境外券商做的好一点,基本都开放了 api,申请很容易,只是有些限制,例如长桥的每月标的限制:
| 用户类型 | 每月可查询的标的数量上限(只) |
|---|---|
| 用户开户 | 100 |
| 总资产达 1 万 HKD | 400 |
| 总资产达 8 万 HKD | 600 |
| 总资产达 40 万 HKD 或 月交易笔数大于 160 笔 | 1000 |
| 总资产达 400 万 HKD 或 月交易笔数大于 1600 笔 | 2000 |
| 总资产达 600 万 HKD 或 月交易笔数大于 2500 笔 | 3000 |
而国内的券商一般需要单独开通量化功能,且数据接口的费用动辄要上万/年。
- 数据供应商
网上可以找到一些专门的数据供应商,可提供按月或者按量收费。下图为国内某公司提供的数据接口:
国外的则更贵一些:
- 开源数据仓库
一般是在公开网站上抓取所需要的信息,不保证稳定性,能提供的接口有限。同时需要自行搭建。如果你像笔者一样贫穷,这可能是唯一选项。
akshare,数据主要来源于国内各类财经网站,维护较为频繁,文档也比较丰富。他最大的好处是提供了 api 库,可以很方便的自行部署 api。
yfinance,数据来源于 Yahoo! Finance。算是国外最大的股票数据仓库了,目前 16k stars。可惜没有提供 api 库。没关系,本文会介绍如何封装成 api。
ranaroussi/yfinance: Download market data from Yahoo! Finance's API
安装 yfinance
创建 python 虚拟环境
python3 -m venv yfinance
安装 yfinance
./yfinance/bin/pip install yfinance
开启 python 命令行
./yfinance/bin/python3
测试输出股票历史数据
import yfinance as yf
dat = yf.Ticker("MSFT")
dat.history(period='1mo')
如果你能输出下图,那就成功了。
但如果有报错 yfinance.exceptions.YFRateLimitError: Too Many Requests. Rate limited. Try after a while.,那大概率是 ip 被封了,需要尝试更换服务器或者 ip。
安装 fastapi
安装 fastapi
./yfinance/bin/pip install "fastapi[standard]"
创建文件 main.py
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def read_root():
return {"Hello": "World"}
开启服务
./yfinance/bin/fastapi dev ./yfinance/main.py
测试接口
curl http://localhost:8000
如果能显示 {"Hello":"World"} 就成功了。
用 fastapi 封装 yfinance
新增一个接口,提供参数 symbol, startdate, enddate, interval 分别表示股票代码,开始日期,结束日期,间隔。具体的格式要求请参考 yfinance 代码内注释。
@app.get("/history")
async def history(symbol: str, startdate: str, enddate: str, interval: str):
获取数据
dat = yf.Ticker(symbol)
df = dat.history(start=startdate, end=enddate, interval=interval)
这里获取的数据是 DateFrame 格式,时间为 index。为了后续的使用,我们需要将时间转为新的一列
df = df.reset_index()
再将表格转为 json 格式返回。这里的 orient="records" 代表输出方式,详见官方文档。epoch 表示时间格式为时间戳
json_str = df.to_json(orient="records", date_format='epoch')
return Response(content=json_str, media_type='application/json')
完整的代码如下
from fastapi import FastAPI, Response
import yfinance as yf
app = FastAPI()
@app.get("/history")
async def history(symbol: str, startdate: str, enddate: str, interval: str):
dat = yf.Ticker(symbol)
df = dat.history(start=startdate, end=enddate, interval=interval)
df = df.reset_index()
json_str = df.to_json(orient="records", date_format='epoch')
return Response(content=json_str, media_type='application/json')