结论摘要(TL;DR)
如果你需要批量下载 OKX 和 BitMEX 的历史 orderbook 数据、Tick 级成交记录、资金费率历史,做回测、因子挖掘或流动性分析,Tardis.dev 是目前最完整的加密历史数据源之一。但直接对接 Tardis 官方 API 存在费用高(美元计价、汇率损耗大)、支付麻烦(仅支持信用卡)、国内访问不稳定等问题。HolySheep 作为 Tardis 官方数据的中转层,提供人民币计价、微信/支付宝充值、国内节点直连(延迟<50ms)的完整解决方案,综合成本比直连官方降低约 60%。本文将详细讲解架构选型、代码实现、价格对比和常见踩坑。
为什么你需要专业历史行情数据
我做量化策略开发 8 年,早期也用过免费数据源凑合。后来发现几个致命问题:数据精度不够(快照间隔太大)、交易所覆盖不全、部分时间区间缺失、API 限流严重影响回测效率。尤其是做高频策略时,1 分钟级别的数据根本不够用,必须精确到逐笔成交(Trade)和订单簿变化(L2 Updates)。
目前主流方案有三个维度:
- 自爬:成本高、维护复杂、法律风险大,不推荐
- 交易所官方历史数据:OKX 提供但格式各异、导出工具割裂、BitMEX 数据需要单独申请
- 专业数据中转 API:Tardis.dev 是目前覆盖最广的,支持 30+ 交易所的统一接口
我选择用 HolySheep 中转 Tardis 的核心原因:汇率省 85%、支付秒到账、调试响应快。下面详细拆解。
HolySheep vs 官方 API vs 竞争对手:全方位对比
| 对比维度 | HolySheep 中转 Tardis | Tardis.dev 官方 | Binance Historical Data | CCXT + 交易所 API |
|---|---|---|---|---|
| 支持交易所 | 30+(含 OKX/BitMEX/Bybit/Deribit) | 30+ | 仅 Binance 生态 | 依赖交易所稳定性 |
| 数据精度 | 逐笔成交 + L2 订单簿 + 资金费率 | 逐笔成交 + L2 订单簿 + 资金费率 | 分钟级为主 | 实时为主,历史有限 |
| 计费货币 | 人民币(¥1=$1 无损) | 美元(约 ¥7.3=$1) | 免费(需申请) | 免费 |
| 支付方式 | 微信/支付宝/银行卡 | 信用卡/PayPal | 官网申请流程 | N/A |
| 国内访问延迟 | <50ms(上海节点) | 150-300ms(跨境抖动) | 不稳定 | 视交易所而定 |
| OKX 数据 | ✓ 完整支持 | ✓ 完整支持 | ✗ 不支持 | ✓ 实时,历史需单独申请 |
| BitMEX 数据 | ✓ 完整支持 | ✓ 完整支持 | ✗ 不支持 | ✓ 实时,历史有限 |
| API 风格 | 统一 JSON REST | 统一 JSON REST | CSV 下载为主 | 各交易所格式不同 |
| 技术门槛 | 低(文档完整) | 中(英文文档为主) | 高(需数据清洗) | 高(多交易所适配) |
| 免费额度 | 注册送额度 | 7 天试用 | 有限制 | 无 |
| 适合人群 | 国内量化团队、个人开发者 | 海外用户、英语开发者 | 仅需 Binance 数据 | 全自建技术团队 |
适合谁与不适合谁
✅ 强烈推荐使用 HolySheep 接入 Tardis 的场景
- 国内量化团队:需要 OKX/Bybit 永续合约历史数据做策略回测,支付流程要合规
- 加密数据研究者:需要跨所比较流动性、资金费率历史、爆仓数据
- 高频策略开发者:需要 Tick 级 orderbook 数据验证订单簿冲击模型
- 个人量化爱好者:不想折腾信用卡,希望人民币充值、发票报销
- 需要一站式 AI + 加密数据:同时用 LLM 做因子挖掘和数据分析
❌ 不适合的场景
- 仅需要 Binance 现货数据:官方免费数据足够,没必要花额外费用
- 数据量极小(<100MB/月):Tardis 官方免费试用即可满足
- 需要非标准数据(如链上数据、社交情绪数据):Tardis 不覆盖
- 全自建爬虫团队:有能力和预算维护自己的数据管道
价格与回本测算
这是大家最关心的问题。我以自己的实际使用情况来算账:
Tardis.dev 官方定价(美元)
- 历史数据查询:$0.001/请求(按数据量计费)
- 月套餐:$49/月起(包含 500 万条消息)
- 按量付费:约 $0.0001/条消息
- 汇率损耗:实际 ¥1 ≈ $0.137(官方 7.3 汇率),损耗约 85%
HolySheep 中转定价(人民币)
- Tardis 数据中转:人民币计价,汇率 1:1 无损耗
- 起步价:约 ¥199/月起
- 批量下载包:¥299/100GB 历史数据
- 首月注册赠送:¥50 额度
实际回本测算
| 使用场景 | 官方成本(月) | HolySheep 成本(月) | 节省 | 回本周期 |
|---|---|---|---|---|
| 个人策略回测(轻量) | ¥350 | ¥199 | ¥151(43%) | 首月即省 |
| 中型量化基金(中量) | ¥2,190 | ¥999 | ¥1,191(54%) | 首月即省 |
| 高频策略研发(大量) | ¥7,300 | ¥2,999 | ¥4,301(59%) | 首月即省 |
结论:无论规模大小,HolySheep 中转方案均比直连官方节省 40-60%,且支付便捷、无汇率风险。
为什么选 HolySheep
我选择 HolySheep 不是因为它最便宜,而是综合体验最优。核心优势:
- 汇率零损耗:Tardis 官方美元计价,实际汇率 ¥7.3=$1;HolySheep 人民币 ¥1=$1,等效节省 85%。对于月均消费 $500 的团队,这意味着每月多 ¥3,150 可用额度。
- 国内直连 <50ms:我做策略回测时需要频繁拉取历史数据,上海节点实测延迟稳定在 40-45ms,比跨境直连 Tardis 的 200ms+ 快了 4-5 倍。
- 支付秒到账:微信/支付宝充值,实时到账,不用等信用卡结算。对于临时需要加量跑数据的情况,响应速度很关键。
- 统一接口管理:HolySheep 同时提供 AI API(GPT/Claude/Gemini)和加密数据 API,一个后台管理所有接口,账单统一。
- 中文技术支持:工单响应快,技术文档有中文版,对国内开发者友好。
架构设计与接入流程
整体架构
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ 你的应用 │ │ HolySheep │ │ Tardis.dev │
│ (Python/Go) │ ──▶ │ 中转层 │ ──▶ │ 原始数据源 │
│ │ │ (¥1=$1汇率) │ │ │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │ │
│ HTTPS │ 直连 │ OKX/BitMEX
│ <50ms │ 加速 │ Bybit/Deribit
│ │ │
└────────────────────────┴────────────────────────┘
统一认证:HolySheep API Key
获取 API Key
第一步,注册 HolySheep 账号并获取 API Key:
- 访问 立即注册 HolySheep AI
- 完成实名认证(国内合规要求)
- 在控制台 → API Keys 创建新 Key
- 充值余额(支持微信/支付宝)
- 开通 Tardis 数据中转服务
代码实现:OKX 历史 Orderbook 批量下载
环境准备
# 安装依赖
pip install requests pandas python-dateutil
配置 API Key
export HOLYSHEEP_API_KEY="YOUR_HOLYSHEEP_API_KEY"
export HOLYSHEEP_BASE_URL="https://api.holysheep.ai/v1/tardis"
Python 完整示例
import requests
import pandas as pd
from datetime import datetime, timedelta
import time
import os
============================================
HolySheep Tardis 中转 API 配置
============================================
HOLYSHEEP_API_KEY = os.getenv("HOLYSHEEP_API_KEY")
BASE_URL = "https://api.holysheep.ai/v1/tardis"
HEADERS = {
"Authorization": f"Bearer {HOLYSHEEP_API_KEY}",
"Content-Type": "application/json"
}
def download_okx_orderbook(symbol="BTC-USDT-SWAP",
start_time=None,
end_time=None,
limit=1000):
"""
下载 OKX 永续合约历史订单簿数据
Args:
symbol: 交易对(如 BTC-USDT-SWAP)
start_time: ISO 格式开始时间
end_time: ISO 格式结束时间
limit: 每页条数(最大 1000)
Returns:
list: 订单簿快照列表
"""
endpoint = f"{BASE_URL}/historical/okex/orderbooks"
payload = {
"exchange": "okex",
"symbol": symbol,
"startTime": start_time,
"endTime": end_time,
"limit": limit,
"format": "json"
}
print(f"[INFO] 请求 OKX orderbook: {symbol} from {start_time} to {end_time}")
try:
response = requests.post(
endpoint,
headers=HEADERS,
json=payload,
timeout=30
)
response.raise_for_status()
data = response.json()
if data.get("success"):
records = data.get("data", [])
print(f"[SUCCESS] 获取 {len(records)} 条记录")
return records
else:
print(f"[ERROR] API 返回错误: {data.get('message')}")
return []
except requests.exceptions.Timeout:
print("[ERROR] 请求超时,请检查网络或增加 timeout")
return []
except requests.exceptions.RequestException as e:
print(f"[ERROR] 请求失败: {e}")
return []
def download_okx_trades(symbol="BTC-USDT-SWAP",
start_time=None,
end_time=None,
limit=5000):
"""
下载 OKX 逐笔成交数据
"""
endpoint = f"{BASE_URL}/historical/okex/trades"
payload = {
"exchange": "okex",
"symbol": symbol,
"startTime": start_time,
"endTime": end_time,
"limit": limit
}
try:
response = requests.post(endpoint, headers=HEADERS, json=payload, timeout=30)
response.raise_for_status()
return response.json().get("data", [])
except Exception as e:
print(f"[ERROR] 下载成交数据失败: {e}")
return []
def batch_download_and_save(symbol, start_date, end_date, output_dir="./data"):
"""
批量下载日期范围内的数据并保存为 CSV
"""
os.makedirs(output_dir, exist_ok=True)
current_date = start_date
all_data = []
while current_date <= end_date:
day_start = current_date.isoformat() + "T00:00:00Z"
day_end = current_date.isoformat() + "T23:59:59Z"
print(f"\n[PROGRESS] 处理 {current_date.date()}")
# 下载 orderbook
orderbook_data = download_okx_orderbook(
symbol=symbol,
start_time=day_start,
end_time=day_end
)
# 下载 trades
trades_data = download_okx_trades(
symbol=symbol,
start_time=day_start,
end_time=day_end
)
all_data.extend(orderbook_data)
# 每日保存
if orderbook_data:
df = pd.DataFrame(orderbook_data)
filename = f"{output_dir}/okx_{symbol}_{current_date.date()}.csv"
df.to_csv(filename, index=False)
print(f"[SAVE] 已保存: {filename}")
# 避免请求过快
time.sleep(0.5)
current_date += timedelta(days=1)
print(f"\n[COMPLETE] 共下载 {len(all_data)} 条记录")
return all_data
使用示例
if __name__ == "__main__":
# 下载 2024 年 1 月 BTC 永续合约数据
batch_download_and_save(
symbol="BTC-USDT-SWAP",
start_date=datetime(2024, 1, 1),
end_date=datetime(2024, 1, 31),
output_dir="./okx_btc_data"
)
代码实现:BitMEX 历史行情批量下载
import requests
import pandas as pd
from datetime import datetime, timedelta
import time
import os
import json
============================================
HolySheep Tardis 中转 API 配置
============================================
HOLYSHEEP_API_KEY = os.getenv("HOLYSHEEP_API_KEY")
BASE_URL = "https://api.holysheep.ai/v1/tardis"
HEADERS = {
"Authorization": f"Bearer {HOLYSHEEP_API_KEY}",
"Content-Type": "application/json"
}
def download_bitmex_orderbook(symbol="XBTUSD",
start_time=None,
end_time=None,
depth=10):
"""
下载 BitMEX 订单簿数据
BitMEX 特有:
- symbol 使用 XBTUSD 等格式
- depth 参数控制订单簿深度(1-25)
"""
endpoint = f"{BASE_URL}/historical/bitmex/orderbooks"
payload = {
"exchange": "bitmex",
"symbol": symbol,
"startTime": start_time,
"endTime": end_time,
"depth": depth,
"aggregation": "1s" # 聚合到 1 秒级别
}
print(f"[INFO] 请求 BitMEX orderbook: {symbol}")
try:
response = requests.post(
endpoint,
headers=HEADERS,
json=payload,
timeout=60 # BitMEX 数据量通常较大
)
response.raise_for_status()
result = response.json()
if result.get("success"):
records = result.get("data", [])
print(f"[SUCCESS] 获取 {len(records)} 条记录")
return records
else:
print(f"[ERROR] {result.get('message')}")
return []
except requests.exceptions.HTTPError as e:
if e.response.status_code == 429:
print("[WARNING] 触发限流,等待 60 秒...")
time.sleep(60)
return download_bitmex_orderbook(symbol, start_time, end_time, depth)
print(f"[ERROR] HTTP 错误: {e}")
return []
except Exception as e:
print(f"[ERROR] 未知错误: {e}")
return []
def download_bitmex_funding(symbol="XBTUSD",
start_time=None,
end_time=None):
"""
下载 BitMEX 资金费率历史(8 小时一次)
用于计算资金成本和套利分析
"""
endpoint = f"{BASE_URL}/historical/bitmex/funding"
payload = {
"exchange": "bitmex",
"symbol": symbol,
"startTime": start_time,
"endTime": end_time
}
try:
response = requests.post(endpoint, headers=HEADERS, json=payload, timeout=30)
response.raise_for_status()
return response.json().get("data", [])
except Exception as e:
print(f"[ERROR] 下载资金费率失败: {e}")
return []
def download_bitmex_trades(symbol="XBTUSD",
start_time=None,
end_time=None,
limit=10000):
"""
下载 BitMEX 逐笔成交数据
"""
endpoint = f"{BASE_URL}/historical/bitmex/trades"
payload = {
"exchange": "bitmex",
"symbol": symbol,
"startTime": start_time,
"endTime": end_time,
"limit": limit
}
try:
response = requests.post(endpoint, headers=HEADERS, json=payload, timeout=60)
response.raise_for_status()
return response.json().get("data", [])
except Exception as e:
print(f"[ERROR] 下载成交数据失败: {e}")
return []
def process_and_analyze_bitmex_data(trades_data, orderbook_data):
"""
处理并分析 BitMEX 数据
计算订单簿深度、买卖价差、成交量分布等
"""
if not trades_data:
return None
df_trades = pd.DataFrame(trades_data)
df_trades['timestamp'] = pd.to_datetime(df_trades['timestamp'])
# 基础统计
analysis = {
'total_trades': len(df_trades),
'total_volume': float(df_trades['size'].sum()) if 'size' in df_trades else 0,
'avg_trade_size': float(df_trades['size'].mean()) if 'size' in df_trades else 0,
'time_range': f"{df_trades['timestamp'].min()} to {df_trades['timestamp'].max()}"
}
# 买卖方向分析(需要 side 字段)
if 'side' in df_trades:
buy_volume = df_trades[df_trades['side'] == 'Buy']['size'].sum()
sell_volume = df_trades[df_trades['side'] == 'Sell']['size'].sum()
analysis['buy_ratio'] = buy_volume / (buy_volume + sell_volume) if (buy_volume + sell_volume) > 0 else 0.5
analysis['buy_volume'] = float(buy_volume)
analysis['sell_volume'] = float(sell_volume)
print(f"[ANALYSIS] 总成交: {analysis['total_trades']} 笔")
print(f"[ANALYSIS] 总成交量: {analysis['total_volume']:,.2f}")
print(f"[ANALYSIS] 多头占比: {analysis.get('buy_ratio', 0):.2%}")
return analysis
def batch_download_bitmex(start_date, end_date, output_dir="./data/bitmex"):
"""
批量下载 BitMEX 历史数据
"""
os.makedirs(output_dir, exist_ok=True)
current_date = start_date
all_trades = []
all_orderbooks = []
all_funding = []
while current_date <= end_date:
day_start = current_date.isoformat() + "T00:00:00Z"
day_end = current_date.isoformat() + "T23:59:59Z"
print(f"\n{'='*50}")
print(f"[{datetime.now()}] 处理 {current_date.date()}")
# 并发下载(线程池)
import concurrent.futures
with concurrent.futures.ThreadPoolExecutor(max_workers=3) as executor:
future_trades = executor.submit(
download_bitmex_trades, "XBTUSD", day_start, day_end
)
future_orderbook = executor.submit(
download_bitmex_orderbook, "XBTUSD", day_start, day_end
)
future_funding = executor.submit(
download_bitmex_funding, "XBTUSD", day_start, day_end
)
trades = future_trades.result()
orderbooks = future_orderbook.result()
funding = future_funding.result()
all_trades.extend(trades)
all_orderbooks.extend(orderbooks)
all_funding.extend(funding)
# 每日保存
date_str = current_date.strftime("%Y%m%d")
if trades:
df_trades = pd.DataFrame(trades)
df_trades.to_csv(f"{output_dir}/trades_XBTUSD_{date_str}.csv", index=False)
print(f"[SAVE] 成交数据: {len(trades)} 条")
if orderbooks:
df_orderbook = pd.DataFrame(orderbooks)
df_orderbook.to_csv(f"{output_dir}/orderbook_XBTUSD_{date_str}.csv", index=False)
print(f"[SAVE] 订单簿数据: {len(orderbooks)} 条")
if funding:
df_funding = pd.DataFrame(funding)
df_funding.to_csv(f"{output_dir}/funding_XBTUSD_{date_str}.csv", index=False)
print(f"[SAVE] 资金费率: {len(funding)} 条")
# 速率限制
time.sleep(1)
current_date += timedelta(days=1)
print(f"\n{'='*50}")
print(f"[COMPLETE] 下载完成")
print(f" 成交记录: {len(all_trades):,} 条")
print(f" 订单簿快照: {len(all_orderbooks):,} 条")
print(f" 资金费率: {len(all_funding):,} 条")
return all_trades, all_orderbooks, all_funding
使用示例
if __name__ == "__main__":
# 下载 2024 Q1 BitMEX XBTUSD 数据
start = datetime(2024, 1, 1)
end = datetime(2024, 3, 31)
trades, orderbooks, funding = batch_download_bitmex(
start_date=start,
end_date=end,
output_dir="./bitmex_xbtusd_q1"
)
# 分析数据
if trades:
analysis = process_and_analyze_bitmex_data(trades, orderbooks)
常见报错排查
错误 1:401 Unauthorized - API Key 无效或未授权
# 错误响应示例
{
"error": {
"code": 401,
"message": "Invalid API key or insufficient permissions"
}
}
排查步骤
1. 确认 API Key 正确(无多余空格/引号)
2. 检查 Key 是否已开通 Tardis 数据服务
3. 确认 Key 未过期(在控制台续期)
4. 验证 base_url 是否正确(应为 https://api.holysheep.ai/v1/tardis)
正确配置
export HOLYSHEEP_API_KEY="sk-xxxxxxxxxxxxxxxxxxxxxxxx"
BASE_URL = "https://api.holysheep.ai/v1/tardis"
HEADERS = {
"Authorization": f"Bearer {HOLYSHEEP_API_KEY}",
"Content-Type": "application/json"
}
错误 2:429 Rate Limit - 请求频率超限
# 错误响应示例
{
"error": {
"code": 429,
"message": "Rate limit exceeded. Retry after 60 seconds."
}
}
原因分析
- 单 IP 请求频率超过限制
- 短时间内大量并发请求
- Tardis 后端限流触发
解决方案
方案 1:添加请求间隔(推荐)
import time
import random
def throttled_request(func, *args, **kwargs):
"""带速率限制的请求包装器"""
time.sleep(random.uniform(0.5, 1.5)) # 随机 0.5-1.5 秒间隔
return func(*args, **kwargs)
方案 2:实现指数退避重试
def retry_with_backoff(func, max_retries=5, base_delay=1):
"""指数退避重试装饰器"""
def wrapper(*args, **kwargs):
for attempt in range(max_retries):
try:
return func(*args, **kwargs)
except requests.exceptions.HTTPError as e:
if e.response.status_code == 429:
delay = base_delay * (2 ** attempt)
print(f"[RETRY] 限流触发,等待 {delay}s(第 {attempt+1}/{max_retries} 次)")
time.sleep(delay)
else:
raise
raise Exception("重试次数耗尽")
return wrapper
方案 3:使用请求令牌桶
import threading
class TokenBucket:
"""令牌桶算法限流器"""
def __init__(self, rate, capacity):
self.rate = rate # 每秒令牌数
self.capacity = capacity
self.tokens = capacity
self.last_update = time.time()
self.lock = threading.Lock()
def acquire(self, tokens=1):
with self.lock:
now = time.time()
elapsed = now - self.last_update
self.tokens = min(self.capacity, self.tokens + elapsed * self.rate)
self.last_update = now
if self.tokens >= tokens:
self.tokens -= tokens
return True
return False
def wait_for_token(self, tokens=1):
while not self.acquire(tokens):
time.sleep(0.1)
使用示例
bucket = TokenBucket(rate=10, capacity=20) # 每秒最多 10 个请求
def limited_request(endpoint, payload):
bucket.wait_for_token()
return requests.post(endpoint, headers=HEADERS, json=payload)
错误 3:400 Bad Request - 参数格式错误
# 常见错误场景
错误 1:时间格式不正确
❌ 错误
payload = {
"startTime": "2024-01-01", # 缺少时间部分
"endTime": "2024-01-31"
}
✅ 正确(ISO 8601 格式)
payload = {
"startTime": "2024-01-01T00:00:00Z",
"endTime": "2024-01-31T23:59:59Z"
}
错误 2:symbol 格式不匹配
❌ OKX 使用 BTC-USDT-SWAP,BitMEX 使用 XBTUSD
payload_okx = {"symbol": "BTC-USDT-SWAP"} # OKX
payload_bmex = {"symbol": "XBTUSD"} # BitMEX
✅ 根据交易所使用正确格式
def get_symbol_format(exchange, base, quote, contract_type="SWAP"):
if exchange == "okex":
return f"{base}-{quote}-{contract_type}" # BTC-USDT-SWAP
elif exchange == "bitmex":
return f"{base}{quote}" # XBTUSD
else:
return f"{base}-{quote}"
错误 3:limit 超限
❌ 错误
payload = {"limit": 50000} # 超过最大值
✅ 正确
payload = {"limit": 1000} # 每页最大 1000 条
分页获取完整数据
def paginated_fetch(endpoint, base_params, max_total=100000):
"""分页获取数据"""
all_data = []
offset = 0
while offset < max_total:
params = {**base_params, "offset": offset}
response = requests.post(endpoint, headers=HEADERS, json=params)
data = response.json().get("data", [])
if not data:
break
all_data.extend(data)
offset += len(data)
print(f"[FETCH] 已获取 {len(all_data)} 条...")
time.sleep(0.3)
return all_data
错误 4:500 Internal Server Error - 服务端异常
# 错误响应
{
"error": {
"code": 500,
"message": "Internal server error"
}
}
排查与处理
1. 检查 HolySheep 状态页(https://status.holysheep.ai)
2. 检查 Tardis 官方状态(https://status.tardis.dev)
3. 记录错误时间和请求参数,提交工单
重试策略
MAX_RETRIES = 3
RETRY_DELAY = 5
for attempt in range(MAX_RETRIES):
try:
response = requests.post(endpoint, headers=HEADERS, json=payload, timeout=60)
response.raise_for_status()
break
except (requests.exceptions.Timeout, requests.exceptions.HTTPError) as e:
if attempt < MAX_RETRIES - 1:
wait_time = RETRY_DELAY * (attempt + 1)
print(f"[RETRY] 等待 {wait_time}s 后重试...")
time.sleep(wait_time)
else:
print(f"[FAIL] 连续 {MAX_RETRIES} 次失败,放弃")
raise
降级方案:使用缓存数据
def get_with_fallback(endpoint, payload):
"""带降级方案的数据获取"""
try:
response = requests.post(endpoint, headers=HEADERS, json=payload)
response.raise_for_status()
return response.json()
except Exception as e:
print(f"[FALLBACK] 实时获取失败,尝试缓存...")
# 可以从本地缓存/MinIO/OSS 加载历史数据
return load_from_cache(endpoint, payload)
错误 5:数据缺失/不完整
# 症状
- 返回数据量明显少于预期
- 部分时间区间为空
- 订单簿快照缺失
原因分析
1. Tardis 历史数据存在盲区(尤其是 2019 年前的 BitMEX)
2. 交易所数据源本身缺失(如极端行情时的熔断)
3. 请求参数导致过滤
验证数据完整性
def validate_data_completeness(data, expected_count, tolerance=0.95):
"""
验证数据完整性
Args:
data: 返回的数据列表
expected_count: 预期条数
tolerance: 容许缺失比例(默认 5%)
Returns:
bool: 是否完整
"""
actual_count = len(data)
completeness = actual_count / expected_count if expected_count > 0 else 0
if completeness < tolerance:
print(f"[WARNING] 数据缺失: {actual_count}/{expected_count} ({completeness:.1%})")
return False
print(f"[OK] 数据完整度: {completeness:.1%}")
return True
def check_time_gaps(data, expected_interval_seconds=60):
"""
检查时间序列中的间隙
Args:
data: 数据列表(需包含 timestamp 字段)
expected_interval_seconds: 预期时间间隔
"""
if len(data) < 2:
return
timestamps = sorted([d['timestamp'] for d in data])
gaps = []
for i in range(1, len(timestamps)):
delta = (timestamps[i] - timestamps[i-1]).total_seconds()
if delta > expected_interval_seconds * 2: # 超过 2 倍预期间隔
gaps.append({
'start': timestamps[i-1],
'end': timestamps[i],