Backtester Documentation

Docs

Backtester Documentation

Welcome to the BDPO documentation. This resource provides detailed information on creating Python-based strategies and indicators, as well as instructions for backtesting and deploying them on a chart. It covers various backtesting functions, the data involved, script creation, and the use cases for different types of backtests. Additionally, it includes sections on developing multi-timeframe strategies, building custom indicators for charts, and accessing and utilizing the marketplace.


Get Started
  • Create and design trading indicators to suit your style
  • Create and customise trading strategies
  • Leverage the help of BDPO AI to develop indicator and strategy scripts
  • Optimise performance and improve returns
  • Robustness and usage across different tickers and asset classes
  • Testing and live execution of your strategy
  • Publish and/or sell on the BDPO marketplace to the trading community

Your Profile & Scripts
Your profile page is home to all of your custom strategies and indicators. Within the 'Scripts' tab on your profile page, you will find all scripts whether you create strategies and indicators from scratch or downloaded them from the marketplace. From here, you can navigate to the script editor or the Backtester Lab to backtest your strategies.

Library Whitelist

To prevent abuse of the backtester lab, we have implemented a whitelist system. This means that only certain Python libraries can be imported and used when defining your strategies and indicators. The following libraries are whitelisted and can be used in your scripts:

  • BDPO
  • datetime
  • pandas
  • numpy
  • pandas_ta
  • scipy
This list is constantly being updated, so if you would like to use a library that is not on this list, please contact us and we will consider adding it to the whitelist.


The Backtester Lab
The Backtester Lab: Introduction

The BDPO Backtester Lab is a Python-based backtesting platform that allows you to create, backtest and optimise your own trading strategies. Designed for traders of all levels, the backtester lab will provide in-depth details on performance, risk, returns, statistics and individual trades.

As well as backtesting your strategy on a historical data, the backtester lab allows for optimisation of your strategy parameters, forward testing on unseen data (both backtests and optimisations), as well as stress testing of your strategy on hundreds of samples of data. This allows you to understand the robustness of your strategy and how it may perform in the future.


Structure

When creating strategies and indicators in the Strategy Editor, these scripts will be written using the Python programming language. As a result, strategies and indicators should be written in a specific format using classes/functions in order to run smoothly when backtesting. The documentation below will provide an overview of the structure of both strategies and indicators, as well as how to write and backtest them.

It is important to note that the backtester lab is designed to be as flexible as possible, allowing for the creation of a wide range of strategies and indicators. If you are not familiar with Python, there are many community written scripts available in the marketplace that you can download and backtest yourself without the need to write any code.


Strategy Editor

The Strategy Editor is where you will create and edit your strategies and indicators. This is an in-browser code editor that allows you to write your scripts in Python. Once you have written your script, you can save it to your profile and backtest strategies using the backtester lab, or apply indicators to your charts in the charting platform.

BDPO+ and BDPO Ultra users can also access the AI assistant to help develop their scripts. The AI assistant will provide suggestions on how to improve your script, as well as provide feedback on potential issues or improvements that can be made. This is a great tool for those who are new to writing scripts, or for those who want to improve the performance of their existing scripts.

In the event of an error in your script, check the console (in the backtester lab for strategies or in the chart console for indicators) for more information on the error. On script save, the editor will also check for syntax errors and highlight them in the editor.

To begin writing your own scripts, visit the Strategy Editor page.


Backtest

Once you have written your strategy or indicator in the Strategy Editor, or downloaded a script from the marketplace, you can backtest it using the backtester lab. This will provide you with detailed information on the performance of your strategy, including returns, risk, statistics and individual trades.

To begin backtesting your scripts, visit the Backtester Lab page.

The Backtester Lab gives you a choice of backtest types to really test your strategy's robustness and accuracy. The backtest types are:

  • Backtest
    • Backtest your strategy on historical data to see how it would have performed in the market

  • Optimisation
    • Optimise your strategy parameters to maximise returns and minimise risk.

  • Forward Test
    • Forward test your strategy on unseen data to see how it would have performed in the future, without lookback bias.

  • Scenario Testing
    • Stress test your strategy on hundreds of samples of unseen data to see how it would have performed in different market conditions. This will give you an idea of the robustness of your strategy.

  • Candle Replay
    • Test your manual trading skills by replaying historical data candle by candle. This will allow you to practice your trading skills and test your strategies in real-time.

  • Candle Replay PRO
    • Test your manual trading skills by replaying historical data tick by tick. This gives a more accurate representation of the market and allows you to practice your trading skills in real-time.


Chart

