Home Platform Algorithms Charts Community Pricing Documentation Register Account

Writing Your Technical Indicators

Cloud9Trader includes a large selection of built in technical indicators that you can plot onto charts and use in your trading algorithms to help you determine if market conditions are good for trading. You can also write and test your own custom indicators. Much like your algorithmic trading scripts, technical indicators are written in JavaScript directly into the browser. In the algorithms screen you'll find the technical indicators panel where you can create a new one to get started.

Once written, your indicator will be available to draw itself onto charts across the platform alongside the built in indicators. It will also be available to your trading algorithms, simply by calling it by its key.

We've open sourced our technical indicator code in our GitHub repository as a reference to help you write your own. Just copy the code into your new indicator edit screen and give them a spin.



Some Examples

Most technical indicators have been invented by some smart statistician who often put their names to them, but don't let these complicated long names fool you - they're normally actually very simple. Here's the code Cloud9Trader uses to produce the Gopalakrishnan Range Index:

function onIntervalClose (periods) {
    var highestHigh = Math.highest(prices.high(periods)),
        lowestLow = Math.lowest(prices.low(periods));
    return {
        overlay: false,
        value: Math.log(highestHigh - lowestLow) / Math.log(periods)
    };
}


Not much to it, right? Here's the Stochastic Oscillator:

function onIntervalClose (periods) {
    var lowestLow = Math.lowest(prices(periods));
    return {
        overlay: false,
        value: ((CLOSE - lowestLow) / (Math.highest(prices(periods)) - lowestLow)) * 100
    };
}


And the Exponential Moving Average:

var EMA;
function getRunUpCount (periods) {
    return periods * 2;
}
function onIntervalClose (periods) {
    if (EMA === undefined) {
        EMA = CLOSE;
    }
    EMA = ((CLOSE - EMA) * (2 / (periods + 1))) + EMA;
    return EMA;
}


In all cases you'll see that the actual indicator value calculations are pretty much one-liners. Of course there are some more complicated indicators out there but the point is that they are normally simple. In any case they need to be fairly light so they can be drawn on charts quickly and don't slow down your trading algorithms. If you're feeling fruity, stick one of the examples above into a technical indicator edit screen and give it a spin.

In practice there's a little more code to the examples above. The Stochastic Oscillator normally also plots a smoothing value and there are a few extra formalities - some code to validate the periods input parameter and to tell the system how much historical price keep in memory.

Let's take a look at how to structure your technical indicators and discuss these factors in more detail.



Structure

You'll have noticed that the onIntervalClose function is the heart of your technical indicator. This is the only function you need to declare. Here's a template showing all the methods that, if you include them, will be called by the system when plotting your indicators or charts or using them in trading algorithms when backtesting or live trading:

// Any initialization code or global variable declarations go here.

function validate (parameter1, parameter2, parameterN) {
    // This function is used to check that any input parameters are the correct format and in range.
    // Use the error() function to throw and abort if any fail.
}

function getBufferSize (parameter1, parameter2, parameterN) {
    // This returns a value that tells the system how much historical price data to keep in memory.
    // Set this as small as is necessary to maximize performance.
}

function getRunUpCount (parameter1, parameter2, parameterN) {
    // This returns a value that tells the system how many intervals are needed prior to the start.
    // The onIntervalClose() function will additionally fire for each of these intervals.
}

function getStudyAxisConfig (parameter1, parameter2, parameterN) {
    // If the indicator has its own y-axis, the return value of this function can be used to configure it.
}

function onStart (parameter1, parameter2, parameterN) {
    // Any initialization code goes here. Useful for performing one off calculations or setting global variables.
}

function onIntervalClose (parameter1, parameter2, parameterN) {
    // Called on each candlestick close. The value returned by this function forms the value of the indicator.
    // Any chart configuration can also be returned.
}

Before we take a look at each of these functions in more detail, a note about parameters:


Parameters

Your indicators will often need some parameters, or arguments, to be passed in to do their thing. The number of price data periods for the indicator to act on is a common one, though they may be envelope margins or smoothing factors or anything.

You tell the system about these parameters simply by listing them as formal arguments of the functions as shown in the template above. You need to make sure that these 6 functions (if used) have the exact same arguments signature.

When you are displaying your indicator on a chart, the system will figure out what inputs it needs to ask for in the dialogue that pops up. If the key of the indicator in the template above is 'myIndicator' and you called myIndicator(10, 20, 30); in one of your trading algorithms, each function would receive these arguments. The current value of the indicator would be returned. You can list as many parameters as you like, or none at all.


validate

The validate function is for performing any checks on your input parameters, which are passed to it as arguments. Use the error function to throw if validation fails, as in the example below:

