riteshcp commited on
Commit
c266f63
·
verified ·
1 Parent(s): 19108ab

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +252 -95
app.py CHANGED
@@ -1,106 +1,263 @@
1
  import streamlit as st
2
  import pandas as pd
3
- import yfinance as yf
4
  import numpy as np
5
- import datetime
6
-
7
- def fetch_stock_data(symbol, start_date, end_date, interval):
8
- try:
9
- valid_intervals = {
10
- "1m": 8, # 1-minute data is available for up to 8 days
11
- "2m": 60, # 2-minute data is available for up to 60 days
12
- "5m": 60, # 5-minute data is available for up to 60 days
13
- "15m": 60, # 15-minute data is available for up to 60 days
14
- "30m": 60, # 30-minute data is available for up to 60 days
15
- "60m": 730, # 60-minute data is available for up to 730 days (2 years)
16
- "90m": 730, # 90-minute data is available for up to 730 days (2 years)
17
- "1h": 730, # 1-hour data is available for up to 730 days (2 years)
18
- "1d": 10000, # Daily data is available for up to maximum available
19
- "5d": 10000, # 5-day data is available for up to maximum available
20
- "1wk": 10000,# Weekly data is available for up to maximum available
21
- "1mo": 10000,# Monthly data is available for up to maximum available
22
- "3mo": 10000 # Quarterly data is available for up to maximum available
23
- }
24
-
25
- if interval not in valid_intervals:
26
- st.error(f"Invalid interval: {interval}. Please choose from {list(valid_intervals.keys())}")
27
- return
28
 
29
- max_days = valid_intervals[interval]
30
- start_date_pd = pd.to_datetime(start_date)
31
- end_date_pd = pd.to_datetime(end_date)
32
- max_start_date = end_date_pd - pd.to_timedelta(max_days, unit='d')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
 
34
- if start_date_pd < max_start_date:
35
- st.warning(f"The selected start date exceeds the maximum allowed range for {interval} interval. Adjusting start date to {max_start_date.strftime('%Y-%m-%d')}")
36
- start_date_pd = max_start_date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
 
 
 
 
 
 
 
 
38
  ticker = yf.Ticker(symbol)
39
- data = ticker.history(start=start_date_pd.strftime('%Y-%m-%d'), end=end_date_pd.strftime('%Y-%m-%d'), interval=interval)
40
- if not data.empty:
41
- data.reset_index(inplace=True)
42
- data.rename(columns={'index': 'Datetime'}, inplace=True)
43
- # Calculate On-Balance Volume (OBV)
44
- data['OBV'] = (np.sign(data['Close'].diff()) * data['Volume']).fillna(0).cumsum()
45
- # Calculate Relative Strength Index (RSI)
46
- delta = data['Close'].diff()
47
- gain = (delta.where(delta > 0, 0)).rolling(window=14).mean()
48
- loss = (-delta.where(delta < 0, 0)).rolling(window=14).mean()
49
- rs = gain / loss
50
- data['RSI'] = 100 - (100 / (1 + rs))
51
- # Calculate Exponential Moving Average (EMA)
52
- data['EMA'] = data['Close'].ewm(span=20, adjust=False).mean()
53
- # Calculate Simple Moving Average (SMA)
54
- data['SMA'] = data['Close'].rolling(window=20).mean()
55
-
56
- st.write(f"Data for {symbol} fetched successfully with OBV, RSI, EMA, and SMA included.")
57
- st.write(data)
58
- else:
59
- st.error(f"No data available for {symbol}. Please check the symbol or try a different date range.")
60
  except Exception as e:
61
  st.error(f"Error fetching data for {symbol}: {e}")
 
62
 
