Example #1
0
    def risk_contribution_chart(self) -> BarChart:
        colors_palette = Chart.get_axes_colors()

        tickers = self.model.input_data.regressors_df.columns.values
        names = [self._get_security_name(ticker) for ticker in tickers]
        risk_contributions = QFSeries(data=self.model.risk_contribution.values,
                                      index=pd.Index(names))

        index_translator = self._get_index_translator(labels=names)
        bar_chart = BarChart(orientation=Orientation.Horizontal,
                             index_translator=index_translator,
                             thickness=self._bars_width,
                             align='center')
        bar_chart.add_decorator(
            DataElementDecorator(risk_contributions, color=colors_palette[1]))
        bar_chart.add_decorator(TitleDecorator("Risk contribution"))
        bar_chart.add_decorator(
            AxesLabelDecorator(x_label="risk contribution [%]"))
        bar_chart.add_decorator(
            AxesFormatterDecorator(x_major=PercentageFormatter()))
        labels = ('{:.2f}'.format(value * 100) for value in risk_contributions)
        self._add_labels_for_bars(bar_chart,
                                  risk_contributions,
                                  labels,
                                  margin=0.001)

        return bar_chart
Example #2
0
    def historical_performance_chart(self) -> LineChart:
        frequency = self.model.input_data.frequency
        analysed_tms = self.model.input_data.analysed_tms
        fitted_tms = self.model.fitted_tms

        cumulative_fund_rets = analysed_tms.to_prices(initial_price=1.0,
                                                      frequency=frequency) - 1
        cumulative_fit_rets = fitted_tms.to_prices(initial_price=1.0,
                                                   frequency=frequency) - 1

        hist_performance_chart = LineChart()
        fund_cummulative_rets_data_elem = DataElementDecorator(
            cumulative_fund_rets)
        fit_cummulative_rets_data_elem = DataElementDecorator(
            cumulative_fit_rets)

        legend_decorator = LegendDecorator(
            legend_placement=Location.LOWER_RIGHT)
        legend_decorator.add_entry(fund_cummulative_rets_data_elem,
                                   self._get_security_name(analysed_tms.name))
        legend_decorator.add_entry(fit_cummulative_rets_data_elem, 'Fit')

        hist_performance_chart.add_decorator(fund_cummulative_rets_data_elem)
        hist_performance_chart.add_decorator(fit_cummulative_rets_data_elem)

        hist_performance_chart.add_decorator(
            TitleDecorator("Historical Performance"))
        hist_performance_chart.add_decorator(
            AxesLabelDecorator(y_label="Cumulative return"))
        hist_performance_chart.add_decorator(legend_decorator)
        hist_performance_chart.add_decorator(
            AxesFormatterDecorator(y_major=PercentageFormatter()))

        return hist_performance_chart
Example #3
0
 def decorate(self, chart: "Chart") -> None:
     drawdown_series = drawdown_tms(self.series)
     drawdown_series *= -1
     ax = chart.axes
     ax.yaxis.set_major_formatter(PercentageFormatter())
     ax.fill_between(drawdown_series.index,
                     0,
                     drawdown_series.values,
                     alpha=self._colors_alpha)
     ax.set_ylim(top=0)
