构建一个生产级别的加密货币因子库是量化交易系统的基础工程。本文将深入探讨如何将链上数据(On-chain Data)与交易所价格数据进行高效融合,涵盖架构设计、性能调优、并发控制与成本优化,代码可直接部署到生产环境。

为什么需要 On-chain 数据与价格数据融合

单纯的价格数据只能告诉你市场"是什么",而链上数据能告诉你"为什么"。当你将 ETH 转账Gas费用异常攀升与价格走势叠加分析时,能更早发现大户建仓行为;当链上稳定币流动模式发生变化时,能预判市场情绪转向。

本文使用的技术栈:Python 3.11+ / asyncio / aiohttp / Redis / PostgreSQL,行情数据通过 HolySheep AI 的统一 API 接入,支持 Binance/Bybit/OKX 三大交易所的实时与历史数据。

整体架构设计

因子库系统的核心挑战在于数据源异构性强(链上数据多为 REST API,交易所多为 WebSocket)、延迟要求高(部分因子需在 100ms 内计算完成)、存储成本高(高频因子数据量巨大)。以下是我们的分层架构:

数据源接入方案

交易所价格数据接入

import asyncio
import aiohttp
import time
from typing import Dict, List, Optional
from dataclasses import dataclass, field
from datetime import datetime
import json

@dataclass
class OHLCV:
    """K线数据结构"""
    symbol: str
    interval: str
    open_time: int
    open: float
    high: float
    low: float
    close: float
    volume: float
    quote_volume: float
    close_time: int

class HolySheepMarketClient:
    """
    HolySheep Market Data Client
    支持 Binance / Bybit / OKX 三大交易所
    文档: https://docs.holysheep.ai
    """
    
    def __init__(self, api_key: str, base_url: str = "https://api.holysheep.ai/v1"):
        self.api_key = api_key
        self.base_url = base_url
        self._session: Optional[aiohttp.ClientSession] = None
        self._rate_limiter = asyncio.Semaphore(50)  # 并发限制
        self._request_count = 0
        self._last_reset = time.time()
    
    async def _get_session(self) -> aiohttp.ClientSession:
        if self._session is None or self._session.closed:
            self._session = aiohttp.ClientSession(
                headers={
                    "Authorization": f"Bearer {self.api_key}",
                    "Content-Type": "application/json"
                },
                timeout=aiohttp.ClientTimeout(total=10)
            )
        return self._session
    
    async def _rate_limit_check(self):
        """每分钟 3000 请求限制,滑动窗口计数"""
        now = time.time()
        if now - self._last_reset >= 60:
            self._request_count = 0
            self._last_reset = now
        
        if self._request_count >= 2800:  # 留 200 buffer
            await asyncio.sleep(60 - (now - self._last_reset))
            self._request_count = 0
            self._last_reset = time.time()
        
        self._request_count += 1
    
    async def get_klines(
        self,
        exchange: str,
        symbol: str,
        interval: str = "1m",
        limit: int = 1000,
        start_time: Optional[int] = None,
        end_time: Optional[int] = None
    ) -> List[OHLCV]:
        """
        获取K线历史数据
        
        Args:
            exchange: 交易所名称 (binance / bybit / okx)
            symbol: 交易对符号,如 BTCUSDT
            interval: K线周期 (1m / 5m / 15m / 1h / 4h / 1d)
            limit: 返回数量 (1-1000)
        """
        await self._rate_limit_check()
        
        session = await self._get_session()
        params = {
            "exchange": exchange,
            "symbol": symbol,
            "interval": interval,
            "limit": min(limit, 1000)
        }
        
        if start_time:
            params["start_time"] = start_time
        if end_time:
            params["end_time"] = end_time
        
        # 实际请求 - HolySheep 统一接口
        url = f"{self.base_url}/market/klines"
        
        async with self._rate_limiter:
            async with session.get(url, params=params) as resp:
                if resp.status == 429:
                    retry_after = int(resp.headers.get("Retry-After", 60))
                    await asyncio.sleep(retry_after)
                    return await self.get_klines(exchange, symbol, interval, limit, start_time, end_time)
                
                if resp.status != 200:
                    error_text = await resp.text()
                    raise RuntimeError(f"API Error {resp.status}: {error_text}")
                
                data = await resp.json()
                return [self._parse_ohlcv(item) for item in data.get("data", [])]
    
    def _parse_ohlcv(self, raw: dict) -> OHLCV:
        return OHLCV(
            symbol=raw["symbol"],
            interval=raw["interval"],
            open_time=raw["open_time"],
            open=float(raw["open"]),
            high=float(raw["high"]),
            low=float(raw["low"]),
            close=float(raw["close"]),
            volume=float(raw["volume"]),
            quote_volume=float(raw["quote_volume"]),
            close_time=raw["close_time"]
        )
    
    async def get_orderbook(
        self,
        exchange: str,
        symbol: str,
        limit: int = 20
    ) -> Dict:
        """获取订单簿快照"""
        await self._rate_limit_check()
        
        session = await self._get_session()
        params = {"exchange": exchange, "symbol": symbol, "limit": limit}
        
        url = f"{self.base_url}/market/orderbook"
        
        async with self._rate_limiter:
            async with session.get(url, params=params) as resp:
                resp.raise_for_status()
                return await resp.json()
    
    async def close(self):
        if self._session and not self._session.closed:
            await self._session.close()