Once you have written your indicator script in the Strategy Editor, or downloaded a script from the marketplace, you can apply it to your charts in the charting platform. This will allow you to see the indicator plotted on the chart, as well as any buy/sell signals that are generated by the indicator.

To begin applying your scripts to your charts, visit the Charting page.


Custom Strategies
Structure

Strategies in the BDPO Backtester Lab are written in Python and should be structured in a specific way in order to be backtested. The strategy itself should be setup as a Python class with this structure:

  • Library Imports
  • Define the Class
  • Set strategy variables
  • '__init__' Method
  • 'next' Method

Here is a skeleton structure of a strategy script:

import BDPO

class Strategy(BDPO.Strategy):

    param1: int = 1
    param2: int = 2

    def __init__(self, data):
        pass

    def next(self, data):
        pass

Within this strategy structure there are three main components. The first being the strategy class variables. The class variables are used to define any parameters that you would like to be able to adjust when backtesting your strategy. These parameters can be adjusted in the backtester lab and will be passed to the strategy when it is initialized. The second component is the __init__ method. This method is used to initialize any variables that you would like to use in your strategy upon start. If there are none, you can pass this method. The third component is the next method. This method is called at each new datapoint and is where you define the logic of your strategy. This is where you will define your buy/sell signals based on the data that is passed to the strategy.

You'll notice that 'data' is passed as an argument to both the __init__ and next methods. This 'data' object contains the OHLCV data for the asset that you are backtesting, and is needed for the strategy to run.


Data Format

Arguably, the most crucial part of creating a strategy of your own is to be able to understand and work with the data in which you are testing on.

The BDPO backtest was designed with speed and accuracy in mind. To achieve this, the data is stored, and passed to the strategy in the form of a Numpy array. To understand how to work with this data, we will look at the format of the data, and also how to access it.


Lets see what happens when printing the following:

def next(self, data):
    self.log(data)


The result of running BDPO.console() (which prints the result to the console) will look something like:

BDPO.Engine2.backtest_engine.Data object at 0x11cf048

This isn't very helpful to us, so lets look at how we can access the data.


the data object stores all timeframes for you to work with:

  • data[0] = Base Timeframe (or whichever timeframe the backtest is being run on)
  • data['1m'] = 1 Minute Timeframe
  • data['5m'] = 5 Minute Timeframe
  • data['15m'] = 15 Minute Timeframe
  • data['30m'] = 30 Minute Timeframe
  • data['1h'] = 1 Hour Timeframe
  • data['4h'] = 4 Hour Timeframe
  • data['1d'] = 1 Day Timeframe
The data is stored so you can access multiple timeframes within the same strategy. When you begin a backtest, you will chose the timeframe you wish to test on. This will be your base timeframe, or timeframe [0].

To demonstrate this, if we set up a backtest to run on the 1hour timeframe, my base timeframe will be the 1hour timeframe, and therefore, data[0] will also be the 1hour timeframe data. The same will apply if I set up a backtest on the 5minute timeframe, I can access this timeframe's data by calling data[0]. Now, if we were to print data[0] it would return us a pandas dataframe of the base timeframe's data. We can access the most recent candle like this:

def next(self, data):
    self.log(data[0].iloc[-1])


Console:
>> t 2024-04-01 09:00:00 o 1.07898 h 1.07909 l 1.07856 c 1.07859 v 3389.0 Name: 290, dtype: object


We have data!

With each candle that passes, we have logged our data. This backtest was run on the 1hour timeframe, and as a result of logging data[0], we have access to the 1hour timeframe data. As the backtest progresses, we will see the dataset evolve and continue to be printed to the console. It is important to note that the data is stored in a pandas dataframe, and as such, we can access the data in the same way we would with any other pandas dataframe.

Note: The next() function runs on each new datapoint on the tick and/or minute timeframe. Therefore, by printing data[0] in the next() function, we are printing the latest candle, which is updated with any new incoming data. As a result, the data is 'built' up as it would be in a live trading environment.

  • Note: While printing data (or any variables) to the console like this is helpful for development, these log statements will slow down your backtest significantly. It is recommended to remove these log statements when they are no longer needed.


It is possible to also access the datetime / open / high / low / close / volume of the previous candle by calling them in the same way.

  • data[0]['t']
  • data[0]['o']
  • data[0]['h']
  • data[0]['l']
  • data[0]['c']
  • data[0]['v']

Now we have accessed the latest candles of our based timeframe using data[0], but what if we wanted to access the latest candles of a different timeframe to create a multi-timeframe strategy?

