fix(UX-001): tighten historical stale state handling

This commit is contained in:
Bu5hm4nn
2026-03-26 10:32:05 +01:00
parent 78de8782c4
commit 82e52f7162
2 changed files with 45 additions and 13 deletions

View File

@@ -290,6 +290,7 @@ def _render_backtests_page(workspace_id: str | None = None) -> None:
).classes("w-full") ).classes("w-full")
def refresh_workspace_seeded_units() -> None: def refresh_workspace_seeded_units() -> None:
validation_label.set_text("")
entry_spot, entry_error = derive_entry_spot() entry_spot, entry_error = derive_entry_spot()
if ( if (
workspace_id workspace_id
@@ -302,10 +303,18 @@ def _render_backtests_page(workspace_id: str | None = None) -> None:
else: else:
entry_spot_hint.set_text("Entry spot unavailable until the scenario dates are valid.") entry_spot_hint.set_text("Entry spot unavailable until the scenario dates are valid.")
render_seeded_summary(entry_spot=entry_spot, entry_spot_error=entry_error) render_seeded_summary(entry_spot=entry_spot, entry_spot_error=entry_error)
if entry_error:
render_result_state("Scenario validation failed", entry_error, tone="warning")
else:
mark_results_stale() mark_results_stale()
def on_form_change() -> None: def on_form_change() -> None:
render_seeded_summary() validation_label.set_text("")
entry_spot, entry_error = derive_entry_spot()
render_seeded_summary(entry_spot=entry_spot, entry_spot_error=entry_error)
if entry_error:
render_result_state("Scenario validation failed", entry_error, tone="warning")
else:
mark_results_stale() mark_results_stale()
def run_backtest() -> None: def run_backtest() -> None:

View File

@@ -187,13 +187,13 @@ def _render_event_comparison_page(workspace_id: str | None = None) -> None:
tone="info", tone="info",
) )
def refresh_preview(*, reset_templates: bool = False, reseed_units: bool = False) -> None: def refresh_preview(*, reset_templates: bool = False, reseed_units: bool = False) -> str | None:
option = preset_lookup.get(str(preset_select.value or "")) option = preset_lookup.get(str(preset_select.value or ""))
if option is None: if option is None:
metadata_label.set_text("") metadata_label.set_text("")
scenario_label.set_text("") scenario_label.set_text("")
render_selected_summary(entry_spot=None) render_selected_summary(entry_spot=None)
return return None
if reset_templates: if reset_templates:
syncing_controls["value"] = True syncing_controls["value"] = True
@@ -202,12 +202,20 @@ def _render_event_comparison_page(workspace_id: str | None = None) -> None:
finally: finally:
syncing_controls["value"] = False syncing_controls["value"] = False
template_slugs = selected_template_slugs()
if not template_slugs:
message = "Select at least one strategy template."
metadata_label.set_text(f"Preset: {option['label']}{option['description']}")
scenario_label.set_text(message)
render_selected_summary(entry_spot=None, entry_spot_error=message)
return message
try: try:
preview_units = float(units_input.value or 0.0) preview_units = float(units_input.value or 0.0)
if workspace_id and config is not None and reseed_units: if workspace_id and config is not None and reseed_units:
preview_scenario = service.preview_scenario( preview_scenario = service.preview_scenario(
preset_slug=str(option["slug"]), preset_slug=str(option["slug"]),
template_slugs=selected_template_slugs(), template_slugs=template_slugs,
underlying_units=1.0, underlying_units=1.0,
loan_amount=float(loan_input.value or 0.0), loan_amount=float(loan_input.value or 0.0),
margin_call_ltv=float(ltv_input.value or 0.0), margin_call_ltv=float(ltv_input.value or 0.0),
@@ -224,7 +232,7 @@ def _render_event_comparison_page(workspace_id: str | None = None) -> None:
syncing_controls["value"] = False syncing_controls["value"] = False
scenario = service.preview_scenario( scenario = service.preview_scenario(
preset_slug=str(option["slug"]), preset_slug=str(option["slug"]),
template_slugs=selected_template_slugs(), template_slugs=template_slugs,
underlying_units=preview_units, underlying_units=preview_units,
loan_amount=float(loan_input.value or 0.0), loan_amount=float(loan_input.value or 0.0),
margin_call_ltv=float(ltv_input.value or 0.0), margin_call_ltv=float(ltv_input.value or 0.0),
@@ -233,7 +241,7 @@ def _render_event_comparison_page(workspace_id: str | None = None) -> None:
metadata_label.set_text(f"Preset: {option['label']}{option['description']}") metadata_label.set_text(f"Preset: {option['label']}{option['description']}")
scenario_label.set_text(str(exc)) scenario_label.set_text(str(exc))
render_selected_summary(entry_spot=None, entry_spot_error=str(exc)) render_selected_summary(entry_spot=None, entry_spot_error=str(exc))
return return str(exc)
preset = service.event_preset_service.get_preset(str(option["slug"])) preset = service.event_preset_service.get_preset(str(option["slug"]))
metadata_label.set_text(f"Preset: {option['label']}{option['description']}") metadata_label.set_text(f"Preset: {option['label']}{option['description']}")
scenario_label.set_text( scenario_label.set_text(
@@ -247,14 +255,21 @@ def _render_event_comparison_page(workspace_id: str | None = None) -> None:
+ f" · Entry spot: ${scenario.initial_portfolio.entry_spot:,.2f}" + f" · Entry spot: ${scenario.initial_portfolio.entry_spot:,.2f}"
) )
render_selected_summary(entry_spot=float(scenario.initial_portfolio.entry_spot)) render_selected_summary(entry_spot=float(scenario.initial_portfolio.entry_spot))
return None
def render_report() -> None: def render_report() -> None:
validation_label.set_text("") validation_label.set_text("")
result_panel.clear() result_panel.clear()
template_slugs = selected_template_slugs()
if not template_slugs:
message = "Select at least one strategy template."
validation_label.set_text(message)
render_result_state("Scenario validation failed", message, tone="warning")
return
try: try:
report = service.run_read_only_comparison( report = service.run_read_only_comparison(
preset_slug=str(preset_select.value or ""), preset_slug=str(preset_select.value or ""),
template_slugs=selected_template_slugs(), template_slugs=template_slugs,
underlying_units=float(units_input.value or 0.0), underlying_units=float(units_input.value or 0.0),
loan_amount=float(loan_input.value or 0.0), loan_amount=float(loan_input.value or 0.0),
margin_call_ltv=float(ltv_input.value or 0.0), margin_call_ltv=float(ltv_input.value or 0.0),
@@ -388,13 +403,21 @@ def _render_event_comparison_page(workspace_id: str | None = None) -> None:
def on_preset_change() -> None: def on_preset_change() -> None:
if syncing_controls["value"]: if syncing_controls["value"]:
return return
refresh_preview(reset_templates=True, reseed_units=True) validation_label.set_text("")
preview_error = refresh_preview(reset_templates=True, reseed_units=True)
if preview_error:
render_result_state("Scenario validation failed", preview_error, tone="warning")
else:
mark_results_stale() mark_results_stale()
def on_preview_input_change() -> None: def on_preview_input_change() -> None:
if syncing_controls["value"]: if syncing_controls["value"]:
return return
refresh_preview() validation_label.set_text("")
preview_error = refresh_preview()
if preview_error:
render_result_state("Scenario validation failed", preview_error, tone="warning")
else:
mark_results_stale() mark_results_stale()
preset_select.on_value_change(lambda _: on_preset_change()) preset_select.on_value_change(lambda _: on_preset_change())