Skip to content

Plugins in chart.py

Benny Thadikaran edited this page Jul 13, 2026 · 2 revisions

chart.py supports custom chart indicators through plugins.

A plugin is a small Python file inside renderer/plugins/ that calculates an indicator and adds it to the chart.

Plugins are enabled from the user configuration file:

defs/user.json

Once enabled, each plugin becomes a command-line option such as:

python chart.py --sym RELIANCE --rsi

Quick setup: use the built-in plugins

The project already includes these plugin files:

renderer/plugins/rsi.py
renderer/plugins/atr.py
renderer/plugins/macd.py
renderer/plugins/bollinger_bands.py
renderer/plugins/supertrend.py

To enable them, add a CHART_PLUGINS block to defs/user.json.

If defs/user.json already has a CHART_PLUGINS section, merge these entries into it. Do not delete unrelated plugins.

Note: In practice, only include plugins you actually need in daily analysis.

{
  "CHART_PLUGINS": {
    "RSI": {
      "name": "rsi",
      "option": "rsi",
      "help": "Add RSI indicator.",
      "lookback": 100,
      "panel": {
        "kind": "lower",
        "axes": 1,
        "share": true,
        "preferred_axis": "any",
        "allow_volume_panel": true
      },
      "period": 14,
      "source": "Close",
      "line_color": "#0F766E",
      "overbought": 70,
      "oversold": 30,
      "overbought_color": "#64748B",
      "oversold_color": "#64748B"
    },
    "ATR": {
      "name": "atr",
      "option": "atr",
      "help": "Add ATR indicator.",
      "lookback": 100,
      "panel": {
        "kind": "lower",
        "axes": 1,
        "share": true,
        "preferred_axis": "any",
        "allow_volume_panel": true
      },
      "period": 14,
      "line_color": "firebrick",
      "width": 1.5,
      "ylabel": "ATR"
    },
    "MACD": {
      "name": "macd",
      "option": "macd",
      "help": "Add MACD indicator.",
      "lookback": 100,
      "panel": {
        "kind": "lower",
        "axes": 2,
        "share": false
      },
      "source": "Close",
      "fastlen": 12,
      "slowlen": 26,
      "siglen": 9,
      "line_color": "royalblue",
      "signal_color": "red",
      "hist_positive_color": "darkgray",
      "hist_negative_color": "darkgray"
    },
    "BOLLINGER_BANDS": {
      "name": "bollinger_bands",
      "option": "bb",
      "help": "Add Bollinger Bands.",
      "lookback": 100,
      "panel": {
        "kind": "price"
      },
      "source": "Close",
      "length": 20,
      "mult": 2.0,
      "basis_color": "dodgerblue",
      "upper_color": "gray",
      "lower_color": "gray",
      "basis_width": 1.2,
      "band_width": 1.0
    },
    "SUPERTREND": {
      "name": "supertrend",
      "option": "supertrend",
      "help": "Add Supertrend indicator.",
      "lookback": 100,
      "panel": {
        "kind": "price"
      },
      "factor": 3,
      "atr_length": 10,
      "up_color": "mediumseagreen",
      "down_color": "crimson",
      "width": 1.2
    }
  }
}

Run a chart with one plugin

python chart.py --sym RELIANCE --rsi

Run a chart with more than one plugin

python chart.py --sym RELIANCE --rsi --macd

Use plugins with other chart options

python chart.py --sym RELIANCE --tf w -volume --rsi

How plugin options work

Each plugin entry in defs/user.json has an option field.

Example:

"option": "rsi"

This creates the command-line flag:

--rsi

Another example:

"option": "bb"

This creates:

--bb

The name field must match the Python file name inside renderer/plugins/, without .py.

Example:

"name": "bollinger_bands"

This loads:

renderer/plugins/bollinger_bands.py

Understanding panels

A chart has one main price panel and optional lower panels.

The main price panel is always:

Panel 0

Lower panels start at:

Panel 1

A plugin can be placed in one of three ways:

  1. On the price chart
  2. In a lower panel that may share space with another indicator
  3. In its own lower panel

Price panel plugins

Use a price panel when the indicator belongs directly on the candles.

Examples:

Bollinger Bands
Supertrend
VWAP
Moving averages
Donchian Channels
Keltner Channels

Configuration:

"panel": {
  "kind": "price"
}

This tells chart.py to plot the plugin on the main candle chart.

Equivalent values are:

price
main
overlay

Use this style when the indicator uses the same price scale as the candles.

Chart Panels


Lower panel plugins

Use a lower panel when the indicator has its own scale.

Examples:

RSI
ATR
MACD
Stochastic
ADX
Momentum oscillators

A lower panel plugin may either share a panel or take its own panel.


Shareable lower panels

A shareable lower panel is suitable for simple indicators, usually one line on one scale.

Example:

"panel": {
  "kind": "lower",
  "axes": 1,
  "share": true,
  "preferred_axis": "any",
  "allow_volume_panel": true
}

This means:

kind: lower

Place the plugin below the price chart.

axes: 1

The plugin needs one y-axis.

share: true

The plugin can share a panel with another compatible indicator.

preferred_axis: any

The plugin can use either the left or right y-axis.

allow_volume_panel: true

The plugin may share the volume panel if volume is enabled and space is available.

This is a good fit for RSI, ATR, or a simple momentum line.


Exclusive lower panels

Some indicators should get their own panel.

Use this when the indicator has multiple parts or when sharing would make the chart hard to read.

Example:

"panel": {
  "kind": "lower",
  "axes": 2,
  "share": false
}

This is a good fit for MACD because MACD has:

MACD line
Signal line
Histogram

The MACD plugin uses both axes internally: one for the lines and one for the histogram.


Panel allocation logic

