Spaces:
Sleeping
Sleeping
Refactor code structure for improved readability and maintainability
Browse files- api/index.py +132 -62
- logs/api.log +774 -0
- tests/run_tests.sh +1 -1
- tests/test_api.py +80 -6
api/index.py
CHANGED
@@ -64,6 +64,9 @@ class TickerData(Base):
|
|
64 |
low: Mapped[float] = mapped_column(Float, nullable=False)
|
65 |
close: Mapped[float] = mapped_column(Float, nullable=False)
|
66 |
volume: Mapped[int] = mapped_column(Integer, nullable=False)
|
|
|
|
|
|
|
67 |
created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
|
68 |
|
69 |
__table_args__ = (
|
@@ -114,12 +117,16 @@ class TickerDataResponse(BaseModel):
|
|
114 |
low: float
|
115 |
close: float
|
116 |
volume: int
|
|
|
|
|
|
|
117 |
created_at: datetime
|
118 |
|
119 |
|
120 |
class DownloadDataRequest(BaseModel):
|
121 |
tickers: Optional[List[str]] = Field(default=None, description="Specific tickers to download. If not provided, downloads all available tickers")
|
122 |
force_refresh: bool = Field(default=False, description="Force refresh even if data exists")
|
|
|
123 |
|
124 |
|
125 |
class DownloadDataResponse(BaseModel):
|
@@ -170,7 +177,8 @@ class Config:
|
|
170 |
with open(config_path, 'r') as f:
|
171 |
return yaml.safe_load(f)
|
172 |
except FileNotFoundError:
|
173 |
-
logging.
|
|
|
174 |
return self._get_default_config()
|
175 |
|
176 |
def _get_default_config(self):
|
@@ -208,6 +216,8 @@ class Config:
|
|
208 |
handlers=handlers,
|
209 |
datefmt='%Y-%m-%d %H:%M:%S'
|
210 |
)
|
|
|
|
|
211 |
|
212 |
@property
|
213 |
def database_url(self) -> str:
|
@@ -253,11 +263,11 @@ class TickerService:
|
|
253 |
|
254 |
df = next((table for table in tables if all(col in table.columns for col in columns_needed)), None)
|
255 |
if df is None:
|
256 |
-
self.logger.error(f"
|
257 |
return []
|
258 |
|
259 |
entries = df[columns_needed].dropna(subset=[ticker_column])
|
260 |
-
self.logger.info(f"
|
261 |
|
262 |
results: List[tuple[str, str, Optional[str], Optional[str]]] = []
|
263 |
for _, row in entries.iterrows():
|
@@ -268,7 +278,7 @@ class TickerService:
|
|
268 |
results.append((ticker, name, sector, subindustry))
|
269 |
return results
|
270 |
except Exception as e:
|
271 |
-
self.logger.error(f"
|
272 |
return []
|
273 |
|
274 |
|
@@ -307,7 +317,7 @@ class TickerService:
|
|
307 |
last = pytz.UTC.localize(last)
|
308 |
delta = now - last
|
309 |
if delta.total_seconds() < 86400:
|
310 |
-
self.logger.info(f"
|
311 |
from sqlalchemy import func
|
312 |
total_tickers = await session.scalar(select(func.count()).select_from(Ticker))
|
313 |
sp500_count = await session.scalar(select(func.count()).select_from(Ticker).where(Ticker.is_sp500 == 1))
|
@@ -360,17 +370,11 @@ class TickerService:
|
|
360 |
"nasdaq100_count": len(nasdaq_list),
|
361 |
"updated_at": current_time
|
362 |
}
|
363 |
-
self.logger.info(
|
364 |
-
"Tickers table updated: total=%d, sp500=%d, nasdaq100=%d at %s",
|
365 |
-
result["total_tickers"],
|
366 |
-
result["sp500_count"],
|
367 |
-
result["nasdaq100_count"],
|
368 |
-
result["updated_at"].isoformat()
|
369 |
-
)
|
370 |
return result
|
371 |
except Exception as e:
|
372 |
await session.rollback()
|
373 |
-
self.logger.error(f"
|
374 |
raise
|
375 |
|
376 |
|
@@ -379,6 +383,31 @@ class YFinanceService:
|
|
379 |
self.config = config
|
380 |
self.logger = logging.getLogger(__name__)
|
381 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
382 |
async def check_tickers_freshness(self, session: AsyncSession) -> bool:
|
383 |
"""
|
384 |
Check if tickers were updated within the last week (7 days).
|
@@ -392,7 +421,7 @@ class YFinanceService:
|
|
392 |
last_update = result.scalar()
|
393 |
|
394 |
if not last_update:
|
395 |
-
self.logger.info("
|
396 |
return False
|
397 |
|
398 |
# Ensure timezone awareness
|
@@ -402,11 +431,11 @@ class YFinanceService:
|
|
402 |
delta = now - last_update
|
403 |
is_fresh = delta.total_seconds() < (7 * 24 * 3600) # 7 days
|
404 |
|
405 |
-
self.logger.info(f"
|
406 |
return is_fresh
|
407 |
|
408 |
except Exception as e:
|
409 |
-
self.logger.error(f"
|
410 |
return False
|
411 |
|
412 |
async def check_ticker_data_freshness(self, session: AsyncSession) -> bool:
|
@@ -422,7 +451,7 @@ class YFinanceService:
|
|
422 |
last_update = result.scalar()
|
423 |
|
424 |
if not last_update:
|
425 |
-
self.logger.info("
|
426 |
return False
|
427 |
|
428 |
# Ensure timezone awareness
|
@@ -432,11 +461,11 @@ class YFinanceService:
|
|
432 |
delta = now - last_update
|
433 |
is_fresh = delta.total_seconds() < (24 * 3600) # 24 hours
|
434 |
|
435 |
-
self.logger.info(f"
|
436 |
return is_fresh
|
437 |
|
438 |
except Exception as e:
|
439 |
-
self.logger.error(f"
|
440 |
return False
|
441 |
|
442 |
async def clear_and_bulk_insert_ticker_data(self, session: AsyncSession, ticker_list: List[str]) -> Dict[str, Any]:
|
@@ -448,20 +477,20 @@ class YFinanceService:
|
|
448 |
# Start timing for total end-to-end process
|
449 |
total_start_time = time.perf_counter()
|
450 |
|
451 |
-
self.logger.info(f"
|
452 |
|
453 |
# Start timing for data download
|
454 |
download_start_time = time.perf_counter()
|
455 |
|
456 |
# Download data for all tickers at once using period
|
457 |
-
data = yf.download(ticker_list, period='
|
458 |
|
459 |
download_end_time = time.perf_counter()
|
460 |
download_duration = download_end_time - download_start_time
|
461 |
-
self.logger.info(f"
|
462 |
|
463 |
if data.empty:
|
464 |
-
self.logger.warning("
|
465 |
return {
|
466 |
"created": 0,
|
467 |
"updated": 0,
|
@@ -472,11 +501,11 @@ class YFinanceService:
|
|
472 |
db_start_time = time.perf_counter()
|
473 |
|
474 |
# Clear all existing ticker data
|
475 |
-
self.logger.info("
|
476 |
clear_start = time.perf_counter()
|
477 |
await session.execute(delete(TickerData))
|
478 |
clear_end = time.perf_counter()
|
479 |
-
self.logger.info(f"
|
480 |
|
481 |
# Prepare data for bulk insert
|
482 |
current_time = datetime.now(pytz.UTC)
|
@@ -491,7 +520,11 @@ class YFinanceService:
|
|
491 |
if len(ticker_list) == 1:
|
492 |
# Single ticker case - data is not grouped
|
493 |
ticker = ticker_list[0]
|
494 |
-
|
|
|
|
|
|
|
|
|
495 |
if pd.isna(row['Close']):
|
496 |
continue
|
497 |
|
@@ -504,6 +537,9 @@ class YFinanceService:
|
|
504 |
'low': float(row['Low']),
|
505 |
'close': float(row['Close']),
|
506 |
'volume': int(row['Volume']),
|
|
|
|
|
|
|
507 |
'created_at': current_time
|
508 |
}
|
509 |
all_records.append(record)
|
@@ -511,14 +547,17 @@ class YFinanceService:
|
|
511 |
# Multiple tickers case - data is grouped by ticker
|
512 |
for ticker in ticker_list:
|
513 |
if ticker not in data.columns.get_level_values(0):
|
514 |
-
self.logger.warning(f"
|
515 |
continue
|
516 |
|
517 |
ticker_data = data[ticker]
|
518 |
if ticker_data.empty:
|
519 |
continue
|
|
|
|
|
|
|
520 |
|
521 |
-
for date_idx, row in
|
522 |
if pd.isna(row['Close']):
|
523 |
continue
|
524 |
|
@@ -531,6 +570,9 @@ class YFinanceService:
|
|
531 |
'low': float(row['Low']),
|
532 |
'close': float(row['Close']),
|
533 |
'volume': int(row['Volume']),
|
|
|
|
|
|
|
534 |
'created_at': current_time
|
535 |
}
|
536 |
all_records.append(record)
|
@@ -540,7 +582,7 @@ class YFinanceService:
|
|
540 |
total_records = len(all_records)
|
541 |
inserted_count = 0
|
542 |
|
543 |
-
self.logger.info(f"
|
544 |
|
545 |
for i in range(0, total_records, chunk_size):
|
546 |
chunk = all_records[i:i + chunk_size]
|
@@ -552,24 +594,24 @@ class YFinanceService:
|
|
552 |
|
553 |
chunk_end = time.perf_counter()
|
554 |
inserted_count += len(chunk)
|
555 |
-
self.logger.info(f"
|
556 |
|
557 |
# Commit all changes
|
558 |
commit_start = time.perf_counter()
|
559 |
await session.commit()
|
560 |
commit_end = time.perf_counter()
|
561 |
-
self.logger.info(f"
|
562 |
|
563 |
db_end_time = time.perf_counter()
|
564 |
db_duration = db_end_time - db_start_time
|
565 |
-
self.logger.info(f"
|
566 |
|
567 |
# Calculate total end-to-end duration
|
568 |
total_end_time = time.perf_counter()
|
569 |
total_duration = total_end_time - total_start_time
|
570 |
-
self.logger.info(f"
|
571 |
|
572 |
-
self.logger.info(f"
|
573 |
return {
|
574 |
"created": inserted_count,
|
575 |
"updated": 0,
|
@@ -581,18 +623,19 @@ class YFinanceService:
|
|
581 |
|
582 |
except Exception as e:
|
583 |
await session.rollback()
|
584 |
-
self.logger.error(f"
|
585 |
raise
|
586 |
|
587 |
-
async def download_all_tickers_data(self, session: AsyncSession, ticker_list: Optional[List[str]] = None) -> Dict[str, Any]:
|
588 |
"""
|
589 |
-
Download data for all or specified tickers for the last
|
590 |
Uses smart strategy: checks data freshness, if > 24h, clears DB and bulk inserts new data.
|
|
|
591 |
"""
|
592 |
try:
|
593 |
# Check ticker freshness and update if needed
|
594 |
if not await self.check_tickers_freshness(session):
|
595 |
-
self.logger.info("
|
596 |
ticker_service = TickerService(self.config)
|
597 |
await ticker_service.update_tickers_in_db(session, force_refresh=True)
|
598 |
|
@@ -605,7 +648,7 @@ class YFinanceService:
|
|
605 |
valid_tickers = [row[0] for row in result.fetchall()]
|
606 |
invalid_tickers = set(ticker_list) - set(valid_tickers)
|
607 |
if invalid_tickers:
|
608 |
-
self.logger.warning(f"
|
609 |
tickers_to_process = valid_tickers
|
610 |
else:
|
611 |
# Get all tickers from database
|
@@ -621,19 +664,31 @@ class YFinanceService:
|
|
621 |
"message": "No valid tickers found to process"
|
622 |
}
|
623 |
|
624 |
-
# Check if ticker data is fresh (less than 24h old)
|
625 |
-
if await self.check_ticker_data_freshness(session):
|
626 |
-
|
627 |
-
|
628 |
-
|
629 |
-
|
630 |
-
|
631 |
-
|
632 |
-
|
633 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
634 |
|
635 |
# Data is stale - use bulk refresh strategy
|
636 |
-
self.logger.info("
|
637 |
result = await self.clear_and_bulk_insert_ticker_data(session, tickers_to_process)
|
638 |
total_created = result["created"]
|
639 |
total_updated = result["updated"]
|
@@ -648,7 +703,7 @@ class YFinanceService:
|
|
648 |
}
|
649 |
|
650 |
except Exception as e:
|
651 |
-
self.logger.error(f"
|
652 |
raise
|
653 |
|
654 |
|
@@ -795,11 +850,12 @@ async def lifespan(app: FastAPI):
|
|
795 |
# Startup
|
796 |
await database.create_tables()
|
797 |
await task_manager.create_table_if_not_exists()
|
798 |
-
logging.
|
|
|
799 |
yield
|
800 |
# Shutdown
|
801 |
await database.engine.dispose()
|
802 |
-
|
803 |
|
804 |
# Create FastAPI app
|
805 |
app = FastAPI(
|
@@ -953,7 +1009,8 @@ async def get_tickers(
|
|
953 |
for t in tickers
|
954 |
]
|
955 |
except Exception as e:
|
956 |
-
logging.
|
|
|
957 |
raise HTTPException(status_code=500, detail="Failed to fetch tickers")
|
958 |
|
959 |
|
@@ -1011,7 +1068,8 @@ async def update_tickers(
|
|
1011 |
**result
|
1012 |
)
|
1013 |
except Exception as e:
|
1014 |
-
logging.
|
|
|
1015 |
raise HTTPException(status_code=500, detail=f"Failed to update tickers: {str(e)}")
|
1016 |
|
1017 |
|
@@ -1157,28 +1215,33 @@ async def delete_old_tasks(api_key: str = Depends(verify_api_key)):
|
|
1157 |
|
1158 |
@app.post("/data/download-all", response_model=DownloadDataResponse)
|
1159 |
async def download_all_tickers_data(
|
|
|
1160 |
session: AsyncSession = Depends(get_db_session),
|
1161 |
api_key: str = Depends(verify_api_key)
|
1162 |
):
|
1163 |
"""
|
1164 |
-
Download daily ticker data for the last
|
1165 |
|
1166 |
**Logic**:
|
1167 |
- Automatically downloads data for all tickers stored in the tickers table
|
1168 |
- Checks if tickers were updated within the last week, updates if needed
|
1169 |
-
- Only downloads if ticker data is older than 24 hours
|
1170 |
-
- Downloads daily data for the last
|
|
|
1171 |
- Uses bulk delete and insert strategy for optimal performance
|
1172 |
- Returns summary with counts and date range
|
1173 |
|
1174 |
**Args**:
|
|
|
1175 |
- **session**: AsyncSession (DB session, injected)
|
1176 |
- **api_key**: str (API key for authentication, injected)
|
1177 |
|
1178 |
**Example request:**
|
1179 |
```bash
|
1180 |
curl -X POST "http://localhost:${PORT}/data/download-all" \
|
1181 |
-
-H "Authorization: Bearer your_api_key"
|
|
|
|
|
1182 |
```
|
1183 |
|
1184 |
**Example response:**
|
@@ -1201,7 +1264,9 @@ async def download_all_tickers_data(
|
|
1201 |
# Use existing service without specifying ticker list (downloads all)
|
1202 |
result = await yfinance_service.download_all_tickers_data(
|
1203 |
session,
|
1204 |
-
ticker_list=
|
|
|
|
|
1205 |
)
|
1206 |
|
1207 |
return DownloadDataResponse(
|
@@ -1215,7 +1280,8 @@ async def download_all_tickers_data(
|
|
1215 |
)
|
1216 |
|
1217 |
except Exception as e:
|
1218 |
-
logging.
|
|
|
1219 |
raise HTTPException(status_code=500, detail=f"Failed to download all ticker data: {str(e)}")
|
1220 |
|
1221 |
|
@@ -1285,6 +1351,9 @@ async def get_ticker_data(
|
|
1285 |
low=data.low,
|
1286 |
close=data.close,
|
1287 |
volume=data.volume,
|
|
|
|
|
|
|
1288 |
created_at=data.created_at
|
1289 |
)
|
1290 |
for data in ticker_data
|
@@ -1293,7 +1362,8 @@ async def get_ticker_data(
|
|
1293 |
except HTTPException:
|
1294 |
raise
|
1295 |
except Exception as e:
|
1296 |
-
logging.
|
|
|
1297 |
raise HTTPException(status_code=500, detail="Failed to fetch ticker data")
|
1298 |
|
1299 |
|
|
|
64 |
low: Mapped[float] = mapped_column(Float, nullable=False)
|
65 |
close: Mapped[float] = mapped_column(Float, nullable=False)
|
66 |
volume: Mapped[int] = mapped_column(Integer, nullable=False)
|
67 |
+
sma_fast: Mapped[Optional[float]] = mapped_column(Float, nullable=True) # SMA 10
|
68 |
+
sma_med: Mapped[Optional[float]] = mapped_column(Float, nullable=True) # SMA 20
|
69 |
+
sma_slow: Mapped[Optional[float]] = mapped_column(Float, nullable=True) # SMA 50
|
70 |
created_at: Mapped[datetime] = mapped_column(DateTime, nullable=False)
|
71 |
|
72 |
__table_args__ = (
|
|
|
117 |
low: float
|
118 |
close: float
|
119 |
volume: int
|
120 |
+
sma_fast: Optional[float] = None # SMA 10
|
121 |
+
sma_med: Optional[float] = None # SMA 20
|
122 |
+
sma_slow: Optional[float] = None # SMA 50
|
123 |
created_at: datetime
|
124 |
|
125 |
|
126 |
class DownloadDataRequest(BaseModel):
|
127 |
tickers: Optional[List[str]] = Field(default=None, description="Specific tickers to download. If not provided, downloads all available tickers")
|
128 |
force_refresh: bool = Field(default=False, description="Force refresh even if data exists")
|
129 |
+
force_indicators: bool = Field(default=False, description="Force calculation of technical indicators even if data is fresh")
|
130 |
|
131 |
|
132 |
class DownloadDataResponse(BaseModel):
|
|
|
177 |
with open(config_path, 'r') as f:
|
178 |
return yaml.safe_load(f)
|
179 |
except FileNotFoundError:
|
180 |
+
logger = logging.getLogger(__name__)
|
181 |
+
logger.warning(f"config_file_not_found path={config_path} using_defaults=true")
|
182 |
return self._get_default_config()
|
183 |
|
184 |
def _get_default_config(self):
|
|
|
216 |
handlers=handlers,
|
217 |
datefmt='%Y-%m-%d %H:%M:%S'
|
218 |
)
|
219 |
+
self.logger = logging.getLogger(__name__)
|
220 |
+
self.logger.info(f"logging_configured level={log_config.get('level', 'INFO')} hf_spaces={os.getenv('SPACE_ID') is not None}")
|
221 |
|
222 |
@property
|
223 |
def database_url(self) -> str:
|
|
|
263 |
|
264 |
df = next((table for table in tables if all(col in table.columns for col in columns_needed)), None)
|
265 |
if df is None:
|
266 |
+
self.logger.error(f"wikipedia_parsing_failed url={url} required_columns={columns_needed}")
|
267 |
return []
|
268 |
|
269 |
entries = df[columns_needed].dropna(subset=[ticker_column])
|
270 |
+
self.logger.info(f"wikipedia_data_fetched url={url} rows={len(entries)}")
|
271 |
|
272 |
results: List[tuple[str, str, Optional[str], Optional[str]]] = []
|
273 |
for _, row in entries.iterrows():
|
|
|
278 |
results.append((ticker, name, sector, subindustry))
|
279 |
return results
|
280 |
except Exception as e:
|
281 |
+
self.logger.error(f"wikipedia_fetch_failed url={url} error={str(e)}")
|
282 |
return []
|
283 |
|
284 |
|
|
|
317 |
last = pytz.UTC.localize(last)
|
318 |
delta = now - last
|
319 |
if delta.total_seconds() < 86400:
|
320 |
+
self.logger.info(f"tickers_update_skipped last_update={last.isoformat()} reason=fresh_data")
|
321 |
from sqlalchemy import func
|
322 |
total_tickers = await session.scalar(select(func.count()).select_from(Ticker))
|
323 |
sp500_count = await session.scalar(select(func.count()).select_from(Ticker).where(Ticker.is_sp500 == 1))
|
|
|
370 |
"nasdaq100_count": len(nasdaq_list),
|
371 |
"updated_at": current_time
|
372 |
}
|
373 |
+
self.logger.info(f"tickers_updated total={result['total_tickers']} sp500={result['sp500_count']} nasdaq100={result['nasdaq100_count']} timestamp={result['updated_at'].isoformat()}")
|
|
|
|
|
|
|
|
|
|
|
|
|
374 |
return result
|
375 |
except Exception as e:
|
376 |
await session.rollback()
|
377 |
+
self.logger.error(f"tickers_update_failed error={str(e)}")
|
378 |
raise
|
379 |
|
380 |
|
|
|
383 |
self.config = config
|
384 |
self.logger = logging.getLogger(__name__)
|
385 |
|
386 |
+
def calculate_technical_indicators(self, df: pd.DataFrame) -> pd.DataFrame:
|
387 |
+
"""
|
388 |
+
Calculate technical indicators for a ticker's data.
|
389 |
+
Adds SMA columns: sma_fast (10), sma_med (20), sma_slow (50)
|
390 |
+
"""
|
391 |
+
if df.empty or 'Close' not in df.columns:
|
392 |
+
return df
|
393 |
+
|
394 |
+
start_time = time.perf_counter()
|
395 |
+
records_count = len(df)
|
396 |
+
|
397 |
+
# Sort by date to ensure proper calculation
|
398 |
+
df = df.sort_index()
|
399 |
+
|
400 |
+
# Calculate Simple Moving Averages
|
401 |
+
df['sma_fast'] = df['Close'].rolling(window=10, min_periods=10).mean()
|
402 |
+
df['sma_med'] = df['Close'].rolling(window=20, min_periods=20).mean()
|
403 |
+
df['sma_slow'] = df['Close'].rolling(window=50, min_periods=50).mean()
|
404 |
+
|
405 |
+
end_time = time.perf_counter()
|
406 |
+
duration = end_time - start_time
|
407 |
+
self.logger.info(f"technical_indicators_calculated records={records_count} duration_ms={duration*1000:.2f}")
|
408 |
+
|
409 |
+
return df
|
410 |
+
|
411 |
async def check_tickers_freshness(self, session: AsyncSession) -> bool:
|
412 |
"""
|
413 |
Check if tickers were updated within the last week (7 days).
|
|
|
421 |
last_update = result.scalar()
|
422 |
|
423 |
if not last_update:
|
424 |
+
self.logger.info("ticker_freshness_check result=no_tickers_found")
|
425 |
return False
|
426 |
|
427 |
# Ensure timezone awareness
|
|
|
431 |
delta = now - last_update
|
432 |
is_fresh = delta.total_seconds() < (7 * 24 * 3600) # 7 days
|
433 |
|
434 |
+
self.logger.info(f"ticker_freshness_check last_update={last_update.isoformat()} is_fresh={is_fresh}")
|
435 |
return is_fresh
|
436 |
|
437 |
except Exception as e:
|
438 |
+
self.logger.error(f"ticker_freshness_check_failed error={str(e)}")
|
439 |
return False
|
440 |
|
441 |
async def check_ticker_data_freshness(self, session: AsyncSession) -> bool:
|
|
|
451 |
last_update = result.scalar()
|
452 |
|
453 |
if not last_update:
|
454 |
+
self.logger.info("ticker_data_freshness_check result=no_data_found")
|
455 |
return False
|
456 |
|
457 |
# Ensure timezone awareness
|
|
|
461 |
delta = now - last_update
|
462 |
is_fresh = delta.total_seconds() < (24 * 3600) # 24 hours
|
463 |
|
464 |
+
self.logger.info(f"ticker_data_freshness_check last_update={last_update.isoformat()} is_fresh={is_fresh}")
|
465 |
return is_fresh
|
466 |
|
467 |
except Exception as e:
|
468 |
+
self.logger.error(f"ticker_data_freshness_check_failed error={str(e)}")
|
469 |
return False
|
470 |
|
471 |
async def clear_and_bulk_insert_ticker_data(self, session: AsyncSession, ticker_list: List[str]) -> Dict[str, Any]:
|
|
|
477 |
# Start timing for total end-to-end process
|
478 |
total_start_time = time.perf_counter()
|
479 |
|
480 |
+
self.logger.info(f"bulk_refresh_started tickers_count={len(ticker_list)} operation=clear_and_insert")
|
481 |
|
482 |
# Start timing for data download
|
483 |
download_start_time = time.perf_counter()
|
484 |
|
485 |
# Download data for all tickers at once using period
|
486 |
+
data = yf.download(ticker_list, period='3mo', group_by='ticker', progress=True, auto_adjust=True)
|
487 |
|
488 |
download_end_time = time.perf_counter()
|
489 |
download_duration = download_end_time - download_start_time
|
490 |
+
self.logger.info(f"data_download_completed tickers_count={len(ticker_list)} duration_ms={download_duration*1000:.2f}")
|
491 |
|
492 |
if data.empty:
|
493 |
+
self.logger.warning("data_download_empty reason=no_data_for_any_tickers")
|
494 |
return {
|
495 |
"created": 0,
|
496 |
"updated": 0,
|
|
|
501 |
db_start_time = time.perf_counter()
|
502 |
|
503 |
# Clear all existing ticker data
|
504 |
+
self.logger.info("database_clear_started operation=delete_all_ticker_data")
|
505 |
clear_start = time.perf_counter()
|
506 |
await session.execute(delete(TickerData))
|
507 |
clear_end = time.perf_counter()
|
508 |
+
self.logger.info(f"database_clear_completed duration_ms={(clear_end - clear_start)*1000:.2f}")
|
509 |
|
510 |
# Prepare data for bulk insert
|
511 |
current_time = datetime.now(pytz.UTC)
|
|
|
520 |
if len(ticker_list) == 1:
|
521 |
# Single ticker case - data is not grouped
|
522 |
ticker = ticker_list[0]
|
523 |
+
|
524 |
+
# Calculate technical indicators
|
525 |
+
data_with_indicators = self.calculate_technical_indicators(data)
|
526 |
+
|
527 |
+
for date_idx, row in data_with_indicators.iterrows():
|
528 |
if pd.isna(row['Close']):
|
529 |
continue
|
530 |
|
|
|
537 |
'low': float(row['Low']),
|
538 |
'close': float(row['Close']),
|
539 |
'volume': int(row['Volume']),
|
540 |
+
'sma_fast': float(row['sma_fast']) if pd.notna(row['sma_fast']) else None,
|
541 |
+
'sma_med': float(row['sma_med']) if pd.notna(row['sma_med']) else None,
|
542 |
+
'sma_slow': float(row['sma_slow']) if pd.notna(row['sma_slow']) else None,
|
543 |
'created_at': current_time
|
544 |
}
|
545 |
all_records.append(record)
|
|
|
547 |
# Multiple tickers case - data is grouped by ticker
|
548 |
for ticker in ticker_list:
|
549 |
if ticker not in data.columns.get_level_values(0):
|
550 |
+
self.logger.warning(f"ticker_data_missing ticker={ticker} reason=not_in_downloaded_data")
|
551 |
continue
|
552 |
|
553 |
ticker_data = data[ticker]
|
554 |
if ticker_data.empty:
|
555 |
continue
|
556 |
+
|
557 |
+
# Calculate technical indicators for this ticker
|
558 |
+
ticker_data_with_indicators = self.calculate_technical_indicators(ticker_data)
|
559 |
|
560 |
+
for date_idx, row in ticker_data_with_indicators.iterrows():
|
561 |
if pd.isna(row['Close']):
|
562 |
continue
|
563 |
|
|
|
570 |
'low': float(row['Low']),
|
571 |
'close': float(row['Close']),
|
572 |
'volume': int(row['Volume']),
|
573 |
+
'sma_fast': float(row['sma_fast']) if pd.notna(row['sma_fast']) else None,
|
574 |
+
'sma_med': float(row['sma_med']) if pd.notna(row['sma_med']) else None,
|
575 |
+
'sma_slow': float(row['sma_slow']) if pd.notna(row['sma_slow']) else None,
|
576 |
'created_at': current_time
|
577 |
}
|
578 |
all_records.append(record)
|
|
|
582 |
total_records = len(all_records)
|
583 |
inserted_count = 0
|
584 |
|
585 |
+
self.logger.info(f"database_insert_started total_records={total_records} chunk_size={chunk_size}")
|
586 |
|
587 |
for i in range(0, total_records, chunk_size):
|
588 |
chunk = all_records[i:i + chunk_size]
|
|
|
594 |
|
595 |
chunk_end = time.perf_counter()
|
596 |
inserted_count += len(chunk)
|
597 |
+
self.logger.info(f"database_chunk_inserted chunk={i//chunk_size + 1}/{(total_records + chunk_size - 1)//chunk_size} records={len(chunk)} duration_ms={(chunk_end - chunk_start)*1000:.2f}")
|
598 |
|
599 |
# Commit all changes
|
600 |
commit_start = time.perf_counter()
|
601 |
await session.commit()
|
602 |
commit_end = time.perf_counter()
|
603 |
+
self.logger.info(f"database_commit_completed duration_ms={(commit_end - commit_start)*1000:.2f}")
|
604 |
|
605 |
db_end_time = time.perf_counter()
|
606 |
db_duration = db_end_time - db_start_time
|
607 |
+
self.logger.info(f"database_operations_completed records_inserted={inserted_count} duration_ms={db_duration*1000:.2f}")
|
608 |
|
609 |
# Calculate total end-to-end duration
|
610 |
total_end_time = time.perf_counter()
|
611 |
total_duration = total_end_time - total_start_time
|
612 |
+
self.logger.info(f"bulk_refresh_completed total_duration_ms={total_duration*1000:.2f} download_ms={download_duration*1000:.2f} database_ms={db_duration*1000:.2f}")
|
613 |
|
614 |
+
self.logger.info(f"bulk_refresh_summary records_inserted={inserted_count} operation=completed")
|
615 |
return {
|
616 |
"created": inserted_count,
|
617 |
"updated": 0,
|
|
|
623 |
|
624 |
except Exception as e:
|
625 |
await session.rollback()
|
626 |
+
self.logger.error(f"bulk_refresh_failed error={str(e)}")
|
627 |
raise
|
628 |
|
629 |
+
async def download_all_tickers_data(self, session: AsyncSession, ticker_list: Optional[List[str]] = None, force_refresh: bool = False, force_indicators: bool = False) -> Dict[str, Any]:
|
630 |
"""
|
631 |
+
Download data for all or specified tickers for the last 3 months.
|
632 |
Uses smart strategy: checks data freshness, if > 24h, clears DB and bulk inserts new data.
|
633 |
+
Calculates technical indicators (SMA 10, 20, 50) for all data.
|
634 |
"""
|
635 |
try:
|
636 |
# Check ticker freshness and update if needed
|
637 |
if not await self.check_tickers_freshness(session):
|
638 |
+
self.logger.info("tickers_update_required reason=stale_data")
|
639 |
ticker_service = TickerService(self.config)
|
640 |
await ticker_service.update_tickers_in_db(session, force_refresh=True)
|
641 |
|
|
|
648 |
valid_tickers = [row[0] for row in result.fetchall()]
|
649 |
invalid_tickers = set(ticker_list) - set(valid_tickers)
|
650 |
if invalid_tickers:
|
651 |
+
self.logger.warning(f"invalid_tickers_ignored count={len(invalid_tickers)} tickers={list(invalid_tickers)}")
|
652 |
tickers_to_process = valid_tickers
|
653 |
else:
|
654 |
# Get all tickers from database
|
|
|
664 |
"message": "No valid tickers found to process"
|
665 |
}
|
666 |
|
667 |
+
# Check if ticker data is fresh (less than 24h old) unless force_refresh
|
668 |
+
if not force_refresh and await self.check_ticker_data_freshness(session):
|
669 |
+
if not force_indicators:
|
670 |
+
self.logger.info("data_download_skipped reason=fresh_data age_limit=24h")
|
671 |
+
return {
|
672 |
+
"tickers_processed": len(tickers_to_process),
|
673 |
+
"records_created": 0,
|
674 |
+
"records_updated": 0,
|
675 |
+
"date_range": {"start_date": "", "end_date": ""},
|
676 |
+
"message": f"Data is fresh, no update needed for {len(tickers_to_process)} tickers"
|
677 |
+
}
|
678 |
+
else:
|
679 |
+
# Data is fresh but force_indicators is True - only recalculate indicators
|
680 |
+
self.logger.info("indicators_recalculation_requested reason=force_indicators_flag data_age=fresh")
|
681 |
+
# TODO: Implement indicators-only recalculation
|
682 |
+
return {
|
683 |
+
"tickers_processed": len(tickers_to_process),
|
684 |
+
"records_created": 0,
|
685 |
+
"records_updated": 0,
|
686 |
+
"date_range": {"start_date": "", "end_date": ""},
|
687 |
+
"message": f"Indicators recalculation for {len(tickers_to_process)} tickers (not implemented yet)"
|
688 |
+
}
|
689 |
|
690 |
# Data is stale - use bulk refresh strategy
|
691 |
+
self.logger.info("bulk_refresh_strategy_selected reason=stale_data age_limit=24h")
|
692 |
result = await self.clear_and_bulk_insert_ticker_data(session, tickers_to_process)
|
693 |
total_created = result["created"]
|
694 |
total_updated = result["updated"]
|
|
|
703 |
}
|
704 |
|
705 |
except Exception as e:
|
706 |
+
self.logger.error(f"download_all_tickers_failed error={str(e)}")
|
707 |
raise
|
708 |
|
709 |
|
|
|
850 |
# Startup
|
851 |
await database.create_tables()
|
852 |
await task_manager.create_table_if_not_exists()
|
853 |
+
logger = logging.getLogger(__name__)
|
854 |
+
logger.info("database_lifecycle event=tables_created_verified")
|
855 |
yield
|
856 |
# Shutdown
|
857 |
await database.engine.dispose()
|
858 |
+
logger.info("database_lifecycle event=connections_closed")
|
859 |
|
860 |
# Create FastAPI app
|
861 |
app = FastAPI(
|
|
|
1009 |
for t in tickers
|
1010 |
]
|
1011 |
except Exception as e:
|
1012 |
+
logger = logging.getLogger(__name__)
|
1013 |
+
logger.error(f"endpoint_error endpoint=get_tickers error={str(e)}")
|
1014 |
raise HTTPException(status_code=500, detail="Failed to fetch tickers")
|
1015 |
|
1016 |
|
|
|
1068 |
**result
|
1069 |
)
|
1070 |
except Exception as e:
|
1071 |
+
logger = logging.getLogger(__name__)
|
1072 |
+
logger.error(f"endpoint_error endpoint=update_tickers error={str(e)}")
|
1073 |
raise HTTPException(status_code=500, detail=f"Failed to update tickers: {str(e)}")
|
1074 |
|
1075 |
|
|
|
1215 |
|
1216 |
@app.post("/data/download-all", response_model=DownloadDataResponse)
|
1217 |
async def download_all_tickers_data(
|
1218 |
+
request: DownloadDataRequest = DownloadDataRequest(),
|
1219 |
session: AsyncSession = Depends(get_db_session),
|
1220 |
api_key: str = Depends(verify_api_key)
|
1221 |
):
|
1222 |
"""
|
1223 |
+
Download daily ticker data for the last 3 months for ALL tickers in database.
|
1224 |
|
1225 |
**Logic**:
|
1226 |
- Automatically downloads data for all tickers stored in the tickers table
|
1227 |
- Checks if tickers were updated within the last week, updates if needed
|
1228 |
+
- Only downloads if ticker data is older than 24 hours (unless force_refresh=true)
|
1229 |
+
- Downloads daily data for the last 3 months for all available tickers
|
1230 |
+
- Calculates technical indicators: SMA 10 (fast), SMA 20 (med), SMA 50 (slow)
|
1231 |
- Uses bulk delete and insert strategy for optimal performance
|
1232 |
- Returns summary with counts and date range
|
1233 |
|
1234 |
**Args**:
|
1235 |
+
- **request**: DownloadDataRequest (force_refresh, force_indicators flags)
|
1236 |
- **session**: AsyncSession (DB session, injected)
|
1237 |
- **api_key**: str (API key for authentication, injected)
|
1238 |
|
1239 |
**Example request:**
|
1240 |
```bash
|
1241 |
curl -X POST "http://localhost:${PORT}/data/download-all" \
|
1242 |
+
-H "Authorization: Bearer your_api_key" \
|
1243 |
+
-H "Content-Type: application/json" \
|
1244 |
+
-d '{"force_refresh": false, "force_indicators": true}'
|
1245 |
```
|
1246 |
|
1247 |
**Example response:**
|
|
|
1264 |
# Use existing service without specifying ticker list (downloads all)
|
1265 |
result = await yfinance_service.download_all_tickers_data(
|
1266 |
session,
|
1267 |
+
ticker_list=request.tickers, # None means download all tickers
|
1268 |
+
force_refresh=request.force_refresh,
|
1269 |
+
force_indicators=request.force_indicators
|
1270 |
)
|
1271 |
|
1272 |
return DownloadDataResponse(
|
|
|
1280 |
)
|
1281 |
|
1282 |
except Exception as e:
|
1283 |
+
logger = logging.getLogger(__name__)
|
1284 |
+
logger.error(f"endpoint_error endpoint=download_all_tickers error={str(e)}")
|
1285 |
raise HTTPException(status_code=500, detail=f"Failed to download all ticker data: {str(e)}")
|
1286 |
|
1287 |
|
|
|
1351 |
low=data.low,
|
1352 |
close=data.close,
|
1353 |
volume=data.volume,
|
1354 |
+
sma_fast=data.sma_fast,
|
1355 |
+
sma_med=data.sma_med,
|
1356 |
+
sma_slow=data.sma_slow,
|
1357 |
created_at=data.created_at
|
1358 |
)
|
1359 |
for data in ticker_data
|
|
|
1362 |
except HTTPException:
|
1363 |
raise
|
1364 |
except Exception as e:
|
1365 |
+
logger = logging.getLogger(__name__)
|
1366 |
+
logger.error(f"endpoint_error endpoint=get_ticker_data ticker={ticker} error={str(e)}")
|
1367 |
raise HTTPException(status_code=500, detail="Failed to fetch ticker data")
|
1368 |
|
1369 |
|
logs/api.log
ADDED
@@ -0,0 +1,774 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
2025-07-30 20:35:48 [INFO] __main__: logging_configured level=INFO hf_spaces=False
|
2 |
+
2025-07-30 20:35:48 [INFO] __mp_main__: logging_configured level=INFO hf_spaces=False
|
3 |
+
2025-07-30 20:35:48 [INFO] watchfiles.main: 1 change detected
|
4 |
+
2025-07-30 20:35:49 [INFO] index: logging_configured level=INFO hf_spaces=False
|
5 |
+
2025-07-30 20:35:49 [INFO] watchfiles.main: 3 changes detected
|
6 |
+
2025-07-30 20:35:50 [INFO] index: database_lifecycle event=tables_created_verified
|
7 |
+
2025-07-30 20:35:51 [INFO] watchfiles.main: 2 changes detected
|
8 |
+
2025-07-30 20:35:52 [INFO] watchfiles.main: 4 changes detected
|
9 |
+
2025-07-30 20:35:57 [INFO] watchfiles.main: 2 changes detected
|
10 |
+
2025-07-30 20:36:02 [INFO] watchfiles.main: 2 changes detected
|
11 |
+
2025-07-30 20:36:04 [INFO] watchfiles.main: 1 change detected
|
12 |
+
2025-07-30 20:36:05 [INFO] watchfiles.main: 1 change detected
|
13 |
+
2025-07-30 20:36:07 [INFO] watchfiles.main: 2 changes detected
|
14 |
+
2025-07-30 20:36:09 [INFO] watchfiles.main: 1 change detected
|
15 |
+
2025-07-30 20:36:12 [INFO] watchfiles.main: 2 changes detected
|
16 |
+
2025-07-30 20:36:17 [INFO] watchfiles.main: 2 changes detected
|
17 |
+
2025-07-30 20:36:20 [INFO] watchfiles.main: 1 change detected
|
18 |
+
2025-07-30 20:36:20 [INFO] watchfiles.main: 1 change detected
|
19 |
+
2025-07-30 20:36:22 [INFO] watchfiles.main: 2 changes detected
|
20 |
+
2025-07-30 20:36:25 [INFO] watchfiles.main: 1 change detected
|
21 |
+
2025-07-30 20:36:27 [INFO] watchfiles.main: 2 changes detected
|
22 |
+
2025-07-30 20:36:32 [INFO] watchfiles.main: 2 changes detected
|
23 |
+
2025-07-30 20:36:35 [INFO] watchfiles.main: 1 change detected
|
24 |
+
2025-07-30 20:36:35 [INFO] watchfiles.main: 1 change detected
|
25 |
+
2025-07-30 20:36:36 [INFO] watchfiles.main: 1 change detected
|
26 |
+
2025-07-30 20:36:37 [INFO] watchfiles.main: 2 changes detected
|
27 |
+
2025-07-30 20:36:41 [INFO] watchfiles.main: 1 change detected
|
28 |
+
2025-07-30 20:36:43 [INFO] watchfiles.main: 2 changes detected
|
29 |
+
2025-07-30 20:36:48 [INFO] watchfiles.main: 2 changes detected
|
30 |
+
2025-07-30 20:36:51 [INFO] watchfiles.main: 1 change detected
|
31 |
+
2025-07-30 20:36:51 [INFO] watchfiles.main: 1 change detected
|
32 |
+
2025-07-30 20:36:53 [INFO] watchfiles.main: 2 changes detected
|
33 |
+
2025-07-30 20:36:56 [INFO] watchfiles.main: 1 change detected
|
34 |
+
2025-07-30 20:36:58 [INFO] watchfiles.main: 2 changes detected
|
35 |
+
2025-07-30 20:37:03 [INFO] watchfiles.main: 2 changes detected
|
36 |
+
2025-07-30 20:37:06 [INFO] watchfiles.main: 1 change detected
|
37 |
+
2025-07-30 20:37:06 [INFO] watchfiles.main: 1 change detected
|
38 |
+
2025-07-30 20:37:08 [INFO] watchfiles.main: 2 changes detected
|
39 |
+
2025-07-30 20:37:11 [INFO] watchfiles.main: 1 change detected
|
40 |
+
2025-07-30 20:37:11 [INFO] watchfiles.main: 1 change detected
|
41 |
+
2025-07-30 20:37:13 [INFO] watchfiles.main: 2 changes detected
|
42 |
+
2025-07-30 20:37:18 [INFO] watchfiles.main: 2 changes detected
|
43 |
+
2025-07-30 20:37:21 [INFO] watchfiles.main: 1 change detected
|
44 |
+
2025-07-30 20:37:22 [INFO] watchfiles.main: 1 change detected
|
45 |
+
2025-07-30 20:37:24 [INFO] watchfiles.main: 2 changes detected
|
46 |
+
2025-07-30 20:37:26 [INFO] watchfiles.main: 1 change detected
|
47 |
+
2025-07-30 20:37:27 [INFO] watchfiles.main: 1 change detected
|
48 |
+
2025-07-30 20:37:29 [INFO] watchfiles.main: 2 changes detected
|
49 |
+
2025-07-30 20:37:34 [INFO] watchfiles.main: 2 changes detected
|
50 |
+
2025-07-30 20:37:37 [INFO] watchfiles.main: 1 change detected
|
51 |
+
2025-07-30 20:37:37 [INFO] watchfiles.main: 1 change detected
|
52 |
+
2025-07-30 20:37:39 [INFO] watchfiles.main: 2 changes detected
|
53 |
+
2025-07-30 20:37:42 [INFO] watchfiles.main: 1 change detected
|
54 |
+
2025-07-30 20:37:43 [INFO] watchfiles.main: 4 changes detected
|
55 |
+
2025-07-30 20:37:43 [INFO] index: database_lifecycle event=connections_closed
|
56 |
+
2025-07-30 20:37:43 [INFO] watchfiles.main: 1 change detected
|
57 |
+
2025-07-30 20:37:44 [INFO] __mp_main__: logging_configured level=INFO hf_spaces=False
|
58 |
+
2025-07-30 20:37:44 [INFO] index: logging_configured level=INFO hf_spaces=False
|
59 |
+
2025-07-30 20:37:44 [INFO] watchfiles.main: 1 change detected
|
60 |
+
2025-07-30 20:37:44 [INFO] watchfiles.main: 1 change detected
|
61 |
+
2025-07-30 20:37:45 [INFO] watchfiles.main: 2 changes detected
|
62 |
+
2025-07-30 20:37:45 [INFO] index: database_lifecycle event=tables_created_verified
|
63 |
+
2025-07-30 20:37:46 [INFO] watchfiles.main: 3 changes detected
|
64 |
+
2025-07-30 20:37:46 [INFO] index: database_lifecycle event=connections_closed
|
65 |
+
2025-07-30 20:37:47 [INFO] watchfiles.main: 1 change detected
|
66 |
+
2025-07-30 20:37:47 [INFO] __mp_main__: logging_configured level=INFO hf_spaces=False
|
67 |
+
2025-07-30 20:37:47 [INFO] index: logging_configured level=INFO hf_spaces=False
|
68 |
+
2025-07-30 20:37:47 [INFO] watchfiles.main: 1 change detected
|
69 |
+
2025-07-30 20:37:48 [INFO] index: database_lifecycle event=tables_created_verified
|
70 |
+
2025-07-30 20:37:49 [INFO] watchfiles.main: 2 changes detected
|
71 |
+
2025-07-30 20:37:53 [INFO] watchfiles.main: 1 change detected
|
72 |
+
2025-07-30 20:37:55 [INFO] watchfiles.main: 3 changes detected
|
73 |
+
2025-07-30 20:37:58 [INFO] watchfiles.main: 2 changes detected
|
74 |
+
2025-07-30 20:38:00 [INFO] watchfiles.main: 1 change detected
|
75 |
+
2025-07-30 20:38:01 [INFO] watchfiles.main: 1 change detected
|
76 |
+
2025-07-30 20:38:03 [INFO] watchfiles.main: 2 changes detected
|
77 |
+
2025-07-30 20:38:06 [INFO] watchfiles.main: 1 change detected
|
78 |
+
2025-07-30 20:38:06 [INFO] watchfiles.main: 1 change detected
|
79 |
+
2025-07-30 20:38:08 [INFO] watchfiles.main: 2 changes detected
|
80 |
+
2025-07-30 20:38:13 [INFO] watchfiles.main: 2 changes detected
|
81 |
+
2025-07-30 20:38:16 [INFO] watchfiles.main: 1 change detected
|
82 |
+
2025-07-30 20:38:17 [INFO] watchfiles.main: 1 change detected
|
83 |
+
2025-07-30 20:38:19 [INFO] watchfiles.main: 2 changes detected
|
84 |
+
2025-07-30 20:38:23 [INFO] watchfiles.main: 2 changes detected
|
85 |
+
2025-07-30 20:38:27 [INFO] watchfiles.main: 3 changes detected
|
86 |
+
2025-07-30 20:38:27 [INFO] index: database_lifecycle event=connections_closed
|
87 |
+
2025-07-30 20:38:28 [INFO] watchfiles.main: 1 change detected
|
88 |
+
2025-07-30 20:38:28 [INFO] __mp_main__: logging_configured level=INFO hf_spaces=False
|
89 |
+
2025-07-30 20:38:28 [INFO] index: logging_configured level=INFO hf_spaces=False
|
90 |
+
2025-07-30 20:38:28 [INFO] watchfiles.main: 1 change detected
|
91 |
+
2025-07-30 20:38:29 [INFO] watchfiles.main: 2 changes detected
|
92 |
+
2025-07-30 20:38:29 [INFO] index: database_lifecycle event=tables_created_verified
|
93 |
+
2025-07-30 20:38:29 [INFO] watchfiles.main: 3 changes detected
|
94 |
+
2025-07-30 20:38:29 [INFO] index: database_lifecycle event=connections_closed
|
95 |
+
2025-07-30 20:38:30 [INFO] watchfiles.main: 1 change detected
|
96 |
+
2025-07-30 20:38:30 [INFO] __mp_main__: logging_configured level=INFO hf_spaces=False
|
97 |
+
2025-07-30 20:38:30 [INFO] index: logging_configured level=INFO hf_spaces=False
|
98 |
+
2025-07-30 20:38:30 [INFO] watchfiles.main: 1 change detected
|
99 |
+
2025-07-30 20:38:31 [INFO] index: database_lifecycle event=tables_created_verified
|
100 |
+
2025-07-30 20:38:32 [INFO] watchfiles.main: 1 change detected
|
101 |
+
2025-07-30 20:38:32 [INFO] watchfiles.main: 1 change detected
|
102 |
+
2025-07-30 20:38:34 [INFO] watchfiles.main: 2 changes detected
|
103 |
+
2025-07-30 20:38:37 [INFO] watchfiles.main: 1 change detected
|
104 |
+
2025-07-30 20:38:39 [INFO] watchfiles.main: 2 changes detected
|
105 |
+
2025-07-30 20:38:44 [INFO] watchfiles.main: 2 changes detected
|
106 |
+
2025-07-30 20:38:47 [INFO] watchfiles.main: 1 change detected
|
107 |
+
2025-07-30 20:38:47 [INFO] watchfiles.main: 1 change detected
|
108 |
+
2025-07-30 20:38:49 [INFO] watchfiles.main: 2 changes detected
|
109 |
+
2025-07-30 20:38:55 [INFO] watchfiles.main: 2 changes detected
|
110 |
+
2025-07-30 20:39:00 [INFO] watchfiles.main: 2 changes detected
|
111 |
+
2025-07-30 20:39:02 [INFO] watchfiles.main: 1 change detected
|
112 |
+
2025-07-30 20:39:03 [INFO] watchfiles.main: 1 change detected
|
113 |
+
2025-07-30 20:39:04 [INFO] watchfiles.main: 2 changes detected
|
114 |
+
2025-07-30 20:39:08 [INFO] watchfiles.main: 1 change detected
|
115 |
+
2025-07-30 20:39:10 [INFO] watchfiles.main: 2 changes detected
|
116 |
+
2025-07-30 20:39:15 [INFO] watchfiles.main: 2 changes detected
|
117 |
+
2025-07-30 20:39:18 [INFO] watchfiles.main: 1 change detected
|
118 |
+
2025-07-30 20:39:19 [INFO] watchfiles.main: 1 change detected
|
119 |
+
2025-07-30 20:39:21 [INFO] watchfiles.main: 2 changes detected
|
120 |
+
2025-07-30 20:39:23 [INFO] watchfiles.main: 1 change detected
|
121 |
+
2025-07-30 20:39:24 [INFO] watchfiles.main: 1 change detected
|
122 |
+
2025-07-30 20:39:25 [INFO] index: database_lifecycle event=connections_closed
|
123 |
+
2025-07-30 20:42:55 [INFO] __main__: logging_configured level=INFO hf_spaces=False
|
124 |
+
2025-07-30 20:42:55 [INFO] __mp_main__: logging_configured level=INFO hf_spaces=False
|
125 |
+
2025-07-30 20:42:55 [INFO] index: logging_configured level=INFO hf_spaces=False
|
126 |
+
2025-07-30 20:42:56 [INFO] watchfiles.main: 1 change detected
|
127 |
+
2025-07-30 20:42:57 [INFO] index: database_lifecycle event=tables_created_verified
|
128 |
+
2025-07-30 20:42:57 [INFO] watchfiles.main: 2 changes detected
|
129 |
+
2025-07-30 20:43:03 [INFO] watchfiles.main: 2 changes detected
|
130 |
+
2025-07-30 20:43:08 [INFO] watchfiles.main: 2 changes detected
|
131 |
+
2025-07-30 20:43:11 [INFO] watchfiles.main: 1 change detected
|
132 |
+
2025-07-30 20:43:11 [INFO] watchfiles.main: 1 change detected
|
133 |
+
2025-07-30 20:43:13 [INFO] watchfiles.main: 2 changes detected
|
134 |
+
2025-07-30 20:43:16 [INFO] watchfiles.main: 1 change detected
|
135 |
+
2025-07-30 20:43:19 [INFO] watchfiles.main: 2 changes detected
|
136 |
+
2025-07-30 20:43:23 [INFO] index: tickers_update_skipped last_update=2025-07-30T15:35:47+00:00 reason=fresh_data
|
137 |
+
2025-07-30 20:43:24 [INFO] watchfiles.main: 2 changes detected
|
138 |
+
2025-07-30 20:43:24 [INFO] index: ticker_freshness_check last_update=2025-07-30T15:35:47+00:00 is_fresh=True
|
139 |
+
2025-07-30 20:43:24 [INFO] index: ticker_data_freshness_check result=no_data_found
|
140 |
+
2025-07-30 20:43:24 [INFO] index: bulk_refresh_strategy_selected reason=stale_data age_limit=24h
|
141 |
+
2025-07-30 20:43:24 [INFO] index: bulk_refresh_started tickers_count=517 operation=clear_and_insert
|
142 |
+
2025-07-30 20:43:27 [INFO] watchfiles.main: 1 change detected
|
143 |
+
2025-07-30 20:43:27 [INFO] watchfiles.main: 1 change detected
|
144 |
+
2025-07-30 20:43:29 [INFO] watchfiles.main: 2 changes detected
|
145 |
+
2025-07-30 20:43:32 [INFO] watchfiles.main: 1 change detected
|
146 |
+
2025-07-30 20:43:34 [INFO] watchfiles.main: 2 changes detected
|
147 |
+
2025-07-30 20:43:34 [ERROR] yfinance:
|
148 |
+
2 Failed downloads:
|
149 |
+
2025-07-30 20:43:34 [ERROR] yfinance: ['BRK.B']: YFPricesMissingError('possibly delisted; no price data found (period=3mo) (Yahoo error = "No data found, symbol may be delisted")')
|
150 |
+
2025-07-30 20:43:34 [ERROR] yfinance: ['BF.B']: YFPricesMissingError('possibly delisted; no price data found (period=3mo)')
|
151 |
+
2025-07-30 20:43:34 [INFO] index: data_download_completed tickers_count=517 duration_ms=10186.18
|
152 |
+
2025-07-30 20:43:34 [INFO] index: database_clear_started operation=delete_all_ticker_data
|
153 |
+
2025-07-30 20:43:34 [INFO] index: database_clear_completed duration_ms=44.55
|
154 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.46
|
155 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.25
|
156 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
157 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.22
|
158 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.49
|
159 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.32
|
160 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.28
|
161 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
162 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
163 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
164 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
165 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
166 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.25
|
167 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.27
|
168 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.30
|
169 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.26
|
170 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
171 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.22
|
172 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.26
|
173 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
174 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
175 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
176 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.28
|
177 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.25
|
178 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
179 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
180 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
181 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.33
|
182 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.25
|
183 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
184 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
185 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.27
|
186 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
187 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.25
|
188 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.25
|
189 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
190 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.28
|
191 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
192 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
193 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
194 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.39
|
195 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.29
|
196 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.48
|
197 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.28
|
198 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.31
|
199 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.25
|
200 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
201 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
202 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
203 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.29
|
204 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.25
|
205 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.25
|
206 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
207 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
208 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
209 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.30
|
210 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
211 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.26
|
212 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.26
|
213 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
214 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.26
|
215 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.25
|
216 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.43
|
217 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.27
|
218 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
219 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.25
|
220 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
221 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.29
|
222 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.25
|
223 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
224 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
225 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
226 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
227 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
228 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
229 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.25
|
230 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.38
|
231 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.25
|
232 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.25
|
233 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.33
|
234 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.35
|
235 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.25
|
236 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.25
|
237 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.30
|
238 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=1.35
|
239 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.32
|
240 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.27
|
241 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.74
|
242 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.28
|
243 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.25
|
244 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.25
|
245 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.39
|
246 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.27
|
247 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
248 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
249 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.25
|
250 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.56
|
251 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.31
|
252 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.52
|
253 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.28
|
254 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.25
|
255 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
256 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.30
|
257 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.27
|
258 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.56
|
259 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.76
|
260 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.56
|
261 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.30
|
262 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.25
|
263 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
264 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
265 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.36
|
266 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.26
|
267 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.25
|
268 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
269 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.36
|
270 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.27
|
271 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.31
|
272 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.25
|
273 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.61
|
274 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.59
|
275 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.33
|
276 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.30
|
277 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.32
|
278 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.25
|
279 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.25
|
280 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
281 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
282 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
283 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
284 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
285 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
286 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.22
|
287 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
288 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
289 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.35
|
290 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
291 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
292 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.22
|
293 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.27
|
294 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
295 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
296 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
297 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
298 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.29
|
299 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
300 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
301 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
302 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.29
|
303 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.28
|
304 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.29
|
305 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
306 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.22
|
307 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.25
|
308 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.25
|
309 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.22
|
310 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
311 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.30
|
312 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.32
|
313 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
314 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
315 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
316 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.26
|
317 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
318 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
319 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
320 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.36
|
321 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.25
|
322 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
323 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
324 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
325 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.28
|
326 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.28
|
327 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.25
|
328 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
329 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.26
|
330 |
+
2025-07-30 20:43:34 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.30
|
331 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.27
|
332 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.30
|
333 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.26
|
334 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.25
|
335 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
336 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
337 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.34
|
338 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.27
|
339 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
340 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
341 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
342 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.26
|
343 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
344 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
345 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
346 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.38
|
347 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.29
|
348 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.25
|
349 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.25
|
350 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
351 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.28
|
352 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
353 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
354 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
355 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
356 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.31
|
357 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.26
|
358 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
359 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
360 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
361 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.34
|
362 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.27
|
363 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.27
|
364 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.26
|
365 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.30
|
366 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.26
|
367 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
368 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
369 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
370 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.29
|
371 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.25
|
372 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
373 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.29
|
374 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.36
|
375 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.27
|
376 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
377 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
378 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
379 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
380 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
381 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
382 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
383 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
384 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.29
|
385 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.36
|
386 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.27
|
387 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
388 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.35
|
389 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.28
|
390 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.26
|
391 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
392 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
393 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
394 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
395 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
396 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
397 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.55
|
398 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.26
|
399 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.25
|
400 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
401 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
402 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.25
|
403 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
404 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
405 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
406 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
407 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
408 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
409 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.44
|
410 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
411 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.28
|
412 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.28
|
413 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
414 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
415 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
416 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.25
|
417 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
418 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
419 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.25
|
420 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.70
|
421 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.79
|
422 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.28
|
423 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.25
|
424 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.27
|
425 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.25
|
426 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
427 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
428 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.31
|
429 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.25
|
430 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
431 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
432 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
433 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.34
|
434 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.25
|
435 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
436 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
437 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
438 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
439 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.22
|
440 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
441 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
442 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
443 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.22
|
444 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
445 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
446 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
447 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.22
|
448 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
449 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
450 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
451 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.22
|
452 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.22
|
453 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.22
|
454 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.38
|
455 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.28
|
456 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
457 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
458 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.33
|
459 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
460 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
461 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
462 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.22
|
463 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
464 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.22
|
465 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
466 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.22
|
467 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.38
|
468 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.28
|
469 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.25
|
470 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
471 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
472 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
473 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
474 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
475 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
476 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
477 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.30
|
478 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
479 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
480 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
481 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.25
|
482 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
483 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.40
|
484 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.28
|
485 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.25
|
486 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
487 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
488 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
489 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
490 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.32
|
491 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.27
|
492 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.25
|
493 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.25
|
494 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.30
|
495 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.25
|
496 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
497 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
498 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.33
|
499 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.25
|
500 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
501 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
502 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
503 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
504 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
505 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.41
|
506 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.31
|
507 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
508 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
509 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.26
|
510 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.25
|
511 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.31
|
512 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.26
|
513 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
514 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.25
|
515 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.41
|
516 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.25
|
517 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.42
|
518 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.26
|
519 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
520 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.25
|
521 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
522 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
523 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.22
|
524 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
525 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
526 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.22
|
527 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
528 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.22
|
529 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.26
|
530 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.26
|
531 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
532 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
533 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.22
|
534 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.32
|
535 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.26
|
536 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.25
|
537 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.27
|
538 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.32
|
539 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.25
|
540 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
541 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.26
|
542 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.26
|
543 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
544 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.25
|
545 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
546 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
547 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.27
|
548 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
549 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
550 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
551 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.31
|
552 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.25
|
553 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.25
|
554 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
555 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.25
|
556 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.27
|
557 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.26
|
558 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.25
|
559 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
560 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.29
|
561 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.25
|
562 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
563 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.27
|
564 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.38
|
565 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.29
|
566 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
567 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
568 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.31
|
569 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.25
|
570 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.25
|
571 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
572 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
573 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
574 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
575 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
576 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
577 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.46
|
578 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.35
|
579 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
580 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
581 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
582 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.26
|
583 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.26
|
584 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
585 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
586 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
587 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.30
|
588 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.26
|
589 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.56
|
590 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.52
|
591 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.25
|
592 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
593 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.25
|
594 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.51
|
595 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.63
|
596 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.56
|
597 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.65
|
598 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.30
|
599 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.55
|
600 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.51
|
601 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.28
|
602 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.33
|
603 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.25
|
604 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
605 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.28
|
606 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.28
|
607 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
608 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
609 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
610 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
611 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.40
|
612 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.27
|
613 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
614 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
615 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
616 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.42
|
617 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.29
|
618 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.25
|
619 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
620 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.32
|
621 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.25
|
622 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
623 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
624 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
625 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.32
|
626 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.26
|
627 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
628 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.59
|
629 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.38
|
630 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.26
|
631 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.25
|
632 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.25
|
633 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.25
|
634 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.28
|
635 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.25
|
636 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.28
|
637 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
638 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.62
|
639 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.29
|
640 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.25
|
641 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.36
|
642 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.25
|
643 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
644 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
645 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
646 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.77
|
647 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.27
|
648 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.32
|
649 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.25
|
650 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
651 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
652 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
653 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
654 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.25
|
655 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
656 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
657 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.25
|
658 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.30
|
659 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
660 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
661 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.22
|
662 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.22
|
663 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.24
|
664 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
665 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.22
|
666 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.22
|
667 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.22
|
668 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
669 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.23
|
670 |
+
2025-07-30 20:43:35 [INFO] index: technical_indicators_calculated records=63 duration_ms=0.22
|
671 |
+
2025-07-30 20:43:35 [INFO] index: database_insert_started total_records=32445 chunk_size=1000
|
672 |
+
2025-07-30 20:43:35 [INFO] index: database_chunk_inserted chunk=1/33 records=1000 duration_ms=7.77
|
673 |
+
2025-07-30 20:43:35 [INFO] index: database_chunk_inserted chunk=2/33 records=1000 duration_ms=7.73
|
674 |
+
2025-07-30 20:43:35 [INFO] index: database_chunk_inserted chunk=3/33 records=1000 duration_ms=7.91
|
675 |
+
2025-07-30 20:43:35 [INFO] index: database_chunk_inserted chunk=4/33 records=1000 duration_ms=7.71
|
676 |
+
2025-07-30 20:43:35 [INFO] index: database_chunk_inserted chunk=5/33 records=1000 duration_ms=7.79
|
677 |
+
2025-07-30 20:43:35 [INFO] index: database_chunk_inserted chunk=6/33 records=1000 duration_ms=32.24
|
678 |
+
2025-07-30 20:43:35 [INFO] index: database_chunk_inserted chunk=7/33 records=1000 duration_ms=7.71
|
679 |
+
2025-07-30 20:43:35 [INFO] index: database_chunk_inserted chunk=8/33 records=1000 duration_ms=7.78
|
680 |
+
2025-07-30 20:43:35 [INFO] index: database_chunk_inserted chunk=9/33 records=1000 duration_ms=7.58
|
681 |
+
2025-07-30 20:43:35 [INFO] index: database_chunk_inserted chunk=10/33 records=1000 duration_ms=7.62
|
682 |
+
2025-07-30 20:43:35 [INFO] index: database_chunk_inserted chunk=11/33 records=1000 duration_ms=7.93
|
683 |
+
2025-07-30 20:43:35 [INFO] index: database_chunk_inserted chunk=12/33 records=1000 duration_ms=7.68
|
684 |
+
2025-07-30 20:43:35 [INFO] index: database_chunk_inserted chunk=13/33 records=1000 duration_ms=7.82
|
685 |
+
2025-07-30 20:43:35 [INFO] index: database_chunk_inserted chunk=14/33 records=1000 duration_ms=7.69
|
686 |
+
2025-07-30 20:43:35 [INFO] index: database_chunk_inserted chunk=15/33 records=1000 duration_ms=7.82
|
687 |
+
2025-07-30 20:43:35 [INFO] index: database_chunk_inserted chunk=16/33 records=1000 duration_ms=7.34
|
688 |
+
2025-07-30 20:43:35 [INFO] index: database_chunk_inserted chunk=17/33 records=1000 duration_ms=7.88
|
689 |
+
2025-07-30 20:43:35 [INFO] index: database_chunk_inserted chunk=18/33 records=1000 duration_ms=38.16
|
690 |
+
2025-07-30 20:43:35 [INFO] index: database_chunk_inserted chunk=19/33 records=1000 duration_ms=7.70
|
691 |
+
2025-07-30 20:43:35 [INFO] index: database_chunk_inserted chunk=20/33 records=1000 duration_ms=7.70
|
692 |
+
2025-07-30 20:43:35 [INFO] index: database_chunk_inserted chunk=21/33 records=1000 duration_ms=7.60
|
693 |
+
2025-07-30 20:43:35 [INFO] index: database_chunk_inserted chunk=22/33 records=1000 duration_ms=7.99
|
694 |
+
2025-07-30 20:43:35 [INFO] index: database_chunk_inserted chunk=23/33 records=1000 duration_ms=7.71
|
695 |
+
2025-07-30 20:43:35 [INFO] index: database_chunk_inserted chunk=24/33 records=1000 duration_ms=7.76
|
696 |
+
2025-07-30 20:43:35 [INFO] index: database_chunk_inserted chunk=25/33 records=1000 duration_ms=7.62
|
697 |
+
2025-07-30 20:43:35 [INFO] index: database_chunk_inserted chunk=26/33 records=1000 duration_ms=7.79
|
698 |
+
2025-07-30 20:43:35 [INFO] index: database_chunk_inserted chunk=27/33 records=1000 duration_ms=7.64
|
699 |
+
2025-07-30 20:43:35 [INFO] index: database_chunk_inserted chunk=28/33 records=1000 duration_ms=7.69
|
700 |
+
2025-07-30 20:43:35 [INFO] index: database_chunk_inserted chunk=29/33 records=1000 duration_ms=43.56
|
701 |
+
2025-07-30 20:43:35 [INFO] index: database_chunk_inserted chunk=30/33 records=1000 duration_ms=7.76
|
702 |
+
2025-07-30 20:43:35 [INFO] index: database_chunk_inserted chunk=31/33 records=1000 duration_ms=7.67
|
703 |
+
2025-07-30 20:43:35 [INFO] index: database_chunk_inserted chunk=32/33 records=1000 duration_ms=7.81
|
704 |
+
2025-07-30 20:43:35 [INFO] index: database_chunk_inserted chunk=33/33 records=445 duration_ms=3.38
|
705 |
+
2025-07-30 20:43:39 [INFO] watchfiles.main: 2 changes detected
|
706 |
+
2025-07-30 20:43:40 [INFO] index: database_commit_completed duration_ms=4540.73
|
707 |
+
2025-07-30 20:43:40 [INFO] index: database_operations_completed records_inserted=32445 duration_ms=5686.00
|
708 |
+
2025-07-30 20:43:40 [INFO] index: bulk_refresh_completed total_duration_ms=15872.39 download_ms=10186.18 database_ms=5686.00
|
709 |
+
2025-07-30 20:43:40 [INFO] index: bulk_refresh_summary records_inserted=32445 operation=completed
|
710 |
+
2025-07-30 20:43:41 [INFO] index: ticker_freshness_check last_update=2025-07-30T15:35:47+00:00 is_fresh=True
|
711 |
+
2025-07-30 20:43:41 [INFO] index: ticker_data_freshness_check last_update=2025-07-30T18:43:34+00:00 is_fresh=True
|
712 |
+
2025-07-30 20:43:41 [INFO] index: indicators_recalculation_requested reason=force_indicators_flag data_age=fresh
|
713 |
+
2025-07-30 20:43:42 [INFO] watchfiles.main: 1 change detected
|
714 |
+
2025-07-30 20:43:42 [INFO] watchfiles.main: 1 change detected
|
715 |
+
2025-07-30 20:43:45 [INFO] watchfiles.main: 2 changes detected
|
716 |
+
2025-07-30 20:43:47 [INFO] watchfiles.main: 1 change detected
|
717 |
+
2025-07-30 20:43:48 [INFO] watchfiles.main: 1 change detected
|
718 |
+
2025-07-30 20:43:50 [INFO] watchfiles.main: 2 changes detected
|
719 |
+
2025-07-30 20:43:56 [INFO] watchfiles.main: 2 changes detected
|
720 |
+
2025-07-30 20:43:58 [INFO] watchfiles.main: 1 change detected
|
721 |
+
2025-07-30 20:43:58 [INFO] watchfiles.main: 1 change detected
|
722 |
+
2025-07-30 20:44:00 [INFO] watchfiles.main: 2 changes detected
|
723 |
+
2025-07-30 20:44:03 [INFO] watchfiles.main: 1 change detected
|
724 |
+
2025-07-30 20:44:06 [INFO] watchfiles.main: 2 changes detected
|
725 |
+
2025-07-30 20:44:11 [INFO] watchfiles.main: 2 changes detected
|
726 |
+
2025-07-30 20:44:13 [INFO] watchfiles.main: 1 change detected
|
727 |
+
2025-07-30 20:44:13 [INFO] watchfiles.main: 1 change detected
|
728 |
+
2025-07-30 20:44:17 [INFO] watchfiles.main: 2 changes detected
|
729 |
+
2025-07-30 20:44:18 [INFO] watchfiles.main: 1 change detected
|
730 |
+
2025-07-30 20:44:18 [INFO] watchfiles.main: 1 change detected
|
731 |
+
2025-07-30 20:44:22 [INFO] watchfiles.main: 2 changes detected
|
732 |
+
2025-07-30 20:44:28 [INFO] watchfiles.main: 2 changes detected
|
733 |
+
2025-07-30 20:44:29 [INFO] watchfiles.main: 1 change detected
|
734 |
+
2025-07-30 20:44:29 [INFO] watchfiles.main: 1 change detected
|
735 |
+
2025-07-30 20:44:33 [INFO] watchfiles.main: 2 changes detected
|
736 |
+
2025-07-30 20:44:34 [INFO] watchfiles.main: 1 change detected
|
737 |
+
2025-07-30 20:44:34 [INFO] watchfiles.main: 1 change detected
|
738 |
+
2025-07-30 20:44:37 [INFO] watchfiles.main: 2 changes detected
|
739 |
+
2025-07-30 20:44:43 [INFO] watchfiles.main: 2 changes detected
|
740 |
+
2025-07-30 20:44:45 [INFO] watchfiles.main: 1 change detected
|
741 |
+
2025-07-30 20:44:45 [INFO] watchfiles.main: 1 change detected
|
742 |
+
2025-07-30 20:44:47 [INFO] watchfiles.main: 2 changes detected
|
743 |
+
2025-07-30 20:44:52 [INFO] watchfiles.main: 2 changes detected
|
744 |
+
2025-07-30 20:44:57 [INFO] watchfiles.main: 2 changes detected
|
745 |
+
2025-07-30 20:44:59 [INFO] watchfiles.main: 1 change detected
|
746 |
+
2025-07-30 20:45:00 [INFO] watchfiles.main: 1 change detected
|
747 |
+
2025-07-30 20:45:02 [INFO] watchfiles.main: 2 changes detected
|
748 |
+
2025-07-30 20:45:05 [INFO] watchfiles.main: 1 change detected
|
749 |
+
2025-07-30 20:45:05 [INFO] watchfiles.main: 1 change detected
|
750 |
+
2025-07-30 20:45:08 [INFO] watchfiles.main: 2 changes detected
|
751 |
+
2025-07-30 20:45:13 [INFO] watchfiles.main: 2 changes detected
|
752 |
+
2025-07-30 20:45:15 [INFO] watchfiles.main: 1 change detected
|
753 |
+
2025-07-30 20:45:15 [INFO] watchfiles.main: 1 change detected
|
754 |
+
2025-07-30 20:45:18 [INFO] watchfiles.main: 2 changes detected
|
755 |
+
2025-07-30 20:45:23 [INFO] watchfiles.main: 2 changes detected
|
756 |
+
2025-07-30 20:45:27 [INFO] watchfiles.main: 2 changes detected
|
757 |
+
2025-07-30 20:45:29 [INFO] watchfiles.main: 1 change detected
|
758 |
+
2025-07-30 20:45:30 [INFO] watchfiles.main: 1 change detected
|
759 |
+
2025-07-30 20:45:32 [INFO] watchfiles.main: 2 changes detected
|
760 |
+
2025-07-30 20:45:34 [INFO] watchfiles.main: 1 change detected
|
761 |
+
2025-07-30 20:45:35 [INFO] watchfiles.main: 1 change detected
|
762 |
+
2025-07-30 20:45:37 [INFO] watchfiles.main: 2 changes detected
|
763 |
+
2025-07-30 20:45:42 [INFO] watchfiles.main: 2 changes detected
|
764 |
+
2025-07-30 20:45:44 [INFO] watchfiles.main: 1 change detected
|
765 |
+
2025-07-30 20:45:44 [INFO] watchfiles.main: 1 change detected
|
766 |
+
2025-07-30 20:45:46 [INFO] watchfiles.main: 2 changes detected
|
767 |
+
2025-07-30 20:45:51 [INFO] watchfiles.main: 2 changes detected
|
768 |
+
2025-07-30 20:45:57 [INFO] watchfiles.main: 2 changes detected
|
769 |
+
2025-07-30 20:45:59 [INFO] watchfiles.main: 1 change detected
|
770 |
+
2025-07-30 20:46:00 [INFO] watchfiles.main: 1 change detected
|
771 |
+
2025-07-30 20:46:02 [INFO] watchfiles.main: 2 changes detected
|
772 |
+
2025-07-30 20:46:05 [INFO] watchfiles.main: 1 change detected
|
773 |
+
2025-07-30 20:46:05 [INFO] watchfiles.main: 1 change detected
|
774 |
+
2025-07-30 20:46:06 [INFO] index: database_lifecycle event=connections_closed
|
tests/run_tests.sh
CHANGED
@@ -46,7 +46,7 @@ fi
|
|
46 |
|
47 |
# Install test dependencies if needed
|
48 |
echo "π¦ Installing test dependencies..."
|
49 |
-
pip install requests python-dotenv > /dev/null 2>&1
|
50 |
|
51 |
# Run the tests
|
52 |
echo "π§ͺ Running API tests..."
|
|
|
46 |
|
47 |
# Install test dependencies if needed
|
48 |
echo "π¦ Installing test dependencies..."
|
49 |
+
pip install requests python-dotenv pandas > /dev/null 2>&1
|
50 |
|
51 |
# Run the tests
|
52 |
echo "π§ͺ Running API tests..."
|
tests/test_api.py
CHANGED
@@ -5,8 +5,10 @@ Tests authentication, endpoints, and security features.
|
|
5 |
|
6 |
Updated for new API architecture:
|
7 |
- Removed /data/download endpoint (now uses only /data/download-all)
|
8 |
-
-
|
9 |
-
- Updated bulk download strategy testing
|
|
|
|
|
10 |
"""
|
11 |
|
12 |
import requests
|
@@ -102,7 +104,7 @@ def test_protected_endpoints_no_auth():
|
|
102 |
protected_endpoints = [
|
103 |
("POST", "/tickers/update", {"force_refresh": False}),
|
104 |
("POST", "/tickers/update-async", {"force_refresh": False}),
|
105 |
-
("POST", "/data/download-all",
|
106 |
("GET", "/tasks", None),
|
107 |
("DELETE", "/tasks/old", None)
|
108 |
]
|
@@ -134,7 +136,7 @@ def test_protected_endpoints_invalid_auth():
|
|
134 |
|
135 |
protected_endpoints = [
|
136 |
("POST", "/tickers/update", {"force_refresh": False}),
|
137 |
-
("POST", "/data/download-all",
|
138 |
("GET", "/tasks", None),
|
139 |
]
|
140 |
|
@@ -229,11 +231,13 @@ def test_data_endpoints():
|
|
229 |
# Test POST /data/download-all (bulk download with automatic freshness check)
|
230 |
# Note: This endpoint now automatically checks if data is <24h old and skips update if fresh
|
231 |
# First run will download all data, subsequent runs may return "data is fresh" message
|
|
|
232 |
try:
|
233 |
response = requests.post(
|
234 |
f"{BASE_URL}/data/download-all",
|
235 |
-
headers=HEADERS_VALID_AUTH,
|
236 |
-
|
|
|
237 |
)
|
238 |
passed = response.status_code == 200
|
239 |
all_passed &= print_result("/data/download-all", "POST", 200, response.status_code, passed)
|
@@ -261,6 +265,16 @@ def test_data_endpoints():
|
|
261 |
if data:
|
262 |
latest = data[0]
|
263 |
print(f" π° Latest close: ${latest.get('close', 0):.2f}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
264 |
except Exception as e:
|
265 |
print(f"β GET /data/tickers/AAPL failed: {e}")
|
266 |
all_passed = False
|
@@ -318,6 +332,7 @@ def main():
|
|
318 |
all_tests_passed &= test_protected_endpoints_invalid_auth()
|
319 |
all_tests_passed &= test_protected_endpoints_valid_auth()
|
320 |
all_tests_passed &= test_data_endpoints()
|
|
|
321 |
all_tests_passed &= test_sql_injection_safety()
|
322 |
|
323 |
# Final results
|
@@ -329,6 +344,8 @@ def main():
|
|
329 |
print("β
SQL injection protection is active")
|
330 |
print("β
Public endpoints are accessible")
|
331 |
print("β
Bulk data download with freshness check is working")
|
|
|
|
|
332 |
print("β
New optimized API architecture is functional")
|
333 |
else:
|
334 |
print("β SOME TESTS FAILED!")
|
@@ -337,5 +354,62 @@ def main():
|
|
337 |
|
338 |
return 0 if all_tests_passed else 1
|
339 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
340 |
if __name__ == "__main__":
|
341 |
exit(main())
|
|
|
5 |
|
6 |
Updated for new API architecture:
|
7 |
- Removed /data/download endpoint (now uses only /data/download-all)
|
8 |
+
- Added force_refresh and force_indicators parameters
|
9 |
+
- Updated bulk download strategy testing with 3-month period
|
10 |
+
- Added technical indicators validation (SMA 10, 20, 50)
|
11 |
+
- Updated response validation for new SMA fields
|
12 |
"""
|
13 |
|
14 |
import requests
|
|
|
104 |
protected_endpoints = [
|
105 |
("POST", "/tickers/update", {"force_refresh": False}),
|
106 |
("POST", "/tickers/update-async", {"force_refresh": False}),
|
107 |
+
("POST", "/data/download-all", {"force_refresh": False, "force_indicators": False}),
|
108 |
("GET", "/tasks", None),
|
109 |
("DELETE", "/tasks/old", None)
|
110 |
]
|
|
|
136 |
|
137 |
protected_endpoints = [
|
138 |
("POST", "/tickers/update", {"force_refresh": False}),
|
139 |
+
("POST", "/data/download-all", {"force_refresh": False, "force_indicators": False}),
|
140 |
("GET", "/tasks", None),
|
141 |
]
|
142 |
|
|
|
231 |
# Test POST /data/download-all (bulk download with automatic freshness check)
|
232 |
# Note: This endpoint now automatically checks if data is <24h old and skips update if fresh
|
233 |
# First run will download all data, subsequent runs may return "data is fresh" message
|
234 |
+
# Now supports force_refresh and force_indicators parameters
|
235 |
try:
|
236 |
response = requests.post(
|
237 |
f"{BASE_URL}/data/download-all",
|
238 |
+
headers=HEADERS_VALID_AUTH,
|
239 |
+
json={"force_refresh": False, "force_indicators": False},
|
240 |
+
timeout=120 # Bulk download might take longer with 3mo data and technical indicators
|
241 |
)
|
242 |
passed = response.status_code == 200
|
243 |
all_passed &= print_result("/data/download-all", "POST", 200, response.status_code, passed)
|
|
|
265 |
if data:
|
266 |
latest = data[0]
|
267 |
print(f" π° Latest close: ${latest.get('close', 0):.2f}")
|
268 |
+
# Check for technical indicators
|
269 |
+
sma_fast = latest.get('sma_fast')
|
270 |
+
sma_med = latest.get('sma_med')
|
271 |
+
sma_slow = latest.get('sma_slow')
|
272 |
+
if sma_fast is not None:
|
273 |
+
print(f" π SMA Fast (10): ${sma_fast:.2f}")
|
274 |
+
if sma_med is not None:
|
275 |
+
print(f" π SMA Med (20): ${sma_med:.2f}")
|
276 |
+
if sma_slow is not None:
|
277 |
+
print(f" π SMA Slow (50): ${sma_slow:.2f}")
|
278 |
except Exception as e:
|
279 |
print(f"β GET /data/tickers/AAPL failed: {e}")
|
280 |
all_passed = False
|
|
|
332 |
all_tests_passed &= test_protected_endpoints_invalid_auth()
|
333 |
all_tests_passed &= test_protected_endpoints_valid_auth()
|
334 |
all_tests_passed &= test_data_endpoints()
|
335 |
+
all_tests_passed &= test_technical_indicators()
|
336 |
all_tests_passed &= test_sql_injection_safety()
|
337 |
|
338 |
# Final results
|
|
|
344 |
print("β
SQL injection protection is active")
|
345 |
print("β
Public endpoints are accessible")
|
346 |
print("β
Bulk data download with freshness check is working")
|
347 |
+
print("β
Technical indicators (SMA 10, 20, 50) are working")
|
348 |
+
print("β
3-month data period and force_indicators flag functional")
|
349 |
print("β
New optimized API architecture is functional")
|
350 |
else:
|
351 |
print("β SOME TESTS FAILED!")
|
|
|
354 |
|
355 |
return 0 if all_tests_passed else 1
|
356 |
|
357 |
+
def test_technical_indicators():
|
358 |
+
"""Test technical indicators functionality."""
|
359 |
+
print_test_header("Technical Indicators Tests")
|
360 |
+
|
361 |
+
all_passed = True
|
362 |
+
|
363 |
+
# Test POST /data/download-all with force_indicators=True
|
364 |
+
try:
|
365 |
+
response = requests.post(
|
366 |
+
f"{BASE_URL}/data/download-all",
|
367 |
+
headers=HEADERS_VALID_AUTH,
|
368 |
+
json={"force_refresh": False, "force_indicators": True},
|
369 |
+
timeout=120
|
370 |
+
)
|
371 |
+
passed = response.status_code == 200
|
372 |
+
all_passed &= print_result("/data/download-all (force_indicators)", "POST", 200, response.status_code, passed)
|
373 |
+
|
374 |
+
if passed:
|
375 |
+
data = response.json()
|
376 |
+
print(f" π Processed {data.get('tickers_processed', 0)} tickers")
|
377 |
+
print(f" π¬ Message: {data.get('message', 'N/A')}")
|
378 |
+
except Exception as e:
|
379 |
+
print(f"β POST /data/download-all (force_indicators) failed: {e}")
|
380 |
+
all_passed = False
|
381 |
+
|
382 |
+
# Test that ticker data now includes technical indicators
|
383 |
+
try:
|
384 |
+
response = requests.get(f"{BASE_URL}/data/tickers/AAPL?days=60", headers=HEADERS_NO_AUTH, timeout=10)
|
385 |
+
passed = response.status_code == 200
|
386 |
+
all_passed &= print_result("/data/tickers/AAPL (indicators validation)", "GET", 200, response.status_code, passed)
|
387 |
+
|
388 |
+
if passed:
|
389 |
+
data = response.json()
|
390 |
+
print(f" π Retrieved {len(data)} days of AAPL data for indicators test")
|
391 |
+
|
392 |
+
# Check that we have enough data and some records have indicators
|
393 |
+
indicators_found = 0
|
394 |
+
for record in data:
|
395 |
+
if (record.get('sma_fast') is not None or
|
396 |
+
record.get('sma_med') is not None or
|
397 |
+
record.get('sma_slow') is not None):
|
398 |
+
indicators_found += 1
|
399 |
+
|
400 |
+
print(f" π Records with indicators: {indicators_found}/{len(data)}")
|
401 |
+
|
402 |
+
# Validate that recent records have indicators (should have SMA after 50+ days)
|
403 |
+
if len(data) >= 50:
|
404 |
+
recent_records = data[:10] # Check most recent 10 records
|
405 |
+
sma_slow_count = sum(1 for r in recent_records if r.get('sma_slow') is not None)
|
406 |
+
print(f" π Recent records with SMA Slow (50): {sma_slow_count}/10")
|
407 |
+
|
408 |
+
except Exception as e:
|
409 |
+
print(f"β Technical indicators validation failed: {e}")
|
410 |
+
all_passed = False
|
411 |
+
|
412 |
+
return all_passed
|
413 |
+
|
414 |
if __name__ == "__main__":
|
415 |
exit(main())
|