User-Defined Indicators

1Introduction

It's easy to write your own indicators for FX Blue charts. User-defined indicators (UDIs) are just Javascript. You can use any editor of your choice, from Notepad to Eclipse or Visual Studio. A UDI typically has only two simple functions, and you can use all the features of the Javascript language. For large complex projects, you can even use an environment such as Typescript or Coffeescript which compiles to produce Javascript.

UDIs can do all the same things as the built-in indicators:

Plot values in a variety of styles, such as lines, points, histograms, channels, and candles

Create drawings on the chart, such as rectangles and lines

Create event markers

Create bar highlights (changing the colours of specific candles on the chart)

UDIs are web workers and have access to all the standard features of the browser's Javascript environment. For example, they can set timers, and they can read external data such as an economic calendar or trading signals, using XMLHttpRequest or web sockets.

You can debug your UDI using the features built into your browser, such as Chrome's Developer Tools.

1.1Example UDI code

There are several example UDI files. You can download them from the following URL:

https://www.fxblue.com/chart/udi/udi-examples.zip

It's possible to learn how to write UDIs just from these example files, without reading this document. The number at the start of each file, e.g. 01-udi-basic-sma.js, is the suggested order for working through the examples.

1.2Adding a UDI to a chart

You add a UDI to a chart - including any of the examples - by clicking on the indicators button in the chart toolbar, choosing Advanced / UDI, and then pasting in the Javascript code.

You can also store your UDI on a web server, and then paste in the URL of the file rather than its contents. Note: your server must be CORS-friendly; your server must set the CORS headers Access-Control-Allow-Origin and Access-Control-Allow-Credentials on its response so that the chart is allowed to make the cross-origin HTTP request.

If you want to share your UDI with other people while protecting how it does its calculation, you can use a code obfuscation service. Examples include javascript-obfuscator (free) and JScrambler (paid).

1.3Lifecycle

The UDI is initialised once, when it is loaded. The UDI.onInit() function is not then called again if there is a change to the chart, such as a switch to a different market or timeframe, nor is it called if there is a change to the user-defined parameters. (This behaviour is different to other platforms such as MT4 and MT5.)

If your UDI needs to react to changes in the choice of market or timeframe (beyond just recalculating all its data, which will happen anyway) then you can watch for these changes by implementing the optional functions UDI.onContextChange() and UDI.onParameterChange()

1.4Alerts

Your UDI automatically participates in the chart's alerting system. The user can set alerts on any of your indicator's plots, unless you set the noAlerts property.

1.5Indicator-on-indicator

Your UDI automatically participates in the chart's ability to apply one indicator to another indicator (unless you turn this off using the noIOI setting).

Another indicator - for example, a moving average - can be applied to any of the plots of your indicator. And, if your indicator has a Source field, then it can receive the values of another indicator rather than candle data such as close prices.

1.6Technical note: web workers

A UDI runs as a web worker, in its own browser thread. It does not have access to the HTML of the chart or the browser DOM. You interact with the chart using the API described in this document and in the example code.

Because it's a web worker, your UDI can use Javascript's importScripts() to include external code libraries.

1.7Technical note: dates

All dates which you need to pass to the chart, such as co-ordinates for creating a drawing or an event marker, should be transmitted as a number of milliseconds (since 1/1/1970, rather than a textual date).

The dates used on the chart depend on the time zone, which is user-configurable. If you need to convert from an external source of dates, e.g. an economic calendar which uses GMT, then you can use the helper functions described below.

2Initialisation: UDI.onInit()

A UDI must have a UDI.onInit() function. This returns a description of your indicator: its name; what it draws (line, histogram etc); and any user-configurable fields it wants such as a number of bars, or a colour.

An example of the UDI.onInit() function is as follows. It returns an object which must contain a name property, a plots[] array, and a settingsFields[] array. There are also other optional properties which can be included.

UDI.onInit = function(data)