Example #4
0
    def _add_rolling_alpha_and_beta(self, timeseries_list):
        freq = timeseries_list[0].get_frequency()
        timeseries_list = [
            tms.dropna().to_simple_returns() for tms in timeseries_list
        ]
        df = pd.concat(timeseries_list, axis=1).fillna(0)

        rolling_window_len = int(freq.value / 2)  # 6M rolling
        step = round(freq.value / 6)  # 2M shift

        legend = LegendDecorator()
        chart = LineChart(start_x=df.index[0], end_x=df.index[-1])
        line_decorator = HorizontalLineDecorator(0, key="h_line", linewidth=1)
        chart.add_decorator(line_decorator)

        def alpha_function(df_in_window):
            strategy_returns = df_in_window.iloc[:, 0]
            benchmark_returns = df_in_window.iloc[:, 1]
            beta, alpha, _, _, _ = stats.linregress(benchmark_returns,
                                                    strategy_returns)
            return beta, alpha

        rolling = df.rolling_time_window(rolling_window_len, step,
                                         alpha_function)
        rolling = pd.DataFrame([[b, a] for b, a in rolling.values],
                               columns=["beta", "alpha"],
                               index=rolling.index)

        rolling_element = DataElementDecorator(rolling["beta"])
        chart.add_decorator(rolling_element)
        legend.add_entry(rolling_element, "beta")

        rolling_element = DataElementDecorator(rolling["alpha"],
                                               use_secondary_axes=True)
        chart.add_decorator(rolling_element)
        legend.add_entry(rolling_element, "alpha")

        chart.add_decorator(legend)
        chart.add_decorator(
            AxesFormatterDecorator(use_secondary_axes=True,
                                   y_major=PercentageFormatter(".1f")))

        # modify axes position to make secondary scale visible
        axes_position = list(self.full_image_axis_position)
        axes_position[2] = axes_position[2] - 0.07
        position_decorator = AxesPositionDecorator(*axes_position)
        chart.add_decorator(position_decorator)

        title_str = "Rolling alpha and beta [{} {} samples]".format(
            rolling_window_len, freq)
        title_decorator = TitleDecorator(title_str, key="title")
        chart.add_decorator(title_decorator)
        self.document.add_element(
            ChartElement(chart, figsize=self.full_image_size, dpi=self.dpi))
def create_returns_bar_chart(
        returns: QFSeries,
        frequency: Frequency = Frequency.YEARLY) -> BarChart:
    """
    Constructs a new returns bar chart based on the returns specified. By default a new annual returns bar chart will
    be created.
    """
    colors = Chart.get_axes_colors()
    # Calculate data.
    aggregate_returns = get_aggregate_returns(returns,
                                              frequency,
                                              multi_index=True)
    data_series = QFSeries(aggregate_returns.sort_index(ascending=True))

    chart = BarChart(Orientation.Horizontal, align="center")
    chart.add_decorator(DataElementDecorator(data_series))
    chart.add_decorator(BarValuesDecorator(data_series))

    # Format the x-axis so that its labels are shown as a percentage.
    chart.add_decorator(AxesFormatterDecorator(x_major=PercentageFormatter()))

    # Format Y axis to make sure we have a tick for each year or 2 years
    if len(data_series) > 10:
        y_labels = data_series[data_series.index % 2 == 1].index
    else:
        y_labels = data_series.index
    chart.add_decorator(
        AxisTickLabelsDecorator(labels=y_labels,
                                axis=Axis.Y,
                                tick_values=y_labels))

    # Add an average line.
    avg_line = VerticalLineDecorator(aggregate_returns.values.mean(),
                                     color=colors[1],
                                     key="avg_line",
                                     linestyle="--",
                                     alpha=0.8)
    chart.add_decorator(avg_line)

    # Add a legend.
    legend = LegendDecorator(key="legend_decorator")
    legend.add_entry(avg_line, "Mean")
    chart.add_decorator(legend)

    # Add a title.
    title = TitleDecorator(str(frequency).capitalize() + " Returns",
                           key="title_decorator")
    chart.add_decorator(title)
    chart.add_decorator(AxesLabelDecorator("Returns", "Year"))

    return chart
