第2章:链上数据基础设施
做量化交易,第一件事是什么?不是写策略,不是调参数,而是——你得先能连上链。
我刚开始做DeFi量化时,就犯过这个错。策略写得飞起,回测数据漂亮得不行,结果一上线,连节点都连不上。嗯,那感觉,就像你准备好了所有食材,却发现没开煤气灶。
这一章,我们就来聊聊链上数据的基础设施。说白了,就是你怎么跟区块链“对话”。
2.1 节点服务商:Infura、Alchemy、QuickNode
自己跑一个全节点?理论上可以,但实操上,我劝你慎重。以太坊全节点现在要几个TB的硬盘,同步一次得花好几天。而且,你还要保证它24小时在线,不能断电断网。
所以,绝大多数量化工程师都会选择节点服务商。它们帮你把节点跑好,你只需要调用API就行。
2.1.1 三大主流服务商对比
| 服务商 | 免费层 | 特点 | 适合场景 |
|---|---|---|---|
| Infura | 10万请求/天 | 老牌,生态最成熟,文档齐全 | 入门学习、小规模项目 |
| Alchemy | 3亿计算单元/月 | 功能丰富,有WebSocket推送,历史数据查询 | 中大规模量化策略 |
| QuickNode | 按需付费 | 速度快,全球节点分布广,支持多链 | 高频交易、多链套利 |
2.1.2 如何选择?
其实没有绝对的好坏。我见过有人用Infura做高频交易,也见过有人用QuickNode只做数据归档。关键看你的需求:
- 请求量:如果每秒请求超过100次,免费层肯定不够,得付费。
- 延迟:做套利的话,延迟就是生命。QuickNode在这一点上确实有优势。
- 数据丰富度:Alchemy的“交易收据”和“日志”查询,比Infura强不少。
2.2 RPC接口调用基础
RPC,全称Remote Procedure Call,远程过程调用。听起来高大上,其实就是一个接口。你发一个请求过去,它给你返回数据。
以太坊的RPC接口,说白了就是一套JSON-RPC协议。你发一个JSON格式的请求,它返回一个JSON格式的响应。
2.2.1 最常用的几个RPC方法
eth_blockNumber:获取最新区块号。这是你判断链是否同步的基础。eth_getBalance:查询地址余额。注意,单位是Wei,不是ETH。eth_call:调用合约的只读方法。比如查一个Uniswap池子的储备量。eth_getLogs:获取事件日志。这是做链上数据分析的核心。eth_sendRawTransaction:发送已签名的交易。做交易执行时必用。
eth_call和eth_getLogs上。前者用来查状态,后者用来抓事件。
2.2.2 一个简单的RPC调用示例
假设你想查一下某个地址的ETH余额。用curl命令就能搞定:
curl -X POST https://mainnet.infura.io/v3/YOUR_PROJECT_ID \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "eth_getBalance",
"params": ["0x742d35Cc6634C0532925a3b844Bc9e7595f2bD18", "latest"],
"id": 1
}'
返回结果长这样:
{
"jsonrpc": "2.0",
"id": 1,
"result": "0x29c4c4f8b3c0000"
}
这个结果是以Wei为单位的十六进制数。换算成ETH,就是0.1888 ETH左右。
2.3 Web3.py与ethers.js入门
直接调RPC接口太原始了。在实际项目中,我们会用封装好的库。Python生态里,Web3.py是首选;JavaScript生态里,ethers.js是主流。
2.3.1 Web3.py:Python量化者的标配
我个人主要用Python做量化,所以Web3.py用得最多。它把RPC调用封装成了Python函数,用起来很顺手。
安装:
pip install web3
连接节点:
from web3 import Web3
# 连接Infura
w3 = Web3(Web3.HTTPProvider('https://mainnet.infura.io/v3/YOUR_PROJECT_ID'))
# 检查是否连接成功
print(w3.is_connected()) # 返回True就对了
查询余额:
address = '0x742d35Cc6634C0532925a3b844Bc9e7595f2bD18'
balance = w3.eth.get_balance(address)
print(f"余额: {w3.from_wei(balance, 'ether')} ETH")
调用合约方法:
# 以USDT合约为例
usdt_contract = w3.eth.contract(
address='0xdAC17F958D2ee523a2206206994597C13D831ec7',
abi=USDT_ABI # 需要提前获取ABI
)
# 查询某个地址的USDT余额
balance = usdt_contract.functions.balanceOf('0x...').call()
print(f"USDT余额: {balance}")
eth_getContractABI接口获取。
2.3.2 ethers.js:前端与Node.js的利器
如果你做的是前端量化工具,或者用Node.js写脚本,ethers.js会更合适。它比Web3.js更轻量,API设计也更现代。
安装:
npm install ethers
连接节点:
const { ethers } = require("ethers");
// 连接Alchemy
const provider = new ethers.providers.JsonRpcProvider(
'https://eth-mainnet.g.alchemy.com/v2/YOUR_API_KEY'
);
// 检查连接
provider.getBlockNumber().then(console.log);
查询余额:
const address = '0x742d35Cc6634C0532925a3b844Bc9e7595f2bD18';
const balance = await provider.getBalance(address);
console.log(`余额: ${ethers.utils.formatEther(balance)} ETH`);
调用合约方法:
const usdtContract = new ethers.Contract(
'0xdAC17F958D2ee523a2206206994597C13D831ec7',
USDT_ABI,
provider
);
const balance = await usdtContract.balanceOf('0x...');
console.log(`USDT余额: ${ethers.utils.formatUnits(balance, 6)}`);
2.4 本章知识体系图
下面这张图,帮你理清这一章的核心逻辑:
这张图展示了从区块链节点到最终量化应用的完整链路。你想想看,每一步都环环相扣。节点服务商是桥梁,RPC是语言,Web3.py和ethers.js是你的工具。