63
- # Stock List
64
- stock_list = [
65
- "RELIANCE.NS", "TCS.NS", "INFY.NS", "HDFCBANK.NS", "ICICIBANK.NS",
66
- "KOTAKBANK.NS", "SBIN.NS", "LT.NS", "HINDUNILVR.NS", "BHARTIARTL.NS"
67
- ]
68
-
69
- # Streamlit app layout
70
- st.title("Share Market Data")
71
- symbol = st.selectbox("Select Stock Symbol", options=stock_list)
72
- interval = st.selectbox("Select Data Interval", options=["1m", "2m", "5m", "15m", "30m", "60m", "90m", "1h", "1d", "5d", "1wk", "1mo", "3mo"])
73
-
74
- # Allow user to select a start date
75
- start_date = st.date_input("Select Start Date", pd.to_datetime('2023-01-01'))
76
- end_date = pd.to_datetime('today').strftime('%Y-%m-%d')
77
-
78
- valid_intervals = {
79
- "1m": 8, # 1-minute data is available for up to 8 days
80
- "2m": 60, # 2-minute data is available for up to 60 days
81
- "5m": 60, # 5-minute data is available for up to 60 days
82
- "15m": 60, # 15-minute data is available for up to 60 days
83
- "30m": 60, # 30-minute data is available for up to 60 days
84
- "60m": 730, # 60-minute data is available for up to 730 days (2 years)
85
- "90m": 730, # 90-minute data is available for up to 730 days (2 years)
86
- "1h": 730, # 1-hour data is available for up to 730 days (2 years)
87
- "1d": 10000, # Daily data is available for up to maximum available
88
- "5d": 10000, # 5-day data is available for up to maximum available
89
- "1wk": 10000,# Weekly data is available for up to maximum available
90
- "1mo": 10000,# Monthly data is available for up to maximum available
91
- "3mo": 10000 # Quarterly data is available for up to maximum available
92
- }
93
-
94
- max_days = valid_intervals[interval]
95
- end_date_pd = pd.to_datetime('today')
96
- max_start_date = end_date_pd - pd.to_timedelta(max_days, unit='d')
97
-
98
- # Adjust the start date if it exceeds the limit
99
- if pd.to_datetime(start_date) < max_start_date:
100
- st.warning(f"The selected start date exceeds the maximum allowed range for {interval} interval. Adjusting start date to {max_start_date.strftime('%Y-%m-%d')}")
101
- start_date = max_start_date
102
-
103
- fetch_data = st.button("Fetch and Display Data")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
104
 
105
- if fetch_data and symbol:
106
- fetch_stock_data(symbol, start_date.strftime('%Y-%m-%d'), end_date, interval)
 
1
  import streamlit as st
2
  import pandas as pd
 
3
  import numpy as np
4
+ import yfinance as yf
5
+ import plotly.graph_objects as go
6
+ from plotly.subplots import make_subplots
7
+ import ta
8
+ from datetime import datetime, timedelta
9
+ import pytz
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
 
11
+ def calculate_technical_indicators(data):
12
+ """Calculate comprehensive technical indicators"""
13
+ # Trend Indicators
14
+ data['SMA_20'] = ta.trend.sma_indicator(data['Close'], window=20)
15
+ data['SMA_50'] = ta.trend.sma_indicator(data['Close'], window=50)
16
+ data['SMA_200'] = ta.trend.sma_indicator(data['Close'], window=200)
17
+ data['EMA_20'] = ta.trend.ema_indicator(data['Close'], window=20)
18
+
19
+ # Momentum Indicators
20
+ data['RSI'] = ta.momentum.rsi(data['Close'], window=14)
21
+ data['MACD'] = ta.trend.macd_diff(data['Close'])
22
+ data['ROC'] = ta.momentum.roc(data['Close'], window=12)
23
+
24
+ # Volume Indicators
25
+ data['OBV'] = ta.volume.on_balance_volume(data['Close'], data['Volume'])
26
+ data['MFI'] = ta.volume.money_flow_index(data['High'], data['Low'], data['Close'], data['Volume'])
27
+
28
+ # Volatility Indicators
29
+ bb = ta.volatility.BollingerBands(close=data['Close'])
30
+ data['BB_upper'] = bb.bollinger_hband()
31
+ data['BB_middle'] = bb.bollinger_mavg()
32
+ data['BB_lower'] = bb.bollinger_lband()
33
+ data['ATR'] = ta.volatility.average_true_range(data['High'], data['Low'], data['Close'])
34
+
35
+ # Additional Indian Market Specific Calculations
36
+ data['Daily_Return'] = data['Close'].pct_change() * 100
37
+ data['52W_High'] = data['Close'].rolling(window=252).max()
38
+ data['52W_Low'] = data['Close'].rolling(window=252).min()
39
+ data['Distance_From_52W_High'] = ((data['Close'] - data['52W_High'])/data['52W_High']) * 100
40
+
41
+ return data
42
 