We can do so in the same way:
  • data[0]['c'] # base timetrame close prices
  • data['1m']['c'] # 1 minute close prices
  • data['5m']['c'] # 5 minute close prices
  • ...


Getting Started

Let's run through a simple SMA Golden Cross strategy template to get started. The first step for every strategy is to import the BDPO library. This is a built-in library that provides powerful and flexible functionality for backtesting. By importing it, you will be able to access all the functions and methods that are required to build your strategy. The strategy will not run without it:

import BDPO
# import other libraries here


The benefits of using a Python based strategy editor is that you have access to all the powerful libraries that Python has to offer. You can import multiple libraries, and use it to enhance your strategy.

NOTE: BDPO has a whitelist of libraries that are allowed to be imported. This is to ensure that the platform is secure and to prevent any malicious code from being executed. Navigate to the Library Whitelist section of this documentation to find out more. After importing your libraries, it is time to set up the strategy Class.

To create a strategy, you need to create a class that inherits from the BDPO.Strategy class. This class will contain all the methods and attributes that are required to run your strategy. We can also define our strategy params here too. These parameters can be modified in the backtester lab when running the strategy. Here is an example of a strategy class:

import BDPO

class Strategy(BDPO.Strategy):

    fast_length: int = 50 # Length of the fast SMA
    slow_length: int = 200 # Length of the slow SMA
    take_profit_pips: float = 0.0005 # Take profit in pips
    stop_loss_pips: float = 0.0005 # Stop loss in pips
    max_positions: int = 3 # Maximum number of positions
    risk: float = 0.01 # Trade size


...


So far we have imported the BDPO library and created a strategy class. We have then also defined the params. The next step is to define the __init__ method. This method is called when the strategy is initialized. It is used to set up class variables which are not modifiable by the user, at the start of the strategy.

import BDPO

class Strategy(BDPO.Strategy):

    fast_length: int = 50 # Length of the fast SMA
    slow_length: int = 200 # Length of the slow SMA
    take_profit_pips: float = 0.0005 # Take profit in pips
    stop_loss_pips: float = 0.0005 # Stop loss in pips
    max_positions: int = 3 # Maximum number of positions
    risk: float = 0.01 # Trade size


    def __init__(self, data):
        self.buys = 0
        self.sells = 0

Here, the __init__ function is defined with 'self' and 'data' as input arguments. This is necessary for the strategy to run.

Within the __init__ function, we define any variables we wish to refer back to during the running of the strategy. If no variables have been defined, the __init__ function must still be defined, but can be passed using 'pass'. In this example, we are defining variables 'buys' and 'sells' and setting them to 0. This is a simple example where the strategy uses the 'buys' and 'sells' class variables but they are not modifiable by the user in the Backtester Lab. You can define any variables you wish. Now for the next() class function and the strategy logic within it:

import BDPO
import pandas_ta as ta

class Strategy(BDPO.Strategy):

    fast_length: int = 50 # Length of the fast SMA
    slow_length: int = 200 # Length of the slow SMA
    take_profit_pips: float = 0.0005 # Take profit in pips
    stop_loss_pips: float = 0.0005 # Stop loss in pips
    max_positions: int = 3 # Maximum number of positions
    risk: float = 0.01 # Trade size

    def __init__(self, data):
        self.buys = 0
        self.sells = 0

    def next(self, data):
        df = data[0] # Main timeframe
        max_length = max(self.fast_length, self.slow_length)

        if len(df) < max_length:
            self.log("Not enough data to calculate indicators.")
            return

        fast_sma = ta.sma(df['c'], length=self.fast_length)
        slow_sma = ta.sma(df['c'], length=self.slow_length)

        if fast_sma is None or slow_sma is None:
            self.log("Could not calculate SMAs")
            return

        current_close = df['c'].iloc[-1]
        current_fast_sma = fast_sma.iloc[-1]
        current_slow_sma = slow_sma.iloc[-1]

        if current_fast_sma > current_slow_sma and fast_sma.iloc[-2] <= slow_sma.iloc[-2] and self.sells == 0:
            if len(self.open_positions()) < self.max_positions:
                trade_lots = self.account_capital() * self.risk * 0.01
                id = self.trade(trade_type='BUY', trade_lots=trade_lots)
                self.buys += 1
                self.log("id:", id, "Opening Buy @" + str(current_close))

        elif current_fast_sma < current_slow_sma and fast_sma.iloc[-2] >= slow_sma.iloc[-2] and self.buys == 0:
            if len(self.open_positions()) < self.max_positions:
                trade_lots = self.account_capital() * self.risk * 0.01
                id = self.trade(trade_type='SELL', trade_lots=trade_lots)
                self.sells += 1
                self.log("id:", id, "Opening Sell @" + str(current_close))

        open_positions = self.open_positions()
        if open_positions:
            if self.pnl() > self.account_capital() * 0.005:
                self.log(f"Closing all positions in profit {self.pnl()}")
                self.close_all()
                self.buys = 0
                self.sells = 0

