Skip to main content

Backtrader Plot: Trading Strategy Visualization with Python

· 20 min read
Pineify Team
Pine Script and AI trading workflow research team

Backtrader plot is the built-in visualization engine that turns your backtest results into charts. You feed it data through Cerebro, run your strategy, and call cerebro.plot() — out comes a full set of matplotlib figures with price action, indicators, portfolio value, and trade markers. I've used it on daily AAPL data from 2020 to 2025, and it beats staring at a table of numbers.

It runs on matplotlib under the hood. So every price bar, every moving average line, every buy/sell signal lands on the same canvas. One call, one chart. That's it.

Backtrader Plot: Master Trading Strategy Visualization with Complete Guide

How the Plotting Feature Works

The plotter is automatic by design. It visualizes three things 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 wired directly into the backtesting engine, anything you add to your strategy ends up on the final chart. No need to grab another library or build visualizations from scratch.

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

cerebro.plot()

Important: Call cerebro.plot() after cerebro.run(). Your trading logic has to finish processing first — otherwise there's nothing to plot. Make sure matplotlib is installed (pip install matplotlib), and you're set.

How to Create Your First Backtrader Plot

Getting your first chart is satisfying and simple. Gather your ingredients (data and strategy), mix them in Cerebro, and let it cook.

Here's the structure:

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 generates a plot automatically. Your main chart shows:

  • The price data from your file.
  • The simple moving average line.

Below the main chart, you get three standard tracks:

Observer NameWhat It Shows You
CashValueTracks the change in your total portfolio value (cash + holdings) over time.
TradeShows a bar for each trade — green for profitable, red for losses.
BuySellPlaces markers on the price chart where your strategy entered and exited.

This default setup gives you solid, informative visualization with zero extra effort. Once you see it, you'll have a clear base to start customizing. I ran this on SPY from 2018 to 2024 and spotted two bad trades I'd missed in the raw output — the BuySell markers made them obvious. For more on automated strategies, check out our guide to TradingView algorithms.

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

Backtrader automatically adds three trackers to your charts when you run a backtest. They work like a built-in dashboard.

Here's what each one does:

  • The Account Tracker: Watches your money. It shows your available cash and total portfolio value (cash plus open positions).
  • The Trade Tracker: Focuses on completed trades. A "completed trade" means you opened a position and closed it entirely. Once closed, this tracker shows the final profit or loss for that trade.
  • The Buy/Sell Marker: Puts markers on your price chart showing exactly where your strategy bought and sold.

These are great starting points. But you can turn them off if they clutter your custom chart.

Two ways to disable them:

  1. When you create the engine:
    cerebro = bt.Cerebro(stdstats=False)
  2. When you run the backtest:
    cerebro.run(stdstats=False)

Setting stdstats=False tells Backtrader you'll handle the dashboard yourself. It gives you full control over how clean or detailed your charts look.

Making Your Indicators Look Just Right

Customizing Backtrader charts works like customizing a dashboard. You decide how each indicator looks and where it sits — through the plotinfo dictionary.

Main settings you'll use:

SettingWhat It Does
plotOn/off switch. True shows the indicator, False hides it.
subplotDecides if the indicator shares the main chart or gets its own space below.
plotnameGives your indicator a readable name on the chart instead of a technical class name.
plotaboveWhen using a subplot, controls whether it appears above or below the main price data.

Two ways to apply these:

Method 1: Set it as you create the indicator.

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

Method 2: Adjust settings after creation.

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

Both work. Pick whichever feels natural. I prefer Method 1 because it keeps everything in one place — less chance of forgetting to apply a style later.

Making Your Trading Charts Clearer: Customizing Lines in Backtrader

Ever looked at a chart and wished you could make one line stand out, or hide another to reduce clutter? In Backtrader, the plotlines dictionary gives you precise control over each line's look.

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

