def val_signals(self,
                    variant='daily',
                    func=None,
                    shares_index=SHARES_DILUTED):
        """
        Calculate valuation signals such as P/E and P/Sales ratios.

        :param variant:
            String with the frequency of the results. Valid options:

            - 'daily': The result has daily data-points, same as share-prices.
            - 'latest' The result is only for the latest share-price dates.

        :param func:
            Function to apply on a per-stock basis on the financial data,
            before calculating the valuation signals. This is useful e.g. to
            calculate multi-year averages of the Net Income and Revenue and
            use those when calculating P/E and P/Sales ratios. This should
            be a real function (not a lambda-function) because its name will
            be used in the cache-filename. Example: `func=sf.avg_ttm_2y`

        :param shares_index:
            String with the column-name for the share-counts. SHARES_DILUTED
            takes the potential diluting impact of stock-options into account,
            so it results in more conservative valuation ratios than
            SHARES_BASIC.

        :return:
            Pandas DataFrame
        """

        # Load the required datasets.
        # This is only really necessary if the cache-file needs refreshing,
        # but it is easier to program like this and the overhead is small.
        df_prices = self.load_shareprices(variant=variant)
        df_income_ttm = self.load_income(variant='ttm')
        df_balance_ttm = self.load_balance(variant='ttm')
        df_cashflow_ttm = self.load_cashflow(variant='ttm')

        # List of datasets used to determine if disk-cache must be refreshed.
        datasets = [('shareprices', variant), ('income', 'ttm'),
                    ('balance', 'ttm'), ('cashflow', 'ttm')]

        # List of arguments used to uniquely identify the cache-file.
        cache_ids = [variant, _func_name(func=func), shares_index]

        # Create dict with disk-cache arguments.
        cache_args = self._cache_args(datasets=datasets, cache_ids=cache_ids)

        # Calculate the signals, or load the DataFrame from the disk-cache.
        df_result = val_signals(df_prices=df_prices,
                                df_income_ttm=df_income_ttm,
                                df_balance_ttm=df_balance_ttm,
                                df_cashflow_ttm=df_cashflow_ttm,
                                shares_index=shares_index,
                                func=func,
                                **self._signal_args,
                                **cache_args)

        return df_result
    def fin_signals(self, variant='daily', func=None):
        """
        Calculate financial signals such as Net Profit Margin, Debt Ratio, etc.

        :param variant:
            String with the frequency of the results. Valid options:

            - 'quarterly': The result has 4 data-points per year.
            - 'daily': The result has daily data-points, same as share-prices.
            - 'latest' The result is only for the latest share-price dates.

        :param func:
            Function to apply on a per-stock basis after the signals have been
            calculated, but before they have been reindexed to daily
            data-points. This is useful e.g. to calculate multi-year averages.
            This should be a real function (not a lambda-function) because its
            name will be used in the cache-filename.
            Example: `func=sf.avg_ttm_2y` to calculate 2-year averages.
            Example: `func=sf.rel_change_ttm_1y` to calculate 1-year change.

        :return:
            Pandas DataFrame
        """

        # Load the required datasets.
        # This is only really necessary if the cache-file needs refreshing,
        # but it is easier to program like this and the overhead is small.
        df_income_ttm = self.load_income(variant='ttm')
        df_balance_ttm = self.load_balance(variant='ttm')
        df_cashflow_ttm = self.load_cashflow(variant='ttm')

        # List of datasets used to determine if disk-cache must be refreshed.
        datasets = [('income', 'ttm'), ('balance', 'ttm'), ('cashflow', 'ttm')]

        # Load dataset with shareprices?
        if variant in ['daily', 'latest']:
            # Load share-prices for the given variant.
            df_prices = self.load_shareprices(variant=variant)

            # Append to the list of datasets we are using here.
            datasets.append(('shareprices', variant))
        elif variant == 'quarterly':
            # Share-prices are not used to reindex the results.
            df_prices = None
        else:
            # Raise exception.
            msg = 'invalid arg variant={0}'.format(variant)
            raise ValueError(msg)

        # List of arguments used to uniquely identify the cache-file.
        cache_ids = [variant, _func_name(func=func)]

        # Create dict with disk-cache arguments.
        cache_args = self._cache_args(datasets=datasets, cache_ids=cache_ids)

        # Calculate the signals, or load the DataFrame from the disk-cache.
        df_result = fin_signals(df_income_ttm=df_income_ttm,
                                df_balance_ttm=df_balance_ttm,
                                df_cashflow_ttm=df_cashflow_ttm,
                                df_prices=df_prices,
                                func=func,
                                **self._signal_args,
                                **cache_args)

        return df_result