function validate (periods) {
    if (typeof periods !== "number") {
        return error("EMA periods must be a number");
    }
    if (periods % 1 !== 0) {
        return error("EMA periods must be an integer");
    }
    if (periods > 100) {
        return error("EMA maximum periods is 100");
    }
    if (periods <= 0) {
        return error("EMA periods must be greater than 0");
    }
}

getBufferSize

The return value of this function tells the system how much historical price data to load and keep in memory. Inside your indicator you can retrieve a price from this buffer using price(n), or a range of prices using prices(n). You can also use for example price.open(n), prices.bid.high(n) and so on, where n specifies the number of intervals back. You should set the buffer size to amount you'll need.

Taking an indicator that just uses the last N periods of interval closes (a Simple Moving Average) to produce an average, you'd want to just return the supplied number of periods, as below:

function getBufferSize (periods) {
    return periods;
}

If you omit this function the buffer will default to 100 intervals. We recommend always using it though to keep your memory use as low as possible and keep your script performant.


getRunUpCount

Some indicators need to act on price data prior to the start time (beginning of chart or start of trading session). This function tells the system how many intervals to load in and to fire the onIntervalClose for, effectively starting the indicator early.

For example, the Exponent Moving Average indicator shown above starts with a close price and then applies a smoothing formula to each last value, never actually using historical data in bulk. This means that its value is disproportionately weighted towards recent data until has run on a sufficient number of intervals. getRunUpCount is used then to ensure it is run against enough data to avoid a skewed reading at start. We can then set the buffer size to 0 with getBufferSize to save memory, as no historical price is needed.


getStudyAxisConfig (and indicator types)

There are broadly two types of technical indicators - overlays and studies. An overlay, such as an EMA, has the same scale as the price and therefore is plotted directly onto the price chart. Studies are plotted onto a separate y-axis below the main chart.

If your indicator returns a study (note that an indicator can return both overlay and study series), the getStudyAxisConfig allows you to customize the configuration for this axis. You can use many of the options described here: HighStock API Reference yAxis. Note that any HighStock option that expects a function as its value can't be used.

Here's an example. If your indicator oscillates between -1 and 1 and generates a signal if it extends outside -0.7 and 0.7, you might want to configure your yAxis to have tick marks at -1, 0 and 1, and plot lines at -0.7 and 0.7, as follows:

function getStudyAxisConfig () {
    return {
        tickPositions: [-1, 0, 1],
        plotLines: [
            {value: -0.7, color: "red", width: 1, dashStyle: "Dash"},
            {value: 0.7, color: "red", width: 1, dashStyle: "Dash"}
        ]
    };
}

onStart

The onStart function is useful for performing any initialization, particularly for setting any global variables or performing one off calculations up front.

In the example below, we use an onStart function for the EMA example upstairs to initialize an exponent variable, which is more performant than calculating it each close.

var exponent;
function onStart (periods) {
    exponent = 2 / (periods + 1);
}

Now that we've calculated this exponent on start, we can replace the line in the example above to read EMA = ((CLOSE - EMA) * exponent) + EMA;


onIntervalClose

This is the important one. Its job is to return a the value of your indicator. The body of code that does your indicator's work will generally live here. You can of course still declare functions elsewhere and call them from inside this on in the normal manner. Use the API Reference to find all the variables and tools available to you.

If the technical indicator is being used to plot onto a chart, price data for the period shown on the chart will be loaded, plus any run up data prior to the start of the chart. The onIntervalClose function will fire once for each of these intervals and its return value will be what is plotted into the chart at that point. If the chart is showing live prices, it will also be called each time a tick comes in that closes the previous candlestick.

If it's used inside your trading algorithms, onIntervalClose will first be called once for each close of run up data and each time an incoming tick closes an interval. The value returned will form the current value of the indicator that will return whenever you call myIndicator() from within your algorithm (where myIndicator is the indicator's key). Note that if the return value is, or contains, chart configuration objects, only the value property of these will form the value. The rest will be stripped.

Let's discuss the possible return values.


Return Values

A technical indicator can comprise one or several data series. Each point of a series will either be a single numerical value or an array of two numerical values in the case of area range plots. Each data series can either be plotted over a price chart, as an overlay, or on a separate axis, as a study.

This is all determined by the return value of the onIntervalClose function. If you'd like to control how the series is displayed on charts, this value can include custom chart configuration, such as the type of series (e.g. line, area or bar), plot color, line widths and so on. Any chart configuration will be stripped out from the actual value of the indicator, such that when used in trading algorithms, only the actual value is returned.

These return values take the same form as values expected by the plot function described in Algorithms API Reference - plot(). Here are the available options.


Line Series Overlay

The simplest option a single line overlaying the price charts simply by returning the value, as follows.