Example #6
0
    def _plot_data(self, datapoints_tms, regression_line, beta, alpha, r_squared, max_ret):
        colors = Chart.get_axes_colors()

        self.axes.scatter(x=datapoints_tms.iloc[:, 0], y=datapoints_tms.iloc[:, 1],
                          c=colors[0], alpha=0.6, edgecolors='black', linewidths=0.5)

        self.axes.axhline(0, color='black', axes=self.axes, linewidth=1)
        self.axes.axvline(0, color='black', axes=self.axes, linewidth=1)

        self.axes.plot(regression_line.index.values, regression_line.values, axes=self.axes, color=colors[1])

        self.axes.set_xlim([-max_ret, max_ret])
        self.axes.set_ylim([-max_ret, max_ret])

        props = dict(boxstyle='square', facecolor='white', alpha=0.5)
        textstr = '$\\beta={0:.2f}$\n$\\alpha={1:.2%}$$\%$\n$R^2={2:.2}$'.format(beta, alpha, r_squared)
        font_size = mpl.rcParams['legend.fontsize']

        self.axes.text(
            0.05, 0.95, textstr, transform=self.axes.transAxes, bbox=props, verticalalignment='top', fontsize=font_size)

        self.axes.xaxis.set_major_formatter(PercentageFormatter())
        self.axes.yaxis.set_major_formatter(PercentageFormatter())
Example #7
0
    def _add_returns_distribution(self):
        if self.initial_risk is not None:
            returns = SimpleReturnsSeries(data=[t.percentage_pnl / self.initial_risk for t in self.trades])
            title = "Distribution of R multiples, Initial risk = {:.2%}".format(self.initial_risk)
            returns_histogram = self._get_distribution_plot(returns, title)
        else:
            returns = SimpleReturnsSeries(data=[t.percentage_pnl for t in self.trades])
            title = "Distribution of returns [%]"
            returns_histogram = self._get_distribution_plot(returns, title)

            # Format the x-axis so that its labels are shown as a percentage in case of percentage returns
            axes_formatter_decorator = AxesFormatterDecorator(x_major=PercentageFormatter(), key="axes_formatter")
            returns_histogram.add_decorator(axes_formatter_decorator)

        self.document.add_element(ChartElement(returns_histogram, figsize=self.full_image_size, dpi=self.dpi))
Example #8
0
    def apply_data_element_decorators(
            self, data_element_decorators: List["DataElementDecorator"]):
        colors = cycle(Chart.get_axes_colors())

        for data_element in data_element_decorators:
            plot_settings = data_element.plot_settings.copy()
            plot_settings.setdefault("color", next(colors))

            series = data_element.data
            trimmed_series = self._trim_data(series)
            drawdown_series = drawdown_tms(trimmed_series)
            drawdown_series *= -1

            axes = self._ax
            axes.yaxis.set_major_formatter(PercentageFormatter())
            axes.fill_between(drawdown_series.index, 0, drawdown_series.values)
            axes.set_ylim(top=0)
Example #9
0
    def performance_attribution_chart(self) -> BarChart:
        colors_palette = Chart.get_axes_colors()

        unexplained_ret = self.model.unexplained_performance_attribution_ret
        factors_ret = self.model.factors_performance_attribution_ret
        fund_ret = self.model.fund_tms_analysis.cagr

        unexplained_name = "Unexplained"
        factors_names = [
            self._get_security_name(ticker)
            for ticker in self.model.coefficients.index.values
        ]

        fund_name = self._get_security_name(
            self.model.input_data.analysed_tms.name)

        all_values = [unexplained_ret] + list(factors_ret) + [fund_ret]
        all_names = [unexplained_name] + list(factors_names) + [fund_name]
        all_returns = SimpleReturnsSeries(data=all_values,
                                          index=pd.Index(all_names))

        colors = [
            colors_palette[0]
        ] + [colors_palette[1]] * len(factors_names) + [colors_palette[2]]

        index_translator = self._get_index_translator(labels=all_names)
        bar_chart = BarChart(orientation=Orientation.Horizontal,
                             index_translator=index_translator,
                             thickness=self._bars_width,
                             align='center')
        bar_chart.add_decorator(DataElementDecorator(all_returns,
                                                     color=colors))
        bar_chart.add_decorator(
            TitleDecorator("Attribution of Fund Annualised Return"))
        bar_chart.add_decorator(
            AxesLabelDecorator(x_label="annualised return [%]"))
        bar_chart.add_decorator(
            AxesFormatterDecorator(x_major=PercentageFormatter()))

        labels = ('{:.2f}'.format(value * 100) for value in all_returns)
        self._add_labels_for_bars(bar_chart, all_returns, labels)

        return bar_chart