Here we have defined a few things so lets go through them one by one:

We now have the next() function which is also compulsory in every strategy (along with the init() function). The next() function is called on every tick/new datapoint. This is where we define our trading logic.

Within the next function, we also include some error handling to ensure that if the data is not available (for example, at the start of the strategy), the function will return and not trade, as the moving averages will not have enough data to calculate.

We then define the current_close, current_fast_sma and current_slow_sma variables. Following this, we define our conditions for opening a trade. In this case, we are using a simple moving average crossover strategy with a grid approach. We have not included any risk management at this point, and this is not recommended. However, as an example of strategy creation, we have kept it simple.

Finally, when we have open positions, we check for a positive PnL above 0.5% of the account capital, and if this is the case, we close all positions. This is a simple way to take profit when the strategy is in profit. We also reset the buys and sells variables to 0, so that the strategy can start again.


Strategy Functions

Below are the built-in functions defined in the BDPO library to help create, manage, execute and optimise strategies:


  • self.log(message)

The 'self.log' function is used to log messages to the Backtester Lab console. This can be useful for debugging (similar to the Python print() function)

self.log("Hello World")



  • self.symbol_information()

This function returns the symbol information of the current symbol. The return of this function is a dictionary containing the following symbol information:
- contract_size
- exchange
- name
- pip
- precision
- type

self.symbol_information()



  • self.open_positions()

This function returns the open positions of the current strategy. The return of this function is a list of dictionaries containing the following position information:

- ID
- TYPE
- SYMBOL
- LOTS
- STOPLOSS
- TAKEPROFIT
- OPENPRICE
- OPENDATE
- HASH
- TRANSACTION_COST
- OPEN_CANDLESTAMP

self.open_positions()



  • self.pending_positions()

This function returns the pending positions of the current strategy. The return of this function is a list of dictionaries containing the following position information:

- ID
- TYPE
- SYMBOL
- LOTS
- STOPLOSS
- TAKEPROFIT
- OPENPRICE
- OPENDATE
- HASH
- TRANSACTION_COST
- OPEN_CANDLESTAMP

self.pending_positions()



  • self.closed_positions()

This function returns the closed positions of the current strategy. The return of this function is a list of dictionaries containing the following position information:

- ID
- TYPE
- SYMBOL
- LOTS
- STOPLOSS
- TAKEPROFIT
- OPENPRICE
- OPENDATE
- HASH
- TRANSACTION_COST
- CLOSEDATE
- CLOSEPRICE
- PNL
- DURATION

self.closed_positions()



  • self.account_capital()

This function returns the account capital of the current strategy. The return of this function is a float.

self.account_capital()



  • self.account_equity()

This function returns the account equity of the current strategy. The return of this function is a float.

self.account_equity()



  • self.pnl()

This function returns the profit and loss of any open trades. The return of this function is a float.

self.pnl()



  • self.trade()

This function is used to execute market and pending orders.

self.trade(
    trade_type,
    trade_lots,
    trade_price,
    trade_stop_loss,
    trade_take_profit,
)

trade_type:
This could be any of the following:

  • BUY or 0
  • SELL or 1
  • BUY_LIMIT or 2
  • SELL_LIMIT or 3
  • BUY_STOP or 4
  • SELL_STOP or 5

trade_lots:
This is the lots to be traded. Min lots = 0.01 and max lots = 20.00

trade_price:
This is only applicable for pending positions, and will be ignored for market orders. For BUYSTOP the price must be above the current price, and for SELLSTOP the price must be below the current price. For BUYLIMIT the price must be below the current price, and for SELLLIMIT the price must be above the current price.

trade_stop_loss:
This is optional and is the stop loss for the trade.

trade_take_profit:
This is optional and is the take profit for the trade.

Note: All open and pending trades will be assigned an ID (incremental) which is also returned when executing a successful self.trade()



  • self.close_by_id(trade_id)

This function is used to close a trade by its trade_id. The function takes the trade ID (int) as a parameter. The trade ID is returned after successfully opening a trade using the self.trade() function.

self.close_by_id(trade_id)



  • self.close_all()

This function is used to close all open AND pending trades at the time of calling.