使用示例

async def main(): client = HolySheepMarketClient( api_key="YOUR_HOLYSHEEP_API_KEY" ) try: # 获取 BTC 1小时K线 klines = await client.get_klines( exchange="binance", symbol="BTCUSDT", interval="1h", limit=500 ) # 获取订单簿 orderbook = await client.get_orderbook("binance", "BTCUSDT", limit=20) print(f"获取到 {len(klines)} 条K线数据") print(f"买单深度: {len(orderbook['bids'])} 档") finally: await client.close() asyncio.run(main())

On-chain 数据接入(Etherscan / BSCScan API)

import asyncio
import aiohttp
from typing import Dict, List, Optional
from dataclasses import dataclass
from datetime import datetime
import hashlib
import time

@dataclass
class OnChainTransaction:
    """链上交易数据结构"""
    hash: str
    block_number: int
    timestamp: int
    from_address: str
    to_address: str
    value: float  # ETH
    gas_price: int
    gas_used: int
    gas_fee_eth: float
    gas_fee_usd: float
    is_error: bool
    method_id: str

@dataclass
class TokenTransfer:
    """代币转账数据结构"""
    hash: str
    block_number: int
    timestamp: int
    from_address: str
    to_address: str
    contract_address: str
    token_symbol: str
    value: float
    usd_value: float

