import streamlit as st import plotly.graph_objects as go from datetime import datetime import pytz from trading.journal import ( create_trades_table, get_open_trades, get_trade_history, add_trade, update_trade, delete_trade, TradeEntry, get_open_trades_summary, get_current_prices, generate_position_id, get_position_summary, get_latest_portfolio_value, update_portfolio_value ) def format_datetime(dt): """Format datetime for display""" if dt: return dt.strftime('%Y-%m-%d %H:%M') return '' def plot_trade_history(trades): """Create a P/L chart using Plotly""" if not trades: return None # Prepare data dates = [] pnl = [] cumulative_pnl = 0 for trade in trades: if trade['exit_price']: trade_pnl = (trade['exit_price'] - trade['entry_price']) * trade['shares'] cumulative_pnl += trade_pnl dates.append(trade['exit_date']) pnl.append(cumulative_pnl) if not dates: return None # Create figure fig = go.Figure() fig.add_trace( go.Scatter(x=dates, y=pnl, mode='lines+markers', name='Cumulative P/L', line=dict(color='blue'), hovertemplate='Date: %{x}
P/L: $%{y:.2f}') ) fig.update_layout( title='Cumulative Profit/Loss Over Time', xaxis_title='Date', yaxis_title='Cumulative P/L ($)', hovermode='x unified' ) return fig def trading_journal_page(): st.header("Trading Journal") # Tabs for different journal functions tab1, tab2, tab3, tab4 = st.tabs(["Open Positions", "Add Trade", "Update Trade", "Trade History"]) with tab1: st.subheader("Open Positions") open_trades = get_open_trades() open_summary = get_open_trades_summary() if open_summary: # Get current prices unique_tickers = list(set(summary['ticker'] for summary in open_summary)) current_prices = get_current_prices(unique_tickers) total_portfolio_value = 0 total_paper_pl = 0 for summary in open_summary: with st.expander(f"{summary['ticker']} Summary"): ticker = summary['ticker'] avg_entry = summary['avg_entry_price'] current_price = current_prices.get(ticker) total_shares = summary['total_shares'] position_value = avg_entry * total_shares col1, col2 = st.columns(2) with col1: st.metric("Total Shares", f"{total_shares:,}") st.metric("Average Entry", f"${avg_entry:.2f}") st.metric("Position Value", f"${position_value:.2f}") with col2: if current_price: current_value = current_price * total_shares paper_pl = (current_price - avg_entry) * total_shares pl_percentage = (paper_pl / position_value) * 100 st.metric("Current Price", f"${current_price:.2f}") st.metric("Paper P/L", f"${paper_pl:.2f}", f"{pl_percentage:.2f}%") total_portfolio_value += current_value total_paper_pl += paper_pl if total_portfolio_value > 0: st.markdown("---") st.subheader("Portfolio Summary") col1, col2 = st.columns(2) with col1: st.metric("Total Portfolio Value", f"${total_portfolio_value:.2f}") with col2: st.metric("Total P/L", f"${total_paper_pl:.2f}", f"{(total_paper_pl / (total_portfolio_value - total_paper_pl)) * 100:.2f}%") with tab2: st.subheader("Add New Trade") ticker = st.text_input("Ticker Symbol").upper() # Add direction selection direction = st.selectbox( "Direction", ["Buy", "Sell"], key="trade_direction" ) if ticker: # Show existing positions for this ticker existing_positions = get_position_summary(ticker) if existing_positions: st.write(f"Existing {ticker} Positions:") for pos in existing_positions: st.write(f"Position ID: {pos['position_id']}") st.write(f"Total Shares: {pos['total_shares']}") st.write(f"Average Entry: ${pos['avg_entry_price']:.2f}") if direction == "Sell": position_id = st.selectbox( "Select Position to Exit", options=[pos['position_id'] for pos in existing_positions], key="position_select" ) else: # Buy add_to_existing = st.checkbox("Add to existing position") if add_to_existing: position_id = st.selectbox( "Select Position ID", options=[pos['position_id'] for pos in existing_positions], key="position_select" ) else: position_id = generate_position_id(ticker) else: if direction == "Sell": st.error("No existing positions found for this ticker") st.stop() position_id = generate_position_id(ticker) col1, col2 = st.columns(2) with col1: shares = st.number_input("Number of Shares", min_value=1, step=1) if direction == "Buy": entry_price = st.number_input("Entry Price", min_value=0.01, step=0.01) else: entry_price = st.number_input("Exit Price", min_value=0.01, step=0.01) with col2: if direction == "Buy": target_price = st.number_input("Target Price", min_value=0.01, step=0.01) stop_loss = st.number_input("Stop Loss", min_value=0.01, step=0.01) strategy = st.text_input("Strategy") else: exit_reason = st.text_area("Exit Reason", key="exit_reason") order_type = st.selectbox("Order Type", ["Market", "Limit"], key="add_trade_order_type") entry_date = st.date_input("Entry Date") entry_time = st.time_input("Entry Time") if direction == "Buy": followed_rules = st.checkbox("Followed Trading Rules") entry_reason = st.text_area("Entry Reason", key="add_trade_reason") notes = st.text_area("Notes", key="add_trade_notes") if st.button("Add Trade"): try: entry_datetime = datetime.combine(entry_date, entry_time) entry_datetime = pytz.timezone('US/Pacific').localize(entry_datetime) trade = TradeEntry( ticker=ticker, entry_date=entry_datetime, shares=shares, entry_price=entry_price, target_price=target_price if direction == "Buy" else None, stop_loss=stop_loss if direction == "Buy" else None, strategy=strategy if direction == "Buy" else None, order_type=order_type, position_id=position_id, followed_rules=followed_rules if direction == "Buy" else None, entry_reason=entry_reason if direction == "Buy" else None, exit_reason=exit_reason if direction == "Sell" else None, notes=notes, direction=direction.lower() ) add_trade(trade) st.success("Trade added successfully!") st.query_params(rerun=True) except Exception as e: st.error(f"Error adding trade: {str(e)}") with tab3: st.subheader("Update Trade") open_trades = get_open_trades() if open_trades: trade_id = st.selectbox( "Select Trade to Update", options=[t['id'] for t in open_trades], format_func=lambda x: f"{next(t['ticker'] for t in open_trades if t['id'] == x)} - {x}", key="trade_select" ) trade = next(t for t in open_trades if t['id'] == trade_id) col1, col2 = st.columns(2) with col1: new_shares = st.number_input("Shares", value=trade['shares']) new_entry = st.number_input("Entry Price", value=float(trade['entry_price'])) new_target = st.number_input("Target Price", value=float(trade['target_price'])) with col2: new_stop = st.number_input("Stop Loss", value=float(trade['stop_loss'])) new_strategy = st.text_input("Strategy", value=trade['strategy']) new_order_type = st.selectbox("Order Type", ["Market", "Limit"], index=0 if trade['order_type'] == "Market" else 1, key="update_trade_order_type") # Add date and time fields entry_date = st.date_input( "Entry Date", value=trade['entry_date'].date(), key="update_entry_date" ) entry_time = st.time_input( "Entry Time", value=trade['entry_date'].time(), key="update_entry_time" ) new_notes = st.text_area("Notes", value=trade['notes'] if trade['notes'] else "", key="update_trade_notes") if st.button("Update Trade"): try: # Combine date and time into datetime entry_datetime = datetime.combine(entry_date, entry_time) entry_datetime = pytz.timezone('US/Pacific').localize(entry_datetime) updates = { 'entry_date': entry_datetime, 'shares': new_shares, 'entry_price': new_entry, 'target_price': new_target, 'stop_loss': new_stop, 'strategy': new_strategy, 'order_type': new_order_type, 'notes': new_notes } update_trade(trade_id, updates) st.success("Trade updated successfully!") st.query_params(rerun=True) except Exception as e: st.error(f"Error updating trade: {str(e)}") else: st.info("No open trades to update") with tab4: st.subheader("Trade History") history = get_trade_history() if history: # Add P/L chart fig = plot_trade_history(history) if fig: st.plotly_chart(fig, use_container_width=True) for trade in history: with st.expander(f"{trade['ticker']} - {format_datetime(trade['entry_date'])}"): profit_loss = (trade['exit_price'] - trade['entry_price']) * trade['shares'] if trade['exit_price'] else None col1, col2 = st.columns(2) with col1: st.metric("Entry Price", f"${trade['entry_price']:.2f}") st.metric("Shares", trade['shares']) if profit_loss: st.metric("P/L", f"${profit_loss:.2f}") with col2: if trade['exit_price']: st.metric("Exit Price", f"${trade['exit_price']:.2f}") st.metric("Exit Date", format_datetime(trade['exit_date'])) st.text(f"Strategy: {trade['strategy']}") if trade['notes']: st.text(f"Notes: {trade['notes']}") else: st.info("No trade history found")