Subham9126 commited on
Commit
fc38b7e
·
verified ·
1 Parent(s): cc48e0d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +67 -23
app.py CHANGED
@@ -72,7 +72,7 @@ def get_time_range_in_unix_ms(start_date_str: str, end_date_str: str, timezone:
72
  return {"start_timestamp_ms": start_timestamp, "end_timestamp_ms": end_timestamp}
73
 
74
 
75
- # --- Asynchronous API & Processing Functions (No changes here) ---
76
 
77
  async def call_price_api_async(session: aiohttp.ClientSession, ticker: str, start: int, end: int, interval: int) -> Dict:
78
  """Asynchronously calls the candle API."""
@@ -87,16 +87,51 @@ async def call_price_api_async(session: aiohttp.ClientSession, ticker: str, star
87
  return {"ticker": ticker, "interval": interval, "data": None, "error": str(e)}
88
 
89
  async def main_task(tickers: List[str], start_time: int, end_time: int, intervals: List[int], progress: gr.Progress):
90
- """Main function to run the asynchronous API calls in batches."""
 
 
 
 
 
 
 
 
 
 
 
91
  results = []
92
  batch_size = 30
93
- async with aiohttp.ClientSession() as session:
94
- for i in progress.tqdm(range(0, len(tickers), batch_size), desc="Processing Batches"):
95
- batch_tickers = tickers[i:i+batch_size]
96
- tasks = [call_price_api_async(session, ticker, start_time, end_time, interval)
97
- for ticker in batch_tickers for interval in intervals]
98
- batch_results = await asyncio.gather(*tasks)
99
- results.extend(batch_results)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
100
  return results
101
 
102
  def process_and_merge_data(results: list) -> str:
@@ -123,7 +158,7 @@ def process_and_merge_data(results: list) -> str:
123
  return json.dumps(list(merged_data.values()), indent=4)
124
 
125
 
126
- # --- Gradio Backend Function (MODIFIED FOR NEW INPUT FORMAT) ---
127
 
128
  async def run_backend_processing(tickers_text: str, progress=gr.Progress(track_tqdm=True)):
129
  """The main function to be called by the Gradio interface."""
@@ -148,48 +183,57 @@ async def run_backend_processing(tickers_text: str, progress=gr.Progress(track_t
148
  return
149
 
150
  today_str = date.today().strftime("%Y-%m-%d")
151
- yield f"Starting processing for {len(tickers)} tickers: {tickers}", "{}"
 
 
 
152
 
153
  try:
154
  time_range = get_time_range_in_unix_ms(today_str, today_str)
155
  start_time, end_time = time_range["start_timestamp_ms"], time_range["end_timestamp_ms"]
156
- intervals = [1440, 15]
157
 
158
- yield "Fetching data from API in batches...", "{}"
 
 
159
  results = await main_task(tickers, start_time, end_time, intervals, progress)
160
 
161
- yield "Processing and merging data...", "{}"
 
 
162
  processed_json = process_and_merge_data(results)
163
 
164
- yield "Processing complete.", processed_json
 
165
 
166
  except Exception as e:
167
  logger.error(f"An unhandled error occurred: {e}")
168
  yield f"An error occurred: {e}", "{}"
169
 
170
 
171
- # --- Gradio UI (MODIFIED FOR NEW INPUT FORMAT) ---
172
 
173
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
174
- gr.Markdown("## Stock Data Backend Processor")
175
- gr.Markdown("Enter a list of stock tickers in Python list format.")
176
 
177
  with gr.Row():
178
  with gr.Column(scale=1):
179
- # Update textbox to be single-line and show the correct example format
180
  tickers_input = gr.Textbox(
181
- lines=1,
182
  label='Enter Tickers as a List',
183
- value='["RELIANCE", "INFY"]'
 
184
  )
185
  start_button = gr.Button("Start Processing", variant="primary")
 
 
 
186
 
187
  with gr.Column(scale=2):
188
- logs_output = gr.Textbox(label="Logs", lines=15, interactive=False)
189
  json_output = gr.JSON(label="Processed JSON Output")
190
 
191
  # --- Triggers ---
192
- # Trigger on button click or pressing Enter in the textbox
193
  start_button.click(
194
  fn=run_backend_processing,
195
  inputs=[tickers_input],
 
72
  return {"start_timestamp_ms": start_timestamp, "end_timestamp_ms": end_timestamp}
73
 
74
 
75
+ # --- FIXED Asynchronous API & Processing Functions ---
76
 
77
  async def call_price_api_async(session: aiohttp.ClientSession, ticker: str, start: int, end: int, interval: int) -> Dict:
78
  """Asynchronously calls the candle API."""
 
87
  return {"ticker": ticker, "interval": interval, "data": None, "error": str(e)}
88
 
89
  async def main_task(tickers: List[str], start_time: int, end_time: int, intervals: List[int], progress: gr.Progress):
90
+ """OPTIMIZED: Calculate all requests first, then process in batches of 30."""
91
+
92
+ # Step 1: Calculate all requests upfront
93
+ all_requests = []
94
+ for ticker in tickers:
95
+ for interval in intervals:
96
+ all_requests.append((ticker, start_time, end_time, interval))
97
+
98
+ total_requests = len(all_requests)
99
+ logger.info(f"Total requests to make: {total_requests}")
100
+
101
+ # Step 2: Process requests in batches of 30
102
  results = []
103
  batch_size = 30
104
+
105
+ async with aiohttp.ClientSession(
106
+ timeout=aiohttp.ClientTimeout(total=30), # Add timeout for better reliability
107
+ connector=aiohttp.TCPConnector(limit=50) # Limit concurrent connections
108
+ ) as session:
109
+
110
+ for i in progress.tqdm(range(0, total_requests, batch_size), desc="Processing Request Batches"):
111
+ batch_requests = all_requests[i:i+batch_size]
112
+
113
+ # Create tasks for this batch
114
+ tasks = [
115
+ call_price_api_async(session, ticker, start, end, interval)
116
+ for ticker, start, end, interval in batch_requests
117
+ ]
118
+
119
+ # Execute batch concurrently
120
+ batch_results = await asyncio.gather(*tasks, return_exceptions=True)
121
+
122
+ # Handle any exceptions in results
123
+ for result in batch_results:
124
+ if isinstance(result, Exception):
125
+ logger.error(f"Request failed with exception: {result}")
126
+ # You can add error handling here if needed
127
+ else:
128
+ results.append(result)
129
+
130
+ # Optional: Add small delay between batches to be respectful to the API
131
+ if i + batch_size < total_requests:
132
+ await asyncio.sleep(0.1)
133
+
134
+ logger.info(f"Completed {len(results)} requests out of {total_requests}")
135
  return results
136
 
137
  def process_and_merge_data(results: list) -> str:
 
158
  return json.dumps(list(merged_data.values()), indent=4)
159
 
160
 
161
+ # --- Gradio Backend Function ---
162
 
163
  async def run_backend_processing(tickers_text: str, progress=gr.Progress(track_tqdm=True)):
164
  """The main function to be called by the Gradio interface."""
 
183
  return
184
 
185
  today_str = date.today().strftime("%Y-%m-%d")
186
+ intervals = [1440, 15]
187
+ total_requests = len(tickers) * len(intervals)
188
+
189
+ yield f"Starting processing for {len(tickers)} tickers: {tickers}\nTotal API requests: {total_requests}", "{}"
190
 
191
  try:
192
  time_range = get_time_range_in_unix_ms(today_str, today_str)
193
  start_time, end_time = time_range["start_timestamp_ms"], time_range["end_timestamp_ms"]
 
194
 
195
+ yield f"Fetching data from API in batches of 30 (Total: {total_requests} requests)...", "{}"
196
+ start_fetch_time = datetime.now()
197
+
198
  results = await main_task(tickers, start_time, end_time, intervals, progress)
199
 
200
+ fetch_duration = (datetime.now() - start_fetch_time).total_seconds()
201
+ yield f"Data fetching completed in {fetch_duration:.2f} seconds. Processing and merging data...", "{}"
202
+
203
  processed_json = process_and_merge_data(results)
204
 
205
+ total_duration = (datetime.now() - start_fetch_time).total_seconds()
206
+ yield f"Processing complete in {total_duration:.2f} seconds. Successfully processed {len(results)} API responses.", processed_json
207
 
208
  except Exception as e:
209
  logger.error(f"An unhandled error occurred: {e}")
210
  yield f"An error occurred: {e}", "{}"
211
 
212
 
213
+ # --- Gradio UI ---
214
 
215
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
216
+ gr.Markdown("## Stock Data Backend Processor (Optimized)")
217
+ gr.Markdown("Enter a list of stock tickers in Python list format. The app will make concurrent API requests in batches of 30.")
218
 
219
  with gr.Row():
220
  with gr.Column(scale=1):
 
221
  tickers_input = gr.Textbox(
222
+ lines=2,
223
  label='Enter Tickers as a List',
224
+ value='["RELIANCE", "INFY", "TCS"]',
225
+ placeholder='["TICKER1", "TICKER2", "TICKER3"]'
226
  )
227
  start_button = gr.Button("Start Processing", variant="primary")
228
+
229
+ # Add some helpful info
230
+ gr.Markdown("**Note:** Each ticker makes 2 API calls (1440m and 15m intervals)")
231
 
232
  with gr.Column(scale=2):
233
+ logs_output = gr.Textbox(label="Logs & Progress", lines=15, interactive=False)
234
  json_output = gr.JSON(label="Processed JSON Output")
235
 
236
  # --- Triggers ---
 
237
  start_button.click(
238
  fn=run_backend_processing,
239
  inputs=[tickers_input],