1267 lines
61 KiB
Python
1267 lines
61 KiB
Python
import streamlit as st
|
|
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 trading.trading_plan import (
|
|
delete_trading_plan,
|
|
TradingPlan, PlanStatus, Timeframe, MarketFocus, TradeFrequency,
|
|
create_trading_plan_table, save_trading_plan, get_trading_plan,
|
|
get_all_trading_plans, update_trading_plan, get_plan_trades,
|
|
link_trades_to_plan, calculate_plan_metrics, unlink_trades_from_plan
|
|
)
|
|
from trading.position_calculator import PositionCalculator
|
|
from pages.rules.strategy_guide_page import strategy_guide_page
|
|
from screener.scanner_controller import run_technical_scanner
|
|
from screener.canslim_controller import run_canslim_screener
|
|
from trading.portfolio import Portfolio, Position
|
|
from trading.position_calculator import PositionCalculator
|
|
import plotly.graph_objects as go
|
|
from plotly.subplots import make_subplots
|
|
|
|
def init_session_state():
|
|
"""Initialize session state variables"""
|
|
if 'page' not in st.session_state:
|
|
st.session_state.page = 'Trading Journal'
|
|
|
|
def load_scanner_reports():
|
|
"""Load and return available scanner reports"""
|
|
import os
|
|
import pandas as pd
|
|
from datetime import datetime
|
|
|
|
reports = []
|
|
reports_dir = "reports"
|
|
|
|
if os.path.exists(reports_dir):
|
|
for file in os.listdir(reports_dir):
|
|
if file.endswith(".csv"):
|
|
file_path = os.path.join(reports_dir, file)
|
|
# Get file creation time
|
|
created = datetime.fromtimestamp(os.path.getctime(file_path))
|
|
reports.append({
|
|
'name': file,
|
|
'path': file_path,
|
|
'created': created
|
|
})
|
|
|
|
# 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}<br>P/L: $%{y:.2f}<extra></extra>')
|
|
)
|
|
|
|
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")
|
|
|
|
# Create tabs for scanner and reports
|
|
scanner_tab, reports_tab = st.tabs(["Run Scanner", "View Reports"])
|
|
|
|
with scanner_tab:
|
|
scanner_type = st.selectbox(
|
|
"Select Scanner",
|
|
["SunnyBands", "ATR-EMA", "ATR-EMA v2"],
|
|
key="tech_scanner_type"
|
|
)
|
|
|
|
# Add interval selection
|
|
interval = st.selectbox(
|
|
"Select Time Interval",
|
|
["Daily", "5 minute", "15 minute", "30 minute", "1 hour"],
|
|
key="interval_select"
|
|
)
|
|
|
|
# Convert interval to format expected by scanner
|
|
interval_map = {
|
|
"Daily": "1d",
|
|
"5 minute": "5m",
|
|
"15 minute": "15m",
|
|
"30 minute": "30m",
|
|
"1 hour": "1h"
|
|
}
|
|
selected_interval = interval_map[interval]
|
|
|
|
# Date range selection
|
|
date_col1, date_col2 = st.columns(2)
|
|
with date_col1:
|
|
start_date = st.date_input("Start Date")
|
|
with date_col2:
|
|
end_date = st.date_input("End Date")
|
|
|
|
col1, col2 = st.columns(2)
|
|
with col1:
|
|
min_price = st.number_input("Minimum Price", value=5.0, step=0.1)
|
|
max_price = st.number_input("Maximum Price", value=100.0, step=0.1)
|
|
|
|
with col2:
|
|
min_volume = st.number_input("Minimum Volume", value=500000, step=100000)
|
|
portfolio_size = st.number_input("Portfolio Size", value=100000.0, step=1000.0)
|
|
|
|
if st.button("Run Scanner"):
|
|
with st.spinner("Running scanner..."):
|
|
try:
|
|
signals = run_technical_scanner(
|
|
scanner_choice=scanner_type.lower().replace(" ", "_"),
|
|
start_date=start_date.strftime("%Y-%m-%d"),
|
|
end_date=end_date.strftime("%Y-%m-%d"),
|
|
min_price=min_price,
|
|
max_price=max_price,
|
|
min_volume=min_volume,
|
|
portfolio_size=portfolio_size,
|
|
interval=selected_interval
|
|
)
|
|
if signals:
|
|
st.success(f"Found {len(signals)} signals")
|
|
for signal in signals:
|
|
with st.expander(f"{signal['ticker']} - ${signal['entry_price']:.2f}"):
|
|
col1, col2 = st.columns(2)
|
|
with col1:
|
|
st.metric("Entry Price", f"${signal['entry_price']:.2f}")
|
|
st.metric("Target", f"${signal['target_price']:.2f}")
|
|
st.metric("Stop Loss", f"${signal['stop_loss']:.2f}")
|
|
with col2:
|
|
st.metric("Shares", signal['shares'])
|
|
st.metric("Position Size", f"${signal['position_size']:.2f}")
|
|
st.metric("Risk Amount", f"${abs(signal['risk_amount']):.2f}")
|
|
else:
|
|
st.info("No signals found")
|
|
except Exception as e:
|
|
st.error(f"Error running scanner: {str(e)}")
|
|
|
|
with reports_tab:
|
|
st.subheader("Scanner Reports")
|
|
|
|
reports = load_scanner_reports()
|
|
if reports:
|
|
# Create a selectbox to choose the report
|
|
selected_report = st.selectbox(
|
|
"Select Report",
|
|
options=reports,
|
|
format_func=lambda x: f"{x['name']} ({x['created'].strftime('%Y-%m-%d %H:%M')})",
|
|
key="tech_scanner_report"
|
|
)
|
|
|
|
if selected_report:
|
|
try:
|
|
# Load and display the CSV
|
|
df = pd.read_csv(selected_report['path'])
|
|
|
|
# Add download button
|
|
st.download_button(
|
|
label="Download Report",
|
|
data=df.to_csv(index=False),
|
|
file_name=selected_report['name'],
|
|
mime='text/csv'
|
|
)
|
|
|
|
# Display the dataframe
|
|
st.dataframe(df)
|
|
|
|
except Exception as e:
|
|
st.error(f"Error loading report: {str(e)}")
|
|
else:
|
|
st.info("No scanner reports found")
|
|
|
|
def canslim_screener_page():
|
|
st.header("CANSLIM Screener")
|
|
|
|
# Create tabs for scanner and reports
|
|
scanner_tab, reports_tab = st.tabs(["Run Scanner", "View Reports"])
|
|
|
|
with scanner_tab:
|
|
# Date range selection
|
|
col1, col2 = st.columns(2)
|
|
with col1:
|
|
start_date = st.date_input("Start Date")
|
|
with col2:
|
|
end_date = st.date_input("End Date")
|
|
|
|
# CANSLIM criteria selection
|
|
st.subheader("Select Screening Criteria")
|
|
|
|
c_criteria = st.expander("Current Quarterly Earnings (C)")
|
|
with c_criteria:
|
|
eps_threshold = st.slider("EPS Growth Threshold (%)", 0, 100, 25)
|
|
sales_threshold = st.slider("Sales Growth Threshold (%)", 0, 100, 25)
|
|
roe_threshold = st.slider("ROE Threshold (%)", 0, 50, 17)
|
|
|
|
a_criteria = st.expander("Annual Earnings Growth (A)")
|
|
with a_criteria:
|
|
annual_eps_threshold = st.slider("Annual EPS Growth Threshold (%)", 0, 100, 25)
|
|
|
|
l_criteria = st.expander("Industry Leadership (L)")
|
|
with l_criteria:
|
|
use_l = st.checkbox("Check Industry Leadership", value=True)
|
|
l_threshold = st.slider("Industry Leadership Score Threshold", 0.0, 1.0, 0.7)
|
|
|
|
i_criteria = st.expander("Institutional Sponsorship (I)")
|
|
with i_criteria:
|
|
use_i = st.checkbox("Check Institutional Sponsorship", value=True)
|
|
i_threshold = st.slider("Institutional Sponsorship Score Threshold", 0.0, 1.0, 0.7)
|
|
|
|
if st.button("Run CANSLIM Screener"):
|
|
with st.spinner("Running CANSLIM screener..."):
|
|
try:
|
|
# Prepare selected screeners dictionary
|
|
selected_screeners = {
|
|
"C": {
|
|
"EPS_Score": eps_threshold / 100,
|
|
"Sales_Score": sales_threshold / 100,
|
|
"ROE_Score": roe_threshold / 100
|
|
},
|
|
"A": {
|
|
"Annual_EPS_Score": annual_eps_threshold / 100
|
|
}
|
|
}
|
|
|
|
if use_l:
|
|
selected_screeners["L"] = {"L_Score": l_threshold}
|
|
if use_i:
|
|
selected_screeners["I"] = {"I_Score": i_threshold}
|
|
|
|
# Convert dates to strings for the screener
|
|
start_str = start_date.strftime("%Y-%m-%d")
|
|
end_str = end_date.strftime("%Y-%m-%d")
|
|
|
|
# Run the screener
|
|
st.session_state.screener_params = {
|
|
"start_date": start_str,
|
|
"end_date": end_str,
|
|
"selected_screeners": selected_screeners
|
|
}
|
|
|
|
# Modify run_canslim_screener to accept parameters
|
|
run_canslim_screener()
|
|
st.success("Screening complete! Check the Reports tab for results.")
|
|
|
|
except Exception as e:
|
|
st.error(f"Error running CANSLIM screener: {str(e)}")
|
|
|
|
with reports_tab:
|
|
st.subheader("CANSLIM Reports")
|
|
|
|
# Use the same report loading function but look in a CANSLIM-specific directory
|
|
reports = load_scanner_reports() # You might want to modify this to look in a CANSLIM directory
|
|
if reports:
|
|
# Create a selectbox to choose the report
|
|
selected_report = st.selectbox(
|
|
"Select Report",
|
|
options=reports,
|
|
format_func=lambda x: f"{x['name']} ({x['created'].strftime('%Y-%m-%d %H:%M')})",
|
|
key="canslim_scanner_report"
|
|
)
|
|
|
|
if selected_report:
|
|
try:
|
|
# Load and display the CSV
|
|
df = pd.read_csv(selected_report['path'])
|
|
|
|
# Add download button
|
|
st.download_button(
|
|
label="Download Report",
|
|
data=df.to_csv(index=False),
|
|
file_name=selected_report['name'],
|
|
mime='text/csv'
|
|
)
|
|
|
|
# Display the dataframe
|
|
st.dataframe(df)
|
|
|
|
except Exception as e:
|
|
st.error(f"Error loading report: {str(e)}")
|
|
else:
|
|
st.info("No CANSLIM reports found")
|
|
|
|
def trading_system_page():
|
|
st.header("Trading System")
|
|
|
|
# Create tabs
|
|
calc_tab, portfolio_tab, value_tab = st.tabs(["Position Calculator", "Portfolio Management", "Portfolio Value"])
|
|
|
|
with calc_tab:
|
|
st.subheader("Position Calculator")
|
|
|
|
# Get latest portfolio value for default account size
|
|
portfolio_data = get_latest_portfolio_value()
|
|
default_account_size = portfolio_data['total_value'] if portfolio_data else 100000.0
|
|
|
|
col1, col2 = st.columns(2)
|
|
with col1:
|
|
account_size = st.number_input("Account Size ($)",
|
|
min_value=0.0,
|
|
value=default_account_size,
|
|
step=1000.0)
|
|
risk_percentage = st.number_input("Risk Percentage (%)",
|
|
min_value=0.1,
|
|
max_value=100.0,
|
|
value=1.0,
|
|
step=0.1)
|
|
stop_loss_percentage = st.number_input("Stop Loss Percentage (%)",
|
|
min_value=0.1,
|
|
max_value=100.0,
|
|
value=7.0,
|
|
step=0.1)
|
|
|
|
with col2:
|
|
entry_price = st.number_input("Entry Price ($)", min_value=0.01, step=0.01)
|
|
target_price = st.number_input("Target Price ($)", min_value=0.01, step=0.01)
|
|
|
|
if st.button("Calculate Position"):
|
|
try:
|
|
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)
|
|
|
|
col1, col2 = st.columns(2)
|
|
with col1:
|
|
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:
|
|
st.metric("Potential Profit", f"${position['potential_profit']:,.2f}")
|
|
st.metric("Risk/Reward Ratio", f"{position['risk_reward_ratio']:.2f}")
|
|
|
|
except Exception as e:
|
|
st.error(f"Error calculating position: {str(e)}")
|
|
|
|
with portfolio_tab:
|
|
st.subheader("Portfolio Management")
|
|
|
|
# Initialize portfolio if not in session state
|
|
if 'portfolio' not in st.session_state:
|
|
st.session_state.portfolio = Portfolio()
|
|
|
|
# Load existing open trades into portfolio
|
|
open_trades = get_open_trades()
|
|
for trade in open_trades:
|
|
position = Position(
|
|
symbol=trade['ticker'],
|
|
entry_date=trade['entry_date'],
|
|
entry_price=trade['entry_price'],
|
|
shares=trade['shares'],
|
|
stop_loss=trade['stop_loss'],
|
|
target_price=trade['target_price']
|
|
)
|
|
st.session_state.portfolio.add_position(position)
|
|
|
|
# Add position form
|
|
with st.expander("Add New Position"):
|
|
col1, col2 = st.columns(2)
|
|
with col1:
|
|
symbol = st.text_input("Symbol").upper()
|
|
shares = st.number_input("Number of Shares", min_value=1, step=1)
|
|
entry_price = st.number_input("Entry Price ($)", min_value=0.01, step=0.01, key="port_entry_price")
|
|
|
|
with col2:
|
|
target_price = st.number_input("Target Price ($)", min_value=0.01, step=0.01, key="port_target_price")
|
|
stop_loss = st.number_input("Stop Loss ($)", min_value=0.01, step=0.01)
|
|
|
|
if st.button("Add Position"):
|
|
try:
|
|
position = Position(
|
|
symbol=symbol,
|
|
entry_date=datetime.now(),
|
|
entry_price=entry_price,
|
|
shares=shares,
|
|
stop_loss=stop_loss,
|
|
target_price=target_price
|
|
)
|
|
st.session_state.portfolio.add_position(position)
|
|
st.success(f"Added position: {symbol}")
|
|
except Exception as e:
|
|
st.error(f"Error adding position: {str(e)}")
|
|
|
|
# Display current portfolio
|
|
positions = st.session_state.portfolio.get_position_summary()
|
|
if positions:
|
|
st.subheader("Current Positions")
|
|
# Add a counter to make unique keys
|
|
for i, pos in enumerate(positions):
|
|
with st.expander(f"{pos['symbol']} Position - {format_datetime(pos['entry_date'])}"):
|
|
col1, col2 = st.columns(2)
|
|
with col1:
|
|
st.metric("Entry Price", f"${pos['entry_price']:.2f}")
|
|
st.metric("Shares", pos['shares'])
|
|
st.metric("Current Value", f"${pos['current_value']:.2f}")
|
|
|
|
with col2:
|
|
st.metric("Stop Loss", f"${pos['stop_loss']:.2f}")
|
|
st.metric("Target", f"${pos['target_price']:.2f}")
|
|
st.metric("Risk/Reward", f"{pos['risk_reward_ratio']:.2f}")
|
|
|
|
if st.button(f"Remove {pos['symbol']}", key=f"remove_{pos['symbol']}_{i}"):
|
|
st.session_state.portfolio.remove_position(pos['symbol'])
|
|
st.rerun()
|
|
else:
|
|
st.info("No positions in portfolio")
|
|
|
|
with value_tab:
|
|
st.subheader("Portfolio Value Management")
|
|
|
|
# Get latest portfolio value
|
|
portfolio_data = get_latest_portfolio_value()
|
|
|
|
if portfolio_data:
|
|
st.metric("Current Portfolio Value", f"${portfolio_data['total_value']:,.2f}")
|
|
st.metric("Cash Balance", f"${portfolio_data['cash_balance']:,.2f}")
|
|
st.text(f"Last Updated: {portfolio_data['date']}")
|
|
else:
|
|
st.info("No portfolio value data found")
|
|
|
|
# Update portfolio value form
|
|
with st.expander("Update Portfolio Value"):
|
|
new_value = st.number_input("New Portfolio Value ($)", min_value=0.0, step=100.0)
|
|
new_cash = st.number_input("New Cash Balance ($)", min_value=0.0, step=100.0)
|
|
notes = st.text_area("Notes (optional)", key="portfolio_value_notes")
|
|
|
|
if st.button("Update Values"):
|
|
try:
|
|
update_portfolio_value(new_value, new_cash, notes)
|
|
st.success("Portfolio value updated successfully!")
|
|
st.set_query_params(rerun=True)
|
|
except Exception as e:
|
|
st.error(f"Error updating portfolio value: {str(e)}")
|
|
|
|
|
|
def trading_plan_page():
|
|
st.header("Trading Plans")
|
|
|
|
# Create tabs for different plan operations
|
|
list_tab, add_tab, edit_tab = st.tabs(["View Plans", "Add Plan", "Edit Plan"])
|
|
|
|
with list_tab:
|
|
st.subheader("Trading Plans")
|
|
plans = get_all_trading_plans()
|
|
|
|
if plans:
|
|
for plan in plans:
|
|
with st.expander(f"{plan.plan_name} ({plan.status.value})"):
|
|
col1, col2 = st.columns(2)
|
|
|
|
with col1:
|
|
st.markdown("### Basic Information")
|
|
st.write(f"**Plan Name:** {plan.plan_name}")
|
|
st.write(f"**Status:** {plan.status.value}")
|
|
st.write(f"**Author:** {plan.plan_author}")
|
|
st.write(f"**Version:** {plan.strategy_version}")
|
|
st.write(f"**Created:** {plan.created_at}")
|
|
st.write(f"**Updated:** {plan.updated_at}")
|
|
|
|
st.markdown("### Market Details")
|
|
st.write(f"**Timeframe:** {plan.timeframe.value}")
|
|
st.write(f"**Market:** {plan.market_focus.value}")
|
|
st.write(f"**Frequency:** {plan.trade_frequency.value}")
|
|
if plan.sector_focus:
|
|
st.write(f"**Sector Focus:** {plan.sector_focus}")
|
|
|
|
with col2:
|
|
st.markdown("### Risk Parameters")
|
|
st.write(f"**Stop Loss:** {plan.stop_loss}%")
|
|
st.write(f"**Profit Target:** {plan.profit_target}%")
|
|
st.write(f"**Risk/Reward Ratio:** {plan.risk_reward_ratio}")
|
|
st.write(f"**Position Size:** {plan.position_sizing}%")
|
|
st.write(f"**Risk per Trade:** {plan.total_risk_per_trade}%")
|
|
st.write(f"**Max Portfolio Risk:** {plan.max_portfolio_risk}%")
|
|
st.write(f"**Max Drawdown:** {plan.maximum_drawdown}%")
|
|
st.write(f"**Max Trades/Day:** {plan.max_trades_per_day}")
|
|
st.write(f"**Max Trades/Week:** {plan.max_trades_per_week}")
|
|
|
|
st.markdown("### Performance Metrics")
|
|
if any([plan.win_rate, plan.average_return_per_trade, plan.profit_factor]):
|
|
col3, col4 = st.columns(2)
|
|
with col3:
|
|
if plan.win_rate:
|
|
st.write(f"**Win Rate:** {plan.win_rate}%")
|
|
if plan.average_return_per_trade:
|
|
st.write(f"**Avg Return/Trade:** {plan.average_return_per_trade}%")
|
|
with col4:
|
|
if plan.profit_factor:
|
|
st.write(f"**Profit Factor:** {plan.profit_factor}")
|
|
|
|
# Add Linked Trades section
|
|
st.markdown("### Linked Trades")
|
|
plan_trades = get_plan_trades(plan.id)
|
|
if plan_trades:
|
|
total_pl = 0
|
|
winning_trades = 0
|
|
total_trades = len(plan_trades)
|
|
|
|
# Display trade statistics first
|
|
st.markdown("#### Trade Statistics")
|
|
col1, col2 = st.columns(2)
|
|
with col1:
|
|
st.write(f"**Total Trades:** {total_trades}")
|
|
st.write(f"**Winning Trades:** {winning_trades}")
|
|
if total_trades > 0:
|
|
st.write(f"**Win Rate:** {(winning_trades/total_trades)*100:.2f}%")
|
|
|
|
with col2:
|
|
st.write(f"**Total P/L:** ${total_pl:.2f}")
|
|
if total_trades > 0:
|
|
st.write(f"**Average P/L per Trade:** ${total_pl/total_trades:.2f}")
|
|
|
|
# Display trades in a table format instead of nested expanders
|
|
st.markdown("#### Individual Trades")
|
|
for trade in plan_trades:
|
|
st.markdown("---")
|
|
cols = st.columns(3)
|
|
with cols[0]:
|
|
st.write(f"**{trade['ticker']} - {trade['entry_date']}**")
|
|
st.write(f"**Direction:** {trade['direction']}")
|
|
if trade['strategy']:
|
|
st.write(f"**Strategy:** {trade['strategy']}")
|
|
|
|
with cols[1]:
|
|
st.write(f"**Entry:** ${trade['entry_price']:.2f}")
|
|
st.write(f"**Shares:** {trade['shares']}")
|
|
|
|
with cols[2]:
|
|
if trade['exit_price']:
|
|
pl = (trade['exit_price'] - trade['entry_price']) * trade['shares']
|
|
total_pl += pl
|
|
if pl > 0:
|
|
winning_trades += 1
|
|
st.write(f"**Exit:** ${trade['exit_price']:.2f}")
|
|
st.write(f"**P/L:** ${pl:.2f}")
|
|
st.write(f"**Exit Date:** {trade['exit_date']}")
|
|
else:
|
|
st.write("**Status:** Open")
|
|
else:
|
|
st.info("No trades linked to this plan")
|
|
|
|
st.markdown("### Strategy Details")
|
|
st.write("**Entry Criteria:**")
|
|
st.write(plan.entry_criteria)
|
|
|
|
st.write("**Exit Criteria:**")
|
|
st.write(plan.exit_criteria)
|
|
|
|
st.write("**Entry Confirmation:**")
|
|
st.write(plan.entry_confirmation)
|
|
|
|
st.write("**Market Conditions:**")
|
|
st.write(plan.market_conditions)
|
|
|
|
st.write("**Technical Indicators:**")
|
|
st.write(plan.indicators_used)
|
|
|
|
st.markdown("### Risk Management")
|
|
st.write("**Drawdown Adjustments:**")
|
|
st.write(plan.adjustments_for_drawdown)
|
|
|
|
st.write("**Risk Controls:**")
|
|
st.write(plan.risk_controls)
|
|
|
|
if plan.fundamental_criteria:
|
|
st.markdown("### Fundamental Analysis")
|
|
st.write(plan.fundamental_criteria)
|
|
|
|
if plan.options_strategy_details:
|
|
st.markdown("### Options Strategy")
|
|
st.write(plan.options_strategy_details)
|
|
|
|
if plan.improvements_needed:
|
|
st.markdown("### Areas for Improvement")
|
|
st.write(plan.improvements_needed)
|
|
|
|
if plan.trade_review_notes:
|
|
st.markdown("### Trade Review Notes")
|
|
st.write(plan.trade_review_notes)
|
|
|
|
if plan.future_testing_ideas:
|
|
st.markdown("### Future Testing Ideas")
|
|
st.write(plan.future_testing_ideas)
|
|
|
|
if plan.historical_backtest_results:
|
|
st.markdown("### Historical Backtest Results")
|
|
st.write(plan.historical_backtest_results)
|
|
|
|
if plan.real_trade_performance:
|
|
st.markdown("### Real Trading Performance")
|
|
st.write(plan.real_trade_performance)
|
|
else:
|
|
st.info("No trading plans found")
|
|
|
|
with add_tab:
|
|
st.subheader("Create New Trading Plan")
|
|
|
|
# Basic Info
|
|
col1, col2 = st.columns(2)
|
|
with col1:
|
|
plan_name = st.text_input("Plan Name", key="add_plan_name")
|
|
status = st.selectbox("Status", [s.value for s in PlanStatus], key="add_status")
|
|
timeframe = st.selectbox("Timeframe", [t.value for t in Timeframe], key="add_timeframe")
|
|
market_focus = st.selectbox("Market Focus", [m.value for m in MarketFocus], key="add_market_focus")
|
|
|
|
with col2:
|
|
trade_frequency = st.selectbox("Trade Frequency", [f.value for f in TradeFrequency], key="add_trade_frequency")
|
|
plan_author = st.text_input("Author", key="add_plan_author")
|
|
strategy_version = st.number_input("Version", min_value=1, value=1, key="add_strategy_version")
|
|
|
|
# Risk Parameters
|
|
st.subheader("Risk Parameters")
|
|
col1, col2 = st.columns(2)
|
|
with col1:
|
|
stop_loss = st.number_input("Stop Loss %", min_value=0.1, value=7.0, key="add_stop_loss")
|
|
profit_target = st.number_input("Profit Target %", min_value=0.1, value=21.0, key="add_profit_target")
|
|
risk_reward_ratio = profit_target / stop_loss if stop_loss > 0 else 0
|
|
st.write(f"Risk:Reward Ratio: {risk_reward_ratio:.2f}")
|
|
|
|
with col2:
|
|
position_sizing = st.number_input("Position Size %", min_value=0.1, value=5.0, key="add_position_sizing")
|
|
total_risk_per_trade = st.number_input("Risk per Trade %", min_value=0.1, value=1.0, key="add_total_risk_per_trade")
|
|
max_portfolio_risk = st.number_input("Max Portfolio Risk %", min_value=0.1, value=5.0, key="add_max_portfolio_risk")
|
|
|
|
# Trade Rules
|
|
st.subheader("Trade Rules")
|
|
col1, col2 = st.columns(2)
|
|
with col1:
|
|
max_trades_per_day = st.number_input("Max Trades per Day", min_value=1, value=3, key="add_max_trades_per_day")
|
|
max_trades_per_week = st.number_input("Max Trades per Week", min_value=1, value=15, key="add_max_trades_per_week")
|
|
maximum_drawdown = st.number_input("Maximum Drawdown %", min_value=0.1, value=20.0, key="add_maximum_drawdown")
|
|
|
|
# Strategy Details
|
|
st.subheader("Strategy Details")
|
|
entry_criteria = st.text_area("Entry Criteria", key="add_entry_criteria")
|
|
exit_criteria = st.text_area("Exit Criteria", key="add_exit_criteria")
|
|
entry_confirmation = st.text_area("Entry Confirmation", key="add_entry_confirmation")
|
|
market_conditions = st.text_area("Market Conditions", key="add_market_conditions")
|
|
indicators_used = st.text_area("Technical Indicators", key="add_indicators_used")
|
|
|
|
# Risk Management
|
|
st.subheader("Risk Management")
|
|
adjustments_for_drawdown = st.text_area("Drawdown Adjustments", key="add_adjustments_for_drawdown")
|
|
risk_controls = st.text_area("Risk Controls", key="add_risk_controls")
|
|
|
|
# Optional Fields
|
|
st.subheader("Additional Information")
|
|
col1, col2 = st.columns(2)
|
|
with col1:
|
|
sector_focus = st.text_input("Sector Focus (optional)", key="add_sector_focus")
|
|
fundamental_criteria = st.text_area("Fundamental Criteria (optional)", key="add_fundamental_criteria")
|
|
|
|
with col2:
|
|
options_strategy_details = st.text_area("Options Strategy Details (optional)", key="add_options_strategy_details")
|
|
improvements_needed = st.text_area("Improvements Needed (optional)", key="add_improvements_needed")
|
|
|
|
if st.button("Create Trading Plan", key="create_plan_button"):
|
|
try:
|
|
plan = TradingPlan(
|
|
plan_name=plan_name,
|
|
status=PlanStatus(status),
|
|
timeframe=Timeframe(timeframe),
|
|
market_focus=MarketFocus(market_focus),
|
|
trade_frequency=TradeFrequency(trade_frequency),
|
|
entry_criteria=entry_criteria,
|
|
exit_criteria=exit_criteria,
|
|
stop_loss=stop_loss,
|
|
profit_target=profit_target,
|
|
risk_reward_ratio=risk_reward_ratio,
|
|
entry_confirmation=entry_confirmation,
|
|
position_sizing=position_sizing,
|
|
maximum_drawdown=maximum_drawdown,
|
|
max_trades_per_day=max_trades_per_day,
|
|
max_trades_per_week=max_trades_per_week,
|
|
total_risk_per_trade=total_risk_per_trade,
|
|
max_portfolio_risk=max_portfolio_risk,
|
|
adjustments_for_drawdown=adjustments_for_drawdown,
|
|
risk_controls=risk_controls,
|
|
market_conditions=market_conditions,
|
|
indicators_used=indicators_used,
|
|
plan_author=plan_author,
|
|
strategy_version=strategy_version,
|
|
sector_focus=sector_focus,
|
|
fundamental_criteria=fundamental_criteria,
|
|
options_strategy_details=options_strategy_details,
|
|
improvements_needed=improvements_needed
|
|
)
|
|
|
|
save_trading_plan(plan)
|
|
st.success("Trading plan created successfully!")
|
|
st.query_params.update(rerun=True)
|
|
except Exception as e:
|
|
st.error(f"Error creating trading plan: {str(e)}")
|
|
|
|
with edit_tab:
|
|
st.subheader("Edit Trading Plan")
|
|
plans = get_all_trading_plans()
|
|
|
|
if plans:
|
|
selected_plan_id = st.selectbox(
|
|
"Select Plan to Edit",
|
|
options=[plan.id for plan in plans],
|
|
format_func=lambda x: next(p.plan_name for p in plans if p.id == x),
|
|
key="edit_plan_select"
|
|
)
|
|
|
|
if selected_plan_id:
|
|
plan = get_trading_plan(selected_plan_id)
|
|
if plan:
|
|
# Basic Info
|
|
col1, col2 = st.columns(2)
|
|
with col1:
|
|
plan_name = st.text_input("Plan Name", value=plan.plan_name, key="edit_plan_name")
|
|
status = st.selectbox("Status", [s.value for s in PlanStatus], index=[s.value for s in PlanStatus].index(plan.status.value), key="edit_status")
|
|
timeframe = st.selectbox("Timeframe", [t.value for t in Timeframe], index=[t.value for t in Timeframe].index(plan.timeframe.value), key="edit_timeframe")
|
|
market_focus = st.selectbox("Market Focus", [m.value for m in MarketFocus], index=[m.value for m in MarketFocus].index(plan.market_focus.value), key="edit_market_focus")
|
|
|
|
with col2:
|
|
trade_frequency = st.selectbox("Trade Frequency", [f.value for f in TradeFrequency], index=[f.value for f in TradeFrequency].index(plan.trade_frequency.value), key="edit_trade_frequency")
|
|
plan_author = st.text_input("Author", value=plan.plan_author, key="edit_plan_author")
|
|
strategy_version = st.number_input("Version", min_value=1, value=plan.strategy_version, key="edit_strategy_version")
|
|
|
|
# Risk Parameters
|
|
st.subheader("Risk Parameters")
|
|
col1, col2 = st.columns(2)
|
|
with col1:
|
|
stop_loss = st.number_input("Stop Loss %", min_value=0.1, value=plan.stop_loss, key="edit_stop_loss")
|
|
profit_target = st.number_input("Profit Target %", min_value=0.1, value=plan.profit_target, key="edit_profit_target")
|
|
risk_reward_ratio = profit_target / stop_loss if stop_loss > 0 else 0
|
|
st.write(f"Risk:Reward Ratio: {risk_reward_ratio:.2f}")
|
|
|
|
with col2:
|
|
position_sizing = st.number_input("Position Size %", min_value=0.1, value=plan.position_sizing, key="edit_position_sizing")
|
|
total_risk_per_trade = st.number_input("Risk per Trade %", min_value=0.1, value=plan.total_risk_per_trade, key="edit_total_risk_per_trade")
|
|
max_portfolio_risk = st.number_input("Max Portfolio Risk %", min_value=0.1, value=plan.max_portfolio_risk, key="edit_max_portfolio_risk")
|
|
|
|
# Trade Rules
|
|
st.subheader("Trade Rules")
|
|
col1, col2 = st.columns(2)
|
|
with col1:
|
|
max_trades_per_day = st.number_input("Max Trades per Day", min_value=1, value=plan.max_trades_per_day, key="edit_max_trades_per_day")
|
|
max_trades_per_week = st.number_input("Max Trades per Week", min_value=1, value=plan.max_trades_per_week, key="edit_max_trades_per_week")
|
|
maximum_drawdown = st.number_input("Maximum Drawdown %", min_value=0.1, value=plan.maximum_drawdown, key="edit_maximum_drawdown")
|
|
|
|
# Strategy Details
|
|
st.subheader("Strategy Details")
|
|
entry_criteria = st.text_area("Entry Criteria", value=plan.entry_criteria, key="edit_entry_criteria")
|
|
exit_criteria = st.text_area("Exit Criteria", value=plan.exit_criteria, key="edit_exit_criteria")
|
|
entry_confirmation = st.text_area("Entry Confirmation", value=plan.entry_confirmation, key="edit_entry_confirmation")
|
|
market_conditions = st.text_area("Market Conditions", value=plan.market_conditions, key="edit_market_conditions")
|
|
indicators_used = st.text_area("Technical Indicators", value=plan.indicators_used, key="edit_indicators_used")
|
|
|
|
# Risk Management
|
|
st.subheader("Risk Management")
|
|
adjustments_for_drawdown = st.text_area("Drawdown Adjustments", value=plan.adjustments_for_drawdown, key="edit_adjustments_for_drawdown")
|
|
risk_controls = st.text_area("Risk Controls", value=plan.risk_controls, key="edit_risk_controls")
|
|
|
|
# Optional Fields
|
|
st.subheader("Additional Information")
|
|
col1, col2 = st.columns(2)
|
|
with col1:
|
|
sector_focus = st.text_input("Sector Focus (optional)", value=plan.sector_focus, key="edit_sector_focus")
|
|
fundamental_criteria = st.text_area("Fundamental Criteria (optional)", value=plan.fundamental_criteria, key="edit_fundamental_criteria")
|
|
|
|
with col2:
|
|
options_strategy_details = st.text_area("Options Strategy Details (optional)", value=plan.options_strategy_details, key="edit_options_strategy_details")
|
|
improvements_needed = st.text_area("Improvements Needed (optional)", value=plan.improvements_needed, key="edit_improvements_needed")
|
|
|
|
if st.button("Update Plan", key="update_plan_button"):
|
|
try:
|
|
# Update the plan with new values
|
|
plan.plan_name = plan_name
|
|
plan.status = PlanStatus(status)
|
|
plan.timeframe = Timeframe(timeframe)
|
|
plan.market_focus = MarketFocus(market_focus)
|
|
plan.trade_frequency = TradeFrequency(trade_frequency)
|
|
plan.plan_author = plan_author
|
|
plan.strategy_version = strategy_version
|
|
plan.stop_loss = stop_loss
|
|
plan.profit_target = profit_target
|
|
plan.position_sizing = position_sizing
|
|
plan.total_risk_per_trade = total_risk_per_trade
|
|
plan.max_portfolio_risk = max_portfolio_risk
|
|
plan.max_trades_per_day = max_trades_per_day
|
|
plan.max_trades_per_week = max_trades_per_week
|
|
plan.maximum_drawdown = maximum_drawdown
|
|
plan.entry_criteria = entry_criteria
|
|
plan.exit_criteria = exit_criteria
|
|
plan.entry_confirmation = entry_confirmation
|
|
plan.market_conditions = market_conditions
|
|
plan.indicators_used = indicators_used
|
|
plan.adjustments_for_drawdown = adjustments_for_drawdown
|
|
plan.risk_controls = risk_controls
|
|
plan.sector_focus = sector_focus
|
|
plan.fundamental_criteria = fundamental_criteria
|
|
plan.options_strategy_details = options_strategy_details
|
|
plan.improvements_needed = improvements_needed
|
|
|
|
update_trading_plan(plan)
|
|
st.success("Plan updated successfully!")
|
|
st.query_params.update(rerun=True)
|
|
except Exception as e:
|
|
st.error(f"Error updating plan: {str(e)}")
|
|
|
|
# Delete button (at the same level as the update button)
|
|
if st.button("Delete Plan", key="delete_plan_button"):
|
|
try:
|
|
delete_trading_plan(plan.id)
|
|
st.success("Plan deleted successfully!")
|
|
st.query_params.update(rerun=True)
|
|
except Exception as e:
|
|
st.error(f"Error deleting plan: {str(e)}")
|
|
|
|
# Add Trade Management section
|
|
st.subheader("Trade Management")
|
|
|
|
# Get current trades for this plan
|
|
plan_trades = get_plan_trades(plan.id)
|
|
|
|
# Display current trades
|
|
if plan_trades:
|
|
st.write("Current Trades:")
|
|
for trade in plan_trades:
|
|
with st.expander(f"{trade['ticker']} - {trade['entry_date']}"):
|
|
col1, col2 = st.columns(2)
|
|
with col1:
|
|
st.write(f"Entry: ${trade['entry_price']:.2f}")
|
|
st.write(f"Shares: {trade['shares']}")
|
|
with col2:
|
|
if trade['exit_price']:
|
|
pl = (trade['exit_price'] - trade['entry_price']) * trade['shares']
|
|
st.write(f"Exit: ${trade['exit_price']:.2f}")
|
|
st.write(f"P/L: ${pl:.2f}")
|
|
|
|
# Add unlink button for each trade
|
|
if st.button("Unlink Trade", key=f"unlink_trade_{trade['id']}"):
|
|
try:
|
|
# Update the single trade
|
|
query = """
|
|
ALTER TABLE stock_db.trades
|
|
UPDATE plan_id = NULL
|
|
WHERE id = %(trade_id)s
|
|
"""
|
|
with create_client() as client:
|
|
client.command(query, {'trade_id': trade['id']})
|
|
|
|
# Recalculate metrics
|
|
metrics = calculate_plan_metrics(plan.id)
|
|
plan.win_rate = metrics['win_rate']
|
|
plan.average_return_per_trade = metrics['average_return']
|
|
plan.profit_factor = metrics['profit_factor']
|
|
update_trading_plan(plan)
|
|
|
|
st.success(f"Trade unlinked successfully!")
|
|
st.query_params.update(rerun=True)
|
|
except Exception as e:
|
|
st.error(f"Error unlinking trade: {str(e)}")
|
|
|
|
# Add button to unlink all trades
|
|
if st.button("Unlink All Trades", key=f"unlink_all_trades_{plan.id}"):
|
|
try:
|
|
if unlink_trades_from_plan(plan.id):
|
|
# Reset metrics
|
|
plan.win_rate = None
|
|
plan.average_return_per_trade = None
|
|
plan.profit_factor = None
|
|
update_trading_plan(plan)
|
|
|
|
st.success("All trades unlinked successfully!")
|
|
st.query_params.update(rerun=True)
|
|
else:
|
|
st.error("Error unlinking trades")
|
|
except Exception as e:
|
|
st.error(f"Error unlinking trades: {str(e)}")
|
|
|
|
# Get available trades
|
|
with create_client() as client:
|
|
query = """
|
|
SELECT
|
|
id,
|
|
ticker,
|
|
entry_date,
|
|
entry_price,
|
|
shares,
|
|
exit_price,
|
|
exit_date,
|
|
direction,
|
|
strategy,
|
|
CASE
|
|
WHEN exit_price IS NOT NULL
|
|
THEN (exit_price - entry_price) * shares
|
|
ELSE NULL
|
|
END as profit_loss
|
|
FROM stock_db.trades
|
|
WHERE plan_id IS NULL
|
|
ORDER BY entry_date DESC
|
|
"""
|
|
result = client.query(query)
|
|
available_trades = [dict(zip(
|
|
['id', 'ticker', 'entry_date', 'entry_price', 'shares',
|
|
'exit_price', 'exit_date', 'direction', 'strategy', 'profit_loss'],
|
|
row
|
|
)) for row in result.result_rows]
|
|
|
|
if available_trades:
|
|
st.write("Link Existing Trades:")
|
|
selected_trades = st.multiselect(
|
|
"Select trades to link to this plan",
|
|
options=[t['id'] for t in available_trades],
|
|
format_func=lambda x: next(
|
|
f"{t['ticker']} - {t['entry_date']} - ${t['entry_price']:.2f} "
|
|
f"({t['direction']}) - {t['strategy']} "
|
|
f"{'[Closed]' if t['exit_price'] else '[Open]'} "
|
|
f"{'P/L: $' + format(t['profit_loss'], '.2f') if t['profit_loss'] is not None else ''}"
|
|
for t in available_trades if t['id'] == x
|
|
),
|
|
key=f"link_trades_{plan.id}"
|
|
)
|
|
|
|
if selected_trades and st.button("Link Selected Trades", key=f"link_trades_button_{plan.id}"):
|
|
if link_trades_to_plan(plan.id, selected_trades):
|
|
st.success("Trades linked successfully!")
|
|
|
|
# Calculate and update metrics
|
|
metrics = calculate_plan_metrics(plan.id)
|
|
plan.win_rate = metrics['win_rate']
|
|
plan.average_return_per_trade = metrics['average_return']
|
|
plan.profit_factor = metrics['profit_factor']
|
|
update_trading_plan(plan)
|
|
|
|
st.query_params.update(rerun=True)
|
|
else:
|
|
st.error("Error linking trades")
|
|
else:
|
|
st.info("No plans available to edit")
|
|
|
|
def main():
|
|
st.set_page_config(page_title="Trading System", layout="wide")
|
|
init_session_state()
|
|
|
|
# Sidebar navigation
|
|
st.sidebar.title("Navigation")
|
|
st.session_state.page = st.sidebar.radio(
|
|
"Go to",
|
|
["Strategy Guide", "Trading Journal", "Technical Scanner", "CANSLIM Screener", "Trading System", "Trading Plans"]
|
|
)
|
|
|
|
# Create necessary tables
|
|
create_trades_table()
|
|
create_trading_plan_table()
|
|
|
|
# Display selected page
|
|
if st.session_state.page == "Strategy Guide":
|
|
strategy_guide_page()
|
|
elif st.session_state.page == "Trading Journal":
|
|
trading_journal_page()
|
|
elif st.session_state.page == "Technical Scanner":
|
|
technical_scanner_page()
|
|
elif st.session_state.page == "CANSLIM Screener":
|
|
canslim_screener_page()
|
|
elif st.session_state.page == "Trading System":
|
|
trading_system_page()
|
|
elif st.session_state.page == "Trading Plans":
|
|
trading_plan_page()
|
|
|
|
if __name__ == "__main__":
|
|
main()
|