Creates a tear sheet for returns analysis of a factor.

    Parameters
    ----------
    factor_data : pd.DataFrame - MultiIndex
        A MultiIndex DataFrame indexed by date (level 0) and asset (level 1),
        containing the values for a single alpha factor, forward returns for
        each period, the factor quantile/bin that factor value belongs to,
        and (optionally) the group the asset belongs to.
        - See full explanation in utils.get_clean_factor_and_forward_returns
    long_short : bool
        Should this computation happen on a long short portfolio? if so, then
        mean quantile returns will be demeaned across the factor universe.
        Additionally factor values will be demeaned across the factor universe
        when factor weighting the portfolio for cumulative returns plots
    group_neutral : bool
        Should this computation happen on a group neutral portfolio? if so,
        returns demeaning will occur on the group level.
        Additionally each group will weight the same in cumulative returns
        plots
    by_group : bool
        If True, display graphs separately for each group.

def create_returns_tear_sheet(factor_data,
                              long_short=True,
                              group_neutral=False,
                              by_group=False):
    """
    Creates a tear sheet for returns analysis of a factor.

    Parameters
    ----------
    factor_data : pd.DataFrame - MultiIndex
        A MultiIndex DataFrame indexed by date (level 0) and asset (level 1),
        containing the values for a single alpha factor, forward returns for
        each period, the factor quantile/bin that factor value belongs to,
        and (optionally) the group the asset belongs to.
        - See full explanation in utils.get_clean_factor_and_forward_returns
    long_short : bool
        Should this computation happen on a long short portfolio? if so, then
        mean quantile returns will be demeaned across the factor universe.
        Additionally factor values will be demeaned across the factor universe
        when factor weighting the portfolio for cumulative returns plots
    group_neutral : bool
        Should this computation happen on a group neutral portfolio? if so,
        returns demeaning will occur on the group level.
        Additionally each group will weight the same in cumulative returns
        plots
    by_group : bool
        If True, display graphs separately for each group.
    """

    factor_returns = perf.factor_returns(factor_data,
                                         long_short,
                                         group_neutral)

    mean_quant_ret, std_quantile = \
        perf.mean_return_by_quantile(factor_data,
                                     by_group=False,
                                     demeaned=long_short,
                                     group_adjust=group_neutral)

    mean_quant_rateret = \
        mean_quant_ret.apply(utils.rate_of_return, axis=0,
                             base_period=mean_quant_ret.columns[0])

    mean_quant_ret_bydate, std_quant_daily = \
        perf.mean_return_by_quantile(factor_data,
                                     by_date=True,
                                     by_group=False,
                                     demeaned=long_short,
                                     group_adjust=group_neutral)

    mean_quant_rateret_bydate = mean_quant_ret_bydate.apply(
        utils.rate_of_return, axis=0,
        base_period=mean_quant_ret_bydate.columns[0]
    )

    compstd_quant_daily = \
        std_quant_daily.apply(utils.std_conversion, axis=0,
                              base_period=std_quant_daily.columns[0])

    alpha_beta = perf.factor_alpha_beta(factor_data,
                                        factor_returns,
                                        long_short,
                                        group_neutral)

    mean_ret_spread_quant, std_spread_quant = \
        perf.compute_mean_returns_spread(mean_quant_rateret_bydate,
                                         factor_data['factor_quantile'].max(),
                                         factor_data['factor_quantile'].min(),
                                         std_err=compstd_quant_daily)

    fr_cols = len(factor_returns.columns)
    vertical_sections = 2 + fr_cols * 3
    gf = GridFigure(rows=vertical_sections, cols=1)

    plotting.plot_returns_table(alpha_beta,
                                mean_quant_rateret,
                                mean_ret_spread_quant)

    plotting.plot_quantile_returns_bar(mean_quant_rateret,
                                       by_group=False,
                                       ylim_percentiles=None,
                                       ax=gf.next_row())

    plotting.plot_quantile_returns_violin(mean_quant_rateret_bydate,
                                          ylim_percentiles=(1, 99),
                                          ax=gf.next_row())

    trading_calendar = factor_data.index.levels[0].freq
    if trading_calendar is None:
        trading_calendar = pd.tseries.offsets.BDay()
        warnings.warn(
            "'freq' not set in factor_data index: assuming business day",
            UserWarning
        )

    for p in factor_returns:

        title = ('Factor Weighted '
                 + ('Group Neutral ' if group_neutral else '')
                 + ('Long/Short ' if long_short else '')
                 + "Portfolio Cumulative Return ({} Period)".format(p))

        plotting.plot_cumulative_returns(
            factor_returns[p],
            period=p,
            freq=trading_calendar,
            title=title,
            ax=gf.next_row()
        )

        plotting.plot_cumulative_returns_by_quantile(
            mean_quant_ret_bydate[p],
            period=p,
            freq=trading_calendar,
            ax=gf.next_row()
        )

    ax_mean_quantile_returns_spread_ts = [gf.next_row()
                                          for x in range(fr_cols)]
    plotting.plot_mean_quantile_returns_spread_time_series(
        mean_ret_spread_quant,
        std_err=std_spread_quant,
        bandwidth=0.5,
        ax=ax_mean_quantile_returns_spread_ts
    )

    plt.show()
    gf.close()

    if by_group:
        mean_return_quantile_group, mean_return_quantile_group_std_err = \
            perf.mean_return_by_quantile(factor_data,
                                         by_date=False,
                                         by_group=True,
                                         demeaned=long_short,
                                         group_adjust=group_neutral)

        mean_quant_rateret_group = mean_return_quantile_group.apply(
            utils.rate_of_return, axis=0,
            base_period=mean_return_quantile_group.columns[0]
        )

        num_groups = len(mean_quant_rateret_group.index
                         .get_level_values('group').unique())

        vertical_sections = 1 + (((num_groups - 1) // 2) + 1)
        gf = GridFigure(rows=vertical_sections, cols=2)

        ax_quantile_returns_bar_by_group = [gf.next_cell()
                                            for _ in range(num_groups)]
        plotting.plot_quantile_returns_bar(mean_quant_rateret_group,
                                           by_group=True,
                                           ylim_percentiles=(5, 95),
                                           ax=ax_quantile_returns_bar_by_group)
        plt.show()
        gf.close()