From c00893360e1574841b1deab12a131e261e47cbc3 Mon Sep 17 00:00:00 2001 From: "Bobby (aider)" Date: Mon, 17 Feb 2025 16:10:12 -0800 Subject: [PATCH] feat: Add watchlist feature with database tables and UI integration --- src/migrations/add_watchlist_tables.py | 36 ++++++++++ src/pages/trading/trading_system_page.py | 90 +++++++++++++++++++++++- src/trading/watchlist.py | 71 +++++++++++++++++++ 3 files changed, 196 insertions(+), 1 deletion(-) create mode 100644 src/migrations/add_watchlist_tables.py create mode 100644 src/trading/watchlist.py diff --git a/src/migrations/add_watchlist_tables.py b/src/migrations/add_watchlist_tables.py new file mode 100644 index 0000000..60093a8 --- /dev/null +++ b/src/migrations/add_watchlist_tables.py @@ -0,0 +1,36 @@ +from db.db_connection import create_client + +def create_watchlist_tables(): + with create_client() as client: + cursor = client.cursor() + + # Create watchlists table + cursor.execute(""" + CREATE TABLE IF NOT EXISTS watchlists ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL UNIQUE, + strategy TEXT, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ) + """) + + # Create watchlist items table + cursor.execute(""" + CREATE TABLE IF NOT EXISTS watchlist_items ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + watchlist_id INTEGER, + ticker TEXT NOT NULL, + entry_price REAL, + target_price REAL, + stop_loss REAL, + notes TEXT, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (watchlist_id) REFERENCES watchlists(id), + UNIQUE(watchlist_id, ticker) + ) + """) + + client.commit() + +if __name__ == "__main__": + create_watchlist_tables() diff --git a/src/pages/trading/trading_system_page.py b/src/pages/trading/trading_system_page.py index a23ed3e..5afeece 100644 --- a/src/pages/trading/trading_system_page.py +++ b/src/pages/trading/trading_system_page.py @@ -5,10 +5,19 @@ from utils.data_utils import get_current_prices from pages.analysis.monte_carlo_page import MonteCarloSimulator from datetime import datetime, timedelta from utils.common_utils import get_stock_data +from trading.watchlist import ( + create_watchlist, get_watchlists, add_to_watchlist, + remove_from_watchlist, get_watchlist_items, WatchlistItem +) def trading_system_page(): st.header("Trading System") - st.subheader("Position Calculator") + + # Create tabs + tab1, tab2 = st.tabs(["Position Calculator", "Watch Lists"]) + + with tab1: + st.subheader("Position Calculator") # Get latest portfolio value and open trades for total portfolio calculation portfolio_data = get_latest_portfolio_value() @@ -173,6 +182,85 @@ def trading_system_page(): with col3: st.metric("Days Projected", f"{days_out}") st.metric("Confidence Level", f"{confidence_level}%") + + # Add to watchlist option + if st.button("Add to Watch List"): + watchlists = get_watchlists() + if not watchlists: + st.warning("No watch lists available. Create one in the Watch Lists tab.") + else: + selected_list = st.selectbox( + "Select Watch List", + options=[(w['id'], w['name']) for w in watchlists], + format_func=lambda x: x[1] + ) + + notes = st.text_area("Notes") + + if st.button("Confirm Add to Watch List"): + item = WatchlistItem( + ticker=ticker, + entry_price=entry_price, + target_price=target_price, + stop_loss=position['stop_loss'], + notes=notes + ) + if add_to_watchlist(selected_list[0], item): + st.success(f"Added {ticker} to watch list!") + else: + st.error("Failed to add to watch list") except Exception as e: st.error(f"Error calculating position: {str(e)}") + + with tab2: + st.subheader("Watch Lists") + + # Create new watch list + with st.expander("Create New Watch List"): + new_list_name = st.text_input("Watch List Name") + strategy = st.selectbox( + "Strategy", + options=["SunnyBand", "Heikin Ashi", "Three ATR EMA", "Other"], + index=3 + ) + if st.button("Create Watch List"): + if new_list_name: + create_watchlist(new_list_name, strategy) + st.success(f"Created watch list: {new_list_name}") + else: + st.error("Please enter a watch list name") + + # Display watch lists + watchlists = get_watchlists() + if watchlists: + selected_watchlist = st.selectbox( + "Select Watch List to View", + options=[(w['id'], w['name']) for w in watchlists], + format_func=lambda x: x[1] + ) + + items = get_watchlist_items(selected_watchlist[0]) + if items: + for item in items: + with st.container(): + col1, col2, col3, col4, col5 = st.columns([2, 2, 2, 2, 1]) + with col1: + st.write(f"**{item.ticker}**") + with col2: + st.write(f"Entry: ${item.entry_price:.2f}") + with col3: + st.write(f"Target: ${item.target_price:.2f}") + with col4: + st.write(f"Stop: ${item.stop_loss:.2f}") + with col5: + if st.button("Remove", key=f"remove_{item.id}"): + if remove_from_watchlist(item.id): + st.rerun() + if item.notes: + st.info(item.notes) + st.divider() + else: + st.info("No items in this watch list") + else: + st.info("No watch lists created yet") diff --git a/src/trading/watchlist.py b/src/trading/watchlist.py new file mode 100644 index 0000000..1e0c7ed --- /dev/null +++ b/src/trading/watchlist.py @@ -0,0 +1,71 @@ +from dataclasses import dataclass +from datetime import datetime +from typing import List, Optional +from db.db_connection import create_client + +@dataclass +class WatchlistItem: + ticker: str + entry_price: float + target_price: float + stop_loss: float + notes: Optional[str] = None + watchlist_id: Optional[int] = None + id: Optional[int] = None + created_at: Optional[datetime] = None + +def create_watchlist(name: str, strategy: Optional[str] = None) -> int: + with create_client() as client: + cursor = client.cursor() + cursor.execute( + "INSERT INTO watchlists (name, strategy) VALUES (?, ?)", + (name, strategy) + ) + return cursor.lastrowid + +def get_watchlists() -> List[dict]: + with create_client() as client: + cursor = client.cursor() + cursor.execute("SELECT * FROM watchlists ORDER BY name") + return [dict(row) for row in cursor.fetchall()] + +def add_to_watchlist(watchlist_id: int, item: WatchlistItem) -> bool: + with create_client() as client: + cursor = client.cursor() + try: + cursor.execute(""" + INSERT INTO watchlist_items + (watchlist_id, ticker, entry_price, target_price, stop_loss, notes) + VALUES (?, ?, ?, ?, ?, ?) + """, (watchlist_id, item.ticker, item.entry_price, item.target_price, + item.stop_loss, item.notes)) + client.commit() + return True + except Exception: + return False + +def remove_from_watchlist(item_id: int) -> bool: + with create_client() as client: + cursor = client.cursor() + cursor.execute("DELETE FROM watchlist_items WHERE id = ?", (item_id,)) + return cursor.rowcount > 0 + +def get_watchlist_items(watchlist_id: Optional[int] = None) -> List[WatchlistItem]: + with create_client() as client: + cursor = client.cursor() + if watchlist_id: + cursor.execute(""" + SELECT i.*, w.name as watchlist_name, w.strategy + FROM watchlist_items i + JOIN watchlists w ON w.id = i.watchlist_id + WHERE watchlist_id = ? + ORDER BY i.created_at DESC + """, (watchlist_id,)) + else: + cursor.execute(""" + SELECT i.*, w.name as watchlist_name, w.strategy + FROM watchlist_items i + JOIN watchlists w ON w.id = i.watchlist_id + ORDER BY i.created_at DESC + """) + return [WatchlistItem(**row) for row in cursor.fetchall()]