It's easy to write your own indicators for Figaro 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.
When a UDI is added to a chart, it can optionally also be given access to the Figaro scripting framework. This lets it see any framework data such as the order list, and lets it carry out actions such as placing trades. In Figaro's terminology, this turns the UDI into a UDIX.
Like anything with framework access, a UDIX can be "not just an indicator". The ability to place trades means that a UDIX can implement a trading algo, and using the indicator features to draw its activity on the chart.
This guide only covers the core indicator functionality of drawing on the trading chart. The scripting framework is described separately:
There are several example UDI files. You can download them from the following URL:
https://www.fxblue.com/figaro/udi/figaro-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.
The code in the example files and this guide uses one convention, entirely optional. When the code wants to store a value for later use, across different calls to UDI.onCalculate(), it does so by storing the value in the
UDI.$myFastMA = FXB.ta.CreateMovingAverage(maType, {period: fastPeriod});
The $ has no special meaning in Javascript. The example code does this for two reasons: (a) to make clear what is a built-in function or property of the
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
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).
The UDI is initialised once, when it is loaded. The
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()
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.
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.
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.
All dates are represented as a number of milliseconds, not as Javascript date objects.
The time zone of a chart is user-configurable. The dates you receive, in data.dates or data.barData.date, are the user's chart time, not UTC.
Similarly, any dates which you need to pass back to the chart, such as co-ordinates for creating a drawing or an event marker, also need to be numbers of milliseconds representing chart time rather than UTC.
If you need to convert the chart times to UTC (or another time zone), then you can use the helper functions described below.
A UDI must have a
An example of the
UDI.onInit = function(data)
{
return {
// Display-name for the indicator
caption: "My SMA",
// Specifies that the indicator 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}
]
};
};
The return value from
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) |
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. | |
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 ( |
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) |
The
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.
Your
Each plot in the array must have a
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 | 3 | Floating histogram from A to B, rather than 0 to B, and/or a coloured histogram. Uses three output buffers. The first two are the A and B values for the histogram. The third buffer contains colours, which can be left as null to use the default colour specified for the plot. |
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
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 |
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 |
The
There is one special field which fundamentally affects the type and behaviour of your UDI. If your indicator has a
The
settingsFields: [
{id: "Source"}
…
]
All other fields in the
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 FXB.ta library |
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, |
The
The same context data is also passed to all other UDI functions, including UDI.onCalculate().
The properties in
Property | Description |
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 |
timezone | Object describing the user-selected time zone for the chart, with the following properties. |
timezone.offset | Base number of minutes ahead of UTC (e.g. 120 for UTC+2) |
timezone.dstMode | Daylight savings schedule: 0 = none; 1 = USA; 2 = Europe; 3 = Australia |
isUDIX | Whether the UDI has been loaded with Framework access |
Your UDI must have a
A simple example of the
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 = 0; i < data.valueCount - period; i++) {
var sum = 0;
for (var j = 0; j < period; j++) sum += data.valueData[i + j];
output.values[0][i] = sum / period;
}
};
The
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
If your UDI does not have a
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
Within
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"]; // Can also use data.parameters.period
…
}
As explained above, your UDI receives either a single array of input values, in
The
The
The
The input data (and also your output data) is indexed with the newest, current value item in [0], and the oldest item in [length-1].
Note: date/time values, in either
To make calculations faster and more efficient, the chart tells your UDI whether only the current bar has changed since the last calculation, in the
Typically, if
The example code contains two versions of an SMA indicator: one which always re-calculates all values, and a more efficient one which uses
You put the results of your UDI's calculations into the
The
plots: [
{type: "channel", …}, // Requires 2 output series
{type: "line", …}, // Requires 1 output series
{type: "candles", …} // Requires 4 output series
]
In this example,
Each sub-array inside
If
UDI.onCalculate = function(data, output)
{
// Get the user-selected value of the "period" parameter
var period = data.parameters["period"];
if (!data.currentBarUpdateOnly) {
// Need to do a full recalculation (repeating the example above)
for (var i = 0; i < data.valueCount - period; i++) {
var sum = 0;
for (var j = 0; j < period; j++) sum += data.valueData[i + j];
output.values[0][i] = sum / period;
}
} else {
// Only the current bar is changing, and we only need to recalculate
// the current indicator value. (Note: the 02-udi-efficient-sma.js example
// demonstrates a more efficient version of this which avoids having
// to use a loop.)
var sum = 0;
for (var j = 0; j < period; j++) sum += data.valueData[j];
output.values[0][0] = sum / period;
}
};
Although the framework pre-creates and pre-populates the arrays inside
// Create and store a moving average calculation. (The $ has no special meaning.
// It just makes clear what is a built-in UDI function/property and what is a private
// addition by our code.)
UDI.$myMA = new FXB.ta.EMA({period: 20});
UDI.$myMA.LoadData(data.valueData);
You can then copy the moving average results into a system-provided output array such as [1]:
// Copy each EMA value into output buffer #1
for (var i = 0; i < data.valueCount; i++) {
output.values[1][i] = UDI.$myMA.GetValue(i);
}
However, it is both simpler and faster just to take the array from the technical analysis calculation and use that to replace the pre-prepared buffer:
output.values[1] = UDI.$myMA.GetValueArray(); // Replaces the array, rather than writing into it
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
Many indicators are based on other technical analysis calculations such as moving averages, standard deviation, or values such as ATR or RSI. (For example, an "Awesome Oscillator" is simply a 5-period moving average minus a 34-period moving average.)
UDIs have access to a library,
The
https://www.fxblue.com/figaro/scripting/help#technical-analysis
One of the features of the
In the settings for your UDI, you include a field with
// Read user-controlled maType and period fields from settings
var maType = data.parameters["maType"];
var period = data.parameters["period"];
// Create a moving average calculation if we don't already have one
if (!UDI.$myMA) UDI.$myMA = FXB.ta.CreateMovingAverage(maType, {period: period});
// Load the data into the calculation. If data.currentBarUpdateOnly is set then
// this automatically becomes an efficient fast update of just the current value.
UDI.$myMA.LoadData(data);
You can pass the data parameter from
UDI.onCalculate = function(data, output) {
// Create an ATR calculation if we don't already have one
if (!UDI.$myATR) UDI.$myATR = new FXB.ta.ATR({period: 14});
// Pass the data into the ATR calculation.
// If data.
UDI.$myATR.LoadData(data);
};
The
UDI.onCalculate = function(data, output) {
// If we haven't already done so in a previous call, create a candle store
if (!UDI.$cs) UDI.$cs = new FXB.CandleStore();
// Load data into the candle store. This will efficiently update just the current
// candle if data.currentBarUpdateOnly = true
UDI.$cs.LoadCandles(data);
// Create an ATR calculation if we haven't already done so
if (!UDI.$atr) UDI.$atr = new FXB.ta.ATR({period: 20});
// Is this a full (re-)calculation?
if (!data.currentBarUpdateOnly) {
// Full recalculation
// Load the entire candle data from the store into the ATR.
UDI.$atr.LoadData(UDI.$cs.GetCandleArray());
} else {
// Only the current bar is changing.
// Update the ATR just with the current candle from the store.
UDI.$atr.UpdateCurrent(UDI.$cs.GetCandle(0));
}
…
If there is a change to the chart, such as a switch of timeframe, or a change by the user to 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()
For example:
UDI.onContextChange = function(data)
{
// Get current chart timeframe
var tf = data.context.instrument.timeframe;
};
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"];
…
}
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
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
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.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.
You create drawings using
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
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([
{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
A drawing can be moved by passing an object containing the ID and a new array of points to
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
// Change drawing text and its colour
UDI.changeDrawing({
drawingId: …,
text: "New text",
style: {text: {color: "red"}}
});
Drawings can have the following properties. Each drawing must have a
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: 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: |
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 |
style.fill | Can contain a |
style.text | Can contain the properties |
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 |
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 |
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 |
diagonal | 2 | Like |
channel | 3 | Filled channel extending to the edge of the chart |
freehandChannel | 4 | In effect, a filled quadrilateral with four price/time co-ordinates |
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) |
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 |
wantClick | Sends notifications when a marker is clicked on (and makes the |
You create markers by passing an individual marker definition, or an array of definitions, to
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
You can also remove individual markers (rather than removing everything and re-creating different ones). The
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
If you specify
UDI.onEventMarkerClick = function(marker)
{
// marker parameter contains the definition of the marker which has been clicked on
};
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([
{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
Individual highlights can be removed by passing a date, or an array of dates, to
UDI.removeBarHighlight([data.barData.date[i - 1], data.barData.date[i - 2]]);
A UDIX has access to the full Figaro framework, and can use any of the notification mechanisms such as creating dialogs.
Without framework access, a UDI only has two ways of displaying notifications to the user:
Creating toast
Changing the panel background colour
A UDI can create "toast": a short-lived pop-up message to the user, displayed at the edge of the screen. You create toast using the
UDI.createToast({
title: "Band breakout",
text: "Price is above trend band"
}, function(toastDef) {
// User has clicked on the toast
// toastDef parameter is the definition passed to createToast
});
The available options for toast are described in full in the Framework reference, but the key values are as follows:
Property | Description |
text | Body text of the notification |
title | Title for the notification. (Not compulsory.) |
icon | Hexadecimal code of an icon in the FontAwesome set, such as |
lifespan | Lifespan of the toast in seconds, overriding the app's default |
A UDI can use
// On some condition which evaluates as an alert…
UDI.setBackground("red");
The parameter for
To remove a background, and revert to the app's default, you simply pass an empty string:
// When the alert is cleared…
UDI.setBackground("");
Your UDI can add HTML to its chart, either just as decoration or to provide additional functionality. This mechanism is very flexible, but it can become complicated if you want the UDI to interact with the HTML.
The HTML is added to the chart in a sandboxed iframe. It has no access to the rest of the web page, and it has no direct access to any framework or indicator features. However, the HTML can send messages to the UDI, and the UDI can send messages to the HTML. Therefore, for example, the HTML can have something like a button which sends a message to the UDI, and the UDI then carries out some action. Similarly, the HTML can have input fields. You can post a message to the UDI containing the value of the fields, and then use this data in the UDI to change its behaviour.
To display only HTML, with no standard indicator drawing, you simply return an empty
You add HTML to the chart using
UDI.createHTML({
foreground: true,
html: "<html><body>Hello!</body></html>"
});
A second call to
The options which are passed to
Property | Description |
html | Text of the HTML to add; a complete document with |
url | URL of a web page to load. Standard browser security requires that this is hosted at a secure https:// address. |
foreground | Controls whether the HTML is displayed in the background, behind the indicator drawing, or in the foreground. If your HTML includes elements for the user to interact with, such as buttons or fields, then you must set |
style | Lets you set CSS style options on the iframe container, particularly the position. For example: style: {opacity: 0.5, pointer-events: none} You cannot set the |
By default, the iframe and your HTML occupy the whole of your indicator's panel:
UDI.createHTML({
style: {
width: "200px",
height: "50px",
bottom: "0px",
right: "0px",
},
…
});
Your HTML can send messages to your UDI using
window.parent.postMessage("Message to UDI", "*");
This is received by the UDI in a
UDI.onHTMLMessage = function(msg) {
// msg == "Message to UDI"
};
Common uses include notifying the UDI of an action such as button click, or a change of value in some input field in the HTML.
Your UDI can send messages to its HTML. It is much more efficient to update the HTML by sending it a message, and having script in the HTML which changes the contents, rather than loading complete replacement HTML using a second call to
The UDI sends a message to the HTML using
UDI.sendHTMLMessage("Some instruction to the HTML");
Script in the HTML listens for messages in the standard way, using a message listener on the window. For example:
window.addEventListener("message", function(event) {
// The message from the UDI will be in event.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,
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.
In addition, Figaro is hosted at an https:// address. Standard browser security will prevent you making any sort of connection to a non-secure http:// address.
The dates used on the chart depend on the user-configurable time zone. 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
// Get base offset in minutes, and daylight savings mode
var offsetMinutes = data.context.timezone.offset;
var dstMode = data.context.timezone.dstMode;
The
You can convert from the user's chart time to UTC using
// Convert a value from data.barData.date[] to UTC
var currentBarDate = data.barData.date[0];
var currentAsUTC = UDI.convertChartDateToUTC(currentBarDate);
// Convert to Javascript date object, and output
var dateobj = new Date(currentAsUTC);
console.log("As UTC, bar date is " + dateobj.toISOString());
You can convert in the opposite direction, from UTC to chart time, using
// Get current date from computer clock (as UTC milliseconds)
var currentDateFromComputerClock = (new Date()).valueOf();
// Convert to chart time
var currentAsChartTime = UDI.convertUTCToChartDate(currentDateFromComputerClock);
You can also convert UTC to and from other time zones. For example, you can convert UTC to US east coast time, or vice versa.
To do this conversion, you provide a
// Base of UTC-5, moving to UTC-4 on the USA daylight savings schedule
{
offset: -300,
dstMode: UDI.DST.USA // = 1
}
You can then convert from UTC to a different zone using
var usec = UDI.convertUTCToZone(someDate, {offset: -300, dstMode: 1});
Similarly, you can convert from another zone to UTC using
// Convert the example above back to UTC
var utc = UDI.convertZoneToUTC(usec, {offset: -300, dstMode: 1});