Files
server-configs/daily_full_report.py
2026-02-13 22:24:27 +08:00

136 lines
4.2 KiB
Python
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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()