43
+ def plot_charts(data, symbol, interval):
44
+ """Create interactive charts using plotly"""
45
+ # Determine the number of rows based on the interval
46
+ minute_intervals = ["1m", "2m", "5m", "15m", "30m", "60m", "90m", "1h"]
47
+ if interval in minute_intervals:
48
+ rows = 3
49
+ subplot_titles = ('Price & Volume', 'RSI', 'MACD')
50
+ row_heights = [0.5, 0.25, 0.25]
51
+ else:
52
+ rows = 4
53
+ subplot_titles = ('Price & Volume', 'RSI', 'MACD', 'Technical Indicators')
54
+ row_heights = [0.4, 0.2, 0.2, 0.2]
55
+
56
+ fig = make_subplots(rows=rows, cols=1,
57
+ shared_xaxes=True,
58
+ vertical_spacing=0.05,
59
+ subplot_titles=subplot_titles,
60
+ row_heights=row_heights)
61
+
62
+ # Main candlestick chart
63
+ fig.add_trace(go.Candlestick(x=data.index,
64
+ open=data['Open'],
65
+ high=data['High'],
66
+ low=data['Low'],
67
+ close=data['Close'],
68
+ name='OHLC'),
69
+ row=1, col=1)
70
+
71
+ # Add Moving Averages
72
+ if 'SMA_20' in data.columns:
73
+ fig.add_trace(go.Scatter(x=data.index, y=data['SMA_20'], name='SMA 20', line=dict(color='orange')), row=1, col=1)
74
+ if 'SMA_50' in data.columns:
75
+ fig.add_trace(go.Scatter(x=data.index, y=data['SMA_50'], name='SMA 50', line=dict(color='blue')), row=1, col=1)
76
+ if 'SMA_200' in data.columns:
77
+ fig.add_trace(go.Scatter(x=data.index, y=data['SMA_200'], name='SMA 200', line=dict(color='red')), row=1, col=1)
78
+
79
+ if interval not in minute_intervals:
80
+ # Volume bars for daily and higher intervals
81
+ colors = ['red' if row['Open'] - row['Close'] >= 0 else 'green' for index, row in data.iterrows()]
82
+ fig.add_trace(go.Bar(x=data.index, y=data['Volume'], name='Volume', marker_color=colors), row=1, col=1)
83
+
84
+ # RSI
85
+ if 'RSI' in data.columns:
86
+ fig.add_trace(go.Scatter(x=data.index, y=data['RSI'], name='RSI', line=dict(color='purple')), row=2, col=1)
87
+ fig.add_hline(y=70, line_dash="dash", line_color="red", row=2, col=1)
88
+ fig.add_hline(y=30, line_dash="dash", line_color="green", row=2, col=1)
89
+
90
+ # MACD
91
+ if 'MACD' in data.columns:
92
+ fig.add_trace(go.Scatter(x=data.index, y=data['MACD'], name='MACD', line=dict(color='blue')), row=3, col=1)
93
+
94
+ if rows == 4:
95
+ # Bollinger Bands
96
+ if 'BB_upper' in data.columns and 'BB_middle' in data.columns and 'BB_lower' in data.columns:
97
+ fig.add_trace(go.Scatter(x=data.index, y=data['BB_upper'], name='BB Upper',
98
+ line=dict(color='gray', dash='dash')), row=4, col=1)
99
+ fig.add_trace(go.Scatter(x=data.index, y=data['BB_middle'], name='BB Middle',
100
+ line=dict(color='gray')), row=4, col=1)
101
+ fig.add_trace(go.Scatter(x=data.index, y=data['BB_lower'], name='BB Lower',
102
+ line=dict(color='gray', dash='dash')), row=4, col=1)
103
+
104
+ # Update layout
105
+ fig.update_layout(
106
+ title=f'{symbol} Technical Analysis Dashboard',
107
+ yaxis_title='Price',
108
+ height=800 if rows == 4 else 600,
109
+ showlegend=True,
110
+ xaxis_rangeslider_visible=False
111
+ )
112
+
113
+ return fig
114
 
115
+ def fetch_stock_data(symbol, start_date, end_date, interval):
116
+ """Fetch stock data with error handling and data validation"""
117
+ try:
118
+ # Convert dates to IST timezone for accuracy
119
+ ist = pytz.timezone('Asia/Kolkata')
120
+ end_date_ist = datetime.now(ist).date()
121
+
122
  ticker = yf.Ticker(symbol)
123
+ data = ticker.history(start=start_date.strftime('%Y-%m-%d'), end=end_date.strftime('%Y-%m-%d'), interval=interval)
124
+
125
+ if data.empty:
126
+ st.error(f"No data available for {symbol} with interval '{interval}'. Please verify the symbol, interval, or try a different date range.")
127
+ return None
128
+
129
+ # Calculate all technical indicators
130
+ data = calculate_technical_indicators(data)
131
+
132
+ # Save data to CSV
133
+ filename = f'{symbol}_{interval}.csv'
134
+ data.to_csv(filename)
135
+
136
+ return data
137
+
 
 
 
 
 
 
138
  except Exception as e:
139
  st.error(f"Error fetching data for {symbol}: {e}")
140
+ return None
141
 
