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:
2026-03-20 17:56:31 +09:00
parent 9cc8241e22
commit e16d944985
4 changed files with 370 additions and 0 deletions

View File

@@ -0,0 +1,70 @@
import streamlit as st
import plotly.graph_objects as go
import pandas as pd
def render_portfolio(portfolio_manager, current_prices: dict, db):
st.header("Portfolio Simulator")
pv = portfolio_manager.get_portfolio_value(current_prices)
cols = st.columns(5)
cols[0].metric("Initial Capital", f"${portfolio_manager.initial_capital:.2f}")
cols[1].metric("Current Value", f"${pv['total_value']:.2f}")
pnl_delta = f"{pv['pnl_pct']:+.1f}%"
cols[2].metric("Total P&L", f"${pv['total_pnl']:+.2f}", pnl_delta)
cols[3].metric("Win Rate", f"{pv['win_rate']:.0f}%")
cols[4].metric("Available Cash", f"${pv['cash']:.2f}")
st.divider()
col_left, col_right = st.columns([3, 2])
with col_left:
st.subheader("Current Holdings")
if portfolio_manager.positions:
rows = []
for sym, pos in portfolio_manager.positions.items():
price = current_prices.get(sym, pos["entry_price"])
value = pos["quantity"] * price
pnl = value - pos["invested_usd"]
pnl_pct = (pnl / pos["invested_usd"] * 100) if pos["invested_usd"] > 0 else 0
rows.append({
"Coin": sym.replace("USDT", ""),
"Invested": f"${pos['invested_usd']:.2f}",
"Qty": f"{pos['quantity']:.6f}",
"Entry": f"${pos['entry_price']:.4f}",
"Current": f"${price:.4f}",
"P&L": f"${pnl:+.2f} ({pnl_pct:+.1f}%)",
})
st.dataframe(pd.DataFrame(rows), use_container_width=True, hide_index=True)
else:
st.info("No open positions")
with col_right:
st.subheader("Allocation")
if portfolio_manager.positions:
labels = [s.replace("USDT", "") for s in portfolio_manager.positions]
values = [p["quantity"] * current_prices.get(s, p["entry_price"])
for s, p in portfolio_manager.positions.items()]
labels.append("Cash")
values.append(pv["cash"])
fig = go.Figure(data=[go.Pie(labels=labels, values=values, hole=0.4)])
fig.update_layout(template="plotly_dark", height=300, margin=dict(t=20, b=20))
st.plotly_chart(fig, use_container_width=True)
else:
st.info("100% Cash")
st.divider()
st.subheader("Trade History")
if portfolio_manager.trades:
trade_rows = []
for t in reversed(portfolio_manager.trades[-20:]):
trade_rows.append({
"Time": t["timestamp"][:16],
"Coin": t["coin"].replace("USDT", ""),
"Side": t["side"],
"Price": f"${t['price']:.4f}",
"Amount": f"${t['amount_usd']:.2f}",
"Reason": t["reason"],
})
st.dataframe(pd.DataFrame(trade_rows), use_container_width=True, hide_index=True)
else:
st.info("No trades yet")