{

return {

// Display-name for the indicator

caption: "My SMA",

// Specifies that it should be in the main panel, not its own sub-panel

isOverlay: true,

// List of things which the indicator draws: just a line in this basic example

plots: [

{type: "line", caption: "avg", color: "blue", lineWidth: 2}

],

// Settings fields which the indicator wants.

settingsFields: [

// The "Source" field has special meaning, and the chart automatically

// assigns a caption to this field

{id: "Source"},

// Also require a number of bars for the average

{id: "period", caption: "Period", type: "int", defaultValue: 14, min: 2}

]

};

};

2.1Return value from UDI.onInit()

The return value from UDI.onInit() should be an object, as in the example above, with the following members:

Member

Description

caption

The name to display for your UDI

isOverlay

True/false depending on whether your UDI should be shown in the main chart panel (true) or in its own sub-panel (false)

plots[]

Array describing the types of values which your UDI draws: lines, histograms, bars etc. The array can be empty if your UDI only creates drawings, or event markers, or bar highlights.

settingsFields[]

Array of user-configurable parameters which your UDI wants. This array can be empty if your indicator does not need any configurable settings.

noAlerts

Can be used to turn off the ability for the user to set alerts on any of the UDI's plots. You can also disable this for each individual plot, using its own noAlerts property.

noIOI

Can be used to prevent your UDI participating in the ability to apply one indicator to another indicator.

axis

Overrides the default settings for the value axis, by including any or all of the following properties. Only applicable if the indicator is in its own sub-panel (isOverlay:false).

axis.min

Minimum value for the axis

axis.max

Maximum value for the axis

axis.step

Increment between markers on the axis

axis.noYGrid

Turns off horizontal grid lines in the sub-panel

axis.baseline

Baseline value (which turns a line plot into a filled area either side of the baseline)

2.2The plots[] array

The plots[] array in the object you return from UDI.onInit() describes the different types of values which your UDI draws: a line, a histogram etc.

Your UDI can draw any number of different plots. For example, to draw something like the standard Bollinger Band indicator, you would use 4 plots: a channel (for the filled high-low range) plus 3 line plots for the centre line, the upper line, and the lower line.

Each plot in the array must have a caption, and a type. The type of plot determines how many values you need to pass back from UDI.onCalculate(). If you are drawing a channel plus a line, then you will need to pass back 3 arrays of values in UDI.onCalculate(): 2 for the channel plus 1 for the line.

Plot type

Values

Description

line

1

Simple line

point

1

Individual points rather than a continuous line

histogram

1

Histogram; column starting at zero

histogramPositiveNegative

1

Histogram with different colours for >0 and <0

floatingHistogram

2

Histogram from X to Y, rather than 0 to Y (first series is the upper value; second series is the lower value)

channel

2

Filled channel (first series is the upper value; second series is the lower value)

candles

4

Candles (with open, high, low, close)

The full details of each plot are as follows. The only compulsory properties which you must provide are caption and type. All the other properties have defaults. Some members of the plot definition only apply to particular types of plot.

Property

Description

caption

The display name for the plot

type

The type of plot; one of the values listed above

noAlerts

Prevent users being able to set alerts on the value of this plot. (Not applicable to point plots; always true.)

color

HTML colour code for the plot, e.g. "red", "#FF0000", "rgba(255,0,0,0.5)". For histogramPositiveNegative or candles plots, this should be a comma-separated list of two colours, e.g. "red,#008000"

lineWidth

Only applicable to line or point plots. Width of the line/marker to draw.

lineStyle

Only applicable to line or point plots. Style for the marker/line: "solid", "dash", "dot". It can also be any character from the Font Awesome font, in the form "icon-aaaa" where "aaaa" is the hex code of the character, such as "f0ab".

markerOffset

Only applicable to point plots. Offset in pixels for displaying the marker. A negative value is above the price; a positive value is below the price. See the example fractal UDI for a typical use of the markerOffset, displaying markers above the high of a bar or below its low.

2.3The settingsFields[] array

The settingsFields[] array in the object you return from UDI.onInit() describes the configurable fields which people should be able to modify for your indicator.

