mutual-fund / app /services /sip_calculator.py
lucifer7210's picture
Upload 21 files
eb606e1 verified
import pandas as pd
from datetime import datetime
from typing import Optional, Dict, Any, List
from app.models.goal_models import SIPCalculationRequest, SIPCalculationResponse, RequiredSIPRequest, RequiredSIPResponse
class SIPCalculator:
"""Calculate SIP returns and goal-based investments"""
@staticmethod
def calculate_sip_maturity(monthly_amount: float, annual_return: float, years: int) -> float:
"""Calculate SIP maturity amount"""
if annual_return == 0:
return monthly_amount * years * 12
monthly_return = annual_return / 12 / 100
months = years * 12
maturity_amount = monthly_amount * (
((1 + monthly_return) ** months - 1) / monthly_return
) * (1 + monthly_return)
return maturity_amount
@staticmethod
def calculate_required_sip(target_amount: float, years: int, expected_return: float) -> float:
"""Calculate required SIP amount for a target"""
if expected_return == 0:
return target_amount / (years * 12)
monthly_return = expected_return / 12 / 100
months = years * 12
required_sip = target_amount / (
((1 + monthly_return) ** months - 1) / monthly_return
) / (1 + monthly_return)
return required_sip
@staticmethod
def calculate_fund_returns(nav_data: pd.DataFrame, investment_amount: float,
start_date: datetime, end_date: datetime) -> Optional[Dict[str, Any]]:
"""Calculate returns for a fund investment"""
try:
filtered_data = nav_data[
(nav_data['date'] >= pd.to_datetime(start_date)) &
(nav_data['date'] <= pd.to_datetime(end_date))
]
if len(filtered_data) < 2:
return None
start_nav = filtered_data.iloc[0]['nav']
end_nav = filtered_data.iloc[-1]['nav']
units = investment_amount / start_nav
final_value = units * end_nav
total_return = ((final_value - investment_amount) / investment_amount) * 100
return {
'start_nav': start_nav,
'end_nav': end_nav,
'units': units,
'final_value': final_value,
'total_return': total_return,
'investment_amount': investment_amount
}
except Exception as e:
print(f"Error calculating returns: {e}")
return None
@staticmethod
def get_sip_calculation(request: SIPCalculationRequest, include_yearly_breakdown: bool = False) -> SIPCalculationResponse:
"""Get SIP calculation with optional yearly breakdown"""
maturity_amount = SIPCalculator.calculate_sip_maturity(
request.monthly_amount, request.annual_return, request.years
)
total_invested = request.monthly_amount * request.years * 12
gains = maturity_amount - total_invested
return_multiple = maturity_amount / total_invested if total_invested > 0 else 0
yearly_breakdown = None
if include_yearly_breakdown:
yearly_breakdown = []
invested_cumulative = 0
for year in range(1, request.years + 1):
maturity_year = SIPCalculator.calculate_sip_maturity(
request.monthly_amount, request.annual_return, year
)
invested_year = request.monthly_amount * year * 12
yearly_breakdown.append({
'Year': year,
'Invested': invested_year,
'Maturity Value': maturity_year,
'Gains': maturity_year - invested_year
})
return SIPCalculationResponse(
maturity_amount=maturity_amount,
total_invested=total_invested,
gains=gains,
return_multiple=return_multiple,
yearly_breakdown=yearly_breakdown
)
@staticmethod
def get_required_sip(request: RequiredSIPRequest) -> RequiredSIPResponse:
"""Get required SIP amount for a target"""
required_sip = SIPCalculator.calculate_required_sip(
request.target_amount, request.years, request.expected_return
)
return RequiredSIPResponse(required_sip=required_sip)