self.close_all()


Custom Indicators

Custom indicators are a powerful tool that can be used to create your own technical indicators. These indicators can be used to visually represent data on your charts, and can be applied to any chart in one click. Custom indicators are also written in Python, saved in your library, and once you have perfected your indicator, you can share it with the community by submitting it to the marketplace.

Structure

Custom indicators are tools built on top of your chart data and therefore should be structured in a specific way in order to be compatible and deployable. The structure of a custom indicator is different to custom stategies, in the fact that indicators are Python functions. The structure is as follows:

  • Library Imports
  • Indicator Function
  • Input Validation
  • Indicator Logic
  • Indicator Plots
  • Returning the Indicator Overlay

Here is a skeleton structure of an Indicator script:

import BDPO

def MyIndicator(data, length: int=10):

    datetime, open, high, low, close, volume = BDPO.data_to_pandas(data)

    # define your indicator logic here

    BDPO.plot(
        type="Spline",
        data=pd.DataFrame({'SMA': sma}),
        props={
            'showSpline': True,
            'splineColor': '#00FF00',
            'splineWidth': 1.5,
            'skipNan': True,
        }
    )

    return BDPO.overlay(
        precision='chart',
        zIndex=1
    )



Within this indicator structure there are 5 main components. The first being the library imports. Next, the indicator function and input validation. This is where you define the logic of your indicator. Once the logic is defined, you can plot the indicator on the chart. This is done using the BDPO.plot() function. Finally, returning the indicator overlay with indicator plots. Let's break down each of these components:

Library Imports: This is where you import the BDPO library (compulsory) and any other libraries. The Library Whitelist outlines which libraries are available for use in custom indicators.

Indicator Function: This is where you define the logic of your indicator. The function itself should be defined with the following arguments:

  • data (compulsory): The data that is passed to the indicator function. This is the data that is displayed on the chart and is crucial to allow the indicator to run
  • (arg: arg_type = arg_default) Optional arguments that can be passed to the indicator function. These arguments can be used to customize the indicator to your liking. They should always be defined with a argument type, and a default value


Input Validation: This is where you validate the input data that is passed to the indicator function. This is important to ensure that the data is in the correct format and can handle any unintended values. For example, with a moving average indicator, many functions require a length parameter. This parameter should be validated to ensure that it is a positive integer > 2.

Indicator Logic: This is where you define the logic of your indicator. This is where you calculate the values of your indicator based on the input data. For example, with a moving average indicator, you would calculate the moving average of the close price of an asset.

Indicator Plots: This is where you plot the indicator on the chart. This is done using the BDPO.plot() function. This function takes in the following arguments:
  • type (compulsory): The type of plot that you want to display. This can be any of the plots defined in Indicator Configuration
  • data (compulsory): The data that you want to plot. This should must be in the form of a pandas DataFrame or Series. Anything else will result in an error for your indicator.
  • props (optional): Additional properties that you can pass to the plot, which are specific to the plot type that you are using. For example, with a line plot, you can pass in the color of the line, the width of the line, etc.


Indicator argument types are defined as follows:

  • int
  • float
  • bool
  • str
  • list

Arg: list
It is worth noting that all indicator arguments (except data itself) are user inputs. Therefore, each argument is modifiable on the chart page to allow for customization of the indicator.
When defining an argument with type = list, this provides the user with a dropdown menu of options to choose from. This is useful for arguments that have a limited number of options (eg. ['SMA', 'EMA', 'WMA'] when selecting a moving average type). Only one option can be selected and therefore only one value will be passed to the indicator function at a time. The list is purely to allow the user to select from a predefined set of options.


Getting Started

Let's create a simple custom indicator that calculates the moving average of the close price of an asset (Simple Moving Average). The first step is to create a new indicator script, and import the BDPO library and any other libraries that are included in the whitelist in which you may need in your script. Next we will define the indicator function:

import BDPO
import pandas as pd
import numpy as np

def Indicator(
    data,
    length: int = 20
):

...

So far we have imported the BDPO library, and the pandas and numpy libraries. We have also defined the indicator function 'def Indicator(...)'. As previously mentioned, we must pass 'data' to the function in order to access the OHLCV data of the asset that the indicator is running on.

In this function we have also declared the 'length' parameter (optional), which is the number of periods that the moving average will be calculated over. It is important to declare each variable type (int, float etc...) as well as a default value. Any variables declared here will be customisable when deploying the indicator on the charting page

After defining the type of the variable, a default value must be assigned to the variable.

Here is an example of each:

