Mplfinance: Feature Request: Adjust a panel's x_lim and title padding

Created on 8 Jul 2020  ·  12Comments  ·  Source: matplotlib/mplfinance

Hello @DanielGoldfarb,

Thanks for reviving _mplfinance_ and making it better! I like the new panel additions. They are awesome!

Is your feature request related to a problem? Please describe.
I was able to target mpf.make_addplot(ylim: tuple)) to adjust a _panel_'s top and bottom __ylim__'s so the _panel_'s are not too close. Is it possible to at least set xlim=(mpf.minx, 0.9 * mpf.max)?

The bottom padding for _title_ seems large. Possible bug? 🤷 Where self.title = "SPY (D)".

Here is the heart of the mpf plot code for the chart below:

mpf_style = mpf.make_mpf_style(base_mpf_style=mpf_styles[-1], rc={'figure.facecolor': '#EDEDED'})
width_config={'candle_linewidth':0.8, 'candle_width':0.525, 'volume_width': 0.525}

mpf.plot(
    self.df.tail(self.recent), type="candle", title=self.title,
    style=mpf_style, datetime_format="%-m - %-d - %Y",
    volume=True, addplot=priceta + volta + rsita,
    figsize=(16, 8), panel_ratios=(1, 0.2, .4), xrotation=32,
    update_width_config=width_config
)

Screen Shot 2020-07-07 at 11 28 00 PM

Describe the solution you'd like
Ideally, I would like the ability to adjust at least the left padding of each _panel_. Or a tight_layout with an adjustable right padding for each _panel_.

Thanks for your time,
KJ

enhancement released

All 12 comments

Kevin,

First l'd like to say that's a great looking plot. I like what you've done with mplfinance: creative and useful.

Please try setting tight_layout=True in the call to mpf.plot( ) and let me know how that works for you. That should automatically adjust your x-axis limits, and should also fix the extra padding around the title at the top of the Figure ("SPY (D)") by placing the title slightly lower and decreasing the padding somewhat.

Beyond that, there are two ways that you can adjust the Figure padding surround the panels. It cannot be done _per-panel_, because by definition the panels are all the same width, and the padding relates to the Figure as a whole.

mpf.plot( ) has a kwarg scale_padding which can be set in one of two ways:

  • scale_padding=<number> where number is an integer or float greater than or less than 1.0 to either increase or decrease the amount of padding around all four sides of the Figure.
  • scale_padding=<dict> where dict contains one or more of the following keys: left, right, top, bottom and the value associated with the key is an integer or float greater than or less than 1.0 to either increase or decrease the amount of padding on the side of the Figure indicated by the dict key(s).

Please note: tight_layout and scale_padding are independent of each other, so you can use one or both, and they will each apply the effect they were coded to apply. Both will affect the padding, but only tight_layout will also affect the x-axis limits and the placement of the Figure title.

HTH. All the best. --Daniel

Hi Daniel,

Thank you for the suggestions. Here are some of my results:

Using tight_layout

mpf_style = mpf.make_mpf_style(base_mpf_style=mpf_styles[-1], rc={'figure.facecolor': '#EDEDED'})
width_config={'candle_linewidth':0.8, 'candle_width':0.525, 'volume_width': 0.525}

mpf.plot(
    self.df.tail(self.recent), type="candle", title=self.title,
    style=mpf_style, datetime_format="%-m - %-d - %Y",
    volume=True, addplot=priceta + volta + rsita,
    figsize=(16, 8), panel_ratios=(1, 0.1, .2), xrotation=32,
    update_width_config=width_config, tight_layout=True
)

yields the following:

Screen Shot 2020-07-08 at 9 52 10 AM

Using tight_layout and scale_padding

So scale_padding.top = 5 helped adjust the title. Whereas scale_padding.left != 1 and scale_padding.right != 1 yield no meaningful results.

mpf_style = mpf.make_mpf_style(base_mpf_style=mpf_styles[-1], rc={'figure.facecolor': '#EDEDED'})
width_config={'candle_linewidth':0.8, 'candle_width':0.525, 'volume_width': 0.525}

mpf.plot(
    self.df.tail(self.recent), type="candle", title=self.title,
    style=mpf_style, datetime_format="%-m - %-d - %Y",
    volume=True, addplot=priceta + volta + rsita,
    figsize=(16, 8), scale_padding={'left': 1, 'top': 5, 'right': 1, 'bottom': 1},
    panel_ratios=(1, 0.2, .4), xrotation=32,
    update_width_config=width_config, tight_layout=True,
)

yields the following:

Screen Shot 2020-07-08 at 11 11 02 AM

That helps. That is the closest I can get to desired output, so I'll run with it. I am gonna refactor some code add it to my repo's example notebook.

Thanks again!
KJ

Thank you for posting your results.

