Files
AI-Trader/api/date_utils.py
Bill 76b946449e feat: implement date range API and on-demand downloads (WIP phase 2)
Phase 2 progress - API integration complete.

API Changes:
- Replace date_range (List[str]) with start_date/end_date (str)
- Add automatic end_date defaulting to start_date for single day
- Add date format validation
- Integrate PriceDataManager for on-demand downloads
- Add rate limit handling (trusts provider, no pre-config)
- Validate date ranges with configurable max days (MAX_SIMULATION_DAYS)

New Modules:
- api/date_utils.py - Date validation and expansion utilities
- scripts/migrate_price_data.py - Migration script for merged.jsonl

API Flow:
1. Validate date range (start <= end, max 30 days, not future)
2. Check missing price data coverage
3. Download missing data if AUTO_DOWNLOAD_PRICE_DATA=true
4. Priority-based download (maximize date completion)
5. Create job with available trading dates
6. Graceful handling of partial data (rate limits)

Configuration:
- AUTO_DOWNLOAD_PRICE_DATA (default: true)
- MAX_SIMULATION_DAYS (default: 30)
- No rate limit configuration needed

Still TODO:
- Update tools/price_tools.py to read from database
- Implement simulation run tracking
- Update .env.example
- Comprehensive testing
- Documentation updates

Breaking Changes:
- API request format changed (date_range -> start_date/end_date)
- This completes v0.3.0 preparation
2025-10-31 16:40:50 -04:00

94 lines
2.3 KiB
Python

"""
Date range utilities for simulation date management.
This module provides:
- Date range expansion
- Date range validation
- Trading day detection
"""
import os
from datetime import datetime, timedelta
from typing import List
def expand_date_range(start_date: str, end_date: str) -> List[str]:
"""
Expand date range into list of all dates (inclusive).
Args:
start_date: Start date (YYYY-MM-DD)
end_date: End date (YYYY-MM-DD)
Returns:
Sorted list of dates in range
Raises:
ValueError: If dates are invalid or start > end
"""
start = datetime.strptime(start_date, "%Y-%m-%d")
end = datetime.strptime(end_date, "%Y-%m-%d")
if start > end:
raise ValueError(f"start_date ({start_date}) must be <= end_date ({end_date})")
dates = []
current = start
while current <= end:
dates.append(current.strftime("%Y-%m-%d"))
current += timedelta(days=1)
return dates
def validate_date_range(
start_date: str,
end_date: str,
max_days: int = 30
) -> None:
"""
Validate date range for simulation.
Args:
start_date: Start date (YYYY-MM-DD)
end_date: End date (YYYY-MM-DD)
max_days: Maximum allowed days in range
Raises:
ValueError: If validation fails
"""
# Parse dates
try:
start = datetime.strptime(start_date, "%Y-%m-%d")
end = datetime.strptime(end_date, "%Y-%m-%d")
except ValueError as e:
raise ValueError(f"Invalid date format: {e}")
# Check order
if start > end:
raise ValueError(f"start_date ({start_date}) must be <= end_date ({end_date})")
# Check range size
days = (end - start).days + 1
if days > max_days:
raise ValueError(
f"Date range too large: {days} days (max: {max_days}). "
f"Reduce range or increase MAX_SIMULATION_DAYS."
)
# Check not in future
today = datetime.now().date()
if end.date() > today:
raise ValueError(f"end_date ({end_date}) cannot be in the future")
def get_max_simulation_days() -> int:
"""
Get maximum simulation days from environment.
Returns:
Maximum days allowed in simulation range
"""
return int(os.getenv("MAX_SIMULATION_DAYS", "30"))