Files
All-in-one-Financial-Analysis/atlas-terminal/server/services/crypto_fetcher.py
T
shawnkim1997andClaude Opus 4.6 b2acda81ee feat: add Atlas Terminal — Next.js 14 + FastAPI full-stack migration
Complete migration from Streamlit to Next.js 14 App Router + FastAPI backend.

Frontend (Next.js 14):
- 10 pages: Overview, Research, Valuation, Technical, Markets, Earnings, News, Portfolio, Filings, Settings
- Terminal Noir dark theme with custom Tailwind config
- TradingView Lightweight Charts for candlestick/volume
- Valuation: DCF, Sensitivity Matrix, Monte Carlo, Tornado, Reverse DCF
- Financial Statements table with YoY growth badges and margin rows
- SEC EDGAR inline filing viewer with section tabs
- News split-view with iframe article embedding
- Technical Analysis with RSI, MACD, Bollinger, Fibonacci, Moving Averages
- Earnings beat/miss visualization
- AI Copilot chat panel with Gemini integration

Backend (FastAPI):
- 13 routers: market_data, financials, valuation, technical, earnings, insider, edgar, news, portfolio, analysis, chat, estimates, fx
- Services: DCF engine, Monte Carlo simulation, sensitivity analysis, risk metrics, SEC parser, technical indicators
- yfinance + yahooquery data sources with fallback pattern
- SQLite caching layer

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-21 02:10:10 +00:00

159 lines
4.6 KiB
Python

"""Crypto price fetcher -- Bithumb (KRW) and Binance (USD) public APIs.
Provides standalone fetcher functions that can be used by the crypto router
or any other service that needs cryptocurrency price data.
"""
import time
from typing import Any, Dict, List, Optional
import requests
# ---------------------------------------------------------------------------
# Top 20 coins
# ---------------------------------------------------------------------------
TOP_20_COINS: List[str] = [
"BTC", "ETH", "BNB", "XRP", "SOL", "ADA", "DOGE", "AVAX", "DOT", "MATIC",
"LINK", "SHIB", "TRX", "UNI", "ATOM", "LTC", "ETC", "XLM", "NEAR", "APT",
]
# ---------------------------------------------------------------------------
# In-memory cache
# ---------------------------------------------------------------------------
_cache: Dict[str, Any] = {}
_cache_ts: Dict[str, float] = {}
_CACHE_TTL = 30 # seconds
def _get_cached(key: str) -> Optional[Any]:
if key in _cache and (time.time() - _cache_ts.get(key, 0)) < _CACHE_TTL:
return _cache[key]
return None
def _set_cached(key: str, value: Any) -> None:
_cache[key] = value
_cache_ts[key] = time.time()
# ---------------------------------------------------------------------------
# Bithumb (KRW)
# ---------------------------------------------------------------------------
def fetch_bithumb_all_krw(symbols: Optional[List[str]] = None) -> Dict[str, float]:
"""Fetch KRW prices from Bithumb ALL_KRW endpoint.
Uses the bulk endpoint (https://api.bithumb.com/public/ticker/ALL_KRW)
to avoid per-symbol rate limits.
Parameters
----------
symbols:
Coin symbols to include. Defaults to TOP_20_COINS.
Returns
-------
dict
Mapping of symbol -> KRW price (float).
"""
cached = _get_cached("bithumb_all_krw")
if cached is not None:
return cached
symbols = symbols or TOP_20_COINS
url = "https://api.bithumb.com/public/ticker/ALL_KRW"
prices: Dict[str, float] = {}
try:
resp = requests.get(url, timeout=10)
resp.raise_for_status()
body = resp.json()
if body.get("status") != "0000":
return prices
data = body.get("data", {})
for sym in symbols:
coin_data = data.get(sym.upper())
if coin_data and isinstance(coin_data, dict):
closing = coin_data.get("closing_price")
if closing:
prices[sym.upper()] = float(closing)
except Exception:
pass
_set_cached("bithumb_all_krw", prices)
return prices
# ---------------------------------------------------------------------------
# Binance (USD)
# ---------------------------------------------------------------------------
def fetch_binance_prices(symbols: Optional[List[str]] = None) -> Dict[str, float]:
"""Fetch USD prices from Binance ticker/price endpoint.
Parameters
----------
symbols:
Coin symbols to include. Defaults to TOP_20_COINS.
Returns
-------
dict
Mapping of symbol -> USD price (float).
"""
cached = _get_cached("binance_prices")
if cached is not None:
return cached
symbols = symbols or TOP_20_COINS
url = "https://api.binance.com/api/v3/ticker/price"
prices: Dict[str, float] = {}
try:
resp = requests.get(url, timeout=10)
resp.raise_for_status()
data = resp.json()
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]
except Exception:
pass
_set_cached("binance_prices", prices)
return prices
# ---------------------------------------------------------------------------
# Combined
# ---------------------------------------------------------------------------
def fetch_top20_prices(
symbols: Optional[List[str]] = None,
) -> List[Dict[str, Any]]:
"""Return top-20 crypto prices with both KRW and USD.
Each entry contains:
- symbol: str
- price_usd: float | None
- price_krw: float | None
Parameters
----------
symbols:
Override default TOP_20_COINS list.
"""
symbols = symbols or TOP_20_COINS
usd = fetch_binance_prices(symbols)
krw = fetch_bithumb_all_krw(symbols)
results: List[Dict[str, Any]] = []
for sym in symbols:
results.append({
"symbol": sym.upper(),
"price_usd": usd.get(sym.upper()),
"price_krw": krw.get(sym.upper()),
})
return results