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