Indicators

Plotting Indicators

Introduction

LEAN provides helper methods to make it simple to create indicator plots.

Plot Update Events

To plot all of the values of some indicators, in the Initializeinitialize method, call the PlotIndicatorplot_indicator method. The method plots each indicator value as the indicator updates. The method accepts up to four indicators.

var symbol = AddEquity("SPY");
var smaShort = SMA(symbol, 10);
var smaLong = SMA(symbol, 20);
PlotIndicator("<chartName>", smaShort, smaLong)
symbol = self.add_equity("SPY")
sma_short = self.sma(symbol, 10)
sma_long = self.sma(symbol, 20)
self.plot_indicator("<chartName>", sma_short, sma_long)

Plot Current Values

To plot the current value of indicators, call the Plotplot method. The method accepts up to four indicators.

// In Initialize
var symbol = AddEquity("SPY");
var smaShort = SMA(symbol, 10);
var smaLong = SMA(symbol, 20);

// In OnData
Plot("<chartName>", smaShort, smaLong)
# In Initialize
symbol = self.add_equity("SPY")
sma_short = self.sma(symbol, 10)
sma_long = self.sma(symbol, 20)

# In OnData
self.plot("<chartName>", sma_short, sma_long)

View Charts

The following table describes where you can access your charts, depending on how to deploy your algorithms:

LocationAlgorithm Lab AlgorithmsCLI Cloud AlgorithmsCLI Local Algorithms
Backtest results pagegreen checkgreen check
Live results pagegreen checkgreen check
/backtests/read endpointgreen checkgreen check
/live/read endpointgreen checkgreen check
ReadBacktest methodgreen checkgreen check
ReadLiveAlgorithm methodgreen checkgreen check
Local JSON file in your <projectName> / backtests / <timestamp> or <projectName> / live / <timestamp> directorygreen checkgreen check

Chart Limits

Not all indicators share the same base type(T), so you may not be able to plot them together since some indicators require points while others require TradeBars.

// Plot indicators that extend the "Indicator" type
PlotIndicators("All Indicator Values", sma, rsi);
Plot("Current Indicator Values", sma, rsi); 

// Plot indicators that extend the "TradeBarIndicator" type
PlotIndicators("All Indicator Values", atr, aroon);
Plot("Current Indicator Values", atr, aroon);
# Plot indicators that extend the "Indicator" type
self.plot_indicators("All Indicator Values", sma, rsi)
self.plot("Current Indicator Values", sma, rsi); 

# Plot indicators that extend the "TradeBarIndicator" type
self.plot_indicators("All Indicator Values", atr, aroon)
self.plot("Current Indicator Values", atr, aroon); 

If your indicator plots are complex, call the Plotplot method with one indicator and plot its Current.Valuecurrent.value property. For more information about plotting, see Charting.

Examples

The following examples demonstrate some common practices for plotting indicators.

Example 1: SMA Ribbon

The following algorithm trades simple moving average (SMA) crossings, which involves five SMA indicators to form an SMA ribbon. To visualize the ribbon, it plots each indicator with a different color.

public class PlottingIndicatorAlgorithm : QCAlgorithm
{
    private Symbol _spy;
    private Dictionary<int, SimpleMovingAverage> _smas;

    public override void Initialize()
    {
        SetStartDate(2019, 1, 1);
        SetEndDate(2021, 7, 1);
        
        // Add SPY data to update indicators and trade.
        _spy = AddEquity("SPY", Resolution.Daily).Symbol;

        // Create a chart to plot the ribbon. To better visualize the plot, use different colors per series.
        var chart = new Chart("Ribbon");
        chart.AddSeries(new Series("Price", SeriesType.Candle, "$"));
        chart.AddSeries(new Series("SMA10", SeriesType.Line, "$", Color.Violet));
        chart.AddSeries(new Series("SMA20", SeriesType.Line, "$", Color.Blue));
        chart.AddSeries(new Series("SMA30", SeriesType.Line, "$", Color.Green));
        chart.AddSeries(new Series("SMA40", SeriesType.Line, "$", Color.Yellow));
        chart.AddSeries(new Series("SMA50", SeriesType.Line, "$", Color.Red));

        // Create 5 SMA indicators to create the SMA ribbon.
        _smas = new[]{10, 20, 30, 40, 50}.ToDictionary(k => k, v =>
        {
            var sma = SMA(_spy, v, Resolution.Daily);
            // Plot the indicator value to create the ribbon when the indicator updates.
            sma.Updated += (_, point) => Plot("Ribbon", $"SMA{v}", point.Value);
            // Warm up the indicator so it's ready to use immediately.
            WarmUpIndicator(_spy, sma);
            return sma;
        });
    }

