riteshcp commited on
Commit
c84ada6
β€’
1 Parent(s): 80b45c4

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +114 -107
app.py CHANGED
@@ -10,7 +10,6 @@ from plotly.subplots import make_subplots
10
  # Configuration and Constants
11
  # ---------------------------
12
 
13
- # Define sector ETFs
14
  SECTORS = {
15
  'Technology': 'XLK',
16
  'Healthcare': 'XLV',
@@ -35,21 +34,26 @@ COLORS = [
35
  # Utility Functions
36
  # ---------------------------
37
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
  @st.cache_data(ttl=60*5) # Cache data for 5 minutes
39
  def fetch_sector_performance(start_date, end_date, interval='1d'):
40
  """
41
- Fetches the performance data for each sector ETF over a specified date range.
42
-
43
- Parameters:
44
- start_date (datetime.date): Start date.
45
- end_date (datetime.date): End date.
46
- interval (str): Data interval (e.g., '1d', '1m').
47
-
48
- Returns:
49
- list of dict: Sector performance data.
50
  """
51
  sector_data = []
52
- adjusted_end_date = end_date + timedelta(days=1) # Include end_date
53
 
54
  india_tz = pytz.timezone('Asia/Kolkata')
55
  start_datetime = datetime.combine(start_date, datetime.min.time()).astimezone(india_tz)
@@ -58,6 +62,7 @@ def fetch_sector_performance(start_date, end_date, interval='1d'):
58
  for sector, ticker in SECTORS.items():
59
  try:
60
  stock = yf.Ticker(ticker)
 
61
  hist = stock.history(start=start_datetime, end=end_datetime, interval=interval)
62
 
63
  if not hist.empty and len(hist['Close']) >= 2:
@@ -66,24 +71,30 @@ def fetch_sector_performance(start_date, end_date, interval='1d'):
66
  oldest_close = hist['Close'][0]
67
  performance = ((latest_close - oldest_close) / oldest_close) * 100
68
  color = '#66BB6A' if performance >= 0 else '#EF5350'
 
 
69
  sector_data.append({
70
  'Sector': sector,
71
  'Performance': performance,
72
  'Color': color,
73
  'Ticker': ticker,
74
- 'Historical Data': hist # Include for debugging
 
 
 
75
  })
76
  else:
77
- # Handle cases with insufficient data
78
  sector_data.append({
79
  'Sector': sector,
80
  'Performance': 0.0,
81
  'Color': 'gray',
82
  'Ticker': ticker,
83
- 'Historical Data': hist # Include for debugging
 
84
  })
85
  except Exception as e:
86
- # Handle exceptions for individual tickers
87
  sector_data.append({
88
  'Sector': sector,
89
  'Performance': 0.0,
@@ -94,9 +105,25 @@ def fetch_sector_performance(start_date, end_date, interval='1d'):
94
  })
95
 
96
  # Sort data by performance descending
 
97
  sector_data.sort(key=lambda x: x['Performance'], reverse=True)
98
  return sector_data
99
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
100
  def get_summary_stats(sector_data):
101
  """
102
  Calculate summary statistics from sector data.
@@ -128,21 +155,6 @@ def format_performance(performance):
128
  """
129
  return f"{performance:+.2f}%"
130
 
131
- def is_market_open():
132
- """
133
- Checks if the NSE market is currently open based on Asia/Kolkata timezone.
134
-
135
- Returns:
136
- bool: True if market is open, False otherwise.
137
- """
138
- india_tz = pytz.timezone('Asia/Kolkata')
139
- now = datetime.now(india_tz)
140
- # Define market hours: 9:15 AM to 3:30 PM IST
141
- market_open = now.replace(hour=9, minute=15, second=0, microsecond=0)
142
- market_close = now.replace(hour=15, minute=30, second=0, microsecond=0)
143
-
144
- return market_open <= now <= market_close
145
-
146
  # ---------------------------
147
  # Streamlit UI
148
  # ---------------------------
@@ -151,17 +163,17 @@ def main():
151
  st.set_page_config(page_title="Market Sector Performance Dashboard", layout="wide")
152
  st.title("πŸ“ˆ Market Sector Performance Dashboard")
153
 
154
- # Initialize session state for refresh counter and last updated time
155
  if 'refresh' not in st.session_state:
156
  st.session_state.refresh = 0
157
  if 'last_updated' not in st.session_state:
158
  st.session_state.last_updated = None
159
 
160
- # Sidebar for date range selection and refresh button
161
  with st.sidebar:
162
  st.header("Controls")
163
 
164
- # Date Range Selection
165
  st.markdown("### Select Date Range")
166
  india_tz = pytz.timezone('Asia/Kolkata')
167
  now = datetime.now(india_tz)
@@ -171,76 +183,69 @@ def main():
171
  start_date = st.date_input("Start Date", default_start)
172
  end_date = st.date_input("End Date", default_end)
173
 
174
- # Validate Date Range
175
  if start_date > end_date:
176
  st.error("Error: Start Date must be before End Date.")
 
 
 
 
 
 
 
 
 
 
 
 
177
 
178
  # Refresh Button
179
  if st.button("πŸ”„ Refresh Data"):
180
  st.session_state.refresh += 1
181
  st.session_state.last_updated = datetime.now(india_tz).strftime("%Y-%m-%d %H:%M:%S")
182
- # Clear cached data by updating refresh counter
183
  st.cache_data.clear()
184
-
185
- st.markdown("---")
186
- st.markdown("**Last Updated:**")
187
- st.write(st.session_state.last_updated if st.session_state.last_updated else "Not updated yet.")
188
-
189
- # Market Status Indicator
190
- market_status = is_market_open()
191
- if market_status:
192
- st.success("πŸ“ˆ Market is currently OPEN.")
193
- else:
194
- st.info("πŸ“‰ Market is CLOSED. Showing latest available data.")
195
 
196
- # Main content area
197
  try:
198
  with st.spinner('Fetching sector data...'):
199
- # Ensure that start_date is before end_date
200
- if start_date <= end_date:
201
- # Determine interval based on market status
202
- interval = '1m' if market_status else '1d'
203
- sector_data = fetch_sector_performance(start_date, end_date, interval=interval)
204
- last_updated = datetime.now(india_tz).strftime("%Y-%m-%d %H:%M:%S")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
205
  else:
206
- sector_data = []
207
- last_updated = datetime.now(india_tz).strftime("%Y-%m-%d %H:%M:%S")
208
-
209
- # Update last updated time if not set
210
- if not st.session_state.last_updated and sector_data:
211
- st.session_state.last_updated = last_updated
212
-
213
- # Calculate summary stats
214
- stats = get_summary_stats(sector_data)
215
-
216
- # Summary Cards
217
- col1, col2, col3, col4 = st.columns(4)
218
-
219
- col1.metric("Sectors Up", stats['positive'], "")
220
- col2.metric("Sectors Down", stats['negative'], "")
221
-
222
- if stats['best'] and stats['best']['Performance'] != 0.0:
223
- col3.metric(
224
- "Best Performer",
225
- f"{stats['best']['Sector']} ({format_performance(stats['best']['Performance'])})"
226
- )
227
- else:
228
- col3.metric("Best Performer", "N/A", "")
229
-
230
- if stats['worst'] and stats['worst']['Performance'] != 0.0:
231
- col4.metric(
232
- "Worst Performer",
233
- f"{stats['worst']['Sector']} ({format_performance(stats['worst']['Performance'])})"
234
- )
235
- else:
236
- col4.metric("Worst Performer", "N/A", "")
237
-
238
- st.markdown("---")
239
-
240
- if sector_data:
241
  # Charts
242
- fig = make_subplots(rows=1, cols=2, subplot_titles=("Performance by Sector", "Sector Distribution"),
243
- specs=[[{"type": "bar"}, {"type": "pie"}]])
 
 
 
244
 
245
  # Bar Chart
246
  fig.add_trace(
@@ -279,9 +284,12 @@ def main():
279
  st.markdown("---")
280
 
281
  # Performance Table
282
- df = pd.DataFrame(sector_data)
283
- df['Performance'] = df['Performance'].apply(format_performance)
284
- df = df.rename(columns={'Sector': 'Sector', 'Performance': 'Performance (%)', 'Ticker': 'Ticker'})
 
 
 
285
 
286
  def color_performance(val):
287
  """
@@ -290,21 +298,22 @@ def main():
290
  try:
291
  num = float(val.strip('%'))
292
  if num > 0:
293
- return 'color: green'
294
  elif num < 0:
295
- return 'color: red'
296
  else:
297
- return 'color: gray'
 
298
  except:
299
  return 'color: black'
300
 
301
- # Using Styler.applymap for compatibility with future versions
302
  styled_df = df.style.applymap(color_performance, subset=['Performance (%)'])
303
 
304
  st.subheader("Sector Performance Table")
305
  st.dataframe(styled_df, use_container_width=True)
306
 
307
- st.markdown(f"**Last Updated:** {last_updated}")
308
 
309
  # Optional: Display historical data for debugging
310
  show_debug = st.checkbox("Show Debugging Information")
@@ -320,17 +329,15 @@ def main():
320
  st.dataframe(hist.tail(10))
321
  else:
322
  st.write("No historical data available.")
323
- else:
324
- st.warning("No sector performance data available for the selected date range.")
325
-
326
  except Exception as e:
327
- st.error(f"An unexpected error occurred: {e}")
328
  st.markdown("""
329
- **Suggestions:**
330
- - Ensure you have a stable internet connection.
331
- - Verify that the `yfinance` library is correctly installed.
332
- - Check for any updates or issues with the `yfinance` library.
333
- - Confirm that the sector ETF symbols are correct.
334
  """)
335
 
336
  if __name__ == "__main__":
 
10
  # Configuration and Constants
11
  # ---------------------------
12
 
 
13
  SECTORS = {
14
  'Technology': 'XLK',
15
  'Healthcare': 'XLV',
 
34
  # Utility Functions
35
  # ---------------------------
36
 
37
+ def get_appropriate_interval(start_date, end_date, market_status):
38
+ """
39
+ Determines the appropriate data interval based on date range and market status.
40
+ """
41
+ days_diff = (end_date - start_date).days
42
+
43
+ if market_status and days_diff <= 7: # Only use 1m data for up to 7 days when market is open
44
+ return '1m'
45
+ elif days_diff <= 60:
46
+ return '1h'
47
+ else:
48
+ return '1d'
49
+
50
  @st.cache_data(ttl=60*5) # Cache data for 5 minutes
51
  def fetch_sector_performance(start_date, end_date, interval='1d'):
52
  """
53
+ Fetches the performance data for each sector ETF with improved error handling.
 
 
 
 
 
 
 
 
54
  """
55
  sector_data = []
56
+ adjusted_end_date = end_date + timedelta(days=1)
57
 
58
  india_tz = pytz.timezone('Asia/Kolkata')
59
  start_datetime = datetime.combine(start_date, datetime.min.time()).astimezone(india_tz)
 
62
  for sector, ticker in SECTORS.items():
63
  try:
64
  stock = yf.Ticker(ticker)
65
+ # Add error handling for download
66
  hist = stock.history(start=start_datetime, end=end_datetime, interval=interval)
67
 
68
  if not hist.empty and len(hist['Close']) >= 2:
 
71
  oldest_close = hist['Close'][0]
72
  performance = ((latest_close - oldest_close) / oldest_close) * 100
73
  color = '#66BB6A' if performance >= 0 else '#EF5350'
74
+
75
+ # Store more detailed data for debugging
76
  sector_data.append({
77
  'Sector': sector,
78
  'Performance': performance,
79
  'Color': color,
80
  'Ticker': ticker,
81
+ 'Historical Data': hist,
82
+ 'Data Points': len(hist),
83
+ 'Start Price': oldest_close,
84
+ 'End Price': latest_close
85
  })
86
  else:
87
+ st.warning(f"Insufficient data for {sector} ({ticker})")
88
  sector_data.append({
89
  'Sector': sector,
90
  'Performance': 0.0,
91
  'Color': 'gray',
92
  'Ticker': ticker,
93
+ 'Historical Data': pd.DataFrame(),
94
+ 'Error': 'Insufficient data'
95
  })
96
  except Exception as e:
97
+ st.warning(f"Error fetching data for {sector} ({ticker}): {str(e)}")
98
  sector_data.append({
99
  'Sector': sector,
100
  'Performance': 0.0,
 
105
  })
106
 
107
  # Sort data by performance descending
108
+ sector_data = [d for d in sector_data if d['Performance'] != 0.0] # Filter out failed fetches
109
  sector_data.sort(key=lambda x: x['Performance'], reverse=True)
110
  return sector_data
111
 
112
+ def is_market_open():
113
+ """
114
+ Checks if the NSE market is currently open based on Asia/Kolkata timezone.
115
+
116
+ Returns:
117
+ bool: True if market is open, False otherwise.
118
+ """
119
+ india_tz = pytz.timezone('Asia/Kolkata')
120
+ now = datetime.now(india_tz)
121
+ # Define market hours: 9:15 AM to 3:30 PM IST
122
+ market_open = now.replace(hour=9, minute=15, second=0, microsecond=0)
123
+ market_close = now.replace(hour=15, minute=30, second=0, microsecond=0)
124
+
125
+ return market_open <= now <= market_close
126
+
127
  def get_summary_stats(sector_data):
128
  """
129
  Calculate summary statistics from sector data.
 
155
  """
156
  return f"{performance:+.2f}%"
157
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
158
  # ---------------------------
159
  # Streamlit UI
160
  # ---------------------------
 
163
  st.set_page_config(page_title="Market Sector Performance Dashboard", layout="wide")
164
  st.title("πŸ“ˆ Market Sector Performance Dashboard")
165
 
166
+ # Initialize session state
167
  if 'refresh' not in st.session_state:
168
  st.session_state.refresh = 0
169
  if 'last_updated' not in st.session_state:
170
  st.session_state.last_updated = None
171
 
172
+ # Sidebar controls
173
  with st.sidebar:
174
  st.header("Controls")
175
 
176
+ # Date Range Selection with improved validation
177
  st.markdown("### Select Date Range")
178
  india_tz = pytz.timezone('Asia/Kolkata')
179
  now = datetime.now(india_tz)
 
183
  start_date = st.date_input("Start Date", default_start)
184
  end_date = st.date_input("End Date", default_end)
185
 
 
186
  if start_date > end_date:
187
  st.error("Error: Start Date must be before End Date.")
188
+ st.stop()
189
+
190
+ # Warn about long date ranges
191
+ date_diff = (end_date - start_date).days
192
+ if date_diff > 365:
193
+ st.warning("Long date ranges may affect data granularity.")
194
+
195
+ # Determine market status
196
+ market_status = is_market_open()
197
+ interval = get_appropriate_interval(start_date, end_date, market_status)
198
+
199
+ st.info(f"Using `{interval}` data interval.")
200
 
201
  # Refresh Button
202
  if st.button("πŸ”„ Refresh Data"):
203
  st.session_state.refresh += 1
204
  st.session_state.last_updated = datetime.now(india_tz).strftime("%Y-%m-%d %H:%M:%S")
 
205
  st.cache_data.clear()
 
 
 
 
 
 
 
 
 
 
 
206
 
207
+ # Main content
208
  try:
209
  with st.spinner('Fetching sector data...'):
210
+ sector_data = fetch_sector_performance(start_date, end_date, interval=interval)
211
+
212
+ if not sector_data:
213
+ st.warning("No valid sector data available for the selected date range.")
214
+ st.stop()
215
+
216
+ # Calculate summary stats
217
+ stats = get_summary_stats(sector_data)
218
+
219
+ # Summary Cards
220
+ col1, col2, col3, col4 = st.columns(4)
221
+
222
+ col1.metric("Sectors Up", stats['positive'], "")
223
+ col2.metric("Sectors Down", stats['negative'], "")
224
+
225
+ if stats['best'] and stats['best']['Performance'] != 0.0:
226
+ col3.metric(
227
+ "Best Performer",
228
+ f"{stats['best']['Sector']} ({format_performance(stats['best']['Performance'])})"
229
+ )
230
  else:
231
+ col3.metric("Best Performer", "N/A", "")
232
+
233
+ if stats['worst'] and stats['worst']['Performance'] != 0.0:
234
+ col4.metric(
235
+ "Worst Performer",
236
+ f"{stats['worst']['Sector']} ({format_performance(stats['worst']['Performance'])})"
237
+ )
238
+ else:
239
+ col4.metric("Worst Performer", "N/A", "")
240
+
241
+ st.markdown("---")
242
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
243
  # Charts
244
+ fig = make_subplots(
245
+ rows=1, cols=2,
246
+ subplot_titles=("Performance by Sector", "Sector Distribution"),
247
+ specs=[[{"type": "bar"}, {"type": "pie"}]]
248
+ )
249
 
250
  # Bar Chart
251
  fig.add_trace(
 
284
  st.markdown("---")
285
 
286
  # Performance Table
287
+ # Create DataFrame excluding 'Color' and 'Historical Data'
288
+ df = pd.DataFrame([{
289
+ 'Sector': item['Sector'],
290
+ 'Performance (%)': format_performance(item['Performance']),
291
+ 'Ticker': item['Ticker']
292
+ } for item in sector_data])
293
 
294
  def color_performance(val):
295
  """
 
298
  try:
299
  num = float(val.strip('%'))
300
  if num > 0:
301
+ color = 'green'
302
  elif num < 0:
303
+ color = 'red'
304
  else:
305
+ color = 'gray'
306
+ return f'color: {color}'
307
  except:
308
  return 'color: black'
309
 
310
+ # Apply styling to the Performance column
311
  styled_df = df.style.applymap(color_performance, subset=['Performance (%)'])
312
 
313
  st.subheader("Sector Performance Table")
314
  st.dataframe(styled_df, use_container_width=True)
315
 
316
+ st.markdown(f"**Last Updated:** {st.session_state.last_updated}")
317
 
318
  # Optional: Display historical data for debugging
319
  show_debug = st.checkbox("Show Debugging Information")
 
329
  st.dataframe(hist.tail(10))
330
  else:
331
  st.write("No historical data available.")
332
+
 
 
333
  except Exception as e:
334
+ st.error(f"An unexpected error occurred: {str(e)}")
335
  st.markdown("""
336
+ **Troubleshooting Steps:**
337
+ 1. Try selecting a shorter date range.
338
+ 2. Refresh the page.
339
+ 3. Check your internet connection.
340
+ 4. If the issue persists, try again later.
341
  """)
342
 
343
  if __name__ == "__main__":