feat(backtest): add dataset-specific date validation and better error handling
- Add DATABENTO_DATASET_MIN_DATES for XNAS.BASIC (2024-07-01) and GLBX.MDP3 (2010-01-01) - Validate start date against dataset minimum before running backtest - Parse Databento API errors and show user-friendly messages - Update date range hint to show dataset-specific availability - Catch BentoClientError and show appropriate warning tone
This commit is contained in:
@@ -48,6 +48,12 @@ SYMBOL_MIN_DATES = {
|
||||
"XAU": date(1970, 1, 1), # XAU index historical data
|
||||
}
|
||||
|
||||
# Minimum dates for Databento datasets (when data became available)
|
||||
DATABENTO_DATASET_MIN_DATES = {
|
||||
"XNAS.BASIC": date(2024, 7, 1), # XNAS.BASIC data available from July 2024
|
||||
"GLBX.MDP3": date(2010, 1, 1), # GLBX.MDP3 futures data from 2010
|
||||
}
|
||||
|
||||
def get_default_backtest_dates() -> tuple[date, date]:
|
||||
"""Get default backtest date range (~2 years ending on most recent Friday or earlier).
|
||||
|
||||
@@ -446,8 +452,21 @@ def _render_backtests_page(workspace_id: str | None = None) -> None:
|
||||
return "GLD" # Default for XNAS.BASIC
|
||||
|
||||
def update_date_range_hint() -> None:
|
||||
"""Update the date range hint based on selected symbol."""
|
||||
"""Update the date range hint based on selected symbol and data source."""
|
||||
symbol = get_symbol_from_dataset()
|
||||
data_source = str(data_source_select.value)
|
||||
|
||||
# Use dataset-specific minimum for Databento
|
||||
if data_source == "databento":
|
||||
dataset = str(dataset_select.value)
|
||||
min_date = DATABENTO_DATASET_MIN_DATES.get(dataset)
|
||||
if min_date:
|
||||
date_range_hint.set_text(
|
||||
f"{dataset} data available from {min_date.strftime('%Y-%m-%d')}"
|
||||
)
|
||||
return
|
||||
|
||||
# Fall back to symbol minimum
|
||||
min_date = SYMBOL_MIN_DATES.get(symbol)
|
||||
if min_date:
|
||||
date_range_hint.set_text(
|
||||
@@ -805,6 +824,20 @@ def _render_backtests_page(workspace_id: str | None = None) -> None:
|
||||
start_date = parse_iso_date(start_input.value, "Start date")
|
||||
end_date = parse_iso_date(end_input.value, "End date")
|
||||
symbol = get_symbol_from_dataset()
|
||||
|
||||
# Validate dataset-specific minimum dates for Databento
|
||||
data_source = str(data_source_select.value)
|
||||
if data_source == "databento":
|
||||
dataset = str(dataset_select.value)
|
||||
dataset_min = DATABENTO_DATASET_MIN_DATES.get(dataset)
|
||||
if dataset_min and start_date < dataset_min:
|
||||
validation_label.set_text(
|
||||
f"Start date must be on or after {dataset_min.strftime('%Y-%m-%d')} for {dataset} dataset. "
|
||||
f"Selected start date {start_date.strftime('%Y-%m-%d')} is before available data."
|
||||
)
|
||||
render_result_state("Invalid start date", validation_label.text, tone="warning")
|
||||
return
|
||||
|
||||
date_range_error = validate_date_range_for_symbol(start_date, end_date, symbol)
|
||||
if date_range_error:
|
||||
validation_label.set_text(date_range_error)
|
||||
@@ -845,11 +878,32 @@ def _render_backtests_page(workspace_id: str | None = None) -> None:
|
||||
validation_label.set_text(str(exc))
|
||||
render_result_state("Scenario validation failed", str(exc), tone="warning")
|
||||
return
|
||||
except Exception:
|
||||
except Exception as exc:
|
||||
entry_spot, entry_error = derive_entry_spot()
|
||||
render_seeded_summary(entry_spot=entry_spot, entry_spot_error=entry_error)
|
||||
if entry_spot is None:
|
||||
entry_spot_hint.set_text("Entry spot unavailable until the scenario dates are valid.")
|
||||
|
||||
# Check for Databento API errors
|
||||
error_msg = str(exc)
|
||||
if "data_start_before_available_start" in error_msg:
|
||||
# Extract the available start date from the error message
|
||||
import re
|
||||
match = re.search(r"available start of dataset [^(]+\('([^']+)'\)", error_msg)
|
||||
if match:
|
||||
available_start = match.group(1).split()[0] # Extract date part
|
||||
validation_label.set_text(
|
||||
f"Data not available before {available_start}. Please set start date to {available_start} or later."
|
||||
)
|
||||
else:
|
||||
validation_label.set_text(
|
||||
"Selected start date is before data is available for this dataset. Please choose a later date."
|
||||
)
|
||||
render_result_state("Invalid start date", validation_label.text, tone="warning")
|
||||
elif "BentoClientError" in error_msg or "422" in error_msg:
|
||||
validation_label.set_text(f"Data source error: {error_msg}")
|
||||
render_result_state("Data unavailable", validation_label.text, tone="warning")
|
||||
else:
|
||||
message = "Backtest failed. Please verify the scenario inputs and try again."
|
||||
logger.exception(
|
||||
"Backtest page run failed for workspace=%s symbol=%s start=%s end=%s template=%s units=%s loan=%s margin_call_ltv=%s",
|
||||
|
||||
Reference in New Issue
Block a user