//  [python]
def vol_slice_fit(bid_vol: pd.Series,
                  ask_vol: pd.Series,
                  model_vols: Union[pd.Series, pd.DataFrame],
                  title: str = None,
                  strike_name: str = 'strike',
                  bid_name: str = 'bid',
                  ask_name: str = 'ask',
                  mid_name: str = 'mid',
                  model_color: str = 'black',
                  bid_color: str = 'red',
                  ask_color: str = 'green',
                  mid_color: str = 'lightslategrey',
                  is_add_mids: bool = False,
                  atm_points: Dict[str, Tuple[float, float]] = None,
                  yvar_format: str = '{:.0%}',
                  xvar_format: Optional[str] = '{:0,.0f}',
                  fontsize: int = 12,
                  ylabel: str = 'Implied vols',
                  x_rotation: int = 0,
                  ax: plt.Subplot = None,
                  **kwargs
                  ) -> Optional[plt.Figure]:

    if ax is None:
        fig, ax = plt.subplots(1, 1, figsize=(8, 8))
    else:
        fig = None

    if isinstance(model_vols, pd.Series):  # optimise for frame
        model_vols = model_vols.to_frame()

    # for legend
    lines = []

    # add fitted vols line
    if len(model_vols.columns) == 1:
        palette = [model_color]
    else:
        palette = sns.husl_palette(len(model_vols.columns), h=.5)
    sns.lineplot(data=model_vols, palette=palette, dashes=False, ax=ax)
    for legend, color in zip(model_vols.columns, palette):
        lines.append((legend, {'color': color}))

    # add mids with error bars
    if is_add_mids:
        aligned_vols = pd.concat([bid_vol, ask_vol], axis=1)
        mid = np.mean(aligned_vols.to_numpy(), axis=1)
        lower_error = aligned_vols.iloc[:, 0].to_numpy()
        upper_error = aligned_vols.iloc[:, 1].to_numpy()
        error = sns.utils.ci_to_errsize((lower_error, upper_error), mid)
        ax.errorbar(x=aligned_vols.index.to_numpy(), y=mid, yerr=error, fmt='o', color=mid_color)
        lines.append((mid_name, {'color': mid_color, 'linestyle': '', 'marker': 'o'}))

    # add bid ask scatter
    legends_data = {bid_name: bid_color, ask_name: ask_color}
    for vol, legend in zip([bid_vol, ask_vol], legends_data.keys()):
        vol.index.name = strike_name
        vol.name = legend
        data = vol.to_frame().reset_index()
        sns.scatterplot(x=strike_name, y=legend, data=data, color=legends_data[legend],
                        edgecolor=None,
                        facecolor=None,
                        s=40,
                        linewidth=3,
                        marker='_',
                        ax=ax)
        lines.append((legend, {'color': legends_data[legend], 'linestyle': '', 'marker': '_'}))

    # atm points
    if atm_points is not None:
        for key, (x, y) in atm_points.items():
            ax.scatter(x, y, marker='*', color='navy', s=40, linewidth=5)
            lines.append(('ATM', {'color': 'navy', 'linestyle': '', 'marker': '*'}))

    ax.legend([create_dummy_line(**l[1]) for l in lines],  # Line handles
              [l[0] for l in lines],  # Line titles
              loc='upper center',
              framealpha=0,
              fontsize=fontsize)
    get_legend_colors(ax)

    if x_rotation != 0:
        [tick.set_rotation(x_rotation) for tick in ax.get_xticklabels()]

    # ticks
    if xvar_format is not None:
        ax.xaxis.set_major_formatter(mticker.FuncFormatter(lambda z, _: xvar_format.format(z)))
    ax.yaxis.set_major_formatter(mticker.FuncFormatter(lambda z, _: yvar_format.format(z)))

    ax.set_ylabel(ylabel, fontsize=fontsize)
    if title is not None:
        ax.set_title(title, fontsize=fontsize, color='darkblue')

    return fig