/ 交易 / 23浏览

搭建 Binance OHLCV 本地数据库

背景

做量化分析的第一步就是数据,而 OHLCV 是基础的数据。刚开始非常简单,直接从 Binance API 拉取数据,保存为 hdf5 文件就可以了。但问题随之来临:

  1. 无法更新数据
  2. 所有数据都放在一个 hdf5 文件,占用太多内存
  3. 所有数据都在一个 hdf5 文件,每次加载时间太长

思考再三,更新数据还算好办。因为我本来就是用相关 framework 去下载数据,所以直接 pickle 下载的对象,下次 update 就可以了。但内存和加载时间就不好弄了。对于像 pandas 这类库,它每次加载都会加载所有的数据,意味着内存一下子就会占去非常多。其次是对于 timeframe 比较小的数据,比如 1min 的数据,加载速度其实算不上快,即使用 SSD。参考了别人一些做法,比如用 Dask 的 dataframe 可以支持内存映射,但受限于我目前使用的量化分析库都不只是 Dask dataframe 等,因此不兼容。所以生态圈意味着我只能使用 pandas 的 dataframe。

解决目标

不过在众多选择中,我明确了一下需求:

  1. 首先是拉取数据过程必须不依赖大内存,而是存磁盘。毕竟是家用 PC,只有一台,不可能花费大量内存就用于下载数据,内存需要用在更加重要的分析上。所以读取整个 dataframe 再更新的方法被否决了。
  2. 其次下载的数据都是“源数据”,意味着它可能是其他很多工具的上游,必须保证多个程序同时访问,因此必须有服务器,从而能在不同的 k8s worker 里面读取,因此 DuckDB 等文件类型的数据库就不考虑了。
  3. 要足够简单,生态丰富。关于数据库,有很多选择比如 Clickhouse,TimescaleDB 或者 InfluxDB 等。但这些都比较复杂,而且 SQL 不标准。另外考虑到 Binance OHLCV 即使以 BTCUSDT 的 1min 数据为例,也不过是几百万条,完全说不上是大数据量。

经过最终考虑,决定使用普通的 postgres 来搭建数据库。使用 schema 作为 timeframe 分割,symbol 作为 table name,每个表最多只有几百万条,一下子问题都解决了。而且需要的时候,可以从 postgres 加载成分组的 parquet 文件到本地文件系统,加载速度非常的好。同时这一切生态成熟,有现成的工具。

简单的架构

本着越简单越好的原则,我在本地使用 Kubernetes 来管理这一切,同时使用 Prefect workflow 来管理任务。Prefect 和 Airflow 很类似,但更轻量和更灵活。它的部署只需要 build 好 image,就可以直连 PREFECT_API_URL 运行脚本就可以把信息上传到 server,然后就可以通过 SDK/API 或直接在 Prefect Dashboard 上面点击触发,还可以选择参数比如 timeframe,1min 还是 1h 从而拉取不同 timeframes 的数据。

像很多云原生的工具一样,使用 Kubernetes Job 可以在触发时再创建 Job 来运行,完成后自动退出,类似于一个 serverless batch job,程序每次启动都是重新启动的,可靠度高,避免长期运行带来的各种潜在问题。而且每次创建 Kubernetes Job 都在数秒内,对于 Batch Job 速度已经足够了。

对于异常,它也能自动重试,其次界面非常友善,还能配置 Cron 自动化。

关于数据拉取

数据拉取目前已经有很多选择了,可以是 ccxt,也可以是 python-binance,或者直接用 request 请求 Binance API 都可以,有 rate limit 限制,以前是单 host 1200 reqs/min,现在可以去到 6000 reqs/min。另一种方法是 websocket,无连接数限制。但是它只能获取最新的数据,历史数据依然需要 historical API 获取。我选择的是 API 拉取,因为每次拉取 1000 rows 就会 upsert db,保证低内存消耗。

其次,测试后使用了 5 个线程拉取,足够跑满 rate limit 的限制。其次做了全局 rate limit 的计数,避免拉取过快导致超出 rate limit 的错误。

防火墙问题

关于中国大陆的防火墙问题,无法直连 Binance API 有两个方案(其实即使没有防火墙也会有问题,因为 Binance 不在中国经营,因为法规也会屏蔽中国 IP 的,所以在大陆拉取数据无论如何你都需要代理)。基本就两个方法

使用稳定的代理来直接拉取数据到本地

这是我目前的做法,理由是简单而且我能找到高速可靠的代理。直接拉到本地,任务就算完成了。即使代理偶尔不稳,由于拉取数据的 workflow 有自动重试功能,所以很少遇到问题。再次即使出现意外,程序本身也会根据已有数据做增量拉取,所以就算有问题也只需重跑即可。

先把数据拉取到 VPS,再拉取到本地

这个的确是一个办法,也是一开始的做法,但实际发现并不划算。首先由于是拉数据,所以 vps 必须大磁盘,大磁盘并不便宜。同时由于我还是要拉到本地,所以存在 vps 的数据大部分在第一次同步到本地后其实是没有意义的。

其次大宽带的 vps 很贵。由于在大陆,所以香港 vps 速度很好,但问题就是带宽非常贵,不然你会拉数据非常慢。因此这也是一个很大的成本。

最后我还需要 workflow 等来管理 tasks,对于 vps 再在上面安装 k8s 或 k3s 等,其实也需要一些 overhead,同时 workflow 也无法给我其他 tasks 使用,性价比很低。

综合算下来,直接拉到本地要直接了当得多。

总结

OHLCV 数据功能基本就解决了,它所需要的功夫其实超出了我的预期。由于需要处理小 timeframe 数据(1min),Infra 的要求一下子就提高了。对于大的 timeframe,比如 1h 或 1d,其实每次全量下载成 hdf5 或 parquet 也非常快。但对于量化分析来说,timeframe 就成了瓶颈,这个问题值得花费一点功夫解决。

0

  1. This post has no comment yet

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注