Example #10
0
    def _add_rolling_chart(self):
        days_rolling = int(252 / 2)  # 6M rolling
        step = round(days_rolling / 5)

        strategy = self.strategy_series.to_prices(1)
        chart = LineChart(start_x=strategy.index[0], end_x=strategy.index[-1])
        line_decorator = HorizontalLineDecorator(0, key="h_line", linewidth=1)
        chart.add_decorator(line_decorator)

        legend = LegendDecorator()

        def tot_return(window):
            return PricesSeries(window).total_cumulative_return()

        def volatility(window):
            return get_volatility(PricesSeries(window), Frequency.DAILY)

        functions = [tot_return, volatility]
        names = ['Rolling Return', 'Rolling Volatility']
        for func, name in zip(functions, names):
            rolling = strategy.rolling_window(days_rolling, func, step=step)
            rolling_element = DataElementDecorator(rolling)
            chart.add_decorator(rolling_element)
            legend.add_entry(rolling_element, name)

        chart.add_decorator(legend)

        chart.add_decorator(
            AxesFormatterDecorator(y_major=PercentageFormatter(".0f")))

        left, bottom, width, height = self.full_image_axis_position
        position_decorator = AxesPositionDecorator(left, bottom, width, height)
        chart.add_decorator(position_decorator)

        title_decorator = TitleDecorator(
            "Rolling Statistics [6 Months]".format(days_rolling), key="title")
        chart.add_decorator(title_decorator)

        self.document.add_element(
            ChartElement(chart, figsize=self.full_image_size, dpi=self.dpi))
Example #11
0
    def _get_rolling_ret_and_vol_chart(self, timeseries):
        freq = timeseries.get_frequency()

        rolling_window_len = int(freq.value / 2)  # 6M rolling
        step = round(freq.value / 6)  # 2M shift

        tms = timeseries.to_prices(1)
        chart = LineChart(start_x=tms.index[0], end_x=tms.index[-1])
        line_decorator = HorizontalLineDecorator(0, key="h_line", linewidth=1)
        chart.add_decorator(line_decorator)

        legend = LegendDecorator()

        def tot_return(window):
            return PricesSeries(window).total_cumulative_return()

        def volatility(window):
            return get_volatility(PricesSeries(window), freq)

        functions = [tot_return, volatility]
        names = ['Rolling Return', 'Rolling Volatility']
        for func, name in zip(functions, names):
            rolling = tms.rolling_window(rolling_window_len, func, step=step)
            rolling_element = DataElementDecorator(rolling)
            chart.add_decorator(rolling_element)
            legend.add_entry(rolling_element, name)

        chart.add_decorator(legend)
        chart.add_decorator(
            AxesFormatterDecorator(y_major=PercentageFormatter(".0f")))

        position_decorator = AxesPositionDecorator(
            *self.full_image_axis_position)
        chart.add_decorator(position_decorator)
        title_str = "Rolling Stats [{} {} samples]".format(
            rolling_window_len, freq)

        title_decorator = TitleDecorator(title_str, key="title")
        chart.add_decorator(title_decorator)
        return chart
