豆包总提供
1 妖股、急涨股:以10日线或者20日为准,跌破10日线或者20日线就卖。
2. 缓缓上涨的票:以60日均线为准,跌破60日均线就卖。
3. 动态止损:当天收盘价相比于4天内最高收盘价跌了10%就卖。
4. 涨停股:第二天下午2:55分看看是否还是涨停,继续涨停继续持有,一旦没涨停了,就卖掉。只要触发其中一个条件,就卖出。
代码如下
import akshare as ak
import pandas as pd
import numpy as np
import yagmail
import os
from datetime import datetime
# ETF代码与名称的映射
etf_names = {
'510300': '300ETF',
'159915': '创业板',
'513050': '中概互联网ETF',
'159941': '纳指ETF',
'518880': '黄金ETF',
'511090': '30年国债ETF'
}
# 获取ETF数据的函数
def fetch_etf_data(symbol="510300"):
# 使用 akshare 的 fund_etf_hist_em 接口获取 ETF 数据
df = ak.fund_etf_hist_em(symbol=symbol, period="daily", 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 generate_sell_signals(df):
# 计算10日、20日和60日均线
df['MA10'] = df['Close'].rolling(window=10).mean()
df['MA20'] = df['Close'].rolling(window=20).mean()
df['MA60'] = df['Close'].rolling(window=60).mean()
# 计算4天内最高收盘价
df['High_4D'] = df['Close'].rolling(window=4).max()
# 计算动态止损条件:当天收盘价相比4天内最高收盘价下跌10%
df['Dynamic_StopLoss'] = df['Close'] < df['High_4D'] * 0.9
# 生成卖出信号
df['Sell_Signal'] = '无信号'
df.loc[df['Close'] < df['MA10'], 'Sell_Signal'] = '跌破10日均线'
df.loc[df['Close'] < df['MA20'], 'Sell_Signal'] = '跌破20日均线'
df.loc[df['Close'] < df['MA60'], 'Sell_Signal'] = '跌破60日均线'
df.loc[df['Dynamic_StopLoss'], 'Sell_Signal'] = '动态止损'
# 涨停股判断(涨停定义为当日涨幅≥9.9%)
df['Previous_Close'] = df['Close'].shift(1)
df['Pct_Change'] = (df['Close'] - df['Previous_Close']) / df['Previous_Close'] * 100
df['Limit_Up'] = df['Pct_Change'] >= 9.9
df['Limit_Up_Signal'] = df['Limit_Up'].shift(1) # 前一天是否涨停
# 如果前一天涨停,且当天未涨停,则卖出
df.loc[df['Limit_Up_Signal'] & ~df['Limit_Up'], 'Sell_Signal'] = '涨停股卖出'
return df
# 发送邮件的函数
def send_email(html_content, today, attachment_path=None):
try:
# 邮件主题
subject = f"ETF 卖出信号分析报告 ({today})"
# 初始化 yagmail
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], attachments=attachment_path)
print("邮件发送成功")
except Exception as e:
print(f"邮件发送失败: {e}")
# 生成简单HTML表格的函数
def generate_simple_html_table(df, name, symbol):
# 表头
html = f"<h3>{name} ({symbol}) 最近2天的卖出信号:</h3>"
html += "<table border='1'><tr><th>日期</th><th>收盘价</th><th>MA10</th><th>MA20</th><th>MA60</th><th>卖出信号</th></tr>"
# 表格内容
for date, row in df.tail(2).iterrows():
# 根据信号设置颜色
if row['Sell_Signal'] == '跌破60日均线':
signal_color = "red" # 标红
else:
signal_color = "black"
html += f"<tr><td>{date.strftime('%Y-%m-%d')}</td><td>{round(row['Close'], 2)}</td><td>{round(row['MA10'], 2)}</td><td>{round(row['MA20'], 2)}</td><td>{round(row['MA60'], 2)}</td><td><span style='color:{signal_color};'>{row['Sell_Signal']}</span></td></tr>"
html += "</table>"
return html
# 主程序
if __name__ == "__main__":
# 获取当前日期
today = datetime.today().strftime('%Y-%m-%d')
# 存储所有ETF的分析结果
results = []
# 遍历所有ETF
for symbol, name in etf_names.items():
print(f"\n正在分析 {name} ({symbol}) 的卖出信号...")
# 获取ETF数据
df = fetch_etf_data(symbol)
# 计算卖出信号
df = generate_sell_signals(df)
# 生成简单HTML表格
html_table = generate_simple_html_table(df, name, symbol)
results.append(html_table)
# 将所有结果合并为HTML内容
html_content = "<html><body>" + "<br>".join(results) + "</body></html>"
# 发送邮件
send_email(html_content, today)