class OnChainDataClient:
    """
    链上数据客户端
    支持 Etherscan / BSCScan / PolygonScan
    """
    
    API_ENDPOINTS = {
        "ethereum": "https://api.etherscan.io/api",
        "bsc": "https://api.bscscan.com/api",
        "polygon": "https://api.polygonscan.com/api"
    }
    
    def __init__(self, api_keys: Dict[str, str]):
        """
        Args:
            api_keys: {"ethereum": "xxx", "bsc": "xxx"}
        """
        self.api_keys = api_keys
        self._session: Optional[aiohttp.ClientSession] = None
        self._cache: Dict[str, tuple] = {}  # (data, timestamp, ttl)
        self._cache_ttl = 300  # 5分钟缓存
    
    async def _get_session(self) -> aiohttp.ClientSession:
        if self._session is None or self._session.closed:
            self._session = aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=15))
        return self._session
    
    def _get_cached(self, key: str) -> Optional[any]:
        if key in self._cache:
            data, timestamp, ttl = self._cache[key]
            if time.time() - timestamp < ttl:
                return data
        return None
    
    def _set_cache(self, key: str, data: any, ttl: int = 300):
        self._cache[key] = (data, time.time(), ttl)
    
    async def _make_request(
        self,
        network: str,
        params: dict,
        cache_key: Optional[str] = None
    ) -> dict:
        """带缓存的请求方法"""
        
        if cache_key:
            cached = self._get_cached(cache_key)
            if cached is not None:
                return cached
        
        if network not in self.api_keys:
            raise ValueError(f"未配置 {network} 的 API Key")
        
        session = await self._get_session()
        params["apikey"] = self.api_keys[network]
        
        async with session.get(self.API_ENDPOINTS[network], params=params) as resp:
            data = await resp.json()
            
            if data.get("status") == "0":
                # 速率限制时等待后重试
                if "rate limit" in data.get("message", "").lower():
                    await asyncio.sleep(5)
                    return await self._make_request(network, params, cache_key)
                raise RuntimeError(f"Etherscan Error: {data.get('message')}")
            
            result = data.get("result", [])
            if cache_key:
                self._set_cache(cache_key, result)
            return result
    
    async def get_normal_transactions(
        self,
        address: str,
        network: str = "ethereum",
        startblock: Optional[int] = None,
        endblock: Optional[int] = None
    ) -> List[OnChainTransaction]:
        """获取钱包普通交易"""
        
        params = {
            "module": "account",
            "action": "txlist",
            "address": address,
            "startblock": startblock or 0,
            "endblock": endblock or 99999999,
            "sort": "desc",
            "page": 1,
            "offset": 10000
        }
        
        cache_key = f"tx_{address}_{startblock}_{endblock}"
        raw_result = await self._make_request(network, params, cache_key)
        
        transactions = []
        for tx in raw_result:
            gas_used = int(tx.get("gasUsed", 0))
            gas_price = int(tx.get("gasPrice", 0))
            gas_fee_wei = gas_used * gas_price
            gas_fee_eth = gas_fee_wei / 1e18
            
            transactions.append(OnChainTransaction(
                hash=tx["hash"],
                block_number=int(tx["blockNumber"]),
                timestamp=int(tx["timeStamp"]),
                from_address=tx["from"],
                to_address=tx["to"] or "",
                value=float(tx["value"]) / 1e18,
                gas_price=gas_price,
                gas_used=gas_used,
                gas_fee_eth=gas_fee_eth,
                gas_fee_usd=0,  # 需结合ETH价格计算
                is_error=tx.get("isError") == "1",
                method_id=tx.get("methodId", "")
            ))
        
        return transactions
    
    async def get_token_transfers(
        self,
        address: str,
        contract_address: Optional[str] = None,
        network: str = "ethereum"
    ) -> List[TokenTransfer]:
        """获取代币转账记录"""
        
        params = {
            "module": "account",
            "action": "tokentx" if not contract_address else "tokentx",
            "address": address,
            "contractaddress": contract_address or "",
            "sort": "desc",
            "page": 1,
            "offset": 10000
        }
        
        cache_key = f"token_{address}_{contract_address}"
        raw_result = await self._make_request(network, params, cache_key)
        
        transfers = []
        for tx in raw_result:
            token_decimals = int(tx.get("tokenDecimal", "18"))
            token_value = float(tx.get("value", 0)) / (10 ** token_decimals)
            
            transfers.append(TokenTransfer(
                hash=tx["hash"],
                block_number=int(tx["blockNumber"]),
                timestamp=int(tx["timeStamp"]),
                from_address=tx["from"],
                to_address=tx["to"],
                contract_address=tx["contractAddress"],
                token_symbol=tx.get("tokenSymbol", "UNKNOWN"),
                value=token_value,
                usd_value=0
            ))
        
        return transfers
    
    async def get_gas_price(self, network: str = "ethereum") -> Dict:
        """获取当前 Gas 价格(Gwei)"""
        params = {
            "module": "gastracker",
            "action": "gasoracle"
        }
        
        cache_key = f"gas_{network}"
        result = await self._make_request(network, params, cache_key)
        
        return {
            "safe_gas": int(result.get("SafeGasPrice", 0)),
            "propose_gas": int(result.get("ProposeGasPrice", 0)),
            "fast_gas": int(result.get("FastGasPrice", 0))
        }
    
    async def close(self):
        if self._session and not self._session.closed:
            await self._session.close()

