Write Your Own Indicator

This tutorial is to guide you how to write your own indicator in our trading platform.

Tutorial Video

Hello everybody, today let's talk about how to write your own custom indicator. Indicators are very important to help us analyze market data.

Market movement is very complicated, so candlestick is not so enough to help us to analyze. We need more intuitive stuff to help us and indicators are very helpful to analyze the market movement. Because you can read the value from your chart straightforward, so it's very intuitive and the result is more precise than candlestick's value.

Maybe someone will tell you we can use Fibonacci to help us as well, they are right. But one thing I hope you to take care of is this kind of technical skills are based on very subjective theory and you can't identify pattern of the market by Fibonacci or Elliott wave. It's very difficult, it's a subjective and I think maybe professional traders can master this kind of skill, but for the beginner it's too difficult. Indicators are very intuitive and we can straightforward read the value, but writing indicator is not so easy.

A lot of trading platforms have provided us with their functions to help us write our own custom indicators. When we don't need to use some regular indicators, we can write our own program logic. But it is complicated and we need to download some stuff and we need to install it.

In Fintechee, everything becomes very easy and you don't need to install, you don't need to download anything. Just open the web page and you can write your own custom indicators here, run them and see the result. It's very intuitive. Let's start learning how to write your own custom indicator at Fintechee trading platform.

Indicators

We have prepared a few of indicators as the built-in functions in our trading platform. Just click this "Arrow" icon on the left menu bar of our and you can see a list of the indicators. They are built-in indicators, you can edit them. But please note that when you open the web page again, they will be restored to the original source code. So, if we want to write your own custom indicator, please rename the indicators and write your own logic. If you don't do that, your logic will be recovered by the original source code.

You can check that built-in indicators logic here by clicking this "Edit" icon. You can refer to the source code. Let's start learning. Before you write your own custom indicator, you need to learn some constant definitions, they are broker name, time frame, order type, where to render, data name, render type, parameter type.

Constant Definition

var BROKER_NAME = {
	DEMO: "CC Demo"
}

var TIME_FRAME = {
	M1: "M1",
	M5: "M5",
	M15: "M15",
	M30: "M30",
	H1: "H1",
	H4: "H4",
	D: "D",
	W: "W",
	M: "M"
}

var ORDER_TYPE = {
	OP_BUY: "BUY",
	OP_SELL: "SELL",
	OP_BUYLIMIT: "BUY LIMIT",
	OP_SELLLIMIT: "SELL LIMIT",
	OP_BUYSTOP: "BUY STOP",
	OP_SELLSTOP: "SELL STOP"
}

var WHERE_TO_RENDER = {
	CHART_WINDOW: "CHART_WINDOW",
	SEPARATE_WINDOW: "SEPARATE_WINDOW"
}

var DATA_NAME = {
	TIME: "Time",
	OPEN: "Open",
	HIGH: "High",
	LOW: "Low",
	CLOSE: "Close",
	HL2: "HL2",
	HLC3: "HLC3",
	HLCC4: "HLCC4"
}

var RENDER_TYPE = {
	HISTOGRAM: "Histogram",
	LINE: "Line",
	ROUND: "Round",
	DASHARRAY: "Dasharray"
}

var PARAMETER_TYPE = {
	INTEGER: "Integer",
	NUMBER: "Number",
	BOOLEAN: "Boolean",
	STRING: "String"
}

We will introduce you how to use them later. We have prepared some common functions here such as SMA EMA SMMA LWMA. If you are familiar with other trading platform and you can understand. These common functions are used to build moving average program logic.

Common Function

function sma (dataInput, dataOutput, calculatedLength, period) {
	var i = calculatedLength

	if (calculatedLength > 0) {
		i--
	} else {
		for (var j = 0; j < period - 1; j++) {
			dataOutput[j] = 0
		}

		i = period - 1
	}

	var sum = 0

	for (var j = i - period + 1; j < i; j++) {
		sum += dataInput[j]
	}

	for (var j = i; j < dataInput.length; j++) {
		sum += dataInput[j]
		dataOutput[j] = sum / period
		sum -= dataInput[j - period + 1]
	}
}

