Skip to main content

Backtrader Plot: Master Trading Strategy Visualization with Complete Guide

· 24 min read

Want to see exactly how your trading strategy performed, without writing tons of extra code? That's where Backtrader's plot feature comes in. It's a built-in tool that turns your backtest results into clear, informative charts with just one command.

It runs on matplotlib, a popular Python plotting library. This means it takes all the complex data from your backtest—price movements, your custom indicators, portfolio value changes, and your trades—and bundles them into a single visual story. It's the fastest way to diagnose what’s working, spot problems, and figure out where to improve your strategy.

Backtrader Plot: Master Trading Strategy Visualization with Complete Guide

How the Plotting Feature Works

The best part is how automatic it is. The plotter is designed to visualize three main things for you, straight out of the box:

  1. The Data: Every data feed you load into Cerebro (like your stock's OHLC prices).
  2. The Indicators: Any indicators you add within your strategy (like moving averages or RSI).
  3. The Observers: Key performance trackers, like your running cash balance and every trade you made.

Because it’s integrated directly with the backtesting engine, anything you add to your strategy automatically gets included in the final chart. You won't need to reach for another charting library or build your own visualizations from scratch.

Using it is straightforward. After you've run your backtest with cerebro.run(), you simply call:

cerebro.plot()

Important: You must run cerebro.plot() after cerebro.run(). All your trading logic needs to be processed first for the charts to be accurate. Just make sure you have matplotlib installed in your Python environment (pip install matplotlib), and you're ready to go.

How to Create Your First Backtrader Plot

Getting a visual chart from your trading strategy is one of the most satisfying parts of using Backtrader. The good news is, creating a basic plot doesn't require much code at all. Think of it as putting together a simple recipe: you gather your ingredients (data and strategy), mix them in the Cerebro engine, and let it cook.

Here’s the straightforward structure to get your first chart up and running:

import backtrader as bt

class MyStrategy(bt.Strategy):
def __init__(self):
self.sma = bt.indicators.SimpleMovingAverage(self.data)

data = bt.feeds.BacktraderCSVData(dataname='your-data.txt')
cerebro = bt.Cerebro()
cerebro.adddata(data)
cerebro.addstrategy(MyStrategy)
cerebro.run()
cerebro.plot()

When you run this, Backtrader will automatically generate a plot for you. It's designed to show you the essentials right away. Your main chart will display:

  • The price data from your file.
  • The simple moving average line you added in the strategy.

Below the main price chart, you'll see three standard tracks that give you a snapshot of what happened during the backtest:

Observer NameWhat It Shows You
CashValueTracks the change in your total portfolio value (cash + holdings) over time.
TradeShows a bar for each trade, making it easy to see which ones were profitable (green) or loss-making (red).
BuySellPlaces markers directly on the price chart to indicate exactly where your strategy entered (buy) and exited (sell).

This default setup gives you a solid, informative visualization without any extra effort. Once you see this, you’ll have a clear base to start customizing plots further. For those looking to build more advanced automated strategies, you might find our comprehensive guide to TradingView algorithms invaluable.

Getting to Know Backtrader’s Built-in Trackers (And How to Tweak Them)

When you run a backtest in Backtrader, it automatically adds three helpful trackers to your charts. Think of them as your built-in dashboard, keeping an eye on key details so you don't have to.

Here’s what each one does in plain terms:

  • The Account Tracker: This one watches your money. It continuously shows you how much cash you have available and the total current value of your entire portfolio (your cash plus the value of your open positions).
  • The Trade Tracker: This focuses on completed trades. A "completed trade" means you opened a position and later closed it entirely. Once you're out, this tracker will show you the final profit or loss for that specific trade.
  • The Buy/Sell Marker: This one is all about visuals. It puts little markers directly on your price chart to show you the exact points where your strategy decided to buy and sell.

These are great for getting started, but you might not always want them. If you're building a custom chart or find the extra info distracting, you can easily turn them off.

You have two simple choices:

  1. Disable them from the start when you create your main engine:
    cerebro = bt.Cerebro(stdstats=False)
  2. Or, disable them when you actually run the backtest:
    cerebro.run(stdstats=False)

Setting stdstats=False simply tells Backtrader, "Thanks for the helpers, but I'll handle the dashboard myself this time." It gives you full control over how clean or detailed your charts look.

Making Your Indicators Look Just Right

Think of Backtrader's charting like customizing a dashboard—you get to decide exactly how each indicator looks and where it sits. All this control comes from something called the plotinfo dictionary. It's the central place for tweaking how your indicators and observers appear on your charts.

Here are the main settings you'll use most often:

SettingWhat It Does
plotThe on/off switch. Set it to True to show the indicator or False to hide it.
subplotDecides if the indicator shares the main price chart or gets its own space below.
plotnameLets you give your indicator a clear, friendly name on the chart instead of a technical class name.
plotaboveWhen using a subplot, this controls whether it appears above or below the main price data.

You have two easy ways to apply these customizations, and you can pick whichever feels more natural for your code.

Method 1: Set it up as you create the indicator. This is great for keeping everything neat and together in one line.

sma = bt.indicators.SimpleMovingAverage(self.data, period=15, plotname='My 15-Day SMA')

Method 2: Adjust the settings after creating it. Prefer to set things up later? Just modify the plotinfo object directly.

sma.plotinfo.plotname = 'My 15-Day SMA'

Both ways work perfectly. It just depends on whether you like to configure things at the start or come back to style them later.

Making Your Trading Charts Clearer: Customizing Lines in Backtrader

Ever looked at your trading chart and wished you could make an indicator line stand out more, or maybe hide another one to reduce clutter? In Backtrader, you have a powerful way to fine-tune exactly how each line looks, using something called the plotlines dictionary.

Think of it as giving individual instructions to each line on your chart. You can control its color, style, and even some special Backtrader behaviors. These special options start with an underscore, like _name or _method.

Here are some of the most useful ones and what they do:

  • _plotskip: Set this to True if you want a specific line to be calculated but not shown on the chart. It's great for background calculations you don't need to see.
  • _plotvalue & _plotvaluetag: These control how the line's latest value is displayed. _plotvalue shows it in the chart's legend, while _plotvaluetag puts a neat little tag for it on the right-hand side of the chart.
  • _name: Don't like the default label for a line? Use this to give it a clearer, more descriptive name on the chart.
  • _skipnan: If your data has gaps (represented as NaN), setting this to True makes the line skip over them instead of breaking or showing gaps.
  • _samecolor: Want two related lines to use the same color? This forces a line to match the color of the line that was plotted just before it.

Changing How a Line is Drawn: The _method Trick

One of the coolest options is _method. This tells Backtrader how to draw the data. By default, everything is a line. But you can change that.

A perfect example is the MACD indicator. The main MACD and signal lines are best as lines, but the histogram is much clearer as a series of bars. Here's how you do that:

plotlines = dict(
histo=dict(_method='bar', alpha=0.50, width=1.0)
)

By setting _method='bar' for the histo line, you get a clean bar chart for the histogram. The alpha controls transparency, and width controls the bar size. You can also use _method='tick' or other Matplotlib styles here.

Making It Pretty: Colors, Markers, and More

Beyond the special _ options, you can use any standard styling keyword that Matplotlib understands. This lets you match your chart to your preferred style.

  • Use color or c to set a specific color (e.g., 'green', '#FF5500').
  • Add marker (like 'o' for circles, '^' for triangles) and markersize to highlight individual data points.
  • Control linestyle with terms like '--' for dashed or ':' for dotted lines.

Here’s a quick reference for the special Backtrader options:

OptionWhat It DoesExample
_plotskipSkips drawing the line entirely (good for hidden calculations)._plotskip=True
_methodChanges the drawing style (e.g., to bars)._method='bar'
_nameChanges the line's name in the legend._name='Fast MA'
_skipnanSkips over NaN values when drawing the line._skipnan=True
_samecolorForces the line to use the same color as the previous one._samecolor=True

By mixing these plotlines options, you can transform a standard, busy chart into a clean, informative one that highlights exactly what you need to see for your trading decisions.

Building Charts with Multiple Trading Indicators

When you're putting together a complete trading strategy, it's common to need several indicators on your chart at once. Backtrader handles this really well by automatically arranging everything for you. It groups indicators logically based on the data they show.

Indicators that move in a similar range to your price data—like a simple moving average—get plotted right on top of your main price chart. Others that have their own distinct scale, such as the RSI or Stochastic Oscillator, are placed in neat sub-charts below. This keeps your chart clean and readable.

Here’s how you can add a set of indicators to your strategy for a full visual picture:

def __init__(self):
bt.indicators.ExponentialMovingAverage(self.datas[0], period=25)
bt.indicators.WeightedMovingAverage(self.datas[0], period=25, subplot=True)
bt.indicators.StochasticSlow(self.datas[0])
bt.indicators.MACDHisto(self.datas[0])
rsi = bt.indicators.RSI(self.datas[0])
bt.indicators.SmoothedMovingAverage(rsi, period=10)
bt.indicators.ATR(self.datas[0], plot=False)

A handy thing to know is that you don’t have to save every indicator to a variable for it to show up on your chart. Once you create it, Backtrader takes care of the rest. Also, you’ll see the last line uses plot=False. This is useful for when you need an indicator’s value for calculations in your strategy logic, but don’t necessarily want it taking up space on your chart.

So, you've been building your trading strategy with Backtrader, and it's working well. But when you go to visualize the results, the charts look a bit… off. Maybe the colors are hard to distinguish, or there's just too much data crammed into one image, making it impossible to read.

That's where system-wide plot customization comes in handy. Think of it as giving your charts a consistent makeover with just a few simple settings, instead of tweaking every single line and bar individually.

The key is the cerebro.plot() method. It has a few special parameters that control the overall look and feel of all your output charts.

Here’s a breakdown of what each one does:

ParameterWhat It's ForIf You Leave It Blank...
plotterThis is where you pass a PlotScheme object. This object holds all the global style rules—like colors for rising and falling candles, grid styles, and background colors.Backtrader will create a default one for you, which is usually fine to start.
numfigsGot a strategy with a dozen indicators over 10 years of data? One chart will be a mess. This lets you split the output across multiple separate figure images.It defaults to 1, meaning everything goes on one (potentially very long) chart.
iplotAre you working inside a Jupyter Notebook? Set this to True (which it is by default) and your charts will magically appear inline as you run the code.If you're running a script from the command line, you can set this to False.
**kwargsA handy shortcut. Instead of creating a full PlotScheme object, you can pass individual style attributes (like candleup='green') directly here to override the defaults.Nothing happens—you'll just use the default or your PlotScheme styles.

Using these parameters, you can quickly ensure all your analysis charts have a uniform, professional, and—most importantly—readable appearance, whether you're reviewing them for yourself or sharing them with others. It’s a small step that makes a big difference in understanding your strategy’s performance at a glance.

Making Your Charts Look Exactly How You Want

Think of the PlotScheme class as your control panel for how your trading charts appear. It lets you tweak all the visual details so the chart makes sense to you at a glance.

Here are the main settings you'll use most often:

  • style: This is the big one. It changes the main look of your price chart. Do you want a simple line, classic bars, or detailed candles?
  • grid: Turns the background grid lines on or off. Some people find them helpful, others find them distracting.
  • volume: A simple yes/no for whether to show trading volume on the chart.
  • voloverlay: If you are showing volume, this lets you choose: pile it at the bottom in its own section, or overlay it on the main price chart?
  • barup / bardown: When using bar or candle styles, these set the colors for "up" (bullish) and "down" (bearish) periods. Green and red are typical, but you can pick any colors you like.

Using these is straightforward. When you go to plot your analysis, you just add your preferences like this:

cerebro.plot(style='candle', grid=False, volume=False)

A Quick Guide to Chart Styles

You have three great choices for the main chart, each showing the price action a bit differently:

StyleWhat It ShowsBest For...
'line' (Default)A single, clean line connecting the closing prices of each period.Getting a quick, clear view of the overall price trend without extra detail.
'bar'Traditional "OHLC" bars. Each bar shows the Open, High, Low, and Close for that period.A more detailed view than a line, clearly showing the trading range each period.
'candle'Candlestick charts. Similar to bars, but uses a "body" to highlight the range between the open and close.Reading market sentiment quickly. The thick bodies make it easy to see bullish vs. bearish periods.

The default is the simple 'line' style. If you want to see the full story of what happened during a trading period—the opens, highs, and lows—switch to 'bar' or 'candle'.

How to Fix the Matplotlib "cannot import name 'warnings'" Error in Backtrader

If you're trying to visualize your trading strategies in Backtrader and get hit with an error like "cannot import name 'warnings' from matplotlib.dates", you're not alone. This is a classic version clash that pops up when a newer version of the matplotlib library doesn't play nicely with an older version of Backtrader's plotting code. It's a frustrating roadblock, but the fix is straightforward.

The root of the problem is a single file in Backtrader's installation. Here’s a step-by-step guide to resolve it.

Important: Before editing any files, it’s a good practice to create a backup or ensure you can reinstall the package if needed.

Step-by-Step Fix

The solution involves finding and editing one specific Python file in your site-packages directory.

StepActionDetails & Command
1Locate your site-packages folder.This is where Python stores all installed packages. You can find its path by running in your terminal: python -c "import site; print(site.getsitepackages())"
2Navigate to the Backtrader plot directory.Inside site-packages, go to backtrader/plot/. The file you need is called locator.py.
3Apply the patch to locator.py.Open the file in a text editor. You need to change the import line at the top. Find this line: from matplotlib.dates import warnings and simply delete it. Save the file.
4Verify the fix.Try running your Backtrader plotting script again. The import error should now be gone.

Why This Works (And Other Options)

This edit works because the code trying to import warnings from matplotlib.dates is outdated. In recent matplotlib versions, the warnings module isn't located there anymore. Removing the incorrect import line clears the path.

If you're not comfortable editing package files directly, you have two other good options:

  1. Pin your matplotlib version. Check Backtrader's official documentation for the recommended matplotlib version. You can then install a compatible version using pip, for example: pip install matplotlib==3.3.4. This is often the easiest and cleanest solution.
  2. Check for a Backtrader update. Sometimes, the fix has already been applied in a newer commit on the project's GitHub repository. Look at the Backtrader GitHub page to see if there's a more recent version or a direct patch you can reference.

This issue is a common hiccup in the open-source world, where different libraries update at their own pace. A quick edit to locator.py is usually all it takes to get your charts plotting smoothly again. If you frequently encounter performance issues with charting platforms, our guide on how to fix TradingView lag and performance issues may offer helpful optimization strategies.

Making Your Charts Clearer for Any Time Period

If you’ve ever run a backtest over a long stretch of time, you’ve probably seen it: a single, super crowded chart where it’s hard to pick out the details. It’s like trying to read a map where everything is squeezed onto one page.

There’s a handy way to fix this. You can use the numfigs parameter to split your results into several neat, separate charts. Think of it as breaking a long movie into clear chapters. For instance, running cerebro.plot(numfigs=4) will divide your whole backtest into four distinct figures. Suddenly, those important patterns and trade signals become much easier to spot.

You can also control how much room your main data and your indicators take up. This is where rowsmajor and rowsminor come in.

By default, the settings are:

ParameterDefault ValueWhat It Means
rowsmajor5The main price/volume charts get 5 parts of the vertical space.
rowsminor1The indicator subcharts below get 1 part of the vertical space.

So, the main chart is five times taller than each indicator pane. This is a great starting point.

But you can change this balance. If your strategy really leans on indicator readings, you might give them more room by increasing rowsminor. If you’re mostly focused on pure price action, you might keep the focus on the main chart by using the default or even increasing rowsmajor. It’s all about adjusting the view to match what you need to see most clearly.

Working with Custom Indicators and Observers

When you build your own custom indicators, they come with built-in tools for creating charts, thanks to the base Indicator class they inherit from. To make your chart's legend clear and helpful, you just need to work with a couple of specific methods.

First, let's talk about the legend label—that's the text next to your indicator line on the chart. By default, it might just show the indicator's name. To add useful details, like the specific settings you used, you implement the _plotlabel() method.

Think of it like this: the method should return a list of the values you want to appear in parentheses after the indicator's main name. For example, the classic RSI indicator uses this to show its period and the type of moving average it's using, but only when you've chosen a setting that's not the default. This way, the chart immediately tells you exactly how the indicator was calculated.

# Example structure for _plotlabel()
def _plotlabel(self):
labels = []
if self.p.period != 14: # If period is not the default 14
labels.append(f'Period: {self.p.period}')
return labels

Next, there's the _plotinit() method. This is your chance to do some setup work just before the chart is drawn. It's perfect for getting everything in place based on the user's unique parameters.

A great example of this in action is, again, the RSI. People use RSI to spot overbought and oversold conditions, which are defined by an upper and lower band (like 70 and 30). The _plotinit() method lets the indicator take the user's custom band levels and tell the plotting system: "Hey, draw horizontal reference lines at these specific values."

Here’s how it typically looks in practice:

def _plotinit(self):
self.plotinfo.plotyhlines = [self.p.upperband, self.p.lowerband]

By setting plotyhlines this way, the horizontal lines on the final chart will always match the configured upperband and lowerband parameters. Whether someone uses the standard 70/30 or changes them to 80/20, the reference lines on the plot adjust automatically. It ensures the visual guide is always accurate, without any extra effort from the person using your indicator.

In short, _plotlabel() helps identify your indicator in the legend, and _plotinit() helps set up the chart's visual environment correctly from the start. Together, they make your custom tool clearer and more user-friendly.

Troubleshooting Your Backtrader Visualizations: Common Questions Answered

Working with Backtrader's plotting features is incredibly useful, but sometimes you hit a snag. Here are answers to some of the most common questions, explained in a straightforward way.

My chart isn't showing up after cerebro.plot(). What's wrong?

This is a common hiccup. First, double-check that you've actually installed matplotlib, which Backtrader uses for all its charts. You can do this by running pip install matplotlib in your terminal.

The most likely culprit, though, is the order of operations. You must call cerebro.run() to execute your backtest before you try to plot the results with cerebro.plot(). The plot function needs the data generated from the run to display anything.

If you're working in a script or a non-interactive environment (like some IDEs or a server), the chart won't pop up automatically. In that case, you have two easy options:

  1. Force it to display by adding import matplotlib.pyplot as plt at the top of your script and then plt.show() after cerebro.plot().
  2. Save the chart directly to an image file using cerebro.plot(style='candle', savefig='my_backtest_chart.png').

How do I change the colors on my Backtrader chart?

Backtrader uses a clean, default color scheme (the Tableau 10 palette), but you can easily customize it. There are two main approaches:

  • For a quick change: Pass a list of colors (a lcolors variable) directly to the plot() method. These colors need to be in a format that matplotlib understands.
  • For full control: Create a custom PlotScheme class. By overriding its color() method, you can define exactly which color is used for each element (up trends, down trends, indicators, etc.) across all your charts.

Can I compare two different strategies on one chart?

This is a bit nuanced. Backtrader is designed to plot everything related to a single strategy run on one chart—that includes your data, all the indicators you added, and any "observers" like trade history or cash.

To visually compare two completely separate trading strategies (like a Moving Average Crossover vs. an RSI strategy), you need to run them as separate backtests. You can, however, add multiple indicators (like two different moving averages) to a single strategy. This is the best way to compare different signal systems or conditions within the same backtest on a unified chart.

I added an indicator for calculations, but I don't want it cluttering the chart. How do I hide it?

No problem at all. When you create the indicator, simply add the argument plot=False. This tells Backtrader to calculate the values (so you can use them in your logic) but to skip drawing it on the chart.

For example:

# This ATR will be calculated but not plotted
hidden_atr = bt.indicators.ATR(self.data, plot=False)

What's the deal with plotinfo and plotlines? They seem similar.

This is a key concept for customization! Think of them as handling different levels of the chart:

  • plotinfo is for the big-picture settings of the entire indicator or object. It controls things like:

    • Should this be plotted in its own subplot or on the main chart?
    • What name should appear in the legend?
    • Is this object plottable at all?
  • plotlines is for styling the individual lines that make up the indicator. It controls the visual details:

    • Line color, style (solid, dashed), and width.
    • Marker types for individual data points.
    • The visual name for that specific line in the legend.

In short, use plotinfo to decide where and if something is plotted. Use plotlines to decide how each line of that plot looks.

What to Try Next

Now that you've got a handle on the basics of plotting with backtrader, the best way to learn is to play around with it. Start by tweaking the PlotScheme settings to change colors, gridlines, or the number of rows in a chart. Find a look that feels right for your analysis—something that makes the data easy on your eyes.

A great project is to build a strategy that mixes different kinds of indicators, like a trend-following moving average, a momentum oscillator (like the RSI), and a volatility measure (like Bollinger Bands). Run a backtest and watch how backtrader automatically arranges everything into a clear, multi-panel chart. It’s a solid way to see the library’s organizational power in action. For inspiration, explore some of the best TradingView strategies that have been proven through backtesting.

Once you're comfortable, dive into customizing your own indicators. By adding _plotlabel() and _plotinit() methods, you can control exactly how your indicator appears on the chart—its name, color, and line style. This is especially useful if you’re developing your own unique indicators and need them to stand out visually.

If you’re using this regularly, it pays to get organized. Create a couple of standard PlotScheme setups—maybe one for quick daily checks and another for detailed weekly reviews. Save these configurations as reusable Python files. It keeps all your charts consistent, whether you’re comparing strategies or sharing results with a team.

Finally, automate the boring part. Instead of manually saving charts, write a small script to have backtrader save your plots directly to image files (like PNGs) after each backtest. This builds a perfect visual history of your work, making it easy to document your process or look back at how a strategy has changed over time.


Speaking of building and visualizing strategies, if you're looking for a more streamlined and powerful way to create, test, and deploy trading indicators without the heavy lifting of manual coding, you might want to explore a dedicated platform.

Pineify Website

Tools like Pineify take this concept to the next level by offering a Visual Editor and an AI Coding Agent specifically for TradingView's Pine Script. Imagine being able to drag-and-drop 235+ technical indicators, combine them into complex strategies, and generate error-free code instantly—all without writing a single line of code. It’s perfect for translating the strategy ideas you prototype in backtrader into production-ready indicators on TradingView. You can read our full analysis in this Pineify Review to see if it fits your workflow.

You can even import custom code to modify existing scripts or use the DIY Strategy Builder to visually set entry/exit rules. For deep analysis, their Professional Backtest Report tool can transform your TradingView backtest data into institutional-grade reports with metrics like Sharpe ratio and Monte Carlo simulations. It essentially automates and professionalizes the entire workflow from idea to tested, visual strategy.