def security_market_line(self, portfolio: Portfolio, date: datetime = None, regression_window: int = 36,
                             benchmark: pd.Series = None):
        """

        :param portfolio:
        :param date:
        :param regression_window:
        :param benchmark:
        :return:
        """
        # '''
        #     The Security Market Line (SML) graphically represents the relationship between the asset's return (on y-axis) and systematic risk (or beta, on x-axis).
        #     With E(R_i) = R_f + B_i * (E(R_m) - R_f), the y-intercept of the SML is equal to the risk-free interest rate, while the slope is equal to the market risk premium
        #     Plotting the SML for a market index (i.e. DJIA), individual assets that are correctly priced are plotted on the SML (in the ideal 'Efficient Market Hypothesis' world).
        #     In real market scenarios, we are able to use the SML graph to determine if an asset being considered for a portfolio offers a reasonable expected return for the risk.
        #     - If an asset is priced at a point above the SML, it is undervalued, since for a given amount of risk, it yields a higher return.
        #     - Conversely, an asset priced below the SML is overvalued, since for a given amount of risk, it yields a lower return.
        # '''
        frequency = self.factors_timedf.df_frequency
        portfolio_copy = portfolio.set_frequency(frequency, inplace=False) \
            .slice_dataframe(to_date=date, from_date=regression_window, inplace=False)

        betas = [
            self.regress_factor_loadings(portfolio=portfolio.df_returns[ticker], benchmark_returns=benchmark, date=date,
                                         regression_window=regression_window).params[1]
            for ticker in portfolio_copy.df_returns]

        mean_asset_returns = portfolio_copy.get_mean_returns()
        date = portfolio_copy.df_returns.index[-1] if date is None else date

        risk_free_rate = risk_free_rates(lookback=regression_window, to_date=date, frequency=frequency).mean() \
                         * freq_to_yearly[frequency[0]]

        risk_premium = market_premiums(lookback=regression_window, to_date=date, frequency=frequency).mean() \
                       * portfolio.freq_to_yearly[frequency[0]]

        x = np.linspace(0, max(betas) + 0.1, 100)
        y = float(risk_free_rate) + x * float(risk_premium)
        fig, ax = plt.subplots(figsize=(10, 10))
        plt.plot(x, y)
        ax.set_xlabel('Betas', fontsize=14)
        ax.set_ylabel('Expected Returns', fontsize=14)
        ax.set_title('Security Market Line', fontsize=18)

        for i, txt in enumerate(portfolio.df_returns):
            ax.annotate(txt, (betas[i], mean_asset_returns[i]), xytext=(10, 10), textcoords='offset points')
            plt.scatter(betas[i], mean_asset_returns[i], marker='x', color='red')

        plt.show()
class NestedClusteredOptimization(PortfolioAllocationModel):
    def __init__(self, portfolio: Portfolio):
        super().__init__(portfolio)

    def solve_weights(self, risk_metric=None, objective=None, leverage=0, long_short_exposure=0):
        pass


if __name__ == '__main__':
    from matilda.data_pipeline.db_crud import companies_in_classification
    from matilda import config

    assets = companies_in_classification(class_=config.MarketIndices.DOW_JONES)
    portfolio = Portfolio(assets=assets)
    portfolio.set_frequency(frequency='M', inplace=True)
    portfolio.slice_dataframe(from_date=datetime(2016, 1, 1), to_date=datetime(2020, 1, 1), inplace=True)
    print(portfolio.df_returns.tail(10))

    MPT = ModernPortfolioTheory(portfolio)
    weights = MPT.solve_weights(use_sharpe=True)
    print(weights)

    market_portfolio = Portfolio(assets='^DJI')
    market_portfolio.set_frequency(frequency='M', inplace=True)
    market_portfolio.slice_dataframe(from_date=datetime(2016, 1, 1), to_date=datetime(2020, 1, 1), inplace=True)

    stats = MPT.markowitz_efficient_frontier(market_portfolio=market_portfolio, plot_assets=True, plot_cal=True)
    pd.set_option('display.max_columns', None)
    print(stats.head())