function ema (dataInput, dataOutput, calculatedLength, period) {
	var i = calculatedLength
	var smthFctr = 2.0 / (period + 1)

	if (i == 0) {
		dataOutput[0] = dataInput[0]
		i++
	} else if (i == 1) {
	} else {
		i--
	}

	while (i < dataInput.length) {
		dataOutput[i] = dataInput[i] * smthFctr + dataOutput[i - 1] * (1 - smthFctr)
		i++
	}
}

function smma (dataInput, dataOutput, calculatedLength, period) {
	var i = calculatedLength
	var sum = 0

	if (i > 0) {
		i--
	} else {
		i = period - 1

		for (var j = 1; j < period; j++) {
			dataOutput[i - j] = 0
			sum += dataInput[i - j]
		}

		sum += dataInput[i]
		dataOutput[i] = sum / period
		i++
	}

	while (i < dataInput.length) {
		sum = dataOutput[i - 1] * period - dataOutput[i - 1] + dataInput[i]
		dataOutput[i] = sum / period
		i++
	}
}

function lwma (dataInput, dataOutput, calculatedLength, period) {
	var i = calculatedLength

	if (i > 0) {
		i--
	} else {
		for (var j = 0; j < period - 1; j++) {
			dataOutput[j] = 0
		}

		i = period - 1
	}

	var sum = 0
	var diffsum = 0
	var weight = 0

	for (var j = 1; j < period; j++) {
		sum += dataInput[i - j] * (period - j)
		diffsum += dataInput[i - j]
		weight += j
	}
	weight += period

	while (i < dataInput.length) {
		sum += dataInput[i] * period
		dataOutput[i] = sum / weight
		diffsum += dataInput[i]
		sum -= diffsum
		diffsum -= dataInput[i - period + 1]
		i++
	}
}

SMA stands for simple moving average, EMA stands for exponent moving average, SMMA stands for smooth moving average, and LWMA stands for lightweight moving average. They are different logic and we will use the common functions later, we will expand them more later.

Structure of the Custom Indicator

Now let's start learning the structure of the custom indicator. If you want to use your own custom indicator, you need to register your indicator first. And if you want to register your indicator, you need to use this API: registerIndicator.

registerIndicator("<Indicator's Name>", "<Description>", function (context) {
	var dataInput = getDataInput(context, <Index of the Data Input>)
	var dataOutput = getDataOutput(context, "<Indicator's Name>")
	var period = getIndiParameter(context, "<Parameter's Name, in this case, it's 'period'>")
	var shift = getIndiParameter(context, "<Parameter's Name, in this case, it's 'shift'>")

	var calculatedLength = getCalculatedLength(context)

	sma(dataInput, dataOutput, calculatedLength, period)

	if (shift != null && calculatedLength == 0) {
		setIndiShift(context, "<Data Output's Name, in this case, the name is 'sma'>", shift)
	}
},[{
	name: "<Parameter's Name, in this case, it's 'period'>",
	value: 5,
	required: true,
	type: PARAMETER_TYPE.INTEGER,
	range: [1, 100]
},{
	name: "<Parameter's Name, in this case, it's 'shift'>",
	value: 0,
	required: false,
	type: PARAMETER_TYPE.INTEGER,
	range: [-30, 30]
}],
[{
	name: DATA_NAME.CLOSE, // We use 'close value' of the candlestick.
	index: 0
}],
[{
	name: "<Data Output's Name, in this case, the name is 'sma'>",
	visible: true,
	renderType: RENDER_TYPE.LINE,
	color: "steelblue"
}],
WHERE_TO_RENDER.CHART_WINDOW)

In order to run it you need to register your indicator into the system, into the training platform. After you register your indicators, you can add your own custom indicator to your chart. The new indicators will be shown here and if you want to rewrite them, you can use this panel and find them and edit them to modify the logic. registerIndicator has several parameters: the name, the description, the callback function, the parameters array, the input data array, the output data array and where to render.

Let's start learning what they mean. The parameter stands for a function parameter. We use the callback function to calculate the value and the callback function has one parameter: context. Context is an object and this object has several properties and we will use the properties to get information from the training system.

registerIndicator("<Indicator's Name>", "<Description>", function (context) {} // callback function
...)

