Polygon API Python 完全指南:批量抓取加密、股票、期货、外汇历史行情

·

关键词:Python、Polygon API、历史行情、K线、加密货币、股票、期货、外汇、RESTClient、pandas

自2019 年以来,Polygon 以 实时、低延迟、全栈覆盖 成为量化交易与数据科学圈最受欢迎的 市场数据 API 提供商。本文手把手教你 纯 Python 方式 连接 Polygon,5 分钟完成环境配置、代码封装,并轻松批量获取加密、股票、期货、外汇的 分钟级历史 K 线,做到 开箱即用


1. 快速安装:三步打包 Polygon

# 1. 创建虚拟环境
python -m venv polygon_env
source polygon_env/bin/activate  # Windows 用 polygon_env\Scripts\activate

# 2. 安装 Polygon 官方客户端
pip install -U polygon-api-client

# 3. 准备 API Key
# 将 Key 保存到 local_settings.py,便于安全 gitignore
# local_settings.py 内容:
# polygon = {"api_key": "YOUR_API_KEY_HERE"}
👉 获取 Polygon API 官方文档与实时行情预览 无需注册即可查看示例代码。

2. 拓展 RESTClient:自带重试、超时控制

Polygon SDK 的 RESTClient 使用简单,但生产环境需要 重试策略网络超时。我们继承并额外嵌入 Retry Adapter 以实现 429 与 5xx 场景的无感重试。

from datetime import date, datetime
import pandas as pd
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
from polygon import RESTClient
from local_settings import polygon as settings

ALLOWED_MARKETS = ['crypto', 'stocks', 'fx', 'futures']  # 支持市场列表

class RobustPolygon(RESTClient):
    """带智能重试的 Polygon 客户端"""
    def __init__(self, auth_key: str = settings['api_key'], timeout: int = 10):
        super().__init__(auth_key, connect_timeout=timeout)
        retry = Retry(
            total=10,
            backoff_factor=0.5,
            status_forcelist=[429, 500, 502, 503, 504],
            raise_on_status=False,
        )
        adapter = HTTPAdapter(max_retries=retry)
        self._session.mount('https://', adapter)

3. 轻松抓取资产列表

交易系统的第一步,是建立资产清单。Polygon 的 reference_tickers_v3 能一次性返回所有股票、加密、外汇、期货的 代号、名称、市场状态。以下封装自动去重并精简字段:

import warnings, itertools, math

class RobustPolygon(RobustPolygon):
    def get_tickers(self, market: str) -> pd.DataFrame:
        if market not in ALLOWED_MARKETS:
            raise ValueError(f"market 必须是 {ALLOWED_MARKETS} 之一。")

        resp = self.reference_tickers_v3(market=market)
        df_list = []
        while True:
            if not hasattr(resp, 'results'):
                break
            df_list.extend(resp.results)
            if not hasattr(resp, 'next_url'):
                break
            resp = self.reference_tickers_v3(next_url=resp.next_url)

        df = pd.DataFrame(df_list)
        if market == 'crypto':  # 仅保留 USD 计价
            df = df[df['currency_symbol'] == 'USD']
            df['name'] = df['base_currency_name']
        df = df[['ticker', 'name', 'market', 'active']].drop_duplicates('ticker')
        return df.reset_index(drop=True)

# 实战:拉取加密货币列表
cli = RobustPolygon()
crypto_df = cli.get_tickers('crypto')
print(crypto_df.head(10))

返回片段:

        ticker               name  market  active
0  X:1INCHUSD              1inch   crypto    True
1   X:AAVEUSD              Aave    crypto    True
15   X:BTCUSD            Bitcoin   crypto    True
26   X:ETHUSD           Ethereum   crypto    True

👉 点击预览 BTC 实时分钟级成交量与价格衍化曲线 直接命中交集行情。


4. 精准下载 K 线:一张代码刷全量

Polygon 聚合接口 crypto_aggregatesstocks_aggregates 支持 日、分钟、秒级 自定义粒度。以下通用封装解决三个痛点:

import time