There is one special field which fundamentally affects the type and behaviour of your UDI. If your indicator has a Source field then it receives, in UDI.onCalculate(), a single data series such as the close prices or the high prices - or the values from another indicator. If your indicator does not have a Source field then it receives the full data for each bar - open, high, low, close, and volume - and your UDI cannot be applied to another indicator.

The Source field is defined in the settingsFields[] array as follows. (It is normal, but not compulsory, for it to be the first field.)

settingsFields: [

{id: "Source"}

]

All other fields in the settingsFields[] array should have an id, a caption, and a type. They can also optionally have other properties depending on the type of field. For example:

settingsFields: [

{id: "Source"},

{id: "threshold", caption: "Threshold", type: "float", defaultValue: 1, min: 1, step: 0.1},

{id: "highlightcolor", caption: "Color", type: "color", defaultValue: "rgba(0,0,255,0.5)"},

{id: "options", caption: "Display type", type: "select", options: [

{k: "line", v: "Display as line"},

{k: "marker", v: "Display as histogram"}

]}

]

The types of field which you can use are as follows:

Field type

Description

int

Integer field

float

Numeric, non-integer field

yesno

Yes/no (Boolean) field

select

List of options

textline

Free-text field

color

Colour field

maType

Type of moving average, for use with the UDI.movingAverage() helper function

The full set of properties which can be defined for each field is as follows:

Property

Description

id

ID for your field

caption

Display text for your field

type

The type of field: "int", "color", "textline" etc

defaultValue

Default value for the field (depending on its type)

omitFromParameters

Tells the chart that the field should not be displayed in the summary of the indicator's values

min

For numeric fields, the minimum value which can be entered

max

For numeric fields, the maximum value which can be entered

step

For numeric fields, the increment between values

options[]

For the select field-type only, an array of options for the user to select from. Each entry in the array should have a key and a displayable caption. For example: {k: "close", v: "Close price"}. The value which your indicator receives, in UDI.onCalculate(), is the k property.

2.4Context data passed to UDI.onInit()

The UDI.onInit() function receives one parameter, data, which contains a single member, context, describing the current settings for the chart as a whole.

The same context data is also passed to all other UDI functions, including UDI.onCalculate().

The properties in data.context are as follows:

Property

Description

chartId

Permanent ID for the chart, persisting across different sessions and reloads

chartInstanceId

Temporary ID for the chart, not persisting across sessions

chartStyle

Descriptor for the type of chart: filled candle, hollow candle etc

indicatorId

ID allocated to the instance of your UDI on the chart, persisting across sessions and reloads of the chart

userId

Unique but anonymised ID for the logged-in user of the charting system

instrument

Description of the selected instrument and timeframe of the chart, containing the following properties.

instrument.symbol

ID for the selected market

instrument.symbolCaption

Display name for the selected market

instrument.timeframe

Timeframe of the chart, as either a number of seconds (e.g. 3600 for H1), or a negative value for a tick-chart (e.g. -30 for a T30 chart)

instrument.pipSize

"Pip" size of the market, e.g. 0.0001 on EUR/USD

instrument.multiplier

Multiplier applied to the chart prices, e.g. 10

instrument.invertPrices

Whether the user has selected to invert the chart prices

instrument.bid

Current bid price

instrument.ask

Current ask price

instrument.tz

Object describing the user-selected time zone for the chart, with the following properties.

instrument.tz.offsetMinutes

Base number of minutes ahead of GMT (e.g. 120 for GMT+2)

instrument.tz.dstMode

Daylight savings schedule: 0 = none; 1 = USA; 2 = Europe; 3 = Australia

3Calculating data: UDI.onCalculate()

Your UDI must have a UDI.onCalculate() function. This is called whenever there is new data and, therefore, your indicator needs (or may need) to update its output values.

A simple example of the UDI.onCalculate() function is as follows:

UDI.onCalculate = function(data, output)

{

// Get the user-selected value of the "period" parameter

var period = data.parameters["period"];

// The data to be worked on is in the array data.valueData.

// We then put our output - the average - in output.values, which

// is an array of arrays, with entries depending on the number of plots

for (var i = period - 1; i < data.valueCount; i++) {

var sum = 0;

for (var j = 0; j < period; j++) sum += data.valueData[i - j];

output.values[0][i] = sum / period;

}

};

