diff --git a/src/pages/journal/__init__.py b/src/pages/journal/__init__.py
new file mode 100644
index 0000000..c045a4e
--- /dev/null
+++ b/src/pages/journal/__init__.py
@@ -0,0 +1 @@
+# Trading journal package
diff --git a/src/pages/journal/trading_journal_page.py b/src/pages/journal/trading_journal_page.py
new file mode 100644
index 0000000..5d04a9d
--- /dev/null
+++ b/src/pages/journal/trading_journal_page.py
@@ -0,0 +1,308 @@
+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")
diff --git a/src/streamlit_app.py b/src/streamlit_app.py
index 6d6a62d..5509fdd 100644
--- a/src/streamlit_app.py
+++ b/src/streamlit_app.py
@@ -3,12 +3,7 @@ import pandas as pd
from datetime import datetime
import pytz
from db.db_connection import create_client
-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
-)
+from pages.journal.trading_journal_page import trading_journal_page
from trading.trading_plan import (
delete_trading_plan,
TradingPlan, PlanStatus, Timeframe, MarketFocus, TradeFrequency,
@@ -54,303 +49,6 @@ def load_scanner_reports():
# Sort by creation time, newest first
return sorted(reports, key=lambda x: x['created'], reverse=True)
-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")
def technical_scanner_page():
st.header("Technical Scanner")