代码如下
import akshare as ak
import pandas as pd
import yagmail
import os
from datetime import datetime, timedelta
# ETF代码与名称的映射
etf_names = {
'510300': '300ETF',
'159915': '创业板',
'513050': '中概互联网ETF',
'159941': '纳指ETF',
'518880': '黄金ETF',
'511090': '30年国债ETF'
}
# 获取ETF历史数据的函数
def fetch_etf_data(symbol, start_date, end_date):
df = ak.fund_etf_hist_em(symbol=symbol, period="daily", start_date=start_date, end_date=end_date, adjust='qfq')
df['日期'] = pd.to_datetime(df['日期'])
df.set_index('日期', inplace=True)
df.rename(columns={
'开盘': 'Open',
'最高': 'High',
'最低': 'Low',
'收盘': 'Close',
'成交量': 'Volume'
}, inplace=True)
return df
# 计算年化波动率的函数
def calculate_annualized_volatility(df, trading_days):
if df.empty:
return None
# 计算收益率
df['收益率'] = df['Close'].pct_change()
# 计算年化波动率
volatility = df['收益率'].std() * (trading_days ** 0.5)
return round(volatility * 100, 2) # 转换为百分比格式
# 发送邮件的函数
def send_email(html_content, today):
try:
subject = f"ETF波动率分析报告({today})"
yag = yagmail.SMTP(user=os.getenv('from_email'), password=os.getenv('password'), host='smtp.qq.com', port=465, smtp_ssl=True)
yag.send(to=os.getenv('to_email'), subject=subject, contents=[html_content])
print("邮件发送成功")
except Exception as e:
print(f"邮件发送失败: {e}")
# 主程序
if __name__ == "__main__":
today = datetime.today()
last_year_today = (today - timedelta(days=365)).strftime('%Y%m%d')
today_str = today.strftime('%Y%m%d')
results = []
for symbol, name in etf_names.items():
print(f"正在处理 {name} ({symbol})...")
df = fetch_etf_data(symbol, start_date=last_year_today, end_date=today_str)
if not df.empty:
# 计算过去一年、过去一个月和过去一周的年化波动率
volatility_1y = calculate_annualized_volatility(df, trading_days=252) # 过去一年
volatility_1m = calculate_annualized_volatility(df.iloc[-21:], trading_days=252) # 过去一个月
volatility_1w = calculate_annualized_volatility(df.iloc[-5:], trading_days=252) # 过去一周
# 判断波动率的变化方向,并添加颜色
trend_1m_color = "red" if volatility_1m > volatility_1y else "green" if volatility_1m < volatility_1y else "gray"
trend_1m_arrow = "↑" if volatility_1m > volatility_1y else "↓" if volatility_1m < volatility_1y else "-"
trend_1w_color = "red" if volatility_1w > volatility_1m else "green" if volatility_1w < volatility_1m else "gray"
trend_1w_arrow = "↑" if volatility_1w > volatility_1m else "↓" if volatility_1w < volatility_1m else "-"
results.append({
'ETF名称': name,
'代码': symbol,
'过去一年年化波动率 (%)': volatility_1y,
'过去一月年化波动率 (%)': f"{volatility_1m} <span style='color:{trend_1m_color}'>{trend_1m_arrow}</span>",
'过去一周年化波动率 (%)': f"{volatility_1w} <span style='color:{trend_1w_color}'>{trend_1w_arrow}</span>"
})
else:
print(f"未获取到 {name} ({symbol}) 的数据。")
# 将结果转换为DataFrame
results_df = pd.DataFrame(results)
# 生成简单的HTML表格
html_content = f"""
<html>
<body>
<h1>ETF波动率分析报告</h1>
<p>报告日期:{today.strftime('%Y-%m-%d')}</p>
<table border="1">
<tr>
<th>ETF名称</th>
<th>代码</th>
<th>过去一年年化波动率 (%)</th>
<th>过去一月年化波动率 (%)</th>
<th>过去一周年化波动率 (%)</th>
</tr>
"""
for _, row in results_df.iterrows():
html_content += f"""
<tr>
<td>{row['ETF名称']}</td>
<td>{row['代码']}</td>
<td>{row['过去一年年化波动率 (%)']}</td>
<td>{row['过去一月年化波动率 (%)']}</td>
<td>{row['过去一周年化波动率 (%)']}</td>
</tr>
"""
html_content += """
</table>
</body>
</html>
"""
# 发送邮件
send_email(html_content, today.strftime('%Y-%m-%d'))