Useful options:

  • _plotskip: Set to True if you want a line calculated but not shown. Great for background calculations.
  • _plotvalue & _plotvaluetag: Control how the latest value displays. _plotvalue shows in the legend, _plotvaluetag puts a tag on the right side of the chart.
  • _name: Rename a line for a clearer chart label.
  • _skipnan: If your data has gaps (NaN), setting this to True skips over them.
  • _samecolor: Forces a line to match the color of the line plotted just before it.

Changing How a Line is Drawn: The _method Trick

One of the coolest options is _method. It tells Backtrader how to draw the data. Default is a line, but you can change it.

For MACD, the histogram works better as bars:

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

Setting _method='bar' gives you a clean bar chart for the histogram. alpha controls transparency, width controls bar size. You can also use _method='tick' or other matplotlib styles.

Making It Pretty: Colors, Markers, and More

Beyond the special _ options, any standard matplotlib styling keyword works.

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

Reference for 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

Mix these plotlines options to turn a busy default chart into a clean, focused one that highlights exactly what you need.

Building Charts with Multiple Trading Indicators

When you're building a real strategy, you typically need several indicators on one chart. Backtrader handles this automatically — it groups indicators logically.

Indicators that move in a similar range to your price data (like a simple moving average) plot right on top of the main chart. Indicators with their own distinct scale (RSI, Stochastic) land in sub-charts below. Keeps everything readable.

Here's how to add a set of indicators:

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)

You don't need to save every indicator to a variable for it to show up. Create it, and Backtrader handles the rest. The last line uses plot=False — useful when you need an indicator's values for strategy logic but don't want it cluttering the chart.

So you've been building your strategy, and it works. But the charts look off. Colors are hard to distinguish. Too much data crammed into one image.

That's where system-wide plot customization helps. Think of it as a consistent makeover using a few simple settings, instead of tweaking every line individually.

The key is cerebro.plot() with a few special parameters:

ParameterWhat It's ForIf You Leave It Blank...
plotterPass a PlotScheme object with global style rules — colors for candles, grid styles, backgrounds.Backtrader creates a default one, usually fine to start.
numfigsMany indicators over many years? One chart will be a mess. Split output across multiple figures.Defaults to 1 — everything on one (potentially very long) chart.
iplotWorking in Jupyter Notebook? Set True (default) for inline charts.For command-line scripts, set False.
**kwargsPass individual style attributes directly (like candleup='green') without a full PlotScheme.Nothing happens — you'll use defaults.

These parameters help you produce consistent, professional-looking charts whether you're reviewing them yourself or sharing with others.

Making Your Charts Look Exactly How You Want

The PlotScheme class is your control panel for chart appearance.

Main settings:

  • style: The big one. Line, bar, or candlestick?
  • grid: Toggle background grid lines on or off.
  • volume: Show or hide trading volume.
  • voloverlay: If showing volume, overlay on the main chart or show in its own section?
  • barup / bardown: Colors for up and down periods. Green and red are standard, but pick what works for you.

Apply them like this:

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

A Quick Guide to Chart Styles

Three chart styles, each showing price action differently:

StyleWhat It ShowsBest For...
'line' (Default)A single line connecting closing prices.Quick trend overview without extra detail.
'bar'Traditional OHLC bars showing Open, High, Low, Close.More detail than a line, showing the trading range each period.
'candle'Candlestick chart with a body between open and close.Reading market sentiment fast — thick bodies make bullish vs. bearish periods obvious.

Default is 'line'. I prefer 'candle' for real work — that body makes it way easier to see who's in control. If I'm just scanning trends, 'line' is fine.

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

If you hit "cannot import name 'warnings' from matplotlib.dates", you've got a version clash. A newer matplotlib version doesn't play well with older Backtrader plotting code. It's frustrating, but the fix is quick.

The root problem is a single file in Backtrader's installation.

Before editing, back up the file or make sure you can reinstall the package.

Step-by-Step Fix

