Problem Description
Solution
To plot a custom data series in QuantConnect, you use the self.plot() method within your algorithm (usually inside on_data).
This method allows you to send values to the front-end charting interface. You can group these values into specific "Charts" and "Series".
Key Method: self.plot()
The syntax is:
self.plot("Chart Name", "Series Name", value)
- Chart Name: The title of the chart panel. If it doesn't exist, LEAN creates a new one.
- Series Name: The label for the specific line or data point.
- Value: The numerical value to plot (int, float, or Decimal).
Complete Strategy Example
The following code demonstrates how to create a custom chart named "Indicators" and plot the closing price of SPY against a 20-day Simple Moving Average (SMA).
# region imports
from AlgorithmImports import *
# endregion
class CustomPlottingAlgorithm(QCAlgorithm):
def initialize(self):
self.set_start_date(2023, 1, 1)
self.set_end_date(2023, 12, 1)
self.set_cash(100000)
# Add an asset
self.spy = self.add_equity("SPY", Resolution.DAILY).symbol
# Create an indicator to plot
self.sma = self.sma(self.spy, 20, Resolution.DAILY)
# Optional: Pre-define chart properties (e.g., Scatter, Bar)
# If you don't do this, it defaults to a Line chart.
# chart = Chart("Trade Plot")
# chart.add_series(Series("Buy", SeriesType.SCATTER, "$", Color.Green, ScatterMarkerSymbol.TRIANGLE))
# chart.add_series(Series("Sell", SeriesType.SCATTER, "$", Color.Red, ScatterMarkerSymbol.TRIANGLE_DOWN))
# self.add_chart(chart)
def on_data(self, slice: Slice):
# Ensure data exists
if not self.sma.is_ready or self.spy not in slice.bars:
return
current_price = slice.bars[self.spy].close
sma_value = self.sma.current.value
# 1. Plotting on a Custom Chart named "Analysis"
# This creates a new chart panel separate from the Strategy Equity
self.plot("Analysis", "SPY Price", current_price)
self.plot("Analysis", "SMA 20", sma_value)
# 2. Plotting the difference on a separate chart named "Spread"
diff = current_price - sma_value
self.plot("Spread", "Price vs SMA", diff)
def on_end_of_algorithm(self):
self.log("Algorithm Finished")
Important Considerations
- Performance Limits: To prevent browser crashes, QuantConnect limits charts to approximately 4,000 data points per series. If you plot every minute in a multi-year backtest, the chart will downsample.
- Default Chart: If you use
self.plot("Strategy Equity", ...)you will draw on the main performance chart. This is generally discouraged for indicators because the scale of the indicator (e.g., RSI at 0-100) might distort the view of your Equity (e.g., $100,000). - Plotting Indicators Shortcut: If you have a registered indicator object, you can use the helper method:
This automatically handles the series naming based on the indicator's internal name.self.plot_indicator("Chart Name", self.sma)
Q&A: Plotting in QuantConnect
Q: Can I change the color or style of the plot line?
A: Yes. To customize colors or use Scatter/Bar charts, you must instantiate a Chart and Series object in initialize using self.add_chart(). You can specify SeriesType.SCATTER, SeriesType.BAR, etc., and define specific Color objects.
Q: Why isn't my chart showing up in the results?
A: Ensure you are calling self.plot() inside a loop that actually executes (like on_data). Also, check that the values you are passing are not None. If the backtest produces no trades and runs very quickly, sometimes charts may not render if the data density is too low, though this is rare.
Q: Can I plot from OnEndOfDay?
A: Yes. Plotting inside on_end_of_day is actually recommended for high-resolution strategies (like Second or Minute resolution) if you only need to visualize daily trends. This saves data points and avoids the 4,000 point limit downsampling.
Q: How do I plot buy and sell markers?
A: While self.plot draws lines, you can create a specific series with SeriesType.SCATTER to draw markers. However, QuantConnect automatically plots Buy/Sell annotations on the main "Strategy Equity" chart whenever an order is filled, so manual plotting of trades is often unnecessary unless you need custom logic.