def MyExample(
    data,
    paramter1: int=10,
    paramter2: float=0.5,
    paramter3: bool=True,
    paramter4: str="Hello",
    paramter5: list=[1, 2, 3]
):
    ...


Next, we will define the body of the function, where the logic of the indicator will be implemented. In this case, we will calculate the simple moving average of the close price of the asset.

import BDPO
import pandas as pd
import numpy as np

def MyIndicator(data, length: int = 10):

    datetime, open, high, low, close, volume = BDPO.data_to_pandas(data)

    if len(close) <= length:
        return BDPO.overlay(
            precision='chart',
            zIndex=1
        )

    # Calculate the Simple Moving Average
    sma = pd.Series(close).rolling(window=length).mean().fillna(np.nan)
...



Data validation! Before defining any logic, it's important to validate any inputs. In this example, the SMA indicator is calculating an average over a number of periods. If the user inputs a length less than the indicator length defined by the user, the indicator will return an empty plot. This is good for error handling, and reducing the risk of any errors that may occur when the indicator is applied to the chart. When the user applies the indicator to their chart, they will be able to input a custom value for the length, and any other parameters that are defined in the function. Therefore it is important to validate the inputs to ensure that the indicator will run correctly.


In our indicator body, we have defined the simple calculations for the moving average (the average of the last n periods). We have also included the BDPO.data_to_pandas function to handle the data into the Indicator function. The data_to_pandas function is used to convert and return the OHLCV components of the data, into a pandas dataframe. For this example, the current chart timeframe (or data[0]) is being used.

The final step in defining the function is to format the indicator and plots. This is done by ensuring that the data is plotted as a pandas DataFrame WITHOUT DATETIMES

import BDPO
import pandas as pd
import numpy as np

def MyIndicator(data, length: int = 10):

    datetime, open, high, low, close, volume = BDPO.data_to_pandas(data)

    if len(close) <= length:
        return BDPO.overlay(
            precision='chart',
            zIndex=1
        )

    # Calculate the Simple Moving Average
    sma = pd.Series(close).rolling(window=length).mean().fillna(np.nan)

    # Plot the SMA line
    BDPO.plot(
        type='Spline',
        data=pd.DataFrame({'SMA': sma}),
        props={
            'showSpline': True,
            'splineColor': '#00FF00',
            'splineWidth': 1.5,
            'skipNan': True,
        }
    )

    return BDPO.overlay(
        precision='chart',
        zIndex=1
    )


We now have a dataframe with one column; 'sma'. It is now ready to be formatted for the chart.


Configuration

