feat(event-comparison): use initial portfolio value instead of underlying units
- Changed UI input from 'Underlying units' to 'Initial portfolio value ($)' - Underlying units are now calculated as initial_value / entry_spot - Updated default value to workspace gold_value instead of gold_ounces * entry_spot - Result summary now shows both 'Initial value' and 'Underlying units' - This allows users to specify how much they invest on day 1, and the system automatically calculates the maximum purchasable shares/contracts
This commit is contained in:
@@ -107,7 +107,10 @@ def _render_event_comparison_page(workspace_id: str | None = None) -> None:
|
||||
ui.label(
|
||||
"Changing the preset resets strategy templates to that preset's default comparison set."
|
||||
).classes("text-xs text-slate-500 dark:text-slate-400")
|
||||
units_input = ui.number("Underlying units", value=default_units, min=0.0001, step=1).classes("w-full")
|
||||
ui.label(
|
||||
"Underlying units will be calculated from initial value ÷ entry spot."
|
||||
).classes("text-xs text-slate-500 dark:text-slate-400")
|
||||
initial_value_input = ui.number("Initial portfolio value ($)", value=default_units * default_entry_spot, min=0.01, step=1000).classes("w-full")
|
||||
loan_input = ui.number("Loan amount", value=default_loan, min=0, step=1000).classes("w-full")
|
||||
ltv_input = ui.number(
|
||||
"Margin call LTV",
|
||||
@@ -140,14 +143,18 @@ def _render_event_comparison_page(workspace_id: str | None = None) -> None:
|
||||
selected_summary.clear()
|
||||
with selected_summary:
|
||||
ui.label("Scenario Summary").classes("text-lg font-semibold text-slate-900 dark:text-slate-100")
|
||||
# Calculate underlying units from initial value and entry spot
|
||||
computed_units = 0.0
|
||||
if entry_spot is not None and entry_spot > 0:
|
||||
computed_units = float(initial_value_input.value or 0.0) / entry_spot
|
||||
with ui.grid(columns=1).classes("w-full gap-4 sm:grid-cols-2 lg:grid-cols-1 xl:grid-cols-2"):
|
||||
cards = [
|
||||
(
|
||||
"Preset",
|
||||
preset_select_options.get(str(preset_select.value), str(preset_select.value or "—")),
|
||||
"Initial portfolio value",
|
||||
f"${float(initial_value_input.value or 0.0):,.0f}",
|
||||
),
|
||||
("Templates", str(len(selected_template_slugs()))),
|
||||
("Underlying units", f"{float(units_input.value or 0.0):,.0f}"),
|
||||
("Underlying units", f"{computed_units:,.0f}" if computed_units > 0 else "—"),
|
||||
("Loan amount", f"${float(loan_input.value or 0.0):,.0f}"),
|
||||
("Margin call LTV", f"{float(ltv_input.value or 0.0):.1%}"),
|
||||
("Entry spot", f"${entry_spot:,.2f}" if entry_spot is not None else "Unavailable"),
|
||||
@@ -205,20 +212,27 @@ def _render_event_comparison_page(workspace_id: str | None = None) -> None:
|
||||
template_slugs = selected_template_slugs()
|
||||
|
||||
try:
|
||||
preview_units = float(units_input.value or 0.0)
|
||||
# Get initial portfolio value from UI and derive entry spot
|
||||
preview_initial_value = float(initial_value_input.value or 0.0)
|
||||
preview_entry_spot = service.derive_entry_spot(
|
||||
preset_slug=str(option["slug"]),
|
||||
template_slugs=template_slugs,
|
||||
)
|
||||
# Calculate underlying units from initial value and entry spot
|
||||
preview_units = preview_initial_value / preview_entry_spot if preview_entry_spot > 0 else 0.0
|
||||
|
||||
if workspace_id and config is not None and reseed_units:
|
||||
preview_entry_spot = service.derive_entry_spot(
|
||||
preset_slug=str(option["slug"]),
|
||||
template_slugs=template_slugs,
|
||||
)
|
||||
preview_units = asset_quantity_from_workspace_config(
|
||||
# Recalculate from workspace config
|
||||
workspace_units = asset_quantity_from_workspace_config(
|
||||
config,
|
||||
entry_spot=preview_entry_spot,
|
||||
symbol="GLD",
|
||||
)
|
||||
syncing_controls["value"] = True
|
||||
try:
|
||||
units_input.value = preview_units
|
||||
initial_value_input.value = workspace_units * preview_entry_spot
|
||||
preview_units = workspace_units
|
||||
preview_initial_value = workspace_units * preview_entry_spot
|
||||
finally:
|
||||
syncing_controls["value"] = False
|
||||
scenario = service.preview_scenario(
|
||||
@@ -235,11 +249,11 @@ def _render_event_comparison_page(workspace_id: str | None = None) -> None:
|
||||
return str(exc)
|
||||
except Exception:
|
||||
logger.exception(
|
||||
"Event comparison preview failed for workspace=%s preset=%s templates=%s units=%s loan=%s margin_call_ltv=%s",
|
||||
"Event comparison preview failed for workspace=%s preset=%s templates=%s initial_value=%s loan=%s margin_call_ltv=%s",
|
||||
workspace_id,
|
||||
preset_select.value,
|
||||
selected_template_slugs(),
|
||||
units_input.value,
|
||||
initial_value_input.value,
|
||||
loan_input.value,
|
||||
ltv_input.value,
|
||||
)
|
||||
@@ -268,10 +282,23 @@ def _render_event_comparison_page(workspace_id: str | None = None) -> None:
|
||||
result_panel.clear()
|
||||
template_slugs = selected_template_slugs()
|
||||
try:
|
||||
# Get initial portfolio value and calculate underlying units
|
||||
initial_value = float(initial_value_input.value or 0.0)
|
||||
# Get entry spot from preview
|
||||
option = preset_lookup.get(str(preset_select.value or ""))
|
||||
if option is None:
|
||||
validation_label.set_text("Select a preset to run comparison.")
|
||||
return
|
||||
entry_spot = service.derive_entry_spot(
|
||||
preset_slug=str(option["slug"]),
|
||||
template_slugs=template_slugs,
|
||||
)
|
||||
underlying_units = initial_value / entry_spot if entry_spot > 0 else 0.0
|
||||
|
||||
report = service.run_read_only_comparison(
|
||||
preset_slug=str(preset_select.value or ""),
|
||||
template_slugs=template_slugs,
|
||||
underlying_units=float(units_input.value or 0.0),
|
||||
underlying_units=underlying_units,
|
||||
loan_amount=float(loan_input.value or 0.0),
|
||||
margin_call_ltv=float(ltv_input.value or 0.0),
|
||||
)
|
||||
@@ -282,11 +309,11 @@ def _render_event_comparison_page(workspace_id: str | None = None) -> None:
|
||||
except Exception:
|
||||
message = "Event comparison failed. Please verify the seeded inputs and try again."
|
||||
logger.exception(
|
||||
"Event comparison page run failed for workspace=%s preset=%s templates=%s units=%s loan=%s margin_call_ltv=%s",
|
||||
"Event comparison page run failed for workspace=%s preset=%s templates=%s initial_value=%s loan=%s margin_call_ltv=%s",
|
||||
workspace_id,
|
||||
preset_select.value,
|
||||
selected_template_slugs(),
|
||||
units_input.value,
|
||||
initial_value_input.value,
|
||||
loan_input.value,
|
||||
ltv_input.value,
|
||||
)
|
||||
@@ -326,6 +353,7 @@ def _render_event_comparison_page(workspace_id: str | None = None) -> None:
|
||||
"Scenario dates used",
|
||||
f"{scenario.start_date.isoformat()} → {scenario.end_date.isoformat()}",
|
||||
),
|
||||
("Initial value", f"${float(initial_value_input.value or 0.0):,.0f}"),
|
||||
("Underlying units", f"{scenario.initial_portfolio.underlying_units:,.0f}"),
|
||||
("Loan amount", f"${scenario.initial_portfolio.loan_amount:,.0f}"),
|
||||
("Margin call LTV", f"{scenario.initial_portfolio.margin_call_ltv:.1%}"),
|
||||
@@ -553,7 +581,7 @@ def _render_event_comparison_page(workspace_id: str | None = None) -> None:
|
||||
|
||||
preset_select.on_value_change(lambda _: on_preset_change())
|
||||
template_select.on_value_change(lambda _: on_preview_input_change())
|
||||
units_input.on_value_change(lambda _: on_preview_input_change())
|
||||
initial_value_input.on_value_change(lambda _: on_preview_input_change())
|
||||
loan_input.on_value_change(lambda _: on_preview_input_change())
|
||||
ltv_input.on_value_change(lambda _: on_preview_input_change())
|
||||
run_button.on_click(lambda: render_report())
|
||||
|
||||
Reference in New Issue
Block a user