Example: Plot a line 0.1 percent above the close price.

function onIntervalClose () {
    return CLOSE * 1.001;
}


Line Series Overlay with Options

If you would like to apply custom chart configuration, these can be specified as properties of a return object whose value property becomes the value of the indicator. Not that these properties are stripped out when the indicator is called in a trading algorithm - just the value will be returned. The available options can be found here: HighStock Options Reference - Plot Options. You can additionally set the name of the series (to be displayed in the hover tooltip) and the type, which will be one of "area", "areaspline", "bar", "line" or "spline". This defaults to "spline" (or "areasplinerange" if the return value is an array of 2 numbers, see below). We've also added a precision property to specify the number of decimal places to display in the tooltip.

Example: Plot a line 0.1 percent above the close price, with chart display options.

function onIntervalClose () {
    return {
        name: "Example Line Series",
        color: "orange",
        lineWidth: 4,
        precision: 5,
        value: CLOSE * 1.001
    };
}


Area Range Series Overlay with Options

If your indicator describes an area range, return an array of two numbers as the value. In this case the type will default to "areasplinerange", though you have "arearange" and "columnrange" available for this type of data.

Note that if you have no specific chart options, you will need to return the data as the first item of a multiple series configuration, i.e. return [[n1, n2]];. Otherwise it will be interpreted as two individual line series (see Multiple Series below).

Example: Plot a range 0.1 percent either side of the close price, with chart display options.

function onIntervalClose () {
    var margin = CLOSE * 0.001;
    return {
        name: "Example Area Range Series",
        value: [CLOSE - margin, CLOSE + margin]
    };
}


Line Series Study with Options

If your indicator series is a study, simply set the overlay property to false on the return value.

Example: Plot the price movement as a line series study.

var previousClose;
function onIntervalClose () {
    var value;
    if (previousClose === undefined) {
        value = null;
    } else {
        value = CLOSE - previousClose;
    }
    previousClose = CLOSE;
    return {
        name: "Example Line Series",
        overlay: false,
        type: "areaspline",
        value: value
    };
}

Notice in the example above we returned null for the first interval close where we didn't have the previousClose so couldn't yet determine a value.


Returning null

By returning null as the value, you are indicating that the indicator is unable to produce a value. Perhaps it doesn't have enough run up data - see getRunUpCount above (though this should be set sufficiently to have a reading by start). No data will be plotted on charts for this interval close, and the indicator will return undefined when called from within a trading algorithm until a not null value is produced.


Multiple Series

All the examples above produce a single series, though a technical indicator can comprise as many series as you choose. To specify multiple series, onIntervalClose should return an array. Each item of the array can be any of the return values as described above.

Example: Plot the SMA, its Bollinger Band deviations, volume bars, the Bollinger Bandwidth and Bollinger %B study.

var previousCloses = [];

function onIntervalClose (periods, deviations) {
    var SMA,
        standardDeviation,
        margin;

    // Push closing mid price to end of values array
    previousCloses.push(CLOSE);

    // Return null if not enough data, or remove oldest close if too much data
    if (previousCloses.length < periods) {
        return null;
    } else if (previousCloses.length > periods) {
        previousCloses.shift();
    }

    // Calculate Simple Moving Average center line
    SMA = Math.mean(previousCloses);

    // Calculate standard deviation of previous closes
    standardDeviation = Math.standardDeviation(previousCloses);

    // Bollinger margin is a multiple of the standard deviation
    margin = standardDeviation * deviations;

    return [
        {name: "Simple Moving Average", dashStyle: "Dash", value: SMA}, 
        [SMA - margin, SMA + margin],
        {name: "Volume", type: "bar", opposite: true, value: VOLUME},
        {name: "Bollinger Bandwidth", overlay: false, value: ((margin * 2) / SMA) * 100},
        {name: "Bollinger %B", overlay: false, opposite: true, value: (CLOSE - SMA + margin) / (margin * 2)},
    ];
}

Note the use of the opposite property above to overlay series with different scales over existing charts by rendering an additional y-axis on the right hand side.


Nested Indicators

Technical indicators can use other indicators inside them in the same way a trading algorithm would. Just call them as a function by their key passing in any parameters.

Note that the historical price data in memory is shared between the all nested indicators. This means the buffer size can only be set once by the dependant indicator. Return values of getBufferSize will be ignored in indicator dependencies, so make sure the indicator loads a sufficient amount for any dependencies.

The same applies to run up data. If dependency indicators need to fire a run up to start, you will have to use a getRunUpCount function on the dependant indicator to accommodate, even where it doesn't need any itself.



Finally, you will want to keep the API Reference handy when writing your code.

Was this page useful? If you find any errors or have any questions please ask on the Community Forum or email support@cloud9trader.com.