160 lines
4.9 KiB
Python
160 lines
4.9 KiB
Python
"""
|
|
数据获取模块:使用ccxt库拉取数字货币市场数据
|
|
"""
|
|
|
|
import ccxt
|
|
import pandas as pd
|
|
import numpy as np
|
|
from datetime import datetime, timedelta
|
|
import time
|
|
from typing import List, Dict, Optional, Tuple
|
|
import logging
|
|
|
|
logging.basicConfig(level=logging.INFO)
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class DataFetcher:
|
|
"""数据获取器:负责从交易所获取K线数据"""
|
|
|
|
def __init__(self, exchange_name: str = 'binance'):
|
|
"""
|
|
初始化数据获取器
|
|
|
|
Args:
|
|
exchange_name: 交易所名称,默认为binance
|
|
"""
|
|
self.exchange_name = exchange_name
|
|
self.exchange = self._init_exchange()
|
|
|
|
def _init_exchange(self) -> ccxt.Exchange:
|
|
"""初始化交易所连接"""
|
|
try:
|
|
exchange_class = getattr(ccxt, self.exchange_name)
|
|
exchange = exchange_class({
|
|
'apiKey': '', # 对于公开数据不需要API密钥
|
|
'secret': '',
|
|
'timeout': 30000,
|
|
'enableRateLimit': True,
|
|
})
|
|
return exchange
|
|
except Exception as e:
|
|
logger.error(f"初始化交易所失败: {e}")
|
|
raise
|
|
|
|
def fetch_klines(self,
|
|
symbol: str,
|
|
timeframe: str = '1h',
|
|
limit: int = 1000,
|
|
since: Optional[int] = None) -> pd.DataFrame:
|
|
"""
|
|
获取K线数据
|
|
|
|
Args:
|
|
symbol: 交易对符号,如'BTC/USDT'
|
|
timeframe: 时间周期,如'1h', '4h', '1d'
|
|
limit: 获取的K线数量
|
|
since: 开始时间戳(毫秒)
|
|
|
|
Returns:
|
|
包含K线数据的DataFrame
|
|
"""
|
|
try:
|
|
logger.info(f"正在获取 {symbol} {timeframe} 数据,数量: {limit}")
|
|
|
|
# 获取原始数据
|
|
ohlcv = self.exchange.fetch_ohlcv(
|
|
symbol=symbol,
|
|
timeframe=timeframe,
|
|
limit=limit,
|
|
since=since
|
|
)
|
|
|
|
if not ohlcv:
|
|
raise ValueError("未获取到数据")
|
|
|
|
# 转换为DataFrame
|
|
df = pd.DataFrame(ohlcv, columns=[
|
|
'timestamp', 'open', 'high', 'low', 'close', 'volume'
|
|
])
|
|
|
|
# 转换时间戳为datetime
|
|
df['datetime'] = pd.to_datetime(df['timestamp'], unit='ms')
|
|
df.set_index('datetime', inplace=True)
|
|
|
|
# 确保数据类型正确
|
|
price_columns = ['open', 'high', 'low', 'close']
|
|
df[price_columns] = df[price_columns].astype(float)
|
|
df['volume'] = df['volume'].astype(float)
|
|
|
|
logger.info(f"成功获取 {len(df)} 条K线数据")
|
|
return df
|
|
|
|
except Exception as e:
|
|
logger.error(f"获取K线数据失败: {e}")
|
|
raise
|
|
|
|
def fetch_multiple_timeframes(self,
|
|
symbol: str,
|
|
timeframes: List[str],
|
|
limit: int = 1000) -> Dict[str, pd.DataFrame]:
|
|
"""
|
|
获取多个时间周期的数据
|
|
|
|
Args:
|
|
symbol: 交易对符号
|
|
timeframes: 时间周期列表
|
|
limit: 每个周期获取的数量
|
|
|
|
Returns:
|
|
字典,键为时间周期,值为对应的DataFrame
|
|
"""
|
|
result = {}
|
|
|
|
for timeframe in timeframes:
|
|
try:
|
|
df = self.fetch_klines(symbol, timeframe, limit)
|
|
result[timeframe] = df
|
|
|
|
# 避免请求过于频繁
|
|
time.sleep(self.exchange.rateLimit / 1000)
|
|
|
|
except Exception as e:
|
|
logger.error(f"获取 {timeframe} 数据失败: {e}")
|
|
continue
|
|
|
|
return result
|
|
|
|
def get_latest_price(self, symbol: str) -> float:
|
|
"""
|
|
获取最新价格
|
|
|
|
Args:
|
|
symbol: 交易对符号
|
|
|
|
Returns:
|
|
最新价格
|
|
"""
|
|
try:
|
|
ticker = self.exchange.fetch_ticker(symbol)
|
|
return float(ticker['last'])
|
|
except Exception as e:
|
|
logger.error(f"获取最新价格失败: {e}")
|
|
raise
|
|
|
|
def validate_symbol(self, symbol: str) -> bool:
|
|
"""
|
|
验证交易对是否有效
|
|
|
|
Args:
|
|
symbol: 交易对符号
|
|
|
|
Returns:
|
|
是否有效
|
|
"""
|
|
try:
|
|
markets = self.exchange.load_markets()
|
|
return symbol in markets
|
|
except Exception as e:
|
|
logger.error(f"验证交易对失败: {e}")
|
|
return False |