Files
server-configs/daily_full_report.py

136 lines
4.2 KiB
Python
Raw Permalink Normal View History

2026-02-13 22:24:27 +08:00
#!/usr/bin/env python3
"""
每日投资报告
- 用户表: 代码 + 持仓数量 + 持仓成本每股成本价
- 自动计算: 总成本 = 持仓成本 × 持仓数量
"""
from datetime import datetime
USER_DB_ID = "2fb105ad78738175bbbde5b87cf101d9"
NOTION_TOKEN = "ntn_c43902219395mirQBetIfYoww1qKCAF14GBRUQeDee29o2"
def get_user_holdings():
import requests
url = f"https://api.notion.com/v1/databases/{USER_DB_ID}/query"
headers = {
"Authorization": f"Bearer {NOTION_TOKEN}",
"Content-Type": "application/json",
"Notion-Version": "2022-06-28"
}
response = requests.post(url, headers=headers, json={}, timeout=30)
if response.status_code == 200:
data = response.json()
holdings = []
for item in data.get("results", []):
props = item.get("properties", {})
code = ""
titles = props.get("股票代码", {}).get("title", [])
if titles:
code = titles[0].get("plain_text", "").upper()
shares = props.get("持仓数量", {}).get("number", 0) or 0
cost_per_share = props.get("持仓成本", {}).get("number", 0) or 0
if code and shares > 0:
total_cost = round(shares * cost_per_share, 2)
holdings.append({
"code": code,
"shares": shares,
"cost_per_share": cost_per_share,
"total_cost": total_cost
})
return holdings
return []
def get_us_price(code):
import yfinance as yf
import time
time.sleep(3) # 避免限流
try:
ticker = yf.Ticker(code)
hist = ticker.history(period="1d")
if len(hist) > 0:
return round(hist['Close'].iloc[-1], 2)
except:
pass
return None
def main():
import requests
import yfinance as yf
print(f"\n{'='*85}")
print(f"📊 每日投资报告")
print(f"更新时间: {datetime.now().strftime('%Y-%m-%d %H:%M')}")
print(f"{'='*85}\n")
print("[1] 从用户表读取...")
holdings = get_user_holdings()
if not holdings:
print("❌ 无法获取数据\n")
return
print(f"\n[2] 获取股价...")
prices = {}
for h in holdings:
code = h["code"]
price = get_us_price(code)
if price:
prices[code] = price
print(f" {code}: ${price}")
else:
print(f" {code}: 获取失败")
print(f"\n{'='*85}")
print("📊 持仓盈亏")
print(f"{'='*85}\n")
print(f"{'代码':<10} {'股数':<10} {'每股成本':<12} {'总成本':<12} {'当前价':<12} {'市值':<12} {'盈亏':<18}")
print("-" * 100)
total_cost = 0
total_value = 0
for h in holdings:
code = h["code"]
shares = h["shares"]
cost_per = h["cost_per_share"]
cost = h["total_cost"]
price = prices.get(code)
if price:
value = round(shares * price, 2)
else:
value = 0
pnl = round(value - cost, 2)
pnl_pct = round((pnl / cost * 100), 2) if cost > 0 else 0
total_cost += cost
total_value += value
price_str = f"${price:.2f}" if price else ""
cost_str = f"${cost:.2f}" if cost else ""
value_str = f"${value:.2f}" if value else ""
emoji = "🟢" if pnl >= 0 else "🔴"
pnl_str = f"{emoji}${pnl:+.2f} ({pnl_pct:+.2f}%)"
cost_per_str = f"${cost_per:.2f}" if cost_per else ""
print(f"{code:<10} {shares:<10.2f} {cost_per_str:<12} {cost_str:<12} {price_str:<12} {value_str:<12} {pnl_str:<18}")
print("-" * 100)
total_pnl = round(total_value - total_cost, 2)
total_pnl_pct = round((total_pnl / total_cost * 100), 2) if total_cost > 0 else 0
total_emoji = "🟢" if total_pnl >= 0 else "🔴"
print(f"{'合计':<10} {'':<10} {'':<12} ${total_cost:<11.2f} {'':<12} ${total_value:<11.2f} {total_emoji}${total_pnl:+.2f} ({total_pnl_pct:+.2f}%)")
print(f"\n{'='*85}")
print("💡 成本规则: 总成本 = 每股成本价(持仓成本) × 持仓数量")
print(f"{'='*85}")
if __name__ == "__main__":
main()