MF / src /models.py
Parthiban97's picture
Upload 15 files
b0e15c1 verified
"""
Data models for MF Portfolio Analysis Tool.
"""
from dataclasses import dataclass, field
from typing import Optional
@dataclass
class Fund:
"""Represents a single mutual fund scheme from the fund universe CSV."""
name: str
category: str
benchmark: str
# Cost
ter: Optional[float] = None # Total Expense Ratio (%)
turnover: Optional[float] = None # Portfolio Turnover (%)
# Returns
mean: Optional[float] = None
cagr_1y: Optional[float] = None
cagr_1y_cat: Optional[float] = None
cagr_1y_bm: Optional[float] = None
cagr_3y: Optional[float] = None
cagr_3y_cat: Optional[float] = None
cagr_3y_bm: Optional[float] = None
cagr_5y: Optional[float] = None
cagr_5y_cat: Optional[float] = None
cagr_5y_bm: Optional[float] = None
cagr_10y: Optional[float] = None
cagr_10y_cat: Optional[float] = None
cagr_10y_bm: Optional[float] = None
cagr_inception: Optional[float] = None
nav: Optional[float] = None
# Valuation
pe_ratio: Optional[float] = None
pb_ratio: Optional[float] = None
# Risk metrics
alpha: Optional[float] = None
beta: Optional[float] = None
std_dev: Optional[float] = None
sharpe: Optional[float] = None
volatility: Optional[float] = None
sortino: Optional[float] = None
up_capture: Optional[float] = None
down_capture: Optional[float] = None
max_drawdown: Optional[float] = None
r_squared: Optional[float] = None
info_ratio: Optional[float] = None
aum: Optional[float] = None
fill_status: Optional[str] = None
# Scoring (computed)
score: Optional[float] = None
rank_in_category: Optional[int] = None
is_top_quartile: bool = False
weightage: Optional[int] = None # Number of periods beating benchmark
order: int = 0 # Preserves original CSV insertion order for sort tiebreaker
@dataclass
class ClientHolding:
"""Represents a single mutual fund holding in a client's portfolio."""
scheme_name: str
current_value: float
invested_amount: Optional[float] = None
sip_amount: Optional[float] = None
sip_frequency: Optional[str] = None # Monthly / Quarterly etc.
# Matched fund data
fund: Optional[Fund] = None
# Computed
allocation_pct: float = 0.0
xirr: Optional[float] = None
is_underperforming: bool = False
# Advisory
suggested_fund: Optional[Fund] = None
switch_reason: Optional[str] = None
@dataclass
class Client:
"""Client details."""
name: str
age: Optional[int] = None
email: Optional[str] = None
mobile: Optional[str] = None
pan: Optional[str] = None
@dataclass
class Advisor:
"""Financial advisor details."""
name: str = "RAVICHANDRAN"
phone: str = "9281364703"
email: str = "c4c.ravi@gmail.com"
arn: str = "ARN-243354"
location: str = "Chennai"
@dataclass
class PortfolioReport:
"""The complete portfolio analysis report for a client."""
client: Client
advisor: Advisor
holdings: list = field(default_factory=list)
# Portfolio-level metrics
total_current_value: float = 0.0
total_invested: float = 0.0
unrealized_gain: float = 0.0
portfolio_xirr: Optional[float] = None
sharpe: Optional[float] = None
alpha: Optional[float] = None
beta: Optional[float] = None
std_dev: Optional[float] = None
# Exposure warnings
amc_exposure: dict = field(default_factory=dict) # AMC -> pct
scheme_exposure: dict = field(default_factory=dict) # scheme -> pct
exposure_warnings: list = field(default_factory=list) # list of warning strings
# Allocation
market_cap_allocation: dict = field(default_factory=dict) # Large/Mid/Small/Other -> pct
sector_allocation: dict = field(default_factory=dict) # sector -> pct
# Wealth projection
wealth_projection: dict = field(default_factory=dict) # years -> projected value