The UDI.onCalculate() function receives two parameters: data and output. The data tells the UDI about the chart's data values, and also about things such as the current value of the UDI's settings. The UDI then does its calculations and puts the results into output.

There are two types of UDI:

If your UDI has a Source field, then it receives a single series of input values, such as close prices or high prices, in data.valueData

If your UDI does not have a Source field, then it receives the full bar data for the chart, in data.barData

3.1Reading settings fields

Your UDI can have settings fields, such as the number of bars for a moving average, and the current value of each of these settings is then available in the data.parameters which are passed to UDI.onCalculate().

Within data.parameters there will be a property for each settings field, using the id you defined in the settingsFields[] array, and the value of the property is the field's current value.

For example:

settingsFields: [

// Number of bars for the average

{id: "period", caption: "Period", type: "int", defaultValue: 14, min: 2}

]

UDI.onCalculate = function(data, output)

{

// Get the user-selected value of the "period" parameter

var period = data.parameters["period"];

}

3.2Input data for the UDI

As explained above, your UDI receives either a single array of input values, in data.valueData, or the complete bar data for the chart, in data.barData, depending on whether the UDI has a Source field.

The data.valueData is a single array of input values, such as close prices or the values from another indicator.

The data.barData contains sub-arrays for date, open, high, low, close, and volume: data.barData.date, data.barData.open, data.barData.high etc

The data.valueCount property defines the number of input data points. It will be same as the length of the data.valueData array, or of each array inside data.barData.

The input data (and also your output data) is indexed with the oldest item in [0], and the latest, current value in [length-1]. (If you are used to the MT4 or MT5 platforms, this is the opposite way round to what you may be familiar with.)

3.3Updates of the current bar only

To make calculations faster and more efficient, the chart tells your UDI whether only the current bar has changed since the last calculation, using the data.currentBarUpdateOnly property.

Typically, if data.currentBarUpdateOnly is set, then you only need to re-calculate the final, most recent item in your output. If it is not set, then you need to re-calculate the whole series of output values from your indicator, because any part of the historic data may have changed.

The example code contains two versions of an SMA indicator: one which always re-calculates all values, and a more efficient one which uses data.currentBarUpdateOnly to update only the current average value if only the current bar is changing.

3.4Calculating output data

You put the results of your UDI's calculations into the output object which is passed to your UDI.onCalculate().

The output has a values member which is an array of arrays. The length, and order, of output.values depends on the plots defined for your indicator. For example:

plots: [

{type: "channel", …}, // Requires 2 output series

{type: "line", …}, // Requires 1 output series

{type: "candles", …} // Requires 4 output series

]

In this example, output.values will contain 7 sub-arrays. Items [0] and [1] will be for the channel plot; item [2] will be for the line plot; and items [3] to [6] will be for the candle plot.

Each sub-array inside output.values will already have been initialised so that it contains the required number of elements (matching the number of bars on the chart). In other words, the length of each sub-array, such as output.values[0].length, will be the same as data.valueCount. You assign values into the sub-arrays rather than using push() to add data.

If UDI.onCalculate() is being called for an update of the current bar only, with data.currentBarUpdateOnly set, then the contents of output.values will be the last results you passed back. Typically, you only then need to re-calculate the final value in each sub-array.

If the update is not just for the current bar only, then the contents of each sub-array in output.values will have been initialised with null values.

3.5Requesting out-of-band updates

There are circumstances where your indicator may need to re-plot its values without a new market price. For example, you may request external data, and then need to update your indicator's output when that data is received.

You can use the function UDI.requestUpdate() to force an immediate recalculation of your indicator. This will generate an asynchronous but immediate call to UDI.onCalculate().

3.6Calculating moving averages

Many indicators involve calculating moving averages in order to prepare the indicator's final values. For example, a MACD indicator is based around the calculation of two moving averages, for its "fast" and "slow" periods.