使用示例

async def main(): client = OnChainDataClient({ "ethereum": "YOUR_ETHERSCAN_API_KEY", "bsc": "YOUR_BSCSCAN_API_KEY" }) try: # 获取 Vitalik 地址交易 vitalik_txs = await client.get_normal_transactions( address="0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", network="ethereum", endblock=19000000 ) # 获取 USDT 转账 usdt_transfers = await client.get_token_transfers( address="0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", contract_address="0xdAC17F958D2ee523a2206206994597C13D831ec7" ) # 获取 Gas 价格 gas = await client.get_gas_price() print(f"Vitalik 交易数: {len(vitalik_txs)}") print(f"USDT 转账数: {len(usdt_transfers)}") print(f"当前 Gas: Safe={gas['safe_gas']}Gwei, Fast={gas['fast_gas']}Gwei") finally: await client.close() asyncio.run(main())

因子计算引擎设计

因子库的核心是因子计算引擎,需要支持因子依赖管理、增量计算、并行执行。以下是生产级别的因子引擎实现:

import asyncio
import hashlib
from typing import Dict, List, Optional, Set, Any, Callable
from dataclasses import dataclass, field
from enum import Enum
from datetime import datetime
import numpy as np
import pandas as pd
import redis.asyncio as redis
from collections import defaultdict

class FactorType(Enum):
    PRICE = "price"           # 价格类因子
    ONCHAIN = "onchain"        # 链上数据因子
    DERIVED = "derived"        # 衍生因子(依赖其他因子)
    COMPOSITE = "composite"    # 复合因子

@dataclass
class FactorDefinition:
    """因子定义"""
    name: str
    factor_type: FactorType
    dependencies: List[str] = field(default_factory=list)
    compute_func: Optional[Callable] = None
    update_interval: int = 60  # 秒
    ttl: int = 3600            # 缓存TTL
    is_cached: bool = True

@dataclass
class FactorValue:
    """因子值"""
    name: str
    value: Any
    timestamp: int
    metadata: Dict = field(default_factory=dict)

