# 数据采集与处理流程 > 量化交易的核心是数据。本文档详细描述从原始数据采集到可用于策略执行的全流程,涵盖数据源、清洗方法、存储方案和实时流处理。 --- ## 一、数据源分类 ### 1.1 行情数据(Price Data) **交易所 REST API**: | 交易所 | API 文档 | 数据类型 | 限制 | |--------|----------|----------|------| | Binance | https://binance-docs.github.io/apidocs/ | K线、Tick、深度 | 1200 req/min | | Bybit | https://bybit-docs.com/ | K线、Tick、深度 | 120 req/min | | OKX | https://www.okx.com/docs-v5/ | K线、Tick、深度 | 60 req/10s | | Coinbase | https://docs.cdp.coinbase.com/ | K线、Tick | 10 req/s | **WebSocket 实时数据**: ``` Binance WebSocket 端点:wss://stream.binance.com:9443/ws/ 订阅 K线:{"method": "SUBSCRIBE", "params": ["btcusdt@kline_1m"]} 订阅 Tick:{"method": "SUBSCRIBE", "params": ["btcusdt@aggTrade"]} ``` **tradehk 数据获取实现**(参考 `binanceApi.ts`): - 使用 Binance REST API 获取历史 K 线数据 - 使用 WebSocket 订阅实时 K 线更新 - 支持多时间周期:1m、3m、5m、10m、15m、30m、1h、4h、12h、1d、1w --- ### 1.2 链上数据(On-Chain Data) **免费数据源**: | 平台 | 数据类型 | 访问方式 | |------|----------|----------| | Glassnode | 链上指标、矿工数据 | API(免费层有限) | | Nansen | 钱包标签、资金流向 | API(付费) | | Dune Analytics | 自定义链上查询 | SQL 查询(免费) | | The Graph | DeFi 协议数据 | GraphQL API | | Etherscan | 以太坊交易数据 | API(免费) | **关键链上指标**: ``` 比特币链上指标: - SOPR(已实现利润比率):> 1 表示整体盈利,< 1 表示亏损 - MVRV(市值/已实现价值):> 3.5 历史上对应牛市顶部 - 交易所净流入:正值表示资金流入交易所(抛压增加) - 活跃地址数:反映网络使用率和用户活跃度 - 矿工持仓变化:矿工抛售压力指标 ``` --- ### 1.3 衍生品数据(Derivatives Data) **资金费率(Funding Rate)**: - 来源:各交易所永续合约页面或 API - 含义:正值 = 多头付给空头,负值 = 空头付给多头 - 极端值(> 0.1% 或 < -0.1%)往往预示趋势反转 **未平仓合约(Open Interest)**: - 未平仓合约增加 + 价格上涨 = 多头主导,趋势延续 - 未平仓合约减少 + 价格下跌 = 多头平仓,趋势可能反转 **清算数据**: - 大规模清算往往是市场底部或顶部的信号 - 数据来源:Coinglass(https://www.coinglass.com/) --- ### 1.4 情绪数据(Sentiment Data) **恐惧贪婪指数(Fear & Greed Index)**: - 来源:Alternative.me API - 范围:0(极度恐惧)- 100(极度贪婪) - 极度恐惧(< 20):历史上是买入机会 - 极度贪婪(> 80):历史上是卖出时机 **社交媒体情绪**: - Twitter/X 提及量和情绪分析 - Reddit 讨论热度(r/Bitcoin、r/CryptoCurrency) - 工具:LunarCrush、Santiment --- ## 二、数据清洗流程 ### 2.1 K 线数据清洗 ```python import pandas as pd import numpy as np def clean_kline_data(df: pd.DataFrame) -> pd.DataFrame: """ K 线数据清洗流程 """ # 1. 删除重复数据 df = df.drop_duplicates(subset=['timestamp']) # 2. 按时间排序 df = df.sort_values('timestamp').reset_index(drop=True) # 3. 检测并处理缺失 K 线(用前值填充) expected_interval = df['timestamp'].diff().mode()[0] df = df.set_index('timestamp').asfreq(expected_interval, method='ffill') # 4. 过滤异常价格(价格为 0 或负值) df = df[(df['open'] > 0) & (df['high'] > 0) & (df['low'] > 0) & (df['close'] > 0)] # 5. 修正 OHLC 逻辑错误(high < low 等) df['high'] = df[['open', 'high', 'low', 'close']].max(axis=1) df['low'] = df[['open', 'high', 'low', 'close']].min(axis=1) # 6. 处理成交量异常(成交量为负) df['volume'] = df['volume'].clip(lower=0) # 7. 过滤"刷量"数据(成交量极端异常) volume_mean = df['volume'].rolling(100).mean() volume_std = df['volume'].rolling(100).std() df = df[df['volume'] < volume_mean + 5 * volume_std] return df ``` ### 2.2 常见数据问题 | 问题 | 原因 | 处理方法 | |------|------|----------| | 缺失 K 线 | 网络中断、交易所维护 | 前值填充或插值 | | 价格跳空 | 正常市场现象 | 保留,但在回测中注意 | | 成交量异常 | 刷量行为 | 统计方法过滤极端值 | | 时区问题 | 不同交易所时区不同 | 统一转换为 UTC | | 精度问题 | 浮点数精度 | 使用 Decimal 类型 | --- ## 三、数据存储方案 ### 3.1 本地存储(适合小规模) ``` 数据目录结构: /data/ ├── klines/ │ ├── BTCUSDT/ │ │ ├── 1m/2024-01.parquet │ │ ├── 1h/2024-01.parquet │ │ └── 1d/all.parquet │ └── ETHUSDT/ │ └── ... ├── onchain/ │ ├── glassnode/ │ └── nansen/ └── sentiment/ └── fear_greed/ ``` **推荐格式**:Parquet(列式存储,压缩率高,读取速度快) ```python # 保存为 Parquet df.to_parquet('BTCUSDT_1h_2024.parquet', compression='snappy') # 读取 Parquet df = pd.read_parquet('BTCUSDT_1h_2024.parquet') ``` ### 3.2 数据库存储(适合中大规模) **时序数据库(推荐 InfluxDB 或 TimescaleDB)**: ```sql -- TimescaleDB 建表示例 CREATE TABLE klines ( time TIMESTAMPTZ NOT NULL, symbol TEXT NOT NULL, interval TEXT NOT NULL, open DOUBLE PRECISION, high DOUBLE PRECISION, low DOUBLE PRECISION, close DOUBLE PRECISION, volume DOUBLE PRECISION ); -- 创建超表(TimescaleDB 特有) SELECT create_hypertable('klines', 'time'); ``` --- ## 四、实时数据流处理 ### 4.1 WebSocket 数据流架构 ``` Binance WebSocket ↓ 数据接收层(asyncio) ↓ 数据解析与验证 ↓ 指标实时计算 ↓ 信号生成 ↓ 订单执行 ``` ### 4.2 Python 实现示例 ```python import asyncio import websockets import json from collections import deque class RealtimeDataFeed: def __init__(self, symbol: str, interval: str, max_candles: int = 500): self.symbol = symbol.lower() self.interval = interval self.candles = deque(maxlen=max_candles) self.ws_url = f"wss://stream.binance.com:9443/ws/{self.symbol}@kline_{interval}" async def connect(self): async with websockets.connect(self.ws_url) as ws: async for message in ws: data = json.loads(message) kline = data['k'] candle = { 'time': kline['t'] // 1000, 'open': float(kline['o']), 'high': float(kline['h']), 'low': float(kline['l']), 'close': float(kline['c']), 'volume': float(kline['v']), 'is_closed': kline['x'] # K 线是否已收盘 } if candle['is_closed']: self.candles.append(candle) await self.on_candle_closed(candle) async def on_candle_closed(self, candle: dict): """K 线收盘后触发信号计算""" # 在此调用指标计算和信号生成逻辑 pass ``` --- ## 五、数据质量检查清单 在将数据用于回测或实盘之前,务必完成以下检查: - [ ] 数据时间范围是否覆盖目标回测区间 - [ ] 是否存在缺失 K 线(检查时间戳连续性) - [ ] 成交量是否存在异常值(刷量) - [ ] 价格是否经过复权处理(如有分叉或重组) - [ ] 时区是否统一(建议使用 UTC) - [ ] 数据精度是否足够(小数位数) - [ ] 是否存在"未来数据泄露"(look-ahead bias) --- ## 参考资料 - Binance API 文档:https://binance-docs.github.io/apidocs/spot/en/ - Glassnode 链上指标:https://glassnode.com/metrics - Coinglass 衍生品数据:https://www.coinglass.com/ - Alternative.me 恐惧贪婪指数:https://alternative.me/crypto/fear-and-greed-index/