Example #12
0
    def _add_simulation_results(self):
        """
        Generate a data frame consisting of a certain number of "scenarios" (each scenario denotes one single equity
        curve).
        """
        self.document.add_element(NewPageElement())
        self.document.add_element(HeadingElement(level=1, text="Monte Carlo simulations\n"))
        self.document.add_element(HeadingElement(level=2, text="Average number of trades per year: {}\n".format(
            int(self._average_number_of_trades_per_year()))))
        if self.initial_risk is not None:
            self.document.add_element(HeadingElement(level=2, text="Initial risk: {:.2%}".format(self.initial_risk)))

        scenarios_df, total_returns = self._get_scenarios()

        # Plot all the possible paths on a chart
        all_paths_chart = self._get_simulation_plot(scenarios_df)
        self.document.add_element(ChartElement(all_paths_chart, figsize=self.full_image_size, dpi=self.dpi))

        # Plot the distribution plot
        distribution_plot = self._get_distribution_plot(
            total_returns, title="Monte Carlo Simulations Distribution (one year % return)", bins=200, crop=True)
        # Format the x-axis so that its labels are shown as a percentage in case of percentage returns

        axes_formatter_decorator = AxesFormatterDecorator(x_major=PercentageFormatter(), key="axes_formatter")
        distribution_plot.add_decorator(axes_formatter_decorator)

        self.document.add_element(ChartElement(distribution_plot, figsize=self.full_image_size, dpi=self.dpi))

        simulations_summary_table = self._get_monte_carlos_simulator_outputs(scenarios_df, total_returns)
        self.document.add_element(simulations_summary_table)

        # Extract the results of each of the scenarios and summarize the data in the tables
        dist_summary_tables = self._get_distribution_summary_table(total_returns)
        self.document.add_element(dist_summary_tables)

        # Add the "Chances of dropping below" and "Simulations summary" tables
        ruin_chances_table = self._get_chances_of_dropping_below_table(scenarios_df)
        self.document.add_element(ruin_chances_table)
Example #13
0
    def _get_rolling_chart(self, timeseries_list, rolling_function,
                           function_name):
        freq = timeseries_list[0].get_frequency()
        timeseries_list = [
            tms.dropna().to_prices(1) for tms in timeseries_list
        ]
        df = pd.concat(timeseries_list, axis=1).fillna(method='ffill')

        rolling_window_len = int(freq.value / 2)  # 6M rolling
        step = round(freq.value / 6)  # 2M shift

        legend = LegendDecorator()

        chart = LineChart(start_x=df.index[0], end_x=df.index[-1])
        line_decorator = HorizontalLineDecorator(0, key="h_line", linewidth=1)
        chart.add_decorator(line_decorator)

        for _, tms in df.iteritems():
            rolling = tms.rolling_window(rolling_window_len,
                                         rolling_function,
                                         step=step)
            rolling_element = DataElementDecorator(rolling)
            chart.add_decorator(rolling_element)
            legend.add_entry(rolling_element, tms.name)

        chart.add_decorator(legend)
        chart.add_decorator(
            AxesFormatterDecorator(y_major=PercentageFormatter(".0f")))

        position_decorator = AxesPositionDecorator(
            *self.full_image_axis_position)
        chart.add_decorator(position_decorator)
        title_str = "{} - Rolling Stats [{} {} samples]".format(
            function_name, rolling_window_len, freq)

        title_decorator = TitleDecorator(title_str, key="title")
        chart.add_decorator(title_decorator)
        return chart
Example #14
0
    def regressors_and_explained_variable_chart(self) -> LineChart:
        regressors_df = self.model.input_data.regressors_df
        fund_tms = self.model.input_data.analysed_tms

        chart = LineChart()
        legend = LegendDecorator()

        # add data to the chart and the legend
        marker_props_template = {'alpha': 0.5}
        stemline_props_template = {'linestyle': '-.', 'linewidth': 0.2}
        baseline_props = {'visible': False}

        regressors_and_fund_df = pd.concat([regressors_df, fund_tms], axis=1)
        colors = cycle(Chart.get_axes_colors())

        for ticker, series in regressors_and_fund_df.iteritems():
            marker_props = marker_props_template.copy()
            stemline_props = stemline_props_template.copy()

            color = next(colors)
            marker_props['markeredgecolor'] = color
            marker_props['markerfacecolor'] = color
            stemline_props['color'] = color
            data_elem = StemDecorator(series,
                                      marker_props=marker_props,
                                      stemline_props=stemline_props,
                                      baseline_props=baseline_props)
            chart.add_decorator(data_elem)
            legend.add_entry(data_elem, self._get_security_name(ticker))

        # add decorators to the chart
        chart.add_decorator(TitleDecorator("Returns"))
        chart.add_decorator(AxesLabelDecorator(y_label="return [%]"))
        chart.add_decorator(legend)
        chart.add_decorator(
            AxesFormatterDecorator(y_major=PercentageFormatter()))

        return chart