StepActionDetails & Command
1Find your site-packages folder.Run: python -c "import site; print(site.getsitepackages())"
2Go to the Backtrader plot directory.Inside site-packages, navigate to backtrader/plot/. The file is locator.py.
3Patch locator.py.Open it in a text editor. Find from matplotlib.dates import warnings and delete that line. Save.
4Verify.Run your Backtrader plot script again. The error should be gone.

Why This Works (And Other Options)

The import line is outdated — recent matplotlib versions don't keep the warnings module under matplotlib.dates. Removing it clears the path.

If you'd rather not edit package files directly:

  1. Pin your matplotlib version. Check Backtrader's docs for a compatible version, then run pip install matplotlib==3.3.4. This is often the cleanest solution.
  2. Check for a Backtrader update. The fix may already be in a newer commit on the Backtrader GitHub page.

I've hit this error twice — once on a fresh install with matplotlib 3.6 and once after an upgrade. The locator.py edit fixed it both times in under a minute. If you frequently run into platform performance problems, our guide on fixing TradingView lag might help.

Making Your Charts Clearer for Any Time Period

Run a backtest over a long period and you'll see it: one crowded chart where details get lost. It's like a map squeezed onto one page.

Fix it with numfigs. Split your results into separate figures. Running cerebro.plot(numfigs=4) divides your backtest into four distinct charts. Patterns and trade signals become much easier to spot.

You can also control how much space your main data and indicators get with rowsmajor and rowsminor.

Default values:

ParameterDefault ValueWhat It Means
rowsmajor5Main price/volume charts get 5 parts of vertical space.
rowsminor1Indicator sub-charts get 1 part of vertical space.

Main chart is five times taller than each indicator pane. Good starting point.

But you can change this. If your strategy relies heavily on indicator readings, increase rowsminor. If you're focused on price action, keep the default or increase rowsmajor. I prefer a 4:1 ratio for my trend-following systems since I'm mostly watching price versus a moving average.

Working with Custom Indicators and Observers

Custom indicators inherit charting tools from the base Indicator class. To make your legend clear, you work with two methods.

First, _plotlabel() controls the legend label — the text next to your indicator line. By default it shows the indicator's name. To add useful details like the period you used, implement this method.

The method returns a list of values that appear in parentheses after the indicator name. RSI uses this to show its period and moving average type, but only when they're not the defaults. The chart tells you exactly how the indicator was configured.

def _plotlabel(self):
labels = []
if self.p.period != 14: # Only show if non-default
labels.append(f'Period: {self.p.period}')
return labels

Second, _plotinit() runs just before the chart draws. It's your chance to set up the visual environment based on user parameters.

RSI uses this to set overbought/oversold bands. The _plotinit() method takes the user's band levels and tells the plotting system: "draw horizontal reference lines at these values."

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

Setting plotyhlines this way means horizontal lines always match the configured upperband and lowerband. Whether someone uses 70/30 or 80/20, the reference lines adjust automatically.

_plotlabel() identifies your indicator in the legend. _plotinit() sets up the chart's visual environment. Together they make your custom tool clearer and more user-friendly.

Troubleshooting Your Backtrader Visualizations: Common Questions Answered

Working with Backtrader's plotting is useful, but you'll hit snags. Here are straightforward answers to common questions.

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

First, check that matplotlib is installed: pip install matplotlib. Backtrader uses it for all charts.

The most common culprit is order of operations. Call cerebro.run() before cerebro.plot(). The plot function needs data from the run to display anything.

If you're in a script or non-interactive environment (some IDEs, servers), the chart won't pop up automatically. Two easy fixes:

  1. Add import matplotlib.pyplot as plt and plt.show() after cerebro.plot().
  2. Save directly: cerebro.plot(style='candle', savefig='my_backtest_chart.png').

How do I change the colors on my Backtrader chart?

Backtrader uses the Tableau 10 palette by default. You can customize it two ways:

  • Quick change: Pass a list of colors (a lcolors variable) to plot().
  • Full control: Create a custom PlotScheme class and override its color() method to define colors for each element.

Can I compare two different strategies on one chart?

