- change_pct 从 "▲ 2.26%" 改为 "2.26%",is_up 控制方向 - 与 brief.html 前端渲染逻辑对齐,避免重复方向符号 - 修复 A 股超时后 cn_indices 为空导致行情行缺失的问题
85 lines
2.9 KiB
Python
85 lines
2.9 KiB
Python
"""Fetch market data: major indices, crypto, forex via public APIs (no API key needed)."""
|
|
import json, os, requests
|
|
from datetime import datetime, timezone, timedelta
|
|
|
|
OUTPUT = "/var/www/nav/data/market.json"
|
|
tz = timezone(timedelta(hours=8))
|
|
|
|
# Public endpoints (no API key)
|
|
COINGECKO_URL = "https://api.coingecko.com/api/v3/simple/price?ids=bitcoin,ethereum,solana,binancecoin,ripple&vs_currencies=usd&include_24hr_change=true"
|
|
# A-share indices via eastmoney (free, no key)
|
|
EM_URL = "https://push2.eastmoney.com/api/qt/ulist.np/get?fltt=2&secids=1.000001,0.399001,0.399006&fields=f2,f3,f4,f12,f14"
|
|
|
|
|
|
def fetch_crypto():
|
|
try:
|
|
resp = requests.get(COINGECKO_URL, timeout=10)
|
|
data = resp.json()
|
|
items = []
|
|
icons = {"bitcoin": "₿", "ethereum": "Ξ", "solana": "◎", "binancecoin": "BNB", "ripple": "XRP"}
|
|
names = {"bitcoin": "BTC", "ethereum": "ETH", "solana": "SOL", "binancecoin": "BNB", "ripple": "XRP"}
|
|
for coin_id, icon in icons.items():
|
|
if coin_id not in data:
|
|
continue
|
|
price = data[coin_id]["usd"]
|
|
change = data[coin_id].get("usd_24h_change", 0) or 0
|
|
items.append({
|
|
"type": "crypto",
|
|
"symbol": names[coin_id],
|
|
"icon": icon,
|
|
"price": f"${price:,.2f}",
|
|
"change_pct": f"{abs(change):.2f}%",
|
|
"is_up": change >= 0,
|
|
})
|
|
return items
|
|
except Exception as e:
|
|
print(f" Crypto error: {e}")
|
|
return []
|
|
|
|
|
|
def fetch_cn_indices():
|
|
"""Fetch A-share indices via Eastmoney API (no key)."""
|
|
try:
|
|
resp = requests.get(EM_URL, timeout=10)
|
|
data = resp.json()
|
|
items = []
|
|
for diff in data.get("data", {}).get("diff", []):
|
|
name = diff.get("f14", "")
|
|
price = diff.get("f2", 0)
|
|
change = diff.get("f4", 0)
|
|
pct = diff.get("f3", 0)
|
|
items.append({
|
|
"type": "cn_index",
|
|
"symbol": name,
|
|
"price": f"{price:.2f}",
|
|
"change_pct": f"{abs(pct):.2f}%",
|
|
"is_up": change >= 0,
|
|
})
|
|
return items
|
|
except Exception as e:
|
|
print(f" CN indices error: {e}")
|
|
return []
|
|
|
|
|
|
def fetch_market():
|
|
crypto = fetch_crypto()
|
|
cn_indices = fetch_cn_indices()
|
|
|
|
result = {
|
|
"updated_at": datetime.now(tz).strftime("%Y-%m-%d %H:%M"),
|
|
"crypto": crypto,
|
|
"cn_indices": cn_indices,
|
|
}
|
|
|
|
os.makedirs(os.path.dirname(OUTPUT), exist_ok=True)
|
|
with open(OUTPUT, "w", encoding="utf-8") as f:
|
|
json.dump(result, f, ensure_ascii=False, indent=2)
|
|
|
|
print(f"Crypto: {len(crypto)} | CN indices: {len(cn_indices)} -> {OUTPUT}")
|
|
return result
|
|
|
|
|
|
if __name__ == "__main__":
|
|
print(f"Fetching market data at {datetime.now(tz).strftime('%Y-%m-%d %H:%M')}")
|
|
fetch_market()
|