mirror of
https://github.com/shawnkim1997/All-in-one-Financial-Analysis.git
synced 2026-08-25 08:18:05 +00:00
140 lines
4.1 KiB
Python
140 lines
4.1 KiB
Python
"""Crypto router -- live cryptocurrency prices from Bithumb (KRW) and Binance (USD)."""
|
|||
|
|
|
||
|
|
from typing import List
|
||
|
|
|
||
|
|
from fastapi import APIRouter, HTTPException
|
||
|
|
|
||
|
|
from server.models.schemas import CryptoPrice
|
||
|
|
|
||
|
|
router = APIRouter()
|
||
|
|
|
||
|
|
# Top 20 symbols tracked by default
|
||
|
|
TOP_SYMBOLS = [
|
||
|
|
"BTC", "ETH", "BNB", "XRP", "SOL", "ADA", "DOGE", "AVAX", "DOT", "MATIC",
|
||
|
|
"LINK", "SHIB", "TRX", "UNI", "ATOM", "LTC", "ETC", "XLM", "NEAR", "APT",
|
||
|
|
]
|
||
|
|
|
||
|
|
# Bithumb uses different ticker names for some coins
|
||
|
|
_BITHUMB_MAP = {
|
||
|
|
"MATIC": "MATIC",
|
||
|
|
"NEAR": "NEAR",
|
||
|
|
"APT": "APT",
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
def _fetch_binance_prices(symbols: List[str]) -> dict:
|
||
|
|
"""Fetch USD prices from Binance API for the given symbols."""
|
||
|
|
import requests
|
||
|
|
|
||
|
|
url = "https://api.binance.com/api/v3/ticker/price"
|
||
|
|
try:
|
||
|
|
resp = requests.get(url, timeout=10)
|
||
|
|
resp.raise_for_status()
|
||
|
|
data = resp.json()
|
||
|
|
except Exception:
|
||
|
|
return {}
|
||
|
|
|
||
|
|
# Build lookup: symbol (no USDT suffix) -> price
|
||
|
|
prices = {}
|
||
|
|
lookup = {item["symbol"]: float(item["price"]) for item in data}
|
||
|
|
for sym in symbols:
|
||
|
|
key = f"{sym.upper()}USDT"
|
||
|
|
if key in lookup:
|
||
|
|
prices[sym.upper()] = lookup[key]
|
||
|
|
return prices
|
||
|
|
|
||
|
|
|
||
|
|
def _fetch_bithumb_prices(symbols: List[str]) -> dict:
|
||
|
|
"""Fetch KRW prices from Bithumb public API."""
|
||
|
|
import requests
|
||
|
|
|
||
|
|
prices = {}
|
||
|
|
for sym in symbols:
|
||
|
|
bithumb_sym = _BITHUMB_MAP.get(sym.upper(), sym.upper())
|
||
|
|
url = f"https://api.bithumb.com/public/ticker/{bithumb_sym}_KRW"
|
||
|
|
try:
|
||
|
|
resp = requests.get(url, timeout=5)
|
||
|
|
resp.raise_for_status()
|
||
|
|
data = resp.json()
|
||
|
|
if data.get("status") == "0000":
|
||
|
|
closing = data.get("data", {}).get("closing_price")
|
||
|
|
if closing:
|
||
|
|
prices[sym.upper()] = float(closing)
|
||
|
|
except Exception:
|
||
|
|
continue
|
||
|
|
return prices
|
||
|
|
|
||
|
|
|
||
|
|
def _fetch_binance_24h_changes(symbols: List[str]) -> dict:
|
||
|
|
"""Fetch 24h percentage changes from Binance."""
|
||
|
|
import requests
|
||
|
|
|
||
|
|
url = "https://api.binance.com/api/v3/ticker/24hr"
|
||
|
|
try:
|
||
|
|
resp = requests.get(url, timeout=10)
|
||
|
|
resp.raise_for_status()
|
||
|
|
data = resp.json()
|
||
|
|
except Exception:
|
||
|
|
return {}
|
||
|
|
|
||
|
|
changes = {}
|
||
|
|
lookup = {item["symbol"]: float(item.get("priceChangePercent", 0)) for item in data}
|
||
|
|
for sym in symbols:
|
||
|
|
key = f"{sym.upper()}USDT"
|
||
|
|
if key in lookup:
|
||
|
|
changes[sym.upper()] = lookup[key]
|
||
|
|
return changes
|
||
|
|
|
||
|
|
|
||
|
|
@router.get(
|
||
|
|
"/prices",
|
||
|
|
response_model=List[CryptoPrice],
|
||
|
|
summary="Top 20 crypto prices (Bithumb KRW + Binance USD)",
|
||
|
|
)
|
||
|
|
async def crypto_prices():
|
||
|
|
"""Return current prices for the top 20 cryptocurrencies.
|
||
|
|
|
||
|
|
USD prices come from Binance; KRW prices from Bithumb.
|
||
|
|
"""
|
||
|
|
try:
|
||
|
|
usd_prices = _fetch_binance_prices(TOP_SYMBOLS)
|
||
|
|
krw_prices = _fetch_bithumb_prices(TOP_SYMBOLS)
|
||
|
|
changes = _fetch_binance_24h_changes(TOP_SYMBOLS)
|
||
|
|
|
||
|
|
results: List[CryptoPrice] = []
|
||
|
|
for sym in TOP_SYMBOLS:
|
||
|
|
results.append(CryptoPrice(
|
||
|
|
symbol=sym,
|
||
|
|
name=sym,
|
||
|
|
price_usd=usd_prices.get(sym),
|
||
|
|
price_krw=krw_prices.get(sym),
|
||
|
|
change_24h_pct=changes.get(sym),
|
||
|
|
))
|
||
|
|
return results
|
||
|
|
except Exception as exc:
|
||
|
|
raise HTTPException(status_code=500, detail=f"Crypto prices failed: {exc}") from exc
|
||
|
|
|
||
|
|
|
||
|
|
@router.get(
|
||
|
|
"/price/{symbol}",
|
||
|
|
response_model=CryptoPrice,
|
||
|
|
summary="Single crypto price",
|
||
|
|
)
|
||
|
|
async def crypto_price(symbol: str):
|
||
|
|
"""Return current price for a single cryptocurrency symbol."""
|
||
|
|
try:
|
||
|
|
sym = symbol.upper()
|
||
|
|
usd_prices = _fetch_binance_prices([sym])
|
||
|
|
krw_prices = _fetch_bithumb_prices([sym])
|
||
|
|
changes = _fetch_binance_24h_changes([sym])
|
||
|
|
|
||
|
|
return CryptoPrice(
|
||
|
|
symbol=sym,
|
||
|
|
name=sym,
|
||
|
|
price_usd=usd_prices.get(sym),
|
||
|
|
price_krw=krw_prices.get(sym),
|
||
|
|
change_24h_pct=changes.get(sym),
|
||
|
|
)
|
||
|
|
except Exception as exc:
|
||
|
|
raise HTTPException(status_code=500, detail=f"Crypto price failed: {exc}") from exc
|