Spaces:
Sleeping
Sleeping
| """ | |
| Crypto Dashboard — Plotly Edition (clean layout) | |
| • убраны colorbar заголовки (percent_change_*) | |
| • уменьшены отступы KPI | |
| • без глобального Markdown-заголовка | |
| """ | |
| import requests | |
| import pandas as pd | |
| import plotly.express as px | |
| from services.llm_client import llm_service | |
| def fetch_coinlore_data(limit=100): | |
| url = "https://api.coinlore.net/api/tickers/" | |
| data = requests.get(url).json()["data"] | |
| df = pd.DataFrame(data) | |
| for col in ["price_usd", "market_cap_usd", "volume24", | |
| "percent_change_1h", "percent_change_24h", "percent_change_7d"]: | |
| df[col] = pd.to_numeric(df[col], errors="coerce") | |
| return df.head(limit) | |
| def _kpi_line(df) -> str: | |
| """Формирует компактную KPI-строку без лишних пробелов""" | |
| tracked = ["BTC", "ETH", "SOL", "DOGE"] | |
| parts = [] | |
| for sym in tracked: | |
| row = df[df["symbol"] == sym] | |
| if row.empty: | |
| continue | |
| price = float(row["price_usd"]) | |
| ch = float(row["percent_change_24h"]) | |
| arrow = "↑" if ch > 0 else "↓" | |
| color = "#4ade80" if ch > 0 else "#f87171" | |
| parts.append( | |
| f"<b>{sym}</b> ${price:,.0f} " | |
| f"<span style='color:{color}'>{arrow} {abs(ch):.2f}%</span>" | |
| ) | |
| return " , ".join(parts) | |
| def build_crypto_dashboard(top_n=50): | |
| df = fetch_coinlore_data(top_n) | |
| # === Treemap === | |
| fig_treemap = px.treemap( | |
| df, | |
| path=["symbol"], | |
| values="market_cap_usd", | |
| color="percent_change_24h", | |
| color_continuous_scale="RdYlGn", | |
| height=420, | |
| ) | |
| fig_treemap.update_layout( | |
| title=None, | |
| template="plotly_dark", | |
| coloraxis_colorbar=dict(title=None), # 🔹 убираем надпись percent_change_24h | |
| margin=dict(l=5, r=5, t=5, b=5), | |
| paper_bgcolor="rgba(0,0,0,0)", | |
| plot_bgcolor="rgba(0,0,0,0)", | |
| ) | |
| # === Bar chart (Top gainers) === | |
| top = df.sort_values("percent_change_24h", ascending=False).head(12) | |
| fig_bar = px.bar( | |
| top, | |
| x="percent_change_24h", | |
| y="symbol", | |
| orientation="h", | |
| color="percent_change_24h", | |
| color_continuous_scale="Blues", | |
| height=320, | |
| ) | |
| fig_bar.update_layout( | |
| title=None, | |
| template="plotly_dark", | |
| coloraxis_colorbar=dict(title=None), # 🔹 убираем надпись percent_change_24h | |
| margin=dict(l=40, r=10, t=5, b=18), | |
| paper_bgcolor="rgba(0,0,0,0)", | |
| plot_bgcolor="rgba(0,0,0,0)", | |
| ) | |
| # === Scatter (Market Cap vs Volume) === | |
| fig_bubble = px.scatter( | |
| df.head(60), | |
| x="market_cap_usd", | |
| y="volume24", | |
| size="price_usd", | |
| color="percent_change_7d", | |
| hover_name="symbol", | |
| log_x=True, | |
| log_y=True, | |
| color_continuous_scale="RdYlGn", | |
| height=320, | |
| ) | |
| fig_bubble.update_layout( | |
| title=None, | |
| template="plotly_dark", | |
| coloraxis_colorbar=dict(title=None), # 🔹 убираем надпись percent_change_7d | |
| margin=dict(l=36, r=10, t=5, b=18), | |
| paper_bgcolor="rgba(0,0,0,0)", | |
| plot_bgcolor="rgba(0,0,0,0)", | |
| ) | |
| # === LLM summary === | |
| summary = _ai_summary(df) | |
| kpi_text = _kpi_line(df) | |
| return fig_treemap, fig_bar, fig_bubble, summary, kpi_text | |
| def _ai_summary(df): | |
| leaders = df.sort_values("percent_change_24h", ascending=False).head(3)["symbol"].tolist() | |
| laggards = df.sort_values("percent_change_24h").head(3)["symbol"].tolist() | |
| prompt = f""" | |
| Summarize today's crypto market based on Coinlore data. | |
| Top gainers: {', '.join(leaders)}. | |
| Top losers: {', '.join(laggards)}. | |
| Include: overall sentiment, volatility/liquidity, short-term outlook. | |
| """ | |
| text = "" | |
| for delta in llm_service.stream_chat( | |
| messages=[{"role": "user", "content": prompt}], | |
| model="meta-llama/Meta-Llama-3.1-8B-Instruct", | |
| ): | |
| text += delta | |
| return text | |