Spaces:
Sleeping
Sleeping
QAway-to
commited on
Commit
·
c1d305f
1
Parent(s):
08efbfa
New tabs and functions v2.5
Browse files- app.py +11 -6
- core/crypto_dashboard.py +78 -43
- core/ui_style.css +14 -17
app.py
CHANGED
|
@@ -70,24 +70,27 @@ with gr.Blocks(css=custom_css) as demo:
|
|
| 70 |
chart_out = gr.Plot(label="Alpha vs BTC")
|
| 71 |
chart_btn.click(fn=build_alpha_chart, inputs=chart_in, outputs=chart_out)
|
| 72 |
|
| 73 |
-
# --- Crypto Intelligence Dashboard (
|
| 74 |
with gr.TabItem("Crypto Intelligence Dashboard"):
|
| 75 |
-
gr.Markdown("### 💹 Coinlore Market Dashboard (
|
| 76 |
|
| 77 |
-
# Controls
|
| 78 |
with gr.Row():
|
| 79 |
top_slider = gr.Slider(
|
| 80 |
label="Top N coins", minimum=20, maximum=100, step=10, value=50, scale=70
|
| 81 |
)
|
| 82 |
load_btn = gr.Button("Generate Dashboard", variant="primary", scale=30)
|
| 83 |
|
| 84 |
-
#
|
| 85 |
with gr.Row(equal_height=True):
|
| 86 |
with gr.Column(scale=70):
|
| 87 |
treemap_plot = gr.Plot(label="Market Composition")
|
| 88 |
with gr.Column(scale=30):
|
| 89 |
-
ai_box = gr.Textbox(
|
|
|
|
|
|
|
| 90 |
|
|
|
|
| 91 |
with gr.Row(equal_height=True):
|
| 92 |
movers_plot = gr.Plot(label="Top Movers", scale=50)
|
| 93 |
scatter_plot = gr.Plot(label="Market Cap vs Volume", scale=50)
|
|
@@ -95,7 +98,9 @@ with gr.Blocks(css=custom_css) as demo:
|
|
| 95 |
from core.crypto_dashboard import build_crypto_dashboard
|
| 96 |
|
| 97 |
|
| 98 |
-
def run_dash(n):
|
|
|
|
|
|
|
| 99 |
|
| 100 |
load_btn.click(
|
| 101 |
fn=run_dash,
|
|
|
|
| 70 |
chart_out = gr.Plot(label="Alpha vs BTC")
|
| 71 |
chart_btn.click(fn=build_alpha_chart, inputs=chart_in, outputs=chart_out)
|
| 72 |
|
| 73 |
+
# --- Crypto Intelligence Dashboard (Coinlore, Plotly Edition) ---
|
| 74 |
with gr.TabItem("Crypto Intelligence Dashboard"):
|
| 75 |
+
gr.Markdown("### 💹 Coinlore Market Dashboard (Plotly Edition)")
|
| 76 |
|
| 77 |
+
# --- Controls ---
|
| 78 |
with gr.Row():
|
| 79 |
top_slider = gr.Slider(
|
| 80 |
label="Top N coins", minimum=20, maximum=100, step=10, value=50, scale=70
|
| 81 |
)
|
| 82 |
load_btn = gr.Button("Generate Dashboard", variant="primary", scale=30)
|
| 83 |
|
| 84 |
+
# --- Layout: Treemap + AI sidebar ---
|
| 85 |
with gr.Row(equal_height=True):
|
| 86 |
with gr.Column(scale=70):
|
| 87 |
treemap_plot = gr.Plot(label="Market Composition")
|
| 88 |
with gr.Column(scale=30):
|
| 89 |
+
ai_box = gr.Textbox(
|
| 90 |
+
label="AI Market Summary", lines=18, elem_id="ai_summary_sidebar"
|
| 91 |
+
)
|
| 92 |
|
| 93 |
+
# --- Layout: Lower charts (Movers + Scatter) ---
|
| 94 |
with gr.Row(equal_height=True):
|
| 95 |
movers_plot = gr.Plot(label="Top Movers", scale=50)
|
| 96 |
scatter_plot = gr.Plot(label="Market Cap vs Volume", scale=50)
|
|
|
|
| 98 |
from core.crypto_dashboard import build_crypto_dashboard
|
| 99 |
|
| 100 |
|
| 101 |
+
def run_dash(n):
|
| 102 |
+
return build_crypto_dashboard(n)
|
| 103 |
+
|
| 104 |
|
| 105 |
load_btn.click(
|
| 106 |
fn=run_dash,
|
core/crypto_dashboard.py
CHANGED
|
@@ -1,10 +1,15 @@
|
|
| 1 |
"""
|
| 2 |
-
|
|
|
|
| 3 |
"""
|
| 4 |
-
import requests
|
|
|
|
|
|
|
|
|
|
| 5 |
from services.llm_client import llm_service
|
| 6 |
|
| 7 |
|
|
|
|
| 8 |
def fetch_coinlore_data(limit=100):
|
| 9 |
url = "https://api.coinlore.net/api/tickers/"
|
| 10 |
data = requests.get(url).json()["data"]
|
|
@@ -15,60 +20,90 @@ def fetch_coinlore_data(limit=100):
|
|
| 15 |
return df.head(limit)
|
| 16 |
|
| 17 |
|
|
|
|
| 18 |
def build_crypto_dashboard(top_n=50):
|
| 19 |
df = fetch_coinlore_data(top_n)
|
| 20 |
|
| 21 |
-
#
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 30 |
|
| 31 |
-
#
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 39 |
|
| 40 |
-
#
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 49 |
|
| 50 |
-
#
|
| 51 |
-
|
| 52 |
|
| 53 |
-
return
|
| 54 |
|
| 55 |
|
| 56 |
def _generate_ai_summary(df):
|
| 57 |
-
leaders = df.sort_values("percent_change_24h", ascending=False).head(3)
|
| 58 |
-
laggards = df.sort_values("percent_change_24h").head(3)
|
|
|
|
| 59 |
prompt = f"""
|
| 60 |
-
|
| 61 |
-
Top gainers: {', '.join(leaders
|
| 62 |
-
Top losers: {', '.join(laggards
|
| 63 |
Include:
|
| 64 |
-
-
|
| 65 |
-
- volatility
|
| 66 |
-
- short-term outlook
|
| 67 |
"""
|
| 68 |
-
|
| 69 |
for delta in llm_service.stream_chat(
|
| 70 |
messages=[{"role": "user", "content": prompt}],
|
| 71 |
-
model="meta-llama/Meta-Llama-3.1-8B-Instruct"
|
| 72 |
):
|
| 73 |
-
|
| 74 |
-
return
|
|
|
|
| 1 |
"""
|
| 2 |
+
Crypto Dashboard — Plotly Edition (Coinlore API)
|
| 3 |
+
Power BI–style visualization with dense layout.
|
| 4 |
"""
|
| 5 |
+
import requests
|
| 6 |
+
import pandas as pd
|
| 7 |
+
import plotly.express as px
|
| 8 |
+
import plotly.graph_objects as go
|
| 9 |
from services.llm_client import llm_service
|
| 10 |
|
| 11 |
|
| 12 |
+
# === Fetch data from Coinlore ===
|
| 13 |
def fetch_coinlore_data(limit=100):
|
| 14 |
url = "https://api.coinlore.net/api/tickers/"
|
| 15 |
data = requests.get(url).json()["data"]
|
|
|
|
| 20 |
return df.head(limit)
|
| 21 |
|
| 22 |
|
| 23 |
+
# === Main Dashboard ===
|
| 24 |
def build_crypto_dashboard(top_n=50):
|
| 25 |
df = fetch_coinlore_data(top_n)
|
| 26 |
|
| 27 |
+
# --- Treemap ---
|
| 28 |
+
fig_treemap = px.treemap(
|
| 29 |
+
df,
|
| 30 |
+
path=["symbol"],
|
| 31 |
+
values="market_cap_usd",
|
| 32 |
+
color="percent_change_24h",
|
| 33 |
+
color_continuous_scale="RdYlGn",
|
| 34 |
+
title="Market Composition by Market Cap (Top Coins)",
|
| 35 |
+
height=400,
|
| 36 |
+
)
|
| 37 |
+
fig_treemap.update_layout(
|
| 38 |
+
template="plotly_dark",
|
| 39 |
+
margin=dict(l=0, r=0, t=40, b=0),
|
| 40 |
+
paper_bgcolor="rgba(0,0,0,0)",
|
| 41 |
+
plot_bgcolor="rgba(0,0,0,0)",
|
| 42 |
+
)
|
| 43 |
|
| 44 |
+
# --- Top Gainers (24h) ---
|
| 45 |
+
top = df.sort_values("percent_change_24h", ascending=False).head(12)
|
| 46 |
+
fig_bar = px.bar(
|
| 47 |
+
top,
|
| 48 |
+
x="percent_change_24h",
|
| 49 |
+
y="symbol",
|
| 50 |
+
orientation="h",
|
| 51 |
+
color="percent_change_24h",
|
| 52 |
+
color_continuous_scale="Blues",
|
| 53 |
+
title="Top 12 Gainers (24h)",
|
| 54 |
+
height=350,
|
| 55 |
+
)
|
| 56 |
+
fig_bar.update_layout(
|
| 57 |
+
template="plotly_dark",
|
| 58 |
+
margin=dict(l=80, r=30, t=40, b=40),
|
| 59 |
+
paper_bgcolor="rgba(0,0,0,0)",
|
| 60 |
+
plot_bgcolor="rgba(0,0,0,0)",
|
| 61 |
+
)
|
| 62 |
|
| 63 |
+
# --- Market Cap vs Volume (Bubble) ---
|
| 64 |
+
fig_bubble = px.scatter(
|
| 65 |
+
df.head(60),
|
| 66 |
+
x="market_cap_usd",
|
| 67 |
+
y="volume24",
|
| 68 |
+
size="price_usd",
|
| 69 |
+
color="percent_change_7d",
|
| 70 |
+
hover_name="symbol",
|
| 71 |
+
log_x=True,
|
| 72 |
+
log_y=True,
|
| 73 |
+
color_continuous_scale="RdYlGn",
|
| 74 |
+
title="Market Cap vs 24h Volume",
|
| 75 |
+
height=350,
|
| 76 |
+
)
|
| 77 |
+
fig_bubble.update_layout(
|
| 78 |
+
template="plotly_dark",
|
| 79 |
+
margin=dict(l=60, r=30, t=40, b=40),
|
| 80 |
+
paper_bgcolor="rgba(0,0,0,0)",
|
| 81 |
+
plot_bgcolor="rgba(0,0,0,0)",
|
| 82 |
+
)
|
| 83 |
|
| 84 |
+
# --- AI Summary ---
|
| 85 |
+
summary = _generate_ai_summary(df)
|
| 86 |
|
| 87 |
+
return fig_treemap, fig_bar, fig_bubble, summary
|
| 88 |
|
| 89 |
|
| 90 |
def _generate_ai_summary(df):
|
| 91 |
+
leaders = df.sort_values("percent_change_24h", ascending=False).head(3)["symbol"].tolist()
|
| 92 |
+
laggards = df.sort_values("percent_change_24h").head(3)["symbol"].tolist()
|
| 93 |
+
|
| 94 |
prompt = f"""
|
| 95 |
+
Summarize today's crypto market based on Coinlore data.
|
| 96 |
+
Top gainers: {', '.join(leaders)}.
|
| 97 |
+
Top losers: {', '.join(laggards)}.
|
| 98 |
Include:
|
| 99 |
+
- overall market sentiment
|
| 100 |
+
- volatility and liquidity notes
|
| 101 |
+
- short-term outlook
|
| 102 |
"""
|
| 103 |
+
summary = ""
|
| 104 |
for delta in llm_service.stream_chat(
|
| 105 |
messages=[{"role": "user", "content": prompt}],
|
| 106 |
+
model="meta-llama/Meta-Llama-3.1-8B-Instruct",
|
| 107 |
):
|
| 108 |
+
summary += delta
|
| 109 |
+
return summary
|
core/ui_style.css
CHANGED
|
@@ -14,7 +14,7 @@ h2, h3, .gr-markdown {
|
|
| 14 |
font-weight: 600 !important;
|
| 15 |
}
|
| 16 |
|
| 17 |
-
/* === Remove Gray Placeholder Icons
|
| 18 |
[data-testid="plot-container"] svg {
|
| 19 |
display: none !important;
|
| 20 |
}
|
|
@@ -23,36 +23,28 @@ h2, h3, .gr-markdown {
|
|
| 23 |
box-shadow: none !important;
|
| 24 |
}
|
| 25 |
|
| 26 |
-
/* === Plot
|
| 27 |
[data-testid="plot-container"] {
|
| 28 |
width: 100% !important;
|
| 29 |
margin: 0 auto 22px auto !important;
|
| 30 |
-
background: transparent !important;
|
| 31 |
-
border: none !important;
|
| 32 |
}
|
| 33 |
[data-testid="plot-container"] canvas {
|
| 34 |
width: 100% !important;
|
| 35 |
height: auto !important;
|
| 36 |
}
|
| 37 |
|
| 38 |
-
/*
|
| 39 |
-
.gr-row { gap: 18px !important; }
|
| 40 |
-
|
| 41 |
-
/* Верхний график (Treemap) */
|
| 42 |
#root [label="Market Composition"] canvas {
|
| 43 |
-
height:
|
| 44 |
}
|
| 45 |
-
|
| 46 |
-
/* Нижние графики (Movers + Scatter) */
|
| 47 |
#root [label="Top Movers"] canvas,
|
| 48 |
#root [label="Market Cap vs Volume"] canvas {
|
| 49 |
-
height:
|
| 50 |
}
|
| 51 |
|
| 52 |
/* === Sidebar (AI Summary) === */
|
| 53 |
-
#ai_summary_sidebar textarea
|
| 54 |
-
|
| 55 |
-
height: 360px !important;
|
| 56 |
background-color: #161b22 !important;
|
| 57 |
color: #f0f6fc !important;
|
| 58 |
border: 1px solid #30363d !important;
|
|
@@ -64,7 +56,7 @@ h2, h3, .gr-markdown {
|
|
| 64 |
resize: none !important;
|
| 65 |
}
|
| 66 |
|
| 67 |
-
/* ===
|
| 68 |
.gr-button {
|
| 69 |
border-radius: 6px !important;
|
| 70 |
font-weight: 600 !important;
|
|
@@ -86,7 +78,7 @@ h2, h3, .gr-markdown {
|
|
| 86 |
background: #6366f1 !important;
|
| 87 |
}
|
| 88 |
|
| 89 |
-
/* === Tables === */
|
| 90 |
.gr-dataframe table {
|
| 91 |
width: 100% !important;
|
| 92 |
color: #c9d1d9 !important;
|
|
@@ -103,3 +95,8 @@ h2, h3, .gr-markdown {
|
|
| 103 |
border-top: 1px solid #30363d !important;
|
| 104 |
padding: 8px !important;
|
| 105 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
font-weight: 600 !important;
|
| 15 |
}
|
| 16 |
|
| 17 |
+
/* === Remove Gray Placeholder Icons === */
|
| 18 |
[data-testid="plot-container"] svg {
|
| 19 |
display: none !important;
|
| 20 |
}
|
|
|
|
| 23 |
box-shadow: none !important;
|
| 24 |
}
|
| 25 |
|
| 26 |
+
/* === Plot Size & Layout === */
|
| 27 |
[data-testid="plot-container"] {
|
| 28 |
width: 100% !important;
|
| 29 |
margin: 0 auto 22px auto !important;
|
|
|
|
|
|
|
| 30 |
}
|
| 31 |
[data-testid="plot-container"] canvas {
|
| 32 |
width: 100% !important;
|
| 33 |
height: auto !important;
|
| 34 |
}
|
| 35 |
|
| 36 |
+
/* --- Chart height tuning --- */
|
|
|
|
|
|
|
|
|
|
| 37 |
#root [label="Market Composition"] canvas {
|
| 38 |
+
height: 400px !important;
|
| 39 |
}
|
|
|
|
|
|
|
| 40 |
#root [label="Top Movers"] canvas,
|
| 41 |
#root [label="Market Cap vs Volume"] canvas {
|
| 42 |
+
height: 350px !important;
|
| 43 |
}
|
| 44 |
|
| 45 |
/* === Sidebar (AI Summary) === */
|
| 46 |
+
#ai_summary_sidebar textarea {
|
| 47 |
+
height: 400px !important;
|
|
|
|
| 48 |
background-color: #161b22 !important;
|
| 49 |
color: #f0f6fc !important;
|
| 50 |
border: 1px solid #30363d !important;
|
|
|
|
| 56 |
resize: none !important;
|
| 57 |
}
|
| 58 |
|
| 59 |
+
/* === Buttons & Sliders === */
|
| 60 |
.gr-button {
|
| 61 |
border-radius: 6px !important;
|
| 62 |
font-weight: 600 !important;
|
|
|
|
| 78 |
background: #6366f1 !important;
|
| 79 |
}
|
| 80 |
|
| 81 |
+
/* === Tables & spacing === */
|
| 82 |
.gr-dataframe table {
|
| 83 |
width: 100% !important;
|
| 84 |
color: #c9d1d9 !important;
|
|
|
|
| 95 |
border-top: 1px solid #30363d !important;
|
| 96 |
padding: 8px !important;
|
| 97 |
}
|
| 98 |
+
|
| 99 |
+
/* === Dashboard Layout === */
|
| 100 |
+
.gr-row {
|
| 101 |
+
gap: 18px !important;
|
| 102 |
+
}
|