171 lines
5.3 KiB
Python
171 lines
5.3 KiB
Python
|
|
#!/usr/bin/env python3
|
|||
|
|
# -*- coding: utf-8 -*-
|
|||
|
|
"""
|
|||
|
|
计算持仓盈亏
|
|||
|
|
"""
|
|||
|
|
|
|||
|
|
import requests
|
|||
|
|
import json
|
|||
|
|
from datetime import datetime
|
|||
|
|
|
|||
|
|
# Yahoo Finance API 配置
|
|||
|
|
YAHOO_API_URL = "https://query1.finance.yahoo.com/v8/finance/chart/"
|
|||
|
|
|
|||
|
|
# 股票配置(从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 calculate_pnl():
|
|||
|
|
"""计算持仓盈亏"""
|
|||
|
|
|
|||
|
|
print("=" * 60)
|
|||
|
|
print("计算持仓盈亏")
|
|||
|
|
print("=" * 60)
|
|||
|
|
print(f"日期: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
|
|||
|
|
print("=" * 60)
|
|||
|
|
|
|||
|
|
total_cost = 0
|
|||
|
|
total_market_value = 0
|
|||
|
|
total_pnl = 0
|
|||
|
|
total_pnl_percent = 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
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
print(f"\n{symbol} - {name}")
|
|||
|
|
print(f" 持仓: {quantity}股 @ ${cost_basis}")
|
|||
|
|
print(f" 当前价: ${current_price:.2f}")
|
|||
|
|
print(f" 总成本: ${cost:.2f}")
|
|||
|
|
print(f" 当前市值: ${market_value:.2f}")
|
|||
|
|
print(f" 盈亏: ${pnl:+.2f} ({pnl_percent:+.2f}%)")
|
|||
|
|
print(f" 日涨跌: ${change:+.2f} ({change_percent:+.2f}%)")
|
|||
|
|
else:
|
|||
|
|
print(f"\n{symbol} - {name}")
|
|||
|
|
print(f" ❌ 无法获取价格数据")
|
|||
|
|
|
|||
|
|
# 计算总盈亏百分比
|
|||
|
|
if total_cost > 0:
|
|||
|
|
total_pnl_percent = (total_pnl / total_cost) * 100
|
|||
|
|
|
|||
|
|
print("\n" + "=" * 60)
|
|||
|
|
print("📊 持仓概览")
|
|||
|
|
print("=" * 60)
|
|||
|
|
print(f"总持仓数量: {len(STOCKS)} 只股票")
|
|||
|
|
print(f"总成本: ${total_cost:.2f}")
|
|||
|
|
print(f"当前总市值: ${total_market_value:.2f}")
|
|||
|
|
print(f"总盈亏: ${total_pnl:+.2f} ({total_pnl_percent:+.2f}%)")
|
|||
|
|
print("=" * 60)
|
|||
|
|
|
|||
|
|
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 main():
|
|||
|
|
"""主函数"""
|
|||
|
|
print("开始计算持仓盈亏...")
|
|||
|
|
print()
|
|||
|
|
|
|||
|
|
pnl_data = calculate_pnl()
|
|||
|
|
|
|||
|
|
if pnl_data["total_cost"] > 0:
|
|||
|
|
print("\n✅ 盈亏计算完成")
|
|||
|
|
print(f"总盈亏: ${pnl_data['total_pnl']:+.2f} ({pnl_data['total_pnl_percent']:+.2f}%)")
|
|||
|
|
else:
|
|||
|
|
print("\n❌ 无法计算盈亏")
|
|||
|
|
|
|||
|
|
if __name__ == "__main__":
|
|||
|
|
main()
|