"""组合再平衡 + Monte Carlo 模拟模型。""" import uuid from datetime import datetime, timezone from sqlalchemy import DateTime, Float, ForeignKey, String, Text from sqlalchemy.orm import Mapped, mapped_column from app.core.database import Base from app.core.types import JSONBType class PortfolioRebalancing(Base): """组合再平衡建议。""" __tablename__ = "portfolio_rebalancings" id: Mapped[str] = mapped_column(String(36), primary_key=True, default=lambda: str(uuid.uuid4())) tenant_id: Mapped[str] = mapped_column(String(36), ForeignKey("tenants.id"), nullable=False, index=True) marginal_returns: Mapped[dict | None] = mapped_column(JSONBType, nullable=True, comment="各企业边际回报率") reallocation_plan: Mapped[dict | None] = mapped_column(JSONBType, nullable=True, comment="再平衡方案") irr_impact: Mapped[float | None] = mapped_column(Float, nullable=True, comment="IRR 影响") dpi_impact: Mapped[float | None] = mapped_column(Float, nullable=True, comment="DPI 影响") status: Mapped[str] = mapped_column(String(20), nullable=False, default="proposed", comment="proposed/approved/executed") created_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), default=lambda: datetime.now(timezone.utc) ) class MonteCarloSimulation(Base): """Monte Carlo 模拟结果。""" __tablename__ = "monte_carlo_simulations" id: Mapped[str] = mapped_column(String(36), primary_key=True, default=lambda: str(uuid.uuid4())) tenant_id: Mapped[str] = mapped_column(String(36), ForeignKey("tenants.id"), nullable=False, index=True) iterations: Mapped[int] = mapped_column(nullable=False, default=10000, comment="模拟次数") irr_distribution: Mapped[dict | None] = mapped_column(JSONBType, nullable=True, comment="IRR 概率分布") dpi_distribution: Mapped[dict | None] = mapped_column(JSONBType, nullable=True, comment="DPI 概率分布") percentile_p5: Mapped[float | None] = mapped_column(Float, nullable=True) percentile_p50: Mapped[float | None] = mapped_column(Float, nullable=True) percentile_p95: Mapped[float | None] = mapped_column(Float, nullable=True) created_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), default=lambda: datetime.now(timezone.utc) )