The chart provides a helper function, UDI.movingAverage(), which you can use instead of calculating moving averages yourself. The example code includes Hull and MACD indicators which demonstrate how to use this function.

The UDI.movingAverage() function takes three parameters:

Parameter

Description

maType

ID for the type of moving average: "sma", "hull" etc

period

Parameter for the moving average (number of bars)

inputData

Array of values to calculate from

The output you get back is an array with the same number of elements as the inputData array. For example, if you pass in an array of 1000 items, and 20 as the number of bars, then a Simple Moving Average will return an array which also has 1000 items, and where the first 19 values in the output array are null (because averages can only be calculated from the 20th value onwards).

The available types of moving average, for the maType parameter, are as follows:

Type

Description

sma

Simple moving average (i.e. arithmetic mean of last N values)

ema

Exponential moving average

smma

Smoothed moving average

wma

Weighted moving average

tma

Triangular moving average

vidya

Variable index dynamic average

lsma

Least squares moving average

hull

Hull moving average

dema

Double exponential moving average

tema

Triple exponential moving average

mcginley

McGinley dynamic moving average

If you want to make the type of average user-configurable, rather than hard-coded into your UDI, then you can include in your settingsFields[] an entry with its type set to maType. For example:

settingsFields: [

{id: "ma", caption: "Moving Average Type", type: "maType"}

]

UDI.onCalculate = function(data, output)

{

// 20-bar average of chosen type

var arrAvg = UDI.movingAverage(data.parameters["ma"], 20, data.valueData);

}

4Changes to the chart or parameters

If there is a change to the chart, such as a switch of timeframe, or a change in your user-defined settings, then the chart will always make your UDI recalculate all its data by issuing a call to UDI.onCalculate().

However, if you need to take special, extra action when these values/settings change, then you can implement the optional functions UDI.onContextChange() and UDI.onParameterChange()

4.1UDI.onContextChange()

UDI.onContextChange() is called whenever there is a change to the chart such as a switch of market or timeframe. The function receives a data parameter which contains the same context information which is passed to UDI.onInit().

For example:

UDI.onContextChange = function(data)

{

// Get current chart timeframe

var tf = data.context.instrument.timeframe;

};

4.2UDI.onParameterChange()

UDI.onParameterChange() is called whenever there is a change to the user-defined settings for your UDI. The function receives a data parameter which contains the new settings, as a parameters object. It also receives the same context information which is passed to UDI.onInit(), in data.context.

For example:

settingsFields: [

// Number of bars for the average

{id: "period", caption: "Period", type: "int", defaultValue: 14, min: 2}

]

UDI.onParameterChange = function(data)

{

// Get the latest user-selected value of the "period" parameter

var period = data.parameters["period"];

}

5Creating drawings, event markers, and bar highlights

In addition to plotting values, using the plots[] array and UDI.onCalculate(), your UDI can also create objects on the chart:

Drawings, such as rectangles, lines, bar markers, trend channels etc

Event markers. These are markers against a specific date/time, displayed at the bottom of the chart, and automatically stacked if there are multiple markers to be displayed against the same bar.

Bar highlights. These change the colour of a specific bar on the chart in order to highlight it.

Note: you cannot create these objects from UDI.onInit(). If you want to create objects on start-up, do so from the first call to UDI.onCalculate() which is always issued immediately after an indicator is loaded.

For examples of the event markers and bar highlights, see the built-in Inside Bars or Outside Bars indicators.

When you create one of these objects, it stays on the chart (until your UDI is removed). You do not need to re-create the object in each call to UDI.onCalculate().

Objects are not removed if the user changes the market or timeframe of a chart. You will often want to remove and re-create objects after this sort of change, which you can detect using UDI.onContextChange().

Objects can be created from any function in your UDI, not just in response to UDI.onCalculate(). For example, you can use XMLHttpRequest to get external data, and then display that data on the chart as drawings:

UDI.onInit = function(data)

{

// Make a data request

var xmlr = new XMLHttpRequest();

xmlr.addEventListener("load", onMyDataLoad);

xmlr.open("GET", "http://www.example.org/mydata");

xmlr.send();

// Return indicator definition

return { … };

}

