feat: Streamlit dashboard with sidebar, detail, portfolio, and main app
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
46
dashboard/sidebar.py
Normal file
46
dashboard/sidebar.py
Normal file
@@ -0,0 +1,46 @@
|
||||
import streamlit as st
|
||||
import json
|
||||
from config import DEFAULT_WEIGHTS
|
||||
|
||||
def render_sidebar(latest_results: dict, db):
|
||||
st.sidebar.title("Crypto Signals")
|
||||
page = st.sidebar.radio("View", ["Signals", "Portfolio"], label_visibility="collapsed")
|
||||
st.sidebar.divider()
|
||||
|
||||
if latest_results:
|
||||
coins = sorted(latest_results.values(), key=lambda x: x["composite"], reverse=True)
|
||||
for coin in coins:
|
||||
signal = coin["signal"]
|
||||
color = {"BUY": "green", "HOLD": "orange", "SELL": "red"}.get(signal, "gray")
|
||||
symbol_short = coin["symbol"].replace("USDT", "")
|
||||
label = f":{color}[{signal}] **{symbol_short}** — {coin['composite']:.0f}"
|
||||
if st.sidebar.button(label, key=coin["symbol"], use_container_width=True):
|
||||
st.session_state["selected_coin"] = coin["symbol"]
|
||||
|
||||
st.sidebar.divider()
|
||||
st.sidebar.subheader("Signal Weights")
|
||||
weights = _load_weights(db)
|
||||
new_weights = {}
|
||||
new_weights["technical"] = st.sidebar.slider("Technical", 0.0, 1.0, weights["technical"], 0.05)
|
||||
new_weights["news"] = st.sidebar.slider("News", 0.0, 1.0, weights["news"], 0.05)
|
||||
new_weights["social"] = st.sidebar.slider("Social", 0.0, 1.0, weights["social"], 0.05)
|
||||
new_weights["ai"] = st.sidebar.slider("AI", 0.0, 1.0, weights["ai"], 0.05)
|
||||
|
||||
total = sum(new_weights.values())
|
||||
if abs(total - 1.0) > 0.01:
|
||||
st.sidebar.warning(f"Weights sum: {total:.2f} (must be 1.0)")
|
||||
else:
|
||||
if new_weights != weights:
|
||||
db.save_setting("weights", json.dumps(new_weights))
|
||||
st.session_state["weights_changed"] = True
|
||||
|
||||
return page
|
||||
|
||||
def _load_weights(db) -> dict:
|
||||
raw = db.load_setting("weights")
|
||||
if raw:
|
||||
try:
|
||||
return json.loads(raw)
|
||||
except json.JSONDecodeError:
|
||||
pass
|
||||
return dict(DEFAULT_WEIGHTS)
|
||||
Reference in New Issue
Block a user