Falcao Zane Vijay commited on
Commit
9b62b05
·
1 Parent(s): b5071fe
Files changed (1) hide show
  1. src/streamlit_app.py +806 -35
src/streamlit_app.py CHANGED
@@ -1,37 +1,808 @@
1
  import streamlit as st
 
 
 
 
 
 
 
 
 
 
 
2
 
3
- """
4
- # Welcome to Streamlit!
5
-
6
- Edit `/streamlit_app.py` to customize this app to your heart's desire :heart:.
7
- If you have any questions, checkout our [documentation](https://docs.streamlit.io) and [community
8
- forums](https://discuss.streamlit.io).
9
-
10
- In the meantime, below is an example of what you can do with just a few lines of code:
11
- """
12
-
13
- num_points = st.slider("Number of points in spiral", 1, 10000, 1100)
14
- num_turns = st.slider("Number of turns in spiral", 1, 300, 31)
15
-
16
- indices = np.linspace(0, 1, num_points)
17
- theta = 2 * np.pi * num_turns * indices
18
- radius = indices
19
-
20
- x = radius * np.cos(theta)
21
- y = radius * np.sin(theta)
22
-
23
- df = pd.DataFrame({
24
- "x": x,
25
- "y": y,
26
- "idx": indices,
27
- "rand": np.random.randn(num_points),
28
- })
29
-
30
- st.altair_chart(alt.Chart(df, height=700, width=700)
31
- .mark_point(filled=True)
32
- .encode(
33
- x=alt.X("x", axis=None),
34
- y=alt.Y("y", axis=None),
35
- color=alt.Color("idx", legend=None, scale=alt.Scale()),
36
- size=alt.Size("rand", legend=None, scale=alt.Scale(range=[1, 150])),
37
- ))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import streamlit as st
2
+ import pandas as pd
3
+ import numpy as np
4
+ import plotly.graph_objects as go
5
+ import plotly.express as px
6
+ from plotly.subplots import make_subplots
7
+ import yfinance as yf
8
+ import pickle
9
+ from datetime import datetime, timedelta
10
+ import warnings
11
+ from curl_cffi import requests
12
+ session = requests.Session(impersonate="chrome")
13
 