function onMyDataLoad()

{

// Parse this.responseText and create objects…

UDI.createDrawing(…);

}

Your UDI cannot see or manipulate objects which do not belong to it. You cannot modify or remove drawings, event markers, or bar highlights which were created by the user or by another indicator.

5.1Creating drawings

You create drawings using UDI.createDrawing(). The parameter for this function is either a single drawing definition, or an array of definitions. For example:

UDI.createDrawing([

{type: "horizontalLine", points: [{value: 1.2345}], style: {line: {color: "red"}}},

{type: "horizontalLine", points: [{value: 1.3456}], style: {line: {color: "blue"}}}

]);

By default, the user can select a drawing and change its properties, but the user cannot move or delete the drawing. You can change this behaviour using the drawing's properties.

You can remove all your drawings from the chart using UDI.removeAllDrawings().

You can also remove individual drawings, or you can reposition a drawing without deleting and recreating it. In order to do this, you need to get the ID of the drawing. These are provided via an optional asynchronous callback function which you can pass to UDI.createDrawing(). For example:

UDI.createDrawing([

{type: "horizontalLine", points: [{value: 1.2345}], style: {line: {color: "red"}}},

{type: "horizontalLine", points: [{value: 1.3456}], style: {line: {color: "blue"}}}

], function (response) {

// The ID of each drawing will be contained in the array response.drawingId[]

UDI.myLine1 = response.drawingId[0];

UDI.myLine2 = response.drawingId[1];

});

Once you have the ID of a drawing, you can delete it by passing the ID to UDI.removeDrawing().

A drawing can be moved by passing an object containing the ID and a new array of points to UDI.moveDrawing(). For example:

UDI.moveDrawing({

drawingId: …,

points[{date: data.barData.date[0]}, value: data.barData.high[0]}]

});

The properties of a drawing - text, colour etc - can be changed using UDI.changeDrawing(). You pass an object containing the ID and any properties you want to change. For example:

// Change drawing text and its colour

UDI.changeDrawing({

drawingId: …,

text: "New text",

style: {text: {color: "red"}}

});

UDI.removeDrawing(), UDI.moveDrawing(), and UDI.changeDrawing() can be given an array as their parameter, rather than a single item.

5.1.1Drawing properties

Drawings can have the following properties. Each drawing must have a type and an array of points[]. The number of required points depends on the type of drawing: one for a horizontal line, two for a rectangle etc

Some properties are only applicable to some types of drawing (e.g. text, icon).

Property

Description

type

Type of drawing, e.g. "lineSegment"

points[]

Array of points, each one consisting of a date and a value. For example:
points: [{date: data.barData.date[0]}, value: data.barData.high[0]}]

For text and rectangle drawings, the points can also be pixel co-ordinates (from the top-left of the chart), instead of time-price co-ordinates. For example:
points: [{x:100, y:100}, {x:200, y:200}]

style

Graphical style for the drawing, consisting of line and/or fill and/or text sub-objects depending on the type of drawing

style.line

Can contain the properties color, width, and lineStyle. The style of the line can be "solid", "dash", or "dot". For some types of drawing, such as lines, the style can also be any character from the Font Awesome font, in the form "icon-aaaa" where "aaaa" is the hex code of the character, such as "f0ab".

style.fill

Can contain a color property, specifying fill color

style.text

Can contain the properties color, fontFamily, fontSize, and fontStyle.

inMainPanel

Only applicable to indicators which are displayed in their own sub-panel (isOverlay:false). Controls whether the drawing is created in the indicator's sub-panel or in the main chart panel.

unselectable

If true, makes the drawing completely unselectable (and unmoveable, and undeletable)

moveable

If true, allows the user to move the drawing

removable

If true, allows the user to delete the drawing

showInBackground

Controls whether the drawing is shown in front of the bars, or behind them

text

Text to display

icon

Icon to display, as a hex code of a character from the Font Awesome character set, such as "f0ab"

iconSize

Size, in pixels, for the icon