Not sure why scale_padding.left != 1 and scale_padding.right != 1 are ineffective. It could be that tight_layout scales them down so much that you would need to either:

(a) try scale_padding _without_ tight_layout, or
(b) make left and right padding significantly more than 1.0 to counteract the downscaling of padding by tight_layout.

Please try _both_ (a) and (b) and let me know how it looks.
I will also consider adding a kwarg to adjust xlimits, since presently the only way to adjust xlimits is via tight_layout.

Thanks. --Daniel

Below are my results:

# static vars
df = self.df.tail(self.recent)
mpf_style = mpf.make_mpf_style(base_mpf_style=mpf_styles[-1], rc={'figure.facecolor': '#EDEDED'})
width_config={'candle_linewidth':0.8, 'candle_width':0.525, 'volume_width': 0.525}

(A) scale_padding _without_ tight_layout:

(1) scale_padding.left=4 and tight_layout=False:

mpf.plot(
    df, type="candle", title=self.title,
    style=mpf_style, datetime_format="%-m - %-d - %Y",
    volume=True, addplot=priceta + volta + rsita,
    figsize=(12, 10), scale_padding={'left': 4, 'top': 1, 'right': 1, 'bottom': 1},
    panel_ratios=pratios, xrotation=32,
    update_width_config=width_config, tight_layout=False,
)

yields:
Screen Shot 2020-07-08 at 2 56 00 PM

(2) scale_padding.right=4 and tight_layout=False:

mpf.plot(
    df, type="candle", title=self.title,
    style=mpf_style, datetime_format="%-m - %-d - %Y",
    volume=True, addplot=priceta + volta + rsita,
    figsize=(12, 10), scale_padding={'left': 1, 'top': 1, 'right': 4, 'bottom': 1},
    panel_ratios=pratios, xrotation=32,
    update_width_config=width_config, tight_layout=False,
)

yields:
Screen Shot 2020-07-08 at 2 57 54 PM

(3) scale_padding.left=4 and scale_padding.right=4 and tight_layout=False:

mpf.plot(
    df, type="candle", title=self.title,
    style=mpf_style, datetime_format="%-m - %-d - %Y",
    volume=True, addplot=priceta + volta + rsita,
    figsize=(12, 10), scale_padding={'left': 4, 'top': 1, 'right': 4, 'bottom': 1},
    panel_ratios=pratios, xrotation=32,
    update_width_config=width_config, tight_layout=False,
)

yields:

Width and height specified must be non-negative
<Figure size 1200x1000 with 0 Axes>

(4) scale_padding.left=2 and scale_padding.right=2 and tight_layout=False:

mpf.plot(
    df, type="candle", title=self.title,
    style=mpf_style, datetime_format="%-m - %-d - %Y",
    volume=True, addplot=priceta + volta + rsita,
    figsize=(12, 10), scale_padding={'left': 2, 'top': 1, 'right': 2, 'bottom': 1},
    panel_ratios=pratios, xrotation=32,
    update_width_config=width_config, tight_layout=False,
)

yields:
Screen Shot 2020-07-08 at 3 01 27 PM

(B) scale_padding.left >> 1 and scale_padding.right >> 1 _with_ tight_layout:

(1) scale_padding.left=4 and scale_padding.right=4 and tight_layout=True:

mpf.plot(
    df, type="candle", title=self.title,
    style=mpf_style, datetime_format="%-m - %-d - %Y",
    volume=True, addplot=priceta + volta + rsita,
    figsize=(12, 10), scale_padding={'left': 4, 'top': 1, 'right': 4, 'bottom': 1},
    panel_ratios=pratios, xrotation=32,
    update_width_config=width_config, tight_layout=True,
)

yields:
Screen Shot 2020-07-08 at 3 05 15 PM

(2) scale_padding.left=2 and scale_padding.right=2 and tight_layout=True:

mpf.plot(
    df, type="candle", title=self.title,
    style=mpf_style, datetime_format="%-m - %-d - %Y",
    volume=True, addplot=priceta + volta + rsita,
    figsize=(12, 10), scale_padding={'left': 2, 'top': 1, 'right': 2, 'bottom': 1},
    panel_ratios=pratios, xrotation=32,
    update_width_config=width_config, tight_layout=True,
)

yields:
Screen Shot 2020-07-08 at 3 09 18 PM

Hope this helps!

Thank you,
KJ

Thanks. Are any of those even close to what you are trying to accomplish? Perhaps the last one (except for maybe the exact placement of the Title)?

I just want to point out that I had never really intended to publicize scale_padding, but I am glad if it is helping. The intention was that scale_padding was a temporary kwarg to help me experiment while developing tight_layout. Afterwards I decided to leave the kwarg in place, "just in case" it might be useful for someone.

Sure.

