Book a Demo
Book a Demo

ETNA's Blog

Table of contents

author

Daniil Safonov

rocket

Industry insights you won’t delete.

Delivered by ETNA

    submit

    19.03.2021

    author

    Daniil Safonov

    Verified

    How to Create a Custom Indicator for ETNA Robot

    img

    er_logo_darkIn the previous article, I’ve mentioned that ETNA Robot is designed primarily for automated trading and is focused on providing market data to strategies, enabling them to make better decisions based on their algorithms. We consider Level I market data (quotes) as primary events for strategies. But these streams are rather huge and fluctuating, so strategies, which are designed to analyze relatively long-term trends and regularities, are usually based on cumulative data sources like time bars, which aggregate prices and volumes for a specific time frame. Time bars may not be the only source of decision making. It’s useful to analyze prices across several periods and discover more long-term regularities, which are lying in the basis. This is done usually with the help of technical indicators. There are tens of known indicators, but some investors may use their own implementations based on existing ones or completely new logic and formulas. I’m going to explain below how to implement custom indicators in Robot and use them in strategies.

    Indicators are Functions

    To say it simply, any indicator in Robot is considered to be a function over a set of bars. This concept is simple and fully describes how indicators are treated by the system. In this way, implementing an indicator is just writing a method, which can calculate a single value on a given list of bars. Indicators may be also used as ‘helping functions’ for strategies, since their calculation is based on double-precision float numbers, though strategies use big-decimal numbers to ensure precision of pricing and quantities during calculations, that’s why indicators are also a good point to inject third-party statistics and math libraries.
    I’d like to show how to implement a simple moving average indicator to make you familiar with the basics of indicators.

    Creating SMA indicator

    The first thing we need to do is to implement two classes, which extend Indicator and Value classes. Also, we can use additional annotations to make displaying of the indicators on the client-side easier.

    public class SmaValue extends Value {
        
        private final double sma;
        
        public  SmaValue(V base) {
            super(base);
            this.sma = base.getSma();
        }
        
        public SmaValue(Date date, double sma) {
            super(date);
            this.sma = sma;
        }
        
        @ValueMeta(name = "SMA", color = 0xff0000, isMain = true)
        public double getSma() {
            return sma;
        }
    }
    @TechnicalIndicator(name = "Simple Moving Average")
    public class SimpleMovingAvgIndicator extends Indicator {
    
        @Override
        protected List<IndicatorBase<List, ? extends Value>> getInitializedBoundIndicators() {
            return emptyList();
        }
    
        @Override
        protected SmaValue calculateIndicator(List source) { … }
    }
    

    There are several special methods and annotations we need to focus on. Let’s look at base classes first. The base class for indicator value is Value. This class plays two roles: it contains the date field, which is required for each indicator, since all their results are bound to price data at a specific date; also this class is a marker to indicate that a particular object is actually an indicator value. Other fields are selected depending on the indicator’s nature. In the current case we have just average price value, but it may be also several values in channels or a sort of enumeration, if the indicator is used as a signal provider for a strategy. Indicator values may also have ValueMeta annotation. This annotation adds chart-specific descriptions to the value point: a human-friendly name, a type of chart, color and whether the value is a primary characteristic of the point. These data are used by the client to properly display indicators.
    There are actually two basic classes for indicators: Indicator and ComplexIndicator. They differ in data sets, which are provided for calculation. A simple indicator uses single-symbol data set; though complex indicator is provided with a multi-symbol set, which means that it can be used to calculate correlations or other common characteristics of several price sequences. We use a single-symbol indicator for SMA, since it uses only prices of a single series. Indicators also have a specific annotation TechnicalIndicator, which is used to properly display indicators’ charts on the client side. This annotation determines a human-friendly name and whether the indicator should be shown in a separate frame.

    Indicator’s Calculation

    Let’s take a look at the methods we need to implement. The first method, which ought to be implemented, is getInitializedBoundIndicators. To properly understand it, we need to become familiar with bound indicators concept.
    Some indicators may rely on values of other indicators. For instance, EMA uses SMA as a first value in the series. You may also create custom indicators, combining different ones together (e.g., RSI and Fisher). In this case, a new indicator will be based on values of underlying or ‘bound’ indicators and the system will ensure that these indicators are calculated before the calculation of a compound indicator begins. Since SMA is not based on other indicators, we just return an empty list.
    When the bound indicators’ values are ready, the calculation of the indicator begins. It’s done in calculateIndicator method. This method receives a list of the latest known bars (they are called in indicators ‘source data’ to distinguish them from bars provided to strategies). The list is ordered by date descending and the first element references to the latest known bar. The convention for this method requires that it should return a new indicator value for the current date (date of the latest bar), or null if the value can’t be calculated. This requirement makes it clear for the strategy and the user when the indicator’s value is zero and when it can exists at all. Assuming said above, the implementation of the SMA calculation may look like this.

    @Override
    protected SmaValue calculateIndicator(List source) {
        // current date
        Date date = source.get(0).getDate();
       
        if (source.size() >= interval) {
            //sum of closing prices divided by period
            double sum = 0;
            for (int i = 0; i < interval; i++) {
                sum += source.get(i).getClose();
            }
            double result=sum / interval;
            return new SmaValue(date,result);
        } else {
            // not enough data - nothing to calculate and return
            return null;
        }
    }
    

    Indicator’s properties

    You may notice, that we reference to an interval variable. It’s known that SMA calculates average price during specific period. This period is called ‘interval’ and measured in bar intervals (e.g., when we use hourly bars and indicator with interval 3, than the value is an average price for the last 3 hours). Also it’s notable that when there are not enough bars to calculate the value (size is less than interval) the method returns null.
    Indicator’s properties, which influence the calculation process and may be changed by user or a strategy, should be specially declared with the Property annotation.

    @Property(name = "Interval", defaultValue = "10")
    public int getInterval() { return interval; }
    public void setInterval(int interval) { this.interval = interval; }

    This annotation lets the system know what parameters can be configured. Also, you should notice that this parameter influences the calculation process and applies certain limitations on the source series; in the current case, the number of bars available should be equal or greater than interval value. To indicate, that the indicator have specific requirements for series length, we can use getRequiredNumOfSourceDataItems method.

    @Override
    public int getRequiredNumOfSourceDataItems() {
        return interval;
    }
    

    This method is used by the indicator’s provider in core to store and provide enough historical data for calculations, but not to over-use memory (store more bars, than could be used).
    In this article I’ve described how to create a simple moving average indicator using ETNA Robot API. This API provides ability to quickly create a simple indicator or go deeply and build new indicators above existing ones. Also it’s possible to create indicators across several bar series to discover their correlation. In the upcoming articles I’m going to demonstrate how to create a simple strategy which uses this indicator.

    Explore our platforms with
    web and mobile demos

    Ria Platform
    Advanced Trading Platform
    Paper Trading Platform

    Demo RIA Software

    Manage portfolios with advanced rebalancing and real-time insights.

    Access customizable client reports and streamlined compliance tools.

    Designed for advisors seeking efficient client and portfolio management.

    Try Web Demo
    image

    Demo Advanced Trading Platform

    Test multi-asset strategies with real-time and historical data.

    Analyze market depth, execute complex options, and algorithmic orders.

    Ideal for refining strategies and risk management before live trading.

    Try Web Demo
    image

    Demo Paper Trading Platform

    Practice trading with virtual funds in real market conditions.

    Simulate cash, margin, and day-trader accounts to gain experience.

    Perfect for honing skills in a risk-free, customizable environment.

    Try Web Demo
    image
    Book a Demo Free Simulator