markerOffset

For the barMarker drawing type, offsets the position of the marker from the price by the specified number of pixels (negative for above, positive for below)

textAboveLine

Boolean value which positions text above the centre of the drawing (e.g. above a horizontal line or a bar marker) rather than below it

5.1.2Types of drawing

The types of drawing which you can create are as follows.

Type

Points

Description

barMarker

1

Marker against a time and price, consisting of an icon and optional text

text

1

Text label. (Note: you can also display text using a rectangle drawing)

horizontalLine

1

Horizontal line (date component of point is ignored and not required)

verticalLine

1

Vertical line (value component of point is ignored and not required)

rectangle

2

Rectangle

circle

2

Circle (the two co-ordinates are the centre and any point on the circumference)

ellipse

2

Ellipse (the two co-ordinates are the centre and any point on the circumference)

triangle

3

Triangle

lineSegment

2

Line from one time/price to another time/price

ray

2

Like lineSegment, but extends the line from the second price to the end of the chart

diagonal

2

Like lineSegment, but extends the line to both chart edges

channel

3

Filled channel extending to the edge of the chart

priceRange

2

Filled range covering the whole of the chart horizontally between two prices (date component of points is ignored and not required)

dateRange

2

Filled range covering the whole of the chart vertically between two dates (date component of points is ignored and not required)

5.2Creating event markers

Event markers are created against specific date/times, and drawn at the bottom of the chart. They are automatically stacked if there is more than one marker for each bar.

(Event markers are intended for things such as calendar events, which apply to a bar as a whole. To draw a marker against both a date/time and also a specific price, use a barMarker drawing.)

An event marker can have the following properties:

Property

Description

date

Date for the event marker, as a number of milliseconds. Does not have to match the start time of a bar. Will be drawn against the bar containing the specified time.

icon

Hex code of a character from the Font Awesome character set, such as "f0ab"

priority

Priority of the event marker compared to other markers, on a numeric scale from 0 (lowest) to 2 (highest). Determines stacking order if there are multiple markers for a single bar

color

Color for the marker, as an HTML colour code such as "red" or "#FF0000" or "rgba(255,0,0,0.5)"

text

Text to display if the user clicks on the marker

noLine

Specifies that no line should be drawn across the chart for the marker

You create markers by passing an individual marker definition, or an array of definitions, to UDI.createEventMarker(). If you are creating several markers at once, it is far more efficient to pass an array than to make multiple calls to UDI.createEventMarker(). For example:

UDI.createEventMarker([

{date: data.barData.date[i], color: "red", icon: "f0ab", text: "My marker 1"},

{date: data.barData.date[i], color: "green", icon: "f0aa", text: "My marker 2"}

]);

You can remove all your event markers from the chart using UDI.removeAllEventMarkers().

You can also remove individual markers (rather than removing everything and re-creating different ones). The UDI.createEventMarker() function can be given an optional callback function which receives, asynchronously, an array of the markers which have been created. For example:

UDI.createEventMarker([

{date: data.barData.date[i], color: "red", icon: "f0ab", text: "My marker 1"},

{date: data.barData.date[i], color: "green", icon: "f0aa", text: "My marker 2"}

], function (asyncResponse) {

// The IDs of the two markers will be contained in asyncResponse.markerId[]

UDI.storeMyUpMarker = asyncResponse.markerId[0];

UDI.storeMyDownMarker = asyncResponse.markerId[0];

});

When you have obtained the IDs using the asynchronous callback, you can then remove an individual marker by passing its ID to UDI.removeEventMarker().

5.3Creating bar highlights

Bar highlights change the colours of specific bars on the chart, in order to highlight them in some way.

The definition of a bar highlight simply has two properties:

Property

Description

date

Date for the highlight, as a number of milliseconds. Does not have to match the start time of a bar. Will change the bar which contains the specified time.

color

Color for the bar, as an HTML colour code such as "red" or "#FF0000" or "rgba(255,0,0,0.5)"

