Spaces:
Running
Running
File size: 4,074 Bytes
b0e15c1 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 | """
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
|