Are any of those even close to what you are trying to accomplish?

Not any of the previous tests. This version is the closest:

df = self.df.tail(self.recent)
mpf_style = mpf.make_mpf_style(base_mpf_style=mpf_styles[-1], rc={'figure.facecolor': '#EDEDED'})
width_config={'candle_linewidth':0.8, 'candle_width':0.525, 'volume_width': 0.525}

mpf.plot(
    df, type="candle", title=self.title,
    style=mpf_style, datetime_format="%-m - %-d - %Y",
    volume=True, addplot=priceta + volta + rsita + zscoreta,
    figsize=(12, 10), scale_padding={'left': 1, 'top': 4, 'right': 1, 'bottom': 1},
    panel_ratios=pratios, xrotation=32,
    update_width_config=width_config, tight_layout=True,
)

yielding:
Screen Shot 2020-07-08 at 7 46 14 PM

Ideally, there is adjustable padding/margin in the orange zone just like when tight_layout=False. Everything in the orange zone would be shifted left so there is some space to the right.

chart example padding

Hope this make sense.

Thanks for your time,
KJ

Thank you. That makes sense. Until we have a xlimit kwarg, you can add some padding on the right side as follows:

dfpad = df.tail(7).copy()
dfpad.loc[:,:] = float('nan')
newdf = df.append(dfpad)

mpf.plot(newdf,...)

To adjust the amount of padding, modify the number you pass into df.tail()
And don't forget to add .copy() otherwise the nans may affect the original dataframe.

When I do the above on my own chart it looks like this:


before padding:
image


after padding:
image

@DanielGoldfarb,

Thanks for the suggested work around.

It's funny that you posted that. Last night I was considering doing something similar, but by adding additional empty Business days to df then passing to plot() like you did.

Also it seems that vlines can only be applied to the _main panel_. But I'll make a seperate _feature request_ so not to drag Issue 212 further.

I will post a chart update after some refactoring.

Thanks again!
KJ

@twopirllc yes, there are already two issues requesting the various lines kwargs be available for make_addplot and on any panel.

will do it, but not yet sure how soon; maybe within the next two to three months. in the meantime, generating line data and plotting that data on any panel with make_addplot() is a mildly tedious, but workable, solution.

Hello @DanielGoldfarb,

Sorry it has taken so long to get back to you with an example. I had to create some preliminary code in my project. Anyhow, here is a basic chart with _right padding_ with future dates and the code I used:

class Chart(object):
    # ...

    def _right_pad_df(self, df: pd.DataFrame, rpad: int, delta_unit: str = "D", range_freq: str = "B"):
        if rpad > 0:
            dfpad = df[-rpad:].copy()
            dfpad.iloc[:,:] = np.NaN

            df_frequency = df.index.value_counts().mode()[0] # Most common frequency
            freq_delta = pd.Timedelta(df_frequency, unit=delta_unit)
            new_dr = pd.date_range(start=df.index[-1] + freq_delta, periods=rpad, freq=range_freq)
            dfpad.index = new_dr # Update the padded index with new dates
            df = df.append(dfpad)

        return df

    def _plot(self, df: pd.DataFrame, plot_ratios: tuple = (12, 1.7), **kwargs):
        if df.empty:
            print(f"[X] DataFrame is missing!")
            return

        if not isinstance(plot_ratios, tuple):
            print(f"[X] plot_ratios must be a tuple")
            return

        ticker = df.ticker

        rpad = kwargs["rpad"] if "rpad" in kwargs else self.rpad
        df = self._right_pad_df(df, rpad)

        df = df.tail(self.last)
        mpf_style = mpf.make_mpf_style(base_mpf_style=mpf_styles[-1], rc={'figure.facecolor': '#EDEDED'})
        width_config={'candle_linewidth':0.8, 'candle_width':0.525, 'volume_width': 0.525}

        mpf.plot(
            df, type="candle", title=ticker,
            style=mpf_style, datetime_format="%-m/%-d/%Y",
            volume=True,
            figsize=(12, 10), scale_padding={'left': 1, 'top': 4, 'right': 1, 'bottom': 1},
            panel_ratios=plot_ratios, xrotation=15,
            update_width_config=width_config, tight_layout=True,
        )

Screen Shot 2020-07-17 at 11 19 33 AM

Thanks again for your time and expertise.
Kevin

kwarg xlim is now available. pip install --upgrade mplfinance

Great! Thank you for your time and excellence.

Regards,
Kevin

Was this page helpful?
0 / 5 - 0 ratings

Related issues

debjyotiarr picture debjyotiarr  ·  3Comments

Full4me picture Full4me  ·  3Comments

franklee00 picture franklee00  ·  4Comments

mattcambs picture mattcambs  ·  3Comments

leochan007 picture leochan007  ·  6Comments