SaudAltamimi / value-mpt

MIT License
2 stars 3 forks source link

Create Margin of Safety Calculator #3

Open SaudAltamimi opened 2 months ago

SaudAltamimi commented 2 months ago
SaudAltamimi commented 1 month ago

Useful code snippet:

import yfinance as yf
from typing import Tuple, Dict

class FinancialData:
    """
    A class to represent financial data for a stock.

    Attributes:
        ticker (str): The stock ticker symbol.
        balance_sheet (pd.DataFrame): Balance sheet data.
        income_statement (pd.DataFrame): Income statement data.
        cash_flow (pd.DataFrame): Cash flow statement data.
    """

    def __init__(self, ticker: str):
        """
        Initialize FinancialData with a stock ticker.

        Args:
            ticker (str): The stock ticker symbol.
        """
        self.ticker = ticker
        self.balance_sheet, self.income_statement, self.cash_flow = self._get_financial_data()

    def _get_financial_data(self) -> Tuple[pd.DataFrame, pd.DataFrame, pd.DataFrame]:
        """
        Fetch financial data from Yahoo Finance.

        Returns:
            Tuple[pd.DataFrame, pd.DataFrame, pd.DataFrame]: Balance sheet, income statement, and cash flow data.
        """
        stock = yf.Ticker(self.ticker)
        return stock.balance_sheet, stock.financials, stock.cashflow

class ValuationMetrics:
    """
    A class to calculate various valuation metrics for a stock.

    Attributes:
        financial_data (FinancialData): Financial data for the stock.
    """

    def __init__(self, financial_data: FinancialData):
        """
        Initialize ValuationMetrics with financial data.

        Args:
            financial_data (FinancialData): Financial data for the stock.
        """
        self.financial_data = financial_data

    def calculate_ncav(self) -> float:
        """
        Calculate Net Current Asset Value (NCAV).

        Returns:
            float: The calculated NCAV.
        """
        current_assets = self.financial_data.balance_sheet.loc['Total Current Assets'].iloc[0]
        total_liabilities = self.financial_data.balance_sheet.loc['Total Liabilities'].iloc[0]
        return current_assets - total_liabilities

    def calculate_epv(self, wacc: float = 0.10) -> float:
        """
        Calculate Earnings Power Value (EPV).

        Args:
            wacc (float): Weighted Average Cost of Capital. Defaults to 0.10.

        Returns:
            float: The calculated EPV.
        """
        income_statement = self.financial_data.income_statement
        cash_flow = self.financial_data.cash_flow

        # Calculate average EBIT margin
        ebit = income_statement.loc['EBIT']
        revenue = income_statement.loc['Total Revenue']
        ebit_margins = ebit / revenue
        avg_ebit_margin = ebit_margins.mean()

        # Normalize EBIT
        current_revenue = revenue.iloc[0]
        normalized_ebit = current_revenue * avg_ebit_margin

        # Calculate after-tax normalized EBIT
        tax_rate = income_statement.loc['Income Tax Expense'].iloc[0] / income_statement.loc['Income Before Tax'].iloc[0]
        after_tax_normalized_ebit = normalized_ebit * (1 - tax_rate)

        # Adjust for depreciation
        avg_depreciation = cash_flow.loc['Depreciation'].mean()
        adjusted_depreciation = 0.5 * tax_rate * avg_depreciation

        # Calculate maintenance capex
        capex = cash_flow.loc['Capital Expenditures'].abs()
        income_growth_rate = income_statement.loc['Net Income'].pct_change().mean()
        maintenance_capex = capex.mean() * (1 - income_growth_rate)

        # Calculate adjusted earnings
        adjusted_earnings = after_tax_normalized_ebit + adjusted_depreciation - maintenance_capex

        return adjusted_earnings / wacc

    @staticmethod
    def calculate_margin_of_safety(intrinsic_value: float, current_price: float) -> float:
        """
        Calculate Margin of Safety.

        Args:
            intrinsic_value (float): The estimated intrinsic value of the stock.
            current_price (float): The current market price of the stock.

        Returns:
            float: The calculated Margin of Safety.
        """
        return 1 - (current_price / intrinsic_value)

class StockAnalyzer:
    """
    A class to analyze a stock using various valuation metrics.

    Attributes:
        ticker (str): The stock ticker symbol.
        financial_data (FinancialData): Financial data for the stock.
        valuation_metrics (ValuationMetrics): Valuation metrics calculator for the stock.
    """

    def __init__(self, ticker: str):
        """
        Initialize StockAnalyzer with a stock ticker.

        Args:
            ticker (str): The stock ticker symbol.
        """
        self.ticker = ticker
        self.financial_data = FinancialData(ticker)
        self.valuation_metrics = ValuationMetrics(self.financial_data)

    def analyze(self) -> Dict[str, float]:
        """
        Perform a comprehensive analysis of the stock.

        Returns:
            Dict[str, float]: A dictionary containing various analysis results.
        """
        stock = yf.Ticker(self.ticker)
        current_price = stock.info['currentPrice']
        shares_outstanding = stock.info['sharesOutstanding']

        ncav = self.valuation_metrics.calculate_ncav()
        epv = self.valuation_metrics.calculate_epv()

        ncav_per_share = ncav / shares_outstanding
        epv_per_share = epv / shares_outstanding

        mos_ncav = self.valuation_metrics.calculate_margin_of_safety(ncav_per_share, current_price)
        mos_epv = self.valuation_metrics.calculate_margin_of_safety(epv_per_share, current_price)

        return {
            'current_price': current_price,
            'ncav_per_share': ncav_per_share,
            'epv_per_share': epv_per_share,
            'mos_ncav': mos_ncav,
            'mos_epv': mos_epv
        }

    def print_analysis(self):
        """
        Print the stock analysis results.
        """
        results = self.analyze()
        print(f"Analysis for {self.ticker}:")
        print(f"Current Price: ${results['current_price']:.2f}")
        print(f"NCAV per share: ${results['ncav_per_share']:.2f}")
        print(f"EPV per share: ${results['epv_per_share']:.2f}")
        print(f"Margin of Safety (NCAV): {results['mos_ncav']:.2%}")
        print(f"Margin of Safety (EPV): {results['mos_epv']:.2%}")

# Example usage
if __name__ == "__main__":
    analyzer = StockAnalyzer("AAPL")
    analyzer.print_analysis()