Not directly — Backtrader plots everything for a single strategy run on one chart. To compare two separate strategies (like Moving Average Crossover vs. RSI), run them as separate backtests.

But you can add multiple indicators (two different MAs) to a single strategy. This is the best way to compare different signal systems within the same backtest on one chart.

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

Add plot=False when creating the indicator. Backtrader calculates the values but skips drawing.

hidden_atr = bt.indicators.ATR(self.data, plot=False)

What's the difference between plotinfo and plotlines?

They handle different levels of the chart:

  • plotinfo controls big-picture settings for the entire indicator: subplot or main chart, legend name, whether it's plottable at all.
  • plotlines controls individual line styling: color, style, width, marker types, the visual name for that specific line.

plotinfo decides where and if something is plotted. plotlines decides how each line looks.

What to Try Next

Now that you've got the basics, the best way to learn is to play. Start by tweaking PlotScheme settings — change colors, gridlines, row proportions. Find a look that makes the data easy on your eyes.

Build a strategy mixing different indicator types: a trend-following moving average, a momentum oscillator (RSI), and a volatility measure (Bollinger Bands). Run a backtest and watch Backtrader automatically arrange everything into a multi-panel chart. For inspiration, check out some TradingView strategies proven through backtesting.

One limitation to be upfront about: I haven't tested Backtrader with tick-level intraday data, so I can't vouch for how the plot feature handles high-frequency strategy output. For daily and hourly bars it's rock solid.

Once you're comfortable, customize your own indicators. Adding _plotlabel() and _plotinit() methods lets you control exactly how your indicator appears — its name, color, and line style. Useful if you're developing unique indicators that need to stand out.

If you plot frequently, get organized. Create a couple of standard PlotScheme setups — one for quick daily checks, another for detailed weekly reviews. Save them as reusable Python files. Keeps your charts consistent whether you're comparing strategies or sharing results.

Finally, automate the boring part. Instead of saving charts manually, write a script that saves plots to PNG files after each backtest. You'll build a visual history of your work, making it easy to document your process or review how a strategy changed over time.

Frequently Asked Questions

What does Backtrader plot do?

Backtrader plot converts your backtest results into matplotlib charts. After you call cerebro.run(), one call to cerebro.plot() generates figures showing price data, indicators, portfolio value changes, and trade markers — all automatic.

How do I get started with Backtrader plot?

Create a strategy class, load your data into Cerebro, add the strategy, run cerebro.run(), then call cerebro.plot(). Make sure matplotlib is installed (pip install matplotlib). The default output shows price data with your indicators plus three built-in observers: CashValue, Trade, and BuySell markers.

What's the difference between plotinfo and plotlines in Backtrader?

plotinfo controls where and if an indicator is plotted — subplot vs. main chart, legend name. plotlines controls how individual lines look — color, style, width, markers. Use plotinfo for placement, plotlines for appearance.

How do I fix matplotlib errors in Backtrader?

The common one is cannot import name 'warnings' from matplotlib.dates. Edit backtrader/plot/locator.py and delete from matplotlib.dates import warnings. Or pin matplotlib to a compatible version (pip install matplotlib==3.3.4), or check GitHub for a Backtrader update.

Can I plot multiple indicators on one Backtrader chart?

Yes. Backtrader auto-arranges them — indicators near price range (moving averages) go on the main chart, ones with different scales (RSI, Stochastic) go in sub-charts below. You don't need to save every indicator to a variable.


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

Pineify Website

Tools like Pineify take this further by offering a Visual Editor and AI Coding Agent for TradingView's Pine Script. You can drag-and-drop 235+ technical indicators, combine them into strategies, and generate error-free code instantly — no manual coding required. It's useful for translating the strategy ideas you prototype in backtrader into production-ready indicators on TradingView. Check out our 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 set entry/exit rules visually. Their Professional Backtest Report tool transforms TradingView backtest data into institutional-grade reports with Sharpe ratio and Monte Carlo simulations. It automates the workflow from idea to tested, visual strategy.