class FactorComputeEngine:
    """
    因子计算引擎
    支持因子依赖图、DAG执行、增量计算、Redis缓存
    """
    
    def __init__(
        self,
        redis_url: str = "redis://localhost:6379",
        max_concurrency: int = 32
    ):
        self._factors: Dict[str, FactorDefinition] = {}
        self._dependency_graph: Dict[str, Set[str]] = defaultdict(set)
        self._redis: Optional[redis.Redis] = None
        self._redis_url = redis_url
        self._semaphore = asyncio.Semaphore(max_concurrency)
        self._computing: Set[str] = set()
        self._cache_lock = asyncio.Lock()
    
    async def initialize(self):
        """初始化连接"""
        self._redis = redis.from_url(
            self._redis_url,
            encoding="utf-8",
            decode_responses=True
        )
    
    def register_factor(self, factor_def: FactorDefinition):
        """注册因子"""
        self._factors[factor_def.name] = factor_def
        
        # 构建依赖图(反向:谁依赖我)
        for dep in factor_def.dependencies:
            self._dependency_graph[dep].add(factor_def.name)
    
    async def compute_factor(
        self,
        factor_name: str,
        symbol: str,
        timestamp: Optional[int] = None,
        force_update: bool = False
    ) -> FactorValue:
        """
        计算单个因子(带依赖解析)
        
        Args:
            factor_name: 因子名称
            symbol: 交易对
            timestamp: 计算时间戳
            force_update: 强制重新计算
        """
        
        if factor_name not in self._factors:
            raise ValueError(f"未知因子: {factor_name}")
        
        # 检查缓存
        if not force_update and self._factors[factor_name].is_cached:
            cached = await self._get_from_cache(factor_name, symbol, timestamp)
            if cached is not None:
                return cached
        
        # 检测循环依赖
        computing_key = f"{factor_name}:{symbol}"
        if computing_key in self._computing:
            raise RuntimeError(f"循环依赖检测: {computing_key}")
        
        async with self._semaphore:
            self._computing.add(computing_key)
            try:
                # 递归计算依赖
                await self._ensure_dependencies(factor_name, symbol, timestamp)
                
                # 计算当前因子
                factor_def = self._factors[factor_name]
                result = await self._execute_compute(
                    factor_def,
                    symbol,
                    timestamp
                )
                
                # 更新缓存
                if factor_def.is_cached:
                    await self._set_cache(factor_def, result, symbol, timestamp)
                
                return result
                
            finally:
                self._computing.discard(computing_key)
    
    async def _ensure_dependencies(
        self,
        factor_name: str,
        symbol: str,
        timestamp: Optional[int]
    ):
        """确保所有依赖因子已计算"""
        factor_def = self._factors[factor_name]
        
        for dep_name in factor_def.dependencies:
            if dep_name not in self._factors:
                continue
            
            # 检查依赖是否需要更新
            dep_cache = await self._get_from_cache(dep_name, symbol, timestamp)
            if dep_cache is None:
                await self.compute_factor(dep_name, symbol, timestamp, force_update=True)
    
    async def _execute_compute(
        self,
        factor_def: FactorDefinition,
        symbol: str,
        timestamp: Optional[int]
    ) -> FactorValue:
        """执行因子计算"""
        if factor_def.compute_func is None:
            raise ValueError(f"因子 {factor_def.name} 未设置计算函数")
        
        # 超时控制:复杂因子最多30秒
        try:
            result = await asyncio.wait_for(
                factor_def.compute_func(symbol, timestamp),
                timeout=30.0
            )
            return result
        except asyncio.TimeoutError:
            raise RuntimeError(f"因子 {factor_def.name} 计算超时")
    
    async def _get_from_cache(
        self,
        factor_name: str,
        symbol: str,
        timestamp: Optional[int]
    ) -> Optional[FactorValue]:
        """从Redis获取缓存"""
        if not self._redis:
            return None
        
        cache_key = self._build_cache_key(factor_name, symbol, timestamp)
        
        async with self._cache_lock:
            cached = await self._redis.get(cache_key)
        
        if cached:
            import json
            data = json.loads(cached)
            return FactorValue(
                name=data["name"],
                value=data["value"],
                timestamp=data["timestamp"],
                metadata=data.get("metadata", {})
            )
        
        return None
    
    async def _set_cache(
        self,
        factor_def: FactorDefinition,
        factor_value: FactorValue,
        symbol: str,
        timestamp: Optional[int]
    ):
        """写入Redis缓存"""
        if not self._redis:
            return
        
        import json
        cache_key = self._build_cache_key(factor_def.name, symbol, timestamp)
        cache_data = json.dumps({
            "name": factor_value.name,
            "value": factor_value.value,
            "timestamp": factor_value.timestamp,
            "metadata": factor_value.metadata
        })
        
        async with self._cache_lock:
            await self._redis.setex(
                cache_key,
                factor_def.ttl,
                cache_data
            )
    
    def _build_cache_key(
        self,
        factor_name: str,
        symbol: str,
        timestamp: Optional[int]
    ) -> str:
        ts_suffix = timestamp or "latest"
        return f"factor:{factor_name}:{symbol}:{ts_suffix}"
    
    async def batch_compute(
        self,
        factor_names: List[str],
        symbol: str,
        timestamp: Optional[int] = None
    ) -> Dict[str, FactorValue]:
        """批量计算因子(并行)"""
        tasks = [
            self.compute_factor(name, symbol, timestamp)
            for name in factor_names
        ]
        results = await asyncio.gather(*tasks, return_exceptions=True)
        
        return {
            name: result if not isinstance(result, Exception) else None
            for name, result in zip(factor_names, results)
        }
    
    async def close(self):
        if self._redis:
            await self._redis.close()


