diff --git a/tests/fetch_3033.py b/tests/fetch_3033.py new file mode 100644 index 0000000..3369b55 --- /dev/null +++ b/tests/fetch_3033.py @@ -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() diff --git a/tests/fetch_au9999.py b/tests/fetch_au9999.py new file mode 100644 index 0000000..2969d44 --- /dev/null +++ b/tests/fetch_au9999.py @@ -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() diff --git a/tests/fetch_btc_okx.py b/tests/fetch_btc_okx.py new file mode 100644 index 0000000..8afa632 --- /dev/null +++ b/tests/fetch_btc_okx.py @@ -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()