You create bar highlights by passing an individual definition, or an array of definitions, to UDI.createBarHighlight (). If you are creating several highlights at once, it is far more efficient to pass an array than to make multiple calls to UDI.createBarHighlight (). For example:

UDI.createBarHighlight([

{date: data.barData.date[i - 1], color: "red"},

{date: data.barData.date[i - 2], color: "green"}

]);

You can remove all your highlights from the chart using UDI.removeAllBarHighlights().

Individual highlights can be removed by passing a date, or an array of dates, to UDI.removeBarHighlight(). For example:

UDI.removeBarHighlight([data.barData.date[i - 1], data.barData.date[i - 2]]);

6.1Collecting external data

Your UDI can collect external data, such as an economic calendar or trading signals, and then draw that data on the chart or incorporate it into the output which your indicator calculates.

Like any Javascript in a web browser, your UDI can use XMLHttpRequest (or web sockets).

However, as always, XMLHttpRequest is subject to browser security: you can only make a cross-origin call to a remote server if that server permits such a call by setting the CORS headers Access-Control-Allow-Origin and Access-Control-Allow-Credentials on its response.

If you are trying to use a third-party service which is not CORS-compatible then you need a proxy in between your UDI and the third-party server. For example, you cannot request the Forex Factory calendar at https://cdn-nfs.forexfactory.net/ff_calendar_thisweek.xml directly from browser Javascript, because the Forex Factory page does not set CORS headers.

If you cannot arrange a proxy of your own, FX Blue provides a shared (and free) proxy service. You need to request white-listing of the URL which you want to connect to, by emailing the details to support@fxblue.com. If accepted, we will send back an ID such as 6c7e287b71544dd2a739ac94659f0324. You can then use that ID in place of the actual target URL, as follows:

// White-listed ID for your URL, provided to you by FX Blue

var strWhitelistId = "6c7e287b71544dd2a739ac94659f0324";

// Make proxy request, with callback to onMyDataLoad() when loaded

UDI.proxyRequest(strWhitelistId, onMyDataLoad);

function onMyDataLoad()

{

// Read XMLHttpRequest response in this.responseText

}

The FX Blue proxy is a shared service which caches data in order to reduce load and to prevent abuse. When we white-list your URL we will notify you of its caching period, such as 60 seconds. If you make more than one request within each 60 second period you will get back the previous cached data rather than a new request. You can test whether you have received cached data by inspecting the X-FromCache header of the response:

function onMyDataLoad()

{

// Will return either 0 or 1

var isCached = this.getResponseHeader("X-FromCache");

}

6.2Converting dates to chart time

The dates used on the chart depend on the time zone, which is user-configurable. For example, the default setting is GMT+2 with daylight savings on the USA schedule (so that fx markets always open at midnight on Monday).

The chart's time zone is provided via the context data which is available via the data parameter for all functions. For example:

// Get base offset in minutes, and daylight savings mode

var offsetMinutes = data.context.instrument.tz.offsetMinutes;

var dstMode = data.context.instrument.tz.dstMode;

You can convert from GMT to the chart time zone using UDI.convertGMTToUserDate(). This returns an adjusted date as a number of milliseconds, which you can then convert to a Date() object if necessary. For example:

// Convert current time (from computer clock) to chart time

var offsetMinutes = data.context.instrument.tz.offsetMinutes;

var dstMode = data.context.instrument.tz.dstMode;

var now = new Date();

var adjustedDate = UDI.convertGMTToUserDate(now, offsetMinutes, dstMode);

console.log("Equivalent chart date to current time: " + (new Date(adjustedDate)));

You can convert in the opposite direction, from chart time to GMT, using UDI.convertUserDateToGMT(). For example:

var offsetMinutes = data.context.instrument.tz.offsetMinutes;

var dstMode = data.context.instrument.tz.dstMode;

var latestChartTime = data.barData.date[data.valueCount - 1];

var adjustedDate = UDI.convertUserDateToGMT(latestChartTime, offsetMinutes, dstMode);

console.log("Equivalent local date to latest chart time: " + (new Date(adjustedDate)));

 
HomeTerms & ConditionsPrivacyCookiesFAQ