Files
etf/fetch_159930.py
aszerW 6b59855c28 experiment(rotation): 同大类扩充与纳指vs标普替换对比实验
技术修复:
- SOCKS5代理IPv6问题:socks5:// → socks5h:// (hybrid_source.py, yfinance_source.py)

目录整理:
- scripts/ → 仅保留策略入口(daily_scheduler, run_rotation, run_cci_screener)
- 实验脚本移至 tests/experiments/
- 工具脚本移至 tests/utils/
- 实验记录新增 docs/experiments/
- results/ 添加到 gitignore

实验结果:

实验001 - 同大类扩充(添加标普500):
├─ 累计收益: 1467.35% → 1176.26% (-291%)
├─ CAGR: 48.10% → 43.82% (-4.28%)
├─ 调仓次数: 459 → 501 (+42次)
└─ 结论: 添加同大类标的不增加跨类分散,反而侵蚀收益

实验002 - 纳指vs标普替换对比:
├─ 累计收益: 1467.35% → 1118.77% (-348%)
├─ CAGR: 48.10% → 42.87% (-5.22%)
├─ Sharpe: 2.21 → 2.08 (-0.13)
├─ MaxDD: -17.33% → -15.14% (+2.18%)
└─ 结论: 纳指100优于标普500,成长风格更适合动量策略

策略建议:
- 保持纳指100作为美股大类代表
- 不添加同大类新标的(避免类内切换成本)
- 新增标的应优先考虑新大类(增加跨类分散)
2026-05-06 20:43:38 +08:00

184 lines
5.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
"""
获取159930 ETF最新10天的收盘价、净值并计算溢价率
"""
import os
import pandas as pd
import tushare as ts
from datetime import datetime, timedelta
# 设置Tushare token
def get_tushare_token():
# 首先尝试从环境变量获取
token = os.environ.get("TUSHARE_TOKEN")
if token:
return token
# 尝试从.env文件获取
try:
from dotenv import load_dotenv
load_dotenv()
token = os.environ.get("TUSHARE_TOKEN")
if token:
return token
except ImportError:
pass
# 手动读取.env文件
env_path = os.path.join(os.path.dirname(__file__), '.env')
if os.path.exists(env_path):
with open(env_path, 'r') as f:
for line in f:
if line.startswith('TUSHARE_TOKEN='):
token = line.strip().split('=', 1)[1].strip().strip('"').strip("'")
if token:
return token
raise ValueError("请设置 TUSHARE_TOKEN 环境变量或在.env文件中配置")
def fetch_etf_data(etf_code: str, days: int = 10):
"""
获取ETF最新N天的价格、净值数据
Args:
etf_code: ETF代码"159930.SZ"
days: 获取天数
Returns:
DataFrame: 包含日期、收盘价、净值、溢价率
"""
pro = ts.pro_api(get_tushare_token())
# 计算日期范围(多取几天确保有足够数据)
end_date = datetime.now()
start_date = end_date - timedelta(days=days + 5)
start_str = start_date.strftime('%Y%m%d')
end_str = end_date.strftime('%Y%m%d')
# 转换代码格式
ts_code = etf_code.replace(".SS", ".SH")
print(f"获取 {etf_code} 数据...")
print(f"日期范围: {start_str} ~ {end_str}")
# 1. 获取ETF价格数据fund_daily接口
print("\n1. 获取ETF价格数据...")
try:
price_df = pro.fund_daily(
ts_code=ts_code,
start_date=start_str,
end_date=end_str
)
if price_df is not None and len(price_df) > 0:
price_df = price_df.sort_values('trade_date')
print(f" 获取到 {len(price_df)} 条价格数据")
print(f" 最新日期: {price_df['trade_date'].max()}")
else:
print(" 未获取到价格数据")
price_df = None
except Exception as e:
print(f" 获取价格数据失败: {e}")
price_df = None
# 2. 获取ETF净值数据fund_nav接口
print("\n2. 获取ETF净值数据...")
try:
# 净值通常滞后,多取一天
nav_end_date = end_date + timedelta(days=1)
nav_end_str = nav_end_date.strftime('%Y%m%d')
nav_df = pro.fund_nav(
ts_code=ts_code,
start_date=start_str,
end_date=nav_end_str
)
if nav_df is not None and len(nav_df) > 0:
nav_df = nav_df.sort_values('nav_date')
print(f" 获取到 {len(nav_df)} 条净值数据")
print(f" 最新日期: {nav_df['nav_date'].max()}")
else:
print(" 未获取到净值数据")
nav_df = None
except Exception as e:
print(f" 获取净值数据失败: {e}")
nav_df = None
# 3. 合并数据并计算溢价率
print("\n3. 合并数据并计算溢价率...")
if price_df is None:
print("错误: 没有价格数据")
return None
# 准备价格数据
price_df['date'] = pd.to_datetime(price_df['trade_date'])
price_df = price_df.set_index('date')
price_series = price_df['close']
# 准备净值数据
if nav_df is not None:
nav_df['date'] = pd.to_datetime(nav_df['nav_date'])
nav_df = nav_df.set_index('date')
nav_series = nav_df['unit_nav']
else:
nav_series = pd.Series()
# 创建结果DataFrame
result = pd.DataFrame({
'收盘价': price_series
})
# 对齐净值数据(按日期)
result = result.join(nav_series.rename('净值'), how='left')
# 计算溢价率
result['溢价率'] = (result['收盘价'] - result['净值']) / result['净值'] * 100
# 取最新N天
result = result.tail(days)
# 格式化输出
result['收盘价'] = result['收盘价'].round(3)
result['净值'] = result['净值'].round(3)
result['溢价率'] = result['溢价率'].round(2)
# 重置索引,将日期作为列
result = result.reset_index()
result['日期'] = result['date'].dt.strftime('%Y-%m-%d')
result = result[['日期', '收盘价', '净值', '溢价率']]
return result
def main():
"""主函数"""
etf_code = "159930.SZ"
days = 10
print("=" * 60)
print(f"ETF: {etf_code} (中证能源ETF)")
print(f"获取最近 {days} 天数据")
print("=" * 60)
df = fetch_etf_data(etf_code, days)
if df is not None and len(df) > 0:
print("\n" + "=" * 60)
print("结果表格:")
print("=" * 60)
print(df.to_string(index=False))
# 保存到CSV
output_file = f"{etf_code.replace('.', '_')}_latest_{days}days.csv"
df.to_csv(output_file, index=False, encoding='utf-8-sig')
print(f"\n数据已保存到: {output_file}")
else:
print("\n获取数据失败")
if __name__ == "__main__":
main()