Example #1
0
    def get_holdings_stats(self, fund_symbol):
        """
        Gets the top 25 companies in their portfolio, as well as the following stats:
            1. Name
            2. % portfolio weight
            3. YTD return
            4. Shares owned
            5. Shares changed
            6. P/E
            7. Price
            8. G/L % (gain/loss percent for the day)

        First get the first 25 most weighted companies from portfolio (desc)
        For each:
            1. Equity view tab
                -Name
                -% portfolio weight
                -Shares owned
                -Shares changed
                -YTD return (could be positive, negative, float, or blank (-) )
                -P/E (could be positive, negative, float, or blank (-) )
            2. Equity prices tab
                -Price
                -G/L % (gain/loss percent)

        Each tab is represented as a table
            -equity view tab:       id = equity_holding_tab
                -get <tbody> with id holding_epage0
            -equity prices tab:     id = equityPrice_holding_tab

        Comparisons between 2+ mutual funds will compare Name and % portfolio weight only
        """

        fund_symbol = fund_symbol.upper()
        response = {}

        try:
            Util.validate_format(fund_symbol)
            url = Util.build_url(Section.HOLDINGS_PAGE_TOP_25, fund_symbol)
            response = self.extractHoldings(url, fund_symbol)

        except FundException.ImproperSymbolFormatError as e:
            raise FundException.ImproperSymbolFormatError(e)
        except FundException.SymbolDoesNotExistError as e:
            raise FundException.SymbolDoesNotExistError(e)
        except FundException.UIChangedError as e:
            raise FundException.UIChangedError(e)
        except FundException.SourceEndpointChangedError as e:
            raise FundException.SourceEndpointChangedError(e)

        return response
Example #2
0
    def get_risk_stats(self, fund_symbol):
        """
        Grabs risk stats. Grabs 8 things, for 4 time periods (3 year, 5 year, 10 year, 15 year):
            1. Alpha
            2. Beta
            3. R-squared
            4. Standard deviation
            5. Sharpe ratio
            6. Sortino ratio
            7. Treynor ratio
            8. Capture ratios
        Return in a JsonResponse encoded object
        """

        #Add data from capture ratios first. We can get all data in capture ratios in 1 GET request, but need multiple GET requests for mpt and volatility
        fund_symbol = fund_symbol.upper()
        response = {}
        try:
            Util.validate_format(fund_symbol)
            response = self.get_capture_ratios(fund_symbol)
            timespans = ["3-Year", "5-Year", "10-Year", "15-Year"]
            for timespan in timespans:
                #Extract and aggregate data for MPT stats and Volatility stats
                mpt_and_volatility = self.get_mpt_and_volatility_data(
                    fund_symbol, timespan)

                #Add these values into the current timespan dict along with the capture ratios
                response[timespan] = {
                    **response[timespan],
                    **mpt_and_volatility
                }

        except FundException.ImproperSymbolFormatError as e:
            raise FundException.ImproperSymbolFormatError(e)
        except FundException.SymbolDoesNotExistError as e:
            raise FundException.SymbolDoesNotExistError(e)
        except FundException.UIChangedError as e:
            raise FundException.UIChangedError(e)
        except FundException.SourceEndpointChangedError as e:
            raise FundException.SourceEndpointChangedError(e)

        return response
    def get_performance_stats(self, fund_symbol):
        fund_symbol = fund_symbol.upper()
        stats = {}
        try:
            Util.validate_format(fund_symbol)
            stats["trailing_returns"] = self.get_trailing_returns(fund_symbol)
            stats["historical_returns"] = self.get_fund_historical_returns(
                fund_symbol)
            stats["10000_growth_data"] = self.get_10000_growth(fund_symbol)

        except FundException.ImproperSymbolFormatError as e:
            raise FundException.ImproperSymbolFormatError(e)
        except FundException.SymbolDoesNotExistError as e:
            raise FundException.SymbolDoesNotExistError(e)
        except FundException.UIChangedError as e:
            raise FundException.UIChangedError(e)
        except FundException.SourceEndpointChangedError as e:
            raise FundException.SourceEndpointChangedError(e)

        return stats
Example #4
0
    def get_general_stats(self, fund_symbol):
        """
        Grabs general stats of the mutual fund. Grabs things:
            1. Price (NAV)
            2. Min. initial investment
            3. Expense ratio
            4. Asset allocation pie chart data(Morningstar's pie chart: Cash, US stock, Non-US stock, bonds, etc)Asset allocation pie chart data(Morningstar's pie chart: Cash, US stock, Non-US stock, bonds, etc)
            5. Morningstar overall rating
            6. Morningstar risk vs category
            7. Morningstar return vs category
            8. Morningstar category
            9. Turnover ratio
        Source = Morningstar, quotes page
        """

        fund_symbol = fund_symbol.upper()
        response = {}

        try:
            Util.validate_format(fund_symbol)
            sections = [
                Section.GENERAL_STATS, Section.ASSET_ALLOCATION,
                Section.RISK_RETURN_VS_CATEGORY, Section.OVERALL_RATING
            ]
            for section in sections:
                response[str(section)] = self.get_section_data(
                    section, fund_symbol)

        except FundException.ImproperSymbolFormatError as e:
            raise FundException.ImproperSymbolFormatError(e)
        except FundException.SymbolDoesNotExistError as e:
            raise FundException.SymbolDoesNotExistError(e)
        except FundException.UIChangedError as e:
            raise FundException.UIChangedError(e)
        except FundException.SourceEndpointChangedError as e:
            raise FundException.SourceEndpointChangedError(e)

        return response
Example #5
0
def validate_format(fund_symbol):
    if len(fund_symbol) != 5 or re.match('^[A-Z]{5}$', fund_symbol) is None:
        raise FundException.ImproperSymbolFormatError(f"Fund symbol is not in the proper format (needs to be 5 characters, capitalized, no spaces, A-Z): {fund_symbol}")