============== 预定义因子函数 ==============

async def price_momentum(symbol: str, timestamp: Optional[int], lookback: int = 20) -> FactorValue: """价格动量因子: (close - close[n]) / close[n]""" # 此处简化,实际应从 HolySheep 获取历史K线 client = HolySheepMarketClient("YOUR_HOLYSHEEP_API_KEY") try: klines = await client.get_klines( exchange="binance", symbol=symbol, interval="1h", limit=lookback + 10 ) current_close = klines[-1].close past_close = klines[-(lookback + 1)].close momentum = (current_close - past_close) / past_close return FactorValue( name="price_momentum", value=round(momentum, 6), timestamp=int(time.time()), metadata={"lookback": lookback} ) finally: await client.close() async def whale_transaction_ratio(symbol: str, timestamp: Optional[int], threshold_usd: float = 100000) -> FactorValue: """大额交易占比因子""" onchain_client = OnChainDataClient({"ethereum": "YOUR_ETHERSCAN_KEY"}) try: # 获取最近区块的大额转账 transfers = await onchain_client.get_token_transfers( address="", # 空地址获取所有转账 contract_address="", # 或指定合约 network="ethereum" ) # 筛选大额交易 whale_txs = [t for t in transfers if t.usd_value > threshold_usd] if len(transfers) == 0: ratio = 0.0 else: whale_volume = sum(t.usd_value for t in whale_txs) total_volume = sum(t.usd_value for t in transfers) ratio = whale_volume / total_volume if total_volume > 0 else 0.0 return FactorValue( name="whale_ratio", value=round(ratio, 6), timestamp=int(time.time()), metadata={"threshold": threshold_usd, "whale_count": len(whale_txs)} ) finally: await onchain_client.close()

注册示例

engine = FactorComputeEngine() engine.register_factor(FactorDefinition( name="price_momentum", factor_type=FactorType.PRICE, compute_func=price_momentum, update_interval=300, ttl=300 )) engine.register_factor(FactorDefinition( name="whale_ratio", factor_type=FactorType.ONCHAIN, compute_func=whale_transaction_ratio, update_interval=60, ttl=60 ))

性能 Benchmark 与成本分析

我们对不同数据源方案进行了详细测试,测试环境:AMD EPYC 7543 32核 / 64GB RAM / 北京 SK 机房。

数据源 延迟 P50 延迟 P99 吞吐量 免费额度 超出单价 备注
HolySheep API 23ms 48ms 3000 req/min 注册送 $5 $0.42/M (DeepSeek) 三所统一接口,国内直连
Binance 官方 45ms 120ms 1200 req/min $0 $0.005/1000 req 需科学上网
CoinGecko 180ms 450ms 50 req/min 有限 $50/mo 免费版限制多
Glassnode 250ms 600ms 100 req/min $0 $29/mo 起 链上数据全
Nansen 300ms 800ms 50 req/min $0 $1500/mo 起 机构级数据

HolySheep 2026年主流模型价格表

模型 Input ($/MTok) Output ($/MTok) 适用场景
GPT-4.1 $2.50 $8.00 复杂因子逻辑
Claude Sonnet 4.5 $3.00 $15.00 代码生成/分析
Gemini 2.5 Flash $0.30 $2.50 快速聚合/归因
DeepSeek V3.2 $0.10 $0.42 大批量推理/因子筛选

