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(
|
ui.label(
|
||||||
"Changing the preset resets strategy templates to that preset's default comparison set."
|
"Changing the preset resets strategy templates to that preset's default comparison set."
|
||||||
).classes("text-xs text-slate-500 dark:text-slate-400")
|
).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")
|
loan_input = ui.number("Loan amount", value=default_loan, min=0, step=1000).classes("w-full")
|
||||||
ltv_input = ui.number(
|
ltv_input = ui.number(
|
||||||
"Margin call LTV",
|
"Margin call LTV",
|
||||||
@@ -140,14 +143,18 @@ def _render_event_comparison_page(workspace_id: str | None = None) -> None:
|
|||||||
selected_summary.clear()
|
selected_summary.clear()
|
||||||
with selected_summary:
|
with selected_summary:
|
||||||
ui.label("Scenario Summary").classes("text-lg font-semibold text-slate-900 dark:text-slate-100")
|
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"):
|
with ui.grid(columns=1).classes("w-full gap-4 sm:grid-cols-2 lg:grid-cols-1 xl:grid-cols-2"):
|
||||||
cards = [
|
cards = [
|
||||||
(
|
(
|
||||||
"Preset",
|
"Initial portfolio value",
|
||||||
preset_select_options.get(str(preset_select.value), str(preset_select.value or "—")),
|
f"${float(initial_value_input.value or 0.0):,.0f}",
|
||||||
),
|
),
|
||||||
("Templates", str(len(selected_template_slugs()))),
|
("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}"),
|
("Loan amount", f"${float(loan_input.value or 0.0):,.0f}"),
|
||||||
("Margin call LTV", f"{float(ltv_input.value or 0.0):.1%}"),
|
("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"),
|
("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()
|
template_slugs = selected_template_slugs()
|
||||||
|
|
||||||
try:
|
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:
|
if workspace_id and config is not None and reseed_units:
|
||||||
preview_entry_spot = service.derive_entry_spot(
|
# Recalculate from workspace config
|
||||||
preset_slug=str(option["slug"]),
|
workspace_units = asset_quantity_from_workspace_config(
|
||||||
template_slugs=template_slugs,
|
|
||||||
)
|
|
||||||
preview_units = asset_quantity_from_workspace_config(
|
|
||||||
config,
|
config,
|
||||||
entry_spot=preview_entry_spot,
|
entry_spot=preview_entry_spot,
|
||||||
symbol="GLD",
|
symbol="GLD",
|
||||||
)
|
)
|
||||||
syncing_controls["value"] = True
|
syncing_controls["value"] = True
|
||||||
try:
|
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:
|
finally:
|
||||||
syncing_controls["value"] = False
|
syncing_controls["value"] = False
|
||||||
scenario = service.preview_scenario(
|
scenario = service.preview_scenario(
|
||||||
@@ -235,11 +249,11 @@ def _render_event_comparison_page(workspace_id: str | None = None) -> None:
|
|||||||
return str(exc)
|
return str(exc)
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.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,
|
workspace_id,
|
||||||
preset_select.value,
|
preset_select.value,
|
||||||
selected_template_slugs(),
|
selected_template_slugs(),
|
||||||
units_input.value,
|
initial_value_input.value,
|
||||||
loan_input.value,
|
loan_input.value,
|
||||||
ltv_input.value,
|
ltv_input.value,
|
||||||
)
|
)
|
||||||
@@ -268,10 +282,23 @@ def _render_event_comparison_page(workspace_id: str | None = None) -> None:
|
|||||||
result_panel.clear()
|
result_panel.clear()
|
||||||
template_slugs = selected_template_slugs()
|
template_slugs = selected_template_slugs()
|
||||||
try:
|
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(
|
report = service.run_read_only_comparison(
|
||||||
preset_slug=str(preset_select.value or ""),
|
preset_slug=str(preset_select.value or ""),
|
||||||
template_slugs=template_slugs,
|
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),
|
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),
|
||||||
)
|
)
|
||||||
@@ -282,11 +309,11 @@ def _render_event_comparison_page(workspace_id: str | None = None) -> None:
|
|||||||
except Exception:
|
except Exception:
|
||||||
message = "Event comparison failed. Please verify the seeded inputs and try again."
|
message = "Event comparison failed. Please verify the seeded inputs and try again."
|
||||||
logger.exception(
|
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,
|
workspace_id,
|
||||||
preset_select.value,
|
preset_select.value,
|
||||||
selected_template_slugs(),
|
selected_template_slugs(),
|
||||||
units_input.value,
|
initial_value_input.value,
|
||||||
loan_input.value,
|
loan_input.value,
|
||||||
ltv_input.value,
|
ltv_input.value,
|
||||||
)
|
)
|
||||||
@@ -326,6 +353,7 @@ def _render_event_comparison_page(workspace_id: str | None = None) -> None:
|
|||||||
"Scenario dates used",
|
"Scenario dates used",
|
||||||
f"{scenario.start_date.isoformat()} → {scenario.end_date.isoformat()}",
|
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}"),
|
("Underlying units", f"{scenario.initial_portfolio.underlying_units:,.0f}"),
|
||||||
("Loan amount", f"${scenario.initial_portfolio.loan_amount:,.0f}"),
|
("Loan amount", f"${scenario.initial_portfolio.loan_amount:,.0f}"),
|
||||||
("Margin call LTV", f"{scenario.initial_portfolio.margin_call_ltv:.1%}"),
|
("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())
|
preset_select.on_value_change(lambda _: on_preset_change())
|
||||||
template_select.on_value_change(lambda _: on_preview_input_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())
|
loan_input.on_value_change(lambda _: on_preview_input_change())
|
||||||
ltv_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())
|
run_button.on_click(lambda: render_report())
|
||||||
|
|||||||
Reference in New Issue
Block a user