Example #15
0
    def _plot_data(self, annual_returns_tms):
        self.axes.yaxis.set_major_formatter(PercentageFormatter())
        self.axes.yaxis.set_major_locator(MaxNLocator(steps=[1, 2, 5, 10], nbins=20, symmetric=True))

        # plot the line for mean
        mean_annual_return = annual_returns_tms.mean()
        self.axes.axhline(mean_annual_return, color='grey', linestyle='--', alpha=0.7)

        # plot the line for 0Y
        self.axes.axhline(0, color='black', linestyle='-')

        # plot yearly returns as bars
        self.axes.xaxis.set_major_formatter(DateFormatter('%Y-%m'))
        annual_returns_tms.plot(ax=self.axes, kind='bar', alpha=0.7, width=0.8)

        self.axes.set_xlabel('Year')
        self.axes.set_ylabel('Returns')
        self.axes.set_title("Annual Returns")

        mean_label = 'mean={0:.2%}'.format(mean_annual_return)
        self.axes.legend([mean_label], loc='best')

        self._adjust_style()
Example #16
0
    def _get_rolling_chart(self, timeseries_list, rolling_function,
                           function_name):
        days_rolling = int(BUSINESS_DAYS_PER_YEAR / 2)  # 6M rolling
        step = round(days_rolling / 5)

        legend = LegendDecorator()
        chart = None

        for i, tms in enumerate(timeseries_list):
            if i == 0:
                chart = LineChart(start_x=tms.index[0], end_x=tms.index[-1])
                line_decorator = HorizontalLineDecorator(0,
                                                         key="h_line",
                                                         linewidth=1)
                chart.add_decorator(line_decorator)

            tms = tms.to_prices(1)
            rolling = tms.rolling_window(days_rolling,
                                         rolling_function,
                                         step=step)
            rolling_element = DataElementDecorator(rolling)
            chart.add_decorator(rolling_element)
            legend.add_entry(rolling_element, tms.name)

        chart.add_decorator(legend)
        chart.add_decorator(
            AxesFormatterDecorator(y_major=PercentageFormatter(".0f")))

        position_decorator = AxesPositionDecorator(
            *self.full_image_axis_position)
        chart.add_decorator(position_decorator)
        title_str = "{} - Rolling Stats [{} days]".format(
            function_name, days_rolling)

        title_decorator = TitleDecorator(title_str, key="title")
        chart.add_decorator(title_decorator)
        return chart
Example #17
0
    def _add_relative_performance_chart(
            self,
            strategy_tms: QFSeries,
            benchmark_tms: QFSeries,
            chart_title: str = "Relative Performance",
            legend_subtitle: str = "Strategy - Benchmark"):
        diff = strategy_tms.to_simple_returns().subtract(
            benchmark_tms.to_simple_returns(), fill_value=0)
        diff = diff.to_prices(1) - 1

        chart = LineChart(start_x=diff.index[0],
                          end_x=diff.index[-1],
                          log_scale=False)
        position_decorator = AxesPositionDecorator(
            *self.full_image_axis_position)
        chart.add_decorator(position_decorator)

        line_decorator = HorizontalLineDecorator(0, key="h_line", linewidth=1)
        chart.add_decorator(line_decorator)
        legend = LegendDecorator()

        series_elem = DataElementDecorator(diff)
        chart.add_decorator(series_elem)
        legend.add_entry(series_elem, legend_subtitle)

        chart.add_decorator(legend)
        title_decorator = TitleDecorator(chart_title, key="title")
        chart.add_decorator(title_decorator)

        chart.add_decorator(
            AxesFormatterDecorator(y_major=PercentageFormatter(".0f")))

        fill_decorator = FillBetweenDecorator(diff)
        chart.add_decorator(fill_decorator)
        self.document.add_element(
            ChartElement(chart, figsize=self.full_image_size, dpi=self.dpi))