    public override void OnData(Slice slice)
    {
        if (slice.Bars.TryGetValue(_spy, out var bar))
        {
            // Plot the candlestick when you get the daily bar.
            Plot("Ribbon", "Price", bar.Open, bar.High, bar.Low, bar.Close);

            // Follow strong upward trend.
            if (IsUptrend(bar.Close, _smas))
            {
                SetHoldings(_spy, 1m);
            }
            // Follow strong downward trend.
            else if (IsDowntrend(bar.Close, _smas))
            {
                SetHoldings(_spy, -1m);
            }
            // Liquidate positions if the trend is less deterministic.
            else
            {
                Liquidate();
            }
        }
    }

    public bool IsUptrend(decimal price, IDictionary<int, SimpleMovingAverage> smas)
    {
        // Uptrend occurs when price is above all SMAs and shorter SMAs are above longer SMAs.
        var smasList = smas.OrderBy(kvp => kvp.Key)
            .Select(kvp => kvp.Value)
            .ToList();
        return Enumerable.Range(1, smasList.Count-1).All(i => smasList[i] < smasList[i-1])
            && price > smasList[0];
    }

    public bool IsDowntrend(decimal price, IDictionary<int, SimpleMovingAverage> smas)
    {
        // Downtrend occurs when price is below all SMAs and shorter SMAs are below longer SMAs.
        var smasList = smas.OrderBy(kvp => kvp.Key)
            .Select(kvp => kvp.Value)
            .ToList();
        return Enumerable.Range(1, smasList.Count-1).All(i => smasList[i] > smasList[i-1])
            && price < smasList[0];
    }
}
class PlottingIndicatorAlgorithm(QCAlgorithm):

    def initialize(self) -> None:
        self.set_start_date(2019, 1, 1)
        self.set_end_date(2021, 7, 1)
        
        # Add SPY data to update indicators and trade.
        self._spy = self.add_equity("SPY", Resolution.DAILY).symbol

        # Create a chart to plot the ribbon. To better visualize the plot, use different colors per series.
        chart = Chart("Ribbon")
        chart.add_series(Series("Price", SeriesType.CANDLE, "$"))
        chart.add_series(Series("SMA10", SeriesType.LINE, "$", Color.VIOLET))
        chart.add_series(Series("SMA20", SeriesType.LINE, "$", Color.BLUE))
        chart.add_series(Series("SMA30", SeriesType.LINE, "$", Color.GREEN))
        chart.add_series(Series("SMA40", SeriesType.LINE, "$", Color.YELLOW))
        chart.add_series(Series("SMA50", SeriesType.LINE, "$", Color.RED))

        # Create 5 SMA indicators to create the SMA ribbon.
        self._smas = {n: self._create_sma(n) for n in [10, 20, 30, 40, 50]}

    def _create_sma(self, n: int) -> SimpleMovingAverage:
        sma = self.sma(self._spy, n, Resolution.DAILY)
        # Plot the indicator value to create the ribbon when the indicator updates.
        sma.updated += lambda sender, point: self.plot("Ribbon", f"SMA{n}", point.value)
        # Warm up the indicator so it's ready to use immediately.
        self.warm_up_indicator(self._spy, sma)
        return sma

    def on_data(self, slice: Slice) -> None:
        bar = slice.bars.get(self._spy)
        if bar:
            # Plot the candlestick when you get the daily bar.
            self.plot("Ribbon", "Price", bar.open, bar.high, bar.low, bar.close)

            # Follow strong upward trend.
            if self._is_uptrend(bar.close, self._smas):
                self.set_holdings(self._spy, 1)
            # Follow strong downward trend.
            elif self._is_downtrend(bar.close, self._smas):
                self.set_holdings(self._spy, -1)
            # Liquidate positions if the trend is less deterministic.
            else:
                self.liquidate()

    def _is_uptrend(self, price: float, smas: List[SimpleMovingAverage]) -> bool:
        # Uptrend occurs when price is above all SMAs and shorter SMAs are above longer SMAs.
        smas_list = [x[1].current.value for x in sorted(smas.items(), key=lambda x: x[0])]
        return all([smas_list[i] < smas_list[i-1] for i in range(1, len(smas_list))]) and price > smas_list[0]

    def _is_downtrend(self, price: float, smas: List[SimpleMovingAverage]) -> bool:
        # Downtrend occurs when price is below all SMAs and shorter SMAs are below longer SMAs.
        smas_list = [x[1].current.value for x in sorted(smas.items(), key=lambda x: x[0])]
        return all([smas_list[i] > smas_list[i-1] for i in range(1, len(smas_list))]) and price < smas_list[0]

You can also see our Videos. You can also get in touch with us via Discord.

Did you find this page helpful?

Contribute to the documentation: