stock_system/src/pages/trading/trading_system_page.py

355 lines
16 KiB
Python

import logging
import streamlit as st
from trading.journal import get_latest_portfolio_value, get_open_trades_summary
from trading.position_calculator import PositionCalculator
from utils.data_utils import get_current_prices
from pages.analysis.monte_carlo_page import MonteCarloSimulator
from datetime import datetime, timedelta
import time
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,
ensure_tables_exist
)
from db.db_connection import create_client
# Configure logging
logger = logging.getLogger(__name__)
def trading_system_page():
# Initialize session state
if 'prefill_watchlist' not in st.session_state:
st.session_state.prefill_watchlist = None
st.header("Trading System")
# 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()
cash_balance = portfolio_data['cash_balance'] if portfolio_data else 0
# Calculate total portfolio value including open positions
open_summary = get_open_trades_summary()
total_position_value = 0
total_paper_pl = 0
if open_summary:
# Get current prices for all open positions
unique_tickers = list(set(summary['ticker'] for summary in open_summary))
current_prices = get_current_prices(unique_tickers)
# Calculate total invested value and paper P/L
for summary in open_summary:
ticker = summary['ticker']
current_price = current_prices.get(ticker, 0)
shares = summary['total_shares']
avg_entry = summary['avg_entry_price']
position_value = current_price * shares if current_price else avg_entry * shares
total_position_value += position_value
total_paper_pl += (current_price - avg_entry) * shares if current_price else 0
total_portfolio_value = cash_balance + total_position_value
# Display portfolio information
col1, col2, col3 = st.columns(3)
with col1:
st.info(f"Available Cash: ${cash_balance:,.2f}")
with col2:
st.info(f"Positions Value: ${total_position_value:,.2f}")
with col3:
st.info(f"Total Portfolio Value: ${total_portfolio_value:,.2f}")
col1, col2 = st.columns(2)
with col1:
account_size = st.number_input("Account Size ($)",
min_value=0.0,
value=total_portfolio_value,
step=1000.0)
risk_percentage = st.number_input("Risk Percentage (%)",
min_value=0.1,
max_value=100.0,
value=1.0,
step=0.1)
use_monte_carlo = st.checkbox("Use Monte Carlo for Analysis", value=True)
if use_monte_carlo:
days_out = st.number_input("Days to Project",
min_value=1,
max_value=30,
value=5,
help="Number of days to project for target price")
confidence_level = st.slider("Confidence Level (%)",
min_value=80,
max_value=99,
value=95)
with col2:
ticker = st.text_input("Ticker Symbol", value="").upper()
entry_price = st.number_input("Entry Price ($)", min_value=0.01, step=0.01)
if not use_monte_carlo:
target_price = st.number_input("Target Price ($)", min_value=0.01, step=0.01)
if st.button("Calculate Position"):
try:
if not ticker:
st.error("Please enter a ticker symbol")
return
# Get historical data for Monte Carlo simulation
if use_monte_carlo:
with st.spinner("Calculating optimal stop loss..."):
df = get_stock_data(
ticker,
datetime.now() - timedelta(days=30), # Last 30 days of data
datetime.now(),
'1m' # Minute data for more accurate simulation
)
if df.empty:
st.error("No data available for the selected ticker")
return
# Initialize Monte Carlo simulator
# Initialize Monte Carlo simulator
simulator = MonteCarloSimulator(df, num_simulations=1000, time_horizon=days_out)
# Calculate stop loss and target prices
stop_loss_price = simulator.calculate_stop_loss(risk_percentage)
target_price = simulator.calculate_target_price(confidence_level)
# Calculate stop loss percentage
stop_loss_percentage = abs((stop_loss_price - entry_price) / entry_price * 100)
else:
stop_loss_percentage = 7.0 # Default value if not using Monte Carlo
calculator = PositionCalculator(
account_size=account_size,
risk_percentage=risk_percentage,
stop_loss_percentage=stop_loss_percentage
)
position = calculator.calculate_position_size(entry_price, target_price)
# Calculate maximum shares possible with available cash
max_shares_by_cash = int(cash_balance / entry_price) if entry_price > 0 else 0
# Adjust shares based on available cash
recommended_shares = min(position['shares'], max_shares_by_cash)
col1, col2 = st.columns(2)
with col1:
if recommended_shares < position['shares']:
st.warning(
f"Position size limited by available cash.\n"
f"Ideal shares: {position['shares']:,}\n"
f"Maximum affordable shares: {recommended_shares:,}"
)
position_value = recommended_shares * entry_price
risk_amount = position['risk_amount'] * (recommended_shares / position['shares'])
st.metric("Recommended Shares", f"{recommended_shares:,}")
st.metric("Position Value", f"${position_value:,.2f}")
st.metric("Risk Amount", f"${risk_amount:,.2f}")
else:
st.metric("Number of Shares", f"{position['shares']:,}")
st.metric("Position Value", f"${position['position_value']:,.2f}")
st.metric("Risk Amount", f"${position['risk_amount']:,.2f}")
with col2:
st.metric("Stop Loss Price", f"${position['stop_loss']:.2f}")
st.metric("Potential Loss", f"${position['potential_loss']:,.2f}")
if 'potential_profit' in position:
potential_profit = (target_price - entry_price) * recommended_shares
risk_reward = abs(potential_profit / (position['stop_loss'] - entry_price) / recommended_shares) if recommended_shares > 0 else 0
st.metric("Potential Profit", f"${potential_profit:,.2f}")
st.metric("Risk/Reward Ratio", f"{risk_reward:.2f}")
# Show percentage of cash being used
if recommended_shares > 0:
cash_usage = (recommended_shares * entry_price / cash_balance) * 100
portfolio_usage = (recommended_shares * entry_price / total_portfolio_value) * 100
st.info(
f"This position would use:\n"
f"- {cash_usage:.1f}% of available cash\n"
f"- {portfolio_usage:.1f}% of total portfolio"
)
# Add Monte Carlo metrics if used
if use_monte_carlo:
st.subheader("Monte Carlo Analysis")
col1, col2, col3 = st.columns(3)
with col1:
st.metric("Stop Loss Price", f"${stop_loss_price:.2f}")
st.metric("Stop Loss %", f"{stop_loss_percentage:.2f}%")
with col2:
st.metric("Target Price", f"${target_price:.2f}")
st.metric("Target %", f"{((target_price - entry_price) / entry_price * 100):.2f}%")
with col3:
st.metric("Days Projected", f"{days_out}")
st.metric("Confidence Level", f"{confidence_level}%")
# Add to watchlist option
st.divider()
st.subheader("Save to Watch List")
if st.button("Prepare for Watch List", key="prepare_watchlist"):
st.session_state.prefill_watchlist = {
'ticker': ticker,
'entry_price': float(entry_price),
'target_price': float(target_price),
'stop_loss': float(position['stop_loss']),
'shares': recommended_shares
}
st.success("Details saved! Switch to Watch Lists tab to complete adding to your 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")
# Add new item section
with st.expander("Add New Item", expanded=bool(st.session_state.prefill_watchlist)):
watchlists = get_watchlists()
if watchlists:
selected_list = st.selectbox(
"Select Watch List",
options=[(w['id'], w['name']) for w in watchlists],
format_func=lambda x: x[1]
)
# Use prefilled data if available
prefill = st.session_state.prefill_watchlist or {}
# Debug output to verify prefill data
if prefill:
st.write("Debug - Prefill ", prefill)
col1, col2 = st.columns(2)
with col1:
ticker = st.text_input("Ticker", value=str(prefill.get('ticker', '')))
entry_price = st.number_input("Entry Price",
value=float(prefill.get('entry_price') or 0.0),
min_value=0.0,
step=0.01,
format="%.2f")
shares = st.number_input("Shares",
value=int(prefill.get('shares') or 0),
min_value=0,
step=1)
with col2:
target_price = st.number_input("Target Price",
value=float(prefill.get('target_price') or 0.0),
min_value=0.0,
step=0.01,
format="%.2f")
stop_loss = st.number_input("Stop Loss",
value=float(prefill.get('stop_loss') or 0.0),
min_value=0.0,
step=0.01,
format="%.2f")
shares = st.number_input("Shares",
value=prefill.get('shares', 0),
min_value=0,
step=1)
with col2:
target_price = st.number_input("Target Price",
value=prefill.get('target_price', 0.0),
min_value=0.0,
step=0.01,
format="%.2f")
stop_loss = st.number_input("Stop Loss",
value=prefill.get('stop_loss', 0.0),
min_value=0.0,
step=0.01,
format="%.2f")
notes = st.text_area("Notes")
if st.button("Add to Watch List"):
try:
ensure_tables_exist()
item = WatchlistItem(
ticker=ticker,
entry_price=entry_price,
target_price=target_price,
stop_loss=stop_loss,
shares=shares,
notes=notes
)
success = add_to_watchlist(selected_list[0], item)
if success:
st.success(f"Added {ticker} to watchlist!")
# Clear the prefill data
st.session_state.prefill_watchlist = None
time.sleep(1)
st.rerun()
else:
st.error("Failed to add to watchlist")
except Exception as e:
st.error(f"Error: {str(e)}")
logger.exception("Error adding to watchlist")
else:
st.warning("Please create a watch list first")
# Display watch lists
st.subheader("Current 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],
key="view_watchlist" # Add a unique key to avoid conflicts
)
items = get_watchlist_items(selected_watchlist[0])
if items:
for item in items:
with st.container():
col1, col2, col3, col4, col5, col6 = st.columns([2, 2, 2, 2, 1, 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:
st.write(f"Shares: {item.shares:,}")
with col6:
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")