class RobustPolygon(RobustPolygon):
    def get_bars(self,
                 market: str,
                 ticker: str,
                 multiplier: int = 1,
                 timespan: str = 'minute',
                 frm: str | date = None,
                 to: str | date = None) -> pd.DataFrame:
        # 检查参数
        if market not in ALLOWED_MARKETS:
            raise ValueError(f"market 必须是 {ALLOWED_MARKETS} 之一。")
        if not ticker:
            raise ValueError("ticker 不能为空。")
        frm = frm or date(2000, 1, 1)
        to = to or date.today()

        # 统一日期格式字符串
        start_str = frm.strftime('%Y-%m-%d') if isinstance(frm, date) else frm
        end_str = to.strftime('%Y-%m-%d') if isinstance(to, date) else to

        # 选择对应市场聚合端点
        if market == 'crypto':
            api_fn = self.crypto_aggregates
        elif market == 'stocks':
            api_fn = self.stocks_aggregates
        else:
            raise NotImplementedError("当前仅支持 crypto 与 stocks。")

        bars, last_ts = [], 0
        while True:
            resp = api_fn(ticker, multiplier, timespan, start_str, end_str, limit=50000, sort='asc')
            if not hasattr(resp, 'results') or not resp.results:
                break
            bars.extend(resp.results)
            last_ts = resp.results[-1]['t']
            start_str = datetime.utcfromtimestamp(last_ts / 1000).strftime('%Y-%m-%d')
            if len(resp.results) < 50000:
                break
            # 轻微限制速率,避免 429
            time.sleep(0.05)

        if not bars:
            return pd.DataFrame(columns=['date', 'open', 'high', 'low', 'close', 'volume'])

        df = pd.DataFrame(bars)
        df['date'] = pd.to_datetime(df['t'], unit='ms')
        # 标准化列名
        rename_map = {'o': 'open', 'h': 'high', 'l': 'low', 'c': 'close', 'v': 'volume'}
        df = df.rename(columns=rename_map)
        return df[['date', 'open', 'high', 'low', 'close', 'volume']].reset_index(drop=True)

# 案例:抓取 2021-01-01 至 2021-12-31 BTC 1 分钟 K 线
btc_df = cli.get_bars('crypto', 'X:BTCUSD', timespan='minute', frm='2021-01-01', to='2021-12-31')
print(btc_df.shape, btc_df.date.min(), btc_df.date.max())
# -> (524160, 2021-01-01 00:00:00, 2021-12-31 23:59:00)

5. FAQ|90% 新手疑问一站式解答

Q1:免费额度够用吗?
免费层每日命中 5 次聚合请求,仅能抓 2 年加密分钟级数据。付费层约 0.0001 美元 / 请求,百美元即可吃透全部加密历史,性价比极高。

Q2:为何提示 429?
你已触发速率限制。RobustPolygon 自带 Retry 已经优化,但仍需注意 单次 50k 上限。切记循环中 sleep。

Q3:秒级数据时间戳精度是多少?
毫秒级,但在股票时间 9:30:00.001 与 9:30:00.002 即有两条。可再参数 timestamp_round='second' 自行压缩。

Q4:证券交易所加密交易所** 的粒度差异?
Polygon 加密端默认 UTC;美股端默认为美东(EST/EDT);调用 df['date'] = df['date'].dt.tz_localize('UTC') 后自行转换即可。

Q5:如何批量更新?
封装 check_last_record断点续传,再配 date.today() 每日 cron。把 DataFrame 写至 Parquet,避免重复 CSV。

Q6:如何接入 Qlib、backtrader?
Polygon 直接输出 pandas DataFrame,存 numpy.savezarr 即可。一套代码适配任何 机器学习框架


6. 实战加速:10 行代码跑回测

分钟数据高频能级标记 转存为 OHLC 文件,送入 backtrader 回测仅需 10 行。

df = cli.get_bars('crypto', 'X:ETHUSD', 5, 'minute', '2023-01-01', '2023-03-31')
df.to_csv('ETHE_min5.csv', index=False)

# backtrader 示例省略 …

7. 结语:把 Polygon 当作 “数据高速公路”

本文仅用两个扩展函数就把 Polygon 100% 实用场景 打通:

不要犹豫,现在就用 3 分钟跑通第一条策略
👉 立刻体验零门槛行情订阅,马上用布林带脚本抓下一轮 Alpha。