Example #18
0
    def historical_out_of_sample_performance_chart(self) -> LineChart:
        analysed_tms = self.model.input_data.analysed_tms
        frequency = self.model.input_data.frequency
        fund_cumulative_rets = analysed_tms.to_prices(
            initial_price=1.0, frequency=frequency) - 1  # type: PricesSeries
        fit_cumulative_rets = self.model.fitted_tms.to_prices(
            initial_price=1.0, frequency=frequency) - 1  # type: PricesSeries

        live_start_date = self.model.oos_start_date

        in_sample_fund_tms = fund_cumulative_rets.loc[:live_start_date]
        in_sample_fit_tms = fit_cumulative_rets.loc[:live_start_date]

        out_of_sample_fund_tms = fund_cumulative_rets.loc[live_start_date:]
        out_of_sample_fit_tms = fit_cumulative_rets.loc[live_start_date:]

        colors = Chart.get_axes_colors()

        in_sample_fund_data_elem = DataElementDecorator(in_sample_fund_tms,
                                                        color=colors[0])
        out_of_sample_fund_data_elem = DataElementDecorator(
            out_of_sample_fund_tms, color=colors[0])

        in_sample_fit_data_elem = DataElementDecorator(in_sample_fit_tms,
                                                       color=colors[1])
        out_of_sample_fit_data_elem = DataElementDecorator(
            out_of_sample_fit_tms, color=colors[1])

        legend_decorator = LegendDecorator(
            legend_placement=Location.LOWER_RIGHT)
        legend_decorator.add_entry(in_sample_fund_data_elem,
                                   self._get_security_name(analysed_tms.name))
        legend_decorator.add_entry(in_sample_fit_data_elem, 'Fit')

        is_vs_oos_performance_chart = LineChart()
        is_vs_oos_performance_chart.add_decorator(in_sample_fund_data_elem)
        is_vs_oos_performance_chart.add_decorator(out_of_sample_fund_data_elem)
        is_vs_oos_performance_chart.add_decorator(in_sample_fit_data_elem)
        is_vs_oos_performance_chart.add_decorator(out_of_sample_fit_data_elem)

        is_vs_oos_performance_chart.add_decorator(
            AxesFormatterDecorator(y_major=PercentageFormatter()))
        is_vs_oos_performance_chart.add_decorator(
            AxesLabelDecorator(y_label="Cumulative return [%]"))
        is_vs_oos_performance_chart.add_decorator(legend_decorator)

        is_vs_oos_performance_chart.add_decorator(
            TextDecorator("In Sample  ",
                          x=DataCoordinate(live_start_date),
                          y=AxesCoordinate(0.99),
                          verticalalignment='top',
                          horizontalalignment='right'))

        is_vs_oos_performance_chart.add_decorator(
            TextDecorator("  Out Of Sample",
                          x=DataCoordinate(live_start_date),
                          y=AxesCoordinate(0.99),
                          verticalalignment='top',
                          horizontalalignment='left'))
        last_date = fund_cumulative_rets.index[-1]
        is_vs_oos_performance_chart.add_decorator(
            VerticalSpanDecorator(x_min=live_start_date, x_max=last_date))

        return is_vs_oos_performance_chart