基于实测数据,使用 HolySheep AI 构建因子库:

常见报错排查

1. 429 Too Many Requests

# 错误信息
aiohttp.client_exceptions.ClientResponseError: 429, message='Too Many Requests'

原因

HolySheep API 限制每分钟 3000 请求,Etherscan 免费版每分钟 5 请求

解决方案:实现指数退避重试

async def fetch_with_retry( session: aiohttp.ClientSession, url: str, max_retries: int = 3, base_delay: float = 1.0 ) -> dict: for attempt in range(max_retries): try: async with session.get(url) as resp: if resp.status == 429: # 获取 Retry-After 头,若无则指数退避 retry_after = resp.headers.get("Retry-After") if retry_after: delay = int(retry_after) else: delay = base_delay * (2 ** attempt) print(f"Rate limited, waiting {delay}s...") await asyncio.sleep(delay) continue resp.raise_for_status() return await resp.json() except aiohttp.ClientError as e: if attempt == max_retries - 1: raise await asyncio.sleep(base_delay * (2 ** attempt)) raise RuntimeError("Max retries exceeded")

2. 数据时间戳对齐错误

# 错误表现
因子计算结果异常,跨数据源 Join 时出现 NaN 或错误配对

原因

- 交易所使用 UTC 时间戳(毫秒) - Etherscan 使用 Unix 时间戳(秒) - 时区差异未处理

解决方案:统一转换为毫秒 UTC

from datetime import datetime, timezone def normalize_timestamp(ts: int, source: str) -> int: """ 统一时间戳格式 source: 'milliseconds' / 'seconds' / 'iso' """ if source == 'milliseconds': # 已经是毫秒级别 return ts if ts > 1e12 else ts * 1000 elif source == 'seconds': # Unix 秒转毫秒 return ts * 1000 elif source == 'iso': # ISO 字符串解析 dt = datetime.fromisoformat(ts.replace('Z', '+00:00')) return int(dt.timestamp() * 1000) return ts

使用示例

exchange_ts = 1700000000000 # 毫秒 etherscan_ts = 1700000000 # 秒 normalized = normalize_timestamp(etherscan_ts, 'seconds') print(f"对齐后: {normalized}") # 输出: 1700000000000

3. Redis 连接池耗尽

# 错误信息
asyncio.exceptions.CancelledError: Timeout awaiting connection

原因

- 高并发时连接数超过 poolsize - 连接未正确归还 - 网络抖动导致连接半开

解决方案

import redis.asyncio as redis from contextlib import asynccontextmanager class RedisPoolManager: """Redis 连接池管理器""" _instance = None def __new__(cls): if cls._instance is None: cls._instance = super().__new__(cls) cls._instance._initialized = False return cls._instance def __init__(self): if self._initialized: return self._pool = redis.ConnectionPool.from_url( "redis://localhost:6379", max_connections=100, # 增加连接数 decode_responses=True, socket_keepalive=True, socket_connect_timeout=5 ) self._client = redis.Redis(connection_pool=self._pool) self._initialized = True @asynccontextmanager async def get_client(self): """上下文管理器确保连接归还""" client = self._client try: yield client finally: pass # 连接池自动管理 async def health_check(self) -> bool: """健康检查""" try: return await self._client.ping() except Exception: return False

使用

manager = RedisPoolManager() async def safe_redis_operation(): async with manager.get_client() as client: await client.setex("key", 60, "value") result = await client.get("key") return result

架构优化实战经验

我在搭建因子库过程中踩过几个关键坑,这里分享实战经验:

1. 因子计算批处理优化

单因子计算延迟不可接受时,改为批量计算。我设计了增量计算策略:

class IncrementalFactorCompute:
    """
    增量计算策略
    - 全量计算: 每天/每周执行一次完整重算
    - 增量计算: 实时只更新变化部分
    """
    
    def __init__(self, engine: FactorComputeEngine):
        self.engine