Above, we have seen how to create and return an indicator. Now, we will look at the options you have when plotting the indicator overlay. The indicator plot is a built-in function with 3 arguments: type, data, and props. The type argument is the type of plot you want to display (e.g. Spline, Area, etc.). The data argument is the data you want to plot, and the props argument is the properties of the plot. The props argument is optional and can be left out if you do not want to customize the plot. Below, we will go over the different types of plots you can use and the properties you can customize:

  • ArrowTrades
    • pd.Series
    • Stacked arrow trades
    • Props:
      • showArrowTrades
        • Type: boolean
          Default: True
      • arrowBuyColor
        • Type: string (hex color)
          Default: #08c65e
      • arrowSellColor
        • Type: string (hex color)
          Default: #e42633
      • arrowSize
        • Type: int
          Default: 7
      • arrowShowLabels
        • Type: boolean
          Default: True
      • arrowMarkerOutline
        • Type: boolean
          Default: True
      • arrowOutlineWidth
        • Type: int
          Default: 4
  • Area
    • pd.Series
    • Area plot with the area under the Series' spline shaded
    • Props:
      • showArea
        • Type: boolean
          Default: True
      • areaColor
        • Type: string (hex color)
          Default: #31ce31
      • areaLineWidth
        • Type: float
          Default: 1.25
      • areaBack1
        • Type: string (hex color)
          Default: #31ce3115
      • areaBack2
        • Type: string (hex color)
          Default: #31ce3101
  • Band
    • pd.DataFrame (3 columns: ['high', 'mid', 'low'])
    • Bands indicator, e.g. BollingerBands
    • Props:
      • showBand
        • Type: boolean
          Default: True
      • bandColor
        • Type: string (hex color)
          Default: #b41d70
      • bandBackColor
        • Type: string (hex color)
          Default: #b41d7022
      • bandLineWidth
        • Type: int
          Default: 1
      • showMid
        • Type: boolean
          Default: True
  • Candles
    • pd.DataFrame (5 columns: ['open', 'high', 'low', 'close', 'volume'])
    • Standard candlestick chart
    • Props:
      • showCandles
        • Type: boolean
          Default: True
      • colorBodyUp
        • Type: string (hex color)
          Default: #41a376
      • colorBodyDw
        • Type: string (hex color)
          Default: #de4646
      • colorWickUp
        • Type: string (hex color)
          Default: #23a77688
      • colorWickDw
        • Type: string (hex color)
          Default: #e5415088
      • colorVolUp
        • Type: string (hex color)
          Default: #41a37682
      • colorVolDw
        • Type: string (hex color)
          Default: #de464682
      • showVolume
        • Type: boolean
          Default: True
      • currencySymbol
        • Type: string
          Default: £
      • showAvgVolume
        • Type: boolean
          Default: True
      • avgVolumeSMA
        • Type: int
          Default: 20
      • colorAvgVol
        • Type: string (hex color)
          Default: #1cccb777
  • Cloud
    • pd.DataFrame (2 columns: ['line1', 'line2'])
    • Cloud indicator, e.g. Ichimoku
    • Props:
      • showCloud
        • Type: boolean
          Default: True
      • cloudColor1
        • Type: string (hex color)
          Default: #55d7b0aa
      • cloudColor2
        • Type: string (hex color)
          Default: #d94d64aa
      • cloudBack1
        • Type: string (hex color)
          Default: #79ffde60
      • cloudBack2
        • Type: string (hex color)
          Default: #ff246c60
      • cloudDrawLines
        • Type: boolean
          Default: True
  • Histogram
    • pd.DataFrame (3 columns: ['hist', 'value', 'signal'])
    • Colored histogram, eg. MACD
    • Props:
      • showHistogram
        • Type: boolean
          Default: True
      • histBarWidth
        • Type: int
          Default: 4
      • histLineWidth
        • Type: int
          Default: 1
      • histColorUp
        • Type: string (hex color)
          Default: #35a776
      • histColorDw
        • Type: string (hex color)
          Default: #e54150
      • histColorSemiUp
        • Type: string (hex color)
          Default: #79e0b3
      • histColorSemiDw
        • Type: string (hex color)
          Default: #ea969e
      • histColorValue
        • Type: string (hex color)
          Default: #3782f2
        histColorSignal
        • Type: string (hex color)
          Default: #f48709
  • PriceLabels
    • pd.Series
    • Price labels that stick to candles (eg. [{"text": "Text", "dir": -1, "pin": "high", "color": "#FF0000", "back": "#220000", "stroke": "#FF0000", "offset": "10"}])
    • Props:
      • showPriceLabels
        • Type: boolean
          Default: True
      • labelColor
        • Type: string (hex color)
          Default: #adadad
      • labelBack
        • Type: string (hex color)
          Default: #14151c
      • labelStroke
        • Type: string (hex color)
          Default: #606060
      • labelBorderRadius
        • Type: int
          Default: 3
      • labelOffset
        • Type: int
          Default: 5
  • Range
    • pd.Series
    • Ranging indicator, e.g. RSI
    • Props:
      • showRange
        • Type: boolean
          Default: True
      • rangeColor
        • Type: string (hex color)
          Default: #ec206e
      • rangeBackColor
        • Type: string (hex color)
          Default: #381e9c16
      • rangeBandColor
        • Type: string (hex color)
          Default: #535559
      • rangeLineWidth
        • Type: int
          Default: 1
      • upperBand
        • Type: int
          Default: 70
      • lowerBand
        • Type: int
          Default: 30
  • Sparse
    • pd.DataFrame (optional 2nd column: ['value', '?direction'])
    • Sparse data points, where direction is either 1 (up) or -1 (down) - only valid when props.sparseShape == 'triangle'
    • Props:
      • showSparse
        • Type: boolean
          Default: True
      • sparseColor
        • Type: string (hex color)
          Default: #898989
      • sparseSize
        • Type: int
          Default: 3
      • sparseShape
        • Type: string
          Default: point (# point | square | cross | triangle)
  • Spline
    • pd.Series
    • Single spline
    • Props:
      • showSpline
        • Type: boolean
          Default: True
      • splineColor
        • Type: string (hex color)
          Default: #31ce31
      • splineWidth
        • Type: int
          Default: 1
      • skipNan
        • Type: boolean
          Default: True
  • SuperBands
    • pd.DataFrame (6 columns: ['high1', 'mid1', 'low1', 'high2', 'mid2', 'low2'])
    • Two bands: above and below the price
    • Props:
      • showSuperBands
        • Type: boolean
          Default: True
      • superBandsColor1
        • Type: string (hex color)
          Default: #d80d3848
      • superBandsColor1dark
        • Type: string (hex color)
          Default: #d80d3824
      • superBandsColor2
        • Type: string (hex color)
          Default: #1edbbe33
      • superBandsColor2dark
        • Type: string (hex color)
          Default: #1edbbe15
  • Trades
    • pd.DataFrame (3 columns: ['dir', 'price', 'label'])
    • Trade markers
    • Props:
      • showTrades
        • Type: boolean
          Default: True
      • tradeBuyColor
        • Type: string (hex color)
          Default: #08b2c6
      • tradeSellColor
        • Type: string (hex color)
          Default: #e42633
      • tradeRadius
        • Type: int
          Default: 4
      • tradeShowLabels
        • Type: boolean
          Default: True
      • tradeMarkerOutline
        • Type: boolean
          Default: True
      • tradeOutlineWidth
        • Type: int
          Default: 4

Default Indicators
Built-In Charting Indicators

Below is a list of built-in charting indicators that can be applied to your chart:


  • Candle Doji
  • Candle Inside Bar
  • Even Better Sine Wave
  • Awesome Oscillator
  • Absolute Price Oscillator
  • Bias
  • Balance of Power
  • Brar
  • Commodity Channel Index
  • Chande Forecast Oscillator
  • Center of Gravity
  • Chande Momentum Oscillator
  • Coppock Curve
  • Correlation Trend Indicator
  • Directional Movement
  • Efficiency Ratio
  • Elder Ray Index
  • Fisher Transform
  • Inertia
  • KDJ
  • KST
  • Moving Average Convergence/Divergence
  • Momentum
  • Pretty Good Oscillator
  • Percentage Price Oscillator
  • Psychological Line
  • Quantitative Qualitative Estimation
  • Rate of Change
  • Relative Strength Index
  • Relative Strength Index Xtra
  • Relative Vigor Index
  • Slope
  • Stochastic Momentum Index Ergodic
  • Squeeze Momentum
  • Squeeze Pro
  • Schaff Trend Cycle
  • Stochastic Oscillator
  • Stochastic RSI Oscillator
  • Tom Demark Sequential
  • TRIX
  • True Strength Indicator
  • Ultimate Oscillator
  • Williams %R
  • Arnaud Legoux Moving Average
  • Double Exponential Moving Average
  • Exponential Moving Average
  • Fibonacci Weighted Moving Average
  • Gann HiLo
  • hl2
  • hlc3
  • Hull Moving Average
  • Hull Weighted Moving Average
  • Ichimoku
  • Jurik Moving Average
  • Kaufman Adaptive Moving Average
  • Linear Regression
  • McGinley Dynamic
  • Midpoint
  • Midprice
  • ohlc4
  • Pascals Weighted Moving Average
  • Wilders Moving Average
  • Sine Weighted Moving Average
  • Simple Moving Average
  • Ehlers Super Smoother
  • Supertrend
  • Symmetric Weighted Moving Average
  • T3
  • Triple Exponential Moving Average
  • Triangular Moving Average
  • Variable Index Dynamic Average
  • Volume Weighted Moving Average
  • Weighted Close Price
  • Weighted Moving Average
  • Zero Lag Moving Average
  • Drawdown
  • Log Return
  • Percent Return
  • Entropy
  • Kurtosis
  • Mean Absolute Deviation
  • Median
  • Quantile
  • Skew
  • Standard Deviation
  • Variance
  • Z-Score
  • Average Directional Movement
  • Archer Moving Averages Trends
  • Aroon
  • Choppiness Index
  • Chande Kroll Stop
  • Decay
  • Decreasing
  • Detrended Price Oscillator
  • Increasing
  • Parabolic Stop and Reverse
  • Qstick
  • TTM Trend
  • Vertical Horizontal Filter
  • Vortex Indicator
  • Aberration
  • Acceleration Bands
  • Average True Range
  • Bollinger Bands
  • Donchian Channels
  • Holt Winter Channel
  • Keltner Channels
  • Mass Index
  • Normalized Average True Range
  • Price Distance
  • Relative Volatility Index
  • Thermo
  • True Range
  • Ulcer Index
  • Accumulation Distribution
  • Archer on Balance Volume
  • Chaikin Money Flow
  • Elders Force Index
  • Ease of Movement
  • Klinger Volume Oscillator
  • Money Flow Index
  • Negative Volume Index
  • On Balance Volume
  • Positive Volume Index
  • Price Volume
  • Price Volume Trend