14
+ from indicators.rsi import rsi
15
+ from indicators.sma import sma
16
+ from indicators.ema import ema
17
+ from indicators.macd import macd
18
+
19
+ from strategy.rule_based_strategy import generate_signals_sma, generate_signals_ema
20
+ from utils.backtester import backtest_signals
21
+
22
+ from indicators.enhanced_features import (
23
+ create_volatility_features, create_enhanced_lag_features,
24
+ create_volume_features, create_momentum_features, create_position_features
25
+ )
26
+
27
+ # Suppress warnings
28
+ warnings.filterwarnings('ignore')
29
+
30
+ # Page config
31
+ st.set_page_config(
32
+ page_title="Complete Stock Trading & Prediction Platform",
33
+ page_icon="📈",
34
+ layout="wide"
35
+ )
36
+
37
+ # Stock symbols
38
+ STOCK_SYMBOLS = [
39
+ 'ADANIENT.NS', 'ADANIPORTS.NS', 'APOLLOHOSP.NS', 'ASIANPAINT.NS',
40
+ 'AXISBANK.NS', 'BAJAJ-AUTO.NS', 'BAJFINANCE.NS', 'BAJAJFINSV.NS',
41
+ 'BEL.NS', 'BHARTIARTL.NS', 'CIPLA.NS', 'COALINDIA.NS', 'DRREDDY.NS',
42
+ 'EICHERMOT.NS', 'GRASIM.NS', 'HCLTECH.NS', 'HDFCBANK.NS', 'HDFCLIFE.NS',
43
+ 'HEROMOTOCO.NS', 'HINDALCO.NS', 'HINDUNILVR.NS', 'ICICIBANK.NS',
44
+ 'INDUSINDBK.NS', 'INFY.NS', 'ITC.NS', 'JIOFIN.NS', 'JSWSTEEL.NS',
45
+ 'KOTAKBANK.NS', 'LT.NS', 'M&M.NS', 'MARUTI.NS', 'NESTLEIND.NS',
46
+ 'NTPC.NS', 'ONGC.NS', 'POWERGRID.NS', 'RELIANCE.NS', 'SBILIFE.NS',
47
+ 'SHRIRAMFIN.NS', 'SBIN.NS', 'SUNPHARMA.NS', 'TATACONSUM.NS', 'TCS.NS',
48
+ 'TATAMOTORS.NS', 'TATASTEEL.NS', 'TECHM.NS', 'TITAN.NS', 'TRENT.NS',
49
+ 'ULTRACEMCO.NS', 'WIPRO.NS', 'ETERNAL.NS'
50
+ ]
51
+
52
+ # Feature list for ML model
53
+ FEATURES = [
54
+ 'Close', 'Volume', 'SMA20', 'SMA50', 'EMA20', 'EMA50',
55
+ 'RSI14', 'MACD', 'MACD_signal', 'MACD_hist',
56
+ 'SMA_crossover', 'RSI_oversold',
57
+ 'return_1d', 'volatility_5d', 'volatility_10d', 'volatility_20d',
58
+ 'volatility_30d', 'vol_ratio_5_20', 'vol_ratio_10_20', 'vol_rank_20',
59
+ 'vol_rank_50', 'return_lag_1', 'return_lag_2', 'return_lag_3',
60
+ 'return_lag_5', 'return_lag_10', 'rsi_lag_1', 'macd_lag_1', 'rsi_lag_2',
61
+ 'macd_lag_2', 'rsi_lag_3', 'macd_lag_3', 'volume_sma_10',
62
+ 'volume_sma_20', 'volume_sma_50', 'volume_ratio_10', 'volume_ratio_20',
63
+ 'volume_ratio_50', 'price_volume', 'pv_sma_5', 'volume_momentum_5',
64
+ 'momentum_3d', 'momentum_5d', 'momentum_10d', 'momentum_20d', 'roc_5d',
65
+ 'roc_10d', 'high_10d', 'low_10d', 'price_position_10', 'high_20d',
66
+ 'low_20d', 'price_position_20', 'high_50d', 'low_50d',
67
+ 'price_position_50', 'bb_upper', 'bb_lower', 'bb_position', 'target'
68
+ ]
69
+
70
+ # ========================= SHARED FUNCTIONS =========================
71
+
72
+ @st.cache_data
73
+ def load_stock_data(symbol, start_date, end_date):
74
+ """Load stock data from Yahoo Finance"""
75
+ try:
76
+ data = yf.download(symbol, start=start_date, end=end_date, session=session)
77
+ # Flatten the MultiIndex columns
78
+ if data.columns.nlevels > 1:
79
+ data.columns = [col[0] for col in data.columns]
80
+ return data
81
+ except Exception as e:
82
+ st.error(f"Error loading data: {e}")
83
+ return None
84
+
85
+ def process_stock_data(df, short_period, long_period, rsi_period):
86
+ """Process stock data to create all features"""
87
+ df = df.copy()
88
+
89
+ # Basic technical indicators
90
+ df['SMA20'] = sma(df, short_period)
91
+ df['SMA50'] = sma(df, long_period)
92
+ df['EMA20'] = ema(df, short_period)
93
+ df['EMA50'] = ema(df, long_period)
94
+ df['RSI14'] = rsi(df, rsi_period)
95
+ df['RSI20'] = rsi(df, rsi_period + 6)
96
+ df['MACD'], df['MACD_signal'], df['MACD_hist'] = macd(df)
97
+
98
+ # Bollinger Bands
99
+ df['Upper_Band'] = df['SMA20'] + 2 * df['Close'].rolling(window=20).std()
100
+ df['Lower_Band'] = df['SMA20'] - 2 * df['Close'].rolling(window=20).std()
101
+
102
+ # Create feature sets
103
+ df = create_volatility_features(df)
104
+ df = create_enhanced_lag_features(df)
105
+ df = create_volume_features(df)
106
+ df = create_momentum_features(df)
107
+ df = create_position_features(df)
108
+
109
+ # Additional features
110
+ df['SMA_crossover'] = (df['SMA20'] > df['SMA50']).astype(int)
111
+ df['RSI_oversold'] = (df['RSI14'] < 30).astype(int)
112
+
113
+ # Target: next-day up/down
114
+ df['next_close'] = df['Close'].shift(-1)
115
+ df['target'] = (df['next_close'] > df['Close']).astype(int)
116
+
117
+ return df
118
+
119
+ # ========================= MAIN APPLICATION =========================
120
+
121
+ # Main navigation
122
+ st.title("📈 Stock Trading & Prediction Platform")
123
+
124
+ # Navigation tabs
125
+ tab1, tab2 = st.tabs(["🔮 Price Prediction", "📊 Trading Dashboard"])
126
+
127
+ # ========================= SIDEBAR CONFIGURATION =========================
128
+
129
+ st.sidebar.header("📊 Configuration")
130
+
131
+ # Common inputs
132
+ selected_stock = st.sidebar.selectbox("Select Stock Symbol", STOCK_SYMBOLS, index=35)
133
+ start_date = st.sidebar.date_input("Start Date", value=datetime(2020, 1, 1))
134
+ end_date = st.sidebar.date_input("End Date", value=datetime.now())
135
+
136
+ st.sidebar.subheader("📈 Technical Indicators")
137
+ rsi_period = st.sidebar.slider("RSI Period", min_value=5, max_value=30, value=14, step=1)
138
+ short_period = st.sidebar.slider("Short-term Period", min_value=5, max_value=50, value=20, step=1)
139
+ long_period = st.sidebar.slider("Long-term Period", min_value=50, max_value=200, value=50, step=1)
140
+
141
+ # Strategy selection (for trading dashboard)
142
+ strategy_type = st.sidebar.selectbox("Strategy Type", ["SMA-based", "EMA-based", "Both"])
143
+
144
+ st.sidebar.subheader("💰 Backtesting Parameters")
145
+ initial_cash = st.sidebar.number_input("Initial Capital (₹)", min_value=10000, value=100000, step=10000)
146
+ transaction_cost = st.sidebar.slider("Transaction Cost (%)", 0.0, 1.0, 0.1, step=0.05) / 100
147
+ stop_loss = st.sidebar.slider("Stop Loss (%)", 0.0, 20.0, 5.0, step=1.0) / 100
148
+ take_profit = st.sidebar.slider("Take Profit (%)", 0.0, 50.0, 15.0, step=5.0) / 100
149
+ use_risk_mgmt = st.sidebar.checkbox("Enable Risk Management", value=True)
150
+
151
+ # ========================= PRICE PREDICTION TAB =========================
152
+
153
+ with tab1:
154
+ st.header(f"🔮 Price Prediction for {selected_stock}")
155
+
156
+ with st.spinner("Loading stock data..."):
157
+ stock_data = load_stock_data(selected_stock, start_date, end_date)
158
+
159
+ if stock_data is not None and not stock_data.empty:
160
+ # Display sample data
161
+ st.subheader("📊 Latest Stock Data")
162
+ st.dataframe(stock_data.tail(10), use_container_width=True)
163
+
164
+ # Process the data
165
+ processed_data = process_stock_data(stock_data, short_period, long_period, rsi_period)
166
+ processed_data = processed_data.dropna()
167
+
168
+ if len(processed_data) > 0:
169
+ # Get the latest row for prediction
170
+ latest_data = processed_data.iloc[-1]
171
+
172
+ # Display current stock info
173
+ col1, col2, col3, col4 = st.columns(4)
174
+ with col1:
175
+ st.metric("Current Price", f"₹{latest_data['Close']:.2f}")
176
+ with col2:
177
+ daily_change = ((latest_data['Close'] - processed_data.iloc[-2]['Close']) / processed_data.iloc[-2]['Close']) * 100
178
+ st.metric("Daily Change", f"{daily_change:.2f}%")
179
+ with col3:
180
+ st.metric("Volume", f"{latest_data['Volume']:,.0f}")
181
+ with col4:
182
+ st.metric("RSI14", f"{latest_data['RSI14']:.2f}")
183
+
184
+
185
+ model = pickle.load(open('models/logistic_regression_model.pkl', 'rb'))
186
+ scaler = pickle.load(open('models/scaler.pkl', 'rb'))
187
+
188
+ # Create feature vector
189
+ feature_vector = latest_data[FEATURES].values.reshape(1, -1)
190
+ feature_vector_scaled = scaler.transform(feature_vector)
191
+
192
+ # Make prediction
193
+ prediction = model.predict(feature_vector_scaled)[0]
194
+ probability = model.predict_proba(feature_vector_scaled)[0].max()
195
+
196
+
197
+ # Display prediction
198
+ st.header("🔮 Prediction Results")
199
+ col1, col2 = st.columns(2)
200
+
201
+ with col1:
202
+ if prediction == 1:
203
+ st.success("📈 **PREDICTION: UP**")
204
+ st.write(f"The model predicts the stock will go **UP** tomorrow with {probability:.1%} confidence.")
205
+ else:
206
+ st.error("📉 **PREDICTION: DOWN**")
207
+ st.write(f"The model predicts the stock will go **DOWN** tomorrow with {probability:.1%} confidence.")
208
+
209
+ with col2:
210
+ # Confidence gauge
211
+ fig_gauge = go.Figure(go.Indicator(
212
+ mode = "gauge+number",
213
+ value = probability * 100,
214
+ domain = {'x': [0, 1], 'y': [0, 1]},
215
+ title = {'text': "Confidence %"},
216
+ gauge = {
217
+ 'axis': {'range': [None, 100]},
218
+ 'bar': {'color': "darkgreen" if prediction == 1 else "darkred"},
219
+ 'steps': [
220
+ {'range': [0, 50], 'color': "lightgray"},
221
+ {'range': [50, 80], 'color': "yellow"},
222
+ {'range': [80, 100], 'color': "lightgreen"}
223
+ ],
224
+ 'threshold': {
225
+ 'line': {'color': "red", 'width': 4},
226
+ 'thickness': 0.75,
227
+ 'value': 90
228
+ }
229
+ }
230
+ ))
231
+ fig_gauge.update_layout(height=300)
232
+ st.plotly_chart(fig_gauge, use_container_width=True)
233
+
234
+ # Technical Analysis Charts
235
+ st.header("📈 Technical Analysis")
236
+
237
+ # Price charts
238
+ col1, col2 = st.columns(2)
239
+
240
+ with col1:
241
+ # SMA Chart
242
+ fig_sma = go.Figure()
243
+ fig_sma.add_trace(go.Scatter(x=processed_data.index[-60:], y=processed_data['Close'][-60:],
244
+ mode='lines', name='Close Price', line=dict(color='blue', width=2)))
245
+ fig_sma.add_trace(go.Scatter(x=processed_data.index[-60:], y=processed_data['SMA20'][-60:],
246
+ mode='lines', name='SMA20', line=dict(color='orange', width=1)))
247
+ fig_sma.add_trace(go.Scatter(x=processed_data.index[-60:], y=processed_data['SMA50'][-60:],
248
+ mode='lines', name='SMA50', line=dict(color='red', width=1)))
249
+ fig_sma.update_layout(title=f"{selected_stock} - Simple Moving Averages", height=400)
250
+ st.plotly_chart(fig_sma, use_container_width=True)
251
+
252
+ with col2:
253
+ # EMA Chart
254
+ fig_ema = go.Figure()
255
+ fig_ema.add_trace(go.Scatter(x=processed_data.index[-60:], y=processed_data['Close'][-60:],
256
+ mode='lines', name='Close Price', line=dict(color='blue', width=2)))
257
+ fig_ema.add_trace(go.Scatter(x=processed_data.index[-60:], y=processed_data['EMA20'][-60:],
258
+ mode='lines', name='EMA20', line=dict(color='orange', width=1)))
259
+ fig_ema.add_trace(go.Scatter(x=processed_data.index[-60:], y=processed_data['EMA50'][-60:],
260
+ mode='lines', name='EMA50', line=dict(color='red', width=1)))
261
+ fig_ema.update_layout(title=f"{selected_stock} - Exponential Moving Averages", height=400)
262
+ st.plotly_chart(fig_ema, use_container_width=True)
263
+
264
+ # RSI and MACD
265
+ col1, col2 = st.columns(2)
266
+
267
+ with col1:
268
+ fig_rsi = go.Figure()
269
+ fig_rsi.add_trace(go.Scatter(x=processed_data.index[-30:], y=processed_data['RSI14'][-30:],
270
+ mode='lines', name='RSI14', line=dict(color='purple')))
271
+ fig_rsi.add_hline(y=70, line_dash="dash", line_color="red", annotation_text="Overbought")
272
+ fig_rsi.add_hline(y=30, line_dash="dash", line_color="green", annotation_text="Oversold")
273
+ fig_rsi.update_layout(title=f"RSI ({rsi_period}-day)", height=300)
274
+ st.plotly_chart(fig_rsi, use_container_width=True)
275
+
276
+ with col2:
277
+ fig_macd = go.Figure()
278
+ fig_macd.add_trace(go.Scatter(x=processed_data.index[-30:], y=processed_data['MACD'][-30:],
279
+ mode='lines', name='MACD', line=dict(color='blue')))
280
+ fig_macd.add_trace(go.Scatter(x=processed_data.index[-30:], y=processed_data['MACD_signal'][-30:],
281
+ mode='lines', name='Signal', line=dict(color='red')))
282
+ fig_macd.update_layout(title="MACD", height=300)
283
+ st.plotly_chart(fig_macd, use_container_width=True)
284
+
285
+ else:
286
+ st.error("Not enough data to make a prediction.")
287
+ else:
288
+ st.error("Unable to load stock data.")
289
+
290
+ # ========================= TRADING DASHBOARD TAB =========================
291
+
292
+ with tab2:
293
+ st.header("📊 Trading Dashboard")
294
+
295
+ with st.spinner(f'Loading data for {selected_stock}...'):
296
+ df = load_stock_data(selected_stock, start_date, end_date)
297
+
298
+ if df is not None and not df.empty:
299
+ st.subheader(f"📊 Stock Data for {selected_stock}")
300
+ st.write(f"**Date Range:** {start_date.strftime('%Y-%m-%d')} to {end_date.strftime('%Y-%m-%d')}")
301
+ st.write(f"**Total Records:** {len(df)} days")
302
+
303
+ # Process data for trading
304
+ df = process_stock_data(df, short_period, long_period, rsi_period)
305
+ df = df.dropna()
306
+
307
+ # Generate trading signals
308
+ if strategy_type in ["SMA-based", "Both"]:
309
+ df = generate_signals_sma(df, rsi_col='RSI14', sma_short_col='SMA20', sma_long_col='SMA50')
310
+
311
+ if strategy_type in ["EMA-based", "Both"]:
312
+ df = generate_signals_ema(df, rsi_col='RSI14', ema_short_col='EMA20', ema_long_col='EMA50')
313
+
314
+ # Initialize variables to avoid NameError
315
+ results = None
316
+ metrics = None
317
+ signal_col = None
318
+ strategy_name = None
319
+
320
+ # Backtesting section
321
+ st.header("🔍 Backtesting Results")
322
+
323
+ if strategy_type == "Both":
324
+ tab_sma, tab_ema = st.tabs(["SMA Strategy", "EMA Strategy"])
325
+
326
+ with tab_sma:
327
+ st.subheader("📊 SMA Strategy Results")
328
+ sma_results, sma_metrics = backtest_signals(
329
+ df, signal_col='SMA_Signal', price_col='Close',
330
+ initial_cash=initial_cash, transaction_cost=transaction_cost if use_risk_mgmt else 0
331
+ )
332
+
333
+ # Set variables for common sections
334
+ results = sma_results
335
+ metrics = sma_metrics
336
+ signal_col = 'SMA_Signal'
337
+ strategy_name = 'SMA'
338
+
339
+ # Display metrics
340
+ col1, col2, col3, col4 = st.columns(4)
341
+ with col1:
342
+ st.metric("💰 Final Value", sma_metrics['Final Portfolio Value'])
343
+ st.metric("📈 Total Return", sma_metrics['Total Return'])
344
+ with col2:
345
+ st.metric("🎯 Buy & Hold Return", sma_metrics['Buy & Hold Return'])
346
+ st.metric("📊 Total Trades", sma_metrics['Total Trades'])
347
+ with col3:
348
+ st.metric("🏆 Win Rate", sma_metrics['Win Rate'])
349
+ st.metric("⚡ Sharpe Ratio", sma_metrics['Sharpe Ratio'])
350
+ with col4:
351
+ st.metric("📉 Max Drawdown", sma_metrics['Maximum Drawdown'])
352
+ st.metric("🔥 Volatility", sma_metrics['Volatility (Annual)'])
353
+
354
+ # SMA Price Chart with Signals
355
+ fig_sma_signals = go.Figure()
356
+ fig_sma_signals.add_trace(go.Scatter(x=df.index, y=df['Close'], mode='lines',
357
+ name='Close Price', line=dict(color='purple', width=2)))
358
+ fig_sma_signals.add_trace(go.Scatter(x=df.index, y=df['SMA20'], mode='lines',
359
+ name='SMA20', line=dict(color='blue', width=2)))
360
+ fig_sma_signals.add_trace(go.Scatter(x=df.index, y=df['SMA50'], mode='lines',
361
+ name='SMA50', line=dict(color='red', width=2)))
362
+
363
+ # Add buy/sell signals
364
+ buy_signals = df[df['SMA_Signal'] == 1]
365
+ sell_signals = df[df['SMA_Signal'] == -1]
366
+
367
+ if not buy_signals.empty:
368
+ fig_sma_signals.add_trace(go.Scatter(x=buy_signals.index, y=buy_signals['Close'],
369
+ mode='markers', name='Buy Signal',
370
+ marker=dict(symbol='triangle-up', size=12, color='green')))
371
+
372
+ if not sell_signals.empty:
373
+ fig_sma_signals.add_trace(go.Scatter(x=sell_signals.index, y=sell_signals['Close'],
374
+ mode='markers', name='Sell Signal',
375
+ marker=dict(symbol='triangle-down', size=12, color='red')))
376
+
377
+ fig_sma_signals.update_layout(title=f"{selected_stock} - SMA Strategy Signals", height=500)
378
+ st.plotly_chart(fig_sma_signals, use_container_width=True)
379
+
380
+ # Portfolio Performance
381
+ buy_hold_value = initial_cash * (df['Close'] / df['Close'].iloc[0])
382
+ fig_perf_sma = go.Figure()
383
+ fig_perf_sma.add_trace(go.Scatter(x=sma_results.index, y=sma_results['Total'],
384
+ mode='lines', name='SMA Strategy', line=dict(color='green', width=3)))
385
+ fig_perf_sma.add_trace(go.Scatter(x=df.index, y=buy_hold_value,
386
+ mode='lines', name='Buy & Hold', line=dict(color='blue', width=2, dash='dash')))
387
+ fig_perf_sma.update_layout(title="SMA Strategy vs Buy & Hold Performance", height=400)
388
+ st.plotly_chart(fig_perf_sma, use_container_width=True)
389
+
390
+ with tab_ema:
391
+ st.subheader("📊 EMA Strategy Results")
392
+ ema_results, ema_metrics = backtest_signals(
393
+ df, signal_col='EMA_Signal', price_col='Close',
394
+ initial_cash=initial_cash, transaction_cost=transaction_cost if use_risk_mgmt else 0
395
+ )
396
+
397
+ # Set variables for common sections
398
+ results = ema_results
399
+ metrics = ema_metrics
400
+ signal_col = 'EMA_Signal'
401
+ strategy_name = 'EMA'
402
+
403
+ # Display metrics
404
+ col1, col2, col3, col4 = st.columns(4)
405
+ with col1:
406
+ st.metric("💰 Final Value", ema_metrics['Final Portfolio Value'])
407
+ st.metric("📈 Total Return", ema_metrics['Total Return'])
408
+ with col2:
409
+ st.metric("🎯 Buy & Hold Return", ema_metrics['Buy & Hold Return'])
410
+ st.metric("📊 Total Trades", ema_metrics['Total Trades'])
411
+ with col3:
412
+ st.metric("🏆 Win Rate", ema_metrics['Win Rate'])
413
+ st.metric("⚡ Sharpe Ratio", ema_metrics['Sharpe Ratio'])
414
+ with col4:
415
+ st.metric("📉 Max Drawdown", ema_metrics['Maximum Drawdown'])
416
+ st.metric("🔥 Volatility", ema_metrics['Volatility (Annual)'])
417
+
418
+ # EMA Price Chart with Signals
419
+ fig_ema_signals = go.Figure()
420
+ fig_ema_signals.add_trace(go.Scatter(x=df.index, y=df['Close'], mode='lines',
421
+ name='Close Price', line=dict(color='purple', width=2)))
422
+ fig_ema_signals.add_trace(go.Scatter(x=df.index, y=df['EMA20'], mode='lines',
423
+ name='EMA20', line=dict(color='blue', width=2)))
424
+ fig_ema_signals.add_trace(go.Scatter(x=df.index, y=df['EMA50'], mode='lines',
425
+ name='EMA50', line=dict(color='red', width=2)))
426
+
427
+ # Add buy/sell signals
428
+ buy_signals = df[df['EMA_Signal'] == 1]
429
+ sell_signals = df[df['EMA_Signal'] == -1]
430
+
431
+ if not buy_signals.empty:
432
+ fig_ema_signals.add_trace(go.Scatter(x=buy_signals.index, y=buy_signals['Close'],
433
+ mode='markers', name='Buy Signal',
434
+ marker=dict(symbol='triangle-up', size=12, color='green')))
435
+
436
+ if not sell_signals.empty:
437
+ fig_ema_signals.add_trace(go.Scatter(x=sell_signals.index, y=sell_signals['Close'],
438
+ mode='markers', name='Sell Signal',
439
+ marker=dict(symbol='triangle-down', size=12, color='red')))
440
+
441
+ fig_ema_signals.update_layout(title=f"{selected_stock} - EMA Strategy Signals", height=500)
442
+ st.plotly_chart(fig_ema_signals, use_container_width=True)
443
+
444
+ # Portfolio Performance
445
+ buy_hold_value = initial_cash * (df['Close'] / df['Close'].iloc[0])
446
+ fig_perf_ema = go.Figure()
447
+ fig_perf_ema.add_trace(go.Scatter(x=ema_results.index, y=ema_results['Total'],
448
+ mode='lines', name='EMA Strategy', line=dict(color='green', width=3)))
449
+ fig_perf_ema.add_trace(go.Scatter(x=df.index, y=buy_hold_value,
450
+ mode='lines', name='Buy & Hold', line=dict(color='blue', width=2, dash='dash')))
451
+ fig_perf_ema.update_layout(title="EMA Strategy vs Buy & Hold Performance", height=400)
452
+ st.plotly_chart(fig_perf_ema, use_container_width=True)
453
+
454
+ else:
455
+ # Single strategy
456
+ signal_col = 'SMA_Signal' if strategy_type == "SMA-based" else 'EMA_Signal'
457
+ strategy_name = strategy_type.split('-')[0]
458
+
459
+ results, metrics = backtest_signals(
460
+ df, signal_col=signal_col, price_col='Close',
461
+ initial_cash=initial_cash, transaction_cost=transaction_cost if use_risk_mgmt else 0
462
+ )
463
+
464
+ # Display metrics
465
+ col1, col2, col3, col4 = st.columns(4)
466
+ with col1:
467
+ st.metric("💰 Final Value", metrics['Final Portfolio Value'])
468
+ st.metric("📈 Total Return", metrics['Total Return'])
469
+ with col2:
470
+ st.metric("🎯 Buy & Hold Return", metrics['Buy & Hold Return'])
471
+ st.metric("📊 Total Trades", metrics['Total Trades'])
472
+ with col3:
473
+ st.metric("🏆 Win Rate", metrics['Win Rate'])
474
+ st.metric("⚡ Sharpe Ratio", metrics['Sharpe Ratio'])
475
+ with col4:
476
+ st.metric("📉 Max Drawdown", metrics['Maximum Drawdown'])
477
+ st.metric("🔥 Volatility", metrics['Volatility (Annual)'])
478
+
479
+ # Price Chart with Signals
480
+ fig_signals = go.Figure()
481
+ fig_signals.add_trace(go.Scatter(x=df.index, y=df['Close'], mode='lines',
482
+ name='Close Price', line=dict(color='purple', width=2)))
483
+
484
+ if strategy_name == 'SMA':
485
+ fig_signals.add_trace(go.Scatter(x=df.index, y=df['SMA20'], mode='lines',
486
+ name='SMA20', line=dict(color='blue', width=2)))
487
+ fig_signals.add_trace(go.Scatter(x=df.index, y=df['SMA50'], mode='lines',
488
+ name='SMA50', line=dict(color='red', width=2)))
489
+ else:
490
+ fig_signals.add_trace(go.Scatter(x=df.index, y=df['EMA20'], mode='lines',
491
+ name='EMA20', line=dict(color='blue', width=2)))
492
+ fig_signals.add_trace(go.Scatter(x=df.index, y=df['EMA50'], mode='lines',
493
+ name='EMA50', line=dict(color='red', width=2)))
494
+
495
+ # Add buy/sell signals
496
+ buy_signals = df[df[signal_col] == 1]
497
+ sell_signals = df[df[signal_col] == -1]
498
+
499
+ if not buy_signals.empty:
500
+ fig_signals.add_trace(go.Scatter(x=buy_signals.index, y=buy_signals['Close'],
501
+ mode='markers', name='Buy Signal',
502
+ marker=dict(symbol='triangle-up', size=12, color='green')))
503
+
504
+ if not sell_signals.empty:
505
+ fig_signals.add_trace(go.Scatter(x=sell_signals.index, y=sell_signals['Close'],
506
+ mode='markers', name='Sell Signal',
507
+ marker=dict(symbol='triangle-down', size=12, color='red')))
508
+
509
+ fig_signals.update_layout(title=f"{selected_stock} - {strategy_name} Strategy Signals", height=500)
510
+ st.plotly_chart(fig_signals, use_container_width=True)
511
+
512
+ # Portfolio Performance
513
+ buy_hold_value = initial_cash * (df['Close'] / df['Close'].iloc[0])
514
+ fig_perf = go.Figure()
515
+ fig_perf.add_trace(go.Scatter(x=results.index, y=results['Total'],
516
+ mode='lines', name=f'{strategy_name} Strategy', line=dict(color='green', width=3)))
517
+ fig_perf.add_trace(go.Scatter(x=df.index, y=buy_hold_value,
518
+ mode='lines', name='Buy & Hold', line=dict(color='blue', width=2, dash='dash')))
519
+ fig_perf.update_layout(title=f"{strategy_name} Strategy vs Buy & Hold Performance", height=400)
520
+ st.plotly_chart(fig_perf, use_container_width=True)
521
+
522
+ # Additional Technical Analysis Charts (only show if we have results)
523
+ if results is not None:
524
+ st.header("📈 Additional Technical Analysis")
525
+
526
+ col1, col2 = st.columns(2)
527
+
528
+ with col1:
529
+ # RSI Chart
530
+ fig_rsi = go.Figure()
531
+ fig_rsi.add_trace(go.Scatter(x=df.index, y=df['RSI14'], mode='lines',
532
+ name='RSI14', line=dict(color='purple', width=2)))
533
+
534
+ # Add buy/sell signals on RSI if available
535
+ if not buy_signals.empty:
536
+ fig_rsi.add_trace(go.Scatter(x=buy_signals.index, y=buy_signals['RSI14'],
537
+ mode='markers', name='Buy Signal',
538
+ marker=dict(symbol='triangle-up', size=10, color='green'),
539
+ showlegend=False))
540
+
541
+ if not sell_signals.empty:
542
+ fig_rsi.add_trace(go.Scatter(x=sell_signals.index, y=sell_signals['RSI14'],
543
+ mode='markers', name='Sell Signal',
544
+ marker=dict(symbol='triangle-down', size=10, color='red'),
545
+ showlegend=False))
546
+
547
+ fig_rsi.add_hline(y=70, line_dash="dash", line_color="red", annotation_text="Overbought (70)")
548
+ fig_rsi.add_hline(y=30, line_dash="dash", line_color="green", annotation_text="Oversold (30)")
549
+ fig_rsi.add_hline(y=50, line_dash="solid", line_color="gray", annotation_text="Midline (50)", opacity=0.5)
550
+
551
+ fig_rsi.update_layout(title="RSI with Trading Signals", yaxis=dict(range=[0, 100]), height=400)
552
+ st.plotly_chart(fig_rsi, use_container_width=True)
553
+
554
+ with col2:
555
+ # MACD Chart
556
+ fig_macd = make_subplots(rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.05, row_heights=[0.7, 0.3])
557
+
558
+ # MACD line
559
+ fig_macd.add_trace(go.Scatter(x=df.index, y=df['MACD'], mode='lines', name='MACD',
560
+ line=dict(color='blue', width=2)), row=1, col=1)
561
+
562
+ # Signal line
563
+ fig_macd.add_trace(go.Scatter(x=df.index, y=df['MACD_signal'], mode='lines', name='Signal Line',
564
+ line=dict(color='orange', width=2)), row=1, col=1)
565
+
566
+ # Zero line
567
+ fig_macd.add_hline(y=0, line_dash="solid", line_color="pink", opacity=0.5, row=1, col=1)
568
+
569
+ # MACD histogram
570
+ colors = ['green' if val >= 0 else 'red' for val in df['MACD_hist']]
571
+ fig_macd.add_trace(go.Bar(x=df.index, y=df['MACD_hist'], name='MACD Histogram',
572
+ marker_color=colors, opacity=0.6), row=2, col=1)
573
+
574
+ fig_macd.update_layout(title="MACD Indicator", height=400, showlegend=True)
575
+ fig_macd.update_xaxes(title_text="Date", row=2, col=1)
576
+ fig_macd.update_yaxes(title_text="MACD Value", row=1, col=1)
577
+ fig_macd.update_yaxes(title_text="Histogram", row=2, col=1)
578
+
579
+ st.plotly_chart(fig_macd, use_container_width=True)
580
+
581
+ # Bollinger Bands
582
+ st.subheader("📈 Bollinger Bands")
583
+ fig_bb = go.Figure()
584
+
585
+ fig_bb.add_trace(go.Scatter(x=df.index, y=df['Close'], mode='lines', name='Close Price',
586
+ line=dict(color='purple', width=2)))
587
+ fig_bb.add_trace(go.Scatter(x=df.index, y=df['SMA20'], mode='lines', name='20-day SMA',
588
+ line=dict(color='blue', width=1.5)))
589
+ fig_bb.add_trace(go.Scatter(x=df.index, y=df['Upper_Band'], mode='lines', name='Upper Band',
590
+ line=dict(color='red', dash='dash', width=1.5)))
591
+ fig_bb.add_trace(go.Scatter(x=df.index, y=df['Lower_Band'], mode='lines', name='Lower Band',
592
+ line=dict(color='green', dash='dash', width=1.5),
593
+ fill='tonexty', fillcolor='rgba(128,128,128,0.2)'))
594
+
595
+ fig_bb.update_layout(title="Bollinger Bands", height=500)
596
+ st.plotly_chart(fig_bb, use_container_width=True)
597
+
598
+ # Drawdown Analysis
599
+ st.subheader("📉 Drawdown Analysis")
600
+
601
+ # Calculate drawdown
602
+ returns = results['Total'].pct_change().fillna(0)
603
+ cumulative = (1 + returns).cumprod()
604
+ running_max = cumulative.expanding().max()
605
+ drawdown = (cumulative - running_max) / running_max
606
+
607
+ fig_dd = go.Figure()
608
+
609
+ fig_dd.add_trace(go.Scatter(
610
+ x=df.index,
611
+ y=drawdown * 100,
612
+ mode='lines',
613
+ name='Drawdown',
614
+ fill='tozeroy',
615
+ fillcolor='rgba(255,0,0,0.3)',
616
+ line=dict(color='red', width=1),
617
+ hovertemplate='<b>Drawdown</b>: %{y:.1f}%<extra></extra>'
618
+ ))
619
+
620
+ fig_dd.update_layout(
621
+ title="Portfolio Drawdown Over Time",
622
+ xaxis_title="Date",
623
+ yaxis_title="Drawdown (%)",
624
+ height=400,
625
+ template='plotly_white'
626
+ )
627
+
628
+ st.plotly_chart(fig_dd, use_container_width=True)
629
+
630
+ # Trade analysis
631
+ if metrics is not None and not metrics['Trades DataFrame'].empty:
632
+ st.subheader("📋 Trade Analysis")
633
+
634
+ trades_df = metrics['Trades DataFrame']
635
+
636
+ # Trade statistics
637
+ col1, col2, col3 = st.columns(3)
638
+ with col1:
639
+ avg_trade_duration = (pd.to_datetime(trades_df['exit_date']) -
640
+ pd.to_datetime(trades_df['entry_date'])).dt.days.mean()
641
+ st.metric("📅 Avg Trade Duration", f"{avg_trade_duration:.1f} days")
642
+
643
+ with col2:
644
+ best_trade = trades_df['return_pct'].max()
645
+ st.metric("🚀 Best Trade", f"{best_trade:.2%}")
646
+
647
+ with col3:
648
+ worst_trade = trades_df['return_pct'].min()
649
+ st.metric("💥 Worst Trade", f"{worst_trade:.2%}")
650
+
651
+ # Trade returns distribution
652
+ st.subheader("📊 Trade Returns Distribution")
653
+
654
+ returns_pct = trades_df['return_pct'] * 100
655
+
656
+ fig_hist = px.histogram(
657
+ x=returns_pct,
658
+ nbins=20,
659
+ title="Distribution of Trade Returns",
660
+ labels={'x': 'Return (%)', 'y': 'Number of Trades'},
661
+ color_discrete_sequence=['steelblue']
662
+ )
663
+
664
+ # Add vertical lines for mean and zero
665
+ fig_hist.add_vline(x=0, line_dash="dash", line_color="red",
666
+ annotation_text="Break Even")
667
+ fig_hist.add_vline(x=returns_pct.mean(), line_dash="solid", line_color="green",
668
+ annotation_text=f"Mean: {returns_pct.mean():.1f}%")
669
+
670
+ fig_hist.update_layout(
671
+ height=400,
672
+ template='plotly_white',
673
+ showlegend=False
674
+ )
675
+
676
+ st.plotly_chart(fig_hist, use_container_width=True)
677
+
678
+ # Trade timeline
679
+ st.subheader("📅 Trade Timeline")
680
+
681
+ fig_timeline = go.Figure()
682
+
683
+ for i, trade in trades_df.iterrows():
684
+ color = 'green' if trade['return_pct'] > 0 else 'red'
685
+ fig_timeline.add_trace(go.Scatter(
686
+ x=[trade['entry_date'], trade['exit_date']],
687
+ y=[trade['entry_price'], trade['exit_price']],
688
+ mode='lines+markers',
689
+ name=f"Trade {i+1}",
690
+ line=dict(color=color, width=3),
691
+ marker=dict(size=8),
692
+ hovertemplate=f'<b>Trade {i+1}</b><br>' +
693
+ f'Entry: ₹{trade["entry_price"]:.2f}<br>' +
694
+ f'Exit: ₹{trade["exit_price"]:.2f}<br>' +
695
+ f'Return: {trade["return_pct"]:.2%}<br>' +
696
+ f'Duration: {(pd.to_datetime(trade["exit_date"]) - pd.to_datetime(trade["entry_date"])).days} days<extra></extra>',
697
+ showlegend=False
698
+ ))
699
+
700
+ fig_timeline.update_layout(
701
+ title="Individual Trade Performance Timeline",
702
+ xaxis_title="Date",
703
+ yaxis_title="Price (₹)",
704
+ height=500,
705
+ template='plotly_white'
706
+ )
707
+
708
+ st.plotly_chart(fig_timeline, use_container_width=True)
709
+
710
+ # Trade history table
711
+ st.subheader("📊 Detailed Trade History")
712
+ display_trades = trades_df.copy()
713
+ display_trades['Entry Date'] = pd.to_datetime(display_trades['entry_date']).dt.strftime('%Y-%m-%d')
714
+ display_trades['Exit Date'] = pd.to_datetime(display_trades['exit_date']).dt.strftime('%Y-%m-%d')
715
+ display_trades['Entry Price'] = display_trades['entry_price'].apply(lambda x: f"₹{x:.2f}")
716
+ display_trades['Exit Price'] = display_trades['exit_price'].apply(lambda x: f"₹{x:.2f}")
717
+ display_trades['P&L (₹)'] = display_trades['profit_loss'].apply(lambda x: f"₹{x:,.2f}")
718
+ display_trades['Return %'] = display_trades['return_pct'].apply(lambda x: f"{x:.2%}")
719
+ display_trades['Duration'] = (pd.to_datetime(trades_df['exit_date']) -
720
+ pd.to_datetime(trades_df['entry_date'])).dt.days
721
+
722
+ trade_display = display_trades[['Entry Date', 'Exit Date', 'Entry Price', 'Exit Price',
723
+ 'P&L (₹)', 'Return %', 'Duration', 'exit_reason']].copy()
724
+ trade_display.columns = ['Entry Date', 'Exit Date', 'Entry Price', 'Exit Price',
725
+ 'Profit/Loss', 'Return %', 'Days', 'Exit Reason']
726
+
727
+ st.dataframe(trade_display, use_container_width=True)
728
+
729
+ else:
730
+ st.info("📝 No trades were executed during this period with the current parameters.")
731
+
732
+ # Signal summary table
733
+ if signal_col is not None:
734
+ st.subheader("📋 Trading Signals Summary")
735
+ signal_summary = df[df[signal_col] != 0].copy()
736
+
737
+ if not signal_summary.empty:
738
+ signal_summary['Signal Type'] = signal_summary[signal_col].map({1: '🟢 BUY', -1: '🔴 SELL'})
739
+ signal_summary['Price'] = signal_summary['Close'].apply(lambda x: f"₹{x:.2f}")
740
+ signal_summary['RSI'] = signal_summary['RSI14'].apply(lambda x: f"{x:.1f}")
741
+ signal_summary[f'{strategy_name}{short_period}'] = signal_summary[f'{strategy_name}{short_period}'].apply(lambda x: f"₹{x:.2f}")
742
+ signal_summary[f'{strategy_name}{long_period}'] = signal_summary[f'{strategy_name}{long_period}'].apply(lambda x: f"₹{x:.2f}")
743
+
744
+ display_signals = signal_summary[['Signal Type', 'Price', 'RSI',
745
+ f'{strategy_name}{short_period}',
746
+ f'{strategy_name}{long_period}']].copy()
747
+ display_signals.index = display_signals.index.strftime('%Y-%m-%d')
748
+
749
+ st.dataframe(display_signals, use_container_width=True)
750
+ else:
751
+ st.info("📝 No trading signals were generated during this period with the current parameters.")
752
+
753
+ # Data Download Section
754
+ st.subheader("💾 Download Data")
755
+ col1, col2 = st.columns(2)
756
+
757
+ with col1:
758
+ csv_data = df.to_csv(index=True)
759
+ st.download_button(
760
+ label="📁 Download Full Dataset (CSV)",
761
+ data=csv_data,
762
+ file_name=f"{selected_stock}_analysis_{start_date.strftime('%Y%m%d')}.csv",
763
+ mime="text/csv"
764
+ )
765
+
766
+ with col2:
767
+ if results is not None:
768
+ results_csv = results.to_csv(index=True)
769
+ st.download_button(
770
+ label="📊 Download Backtest Results (CSV)",
771
+ data=results_csv,
772
+ file_name=f"{selected_stock}_backtest_{start_date.strftime('%Y%m%d')}.csv",
773
+ mime="text/csv"
774
+ )
775
+
776
+ else:
777
+ st.error("❌ No data found for the selected stock and date range.")
778
+
779
+ # ========================= SIDEBAR INFORMATION =========================
780
+
781
+ st.sidebar.markdown("---")
782
+ st.sidebar.header("ℹ️ About")
783
+ st.sidebar.write("""
784
+ **Price Prediction Features:**
785
+ - Logistic Regression model for next-day prediction
786
+ - 59+ technical features including volatility, momentum, and lag features
787
+ - Confidence gauge and feature importance analysis
788
+
789
+ **Trading Dashboard Features:**
790
+ - SMA and EMA-based strategies
791
+ - Comprehensive backtesting with risk management
792
+ - Detailed performance metrics and trade analysis
793
+ - Interactive visualizations with Plotly
794
+
795
+ **Disclaimer**: This is for educational purposes only. Always do your own research before making investment decisions.
796
+ """)
797
+
798
+ st.sidebar.markdown("---")
799
+ st.sidebar.write("**Model Performance:**")
800
+ st.sidebar.write("• Accuracy: 55%")
801
+ st.sidebar.write("• F1 Score: 0.4839")
802
+ st.sidebar.write("• AUC: 0.5370")
803
+ st.sidebar.write("• Average Precision: 0.5300")
804
+
805
+ # Footer
806
+ st.markdown("---")
807
+ st.markdown("**⚠️ Disclaimer**: This platform is for research and educational purposes only. Stock market investments are subject to market risks. Please consult with a financial advisor before making investment decisions.")
808
+ st.markdown("**Developed by**: Zane Vijay Falcao")