docs: def average_cumulative_return_by_quantile(factor_data,
                                            prices,
                                            periods_before=10,
                                            periods_after=15,
                                            demeaned=True,
                                            group_adjust=False,
                                            by_group=False):
      """
      Plots average cumulative returns by factor quantiles in the period range
      defined by -periods_before to periods_after
  
      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
      prices : pd.DataFrame
          A wide form Pandas DataFrame indexed by date with assets
          in the columns. Pricing data should span the factor
          analysis time period plus/minus an additional buffer window
          corresponding to periods_after/periods_before parameters.
      periods_before : int, optional
          How many periods before factor to plot
      periods_after  : int, optional
          How many periods after factor to plot
      demeaned : bool, optional
          Compute demeaned mean returns (long short portfolio)
      group_adjust : bool
          Returns demeaning will occur on the group level (group
          neutral portfolio)
      by_group : bool
          If True, compute cumulative returns separately for each group
      Returns
      -------
      cumulative returns and std deviation : pd.DataFrame
          A MultiIndex DataFrame indexed by quantile (level 0) and mean/std
          (level 1) and the values on the columns in range from
          -periods_before to periods_after
          If by_group=True the index will have an additional 'group' level
          ::
              ---------------------------------------------------
                          |       | -2  | -1  |  0  |  1  | ...
              ---------------------------------------------------
                quantile  |       |     |     |     |     |
              ---------------------------------------------------
                          | mean  |  x  |  x  |  x  |  x  |
                   1      ---------------------------------------
                          | std   |  x  |  x  |  x  |  x  |
              ---------------------------------------------------
                          | mean  |  x  |  x  |  x  |  x  |
                   2      ---------------------------------------
                          | std   |  x  |  x  |  x  |  x  |
              ---------------------------------------------------
                  ...     |                 ...
              ---------------------------------------------------
      """
  
      def cumulative_return(q_fact, demean_by):
          return common_start_returns(q_fact, prices,
                                      periods_before,
                                      periods_after,
                                      True, True, demean_by)
  
      def average_cumulative_return(q_fact, demean_by):
          q_returns = cumulative_return(q_fact, demean_by)
          return pd.DataFrame({'mean': q_returns.mean(axis=1),
                               'std': q_returns.std(axis=1)}).T
  
      if by_group:
          #
          # Compute quantile cumulative returns separately for each group
          # Deman those returns accordingly to 'group_adjust' and 'demeaned'
          #
          returns_bygroup = []
  
          for group, g_data in factor_data.groupby('group'):
              g_fq = g_data['factor_quantile']
              if group_adjust:
                  demean_by = g_fq  # demeans at group level
              elif demeaned:
                  demean_by = factor_data['factor_quantile']  # demean by all
              else:
                  demean_by = None
              #
              # Align cumulative return from different dates to the same index
              # then compute mean and std
              #
              avgcumret = g_fq.groupby(g_fq).apply(average_cumulative_return,
                                                   demean_by)
              if len(avgcumret) == 0:
                  continue
  
              avgcumret['group'] = group
              avgcumret.set_index('group', append=True, inplace=True)
              returns_bygroup.append(avgcumret)
  
          return pd.concat(returns_bygroup, axis=0)
  
      else:
          #
          # Compute quantile cumulative returns for the full factor_data
          # Align cumulative return from different dates to the same index
          # then compute mean and std
          # Deman those returns accordingly to 'group_adjust' and 'demeaned'
          #
          if group_adjust:
              all_returns = []
              for group, g_data in factor_data.groupby('group'):
                  g_fq = g_data['factor_quantile']
                  avgcumret = g_fq.groupby(g_fq).apply(cumulative_return, g_fq)
                  all_returns.append(avgcumret)
              q_returns = pd.concat(all_returns, axis=1)
              q_returns = pd.DataFrame({'mean': q_returns.mean(axis=1),
                                        'std': q_returns.std(axis=1)})
              return q_returns.unstack(level=1).stack(level=0)
          elif demeaned:
              fq = factor_data['factor_quantile']
              return fq.groupby(fq).apply(average_cumulative_return, fq)
          else:
              fq = factor_data['factor_quantile']
              return fq.groupby(fq).apply(average_cumulative_return, None)