feat(tests): 添加多个数据获取脚本测试示例
- 新增获取3033.HK复权与不复权价格对比脚本,支持代理配置 - 新增使用Tushare获取AU9999黄金现货数据脚本,支持日期范围查询和CSV保存 - 新增从OKX通过CCXT库获取BTC/USDT日线数据脚本,支持HTTP代理和时间范围过滤 - 所有脚本均包含打印数据显示的格式化输出 - 各脚本提供主函数入口,易于独立运行和调试
This commit is contained in:
100
tests/fetch_3033.py
Normal file
100
tests/fetch_3033.py
Normal file
@@ -0,0 +1,100 @@
|
||||
"""
|
||||
获取 3033.HK 最新10条数据
|
||||
对比复权和不复权价格
|
||||
"""
|
||||
|
||||
import yfinance as yf
|
||||
import pandas as pd
|
||||
from typing import Optional
|
||||
import os
|
||||
|
||||
|
||||
def fetch_3033(period: str = "10d", proxy: Optional[str] = None) -> None:
|
||||
"""
|
||||
获取 3033.HK 数据,对比复权和不复权价格
|
||||
|
||||
Args:
|
||||
period: 获取周期,默认10天
|
||||
proxy: 代理地址,如 "socks5://127.0.0.1:1080"
|
||||
"""
|
||||
# 设置代理
|
||||
if proxy:
|
||||
os.environ['HTTP_PROXY'] = proxy
|
||||
os.environ['HTTPS_PROXY'] = proxy
|
||||
print(f"使用代理: {proxy}")
|
||||
|
||||
code = "3033.HK"
|
||||
ticker = yf.Ticker(code)
|
||||
|
||||
print(f"\n{'='*80}")
|
||||
print(f"获取 {code} 最新数据")
|
||||
print(f"{'='*80}\n")
|
||||
|
||||
# 1. 不复权价格 (auto_adjust=False)
|
||||
print("【1. 不复权价格】(auto_adjust=False)")
|
||||
print("-" * 80)
|
||||
data_raw = ticker.history(period=period, auto_adjust=False)
|
||||
if not data_raw.empty:
|
||||
print(f"{'日期':<12} {'开盘':>10} {'收盘':>10} {'最高':>10} {'最低':>10} {'成交量':>12}")
|
||||
print("-" * 80)
|
||||
for date, row in data_raw.tail(10).iterrows():
|
||||
date_str = date.strftime('%Y-%m-%d')
|
||||
print(f"{date_str:<12} {row['Open']:>10.3f} {row['Close']:>10.3f} {row['High']:>10.3f} {row['Low']:>10.3f} {row['Volume']:>12.0f}")
|
||||
print(f"\n最新收盘价: {data_raw['Close'].iloc[-1]:.3f}")
|
||||
else:
|
||||
print("无数据")
|
||||
|
||||
# 2. 前复权价格 (auto_adjust=True,默认)
|
||||
print(f"\n{'='*80}")
|
||||
print("【2. 前复权价格】(auto_adjust=True,默认)")
|
||||
print("-" * 80)
|
||||
data_adj = ticker.history(period=period, auto_adjust=True)
|
||||
if not data_adj.empty:
|
||||
print(f"{'日期':<12} {'开盘':>10} {'收盘':>10} {'最高':>10} {'最低':>10} {'成交量':>12}")
|
||||
print("-" * 80)
|
||||
for date, row in data_adj.tail(10).iterrows():
|
||||
date_str = date.strftime('%Y-%m-%d')
|
||||
print(f"{date_str:<12} {row['Open']:>10.3f} {row['Close']:>10.3f} {row['High']:>10.3f} {row['Low']:>10.3f} {row['Volume']:>12.0f}")
|
||||
print(f"\n最新收盘价: {data_adj['Close'].iloc[-1]:.3f}")
|
||||
else:
|
||||
print("无数据")
|
||||
|
||||
# 3. 对比
|
||||
if not data_raw.empty and not data_adj.empty:
|
||||
print(f"\n{'='*80}")
|
||||
print("【3. 价格对比】")
|
||||
print("-" * 80)
|
||||
latest_raw = data_raw['Close'].iloc[-1]
|
||||
latest_adj = data_adj['Close'].iloc[-1]
|
||||
print(f"不复权最新价: {latest_raw:.3f}")
|
||||
print(f"前复权最新价: {latest_adj:.3f}")
|
||||
print(f"差异: {abs(latest_raw - latest_adj):.3f} ({abs(latest_raw - latest_adj)/latest_raw*100:.2f}%)")
|
||||
|
||||
print(f"\n{'='*80}")
|
||||
|
||||
|
||||
def main():
|
||||
"""主函数"""
|
||||
# 检查是否需要使用代理
|
||||
# 如果 Clash 开启,使用 Clash HTTP 代理
|
||||
# 如果 SSH 隧道开启,使用 SOCKS5 代理
|
||||
|
||||
# 使用 Clash HTTP 代理
|
||||
proxy = "http://127.0.0.1:7890"
|
||||
|
||||
# 如果 Clash 不可用,尝试 SOCKS5 代理(SSH 隧道)
|
||||
# proxy = "socks5://127.0.0.1:1080"
|
||||
|
||||
try:
|
||||
fetch_3033(period="10d", proxy=proxy)
|
||||
except Exception as e:
|
||||
print(f"使用代理 {proxy} 失败: {e}")
|
||||
print("\n尝试不使用代理...")
|
||||
try:
|
||||
fetch_3033(period="10d", proxy=None)
|
||||
except Exception as e2:
|
||||
print(f"无代理也失败: {e2}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
119
tests/fetch_au9999.py
Normal file
119
tests/fetch_au9999.py
Normal file
@@ -0,0 +1,119 @@
|
||||
"""
|
||||
使用 Tushare 获取 AU9999 黄金数据
|
||||
"""
|
||||
|
||||
import os
|
||||
import pandas as pd
|
||||
import tushare as ts
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Optional
|
||||
from dotenv import load_dotenv
|
||||
|
||||
|
||||
def get_tushare_token() -> str:
|
||||
"""从环境变量获取 Tushare token"""
|
||||
load_dotenv()
|
||||
token = os.environ.get('TUSHARE_TOKEN')
|
||||
if not token:
|
||||
raise ValueError("未设置 TUSHARE_TOKEN 环境变量")
|
||||
return token
|
||||
|
||||
|
||||
def fetch_au9999(start_date: str, end_date: str) -> Optional[pd.DataFrame]:
|
||||
"""
|
||||
获取 AU9999 黄金现货数据(使用上期所黄金主力合约 AU.SHF)
|
||||
|
||||
Args:
|
||||
start_date: 开始日期 (YYYY-MM-DD)
|
||||
end_date: 结束日期 (YYYY-MM-DD)
|
||||
|
||||
Returns:
|
||||
DataFrame with columns: date, open, high, low, close, vol
|
||||
"""
|
||||
try:
|
||||
# 初始化 Tushare
|
||||
pro = ts.pro_api(get_tushare_token())
|
||||
|
||||
# 转换日期格式
|
||||
start_str = start_date.replace('-', '')
|
||||
end_str = end_date.replace('-', '')
|
||||
|
||||
print(f"从 Tushare 获取 AU9999 数据...")
|
||||
print(f"时间范围: {start_date} ~ {end_date}")
|
||||
|
||||
# 获取黄金期货主力合约数据
|
||||
# ts_code: AU.SHF 表示上海期货交易所黄金主力合约
|
||||
df = pro.fut_daily(ts_code='AU.SHF', start_date=start_str, end_date=end_str)
|
||||
|
||||
if df is None or df.empty:
|
||||
print("未获取到数据")
|
||||
return None
|
||||
|
||||
# 标准化列名
|
||||
df = df.rename(columns={
|
||||
'trade_date': 'date',
|
||||
'open': 'open',
|
||||
'high': 'high',
|
||||
'low': 'low',
|
||||
'close': 'close',
|
||||
'vol': 'volume',
|
||||
})
|
||||
|
||||
# 转换日期格式
|
||||
df['date'] = pd.to_datetime(df['date'])
|
||||
df = df.set_index('date')
|
||||
df = df.sort_index()
|
||||
|
||||
# 选择需要的列
|
||||
df = df[['open', 'high', 'low', 'close', 'volume']]
|
||||
|
||||
print(f"✓ 获取成功: {len(df)} 条数据")
|
||||
print(f"时间范围: {df.index[0].strftime('%Y-%m-%d')} ~ {df.index[-1].strftime('%Y-%m-%d')}")
|
||||
|
||||
return df
|
||||
|
||||
except Exception as e:
|
||||
print(f"获取数据失败: {e}")
|
||||
return None
|
||||
|
||||
|
||||
def print_au9999_data(df: pd.DataFrame):
|
||||
"""打印 AU9999 数据"""
|
||||
print("\n" + "="*80)
|
||||
print("AU9999 黄金数据 (上期所主力合约 AU.SHF)")
|
||||
print("="*80)
|
||||
print(f"{'日期':<15} {'开盘价':>12} {'最高价':>12} {'最低价':>12} {'收盘价':>12} {'成交量':>12}")
|
||||
print("-"*80)
|
||||
|
||||
for date, row in df.iterrows():
|
||||
date_str = date.strftime('%Y-%m-%d')
|
||||
print(f"{date_str:<15} {row['open']:>12.2f} {row['high']:>12.2f} {row['low']:>12.2f} {row['close']:>12.2f} {row['volume']:>12.2f}")
|
||||
|
||||
print("="*80)
|
||||
|
||||
|
||||
def main():
|
||||
"""主函数"""
|
||||
# 计算最近30天的日期范围
|
||||
end_date = datetime.now()
|
||||
start_date = end_date - timedelta(days=30)
|
||||
|
||||
start_str = start_date.strftime('%Y-%m-%d')
|
||||
end_str = end_date.strftime('%Y-%m-%d')
|
||||
|
||||
# 获取数据
|
||||
df = fetch_au9999(start_str, end_str)
|
||||
|
||||
if df is not None:
|
||||
print_au9999_data(df)
|
||||
|
||||
# 保存到CSV
|
||||
output_file = "au9999_data.csv"
|
||||
df.to_csv(output_file)
|
||||
print(f"\n数据已保存: {output_file}")
|
||||
else:
|
||||
print("获取数据失败")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
109
tests/fetch_btc_okx.py
Normal file
109
tests/fetch_btc_okx.py
Normal file
@@ -0,0 +1,109 @@
|
||||
"""
|
||||
从 OKX 获取 BTC 日线数据
|
||||
使用 CCXT 库,支持本地 Clash HTTP 代理
|
||||
"""
|
||||
|
||||
import ccxt
|
||||
import pandas as pd
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Optional
|
||||
import os
|
||||
|
||||
|
||||
def fetch_btc_okx(days: int = 10, http_proxy: str = "http://127.0.0.1:7890") -> Optional[pd.DataFrame]:
|
||||
"""
|
||||
从 OKX 获取 BTC/USDT 日线数据
|
||||
|
||||
Args:
|
||||
days: 获取最近多少天的数据,默认10天
|
||||
http_proxy: HTTP 代理地址,默认使用本地 Clash 代理 http://127.0.0.1:7890
|
||||
|
||||
Returns:
|
||||
DataFrame with columns: date, open, high, low, close, volume
|
||||
"""
|
||||
try:
|
||||
# 配置 CCXT
|
||||
config = {'enableRateLimit': True}
|
||||
|
||||
# 设置 HTTP 代理
|
||||
if http_proxy:
|
||||
config['proxies'] = {'http': http_proxy, 'https': http_proxy}
|
||||
print(f"使用 HTTP 代理: {http_proxy}")
|
||||
|
||||
# 创建 OKX 交易所实例
|
||||
exchange = ccxt.okx(config)
|
||||
|
||||
# 计算时间范围
|
||||
end_date = datetime.now()
|
||||
start_date = end_date - timedelta(days=days + 5) # 多取几天确保有足够数据
|
||||
|
||||
since = int(start_date.timestamp() * 1000)
|
||||
|
||||
print(f"从 OKX 获取 BTC/USDT 最近{days}天数据...")
|
||||
print(f"时间范围: {start_date.strftime('%Y-%m-%d')} ~ {end_date.strftime('%Y-%m-%d')}")
|
||||
|
||||
# 获取日线数据
|
||||
ohlcv = exchange.fetch_ohlcv('ETH/USDT', '1d', since, limit=100)
|
||||
|
||||
if not ohlcv:
|
||||
print("未获取到数据")
|
||||
return None
|
||||
|
||||
# 转换为 DataFrame
|
||||
df = pd.DataFrame(ohlcv, columns=['timestamp', 'open', 'high', 'low', 'close', 'volume'])
|
||||
|
||||
# 转换时间戳为日期索引(UTC -> 北京时间)
|
||||
df['date'] = pd.to_datetime(df['timestamp'], unit='ms', utc=True).dt.tz_convert('Asia/Shanghai')
|
||||
df = df.set_index('date')
|
||||
df = df[['open', 'high', 'low', 'close', 'volume']]
|
||||
|
||||
# 过滤最近N天
|
||||
df = df.tail(days)
|
||||
|
||||
print(f"✓ 获取成功: {len(df)} 条数据")
|
||||
print(f"时间范围: {df.index[0].strftime('%Y-%m-%d %H:%M')} ~ {df.index[-1].strftime('%Y-%m-%d %H:%M')}")
|
||||
|
||||
return df
|
||||
|
||||
except Exception as e:
|
||||
print(f"获取数据失败: {e}")
|
||||
return None
|
||||
|
||||
|
||||
def print_btc_data(df: pd.DataFrame):
|
||||
"""打印 BTC 数据"""
|
||||
print("\n" + "="*80)
|
||||
print("BTC/USDT 日线数据 (OKX)")
|
||||
print("="*80)
|
||||
print(f"{'日期':<20} {'开盘价':>12} {'最高价':>12} {'最低价':>12} {'收盘价':>12} {'成交量':>12}")
|
||||
print("-"*80)
|
||||
|
||||
for date, row in df.iterrows():
|
||||
date_str = date.strftime('%Y-%m-%d %H:%M')
|
||||
print(f"{date_str:<20} {row['open']:>12.2f} {row['high']:>12.2f} {row['low']:>12.2f} {row['close']:>12.2f} {row['volume']:>12.4f}")
|
||||
|
||||
print("="*80)
|
||||
|
||||
|
||||
def main():
|
||||
"""主函数"""
|
||||
# 使用本地 Clash HTTP 代理(默认端口 7890)
|
||||
# 如果 Clash 使用其他端口,请修改此处
|
||||
http_proxy = "http://127.0.0.1:7890"
|
||||
|
||||
# 获取数据
|
||||
df = fetch_btc_okx(days=10, http_proxy=http_proxy)
|
||||
|
||||
if df is not None:
|
||||
print_btc_data(df)
|
||||
|
||||
# 保存到CSV
|
||||
output_file = "btc_okx_1d.csv"
|
||||
df.to_csv(output_file)
|
||||
print(f"\n数据已保存: {output_file}")
|
||||
else:
|
||||
print("获取数据失败")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user