feat: defer entry spot derivation to backtest run (BT-005)
- Remove async refresh_workspace_seeded_units from date change handlers - Date changes now only call on_form_change() (updates cost estimates, marks results stale) - Entry spot is derived only when user clicks Run button - Form remains responsive during configuration - No more API errors when changing dates while configuring other fields
This commit is contained in:
@@ -5,7 +5,7 @@ from datetime import date, datetime
|
|||||||
from typing import TYPE_CHECKING, Any
|
from typing import TYPE_CHECKING, Any
|
||||||
|
|
||||||
from fastapi.responses import RedirectResponse
|
from fastapi.responses import RedirectResponse
|
||||||
from nicegui import run, ui
|
from nicegui import ui
|
||||||
|
|
||||||
from app.domain.backtesting_math import asset_quantity_from_workspace_config
|
from app.domain.backtesting_math import asset_quantity_from_workspace_config
|
||||||
from app.models.backtest import ProviderRef
|
from app.models.backtest import ProviderRef
|
||||||
@@ -784,32 +784,6 @@ def _render_backtests_page(workspace_id: str | None = None) -> None:
|
|||||||
logger.warning(f"Failed to save backtest settings for workspace {workspace_id}: {e}")
|
logger.warning(f"Failed to save backtest settings for workspace {workspace_id}: {e}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
async def refresh_workspace_seeded_units() -> None:
|
|
||||||
"""Derive entry spot asynchronously and update units when dates change."""
|
|
||||||
validation_label.set_text("")
|
|
||||||
progress_label.set_text("Fetching entry spot...")
|
|
||||||
|
|
||||||
try:
|
|
||||||
# Run derive_entry_spot in background thread
|
|
||||||
entry_spot, entry_error = await run.io_bound(derive_entry_spot)
|
|
||||||
|
|
||||||
if workspace_id and config is not None and config.gold_value is not None and entry_spot is not None:
|
|
||||||
units_input.value = asset_quantity_from_workspace_config(
|
|
||||||
config, entry_spot=entry_spot, symbol=get_symbol_from_dataset()
|
|
||||||
)
|
|
||||||
update_cost_estimate()
|
|
||||||
render_seeded_summary(entry_spot=entry_spot, entry_spot_error=entry_error)
|
|
||||||
if entry_error:
|
|
||||||
validation_label.set_text(entry_error)
|
|
||||||
render_result_state("Scenario validation failed", entry_error, tone="warning")
|
|
||||||
return
|
|
||||||
validation_error = validate_current_scenario(entry_spot=entry_spot)
|
|
||||||
if validation_error:
|
|
||||||
validation_label.set_text(validation_error)
|
|
||||||
render_result_state("Scenario validation failed", validation_error, tone="warning")
|
|
||||||
finally:
|
|
||||||
progress_label.set_text("")
|
|
||||||
|
|
||||||
def on_form_change() -> None:
|
def on_form_change() -> None:
|
||||||
"""Handle form changes with minimal API calls."""
|
"""Handle form changes with minimal API calls."""
|
||||||
validation_label.set_text("")
|
validation_label.set_text("")
|
||||||
@@ -1061,13 +1035,14 @@ def _render_backtests_page(workspace_id: str | None = None) -> None:
|
|||||||
update_cost_estimate()
|
update_cost_estimate()
|
||||||
|
|
||||||
# Wire up event handlers
|
# Wire up event handlers
|
||||||
# Only call expensive derive_entry_spot on date changes
|
# Date changes should NOT trigger expensive derive_entry_spot API call
|
||||||
|
# Entry spot derivation happens when user clicks Run
|
||||||
data_source_select.on_value_change(lambda e: on_form_change())
|
data_source_select.on_value_change(lambda e: on_form_change())
|
||||||
dataset_select.on_value_change(lambda e: (update_date_range_hint(), on_form_change()))
|
dataset_select.on_value_change(lambda e: (update_date_range_hint(), on_form_change()))
|
||||||
schema_select.on_value_change(lambda e: on_form_change())
|
schema_select.on_value_change(lambda e: on_form_change())
|
||||||
symbol_select.on_value_change(lambda e: update_date_range_hint())
|
symbol_select.on_value_change(lambda e: update_date_range_hint())
|
||||||
start_input.on_value_change(lambda e: refresh_workspace_seeded_units())
|
start_input.on_value_change(lambda e: on_form_change())
|
||||||
end_input.on_value_change(lambda e: refresh_workspace_seeded_units())
|
end_input.on_value_change(lambda e: on_form_change())
|
||||||
# Don't trigger API calls on these changes
|
# Don't trigger API calls on these changes
|
||||||
start_price_input.on_value_change(lambda e: mark_results_stale())
|
start_price_input.on_value_change(lambda e: mark_results_stale())
|
||||||
template_select.on_value_change(lambda e: mark_results_stale())
|
template_select.on_value_change(lambda e: mark_results_stale())
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ notes:
|
|||||||
- Pre-alpha policy: we may cut or replace old features without backward compatibility until alpha is declared.
|
- Pre-alpha policy: we may cut or replace old features without backward compatibility until alpha is declared.
|
||||||
- Alpha migration policy: once alpha is declared, compatibility only needs to move forward; backward migrations are not required.
|
- Alpha migration policy: once alpha is declared, compatibility only needs to move forward; backward migrations are not required.
|
||||||
priority_queue:
|
priority_queue:
|
||||||
- BT-005
|
|
||||||
- BT-004
|
- BT-004
|
||||||
- EXEC-002
|
- EXEC-002
|
||||||
- DATA-DB-005
|
- DATA-DB-005
|
||||||
@@ -25,6 +24,7 @@ priority_queue:
|
|||||||
- GCF-001
|
- GCF-001
|
||||||
- DATA-DB-006
|
- DATA-DB-006
|
||||||
recently_completed:
|
recently_completed:
|
||||||
|
- BT-005
|
||||||
- CORE-003
|
- CORE-003
|
||||||
- CONV-001
|
- CONV-001
|
||||||
- DATA-DB-004
|
- DATA-DB-004
|
||||||
@@ -50,7 +50,6 @@ recently_completed:
|
|||||||
- CORE-002B
|
- CORE-002B
|
||||||
states:
|
states:
|
||||||
backlog:
|
backlog:
|
||||||
- BT-005
|
|
||||||
- BT-004
|
- BT-004
|
||||||
- DATA-DB-005
|
- DATA-DB-005
|
||||||
- DATA-DB-006
|
- DATA-DB-006
|
||||||
@@ -63,6 +62,7 @@ states:
|
|||||||
- GCF-001
|
- GCF-001
|
||||||
in_progress: []
|
in_progress: []
|
||||||
done:
|
done:
|
||||||
|
- BT-005
|
||||||
- CORE-003
|
- CORE-003
|
||||||
- CONV-001
|
- CONV-001
|
||||||
- DATA-DB-003
|
- DATA-DB-003
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
id: BT-005
|
id: BT-005
|
||||||
title: Defer Entry Spot Derivation to Backtest Run
|
title: Defer Entry Spot Derivation to Backtest Run
|
||||||
status: backlog
|
status: done
|
||||||
priority: P1
|
priority: P1
|
||||||
effort: S
|
effort: S
|
||||||
depends_on: []
|
depends_on: []
|
||||||
@@ -14,9 +14,7 @@ acceptance_criteria:
|
|||||||
- Clear loading indicator shows when entry spot is being fetched during run.
|
- Clear loading indicator shows when entry spot is being fetched during run.
|
||||||
- Previous entry spot value is retained until new one is derived.
|
- Previous entry spot value is retained until new one is derived.
|
||||||
notes:
|
notes:
|
||||||
Current behavior calls derive_entry_spot on every date change which causes
|
Removed async refresh_workspace_seeded_units from date change handlers.
|
||||||
API errors if user is still configuring other fields.
|
Entry spot derivation now only happens inside run_read_only_scenario when
|
||||||
|
the user clicks Run. Form changes now call on_form_change() which only
|
||||||
The refresh_workspace_seeded_units function should not be called on date changes.
|
updates cost estimates and marks results stale.
|
||||||
Entry spot derivation should happen inside start_backtest or as a separate
|
|
||||||
explicit "fetch spot" button if user wants to preview.
|
|
||||||
Reference in New Issue
Block a user