The input array will declare which kind of data inputs will be used and in our example. After the callback function, there is a parameters array, we will have two parameters, one is "period" and the other one is "shift" and we will use the two parameters.

registerIndicator("<Indicator's Name>", "<Description>", function (context) {},
[{
    ...
},{
    ...
}],  // declare of an array for parameters
...)

How to calculate the indicator's output? The array below the array for data input is for data output and we will use the "close" value of the candlestick, we will map the value to "zero" index.

registerIndicator("<Indicator's Name>", "<Description>", function (context) {},
[{
    ...
},{
    ...
}],  // declare of an array for parameters
[{
    ...
},{
    ...
}],  // declare of an array for data input
[{
    ...
},{
    ...
}],  // declare of an array for data output
...)

If we map the name to the index, we will specify the index to get the data input. Please refer to this line and see how to get the data input. We know every indicator will has output and input, we will rely on the input data to calculate the the output. Which kind of data to input is very important. We can not only define "close" value as our input data, but we can define "high" value of candlestick or "low" value of candlestick or "open" value of candlestick to input to our program logic and to calculate the output. In our example, we will use "close" value. Before we use it, the first thing is to map it to index. And after we map them, we will get "close" value of the candlestick by specifying 0 as the parameter of getDataInput.

[{
	name: DATA_NAME.CLOSE, // We use 'close value' of the candlestick.
	index: 0
}],

getDataInput is another API of our SDK. In our example, there is just one input data, so we just use "close" value of candlestick.

var dataInput = getDataInput(context, <Index of the Data Input>)

And we will have just one output, so we defined the output line here. The first thing is to give the output data a name, the name is 'sma'.

[{
	name: "<Data Output's Name, in this case, the name is 'SMA'>",
	visible: true,
	renderType: RENDER_TYPE.LINE,
	color: "steelblue"
}],

Why we need to name them is because we need to get the reference for the output array by specifying the name. So we need to define the name first and then use getDataOutput API to specify which data to refer to.

var dataOutput = getDataOutput(context, "<Indicator's Name>")

We will get the reference of the array of the output and put the calculated result into the array. The output data will be shown on the chart.

This parameter specifies where to render the indicator. "CHART_WINDOW" stands for this area which is the same with the main chart. And "SEPARATE_WINDOW" stands for this area separated from the main chart. We need to tell the system how to render and where to render.

registerIndicator("<Indicator's Name>", "<Description>", function (context) {},
[{
    ...
},{
    ...
}],  // declare of an array for parameters
[{
    ...
},{
    ...
}],  // declare of an array for data input
[{
    ...
},{
    ...
}],  // declare of an array for data output
WHERE_TO_RENDER.CHART_WINDOW // This parameter specifies where to render. We have two options, one is to render on the main chart with the candlesticks, the other one is to render in separate window by using WHERE_TO_RENDER.SEPARATE_WINDOW
)

Now, let's learn how to write a callback function. The callback function is where the program logic is.

We can get the input data, we can get output data, we can get indicators' parameters and we can get the candlesticks' number. We can put them all into this common function to calculate the moving averages. If you want to move the line to the left, we need to specify "shift" parameter as a negative value. If we want to move the line to the right, we need to specify "shift" parameter as a positive value. The logic is very simple compared to other training platforms.

function (context) {
	var dataInput = getDataInput(context, <Index of the Data Input>)
	var dataOutput = getDataOutput(context, "<Indicator's Name>")
	var period = getIndiParameter(context, "<Parameter's Name, in this case, it's 'period'>")
	var shift = getIndiParameter(context, "<Parameter's Name, in this case, it's 'shift'>")

	var calculatedLength = getCalculatedLength(context)

	sma(dataInput, dataOutput, calculatedLength, period)

	if (shift != null && calculatedLength == 0) {
		setIndiShift(context, "<Data Output's Name, in this case, the name is 'sma'>", shift)
	}
}

After we write our own custom indicators program logic, we need to run them. What you need to do is just to copy the source code and paste the source code here to run. After you run this logic, the indicator will be registered into your system. Then you can use your own custom indicator here. Hope you can get your own custom indicator soon. Thank you for reading. See you next time.