142
+ def main():
143
+ st.set_page_config(layout="wide")
144
+ st.title("Indian Stock Market Technical Analysis Dashboard")
145
+
146
+ # Define minute-level and daily/higher intervals
147
+ minute_intervals = ["1m", "2m", "5m", "15m", "30m", "60m", "90m", "1h"]
148
+ daily_or_higher_intervals = ["1d", "5d", "1wk", "1mo", "3mo"]
149
+ all_intervals = minute_intervals + daily_or_higher_intervals
150
+
151
+ # Sidebar for inputs
152
+ st.sidebar.header("Configuration")
153
+ symbol = st.sidebar.text_input("Enter Stock Symbol (e.g., RELIANCE.NS, TCS.NS):", "RELIANCE.NS")
154
+
155
+ interval = st.sidebar.selectbox(
156
+ "Select Data Interval",
157
+ all_intervals,
158
+ index=8 # Default to '1d'
159
+ )
160
+
161
+ # Timezone and current date in IST
162
+ ist = pytz.timezone('Asia/Kolkata')
163
+ end_date_ist = datetime.now(ist).date()
164
+
165
+ # Handle start date based on interval
166
+ if interval in minute_intervals:
167
+ # Automatically set start_date to last 7 days
168
+ start_date = end_date_ist - timedelta(days=7)
169
+ st.sidebar.write(f"**Note:** For interval '{interval}', only the last 7 days of data are available.")
170
+ st.sidebar.write(f"**Start Date:** {start_date.strftime('%Y-%m-%d')}")
171
+ else:
172
+ # Allow user to select start_date for daily and higher intervals
173
+ # Define a reasonable maximum history, e.g., 5 years
174
+ max_history = 5 * 365 # 5 years
175
+ default_start = end_date_ist - timedelta(days=365) # Default to 1 year ago
176
+ start_date = st.sidebar.date_input(
177
+ "Start Date",
178
+ value=default_start,
179
+ min_value=end_date_ist - timedelta(days=max_history),
180
+ max_value=end_date_ist - timedelta(days=1)
181
+ )
182
+
183
+ # Define end_date as today
184
+ end_date = end_date_ist
185
+
186
+ # Add fetch button
187
+ if st.sidebar.button("Fetch Data and Update Charts"):
188
+ with st.spinner('Fetching data...'):
189
+ data = fetch_stock_data(symbol, start_date, end_date, interval)
190
+
191
+ if data is not None:
192
+ # Display basic info
193
+ st.subheader(f"Analysis for {symbol}")
194
+ current_price = data['Close'][-1]
195
+ if len(data) >= 2:
196
+ daily_change = ((data['Close'][-1] - data['Close'][-2])/data['Close'][-2]) * 100
197
+ else:
198
+ daily_change = 0.0 # Not enough data to calculate change
199
+
200
+ col1, col2, col3, col4 = st.columns(4)
201
+ col1.metric("Current Price", f"₹{current_price:.2f}", f"{daily_change:.2f}%")
202
+
203
+ if '52W_High' in data.columns and '52W_Low' in data.columns:
204
+ col2.metric("52 Week High", f"₹{data['52W_High'][-1]:.2f}")
205
+ col3.metric("52 Week Low", f"₹{data['52W_Low'][-1]:.2f}")
206
+
207
+ if 'RSI' in data.columns:
208
+ col4.metric("RSI", f"{data['RSI'][-1]:.2f}")
209
+
210
+ # Plot interactive charts
211
+ fig = plot_charts(data, symbol, interval)
212
+ st.plotly_chart(fig, use_container_width=True)
213
+
214
+ # Technical Analysis Summary
215
+ st.subheader("Technical Analysis Summary")
216
+
217
+ # Simple trading signals
218
+ signals = []
219
+ if 'RSI' in data.columns:
220
+ rsi = data['RSI'][-1]
221
+ if rsi < 30:
222
+ signals.append("RSI indicates oversold conditions")
223
+ elif rsi > 70:
224
+ signals.append("RSI indicates overbought conditions")
225
+
226
+ if 'MACD' in data.columns:
227
+ macd = data['MACD'][-1]
228
+ if macd > 0:
229
+ signals.append("MACD is positive - Bullish momentum")
230
+ else:
231
+ signals.append("MACD is negative - Bearish momentum")
232
+
233
+ if 'SMA_20' in data.columns and 'SMA_50' in data.columns:
234
+ current_price = data['Close'][-1]
235
+ sma_20 = data['SMA_20'][-1]
236
+ sma_50 = data['SMA_50'][-1]
237
+
238
+ if current_price > sma_20 and current_price > sma_50:
239
+ signals.append("Price is above major moving averages - Bullish")
240
+ elif current_price < sma_20 and current_price < sma_50:
241
+ signals.append("Price is below major moving averages - Bearish")
242
+
243
+ for signal in signals:
244
+ st.write(f"• {signal}")
245
+
246
+ if not signals:
247
+ st.write("No clear technical signals detected.")
248
+
249
+ # Display raw data in expandable section
250
+ with st.expander("View Raw Data"):
251
+ st.dataframe(data)
252
+
253
+ # Provide download link for CSV
254
+ csv = data.to_csv(index=True)
255
+ st.download_button(
256
+ label="Download data as CSV",
257
+ data=csv,
258
+ file_name=f'{symbol}_{interval}.csv',
259
+ mime='text/csv',
260
+ )
261
 
262
+ if __name__ == "__main__":
263
+ main()