#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ 每日美股持仓盈亏报告任务 """ import smtplib from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart from email.header import Header import requests import json from datetime import datetime # SMTP 配置 SMTP_SERVER = "smtp.163.com" SMTP_PORT = 465 EMAIL = "work_fyx02@163.com" AUTH_CODE = "QLrTpw7SDxrMuAzh" # 收件人 TO_EMAIL = "Yaxing_feng@dgmaorui.com" # Yahoo Finance API 配置 YAHOO_API_URL = "https://query1.finance.yahoo.com/v8/finance/chart/" # 指数配置 INDEXES = { "^GSPC": "标普500", "^IXIC": "纳斯达克", "^DJI": "道琼斯" } # 股票配置(从Notion数据库读取) STOCKS = [ {"symbol": "MSFT", "name": "Microsoft Corporation", "quantity": 1, "cost_basis": 439.00} ] def get_stock_price(symbol): """获取股票实时价格""" try: url = f"{YAHOO_API_URL}{symbol}" params = { "interval": "1d", "range": "1d" } headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36" } response = requests.get(url, params=params, headers=headers, timeout=10) if response.status_code == 200: data = response.json() if "chart" in data and "result" in data["chart"]: result = data["chart"]["result"] if result and len(result) > 0: meta = result[0].get("meta", {}) current_price = meta.get("regularMarketPrice") previous_close = meta.get("chartPreviousClose") if current_price and previous_close: change = current_price - previous_close change_percent = (change / previous_close) * 100 return { "symbol": symbol, "price": current_price, "previous_close": previous_close, "change": change, "change_percent": change_percent, "currency": meta.get("currency", "USD") } return None except Exception as e: print(f"获取 {symbol} 价格失败: {e}") return None def get_index_prices(): """获取指数价格""" indexes = {} for symbol, name in INDEXES.items(): price_data = get_stock_price(symbol) if price_data: indexes[symbol] = { "name": name, "price": price_data["price"], "change": price_data["change"], "change_percent": price_data["change_percent"] } return indexes def calculate_pnl(): """计算持仓盈亏""" total_cost = 0 total_market_value = 0 total_pnl = 0 stock_details = [] for stock in STOCKS: symbol = stock["symbol"] name = stock["name"] quantity = stock["quantity"] cost_basis = stock["cost_basis"] # 计算总成本 cost = quantity * cost_basis total_cost += cost # 获取当前价格 price_data = get_stock_price(symbol) if price_data: current_price = price_data["price"] change = price_data["change"] change_percent = price_data["change_percent"] # 计算当前市值 market_value = quantity * current_price total_market_value += market_value # 计算盈亏 pnl = market_value - cost total_pnl += pnl # 计算盈亏百分比 pnl_percent = (pnl / cost) * 100 stock_details.append({ "symbol": symbol, "name": name, "quantity": quantity, "cost_basis": cost_basis, "current_price": current_price, "cost": cost, "market_value": market_value, "pnl": pnl, "pnl_percent": pnl_percent, "change": change, "change_percent": change_percent }) # 计算总盈亏百分比 total_pnl_percent = 0 if total_cost > 0: total_pnl_percent = (total_pnl / total_cost) * 100 return { "date": datetime.now().strftime("%Y-%m-%d %H:%M:%S"), "total_cost": total_cost, "total_market_value": total_market_value, "total_pnl": total_pnl, "total_pnl_percent": total_pnl_percent, "stocks": stock_details } def send_pnl_report(): """发送盈亏报告邮件""" print("=" * 60) print("美股持仓盈亏日报") print("=" * 60) print(f"日期: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}") print(f"发件人: {EMAIL}") print(f"收件人: {TO_EMAIL}") print("=" * 60) try: # 获取盈亏数据 print("\n正在获取盈亏数据...") pnl_data = calculate_pnl() # 获取指数数据 print("正在获取指数数据...") indexes = get_index_prices() # 格式化邮件内容 date_str = datetime.now().strftime("%Y年%m月%d日") body = f"""📧 **美股持仓盈亏日报** **日期:** {date_str} --- ## 📊 持仓概览 **总持仓市值:** ${pnl_data['total_market_value']:.2f} **总成本:** ${pnl_data['total_cost']:.2f} **总盈亏:** ${pnl_data['total_pnl']:+.2f} ({pnl_data['total_pnl_percent']:+.2f}%) --- ## 📈 个股表现 """ # 添加个股详情 for stock in pnl_data["stocks"]: body += f""" ### {stock['symbol']} - {stock['name']} - **持仓:** {stock['quantity']}股 @ ${stock['cost_basis']} - **当前价:** ${stock['current_price']:.2f} - **总成本:** ${stock['cost']:.2f} - **当前市值:** ${stock['market_value']:.2f} - **盈亏:** ${stock['pnl']:+.2f} ({stock['pnl_percent']:+.2f}%) - **日涨跌:** ${stock['change']:+.2f} ({stock['change_percent']:+.2f}%) """ # 添加指数数据 body += """ --- ## 📊 大盘指数 """ for symbol, data in indexes.items(): body += f""" - **{data['name']}:** {data['price']:.2f} ({data['change_percent']:+.2f}%) """ # 添加总结 body += f""" --- ## 📝 总结 **今日盈亏:** ${pnl_data['total_pnl']:+.2f} **盈亏百分比:** ${pnl_data['total_pnl_percent']:+.2f}% **持仓股票数量:** {len(pnl_data['stocks'])} 只 --- **此报告由 OpenClaw 自动发送** **每日定时任务:美东时间收盘后(北京时间早上6点后)** """ # 创建邮件 msg = MIMEMultipart() msg["From"] = Header(f"OpenClaw <{EMAIL}>", "utf-8") msg["To"] = Header(TO_EMAIL, "utf-8") msg["Subject"] = Header(f"美股持仓盈亏日报 - {date_str}", "utf-8") msg.attach(MIMEText(body, "plain", "utf-8")) # 连接 SMTP 服务器并发送邮件 print("\n正在连接 SMTP 服务器...") with smtplib.SMTP_SSL(SMTP_SERVER, SMTP_PORT) as server: print(f"连接成功: {SMTP_SERVER}:{SMTP_PORT}") print("正在登录...") server.login(EMAIL, AUTH_CODE) print(f"登录成功: {EMAIL}") print("正在发送邮件...") server.send_message(msg) print("邮件发送成功!") print("\n" + "=" * 60) print("✅ 盈亏报告发送完成") print("=" * 60) print(f"收件人: {TO_EMAIL}") print(f"总盈亏: ${pnl_data['total_pnl']:+.2f} ({pnl_data['total_pnl_percent']:+.2f}%)") print("=" * 60) return True except Exception as e: print("\n" + "=" * 60) print("❌ 发送失败") print("=" * 60) print(f"错误信息: {e}") print("=" * 60) return False def main(): """主函数""" print("开始执行每日美股持仓盈亏报告任务...") print() success = send_pnl_report() if success: print("\n💡 任务完成:") print("1. 已获取股票价格和指数数据") print("2. 已计算持仓盈亏") print("3. 已发送报告到邮箱") else: print("\n🔧 任务失败:") print("请检查网络连接和API配置") if __name__ == "__main__": main()