File size: 4,991 Bytes
80b408c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8ce1623
 
80b408c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
134
import logging

import yfinance as yf

class FundamentalAnalysis:
    def __init__(
            self,
            ticker: str,
            debug: bool=False):
        """
        Initialize FundamentalAnalysis object.
        Args:
        ticker : str
            Stock ticker to analyze.
        debug : bool, optional, default: False
            Whether to run in debug mode, so that logging is produced at debug level.
        """
        # set up logging
        if debug:
            self.logger_level = logging.DEBUG
        else:
            self.logger_level = logging.INFO
        self.logger = logging.getLogger(__name__)
        logging.basicConfig(level=self.logger_level)  # filename='FundamentalAnalysis.log', 

        # input args
        self.ticker = ticker
        # done initializing
        self.logger.info(f'Initialized TechnicalAnalysis object for ticker: {ticker}')

    def run(
            self
            ) -> dict:
        """
        Runs the fundamental analysis for the given ticker.
        Returns:
        dict: A dictionary with fundamental metrics for the given ticker.
        """
        self.info = self.fetch_stock_info()
        if len(self.info) > 0:
            result = self.get_stock_fundamentals()
        else:
            result = {}
        return result

    def fetch_stock_info(
            self
            ) -> dict:
        """
        Fetches all available information for a given stock ticker using yfinance.
        
        Returns:
        dict: A dictionary of key-value pairs with available information for the given ticker.
        """
        try:
            stock = yf.Ticker(self.ticker)
            info = stock.info  # Fetch all available info
        except Exception as e:
            self.logger.error(f"Error fetching stock info for {self.ticker}: {str(e)}")
            info = {}
        if len(info) == 1:
            info = {}
        else:
            self.logger.info(f"Fetched stock info for {self.ticker}")
        return info
    
    def get_stock_fundamentals(
            self
            ) -> dict:
        """
        Attempts to fetches key fundamental metrics for a given stock ticker using yfinance.
        Returns:
        dict: Dictionary of fundamental metrics with human-readable keys
        """

        # Define a safe fetch method to avoid entire function failure
        def safe_get(key, transform=None):
            value = self.info.get(key, "Metric not available")
            if value != "Metric not available" and transform:
                try:
                    return transform(value)
                except Exception:
                    return "metric not available"
            return value

        # Extract key financial metrics
        fundamentals = {
            "Company Name": safe_get("longName"),
            "Sector": safe_get("sector"),
            "Industry": safe_get("industry"),
            "Market Capitalization": safe_get("marketCap"),
            
            # Valuation Metrics
            "P/E Ratio (Trailing)": safe_get("trailingPE"),
            "P/E Ratio (Forward)": safe_get("forwardPE"),
            "P/B Ratio (Price to Book)": safe_get("priceToBook"),
            "P/S Ratio (Price to Sales)": safe_get("priceToSalesTrailing12Months"),
            "Dividend Yield (%)": safe_get("dividendYield", lambda x: round(x * 100, 2)),

            # Profitability Metrics
            "Earnings Per Share (EPS)": safe_get("trailingEps"),
            "Return on Equity (ROE)": safe_get("returnOnEquity"),
            "Return on Assets (ROA)": safe_get("returnOnAssets"),
            "Gross Margin (%)": safe_get("grossMargins", lambda x: round(x * 100, 2)),
            "Operating Margin (%)": safe_get("operatingMargins", lambda x: round(x * 100, 2)),

            # Financial Health Metrics
            "Debt-to-Equity Ratio": safe_get("debtToEquity"),
            "Current Ratio": safe_get("currentRatio"),
            "Quick Ratio": safe_get("quickRatio"),
            "Interest Coverage Ratio": safe_get(
                "ebitda",
                lambda ebitda: round(ebitda / self.info["totalDebt"], 2) if self.info.get("totalDebt") else "Metric not available"
            ),

            # Growth Metrics
            "Revenue Growth (%)": safe_get("revenueGrowth", lambda x: round(x * 100, 2)),
            "EPS Growth (%)": safe_get("earningsGrowth", lambda x: round(x * 100, 2)),

            # Market & Ownership
            "Institutional Ownership (%)": safe_get("heldPercentInstitutions", lambda x: round(x * 100, 2)),
            "Insider Ownership (%)": safe_get("heldPercentInsiders", lambda x: round(x * 100, 2)),
        }
        
        return fundamentals

if __name__ == '__main__':
    dict_fundamentals = FundamentalAnalysis(ticker="GOOG").run()
    if len(dict_fundamentals) > 0:
        for key, value in dict_fundamentals.items():
            print(f"{key}: {value}")
    else:
        print("No fundamental data available.")