chart.py automatically decides panel numbers at runtime.

The allocation order is:

  1. Volume
  2. Volume moving averages
  3. Built-in RS indicators
  4. Plugins

Volume

If --volume is used, volume is always assigned to panel 1.

python chart.py --sym RELIANCE --volume

Panel layout:

Panel 0: Price
Panel 1: Volume

Volume moving average

If --vol-sma is used with --volume, the volume moving average is drawn on the volume panel.

python chart.py --sym RELIANCE --volume --vol-sma 20

Panel layout:

Panel 0: Price
Panel 1: Volume + volume moving average

Shareable plugins

A shareable plugin looks for an available panel and axis.

If volume is enabled and the plugin allows sharing the volume panel, it may be placed on the available axis of panel 1.

Example:

python chart.py --sym RELIANCE --volume --rsi

Possible layout:

Panel 0: Price
Panel 1: Volume + RSI

If no suitable panel is available, chart.py creates a new lower panel.

Exclusive plugins

An exclusive plugin always gets a new lower panel.

Example:

python chart.py --sym RELIANCE --volume --macd

Possible layout:

Panel 0: Price
Panel 1: Volume
Panel 2: MACD

Price overlay plugins

Price overlay plugins always go on panel 0.

Example:

python chart.py --sym RELIANCE --bb --supertrend

Layout:

Panel 0: Price + Bollinger Bands + Supertrend

Setting up your own custom plugin

A custom plugin needs two parts:

  1. A Python file in renderer/plugins/
  2. A config entry in defs/user.json

Step 1: Create the plugin file

Create a new file:

renderer/plugins/my_indicator.py

Every plugin must define an apply() function with this signature:

from __future__ import annotations

from typing import Any

import pandas as pd


def apply(
    df: pd.DataFrame,
    plot_args: dict[str, Any],
    options: dict[str, Any],
    display_period: int,
) -> None:
    ...

The plugin receives the full chart data as df.

Typical columns are:

Open
High
Low
Close
Volume

Calculate your indicator on the full dataset, then plot only the visible section:

indicator.iloc[-display_period:]

Step 2: Add plots with make_addplot

Plugins add lines, bars, or scatter markers through mplfinance.make_addplot.

A simple lower-panel example:

from __future__ import annotations

from typing import Any

import pandas as pd
from mplfinance import make_addplot


def apply(
    df: pd.DataFrame,
    plot_args: dict[str, Any],
    options: dict[str, Any],
    display_period: int,
) -> None:
    length = int(options.get("length", 20))
    source_name = str(options.get("source", "Close"))
    line_color = str(options.get("line_color", "royalblue"))
    width = float(options.get("width", 1.5))
    label = str(options.get("label", f"SMA {length}"))
    ylabel = str(options.get("ylabel", "SMA"))

    panel = options.get("plot_panel", "lower")
    secondary_y = bool(options.get("secondary_y", False))

    indicator = df[source_name].rolling(length).mean()

    addplots = plot_args.setdefault("addplot", list())
    addplots.append(
        make_addplot(
            indicator.iloc[-display_period:],
            label=label,
            panel=panel,
            secondary_y=secondary_y,
            color=line_color,
            ylabel=ylabel,
            width=width,
        )
    )

Step 3: Add the config entry

Add this inside the top-level CHART_PLUGINS object in:

defs/user.json
{
  "CHART_PLUGINS": {
    "MY_INDICATOR": {
      "name": "my_indicator",
      "option": "my-indicator",
      "help": "Add My Indicator.",
      "lookback": 100,
      "panel": {
        "kind": "lower",
        "axes": 1,
        "share": true,
        "preferred_axis": "any",
        "allow_volume_panel": true
      },
      "length": 20,
      "source": "Close",
      "line_color": "royalblue",
      "width": 1.5,
      "label": "My Indicator",
      "ylabel": "My Indicator"
    }
  }
}

This creates the CLI option:

--my-indicator

Run it with:

python chart.py --sym RELIANCE --my-indicator

Plugin config fields

Each plugin config should include these fields:

"name": "my_indicator"

The Python module name inside renderer/plugins/.

"option": "my-indicator"

The command-line option used to enable the plugin.

"help": "Add My Indicator."

The help text shown in the CLI.

"lookback": 100

Extra history loaded before the visible chart period. Use a value larger than the longest calculation period in the indicator.

"panel": {
  "kind": "lower"
}

Controls where the indicator is plotted.

Other fields are passed into the plugin through the options dictionary. You can use them for periods, colors, widths, labels, and source columns.


Reusing helper functions

Common indicator calculations are available in:

renderer/plugins/utils.py

Available helpers include:

simple_moving_average(source, length)
exponential_moving_average(source, length)
wilders_moving_average(source, length)
average_true_range(high, low, close, length=14)
relative_strength_index(source, length=14)
macd(source, fastlen=12, slowlen=26, siglen=9)
bollinger_bands(source, length=20, mult=2.0)
supertrend(high, low, close, factor=3.0, atr_length=10)

Import helpers like this:

from .utils import relative_strength_index

Example:

rsi = relative_strength_index(source=df["Close"], length=14)

Use these helpers when possible. It keeps plugins shorter and more consistent.


Practical guidance

Use a price overlay when the indicator belongs on the candles.

Use a shareable lower panel for one-line indicators.

Use an exclusive lower panel for indicators with histograms, multiple scales, or several parts.

Set lookback high enough for the longest calculation. For example, if the indicator uses a 200-period average, use:

"lookback": 250

Keep plugin option names short and easy to type.

Good examples:

--rsi
--macd
--bb
--supertrend

Avoid editing core chart files when adding a new indicator. In most cases, only these two files are needed:

renderer/plugins/<your_plugin>.py
defs/user.json

Clone this wiki locally