71 lines
2.9 KiB
Python
71 lines
2.9 KiB
Python
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")
|