#!/usr/bin/env python3 """ 每日美股持仓盈亏报告 """ import requests import smtplib from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart from datetime import datetime # Notion API 配置 NOTION_API_KEY = "ntn_c43902219395mirQBetIfYoww1qKCAF14GBRUQeDee29o2" DATABASE_ID = "2fb105ad-7873-8175-bbbd-e5b87cf101d9" # 邮件配置 SMTP_SERVER = "smtp.163.com" SMTP_PORT = 465 EMAIL_USER = "work_fyx02@163.com" EMAIL_PASSWORD = "QLrTpw7SDxrMuAzh" RECIPIENT_EMAIL = "Yaxing_feng@dgmaorui.com" # 指数代码 INDICES = { "^GSPC": "标普500", "^IXIC": "纳斯达克", "^DJI": "道琼斯" } def get_current_price(symbol): """获取股票当前价格(从Yahoo Finance)""" try: url = f"https://query1.finance.yahoo.com/v8/finance/chart/{symbol}" params = {"range": "1d", "interval": "1m"} response = requests.get(url, params=params, timeout=10) if response.status_code == 200: data = response.json() chart_result = data.get("chart", {}).get("result", [{}]) if chart_result: meta = chart_result[0].get("meta", {}) return meta.get("regularMarketPrice") except Exception as e: print(f"获取价格失败 {symbol}: {e}") return None def get_index_prices(): """获取指数价格""" prices = {} for symbol, name in INDICES.items(): price = get_current_price(symbol) if price: prices[symbol] = {"name": name, "price": price} return prices def get_all_positions(): """从Notion获取所有持仓""" url = "https://api.notion.com/v1/databases/" + DATABASE_ID + "/query" headers = { "Authorization": f"Bearer {NOTION_API_KEY}", "Content-Type": "application/json", "Notion-Version": "2022-06-28" } response = requests.post(url, headers=headers) if response.status_code == 200: results = response.json().get("results", []) positions = [] for page in results: props = page["properties"] symbol = props.get("股票代码", {}).get("title", [{}])[0].get("text", {}).get("content", "") shares = props.get("持仓数量", {}).get("number", 0) cost = props.get("买入成本", {}).get("number", 0) current_price = props.get("当前价格", {}).get("number", 0) market_value = props.get("当前市值", {}).get("number", 0) pnl_amount = props.get("盈亏金额", {}).get("number", 0) pnl_percent = props.get("盈亏百分比", {}).get("number", 0) if symbol: positions.append({ "symbol": symbol, "shares": shares, "cost": cost, "current_price": current_price, "market_value": market_value, "pnl_amount": pnl_amount, "pnl_percent": pnl_percent }) return positions else: print(f"获取持仓失败: {response.status_code}") return [] def calculate_portfolio_summary(positions): """计算投资组合汇总""" total_cost = sum(p.get("cost", 0) or 0 for p in positions) total_market_value = sum(p.get("market_value", 0) or 0 for p in positions) total_pnl = total_market_value - total_cost total_pnl_percent = (total_pnl / total_cost * 100) if total_cost > 0 else 0 return { "total_cost": total_cost, "total_market_value": total_market_value, "total_pnl": total_pnl, "total_pnl_percent": total_pnl_percent } def generate_html_report(positions, summary, indices): """生成HTML报告""" # 获取当前时间(UTC+8) now = datetime.utcnow() date_str = now.strftime("%Y-%m-%d %H:%M") # 生成持仓明细HTML positions_html = "" for p in positions: symbol = p.get("symbol", "") shares = p.get("shares", 0) or 0 cost = p.get("cost", 0) or 0 current_price = p.get("current_price", 0) or 0 market_value = p.get("market_value", 0) or 0 pnl_amount = p.get("pnl_amount", 0) or 0 pnl_percent = p.get("pnl_percent", 0) or 0 pnl_color = "green" if pnl_amount >= 0 else "red" positions_html += f"""
报告时间:{date_str} (北京时间)
| 股票代码 | 持仓数量 | 买入成本 | 当前价格 | 当前市值 | 盈亏金额 | 盈亏百分比 |
|---|
| 指数名称 | 当前点位 |
|---|
此报告由 OpenClaw 自动生成
数据来源:Yahoo Finance API
更新时间:每日早上 6:30 (北京时间)