fix(CORE-002C): align historical units with workspace weight

This commit is contained in:
Bu5hm4nn
2026-03-25 21:37:55 +01:00
parent aae67dfd9b
commit 87900b01bf
6 changed files with 54 additions and 20 deletions

View File

@@ -3,8 +3,9 @@ from __future__ import annotations
from dataclasses import dataclass
from decimal import Decimal
from app.domain.units import BaseCurrency, Money, _coerce_currency, decimal_from_float, to_decimal
from app.domain.units import BaseCurrency, Money, Weight, WeightUnit, _coerce_currency, decimal_from_float, to_decimal
from app.models.backtest import BacktestPortfolioState
from app.models.portfolio import PortfolioConfig
@dataclass(frozen=True, slots=True)
@@ -94,6 +95,23 @@ def asset_quantity_from_floats(portfolio_value: float, entry_spot: float, symbol
return float(asset_quantity_from_money(notional, spot).amount)
def asset_quantity_from_workspace_config(config: PortfolioConfig, *, entry_spot: float, symbol: str) -> float:
if config.gold_ounces is not None and config.gold_ounces > 0:
try:
from app.domain.instruments import asset_quantity_from_weight
quantity = asset_quantity_from_weight(
symbol,
Weight(amount=decimal_from_float(float(config.gold_ounces)), unit=WeightUnit.OUNCE_TROY),
)
return float(quantity.amount)
except ValueError:
pass
if config.gold_value is not None:
return asset_quantity_from_floats(float(config.gold_value), entry_spot, symbol)
raise ValueError("Workspace config must provide collateral weight or value")
def materialize_backtest_portfolio_state(
*,
symbol: str,

View File

@@ -5,7 +5,7 @@ from datetime import date, datetime
from fastapi.responses import RedirectResponse
from nicegui import ui
from app.domain.backtesting_math import asset_quantity_from_floats
from app.domain.backtesting_math import asset_quantity_from_workspace_config
from app.models.workspace import get_workspace_repository
from app.pages.common import dashboard_page, render_workspace_recovery
from app.services.backtesting.ui_service import BacktestPageRunResult, BacktestPageService
@@ -66,8 +66,8 @@ def _render_backtests_page(workspace_id: str | None = None) -> None:
config = repo.load_portfolio_config(workspace_id) if workspace_id else None
default_entry_spot = service.derive_entry_spot("GLD", date(2024, 1, 2), date(2024, 1, 8))
default_units = (
asset_quantity_from_floats(float(config.gold_value or 0.0), default_entry_spot, "GLD")
if config and config.gold_value is not None and default_entry_spot > 0
asset_quantity_from_workspace_config(config, entry_spot=default_entry_spot, symbol="GLD")
if config is not None and default_entry_spot > 0
else 1000.0
)
default_loan = float(config.loan_amount) if config else 68000.0
@@ -217,7 +217,7 @@ def _render_backtests_page(workspace_id: str | None = None) -> None:
)
except (ValueError, KeyError):
return
units_input.value = asset_quantity_from_floats(float(config.gold_value), entry_spot, "GLD")
units_input.value = asset_quantity_from_workspace_config(config, entry_spot=entry_spot, symbol="GLD")
entry_spot_hint.set_text(f"Auto-derived entry spot: ${entry_spot:,.2f}")
def run_backtest() -> None:

View File

@@ -3,7 +3,7 @@ from __future__ import annotations
from fastapi.responses import RedirectResponse
from nicegui import ui
from app.domain.backtesting_math import asset_quantity_from_floats
from app.domain.backtesting_math import asset_quantity_from_workspace_config
from app.models.workspace import get_workspace_repository
from app.pages.common import dashboard_page, render_workspace_recovery
from app.services.event_comparison_ui import EventComparisonPageService
@@ -54,8 +54,8 @@ def _render_event_comparison_page(workspace_id: str | None = None) -> None:
)
default_entry_spot = default_preview.initial_portfolio.entry_spot
default_units = (
asset_quantity_from_floats(float(config.gold_value or 0.0), default_entry_spot, "GLD")
if config and config.gold_value is not None and default_entry_spot > 0
asset_quantity_from_workspace_config(config, entry_spot=default_entry_spot, symbol="GLD")
if config is not None and default_entry_spot > 0
else 1000.0
)
@@ -129,7 +129,7 @@ def _render_event_comparison_page(workspace_id: str | None = None) -> None:
template_select.value = list(service.default_template_selection(str(option["slug"])))
try:
preview_units = float(units_input.value or 0.0)
if workspace_id and config is not None and config.gold_value is not None:
if workspace_id and config is not None:
preview_scenario = service.preview_scenario(
preset_slug=str(option["slug"]),
template_slugs=selected_template_slugs(),
@@ -137,10 +137,10 @@ def _render_event_comparison_page(workspace_id: str | None = None) -> None:
loan_amount=float(loan_input.value or 0.0),
margin_call_ltv=float(ltv_input.value or 0.0),
)
preview_units = asset_quantity_from_floats(
float(config.gold_value),
float(preview_scenario.initial_portfolio.entry_spot),
"GLD",
preview_units = asset_quantity_from_workspace_config(
config,
entry_spot=float(preview_scenario.initial_portfolio.entry_spot),
symbol="GLD",
)
units_input.value = preview_units
scenario = service.preview_scenario(