Figaro - scripting reference

1Framework fundamentals

You can use the Figaro framework to build trading algos, indicators for charts, and whole new tools within the Figaro platform.

The Figaro framework provides three types of software development:

Widgets. All the built-in tools in Figaro - charts, quote board, trade list etc - are widgets, and any widgets which you create have access to all the same features as built-in widgets.

Scripts. You can create and save scripts using the Run Script widget in Figaro. Scripts can be given icons in the platform's banner, and can be set to start automatically when the platform is loaded. (Scripts can also be given a context, so that they are available in specific places within the Figaro platform.)

User-defined indicators, for charts.

User-defined indicators subdivide into two types:

Standard indicators (UDI), which only have access to the chart on which they run.

Enhanced indicators (UDIX) which also have access to the Figaro framework described here. They can see data such as the trade list, and they can carry out actions such as placing trades.

Creating indicators is described elsewhere, at https://www.fxblue.com/figaro/udi/help. A UDIX is simply an indicator which also has a Framework object, in addition to the indicator features which are described separately. You choose whether to give a UDI access to the Framework, turning it into a UDIX, when you add it to a chart.

In terms of the web browser:

Widgets are iframes (sandboxed). A widget is simply an HTML document which integrates with Figaro by including the framework script and CSS.

Scripts and UDIXes are web workers. They have no user interface of their own, but they can create dialogs and interact with the platform.

Figaro is a web-based platform, and therefore the programming language for creating widgets, scripts, and UDIXes is simply Javascript. You can also write your code in any language such as TypeScript or CoffeeScript which compiles to Javascript.

You can use any development environment, from Notepad to Visual Studio or Eclipse. You can paste HTML/Javascript into the Run Script and External Widget windows in Figaro. You don't have to write your code inside the Figaro platform.

Note: in addition to the facilities described here for extending the Figaro platform, it is also possible to use the Excel RTD widget to write code outside the Figaro platform, in any language of your choice such as Node.js, C#, or Python, communicating with the web browser using HTTP or websockets. It is even possible to download data directly into Excel without any programming. For more information, see the document about the Excel RTD widget:

https://www.fxblue.com/figaro/excel-rtd/help

1.1Examples

The Figaro platform contains examples of scripts, of a widget, and of a UDI (but not specifically a UDIX). You can use these as a starting point in addition to this guide.

The Run Script widget lets users without programming experience nevertheless create scripts from common actions such as placing trades, and then save those scripts for future use, make them accessible as icons in the software's banner etc.

The screen for adding a UDI to a trading chart includes an example UDI.

The External Widget contains a simple HTML example of a widget which displays a configurable market, current prices for that selected market, and a button for opening the platform's standard deal ticket.

The full code of the platform's built-in Market Ranges widget is available, as an example, at the following URL. You can host these files on a server and then load them using the External Widget:

https://www.fxblue.com/figaro/scripting/example-market-ranges-widget.zip

1.2Framework access

All the features of the Figaro framework are provided via a single base object. This is accessed slightly differently in widgets versus scripts and UDIXes.

In a script or UDIX, there is automatically a Framework object which gives you access to Figaro. For example: Framework.SendOrder()

In a widget, you need to do four things in the widget's web page:

Include the framework Javascript and CSS

Create an instance of the framework

Wait for the framework to become available

Don't reload!

1.2.1Linking to the framework's Javascript and CSS in a widget

Scripts and UDIXes have automatic access to the framework. A widget needs to include the following references in its HTML (usually in the <head>):

<script src="https://figaro.fxbluelabs.com/scripts/widget-js"></script>

<link rel="stylesheet" type="text/css" href="https://figaro.fxbluelabs.com/css/widget-css"/>

1.2.2Creating an instance of the framework in a widget

Scripts and UDIXes automatically have a Framework object. A widget needs to declare a new instance of the framework, in its page's Javascript, rather than having one provided for you automatically:

<script>

// Create an instance of the framework

var Framework = new FXB.Framework();

1.2.2.1Alternative, class-based framework access in widgets

For consistency across widgets, scripts, and UDIXes, this document refers to widgets having access to the framework by creating a new FXB.Framework() object: var Framework = new FXB.Framework();

However, there is also class-based syntax which you can use if you prefer. For example:

// Define a class for your widget which inherits from FXB.Framework

class MyWidget extends FXB.Framework

{

OnLoad()

{

}

etc.

}

// Create an instance of the class to service the web page

var widget = new MyWidget();

Within the class members, you can then refer to framework properties and methods using this. For example, this.SendOrder() instead of Framework.SendOrder().

1.2.3Waiting for the framework to become available in a widget

As described in more detail below, widgets must not use any features in the framework until it signals that it is ready by calling the OnLoad() function in your Framework object. Scripts and UDIXes can start using their Framework object as soon as they start running.

1.2.4Reloading or navigation in widgets

A widget must not navigate to another page, or reload itself. This will break its communication with the framework.

For example, if your widget submits a form, it must do so using something like Ajax to send the data, rather than doing a full page POST which causes a reload or navigation of your widget page.

1.3Browser environment

Widgets, scripts, and UDIXes have normal access to the browser's Javascript environment. For example, they can use setInterval() or setTimeout() to carry out delayed actions, and they can use XMLHttpRequest to collect external data (though see below).

Scripts and UDIXes run as web workers. Therefore, there is no web page for them to interact with, and web workers also don't have access to some browser features such as localStorage. But, because they are web workers, scripts and UDIXes can use importScripts() to include external code - subject to standard browser security as described below.

Widgets, scripts, and UDIXes can create web workers to offload complex tasks (in the case of scripts and UDIXes, web workers creating other web workers). In other words, the browser environment makes it possible to have fully multi-threaded processing, with time-consuming tasks done in the background.

Nothing in Figaro is allowed (or able) to use browser modals such as alert() and confirm(). Web workers don't have access to these features, and widgets are prevented from using them by the sandboxing of their iframe. The framework provides alternatives such as Ask() which you can use instead of browser modals - but please note that these are asynchronous.

You can use standard features of your browser to inspect and debug your code. For example, you can insert the debugger; keyword in your Javascript to pause your code in the browser's Developer Console.

1.4Asynchronous operation

Most operations in the framework are asynchronous. The most obvious example is placing a trade: you send a trading request and you provide a callback function (or "completion handler", or whatever term you prefer) which the framework later calls when the action completes. For example:

Framework.SendOrder(orderDefinition, function(MsgResult) {

// Function called asynchronously by the framework when the order succeeds or fails

});

Another example would be asking the user a question by displaying the framework's standard Ask() dialog:

Framework.Ask("Are you sure?", function (Msg) {

// Called asynchronously by the framework

});

This example is in fact slightly more complicated than it looks. Your asynchronous callback from Ask() gets called twice, not once. The second call is the result; the user's response. The first call is an update which gives you the ID of the dialog, giving you the ability to cancel the dialog yourself - for example, if the user takes too long to decide.

And some types of asynchronous callback can get called multiple times. For example, if you issue a request for candle data then - unless you specifically say that you don't want streaming - your callback function will get called multiple times: with an initial batch of candles, and then with each update as the price changes and the current candle is modified or a new candle is formed. For example:

Framework.RequestCandles(candleDef, function(Msg) {

// Called repeatedly, until you terminate it with TerminateCandleRequest()

});

1.5Javascript Promises

For every function in the framework which uses an asynchronous callback, there are two versions: one which issues a Promise, and one which takes a more traditional callback function. For example, the code to display a dialog to select an instrument can be written in either of the following ways:

// Traditional version

Framework.SelectInstrument(function(instrumentId) {

// Called asynchronously by the framework when the user selects a market, or cancels

});

// Version using Promise (and optional arrow function)

Framework.pSelectInstrument().then( (instrumentId) => {

// Called asynchronously by the framework when the user selects a market, or cancels

});

The framework uses a simple naming convention where the "standard" version of the function is something like SelectInstrument() and the Promise version has a p prefix such as pSelectInstrument().

Promises in the framework always settle by fulfilling, never by rejecting. For example, a trading request settles by fulfilling regardless of whether it succeeded or failed, with a parameter describing the success or failure result.

There can be subtle differences between the standard and Promise versions of functions. For example - as described in the previous section - RequestCandles() issues multiple, repeated, streaming callbacks as price changes modify the current candle or start a new one. But a Promise can only settle once; it can't have multiple callbacks. Therefore, pRequestCandles() is always treated as a static, non-streaming request.

1.5.1Async functions and await syntax

The existence of Promises also means that it's possible to use Javascript await syntax, instead of the callback functions - or then() - which are used in this document, and generated by the Run Script widget.

Scripts are automatically run inside an async function. Therefore, it's possible to write code such as the following:

var result = await Framework.pSendOrder({"tradingAction": FXB.OrderTypes.FLATTEN});

Framework.EndScript();

It's not necessary to declare an async function and then pass execution into that:

async function MyAsyncFunction()

{

var result = await Framework.pSendOrder({"tradingAction": FXB.OrderTypes.FLATTEN});

Framework.EndScript();

}

MyAsyncFunction();

1.6.1Script lifecycle

Scripts run forever until they call Framework.EndScript(), or the user shuts them down. They don't simply run "from top to bottom" of their code and then automatically terminate. This is because most operations in the framework are asynchronous, and the script may be waiting for an action to complete. The script needs to tell the framework when it has finished.

Consider the following example:

Framework.Ask("Are you sure?", function (Msg) {

// Called asynchronously by the framework

});

Framework.EndScript();

This won't work. The script will create the ask dialog, but then immediately terminate (also destroying the dialog) before it has got a response. This script should instead be written as follows:

Framework.Ask("Are you sure?", function (Msg) {

// Called asynchronously by the framework

// … do something based on the user's response

// And, finally, tell the framework that we're done

Framework.EndScript();

});

// When execution gets here, we're waiting for the user's response to the Ask().

// We don't want to terminate.

1.6.2UDIX lifecycle

The lifecycle of a UDIX is simply tied to the chart on which it is running. A UDIX cannot remove itself from its trading chart; only the user can do this.

1.6.3Widget lifecycle

Widget lifecycle is a little more complicated because a widget is a web page and there are two separate events: loading of the HTML page, and loading of the framework.

Widgets should (normally) have an OnLoad() function. This is called by the framework when it has finished loading, and your code should not try to use any part of the framework - retrieving data, carrying out actions such as placing trades - before the framework says it is ready by calling your OnLoad().

For example, the following code is not safe and won't work:

// Create an instance of the framework

var Framework = new FXB.Framework();

// OnLoad handler

Framework.OnLoad = function() { … Called asynchronously. Framework is now ready };

// **WON'T WORK**. This is executed immediately, before the framework has

// had a chance to set itself up, and before it has done a callback into OnLoad().

Framework.Ask(…);

If the user has added your widget into the Figaro page container, then the widget runs until the user switches to another page. You can't "close" your widget in this context (which would leave a blank space in the page).

If the user has loaded your widget as a dialog or floating panel, then you can choose to close the window. The framework's CancelDialog() function will close the window (and will have no effect if your widget has been added into the page container).

1.6.3.1Permanent widgets

By default, widgets in the page container are unloaded when the user navigates to a different page of the app's configuration. (They can save state and later reload it in order to provide a seamless transition between pages.)

However, it is possible for a widget to keep running after a change of the active page. Such widgets are also always reloaded on restart, even if they are not on the active page.

This mechanism is designed for trading algos (and for similar widgets such as the Excel RTD) which need to keep providing services even if they are not on the active page. It should only be used where necessary. Asking to run permanently, rather than saving state and reloading as the user switches between pages, can affect the performance of the whole Figaro app.

A widget specifies that it needs to run permanently by returning mustRemain:truein its state. The widget must implement OnGetState(). For example:

Framework.OnGetState = function()

{

return {

// Keeps the widget open, and reloads it immediately on app startup even if

// not on the active page

mustRemain: true,

// Any other state which the widget wants to store/report

};

};

1.7External data and hosting

Widgets, scripts, and UDIXes can use the standard features of the browser environment to collect external data. For example, you can use XMLHttpRequest to collect data from remote servers, or make websocket connections.

Scripts and UDIXes are web workers, and therefore they can use importScripts() to load external code. For example, you can host the Javascript for a script externally, and then run it within Figaro using a single line of code:

importScripts("https://www.myserver.com/myscript.js");

However, all use of external resources is subject to standard browser security restrictions (which it's not possible for Figaro to override, even if wanted to):

For XMLHttpRequest, the remote server must put CORS headers on its response. If it doesn't, the browser will block the response.

All requests must be made over secure connections (https:// or wss://). Figaro is hosted at an https:// address, and standard browser security prevents anything inside it making calls to insecure addresses. You cannot link to resources at http:// addresses or request data from http:// addresses.

Similarly, if you host a widget externally rather than pasting its HTML into the External Widget, then the URL for your widget must be https://, not http:// (and you obviously must not set any X-Frame-Options header on your page).

1.8Account capabilities

The features which the framework can provide depend on the capabilities of the trading back-end/account which Figaro is logged into. For example, some accounts support trailing stops whereas others do not. Some accounts can only provide the historic trades on the account, not the historic credit movements.

Perhaps most importantly, some accounts support "hedging" - a buy order and then a sell order for the same market create two separate open trades in that market rather than cancelling each other out - whereas other accounts are "netting" and only ever report a single net position (or no position) in a market.

You can inspect what the back-end system supports using the features information in the Framework.Account.

1.9Data representations and standards

The framework uses some standard representations of data:

The framework makes widespread use of a standard dictionary/hashtable class, FXB.utils.Dictionary().

All dates/times are UTC and expressed as a number of milliseconds since January 1st 1970. The framework provides functions for converting between UTC and different time zones.

Time zones are represented as a structure consisting of an offset, in minutes added to UTC, and a dstMode which defines a daylight-savings schedule: 0 = none, 1 = US schedule, 2 = European schedule, 3 = Australian schedule. For example, New York time is defined as {offset: -300, dstMode: 1}.

Regardless of the back-end system which it is logged into, the framework represents all trading volumes, such as the volume of an FXB.Order, as cash/units, such as 10,000, rather than as a figure in lots such as 0.10. You can convert between cash/units and lots using the contractSize of an FXB.Instrument.

All candle data in Figaro is based on bid prices. Volume - where available from the broker's back-end system - may be either real traded volume or tick pseudo-volume (i.e. MT4-style volume where the "volume" is a count of the number of price changes in the period).

All chart timeframes for candle data, such as M1 or H4, are expressed as a number of seconds, e.g. 3600 for H1. Figaro uses 604800 for weekly (W1) and 2592000 for monthly (MN1).

1.10Future additions to the framework

There will be future additions to the framework. The web version of Figaro will automatically update, but the mobile/tablet app will require user intervention.

The framework has a RELEASE property, currently 1. This will be incremented in future versions, and this guide will note where functions/features require a compatibility level greater than 1.

If your code uses new features of the framework it may need to check the RELEASE level and warn if the mobile/tablet app has not been updated - that the feature you are trying to use is not available.

1.10.1Undocumented members of the framework

You may notice - for example, through the browser's Developer Console - that there are objects and properties in the Framework which are not described in this guide. These are not safe to use. Backward compatibility and consistent behaviour are only guaranteed for things which are covered in this guide.

1.11Questions about the framework

You can send queries about this framework to support@fxbluelabs.com.

However, we will strongly prioritise enquiries which appear to relate to a bug in Figaro, or where this documentation is unclear or incomplete. If you ask us something which we think is clearly answered by this document already, you will probably just receive a one-line reply pointing you to the relevant section of this document - and probably after quite a long delay.

We cannot provide support on general Javascript or web development queries which are not specifically related to the Figaro framework. For example, if you ask us "how do I get the EUR/USD price from the framework?", then we'll send you a one-line response pointing you to the section below. But if you ask us something which is about web development generally rather than about the framework specifically, such as "How do I display a table of prices from the framework in a web page?", we may not respond at all. You need to be proficient in Javascript to use the framework (and in HTML/CSS to build widgets). If you are not, the internet is full of excellent learning resources to help you.

2.1Framework collections

All the collections in the framework are Dictionary objects, though some of them modify or extend the standard dictionary functions and behaviour.

You can listen for changes to prices or in the order list using an OnMessage() handler. You need to make explicit requests for prices which you are interested in. Prices in the Instruments list do not automatically update.

Collection

Description

Instruments

List of all available markets for the current trading account.

Orders

List of all open trades and pending orders (potentially multiple open trades for each market unless the account is netting/non-hedging)

Positions

List of open trades aggregated into a single long/short position for each market

TradeHistory

List of historic trades and credit movements on the account. This collection is empty by default and must be explicitly requested using Framework.RequestAccountHistory()

2.2Framework objects

The Framework object contains the following sub-objects describing the current Figaro environment:

Object

Description

Account

The trading account which Figaro is currently logged into. Availability of some features in the framework depends on the functionality provided by the broker account, and reported in the account's features.

Environment

Contains a persistent identifier for the client's browser/app

Translation

Contains functions for translating variables into the user's currently selected language

Theme

Contains information about the user's selected graphical theme for the software (e.g. light, dark)

There is also a class which is widely used as a response from actions such as trading requests and dialogs:

Object

Description

FXB.Result

Standard response/error object

2.3Framework properties

The framework provides the following properties. All of these are read-only.

Property

Description

RELEASE

See introduction

isReady

Indicates whether the framework is ready for use. Always immediately true in scripts and UDIXes. Not immediately true in widgets.

id

Unique ID for this instance of the widget/script/UDIX. This changes each time your code is loaded. For widgets loaded into the Figaro page container, it is not a persistent ID across reloads of the widget's page, or across the user logging out and back in. It changes on each load of the widget.

type

Not applicable to scripts and UDIXes. For widgets, a unique category allocated to your widget. Can be used for displaying a widget as its own dialog.

_parentWidget

Not applicable to scripts and UDIXes. For widgets, the ID of the widget's parent/creator

inWindow

Not applicable to scripts and UDIXes. Boolean indicating whether a widget has been loaded into a pop-up browser window rather than the main window.

inPageContainer

Not applicable to scripts and UDIXes. Boolean indicating whether a widget has been loaded into the main Figaro page container, or whether it is a temporary dialog or floating panel.

isDialog

Not applicable to scripts and UDIXes. Boolean indicating whether a widget is being displayed as a dialog or floating panel rather than being persistently loaded into the Figaro page container.

fullScreen

Not applicable to scripts and UDIXes. Boolean indicating whether a widget is being displayed in full-screen mode on a small device such as mobile phone.

privateSettings

See the section about settings and state storage

categorySettings

See the section about settings and state storage

context

Any context of market and/or timeframe selection, for a widget which is loaded in the Figaro page container.

2.4Framework.Instruments collection

The framework provides a list of all the markets available on the current trading account. The Framework.Instruments collection is a Dictionary of FXB.Instrument objects keyed by their instrumentId values. For example, you can get a specific market using Instruments.get() or iterate through all markets using Instruments.forEach().

2.4.1Instruments.get(), Instruments.getOrBlank(), and standardisation

Figaro is designed to work with FX/CFD platforms, and they have almost no standardisation of market names and IDs across different brokers, or even between accounts with the same broker. Figaro tries to impose some standardisation of its own: for example, it will try always to give the euro-dollar FX pair the instrumentId "EUR/USD". But there are limits to what is possible.

As a result, Instruments.get() is not a standard dictionary getter. Its behaviour is overridden. If you do a get() for an instrumentId which does not exist, then get() will try a number of other possible matches:

Does your key match an instrument's .caption?

For fx markets, does your key match the base currency + quote currency? For example, if you pass in "EURUSD" (and the instrumentId of the market is "EUR/USD", and doesn't match), is there an instrument where the .baseCurrency is EUR and the .quoteCurrency is USD?

Does your key match any common known aliases? For example, is your key "GOLD" which is known to match against an instrument where the ID is "XAU/USD"?

You also need to bear in mind that a market may exist on one account and not on another, as well as having different IDs. As a result, you need to consider the following possibility:

Your widget/script stores the user's current settings, including a choice of market

The user logs in to a different broker account and your widget/script is re-loaded

The market in your saved settings does not exist on the new account

As a result, it is more common to use getOrBlank() than get(). If the key you ask for does not exist in the current list of markets, this returns a dummy FXB.Instrument with the following properties:

instrumentId: your key for the missing market

caption set to "?"

Addition of missing:true

This potentially stops your code from breaking when the user logs in to an account where your saved settings refer to a market which doesn't exist. Without needing to do an explicit check for missing markets, your code will typically continue relatively gracefully - for example, displaying ? as the market's caption and zero prices.

2.4.2Instruments.GetTradeableInstruments()

Helper function which returns an array of all instruments with tradeable:true, sorted by their caption.

2.4.3Instruments.GetInstrumentCategories()

Helper function which returns an array of available instrument categories. It scans the list of markets, and builds a de-duplicated list from each market's categoryId.

2.5FXB.Instrument object

The FXB.Instrument object describes an available market on the account. As well as providing properties which describe the market, it also has various helper functions.

2.5.1FXB.Instrument properties

The properties of the FXB.Instrument are as follows. Names marked with * are derived properties - using Object.defineProperty() in Javascript - and will not get included if you clone the object.

Prices do not automatically update. You need to request prices which you are interested in.

Property

Description

instrumentId

ID of the instrument, potentially the result of standardisation

caption

Display-name of the market

tradeable

Boolean indicating whether the market can be traded, or is included by the broker for information only

categoryId

Market category - forex, metal, index etc

underlyingCurrency

Underlying/profit currency of the market, e.g. USD for the DJIA. For FX pairs AAA/BBB, it is reported as BBB. See note on spread-betting accounts.

isFX

Boolean indicating whether the market is an FX pair

baseCurrency

For FX pairs only, the AAA currency of the AAA/BBB pair

quoteCurrency

For FX pairs only, the BBB currency of the AAA/BBB pair. See note on spread-betting accounts.

digits

Number of digits in the market's price

tickSize

Minimum price movement in the market. Typically 10^-digits, but not necessarily so; a market may have digits=2 but tickSize=0.05

pipSize

For FX pairs, the standard definition of a "pip": 0.01 on JPY and HUF pairs; 0.0001 on everything else. For other asset classes, there is no universal consensus on what constitutes a "pip". The framework will generally report a value of 1.00 on non-fx markets, with some special exceptions for markets such as gold and oil.

contractSize

Market contract size, e.g. typically 100,000 on FX markets

minVolume

Minimum trading volume (as cash/notional, such as 1000, rather than a value in lots, such as 0.01)

maxVolume

Maximum trading volume (again cash/notional, not lots)

incrementVolume

Minimum step in trading volume. Almost always the same as minVolume.

time

Time of the most recent quote

bid

Latest bid price

ask

Latest ask price

high

High of the current period. Availability depends on account features.

low

Low of the current period. Availability depends on account features.

change

Change amount during the current period. Availability depends on account features.

changePc

Change amount as a percentage. Availability depends on account features.

spread *

Difference between ask and bid (as price amount such as 0.00023)

spreadPips *

Spread in pips, i.e. spread divided by pipSize

midPrice *

Mid price: (ask + bid) / 2

minLot *

Minimum trade volume, in lots: minVolume / contractSize

maxLot *

Maximum trade volume, in lots: maxVolume / contractSize

lotStep *

Minimum trade increment, in lots: incrementVolume / contractSize

tickValue *

Cash value, in the account's deposit currency, of a change in price by the tickSize when trading the contractSize. For example, if trading EUR/USD on a GBP account, with a contractSize of 100K and a tickSize of 0.00001, then tickValue will be the current GBP equivalent of USD 1.00.

2.5.2Market categories

Figaro does its own categorisation of markets (again looking to standardise across different platforms). Every FXB.Instrument is assigned a categoryId from the following FXB.InstrumentCategories list. For example, the euro-dollar FX pair (EUR/USD) will have categoryId=FXB.InstrumentCategories.FX_MAJOR.

FXB.InstrumentCategories.

Description

FX_MAJOR

FX majors: USD crosses with major currencies. EUR/USD, USD/JPY etc

FX_MINOR

FX minors: non-USD crosses between major currencies. EUR/GBP etc.

FX_EXOTIC

Crosses involving non-major currencies.

FOREX

(Present in FXB.InstrumentCategories, but unused)

COMMODITY

Commodity such as oil or gas

EQUITY_INDEX

Equity index such as the DJIA or DAX

EQUITY

Individual equity (can currently be either physical or CFD)

INTEREST_RATE

Interest rates such as US Treasuries

METAL

Metals: gold, silver, palladium etc

CFD

General category for non-FX CFD markets

CRYPTO

Cryptocurrency

OTHER

Anything not categories as any of the above

You can translate these categories into readable text using TranslateInstrumentCategory().

2.5.3Market data on spread-betting accounts

In the FX/CFD world, there are two different types of account:

"Normal", CFD accounts. A trade in EUR/USD is buying or selling a number of euros, and has a profit in US dollars (converted to the account deposit currency, if different).

Spread-bet accounts. All trades are denominated in the account deposit currency, such as GBP or AUD. A trade on EUR/USD is a bet in GBP (or AUD etc) on the EUR/USD price, on a "pound per point" basis. For example, if the EUR/USD price rises by 0.0001 (i.e. 1/100th of a US cent) then the trade has profit of GBP 1.

On a CFD account, the quoteCurrency of an FX pair will be the same as the underlyingCurrency. For an FX pair AAA/BBB, both the quoteCurrency and the underlyingCurrency will be BBB.

On a spread-bet account, the underlyingCurrency of an FX pair will be the account deposit currency, not the BBB of the AAA/BBB pair. All trades are bets denominated in the deposit currency.

2.5.4FXB.Instrument helper functions

The FXB.Instrument provides some helper functions to assist with common conversion and calculation tasks in relation to a market.

FXB.Instrument.

Description

ConvertVolumeToLots(x)

Converts a cash/unit amount x to lots: simply divides by the contract size

ConvertLotsToVolume(x)

Converts a lots amount x to cash/units: simply multiplies by the contract size

RoundPrice(x)

Rounds a price x (or a price differential, such as 0.0020) to the nearest multiple of the tickSize, i.e. closest valid price

GetPriceMovementValue(x, volume)

For a price movement, such as 0.0020, and a volume (in cash/units, not lots), calculates the value of the move in the account's deposit currency. The volume parameter can be omitted, and defaults to the contractSize. For example, for EUR/USD on a GBP account, a price movement of 0.0020 with a volume of 100,000 is worth USD 200, and this function will return the current GBP equivalent of USD 200.

2.6Framework.Orders collection

The Framework.Orders collection is a Dictionary of all the pending/working orders and open trades on the account. The framework uses the same FXB.Order class to represent both pending orders and open trades (and also historic trades and credit movements).

Framework.Orders is then a collection of FXB.Order objects, keyed by their orderId.

To watch for changes in the order list, you can use an OnMessage() handler.

In addition to standard Dictionary functions, the Framework.Orders collection offers, for convenience, the following derived properties:

Property

Description

tradeNetProfit

Current net profit on all open trades - should normally match Account.floatingPL

openTradeCount

Number of open trades

pendingOrderCount

Number of pending orders

In other words, Orders.length (inherited from Dictionary) = Orders.openTradeCount + Orders.pendingOrderCount.

2.7FXB.Order object

The framework uses the FXB.Order object to represent all of the following, because of their similarities:

Pending/working orders

Open trades

Historic closed trades

Historic, cancelled orders

Credit movements on the account (such as deposits and withdrawals)

Some properties within the object are only applicable depending on its context (its orderType). For example, credit movements only have an orderId, reference, orderType, openTime, closeTime (same as openTime), profit, and comment (if the platform supports it). Properties such as instrumentId or volume are obviously inapplicable on a credit movement, and not defined.

Property

Description

orderId

ID of the order (usually but not necessarily numeric)

reference

Client-facing reference number for the order

orderType

Type of order (or credit movement etc)

instrumentId

ID of the FXB.Instrument

volume

Trading volume - always specified in cash/units terms, such as 10000, not lots such as 0.10

openPrice

Opening price of the order

openTime

Open/creation time

sl

Current stop-loss price on the order, or zero if none

tp

Current take-profit price on the order, or zero if none

trail

Current trailing-stop distance on the order, as a price amount such as 0.0020, or zero. Requires the account to offer trailing stops.

comment

Textual comment on the order. Requires the account to support order comments.

magicNumber

MT4/5-style "magic number" on the order. Requires the account to support magic numbers.

profit

Profit on the order (zero for pending or cancelled orders)

swap

Accumulated rollover/interest/swap on the order

commission

Commission charged on the order

closeTime

Close/cancellation time of the order. Zero for open trades and pending orders.

closePrice

Current or close price. For closed trades and cancelled orders: the final price. For open trades: the opposite side of the spread. For pending orders: the same side of the spread (e.g. the bid price on a pending sell-limit).

2.7.1Order types

The orderType of an FXB.Order can be any of the following members of the FXB.OrderTypes enumeration.

FXB.OrderTypes.

Trades and pending orders

BUY

Open or closed buy trade

SELL

Open or closed sell trade

BUY_LIMIT

Pending or cancelled buy-limit order

SELL_LIMIT

Pending or cancelled sell-limit order

BUY_STOP

Pending or cancelled buy-stop order

SELL_STOP

Pending or cancelled sell-stop order

BUY_STOP_LIMIT

Pending or cancelled buy-stop-limit order (see below)

SELL_STOP_LIMIT

Pending or cancelled sell-stop-limit order (see below)

FXB.OrderTypes.

Credit movements

DEPOSIT

Account deposit

WITHDRAWAL

Account withdrawal

TRANSFER

Internal credit transfer between accounts

COMMISSION

Commission debit or credit

DIVIDEND

Dividend

FINANCING

Financing - rollover/interest/swap

CREDIT

Account credit (typically a temporary incentive by the broker)

INTEREST

Interest payment or charge

TAX

Tax charge

BONUS

Bonus

OTHERCHARGE

Any credit movement not allocated to another category above

These values are numeric. You can reliably test for credit-movement types by looking at orderType >= FXB.OrderTypes.DEPOSIT - though it is more elegant to use the helper FXB.OrderTypes.IsCredit(orderType).

Granularity/accuracy of the subdivision between different types of credit movements depends on the broker platform. Some platforms will report everything as either only a deposit or withdrawal - i.e. a positive or negative movement, with no further detail.

Some platforms report commission and swap against individual orders, using the commission and swap properties, whereas others record these charges as credit movements against the account.

The FXB.OrderTypes enumeration is also used in relation to submitting trading requests, and has additional valid members in that context.

Stop-limit orders are not widely available. The account features indicate whether the account offers them.

2.7.2Order-type helper functions

The framework provides a range of functions for helping to work with orderType values. For example:

var oppositeType = FXB.OrderTypes.Invert(FXB.OrderTypes.BUY_STOP);

FXB.OrderTypes.

Helper function

IsLong(x)

Tests whether the orderType represents a long order or trade (is one of BUY, BUY_LIMIT, BUY_STOP, BUY_STOP_LIMIT)

IsShort(x)

Tests whether the orderType represents a short order or trade (is one of SELL, SELL_LIMIT, SELL_STOP, SELL_STOP_LIMIT)

IsMarket(x)

Tests whether the orderType represents a trade rather than pending order (is one of BUY, SELL)

IsPending(x)

Tests whether the orderType represents a pending order rather than a trade (is one of BUY_LIMIT, BUY_STOP, SELL_LIMIT, SELL_STOP, BUY_STOP_LIMIT, SELL_STOP_LIMIT)

IsLimit(x)

Tests whether the orderType represents a limit pending order (is one of BUY_LIMIT, SELL_LIMIT)

IsStop(x)

Tests whether the orderType represents a limit pending order (is one of BUY_STOP, SELL_STOP)

IsStopLimit(x)

Tests whether the orderType represents a stop-limit pending order (is one of BUY_STOP_LIMIT, SELL_STOP_LIMIT)

IsCredit(x)

Tests whether the orderType represents a credit movement rather than an order or trade

Invert(x)

Inverts the order-type. For example, Invert(FXB.OrderTypes.BUY) = FXB.OrderTypes.SELL, and Invert(FXB.OrderTypes.SELL_LIMIT) = FXB.OrderTypes.BUY_STOP

2.8Framework.Positions collection

As well as listing all the individual open trades on the account, in Instrument.Orders, the framework also calculates a total net position for each market. The Instrument.Positions collection is a Dictionary of FXB.Position objects keyed by their instrumentId properties.

(If the trading account is singleNetPosition:true, then the Positions collection should simply match the list of open trades because the back-end platform will only report one trade per market.)

The Instrument.Positions collection only contains entries for markets which have pending orders or open trades. If there is no open order/trade on a market, then it will have no entry in the Instrument.Positions collection.

However, the collection provides the following helper function for working with individual markets:

Instrument.Positions.

Description

getOrEmpty(x)

Extends the standard Dictionary get(), and returns an object representing no position if the market with instrumentId=x has no open trades or pending orders, and so no entry in the dictionary. The no-position object has positionType:EMPTY and zero for all values.

To watch for changes in positions and their profit, you can use an OnMessage() handler.

2.9FXB.Position object

An FXB.Position object represents all the open trades in a market aggregated up into a single net position. It has the following properties:

Property

Description

instrumentId

ID of the FXB.Instrument

positionType

Type of position - one of the values below.

longTradeCount

Number of open long trades

longVolume

Total volume on open long trades (as cash/units, not lots)

longVWAP

Average entry price for the long trades, or zero if none

shortTradeCount

Number of open short trades

shortVolume

Total volume on open short trades (as cash/units, not lots)

shortVWAP

Average entry price for the short trades, or zero if none

pendingOrderCount

Number of open pending orders for the market

netVolume

Net volume: long volume minus short volume. Therefore, directional: positive values indicate net long and negative values indicate net short.

hasHedging

Whether the position is partially or fully hedged; whether there are both long and short open trades

netVWAP

Net average entry price

profit

Current profit on all the open trades

swap

Current accumulated swap on all the open trades

commission

Commission charged on all the open trades

netProfit

Net profit: profit + swap + commission

The positionType is derived from the number of open trades and their directions. It is one of the FXB.PositionTypes enumeration:

FXB.PositionTypes.

Description

LONG

Position in the market is net-long (but may consist of both long and short trades)

SHORT

Position in the market is net-short (but may consist of both long and short trades)

FULLYHEDGED

Position is fully hedged; flat. Long volume and short volume are non-zero and exactly equal.

PENDINGONLY

No open position, but the market has pending orders

EMPTY

No open trades or pending orders. Only exists in the context of the Positions.getOrEmpty() helper function.

2.10Framework.TradeHistory collection

The Framework.TradeHistory collection is a Dictionary of all historic activity on the account: closed trades, cancelled pending orders, and also credit movements (provided that the back-end platform provides this data).

The history must be explicitly requested. It is empty by default.

To watch for the list being filled, and subsequently updated - you can use an OnMessage() handler.

If/once filled, the collection is a Dictionary of FXB.Order objects, keyed by their orderId properties. (As noted above, FXB.Order is used to represent a number of things, including all of trades, pending orders, and credit movements.)

2.11Framework.Account object

The Framework.Account describes the broker trading account which Figaro is currently logged into. It provides static information about the account such as its ID and deposit currency, plus changing metrics such as balance and margin usage.

Different accounts can have different capabilities - for example, availability of trailing stops or market depth. These are described by the Account.features.

2.11.1Framework.Account properties

The Framework.Account has the following properties. To watch for changes in the account metrics - balance, equity etc - you can use an OnMessage() handler.

Property

Description

accountId

ID for the account. Broker-dependent, and not necessarily numeric.

caption

User-defined caption for the account, created on the Figaro login screen

depositCurrency

Deposit currency of the account

currencySymbol

A suggested display symbol for the depositCurrency. For example, $ for US dollar, £ for sterling, A$ for Australian dollar

accountReadOnly

Boolean indicating whether access to the account is read-only, and all trading requests will fail (e.g. logged in to an MT4 or MT5 account using the read-only "investor" password)

balance

Current account balance

equity

Current account equity: cash balance plus/minus the profit on any open trades

floatingPL

Current profit/loss on open trades

usedMargin

Margin currently in use

freeMargin

Margin currently free and available for use

isSpreadBet

Boolean value indicating whether the account is spread-bet, and a UI will typically want to display a caption such as "£pp" for trade-volume fields. Note: this setting is not entirely reliable. It can have false-negatives. Some broker platforms do not report whether accounts are spread-betting or not, and isSpreadBet will always be false. See also the comments in relation to FXB.Instrument values on spread-bet accounts.

defaultInstrumentId

Suggested instrumentId of the default instrument for widgets/scripts to use if they need to display a market and don't have an existing user selection. Usually "EUR/USD".

features

A block of sub-properties describing the capabilities of the account

2.11.2Account features

Different accounts can have different capabilities. The features available on the current account are described by its Account.features block.

Feature

Description

platform

General ID for the type of broker platform which Figaro is logged into (e.g. "TradingHub")

platformInstance

Specific instance of the back-end platform which Figaro is logged into (e.g. "MyBroker-Live" or "MyBroker-Demo")

hedging

Boolean. Indicates whether the back-end platform is hedging/non-netting; whether it allows simultaneous open trades in opposite directions on the same market.

singleNetPosition

Boolean. Indicates that the back-end platform only ever reports a single net position for each market. If singleNetPosition:true then hedging will by definition be false. But if singleNetPosition:false then hedging can still be false; the platform may allow multiple open trades in a single market, but only in the same direction.

chartTimeframes[]

Array of chart timeframes for which candle data is available. For example, [60, 300, 1800, 3600 etc]

dataOnly

Boolean. Indicates that the account is a data-only connection with no actual trading account or trading functionality. All account metrics will be zero, and trade lists will be empty. (In addition, Account.accountReadOnly will be true.)

dayHighLow

Boolean. Indicates that high and low are available within FXB.Instrument data.

dayChange

Boolean. Indicates that change and changePc are available within FXB.Instrument data.

comment

Boolean. Indicates that orders can have textual comments.

magicNumber

Boolean. Indicates that orders can have MT4/5-style "magic numbers".

trail

Boolean. Indicates that orders can have trailing stops.

changePendingVolume

Boolean. Indicates that the volume can be changed on a pending order. (If not, volume can only be changed by deleting the order and placing a new one.)

hasStopLimit

Boolean. Indicates that the account supports stop-limit orders.

marketDepth

Boolean. Indicates that the account provides market depth.

orderBook

Boolean. Indicates that the account provides client order book.

noHistoricCredits

Boolean. If set, indicates that account history does not include credit movements, and only provides historic trading activity. (Therefore, not possible to calculate statistics such as % returns.)

noHistoricSLTP

Boolean. If set, indicates that account history does not include s/l or t/p on historic trades.

2.12Framework.Environment object

The Framework.Environment object contains information about the Figaro environment:

Property

Description

deviceId

Unique ID for the user's device. See below.

loginSessionId

Unique ID for the current logged-in session. Changes each time that the user logs in to an account. (Therefore, not persistent across a browser session where the user logs in and out of accounts.)

hasMailSystem

Describes whether Figaro's internal mail system is available in this version of the platform

hasCalendar

Describes whether an economic calendar is available in this version of the platform

The deviceId uniquely identifies a device such a web browser. The deviceId is "persistent" but not truly "permanent":

It will be different for each web browser - Chrome, Safari etc - on a single computer.

It will be different for an incognito/private browser window versus a normal window (and different each time an incognito session is opened)

If the user uninstalls and reinstalls the mobile app, the deviceId will change

This deviceId in Framework.Environment is (deliberately) not the identifier for the device in the Figaro mail system. The mail ID cannot be discovered programmatically by a widget, script, or UDIX.

2.13Framework.Translation object

The Framework.Translation object contains functions for translating text-variables into the user's selected language for the Figaro platform. Note: the language cannot change while code is running. It requires the user to log out of the software, and log back in.

The Framework.Translation object contains two properties:

Property

Description

locale

An identifier for the selected language. May be a broad categorisation such as "en", or a sub-specification such as "zh-CN" or "zh-HK".

isRTL

True/false depending on whether the language is usually written right-to-left instead of left-to-right. Note: Figaro automatically sets a CSS class on a widget's <body> depending on the text direction: either LTR or RTL. Widget CSS can therefore use BODY.RTL as a CSS selector for making changes to handle right-to-left languages.

The functions in Framework.Translation are as follows:

Function

Description

TranslateDOM()

Translates language variables in HTML

TranslateItem()

Translates a single language variable

TranslateError()

Translates an error result into the equivalent text

TranslateTimeframe()

Translates a chart timeframe, such as 3600, into corresponding text

TranslateInstrumentCategory()

Translates a market category ID into corresponding text

2.13.1List of available language variables

The reference to the available language variables is simply the list provided by the Figaro platform:

https://figaro.fxbluelabs.com/lexicon/list

This returns a plain-text file listing all the variables and their English equivalents, in the format variable=text. Some variables include placeholders, in the form @1, @2 etc. These are described in relation to TranslateItem().

2.13.2Framework.Translation.TranslateDOM()

The framework supports a mechanism where language variables are included in HTML in curly brackets such as {SetMarket}, and Javascript can then use TranslateDOM() to scan the HTML and replace all these variables with the equivalent text in the user's selected language.

For example, partial HTML:

<div>

<div class="SectionHeader">{MarketList}</div>

<button title="{SetMarket}">{Market}</button>

etc.

Widget code can then automatically translate all these variables using TranslateDOM(), typically in OnLoad():

Framework.OnLoad = function() {

this.Translation.TranslateDOM();

… and other initialisation actions …

};

The TranslateDOM() can be given an optional parameter of the base node to start at. If omitted, it simply scans through the whole document body.

2.13.3Framework.Translation.TranslateItem() and Framework.$T()

TranslateItem() translates a single variable into its equivalent text. For example:

// Will return "% of equity"

var text = Framework.Translation.TranslateItem("VolumePercentOfEquity");

Some of the available variables include placeholders, designed to be replaced by run-time values. For example, MaxSelectMarkets is defined as "Please select a maximum of @1 markets" where @1 is designed to be replaced at run-time by a number.

You can provide values for these placeholders by supplying an array as an optional second parameter to TranslateItem(). For example:

// Create message saying that the user is allowed to select a maximum of 20 markets

var maxAllowedMsg = Framework.Translation.TranslateItem("MaxSelectMarkets", [20]);

Note: because translation is so widely used within Figaro, the framework provides a helper shortcut to stop programmers' fingers wearing down. You can use Framework.$T(…) as an abbreviation of Framework.Translation.TranslateItem(…). For example, the above code can also be written as follows:

var maxAllowedMsg = Framework.$T("MaxSelectMarkets", [20]);

2.13.4Framework.Translation.TranslateError()

The TranslateError() function takes an FXB.Result object describing an error, for example resulting from a failed trading request, and generates the text to display to the user. For example:

Framework.SendOrder(def, function (MsgResult) {

if (MsgResult.result.isOkay) {

// Succeeded

} else {

// Failed. Display error message…

var errorText = Framework.Translation.TranslateError(MsgResult.result);

}

});

2.13.5Framework.Translation.TranslateTimeframe()

The TranslateTimeframe() function takes a chart timeframe, such as 3600, and generates the corresponding text such as "H1". For example:

var txt = Framework.Translation.TranslateTimeframe(86400); // = "D1"

2.13.6Framework.Translation.TranslateInstrumentCategory()

The TranslateInstrumentCategory() function takes a market category ID, such as FXB.InstrumentCategories.COMMODITY, and returns the corresponding display text. For example:

var market = Framework.Instruments.getOrBlank(someInstrumentId);

var categoryText = Framework.Translation.TranslateInstrumentCategory(market.categoryId);

2.14Framework.Theme object

(This section is irrelevant to scripts and UDIXes because they have no user interface of their own. It is only applicable to widgets.)

The user can change the graphical theme of Figaro - e.g. from dark to light - while it is running, without a restart. Therefore, the theme can change while your widget is running.

By including the framework's CSS and Javascript, your widget automatically responds to changes in the selected theme - for example, body background and text colour automatically change. However, you can get the ID of the current theme using the Framework.Theme object, and you can watch for user changes by listening to the THEME message.

Property

Description

id

ID of the selected theme. Subject to change/extension, but currently one of "standard" (dark), "blue", or "light"

The framework automatically sets (and changes, when applicable) a CSS class on the widget's <body> which identifies the current theme.

2.15FXB.Result object

Results/responses to actions are widely represented within the framework using the FXB.Result class. For example, the result of trading actions and of dialogs is an FXB.Result.

An FXB.Result has the following properties:

Property

Description

code

Error (or success) code from FXB.ErrorCodes

isOkay

Derived boolean property indicating whether the code describes success. Note: user-cancellation (CANCELLED) and no-action (NO_ACTION_REQUIRED) are treated as success.

isError

Derived boolean property indicating whether the code describes an error.

An error FXB.Result such as a failed trade can be translated into equivalent readable text using Framework.Translation.TranslateError().

Depending on the nature of the response, the FXB.Result may contain other properties giving additional information. Commonly used properties include msg, param1, param2, and param3.

For example, in the response from the standard Ask() dialog, the msg property indicates which button the user selected:

Framework.Ask("Are you sure?", function (Msg) {

if (Msg.result) {

if (Msg.result.code == FXB.ErrorCodes.OKAY) {

if (Msg.result.msg == "Yes") {

// User selected the yes button

}

} else {

// User cancelled the dialog without clicking a button

}

} else {

// Message is an interim update from the dialog, not the final result

}

});

However, it's only usually necessary to check for a specific response or do nothing, and so the code above would/could typically be reduced to the following:

Framework.Ask("Are you sure?", function (Msg) {

if (Msg.result && Msg.result.msg == "Yes") {

// User selected the yes button (as opposed to No button, or cancellation without

// response, or an interim update from the dialog rather than a final result)

}

});

3Functions in the framework

The External Widget example in Figaro and some of the code in this guide assigns extra ad-hoc functions and properties into the Framework object. For instance: Framework.$DisplayPrices and Framework.$currentInstrumentId in the External Widget example.

The example code prefaces such property and function names with $. This is absolutely not compulsory (and nor does it have any special meaning in Javascript). It is done for two reasons:

It makes it clear which functions are built into the Framework object versus which are private additions by the widget (or script or UDIX).

It avoids accidental collision with the names of built-in members, which would delete and replace them. For example, choosing to overwrite Framework.Instruments or Framework.SendOrder with your own data or function would be a problem. If you preface names with $ then the only possible overlap is with the framework's own $T() helper function.

As described in the introduction, all functions in the framework which generate asynchronous callbacks exist in two versions: a "standard" version with a callback function as a parameter, and a version which issues a Javascript Promise. This guide mainly describes the standard versions. If you want to use the Promise versions, then apply the following rules to the documentation below:

The Promise version of the function starts with p, e.g. pRequestCandles() instead of RequestCandles()

The callback is dropped from the function's parameter list (and replaced by returning a Promise). For example, pRequestCandles() takes a single parameter: the definition of the candle request. Similarly, pSendOrder() takes either one or two parameters: the compulsory description of the trading request, and optional settings regarding how the request is processed.

Any dialog function which returns both an initial update and a final result is modified in the Promise version so that it only returns the final dialog result.

3.1Price requests

Prices, in the Framework.Instruments collection, do not automatically update. You do not automatically receive new prices for all available markets in the platform.

You need to request prices which you are interested in, using Framework.RequestPrices(). This function takes an array of the instrumentId values which you want. For example:

// Request EUR/USD

Framework.RequestPrices(["EUR/USD"]);

// Replace with request for EUR/USD and GBP/USD

Framework.RequestPrices(["GBP/USD", "EUR/USD"]);

If you only want the price for a single instrument, you can pass a string as the parameter, rather than an array:

// Single request, as a string rather than an array

Framework.RequestPrices("EUR/USD");

You can receive the prices either by listening for PRICE updates in the main OnMessage handler, or by defining a specific OnPriceChange handler.

You can also make a specific one-off request for the latest price on a market, using GetLatestPrice(). This takes an instrumentId and an asynchronous callback. For example:

Framework.GetLatestPrice("EUR/USD", function (Msg) {

// Same as a PRICE message: contains a quotes[] array with the single, requested market

});

If the framework has already been asked to collect prices for this market from the broker server, the response back to your asynchronous function will be nearly instant because the framework already has the latest price. If not, the framework will request the price from the broker server and then issue a more delayed callback to your function.

Using GetLatestPrice() also sets up a streaming subscription, as well as collecting the latest price.

3.2Historic candle data

This section describes how to request candle data from the framework. There is a separate section below about features to help you work with that candle data: storing it, adding technical analysis calculations etc.

You can request chart/candle data using Framework.RequestCandles(). The RequestCandles() function takes two parameters:

Parameter

Description

requestDef

Details of the candle request: market, timeframe etc

callback

Callback function which asynchronously receives the requested data (and then further updates depending on the type of request)

For example:

// Request latest candles on EUR/USD H1 (a default number of candles filled in by the framework)

Framework.RequestCandles({

instrumentId: "EUR/USD",

timeframe: 3600

}, function (MsgCandles) {

// Function which asynchronously receives the candle data from the framework

});

There are three types of candle request:

The most common: a request for the last N candles up to the current time, e.g. in order to display a trading chart.

A request for the N candles up to a historic point in time, e.g. in order to add further history when a user scrolls back in time on a chart

A request for the candles between start and end dates.

All requests must include an instrumentId and a timeframe (as in the above example). These are the only compulsory properties in a request.

The list of available timeframes depends on the account and trading back-end which Figaro is connected to. This information is provided in the account features.

A request for N candles (up to the current time or a specific historic date) can include a count property specifying how many candles you want. However, we recommend that you omit this unless absolutely necessary, and let the framework pick a value for you. The cost of collecting candle data can vary based on the back-end system. If you omit count, the framework has discretion to make a smaller or larger request depending on how quickly the back-end system processes requests. As a general rule, count should not exceed 1000. If you need more candles than this, request them in separate batches (going backwards in time as each batch is received).

You can specify a start date and/or end date for the candle request using startDate and endDate.

If you make a request for candles up to the current time then, by default, the request will stream: your callback function will get an initial call with the existing candle data, and will then get further calls on each change in the market price - updating the current candle or adding a new candle. If you don't require streaming, you can turn it off by specifying noStreaming:true.

In full, the possible properties of a candle request are therefore as follows:

Property

Description

instrumentId

ID of the FXB.Instrument for which you want data

timeframe

Chart timeframe, such as 3600

count

Number of candles (not applicable if startDate is provided)

startDate

Start date for historic request. If provided, must also set endDate.

endDate

End date for historic request

noStreaming

Optional flag which can be used to turn off streaming on requests. (Automatic if a request has an endDate.)

requestId

Optional ID for the request, repeated back in messages to the callback function. If not provided, the framework will allocate one automatically. If you want to allocate your own ID, you are recommended to use FXB.utils.GenerateGuid().

timezone

Optional time zone settings for the request. See below.

convertTimes

Option to do conversion of UTC times before returning the data. See below.

Note: the Promise version of the function, pRequestCandles(), always sets noStreaming:true because a Promise cannot settle more than once; it cannot receive multiple callbacks with streaming data.

3.2.1Candle response messages

The framework's response into your callback function will be a message object containing the following properties:

Property

Description

result

FXB.Result object describing whether your request succeeded

requestId

ID of the candle request, either allocated automatically or set by you in your request definition. This can be used to terminate streaming requests.

candles[]

If your request was successful, an array of initial or changed candles

timezone

Time zone used for the candle request. See below. Either repeats a timezone which you specified in your request or, if none, contains the user's default time zone for charts (Framework. GetUserChartTimezone).

For example:

Framework.RequestCandles(requestDef, function (MsgCandles) {

if (MsgCandles.result.isOkay) {

// MsgCandles will contain a candles[] array

} else {

// Some problem with the request; see MsgCandles.result.code

}

});

Each item in a candle array has the following properties:

Candle property

Description

i

Unique time-based index value for the candle. Not necessarily the same as ts. You should use i rather than ts to store/index and update candles.

ts

Start time of the candle - see notes below about time zones. Depending on the back-end system, this is not necessarily the exact start of a timeframe-based period. For example, if you have requested hourly data, then it is possible when connected to some back-end systems for the ts to be e.g. 10:02, if that was the first price movement in the hour, rather than 10:00.

o

Open price

h

High price

l

Low price

c

Close price (current price for current candle)

v

Volume. Not available on all platforms.

The candles[] array is always sorted by time (ts), in descending order - most recent first.

In a streaming request, the following will happen:

Your callback function will receive an initial call with an array of, say, 1000 candles

Each time the price changes, the current candle will update, and your callback function will receive a candles[] array containing a single item: the update to the current candle.

When the price changes and enters a new time period, you will receive a candles[] array containing an item with an i (and ts) which you have not previously received.

3.2.2Terminating streaming requests

You can terminate a streaming candle request using Framework.TerminateCandleRequest(). This takes a single parameter: the ID of the candle request.

The ID of the request is included by the framework in its response messages. You can also specify an ID of your own at outset, rather than having one allocated automatically, using a requestId in your request.

3.2.3Time zones for candle requests

Like all times in the framework, candle ts values are UTC. However, candle timestamps are more complicated than that.

For timeframes above H1, you can set a timezone value defining how you want candles to be aggregated.

For example, rather than requesting daily candles based on UTC days, you can instead say that you want aggregation based on UTC+2. This is extremely common in forex because the markets open at 5pm New York time on Sunday. If you request candles based on UTC, there are then six D1 candles per week, not five: candles for Monday to Friday, plus a stub of candle covering the period from 9pm/10pm UTC (5pm in New York) to midnight on Sunday.

Let's say that you request D1 candles aggregated based on UTC+2 (as illustrated below). Each daily candle will cover 24 hours. But its start time will not be midnight UTC. The ts value of each candle will represent 10pm (or 9pm during periods of daylight savings).

You can specify aggregation in your candle request using a timezone object (see also time conversion). This has two properties:

Property

Description

offset

Base offset in minutes from UTC, e.g. 120 or -300. Note: although this offset lets you specify a number of minutes, all existing back-end platforms require that any offset for candle requests is a number of hours. Therefore, offset should be a multiple of 60.

dstMode

Daylight savings mode. One of the values of the FXB.DSTModes enumeration: NONE, USA, EUROPE, AUSTRALIA

For example, the most common request for forex analysis looks like this:

Framework.RequestCandles({

instrumentId: "EUR/USD",

timeframe: 86400, // D1,

timezone: {

// 2 hours ahead of UTC, with USA daylight savings.

// Therefore, constant 7 hours ahead of New York time.

// Market open at 17:00 NYC on Sunday is reported as a candle ts of 00:00

offset: 120,

dstMode: FXB.DSTModes.USA

}

}, function (MsgCandles) {

});

This requests aggregation based on UTC+2, changing to UTC+3 on the USA daylight savings schedule. In effect, candles are aggregated based on a day running from 5pm-5pm New York time.

The further and very important thing to note is that the user can set a preference in the Figaro platform for how candle data should be aggregated. If you do not explicitly set a timezone in your candle request, you will automatically get the user's preferred settings. And the default settings in Figaro are not UTC; they're UTC+2 with USA daylight savings.

In other words: if you do not set your own timezone in your candle request, what you will get is aggregation based on UTC+2/3, unless the user has changed the standard settings in the Figaro platform. Daily candles will have a ts at 9pm or 10pm UTC, not midnight UTC.

However, in your request you can also optionally specify convertTimes:true. If set, this automatically converts all the candles ts values before returning the data to you (using Framework.AdjustUTCToZone).

3.3Trading

The framework provides two types of trading requests:

Standard requests for an individual order: opening, closing, or modifying it

Aggregated actions on a market or on the whole account. For example, you can issue a request of type FXB.OrderTypes.FLATTEN. This closes all orders and trades on the account, rather than your code having to iterate though the orders in Framework.Orders and issue its own separate requests to close each one.

In addition, you can either send a single request or an array containing a batch of requests. If you issue a batch, then you get a response either after all requests succeed or when the first one fails. (You don't get a response from a batch listing each individual request and whether it succeeded or failed.)

You issue trading requests using Framework.SendOrder(). This has two compulsory parameters and one optional parameter:

Parameter

Description

request

Definition of an individual request, or an array of requests

callback

Callback function which asynchronously receives success of the request or (first) failure

settings

Optional settings controlling how the request is processed. For example, the default is that the framework will automatically display an error message if a trading request fails, but you can use the settings to say that the framework should not display its own error dialog, and that you will do this yourself.

For example:

Framework.SendOrder({

// Buy 10K (0.10 lots) EUR/USD

tradingAction: FXB.OrderTypes.BUY,

instrumentId: "EUR/USD",

volume: 10000

}, function (MsgResult) {

// Asynchronously called with the result of the request

});

Or, using a batch of multiple requests, with optional settings specifying forced confirmation by the user before the batch is started:

Framework.SendOrder([

{

// Close all EUR/USD orders and trades

tradingAction: FXB.OrderTypes.CLOSEPOSITION,

instrumentId: "EUR/USD"

},{

// Buy 5K GBP/USD, specifying the volume in lots

tradingAction: FXB.OrderTypes.BUY,

instrumentId: "GBP/USD",

volume: {lots: 0.05}

}

]}, function (Msg) {

// Asynchronously called when the whole batch has succeeded or on the first failure

}, {

// Force confirmation by the user before the batch is processed

confirm: true

});

3.3.1Overview of trading requests

The request definition which you submit as a parameter to SendOrder() always has one compulsory property: tradingAction. It then has other compulsory or optional properties depending on the type of trading action.

Useful note! The Run Script widget in the Figaro platform provides interactive help on constructing the properties for trading requests. If you use Create From > Trading Action it will show you the script and request settings corresponding to a set of options in the deal ticket.

For the purposes of trading requests, the FXB.OrderTypes enumeration has values which are not applicable in the context of an FXB.Order. These extra values are described in the sections below.

Particularly when opening orders and trades, some options can be specified in multiple forms. This saves your code from doing its own calculations, and makes it easier to translate from user interface fields to properties in a trading request. For example, all the following (and others listed below) are valid ways of specifying the volume when opening a new order or trade:

Example

Description

volume: 10000

Standard request, defining volume in terms of cash/units

volume: {lots: 0.1}

Framework converts the specified lots quantity to cash/units using the contractSize of the FXB.Instrument

volume: {equityPercent: 0.5}

Can only be used in combination with a stop-loss. The framework calculates the trade volume such that the cash value of hitting the stop-loss is (a maximum of) the specified percentage of current account equity

In other words, the framework lets you send a request saying "stop-loss at 50 pips and risking 1% of account equity" without needing to do all the accompanying calculations yourself in your own code:

Framework.SendOrder({

sl: {pips: 50},

volume: {equityPercent: 1},

3.3.2New pending orders and trades

To open a new pending order or trade (market order), you set the tradingAction to one of the FXB.OrderTypes: BUY, SELL_LIMIT etc. For example:

var request = {

tradingAction: FXB.OrderTypes.SELL_LIMIT,

openPrice: {offset: 0.0020}, // Offset of 0.0020 from current price (bid, because sell)

instrumentId: "EUR/USD",

volume: {lots: 0.20}

};

However, for pending orders, you can use two additional values: BUY_PENDING and SELL_PENDING. These let you tell the framework to work out whether your specified entry price for the pending order is a limit or a stop. If you only care about the entry price, e.g. "enter at 1.23456", and not its position relative to the current market price, then you can set the entry price and use BUY_PENDING or SELL_PENDING, and the framework will convert that into the correct combination of buy/sell and limit/stop, e.g. turning the SELL_PENDING into a SELL_STOP.

Compulsory and optional request properties when opening a new trade or order are as follows:

Example

Description

instrumentId

instrumentId of the FXB.Instrument to trade. Always compulsory.

volume

Volume to trade. Always compulsory.

openPrice

Open price for a pending order. Compulsory on pending orders; ignored on trades. For stop-limit orders, the openPrice is the limit price.

triggerPrice

Trigger (stop) price for stop-limit orders. Compulsory on stop-limit orders; ignored on all other types.

sl

Stop-loss to set on the order/trade. Always optional.

tp

Take-profit to set on the order/trade. Always optional.

trail

Trailing stop to apply to the order/trade. Always optional, and not necessarily available. Requires that the broker account supports trailing stops.

expiry

Optional expiration on pending orders. Ignored on trades.

comment

Textual comment to set on the order/trade, if supported by the back-end platform.

magicNumber

MT4/5-style "magic number" to set on the order/trade, if supported by the back-end platform.

3.3.2.1Volume on order and trade requests

The volume can be specified in any of the following ways:

Volume

Description

number

Simple numeric value in cash/units (not lots), such as 10000

{notional: x}

Same as a simple number: value in cash/units (not lots), such as 10000

{lots: x}

Value in lots, such as 0.10, which the framework converts using the market's contractSize

{poundPerPoint: x}

Converted by the framework so that cash value of a movement by the market's pipSize is (a maximum of) the specified amount. Despite the property name ("pound"), deriving from UK spread-bet accounts, the value x is interpreted using whatever is the account's deposit currency. For example: trading EUR/USD on a GBP account, with pipSize of 0.00010. Each movement of the 0.0001 pipSize will gain or lose USD 10 (with contractSize of 100,000). If, on the GBP account, you set {poundPerPoint: 1}, then the framework will calculate the volume which, rounding down, leads to each 0.0001 movement gaining or losing £1. Calculated volume will be around 7000 (0.07 lots), depending on the current GBP-USD exchange rate.

{cashRisk: x}

Requires a stop-loss on the order/trade. The framework calculates the volume which, in combination with the specified stop-loss, leads to a maximum loss of no more than x.

{equityPercent: x}

Requires a stop-loss on the order/trade. The framework calculates the volume which, in combination with the specified stop-loss, leads to a maximum loss of no more than x% of current account equity.

{balancePercent: x}

Requires a stop-loss on the order/trade. The framework calculates the volume which, in combination with the specified stop-loss, leads to a maximum loss of no more than x% of current account balance.

3.3.2.2Open price on pending orders

The openPrice on a pending order, and the triggerPrice on stop-limit orders, can be specified using any of the following. (It is ignored on new trades.)

openPrice:

Description

number

Fixed price such as 1.23456

{price: x}

Same as a simple number: fixed price such as 1.23456

{offset: x}

Offset from the current market price, such as 0.0020. The value is directional depending on the tradingAction. For a sell-limit or buy-stop, it is added to the current price. For a sell-stop or buy-limit, it is subtracted from the current price.

{pips: x}

Offset from the current market price in pips, such as 20. Converted by the framework using the market's pipSize. The value is directional depending on the tradingAction.

{bar: barDef}

See the description of candle-based prices below

3.3.2.3Stop-loss on orders and trades

The sl on a pending order or trade can be specified using any of the following:

sl:

Description

number

Fixed price such as 1.23456 (zero or omitted for no stop-loss)

{price: x}

Same as a simple number: fixed price such as 1.23456

{offset: x}

Offset from the current market price, such as 0.0020. The value is directional depending on the tradingAction: subtracted from the current price for buy orders, added for sells.

{pips: x}

Offset from the current market price in pips, such as 20. Converted by the framework using the market's pipSize. The value is directional depending on the tradingAction.

{bar: barDef}

See the description of candle-based prices below

{cashRisk: x}

Calculates the stop-loss so that, based on the volume, it equates to a cash value no more than x (in the account's deposit currency). Must be used with a fixed volume; cannot be used with a volume which is also variable/derived such as volume:{cashRisk:x}

{equityPercent:x}

Calculates the stop-loss so that, based on the volume, it equates to no more than x% of the current account equity. Must be used with a fixed volume; cannot be used with a volume which is also variable/derived such as volume:{cashRisk:x}

{balancePercent: x}

Calculates the stop-loss so that, based on the volume, it equates to no more than x% of the current account balance. Must be used with a fixed volume; cannot be used with a volume which is also variable/derived such as volume:{cashRisk:x}

3.3.2.4Take-profit on orders and trades

The tp on a new order or trade can use all the same options as the sl . However, there is one further option available in relation to the take-profit:

tp:

Description

{rrr: x}

Calculates the take-profit so that it is rrr times the distance of the stop-loss. For example, if rrr:2 then the take-profit will be twice as far from entry as the stop-loss (and represent twice the cash profit/loss). Must be used in conjunction with an sl; the sl cannot be zero/omitted.

3.3.2.5Trailing stop on orders and trades

Trailing stops are not necessarily supported; this depends on the capabilities of the broker account and its back-end trading platform. The trail property will be ignored if not supported by the back-end platform.

If supported, the trail property can be one of the following:

trail:

Description

number

Price offset, such as 0.0020

{pips: x}

Offset in pips, such as 20. Converted by the framework using the market's pipSize.

3.3.2.6Expiry on pending orders

The expiry on a pending order can be specified using any of the following. (It is ignored on new trades.)

expiry:

Description

number

An absolute expiry time (UTC, milliseconds)

"day" (expiry: "day")

End-of-day. The time zone for the "day" is taken from the user's settings for the Figaro platform.

{seconds: x}

Number of seconds from current time

{minutes: x}

Number of minutes from current time

{hours: x}

Number of hours from current time

3.3.2.7Candle-based prices in order definitions

The definition of the openPrice, sl, and tp in an order or trade can instruct the framework to calculate a price from historic candle data. For example: "open price at the high of yesterday's D1 [daily] candle, plus 1 pip".

Note: this can lead to a significant delay in processing a request unless the framework has already been asked to collect this candle data.

To use candle-based prices, the trading request must include a timeframe. The definitions of the open price, stop-loss, and take-profit must use the same timeframe (or none); you cannot mix definitions from different timeframes within the same request.

The definition of the openPrice, sl, and/or tp can then include the following values. The index and price are compulsory. The offsetPips is optional.

Bar-based price

Description

index

Zero-based candle index. Zero means "the current candle"; 1 means "the last complete candle" etc.

price

The price to use from the candle: one of o, h, l, c

offsetPips

Optional offset in pips to add (note: always add) to the candle price

For example, the following request specifies the following:

Buy-limit pending order

Open price at the high of yesterday's D1 candle

Stop-loss at the low of yesterday's D1 candle, minus 5 pips

Take-profit at 30 pips from the current price (not candle-based)

Framework.SendOrder({

tradingAction: FXB.OrderTypes.BUY_LIMIT,

timeframe: 86400, // Timeframe for candle-based open price and stop-loss

openPrice: {bar: {index: 1, price: "h"}},

sl: {bar: {index: 1, price: "l", offsetPips: -5}},

tp: {pips: 30},

3.3.3Modifying open trades and pending orders

You can modify a pending order or open trade using FXB.OrderTypes.CHANGE as the tradingAction. You must supply the orderId of an open order or trade in the Framework.Orders list. For example:

// Change the stop-loss on order O12345 to 30 pips from the current market price

// (leaving all other settings unchanged)

Framework.SendOrder({

orderId: "O12345",

tradingAction: FXB.OrderTypes.CHANGE,

sl: {pips: 30}

}, function (MsgResult) {

});

The stop-loss (sl) and take-profit (tp) of a trade or order can always be changed. You can alter the openPrice of a pending order but not of an open trade - obviously! Support for trailing stops (trail) is determined by the features of the trading account and its back-end platform. The ability to change the volume of a pending order also depends on the features of the account and back-end platform. A new value for the volume will be ignored if the back-end platform does not support such a change.

When modifying an order or trade, you can use all the same options as when making a new request. For example, you can change an open trade as follows:

Framework.SendOrder({

orderId: "T12345",

tradingAction: FXB.OrderTypes.CHANGE,

timeframe: 3600, // Timeframe for candle-based stop-loss

sl: {bar: {index: 1, price: "l"}}, // Set the stop-loss to the low of the previous H1 bar

tp: {pips: 30} // Set the take-profit to 30 pips from the current market price

}, function (MsgResult) {

});

3.3.4Closing individual trades and orders

You can close an open trade or delete a pending order by using FXB.OrderTypes.CLOSE as the tradingAction. For closure/deletion, you must supply the orderId of an open order or trade in the Framework.Orders list.

Deletion of a pending order is simple because it can have no properties other than the tradingAction and orderId:

// Delete pending order O12345

Framework.SendOrder({

tradingAction: FXB.OrderTypes.CLOSE,

orderId: "O12345"

}, function (MsgResult) {

});

Closure of an open trade is slightly more complicated because you can optionally do a partial close instead of a complete close.

The definition for a full close of an open trade is the same as deletion of a pending order:

// Close (completely) open trade T12345

Framework.SendOrder({

tradingAction: FXB.OrderTypes.CLOSE,

orderId: "T12345"

}, function (MsgResult) {

});

To do a partial close, you specify a volume. This can be one of the following values:

expiry:

Description

number

Simple numeric value in cash/units, not lots, to close, such as 10000. (If this exceeds the trade volume, the framework will simply close the trade entirely rather than reporting an error.)

{lots: x}

Portion to close in lots, converted by the framework to cash/units using the market's contractSize

{percent: x}

Close the specified percentage of the trade, always rounding up to the nearest permitted trading increment. See the example below.

The following request closes 75% of trade T12345. This is subject to the trading minimums and increments for the market. For example, if T12345 is a position of 3000 cash/units, and the minimum/increment volume for orders in this market is 1000, then the following instruction will be interpreted as a complete close. The framework always rounds up when doing a close. It will calculate 75% of 3000 = 2250, and round that up to the nearest permitted increment of 1000… which is a complete close of all 3000 cash/units.

// Close 75% of trade T12345

Framework.SendOrder({

tradingAction: FXB.OrderTypes.CLOSE,

orderId: "T12345",

volume: {percent: 75}

}, function (MsgResult) {

});

3.3.5Closing all open trades or pending orders on a single market

The following tradingAction types close all the open trades and/or pending orders on a single market. They simply save your code having to scan the Framework.Orders list and build its own list of individual trading requests. With some types of account/back-end trading platform the framework may be able to use a more optimised trading request than closing each order/trade individually.

For the following actions, an instrumentId for the FXB.Instrument is compulsory. No other values are required or used.

For example, you can close all the loss-making open trades on EUR/USD with the following request:

Framework.SendOrder({

tradingAction: FXB.OrderTypes.CLOSEPOSLOSERS,

instrumentId: "EUR/USD"

}, function (MsgResult) {

});

FXB.OrderTypes.

Description

CLOSEPOSITION

Closes all open trades and pending orders on the market

CLOSEPOSTRADES

Closes open trades on the market, but not pending orders

CLOSEPOSPENDING

Closes pending orders, but not open trades

CLOSEPOSWINNERS

Closes profitable open trades (netProfit > 0)

CLOSEPOSLOSERS

Closes loss-making open trades (netProfit < 0)

CLOSEPOSLONG

Closes all long trades and pending orders

CLOSEPOSSHORT

Closes all short trades and pending orders

HEDGEPOSITION

If supported by the account, hedges the current position by placing an open trade in the opposite direction for the current open volume. For example, you are currently short 20,000 EUR/USD this will open a buy trade for 20,000. Not allowed if the account is netting/non-hedging.

DOUBLEPOSITION

Doubles the current position. For example, if you are currently (net) short 20,000 EUR/USD this will open a new sell trade for a further 20,000

REVERSEPOSITION

Reverses the current position by placing a new trade for the opposite volume, without closing the current open trade(s). For example, if you are currently short 20,000 EUR/USD this will open a buy trade for 40,000. Not allowed if the account is netting/non-hedging.

CLOSEANDREVERSE

Like REVERSEPOSITION, but closes the current open trade(s) rather than just opening a new trade in the opposite direction, and is therefore permitted on netting/non-hedging accounts. For example, if you are currently short 20,000 EUR/USD this will close the existing open trade(s) and open a new buy trade for 20,000.

3.3.6Closing all open trades or pending orders on the whole account

The following tradingAction types close all the open trades and/or pending orders across the whole account. Like position actions, they save your code having to scan the Framework.Orders list and build its own list of individual trading requests. With some types of account/back-end trading platform the framework may be able to use a more optimised trading request than closing each order/trade individually.

For example, the following request completely flattens your entire account - closes all open trades and deletes all pending orders:

Framework.SendOrder({

tradingAction: FXB.OrderTypes.FLATTEN

}, function (MsgResult) {

});

All the following tradingAction types require no other properties in the request, because they simply operate across the whole account:

FXB.OrderTypes.

Description

FLATTEN

Closes all open trades and pending orders on all markets

FLATTENTRADES

Closes all open trades on all markets, but not pending orders

FLATTENPENDING

Deletes all pending orders on all markets, but not open trades

FLATTENWINNERS

Closes all profitable open trades on all markets (netProfit > 0)

FLATTENLOSERS

Closes all loss-making open trades on all markets (netProfit < 0)

FLATTENLONG

Closes all long trades and pending orders on all markets

FLATTENSHORT

Closes all short trades and pending orders on all markets

3.3.7Asynchronous result callback for SendOrder()

The callback function which you provide as a parameter to SendOrder() receives success or (first) failure of the request. It receives one parameter: a message which contains an FXB.Result object in a property called result. For example:

Framework.SendOrder({

… request definition

}, function (Msg) {

if (Msg.result.isError) {

// Trading request failed

} else {

// Trading request did not fail. However…

}

});

Broadly, there are four possible outcomes from a trading request:

Request fails. The FXB.Result will return isError=true

Request is carried out and succeeds. The FXB.Result will return isOkay=true and code=FXB.ErrorCodes.OKAY

Request requires no action - for example, asking to do a FLATTEN on the account when there are no open orders. The FXB.Result will return isOkay=true and code=FXB.ErrorCodes.NO_ACTION_REQUIRED

Your request is subject to user confirmation, and the user cancels the request before it is started. The FXB.Result will return isOkay=true and code=FXB.ErrorCodes.CANCELLED

In other words, isOkay=true in the FXB.Result does not necessarily mean that any trading action has been carried out. In particular, if you want to look for cancellation by the user, you need to check the code instead of isOkay or isError. User cancellation is not treated as an error condition by an FXB.Result.

3.3.8Optional settings for trading requests

A trading instruction through SendOrder() can have an optional third settings parameter controlling how the request is executed by the framework. This settings block can have the following properties:

Property

Description

confirm

The user can control, in Figaro's settings, whether each widget has its trading activity confirmed or not. If the user has turned confirmation off for your widget you can turn it on setting confirm:true. But you can't do the opposite. You cannot turn off confirmation if the user has turned it on.

showResultDialog

Controls whether the framework displays a message about success/failure of the request. By default, the framework will display an error message for failed requests but nothing for successful requests. If you want to hide the framework's own message and display your own confirmation, then you can set showResultDialog:false (or showResultDialog:""). Other possible values for showResultDialog are "all", meaning "show a message for successes as well as failures", and "noaction" meaning "don't show a message for a request which was successfully carried out, but do show a message for a request which didn't require any action".

fifo

Boolean which can be used to force non-hedging behaviour on hedging accounts (irrelevant and ignored on netting accounts). For example, if fifo:true and the user is long EUR/USD, then placing a sell market order for EUR/USD will close (or partially close) the existing long ticket rather than opening a new one. If omitted, this defaults to the user-controllable setting in the Figaro platform.

3.3.9Forcing confirmation of all subsequent trading

A widget, script, or UDIX can use the function Framework.ConfirmAllTrading() to turn on confirmation of all subsequent trading requests which it submits during the lifetime of that instance. (ConfirmAllTrading() has no effect on other components, only on the caller.) Once set, the effects of ConfirmAllTrading() cannot be turned off.

This function is documented here for completeness, but it not actually designed to be used by a widget, script, or UDIX. It is meant to be a simple safety mechanism for end-users. If someone is given a script - via whatever means - they can ensure that it can't trade automatically just by adding Framework.ConfirmAllTrading() to the start of the script. For example, if a script is loaded into Figaro using its Data Transfer widget, Framework.ConfirmAllTrading() is automatically added to the script.

3.3.10Validating trading requests without execution

You can use Framework.ValidateOrder() to validate trading requests without executing them. As well as checking that a request passes the framework's internal validation, this also does two other things:

It splits a bulk action such as FLATTEN into the individual jobs for closing each open order or trade

It converts variable definitions such as openPrice:{offset:0.0020} or volume:{equityPercent:2} into fixed amounts

ValidateOrder() takes two parameters: the same trading request which you pass to SendOrder(), and an asynchronous callback function. For example:

Framework.ValidateOrder({

tradingAction: FXB.OrderTypes.SELL_LIMIT,

openPrice: {offset: 0.0020},

instrumentId: "EUR/USD",

volume: {lots: 0.20}

}, function (Msg) {

… result of validation

});

The Msg which is passed to the callback function can contain two properties:

Property

Description

result

An FXB.Result object. If validation succeeded then isOkay will be true. If validation failed, then isError will be true, and the code will be one of the framework's error codes.

tradingJobs[]

If successful, an array of the individual trading jobs after validation

For example, after validation of the above request, tradingJobs[] will have a single entry such as the following:

{

instrumentId: "EUR/USD",

tradingAction: FXB.OrderTypes.SELL_LIMIT,

volume: 20000, // conversion of {lots:0.20}

openPrice: 1.23456 // conversion of variable {offset:0.0020} to fixed price

}

3.4Account history

The history on an account - closed trades and credit movements - is potentially (a) very large and (b) time-consuming to collect from the broker's back-end system. The Framework.TradeHistory is empty by default, and must explicitly be filled by using Framework.RequestAccountHistory(). This then sends ACCOUNT_HISTORY updates to your message handler and fills the Framework.TradeHistory collection.

There can be a long delay between using Framework.RequestAccountHistory() and receiving the initial history. You may get an almost immediate response (asynchronous) if another widget has already initiated collection of the history or, at the other extreme, there can be a delay of many seconds while the framework has to make multiple calls to the broker server to collect the initial history.

The contents and detail of the history are slightly variable depending on the capabilities of the account. For example, a couple of broker back-end systems can only provide the historic trades on an account, not the credit movements.

3.5Dialogs

Widgets, scripts, and UDIXes have a wide variety of options for displaying dialogs in order to present information to the user or to ask questions. Note: you cannot use browser modals such as alert() and confirm().

3.5.1Standard dialog behaviour and settings

The many types of dialog offered by the framework share some common behaviour and settings.

By default, dialogs are tied to their creator. If your widget, script, or UDIX terminates, any open dialog(s) from it will be automatically destroyed. You can change this lifecycle by setting keepAfterParent:true in the settings for the dialog.

There are some standard settings which you can apply when creating any type of dialog:

Function

Description

keepAfterParent

Defaults to false, causing a dialog to be destroyed by the framework automatically if its creator terminates. Can be changed to true, giving the dialog independent life.

noCancel

Defaults to false. If set to true, the user cannot dismiss a dialog by clicking outside it. They must respond to one of the dialog's buttons.

noResize

Prevents a dialog from being resizable by the user

windowWidth

Overrides the standard width of the dialog. The value can be specified in pixels, e.g. "450px", or as a % of the browser window size, e.g. "75%". (Ignored on small screens such as mobile phones, where dialogs are always made full-screen.)

windowHeight

Overrides the standard height of the dialog. Same options as windowWidth.

Note: you must set both windowWidth and windowHeight, or neither. If you only set one of the properties, it is ignored.

In some cases, you don't just get a final result from a dialog. You also get an initial callback which gives you the ID of the dialog. This makes it possible for you to destroy the dialog, using Framework.DestroyDialog(), before the user responds. There is an example of this below in relation to a simple Ask() message.

3.5.2Common information/alert/question dialogs

The framework provides a set of standard dialogs for displaying simple messages to the user, or asking yes/no questions.

These are all fundamentally the same dialog, but with different cosmetic styling and default buttons. For example, you can change the error dialog so that it has yes/no buttons, and you can change the confirmation dialog so that it only has a single "okay" button.

The functions provided by the framework in this area are as follows:

Function

Description

Ask()

Asks the user a question

ErrorMessage()

Displays a message styled as an error

SuccessMessage()

Displays a message styled as success

AlertMessage()

Displays a message styled as an alert/notification

InfoDialog()

Displays a message styled as information

Each of these functions takes two parameters: a block of settings, and a callback function which receives the dialog result. For example:

Framework.Ask("Are you sure?", function(Msg) {

});

The settings can either be a simple string, as in the above example, or an object with properties. For example:

Framework.Ask({

title: "Really?",

text: "Are you sure you want to do this?",

buttons: [

{text: "Yes, definitely", value: "Yup", className: "btn-red"},

{text: "On second thoughts…", value: "No"}

]

}, function (Msg) {

});

3.5.2.1Button styles for common dialogs

As you can see in the above example, you can not only set the title and text of the dialog, but you can also override the buttons. Each button definition can have the following properties:

Button property

Description

text

Text to display in the button

value

Value to pass back to your asynchronous handler if the button is clicked

className

Optional CSS class for the button. Common options are btn-red, btn-blue, btn-green, btn-pink, btn-amber, btn-standard

3.5.2.2Callbacks from common dialogs

Any asynchronous message handler which you specify for a common dialog is called twice, not once. The first call is an update/initialisation. You will generally want to ignore this, but it gives you the ID of the dialog, and makes it possible for you to destroy the dialog before the user makes a selection.

The second call to your asynchronous function is the result from the dialog. You can test for the final result callback by looking for Msg.result. For example:

Framework.Ask(… , function(Msg) {

if (Msg.result) {

// Final result

} else {

// Initial creation/update

}

});

The final result, Msg.result, is an FXB.Result object. The code will be FXB.ErrorCodes.CANCELLED if the user closed the dialog without a response. If the user clicked one of the buttons, then the code will be FXB.ErrorCodes.OKAY and the FXB.Result will have a msg property which is the value of the selected button.

The standard Ask() function - if you do not override it, like the above example - has buttons which return the value/msg "Yes" and "No". Therefore, typical handling of the Ask() function very simply looks like this:

Framework.Ask(…, function(Msg) {

if (Msg.result && Msg.result.msg == "Yes") {

// Do something if this is the final result from the dialog, and the user said "yes".

// Ignore cases - do nothing - if this message is not the final dialog result,

// or if the user clicked no or cancelled the dialog.

}

});

3.5.2.3Manually destroying common dialogs

As described above, the initial update message from the dialog gives you its ID, in Msg.widgetId, and that makes it possible for you to use Framework.DestroyDialog() to close the dialog. For example, the following Ask() cancels itself if the user does not respond within 5 seconds:

Framework.Ask({

title: "Are you sure?",

text: "You have 5 seconds to answer...",

}, function (Msg) {

if (!Msg.result) {

// No .result in message. Therefore, it's the initial creation of the dialog.

// Set a timer which destroys the dialog after 5 seconds if the

// user has not answered.

setTimeout(function() {

Framework.DestroyDialog(Msg.widgetId);

}, 5000);

} else {

// Result from the dialog. Can either be the user clicking the button,

// or termination of the dialog by the above DestroyDialog()

}

});

3.5.2.4Floating, non-modal common dialogs

All the common dialogs can also be made floating instead of modal (so that they are not destroyed by clicking outside them). You do this by setting floating:true in the properties for the dialog. For example:

Framework.InfoMessage({

text: "Must be explicitly closed. Not destroyed if the user clicks outside it",

floating: true

});

3.5.3Selecting a market or timeframe

The framework provides two standard dialogs for selecting a market or timeframe. Note: these functions only give you a final result. You don't get an initial creation message from the dialog which gives you the ability to destroy it. You also can't customise the size or style of these dialogs, or set noCancel on them.

You can ask the user to select a market using Framework.SelectInstrument(). The asynchronous return value is the instrumentId of an FXB.Instrument, or null. For example:

Framework.SelectInstrument(function (instrumentId) {

if (instrumentId) {

var market = Framework.Instruments.get(instrumentId);

} else {

// Cancellation of dialog

}

});

You can ask the user to select a timeframe using Framework.SelectTimeframe(). The asynchronous return value is the numeric chart timeframe, or null. For example:

Framework.SelectTimeframe(function (timeframe) {

if (timeframe) {

} else {

// Cancellation of dialog

}

});

3.5.4Asking for a text value

You can ask the user for a text value using AskForText(). Note: this function only gives you a final result. You don't get an initial creation message from the dialog which gives you the ability to destroy it.

The simple usage is equivalent to the browser modalprompt(). You supply a string, and the user has the opportunity to change it. For example:

Framework.AskForText("Current value", function (result) {

// Text entered by user, or null if the user cancelled.

});

Instead of simply supplying the pre-populated value for the text field, you can supply a full dialog definition in which you can set a title plus standard dialog options such as noCancel. For example:

Framework.AskForText({

title: "Please enter your name",

heading: "What are you called?",

needValue: true, // User must enter a value, or else Save is disabled

text: "", // Text to prepopulate with

}, function (result) {

// Text entered by user, or null if the dialog is cancelled

});

In addition to standard dialog options, the full set of properties which you can provide is as follows:

Property

Description

title

Title for the dialog

heading

Optional heading to display above the text field

needValue

If true, the Save button is disabled unless the user enters a value. Note: if you set needValue but not the standard noCancel, then the user cannot click the Save button without entering a value, but can cancel the dialog.

3.5.5Selecting from a list

The framework provides a SelectFromList() function to display a standard dialog for selecting from a simple list of options. A couple of notes:

This function only gives you a final result. You don't get an initial creation message from the dialog which gives you the ability to destroy it.

You can display a more complex list, with multiple levels and with additional options such as icons, using Framework.MenuListDialog()

SelectFromList() takes two parameters: the list to display, and an asynchronous callback function which receives the user's selection (or cancellation). For example:

Framework.SelectFromList([

{value: "close", caption: "Close"},

{value: "reverse", caption: "Reverse"},

{value: "double", caption: "Double", someExtraData: "x2"}

], function (selection) {

// Either null, or one of the objects in the array

});

You can pass three types of list as the first parameter:

A simple array of values, such as [1, 2, 3, 4, 5] or ["Red", "Green", "Blue"]. What you get back as the selection is simply the selected item in the array, such as 3 or "Green".

An array of objects, like the example above. Each object must have a caption, which is what is displayed to the user. What you get back is the entire selected object, such as {value: "double", caption: "Double", someExtraData: "x2"}

Full dialog initialisation, including the ability to set standard properties such as noCancel, containing an items[] array which is then like the example above.

As an example of the third type, the following code displays the same list of options as the example above, but customises the title and adds the noCancel style into the dialog which is displayed to the user:

Framework.SelectFromList({

title: "What shall we do?",

noCancel: true,

items: [

{value: "close", caption: "Close"},

{value: "reverse", caption: "Reverse"},

{value: "double", caption: "Double", someExtraPrivateData: "x2"}

]

}, function (selection) {

// Either null, or one of the objects in the array

});

3.5.5.1Checkbox list

SelectFromList() can also be used to display a list of items with checkboxes, where the user can then select multiple items rather than a single item.

There are two requirements in order to use SelectFromList() in this way: the options for SelectFromList() must include checkboxes:true, and the items[] array must contain objects where each one has id and caption members (in addition to any other data which you want to receive back from the dialog). For example:

Framework.SelectFromList({

title: "Which do you want?",

checkboxes: true,

items: [

{id: "item1", caption: "Item 1"},

{id: "item2", caption: "Item 2", checked: true}, // Checked by default

{id: "item3", caption: "Item3", someExtraPrivateData: "x2"}

]

}, function (selection) {

// Either null, or an array of the checked items

});

The return value from SelectFromList() is an array of the checked items (rather than a single selection).

By default, the dialog's Save button is/remains enabled even if no items are checked (and the return value is then an empty array). If you want to prevent this, and to force the user either to cancel the dialog or to select at least one item, then you can specify checkboxesPreventNone: true in the options for SelectFromList().

3.5.6Menu-list dialogs (with icons, sub-menus etc)

On small screens such as mobile phones, the framework modifies pop-up menus in widgets so that they are instead displayed as a list in a dialog. This way of presenting a choice of values is also available as the Framework.MenuListDialog() function, letting you display a list with multiple levels and with options such as icons for each item.

MenuListDialog() takes two parameters: a definition of the dialog, and a callback function which is notified about initial creation of the dialog and the user's selection (or cancellation of the dialog). For example:

Framework.MenuListDialog({

title: "My list",

items: [

{text: "Item 1", actionData: "some value"},

{text: "Item 2", checkbox: true, separator: true, id: 98},

{text: "Disabled item", disabled: true},

{text: "Has a sub-menu", icon: "xf002", items: [

{text: "Sub item 1", color: "red", id: 37},

{text: "Sub item 2", icon: "xf00d", id: 43}

]},

]

}, function (Msg) {

if (Msg.result && Msg.result.itemData) {

// User made a selection. Selected item is in Msg.result.itemData

}

});

The definition of the list can have a title, and the standard dialog settings such as width and height. It should also have an items[] array, as in the example above, defining the options to display to the user.

Each entry in the items[] array can have the following properties.

Property

Description

text

Text to display for the item

items:[]

Optional array of sub-items, as in the example above. The menu can have any level of nesting: sub-menus which have sub-menus which have sub-menus etc.

disabled

If true, the item cannot be selected, and is visually styled as disabled

unselectable

If true, the item cannot be selected, but without being visually styled as disabled

separator

If true, the framework displays a separator bar between this item and the one before it.

separatorAfter

If true, the framework displays a separator bar between this item and the one after it.

icon

Icon to display next to the item. This can be the hexadecimal code of any Font Awesome icon, prefaced with x. For example, xf34e to display an alarm-clock icon.

checkbox

If not null, the item is given either a ticked (checkbox:false) or unticked (checkbox:true) checkbox icon. If both checkbox and icon are set, then icon takes precedence.

checked

If not null, the item is given either a unfilled (checked:false) or filled (checked:true) radio-button icon. If both checked and icon are set, then icon takes precedence.

color

Overrides the text colour using a normal CSS colour value such as "red" or "#FF0000"

iconColor

Overrides the icon colour using a normal CSS colour value such as "red" or "#FF0000"

backgroundColor

Overrides the background colour for the item's row using a normal CSS colour value such as "red" or "#FF0000"

Each entry in the array can also have any number of other properties for your own reference, such as id, value, action etc. When an item is selected, the full data for the item, including any extra properties such as these, is passed into your asynchronous callback function (as Msg.result.itemData).

Your callback function is called twice: once with initialisation data which gives you the ID of the dialog, and a second time with the result of the dialog: either the user's selection, or cancellation.

The final result callback has a result member, an FXB.Result object, in the Msg. The code of Msg.result will be FXB.ErrorCodes.CANCELLED if the user closed the dialog without a response. If the user selected one of the items then the code will be FXB.ErrorCodes.OKAY and the FXB.Result will have an itemData property which gives you the full data of the selected item from the items[] array. The simplest way to check for a user selection versus cancellation or an initial update from the dialog is therefore like the example above:

if (Msg.result && Msg.result.itemData) {

// User has made a selection

} else {

// Initial update message, or user cancelled the dialog without response

}

3.5.7Displaying a settings dialog

The framework provides an AskSettings() function which lets you display a list of settings such as a combination of numeric and text fields. Note: this function only gives you a final result. You don't get an initial creation message from the dialog which gives you the ability to destroy it.

The settings which you display can potentially be long and complicated, divided up into multiple sections or even multiple pages. A simple example is as follows:

Framework.AskSettings([

{type: "int", caption: "A number field", value: 23},

{type: "text", caption: "A text field", value: "Some text"},

{type: "instrumentId", caption: "A market", value: "GBP/USD"},

{type: "list", caption: "A list of options", value: "value2", options: [

{k: "value1", v: "Option 1"},

{k: "value2", v: "Option 2"}

]}

], function (MsgResult) {

});

Each individual field in the settings can have the following properties:

Property

Description

id

Optional, but helps to identify the field when you receive the results back from the dialog

caption

Label to display next to the field

type

Type of field: one of text, textarea, yesno, list, int, float, color, time, email, instrumentId, timeframe

value

Initial value to pre-populate into the field:

· For a list, this should be the k value of one of the options

For a chart timeframe, this should be the usual ID; a number of seconds.

For a color, this should be a CSS color value such as "darkolivegreen" or "#FF0000"

For a time, this should be a string in the form hh:ss such as "14:18"

options

Only used for a list field. As in the above example, an array of objects which have k and v properties (key, and text to display to the user)

The configuration which you pass into AskSettings() can be any of the following:

An array of fields, as in the example above

An object with a fields[] array

An object with a sections[] array

An object with a pages[] array

If you pass an object rather than a bare array, then you can set a title plus standard dialog options such as noCancel.

Passing an object with a sections[] array lets you break up a large set of fields into multiple sections, to make it easier for the user to understand. For example:

Framework.AskSettings({

sections: [

{

caption: "Section 1",

fields: [

{type: "int", caption: "A number field", value: 17},

]

}, {

caption: "Section 2",

fields: [

{type: "int", caption: "Another number field", value: 46},

]

}

]

}, function (MsgResult) {

});

Your settings can even be split up into multiple pages; a pages[] array. Each page must have a caption and a sections[] array.

What you get back in your asynchronous callback function depends on the input you provide. The Msg which is passed to the callback is always an FXB.Result object. The code will be FXB.ErrorCodes.CANCELLED if the user cancelled the dialog.

If the user saves the settings, then you will receive one of the following inside the Msg object, depending on the input your provided for the dialog:

A pages[] array, repeating the pages[] array which you passed in, and filling in the selected values for each field

A sections[] array, repeating the sections[] array which you passed in, and filling in the selected values for each field

Or, just a fields[] array

3.5.8Custom HTML dialogs

You can get the framework to display a completely custom dialog consisting of your own HTML. This mechanism is very flexible, because you can do bi-directional communication between your widget/script and the dialog while it is open, but it is complicated. It involves you having to assemble a string containing HTML and Javascript inside your code (unless you use a web page hosted externally). In a widget, but not a script or UDIX, it may be simpler to have the widget act as its own dialog.

You create an HTML dialog using Framework.CreateHtmlDialog(). This has some fundamental ground rules:

You can send messages from your widget/script/UDIX to the dialog.

The dialog can send messages to your widget/script/UDIX.

The dialog has no access to the framework. You can send messages to the dialog containing framework data, and the dialog can send you messages asking your widget/script/UDIX to carry out framework actions. But the dialog itself cannot access the framework.

The dialog cannot close itself. It needs to send a message to your widget/script/UDIX, and your widget/script/UDIX then uses Framework.DestroyDialog() to close the dialog.

As an example, take the following widget/script/UDIX code which builds a string containing an HTML document, including a <script> block. This will the HTML of our dialog:

var html = "<html><head>";

html += "<script>";

html += "function SendUpdateToParent(txt) {window.parent.postMessage(txt, '*');};"

html += "window.addEventListener('message', function(event) {";

html += "document.getElementById('lblEquity').innerText = event.data;";

html += "});"

html += "</script>";

html += "</head><body>";

html += "<p>Equity: <span id='lblEquity'>...</span></p>"

html += "<p><button onclick='SendUpdateToParent(\"close-me\");'>Close</button></p>";

html += "</body></html>";

Various things are happening here:

The <script> in the HTML defines a SendUpdateToParent() function. This sends messages from the dialog to your widget/script/UDIX, using the standard browser window.parent.postMessage().

There is a button in the page which uses SendUpdateToParent() to send the message "close-me" to your widget/script/UDIX.

The Javascript in the HTML sets up a listener on window.message. This receives updates from your widget/script/UDIX. In this basic example, the handler simply dumps the content of a message into the <span id="lblEquity"> in the page. In a more sophisticated example you might send a message to the HTML by building some JSON and doing JSON.stringify() on it, and the dialog might parse it with JSON.parse(event.data) and then inspect the contents.

You can then create such a dialog as follows:

Framework.CreateHtmlDialog({

title: "My dialog",

html: html

}, function(Msg) {

if (Msg.result) {

// Dialog has been closed/cancelled. Remove the timer which we set up earlier.

clearInterval(Framework.$myTimer);

} else if (Msg.update) {

// Some kind of message from the dialog, via window.parent.postMessage()

if (Msg.update == "close-me") {

// Dialog is asking to be closed

Framework.DestroyDialog(Msg.widgetId);

} else {

// Some other message

}

} else {

// If not a result and not an update, then this is the initial message

// giving us the dialog's ID (in Msg.widgetId).

// As a simple example, we will send the dialog the current account equity

// once per second.

setInterval(function() {

Framework.$myTimer = Framework.SendHtmlDialogUpdate(Msg.widgetId, Framework.Account.equity);

}, 1000);

}

});

Again, a number of things are happening in this widget/script/UDIX code:

The asynchronous callback handler from CreateHtmlDialog() can receive a number of types of message.

If the Msg parameter contains a result, then the dialog has been closed/cancelled.

If the Msg contains an update, then it is a message from the dialog Javascript, issued using window.parent.postMessage() as illustrated above. The example code inspects the text of the message from the dialog. If the dialog is saying "close-me", then the code closes the dialog using DestroyDialog().

If the Msg contains neither an update nor a result, then it is the initial creation message about the dialog. What the code does in this example is to set up a timer which, every second, sends the dialog the current value of account equity using Framework. SendHtmlDialogUpdate(). The dialog receives this in its window.message handler, and puts the value into the <span id="lblEquity"> as described above.

All this is obviously not a very realistic example in itself. But exactly the same principles used here can be extended to create very feature-rich dialogs, all ultimately based on the same message-exchange demonstrated above.

Finally, in addition to the title and html properties used above, there are further settings which you can supply when creating the dialog:

Property

Description

url

If you supply a url, that remote address is loaded into the dialog (instead of an HTML string). Allows you to host the content for a dialog on an external server rather than building it inside your widget/script/UDIX. Like all other resources, such a page must be hosted at an https:// address.

noTitle

If noTitle:true is set then the dialog is not given a title bar. Your HTML/url content takes up the whole dialog window.

noStandardCss

By default the framework will insert its own CSS into your HTML (not applicable if you use a url), in order to make the styling consistent with the rest of Figaro. You can prevent this using noStandardCss:true.

buttons

You can optionally give the dialog standard buttons, in a footer, in exactly the same way as for dialogs such as Ask(). These buttons will close your dialog in the usual way, sending back a Msg.result containing the value of the clicked button.

3.5.9Widget displaying itself as its own dialog

This section only applies to widgets. It does not apply to scripts or UDIXes because they have no web page.

A widget can display itself as a dialog. This can be simpler than using CreateHtmlDialog(). For example, your widget can have HTML as follows:

<html>

<head>…</head>

<body>

<div id="NormalContent">

<!-- Normal widget content -->

</div>

<div id="DialogContent">

<!-- Different content when displayed as a dialog -->

</div>

</body>

</html>

On start-up, typically in OnLoad(), you check the mode. If the widget is being displayed as its own dialog then you hide the #NormalContent and show the #DialogContent. For example:

Framework.OnLoad = function() {

// Check the mode. See below…

if (Framework.privateSettings.asDialog) {

// Dialog mode

// Hide the normal content and show the dialog content

document.getElementById("NormalContent").style.display = "none";

document.getElementById("DialogContent").style.display = "block";

// Do initialisation for dialog mode

} else {

// Normal mode

// Show the normal content and hide the dialog content

document.getElementById("NormalContent").style.display = "block";

document.getElementById("DialogContent").style.display = "none";

// Do normal initialisation

}

};

From normal mode, you display the widget as its own dialog using CreateDialog(). For example:

Framework.CreateDialog({

// Use own type as the type for the dialog

type: Framework.type,

// Pass settings to the dialog. These are received as Framework.privateSettings

// The asDialog flag signals to the widget's own code that it is in dialog mode

// rather than normal mode, as illustrated above. The settings:{} block can

// contain any further data which you want to pass into the dialog

settings: {

asDialog: true

}

}, function (Msg) {

// Asynchronously receives a result, plus any updates, from the dialog

// (Behaves just like the built-in dialogs described in the sections above)

});

In other words, the main widget passes settings to the dialog containing asDialog:true (and can include any other initialisation data which it wants). The widget running as a dialog receives these settings in Framework.privateSettings, triggering it to enter dialog mode as in the example above.

The dialog can send a result back to the main widget using Framework.SendDialogResult(). Using SendDialogResult() automatically closes the dialog. The result data, passed as a parameter to SendDialogResult() can be anything, but standard behaviour is for it to be an FXB.Result object. For example:

// Create FXB.Result object

var result = new FXB.Result(0); // Can be initialised with 0 (okay) or any error code

// Add in any proprietary data to the FXB.Result. For example:

result.myData = {

someKey: someValue

};

// Send the result back to the creator. This closes the dialog.

Framework.SendDialogResult(result);

The dialog can cancel itself, rather than sending a result, using Framework.CancelDialog().

The dialog can also send interim updates back to the creator, using Framework.SendDialogUpdate(). Again, the parameter for SendDialogUpdate() can be anything, but standard behaviour is for it to be an FXB.Result.

The main widget receives information from or about the dialog in its asynchronous callback function for CreateDialog(). Like the standard dialog usage described above, it gets an initial message which provides the ID of the dialog and then receives any interim updates followed by a final result. For example:

Framework.CreateDialog({ … }, function (Msg) {

if (Msg.result) {

// If Msg has a .result, then the dialog has now closed (or been dismissed)

} else if (Msg.update) {

// If Msg has an .update, then the dialog is using SendDialogUpdate()

} else {

// If neither .result nor .update, then it's the initial message about creation

}

});

(It's also possible for the main widget and the instance acting as a dialog to exchange other messages, as well as using the standard SendDialogUpdate() and SendDialogResult(). The dialog gets the ID of the main widget as Framework._parentWidget. The main widget gets the ID of the dialog in the first message to the asynchronous callback function. Having each other's IDs allows the main widget and dialog to exchange messages as described below.)

3.5.10Displaying the platform's standard deal ticket

You can display the platform's standard deal ticket, instead of placing an order directly in your own code. Figaro has two related but separate trading dialogs:

The dealticket. This handles placement of new orders, and also has a subsidiary panel for carrying out actions across a whole market (e.g. closing all EUR/USD trades).

The trading-action. This embeds the dealticket, and provides additional options for carrying out actions on the whole account such as flattening all trades and orders.

You can open either window, with no pre-population, using very simple calls to Framework.CreateDialog(). For example:

Framework.CreateDialog({type: "dealticket"});

Framework.CreateDialog({type: "trading-action"});

In both cases, CreateDialog() can have an asynchronous callback function which receives an update when the dialog is created, and a final result when the dialog closes itself. For example:

Framework.CreateDialog({type: "dealticket"}, function (Msg) {

if (Msg.result) {

// Final result of the dialog, which is now closed

} else {

// Initial update, providing the ID of the dialog

}

});

Both dialogs can be pre-populated with the following optional settings:

Property

Description

instrumentId

Initial instrument to set in the dialog

orderDefinition

A complete order definition to pre-populate into the dialog. The possible properties of orderDefinition are simply the same as passing a request to SendOrder(). If both are present, orderDefinition takes precedence over instrumentId. You can pass a wider range of orderDefinition values into the trading-action than into the dealticket. The dealticket can only pre-populate with an instruction to open a new trade or order, whereas the trading-action can pre-populate with anything which can be passed to SendOrder().

handleExecution

Defaults to true. If set to false, the dialog returns an orderDefinition to you, in its result, rather than executing the trading action itself.

message

A brief message to display at the top of the dialog

For example:

// Display a suggested trade, with an accompanying message, and get the

// user's selection back from the dealticket rather than having the order

// executed by the dealticket

Framework.CreateDialog({

type: "dealticket",

settings: {

orderDefinition: {

tradingAction: FXB.OrderTypes.SELL,

instrumentId: "AUD/USD",

volume: 30000,

sl: {pips: 20}

},

handleExecution: false,

message: "This is my suggested trade"

}

}, function (Msg) {

if (Msg.result && Msg.result.orderDefinition) {

// Dialog is passing back the user's settings rather than executing them itself

} else {

// Initial update, or cancellation of the dialog

}

});

The dealticket has further settings which can be used to customise it:

Property

Description

allowInstrumentChange

Defaults to true. If false, prevents the user from changing the initial market (which you set using instrumentId or orderDefinition)

noPosition

If true, the dealticket does not display the current-position indicator and the secondary panel for carrying out actions on the whole position. Turning off allowInstrumentChange automatically turns on noPosition, and means that the deal ticket can only be used for placing new orders, not for closing existing orders on the selected market.

fullMode

By default the dealticket selects between its "simple" and "full" modes depending on whether the pre-populated or default order definition can be represented in simple mode. You can force the deal-ticket to open in full mode by setting fullMode:true

noToolsIcon

Can be used to turn off the settings icon in the dealticket, preventing a switch between simple and full mode, and preventing access to saved order templates

3.5.10.1Selecting an order template

You can ask the user to select a saved order template using ChooseOrderTemplate(). The template can then be passed into the deal ticket, as the orderDefinition, pre-populating the deal ticket. For example:

Framework.ChooseOrderTemplate(null, function(template) {

// Asynchronously receives the selected template, or null

if (template) {

// Potentially modify or add to the template

// …

// And then pass the template into the deal ticket

Framework.CreateDialog({

type: "dealticket",

settings: {

orderDefinition: template

}

});

}

});

Alternatively, you can add an instrumentId into the template and then execute the order yourself, without displaying a deal ticket:

Framework.ChooseOrderTemplate(null, function(template) {

// Asynchronously receives the selected template, or null

if (template) {

// Put the required market into the template

template.instrumentId = <some market ID>;

// And then pass the template into the deal ticket

Framework.SendOrder(template, function (MsgResult) {

// … handle asynchronous success or failure

});

}

});

The first parameter for ChooseOrderTemplate() is an optional block of settings which can contain the following properties:

Property

Description

showNone

Adds a "None" button to the selection dialog. Allows you to distinguish between the user cancelling the dialog versus saying "I don't want to use a stored template". If the button is clicked then the return value from ChooseOrderTemplate() is the string "none" (rather than a template object).

showDefault

Similar to showNone, but with different styling. Displays a "Default" button rather than a "None" button, and returns the string "default".

There is no functional difference between showNone and showDefault. Choosing to use the term "Default" is slightly clearer to the user that, if nothing is explicitly pre-populated into the deal ticket, then the deal ticket will automatically select any user-defined default template. For example:

Framework.ChooseOrderTemplate({showDefault: true}, function(template) {

if (template == "default") {

// User clicked the Default button.

// Display the deal ticket. A blank or omitted orderDefinition will mean

// that the deal ticket automatically selects any user-defined default

Framework.CreateDialog({

type: "dealticket"

});

} else if (template) {

// User selected a template

// Pre-populate the selected template into the deal ticket

Framework.CreateDialog({

type: "dealticket",

settings: {

orderDefinition: template

}

});

} else {

// (User cancelled the dialog)

}

});

3.5.11Notifying the user by generating mail

It is not strictly a dialog, because there is no direct user response, but it is worth noting in this section that it is possible to generate alerts and notifications to users by sending internal mail within Figaro.

For example, a widget, script, or UDIX can notify the user of an important event by generating mail with priority 2 or even 3 - as described below.

3.5.12Toast

"Toast" is the common, standard terminology for small notifications which are displayed at the edge of the screen, and which automatically disappear after a few seconds. Toast is used by the Figaro app itself if the user turns on options in the Notifications section of the app's settings.

3.5.12.1Creating toast

A widget, script, or UDIX can create its own toast pop-ups using Framework.CreateToast(). For example:

Framework.CreateToast({

title: "Finished!",

text: "The task has been completed",

icon: "f05a"

});

The standard properties of the toast definition 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 "f05a" for an info icon. (Not compulsory.)

Framework.CreateToast() can have a callback function. This is called if the user clicks on the toast notification and/or when the toast is destroyed. For example:

Framework.CreateToast({

title: "Finished!",

text: "The task has been completed",

icon: "f05a"

}, function (ToastMsg) {

if (ToastMsg.clicked) {

// User has clicked on the toast

} else {

// The toast is being destroyed

}

});

The ToastMsg which is passed to the callback function has the following properties:

Property

Description

clicked

Indicates whether the message is the result of a click. If not, then it means that the toast is being destroyed and no further messages will be sent.

toastId

ID assigned to the toast

toast

Definition of the toast, as originally passed in to CreateToast()

By default, a toast notification is destroyed if the user clicks on it. Therefore, unless you modify the standard behaviour using the properties described below, if the user clicks on the toast then there will be two messages: a click message with clicked:true and then a destruction message with clicked:false.

Other properties which you can use to modify the toast are as follows:

Property

Description

lifespan

Lifespan of the toast in seconds, overriding the app's default

toastId

Optional ID to assign to the toast, so that the toast has a known ID which can be manually destroyed. Callers are recommended to use FXB.utils.GenerateGuid().

noDestroyOnClick

Prevents the toast being automatically destroyed when the user clicks on it. The caller can destroy it manually (rather than waiting for its natural expiry) by implementing a click callback and using that to destroy the toast.

toastCreationGroup

Ignored if blank/null. Suppresses creation of this new toast if there are any existing items with the same toastCreationGroup value.

toastReplacementGroup

Ignored if blank/null. The opposite of toastCreationGroup. If there are any existing items with the same toastReplacementGroup value, they are destroyed (and replaced by this new toast).

toastClickGroup

Ignored if blank/null. Automatically destroys all the items with the same toastClickGroup value when any one of them is clicked on.

toastClass

Optional display class for the toast, changing its formatting. Currently available values are ToastSuccess and ToastError.

dialog

Optional description of a dialog to display in response to a click on the toast. See below.

3.5.12.2Dialogs from toast clicks

There are two ways of displaying further information in response to toast clicks:

Implement a callback function, as illustrated above, and manually display a dialog in response to a message with clicked:true

Or, provide a dialog definition as part of the toast details. This automatically displays a dialog when the toast is clicked on.

An example of a dialog definition:

Framework.CreateToast({

title: "Something happened",

text: "The text of the toast",

dialog: {

type: "infodialog",

settings: {

text: "More detail to display when the toast is clicked on"

}

}

});

You can use the dialog block to create any of the common dialogs in response to toast clicks. Note: these dialogs are always and automatically made floating rather than modal.

The type for the dialog block is one of the following: infodialog, alertdialog, successdialog, or errordialog.

The settings block is simply the same properties which are passed to a function such as InfoDialog(): text, plus an optional title and button definitions.

3.5.12.3Destroying toast

The normal lifecycle is that a toast notification is destroyed when either of the following happens:

The user clicks on it

The toast reaches its expiry time (either the app's default of a few seconds, or an explicit lifespan provided when creating the toast)

There can be two exceptions to this lifespan:

The toast is given noDestroyOnClick:true in its properties. A click does not destroy the toast, and it survives until its expiry.

And/or, the toast is given a long lifespan, keeping it on screen for a long time.

In either of these circumstances, the caller may want to remove the toast manually, using Framework.DestroyToast(). For example:

// Note: no callback. DestroyToast() is fire-and-forget.

Framework.DestroyToast("1234-5678-abcd");

The ID of the toast, for passing to DestroyToast(), can be obtained in two ways:

By providing an explicit toastId in the call to CreateToast()

Using the toastId in the click callback function

For example, to display a permanent toast until the user clicks on it (replacing the automatic destruction in the event of a click):

Framework.CreateToast({

title: "Permanent…",

text: "Stays on screen until manually destroyed",

noDestroyOnClick: true,

lifespan: 999999999

}, function (ToastClickMsg) {

… some asynchronous process during which the toast remains on screen,

and then …

Framework.DestroyToast(ToastClickMsg.toastId);

});

3.6Settings and state storage

A widget, script, or UDIX can use standard browser features such as indexedDB to store and retrieve data. Remember that scripts and UDIXes are web workers, and therefore the browser does not give them access to localStorage. Only widgets can use that. You can, of course, also store data on external servers, sending and receiving it using something like XMLHttpRequest.

The framework also has its own storage mechanisms which a widget, script, or UDIX can use to store settings - saving and receiving back any JSON data. There are two important things to note about these framework features:

They are designed for storing small amounts of settings information, such as a user's most recent selection of market and timeframe etc. You should not use them to store large amounts of data (e.g. candles). The framework may ignore requests where the stringified size of your data exceeds 4KB.

You should not make rapid repeated calls to the storage functions. The framework has its own internal throttles, but instead of making lots of save-calls in quick succession you should implement a delay/queue yourself, and/or redesign your UI so that changes to settings are more monolithic and occasional.

3.6.1Types of framework storage: private and category

The framework has two types of storage: "private" and "category".

"Category" settings are data which is shared by all copies of your widget, script, or UDIX.

"Private" settings only apply to widgets, not to a script or UDIX, and only apply to widgets which have been loaded into the Figaro page container, not to a widget which has been opened in a temporary floating or pop-up window.

You can think of "private" settings as "instance" settings. If a widget is loaded into the page container then it may be loaded and unloaded as the user switches between pages, or when the user logs out and logs back in again, but it remains the same instance of that widget. Whereas the same type of widget loaded into a new or temporary window is a different instance in each temporary window.

Each time a script or UDIX is loaded, it is treated as a new instance of that script/UDIX. Therefore, scripts and UDIXes don't have private settings.

3.6.2Loading and saving category settings

You can load category settings using Framework.LoadCategorySettings(), passing the function a name which you want to identify your widget/script/UDIX. For example:

Framework.LoadCategorySettings("my-widget", function(settings) {

// Asynchronously receives existing settings, or an empty object {}

// The retrieved settings are also now available thorough Framework.categorySettings

});

As well as being sent to your asynchronous callback function, the settings data is also stored in Framework.categorySettings.

You must use LoadCategorySettings() before trying to save settings, even if you know that there are no stored settings and that it will return an empty object, {}. This is because the parameter which you pass to LoadCategorySettings() also identifies your widget/script/UDIX for later saving of data.

You should regard the ID which you use in LoadCategorySettings() as mildly sensitive. If someone else's code knows the ID which you are using, it can read and overwrite your data - which might, for example, include a password which you have asked the user for, in order to log in to some external service.

You can save category settings by using Framework.SaveCategorySettings(). You simply pass in the block of data which you want to store. For example:

Framework.SaveCategorySettings({

instrumentId: "EUR/USD",

timeframe: 3600,

visualOptions: {

color: "blue"

}

});

If there are multiple running copies of your widget, script, or UDIX, then they all - including the one which makes the change - receive a CATEGORY_SETTINGS_CHANGE message telling them about the update.

For example, let's say that you have some sort of "view" mode in your widget, switchable between mode A and mode B. If the user changes the setting in one widget, you want all other running copies of your widget to update automatically. You can do the following:

In the widget where the user has changed the selection, you use SaveCategorySettings() to store the new settings.

But you don't act on the change immediately.

Instead, you wait for the CATEGORY_SETTINGS_CHANGE message, and both the widget making the change plus any other copies of your widget all have the same single path of execution where they update themselves in response to a change in settings.

However, one final note: you should not use SaveCategorySettings() as a way of communicating between widgets (and scripts and UDIXes) unless you specifically need permanent storage of the data. If you just want to send transient information between widgets, there are better and (even) faster ways of communicating.

3.6.3Loading and saving private settings

As explained above, private settings only apply to widgets, not to a script or UDIX, and they only apply to a widget which has been loaded into the Figaro page container. Other types of component can attempt to save private settings for themselves, but the data will be quietly discarded by the framework.

A widget can save private settings using Framework.SavePrivateSettings(). For example:

Framework.SavePrivateSettings({

instrumentId: "EUR/USD",

timeframe: 3600,

visualOptions: {

color: "blue"

}

});

When this instance of the widget is next reloaded by the page container, any existing saved settings will be present in Framework.privateSettings. (Unlike category settings, private settings are immediately and automatically available, and do not need a separate asynchronous load.)

3.6.4Combining category settings and private settings

A widget may have both private settings and category settings, and it is entirely up to you how you combine them. A typical - but absolutely not compulsory - path of execution would be:

If privateSettings are not empty, use those

Load category settings

If categorySettings are then not empty, use those

Otherwise, use defaults

Note: if your settings involve a selection of market or timeframe, you may want to include an extra step: respecting context from the Figaro page container.

The only extra consideration then is that, even if there are private settings, you may still want to call LoadCategorySettings() anyway, because you need to call LoadCategorySettings() first if you ever want to save category settings.

For example, using FXB.utils.IsEmpty() as a helper to check for empty objects:

Framework.OnLoad = function() {

// Start with a null value

Framework.$myState = null;

// If private settings exist, set the record of the state

if (!FXB.utils.IsEmpty(Framework.privateSettings)) {

Framework.$myState = Framework.privateSettings;

}

// Always do a LoadCategorySettings(), even if we have private settings

Framework.LoadCategorySettings("my-widget", function(categorySettings) {

if (Framework.$myState) {

// If we already have private settings, ignore the category settings

} else {

// Put the category settings - which may be empty - into the state

Framework.$myState = categorySettings;

}

// If myState is still empty, because we have neither private nor category

// settings, then use some defaults

if (FXB.utils.IsEmpty(Framework.$myState)) {

Framework.$myState = { … some defaults … };

}

// And, finally, do UI initialisation based on the settings or defaults

Framework.$UpdateMyUI();

});

};

3.6.5Widget state (for new windows etc)

The user can choose to take any widget in the Figaro page container and pop it out into a new browser window. You can copy the settings from the existing widget into the new window using a mechanism very similar to, and often related to, privateSettings.

You need to handle Framework.OnGetState() which the framework calls in order to ask you, "Do you have any stored settings which you want to pass into the new window?". For example:

Framework.OnGetState = function()

{

return {

instrumentId: "EUR/USD",

timeframe: 3600,

visualOptions: {

color: "blue"

}

};

}

The new pop-up browser window will receive this stored state in its Framework.privateSettings.

As a result, it's very common for a widget's storage of private settings and its handling of OnGetState() to be linked. For example:

The widget uses OnGetState() as a general function for building a representation of its current settings.

When saving private settings as a result of a user change, the widget calls its own OnGetState() function.

For example:

// Build some representation of the widget's state. This is returned both internally

// and to the framework when opening a new window.

Framework.OnGetState = function() {

var settings = { … };

return settings;

};

// Private internal function which our widget calls whenever the user makes a change

// and new settings need to be saved. We simply re-use OnGetState() as a way

// of getting the current state.

Framework.$SaveMySettings = function() {

Framework.SavePrivateSettings(Framework.OnGetState());

}

Note: OnGetState() is also the mechanism which allows a widget to keep running even if the user navigates to a different page of the app.

3.6.6Saving both private and category settings

It is very common for a widget's private and category settings to be the same thing. For example, if you make a change in Figaro's standard quote board such as switching the view between grid and deal tickets then that doesn't alter other open quote boards - they ignore CATEGORY_SETTINGS_CHANGE - but the most recent changes do become the default for all new future instances of the quote board.

In other words, on a change of settings, it's very common for a widget to do the following:

var newState = Framework.OnGetState(); // See example above

Framework.SavePrivateSettings(newState); // Private settings for this instance

Framework.SaveCategorySettings(newState); // Use same settings as defaults for new instances

This can be made slightly more efficient by combining the two saves into a single call to Framework.SaveSettings(). This takes two parameters: private settings first, category settings second. The above example would then become:

var newState = Framework.OnGetState();

Framework.SaveSettings(newState, newState);

3.7.1Handling context - synchronization in the page container

This section is only applicable to widgets. A script or UDIX cannot (directly) be part of the Figaro page container.

If your widget offers a selection of market or timeframe, you may want to respect "context" where the user can choose to synchronize the markets and/or timeframes on a page. Doing this consists of three parts:

Reading any context when your widget starts up

Handling changes of context while your widget is running

Sending changes of context to the page container if the user changes the market or timeframe selection from inside your widget

Any existing context on start-up is provided by the Framework.context property. If present, this may contain one or both of an instrumentId and a timeframe. The typical way of handling this is to inspect it and, if present, to use it to override any private or category settings which the widget has.

The example above about widget settings has the following code:

... code which examines private and category settings, and builds Framework.$myState …

// And, finally, do UI initialisation based on the settings or defaults

Framework.$UpdateMyUI();

A typical way of handling context would be, as a final stage after loading stored settings or using defaults, to overwrite any market or timeframe selection if the page container is providing context. For example:

... code which examines private and category settings, and builds Framework.$myState …

// The addition:

// If the page container has instrument context, overwrite whatever settings we have built up

if (Framework.context && Framework.context.instrumentId) {

Framework.$myState.instrumentId = Framework.context.instrumentId;

}

// And, finally, do UI initialisation based on the settings or defaults

Framework.$UpdateMyUI();

There may also be changes in context while your widget is running, as a result of the user changing the market and/or timeframe selection in another widget. You receive these changes as a WIDGET_CONTEXT_CHANGE message, and you would typically handle it the same way as the user changing the selection within your own widget.

Finally, if the user changes the market and/or timeframe selection within your own widget, using your own UI, you broadcast that change to any other widgets in the same page as yours. You do this using Framework.ReportContext(). For example:

Framework.ReportContext({

instrumentId: <new market selection>,

timeframe: <new timeframe selection>

});

3.7.2Changing the market or timeframe of the current page

Any widget, script, or even UDIX can change the context of the current page in the Figaro container without being part of the current page, using the functions Framework.SetCurrentPageInstrument() and Framework.SetCurrentPageTimeframe().

For example, the following script sets up a "slideshow", cycling the current page between different markets every 5 seconds until the script is terminated by the user:

var instrumentList = ["EUR/USD", "USD/JPY", "GBP/USD", "USD/CHF"];

var currentIndex = -1;

setInterval(function() {

currentIndex++;

if (currentIndex >= instrumentList.length) currentIndex = 0;

Framework.SetCurrentPageInstrument(instrumentList[currentIndex]);

}, 5000);

3.7.3Changing the current page displayed in Figaro

You can change the current page which is being displayed in Figaro using Framework.NavigateToPage().

The parameter for this function is the user-controlled name of the page, such as "Quote board", or "My new page". There is no mechanism for saying "go to the quote board, whatever the user has called the page", or "go to the first/any page which contains the quote board widget".

3.7.4Chart actions

Figaro provides functions which allow any widget, script, or UDIX to make changes either to all open charts, or just to the active chart (the one which the user last clicked on). This is done using Framework.ChartChange().

The Run Script widget in Figaro serves as a full and interactive guide to the available chart actions. You can get information on how to build chart-change code by using the options in the Run Script widget.

3.7.5Chart widgets

A widget can be loaded as a "chart widget", into the chart's dock area as used by the built-in Market Timer and Notepad indicators. You do this by choosing Add Indicator > External Widget from a chart. Widgets which are loaded in this way have access to some extra features, letting them behave like user-defined indicators (UDIs). They can create drawings, event markers, or bar highlights on the chart.

You can detect whether your widget is loaded as a chart widget by checking privateSettings.isChartWidget. If this is not turned on, the following features will not be available, and attempting to use a function such as ChartWidget_CreateDrawing() will raise an exception.

3.7.5.1Getting and setting the chart instrument and timeframe

Chart widgets receive the chart's selected instrument and timeframe in their Framework.context, and subsequent changes are similarly received as a WIDGET_CONTEXT_CHANGE message. Your widget can also detect the selected instrument and timeframe, together with a list of the chart's candles, using a ChartWidget_OnContextChange() handler.

A chart widget can change its chart's instrument and/or timeframe by using ReportContext().

3.7.5.2Chart widget dock title

Each component in the chart's dock area has a title, and by default the platform will use the <title> from your HTML. Your widget can change its title while it is running by using Framework.ChartWidget_SetTitle().

3.7.5.3Chart widget height

By default, chart widgets take up all available space in the dock. If you instead want to use a fixed height, like the built-in Market Timer indicator, then you can do this by passing a number of pixels to Framework.ChartWidget_SetHeight().

3.7.5.4Chart bar data and events

Chart widgets can receive the chart's bar data by defining a ChartWidget_OnContextChange() handler. The parameter which is passed to this function contains a candles[] array, as well as an instrumentId and timeframe. The handler is called on initial load of your widget, followed by any addition to the list of candles or any re-load following a change of instrument or timeframe.

Changes to the current in-progress candle can be received using a ChartWidget_OnCandleChange() handler. The parameter passed to this function contains a single candle object.

For example:

Framework.ChartWidget_OnContextChange = function(data) {

// Store the list of candles in data.candles[]

}

Framework.ChartWidget_OnCandleChange = function(data) {

// Update the previous stored list from data.candle

}

3.7.5.5Drawings, event markers, and bar highlights

Chart widgets have similar features to user-defined indicators. They can create drawings, event markers, bar highlights, and additional HTML on the chart. For example:

Framework.ChartWidget_CreateDrawing({

type: "horizontalLine",

points: [{value: 1.2345}],

style: {line: {color: "red"}}

});

These functions are described in detail in the UDI documentation at https://www.fxblue.com/figaro/udi/help. Each UDI function has an equivalent framework function, prefaced with ChartWidget_.

UDI function

Chart widget function

createDrawing

ChartWidget_CreateDrawing

changeDrawing

ChartWidget_ChangeDrawing

removeDrawing

ChartWidget_RemoveDrawing

removeAllDrawings

ChartWidget_RemoveAllDrawings

createEventMarker

ChartWidget_CreateEventMarker

removeEventMarker

ChartWidget_RemoveEventMarker

removeAllEventMarkers

ChartWidget_RemoveAllEventMarkers

createBarHighlight

ChartWidget_CreateBarHighlight

removeBarHighlight

ChartWidget_RemoveBarHighlight

removeAllBarHighlights

ChartWidget_RemoveAllBarHighlights

setBackground

ChartWidget_SetBackground

createHTML

ChartWidget_CreateHTML

removeHTML

ChartWidget_RemoveHTML

sendHTMLMessage

ChartWidget_SendHTMLMessage

A few further notes:

Like the UDI functions, framework functions such as ChartWidget_CreateDrawing() can be given either a single item as a parameter or an array.

Like the UDI functions, you can pass a callback to ChartWidget_CreateDrawing() and ChartWidget_CreateEventMarker() in order to receive the ID(s) of the created drawing(s) or marker(s).

If you set wantClick:true in your definition for an event marker, clicks on the marker are sent to a ChartWidget_OnEventMarkerClick() handler.

Messages sent by HTML which you create are sent to a ChartWidget_OnHTMLMessage() handler.

3.8Communicating between widgets/scripts/UDIXes

Different instances of a widget, script, or UDIX can share saved "category" settings as described above, with notifications when the shared settings change.

It is also possible for different widgets, scripts, and UDIXes to send messages to each other without permanent data storage. This comes in three parts.

Every widget, script, or UDIX has an ID, Framework.id.

A component which is interested in talking to another component sends out a broadcast message.

The other component(s) receive the broadcast message, in OnMessage(), and can see the sender's ID. They can now send a direct message back. On receipt of a direct message (or a broadcast message), the original sender can now see the recipient's ID, and send further direct messages.

You send messages using Framework.PostMessage(). The only compulsory parameter is an FXB.Message object, which you can construct as follows. The initialisation must have a msgType property, and can then contain any other data that you want.

The framework has special message types for widgets to exchange ad hoc data. There are BROADCASTx messages which can be sent without knowing a recipient's ID, and GENERICx messages which can be used for direct, peer-to-peer communication.

For example:

var myMessage = new FXB.Message({

msgType: FXB.MessageTypes.BROADCAST1, // A msgType is compulsory for FXB.Message

myAction: "Want to chat?",

anyOtherData: {

moreData: 231,

},

someProperty: someValue

});

Framework.PostMessage(myMessage);

Broadcast messages are sent to all other widgets, scripts, and UDIXes in the platform. Those can then listen for broadcasts in OnMessage(), and choose to respond. For example:

Framework.OnMessage = function(Msg) {

if (Msg.is(FXB.MessageTypes.BROADCAST1)) {

// Is this a message which we understand?

if (Msg.myAction && Msg.myAction == "Want to chat?") {

// Send a response

var response = new FXB.Message({

msgType: FXB.MessageTypes.GENERIC1,

_to: Msg._from, // The _from of the Msg gives us the sender's ID

myResponse: "Yes, I want to chat"

});

Framework.PostMessage(response);

}

}

};

When a component receives a message, the _from property tells it the sender's ID. This gives it the ability to construct a direct message back to the sender, by setting the _to property in a response message - as in the example above.

The original sender could then listen for acceptance of its original broadcast as follows:

Framework.OnMessage = function(Msg) {

if (Msg.is(FXB.MessageTypes.GENERIC1)) {

// Is this a message we understand?

if (Msg.myResponse && Msg.myResponse == "Yes, I want to chat") {

// The recipient of broadcast knows this sender's ID,

// and this sender now knows the responder's ID because

// it is the _from of this response message

}

}

};

Once a direct connection is established, there is a further mechanism which can simplify the sender/response code. The Framework.PostMessage() function has an optional second parameter: an asynchronous callback which receives responses. This is called if a recipient of a message uses Framework.Respond().

For example, once they have each other's IDs, one component can send a message to another component as follows:

Framework.PostMessage(myNewMessage, function (Msg) {

// Callback triggered by the recipient using Respond().

// The response message also passes through OnMessage(), but handling it "inline"

// like this will often be simpler and cleaner.

});

The recipient triggers the response callback in the sender by using Framework.Respond() instead of Framework.PostMessage(). The Respond() function takes two parameters: the new message to send back, and the message to respond to. For example:

var myResponse = new FXB.Message({

msgType: FXB.MessageTypes.GENERIC1,

myData: "some communication"

});

Framework.Respond(myResponse, someOriginalMessage);

(If you are using Respond(), you don't need to set an explicit _to on the message you are sending. Respond() fills this in for you.)

The one thing to note is that an asynchronous callback for PostMessage() is only called once (or not at all, if nothing responds). If you use an asynchronous callback with a broadcast message, then it will only receive the first response, and be terminated after that. If there are second and subsequent responses from other components, then those will be ignored (unless also separately handled in OnMessage).

In other words: an asynchronous callback on PostMessage() is great for direct, peer-to-peer messages, but generally not suitable for broadcast messages unless you are specifically only expecting a single response.

3.9Time conversion functions

All times in the framework - such as the openTime of an order or the ts of a candle - are UTC. The framework provides a variety of functions for helping with time conversion.

Note: all the parameters and return values for the framework's date functions are numbers (of milliseconds), not Javascript Date objects.

3.9.1Time zone definitions

The framework uses the following properties to define a timezone:

Property

Description

offset

Base offset in minutes from UTC, e.g. 120 (UTC+2) or -300 (UTC-5)

dstMode

Daylight savings schedule. One of the values of the FXB.DSTModes enumeration: NONE, USA, EUROPE, AUSTRALIA

For example, the following defines a base offset 2 hours ahead of UTC, changing into daylight savings (UTC+3) on the USA schedule:

timezone: {

offset: 120,

dstMode: FXB.DSTModes.USA

}

Note: this is a time zone widely used in forex trading, but not a setting you will ever see on a normal clock. It is widely referred to as "central European time" or "Cyprus time" (because many fx brokers are based there), but that is not what it is. It is 2/3 hours ahead of UTC, but it changes on the USA daylight-savings schedule, not the European schedule. It will be the same as Cyprus time for most of the year, but it will be different during the periods when the USA is in daylight-savings but Europe is not.

3.9.2Getting the current time - Framework.Now()

The Framework.Now() function returns current UTC time, in milliseconds, according to the computer clock.

It is therefore almost entirely equivalent to new Date().valueOf(). However, the framework includes a time check with the Figaro server, and watches for computer clocks which are materially wrong (current threshold: >30 seconds wrong). One of the most common issues it picks up is a clock which the user has changed to local time without adjusting the computer's time zone. The result is that the computer clock shows the "correct" local time to the user, but its calculation of UTC is hours out.

If the framework detects a problem with the local clock, Framework.Now() will return an adjusted time, correcting for errors in the local clock.

3.9.2Converting between UTC and a different time zone

The function Framework.AdjustUTCToZone() converts from UTC to a different time zone. It takes two parameters: a UTC time in milliseconds and a timezone description. For example:

var someDate = new Date(Date.UTC(2020, 6, 1, 0, 0, 0)); // July 1st 2020, midnight UTC

var newYork = Framework.AdjustUTCToZone(someDate.valueOf(), {

offset: -300, /* USA east coast at UTC-5 with daylight savings */

dstMode: FXB.DSTModes.USA

});

This will return 1593547200000. Javascript has no way of specifying that a Date object belongs to a particular zone. What AdjustUTCToZone() has to do is to return a date/time where the UTC functions such as getUTCHours() will give the adjusted time.

To put it another way, 1593547200000 represents June 30th 8pm UTC - the original time adjusted backwards by 4 hours (not 5 hours, because the USA was in daylight savings on the specified date). For example:

var date = new Date(1593547200000);

var hours = date.getUTCHours(); // Returns 20

var dayOfMonth = date.getUTCDate(); // Returns 30

var month = date.getUTCMonth(); // Returns 5 (June)

You can do the conversion in the opposite direction using Framework.AdjustZoneToUTC(). You pass in a number of milliseconds which represents a date/time in another zone, and the function adjusts that to UTC. For example:

var newYork = 1593547200000;

var utcTime= Framework.AdjustZoneToUTC(newYork, {

offset: -300, /* USA at a UTC-5 with daylight savings */

dstMode: FXB.DSTModes.USA

});

This reverses the process in the example above and returns 1593561600000 - July 1st 00:00 UTC.

3.9.3The user's time zone preferences

The settings in Figaro let the user set two different default time zones: one for charts, and one for general purposes such as open and close times in the trade list. These will normally be the same value, but don't have to be.

You can get the user's preferences using Framework.GetUserChartTimezone() and Framework.GetUserGeneralTimezone(). These both return timezone definitions, like the examples above.

For example, you can convert a UTC date/time to the user's general time zone using:

var userTime = Framework.AdjustUTCToZone(someUtc, Framework.GetUserGeneralTimezone());

It's up to you whether you respect the user's preferences and convert a date before displaying it - and it may not always be appropriate to do so, if you are dealing with something where the user will expect to see their local time despite their preferred settings for the platform.

3.10Helper functions for formatting values

The framework has a variety of functions for doing standard formatting of numbers, dates, prices etc.

3.10.1Framework.FormatNumber()

FormatNumber() is a general-purpose function for formatting numbers. It has two parameters: the number to convert, and a block of options. For example:

var text = Framework.FormatNumber(1234.5678, {digits: 3, show_plus: true})

// With standard settings, returns "+1,234.568"

Option property

Description

digits

Number of decimal places to include (with rounding). Defaults to 2 if neither digits nor max_digits is specified

max_digits

Allows a variable number of decimal places, as required, up to a maximum of max_digits. For example, if max_digits=2 then the results of the formatting will be as follows:

123 = "123"

123.1 = "123.1"

123.456 = "123.46"

roundUp001

Designed for use with percentages where, even if the rounding is mathematically accurate, you don't want to display a value as 0.0 or 100.0 unless it is exactly 0 or 100. If roundUp001 true and digits>0, value which would be rounded down to 0 or up to 100 will instead be displayed as the minimum "increment" such as 0.01 or 99.99

thousands

Display numbers in millions or thousands as xM or xK, with the fractional amount of thousands/millions controlled by digits or max_digits. For example, with {thousands: true, max_digits: 1}:

1456 = "1.5K"

1000000 = "1M"

show_plus

Add a + as a prefix to positive numbers, typically used in relation to profit/loss amounts, or percentages which can be either positive or negative

zero

If the number is exactly zero, and options.zero is not null, then the function returns the value of options.zero - for example, an empty string, or a placeholder such as -

thousands_separator

Automatically set based on user's choice of language/region

decimal_separator

Automatically set based on user's choice of language/region

prefix

Any prefix (such as a currency symbol) to add to the start of the text

suffix

Any suffix to add to the end of the text

3.10.2Framework.FormatCash()

FormatCash() formats a currency amount. It is a simple bare wrapper around FormatNumber(), and accepts the same options as FormatNumber(). It is intended to be used with digits:null rather than a specific number, and then automatically selects a number of digits to use based on the account deposit currency. For example, on a JPY account it will convert the value at 0DP.

3.10.3Framework.FormatPercent()

FormatPercent() formats a percentage, appending a % symbol. It is a simple bare wrapper around FormatNumber(), and accepts the same options as FormatNumber(). The position of the % symbol depends on the user's selected language for the Figaro platform: prepended for Japanese; appended for everything else.

3.10.4Framework.FormatVolume()

FormatVolume() formats a trading volume. By default, it reads the user's preferences in the Figaro settings about whether volumes should be displayed as cash/units or lots. For example, volume of 20000 may be formatted as either "0.20 lots" or "20K".

Because lot-size conversion is market-specific, the FormatVolume() function requires an FXB.Instrument. For example:

var market = Framework.Instruments.get(instrumentId);

var text = Framework.FormatVolume(20000, market, {append_lots:true});

// For example, text = "0.20 lots"

FormatVolume() has three parameters:

Parameter

Description

volume

Volume to format - always, as standard in the framework, an amount in cash/units, not lots

instrument

FXB.Instrument object to use for the price formatting, or the instrumentId of an instrument. Only used in "lots" mode, and optional if you use the options to force "cash" mode.

options

See below

FormatVolume() is again a wrapper around FormatNumber(). You can use the full features of FormatNumber(), but typically you will only want to use the following properties in the options:

Option property

Description

mode

Defaults to the user's preference in the Figaro settings. Can be overridden to either "lots" or "volume".

append_lots

Ignored in "volume" mode. In "lots" mode, returns the text as "X lots" rather than just "X".

show_plus

Same as for FormatNumber(): adds a + at the beginning of positive numbers. Typically used for values such as position net-volume where the value can be either positive or negative

3.10.5Framework.FormatDate()

FormatDate() formats a date, by default in the format yyyy/mm/dd hh:mm:ss

Parameter

Description

datetime

Date/time to format. Although the framework's standard representation of date/times is milliseconds, you are allowed to pass a Javascript Date object into this function.

options

Options for formatting the date

The possible options for FormatDate() are as follows:

Option

Description

format

String describing the format for the date. If omitted, or none of the items below, then the format is yyyy/mm/dd hh:mm:ss.

"dateonly": formats as yyyy/mm/dd, i.e. no time component

"yyyymm": formats as yyyy/mm, i.e. no day or time

"timeonly": formats as hh:mm:ss, i.e. no date component

"timehhmm": formats as hh:mm, i.e. no date component and no seconds

"datehhmm": formats as yyyy/mm/dd hh:mm, i.e. a full date-time, but without the seconds

"textdate": language specific, but in English formats as dddd d mmmm yyyy, e.g. Saturday 12 December 2020 (no time component)

localtime

By default, FormatDate() uses the UTC properties of the date: getUTCHours(), getUTCMonth() etc. Setting localtime:true tells it instead to use getHours(), getMonth() etc - which therefore converts the date/time from UTC to the user's local time zone

date_separator

Can be used to override the separator between the parts of the date. Defaults to /

time_separator

Can be used to override the separator between the parts of the time. Defaults to :

3.10.6Framework.DisplayPrice()

DisplayPrice() formats a price into an HTML element, rather than returning text. This is because it implements Figaro's standard formatting of prices such as 1.23456 which requires HTML representation, with specific CSS classes, rather than plain text. (Therefore, DisplayPrice() is only relevant to a widget, not to a script or UDIX.)

Note: the DOM node which receives the output must be empty on first use of DisplayPrice().

The parameters for DisplayPrice() are as follows.

Parameter

Description

target

Either a DOM node into which to put the price, or the ID of a node. If target is a string, then the framework does $$(target).

instrument

The market to use for formatting the price. Can be specified either as an FXB.Instrument object or the instrumentId of a market

price

The price to format

options

Optional. Number-formatting options which are ultimately passed to FormatNumber(). For example, to suppress thousands-separators, set thousands_separator to a blank string

For example:

<!-- Note: the element which is the target of DisplayPrice() must be empty on the first call -->

<span id="bid"></span>

var market = Framework.Instruments.get("EUR/USD");

Framework.DisplayPrice("bid", market, market.bid);

3.10.7Generating a textual description of an order or trading action

Framework.DescribeOrderOrTemplate() turns an FXB.Order or a trading request into a textual description such as "Buy 0.20 lots EUR/USD". Note: the resulting text is automatically run through the Translation system and will be adjusted to the user's selected language.

For example:

// Generate a caption for trade with orderId T12345

var tradeCaption = Framework.DescribeOrderOrTemplate(Framework.Orders.get("T12345"))

// Generate a caption for a trading request to sell 0.20 lots EUR/USD

var requestCaption = Framework.DescribeOrderOrTemplate({

tradingAction: FXB.OrderTypes.SELL,

instrumentId: "EUR/USD",

volume: {lots: 0.2}

});

3.11Trading validators

Trading validators are not - currently - used by any built-in part of the Figaro software itself. They are a service which is provided solely for use by third-party code, to extend the platform.

A trading validator receives notification of all trading requests passing through Figaro, and has the option to block them. The idea is that a trading validator can implement custom checks on trading activity such as the following:

Is the spread on a market too wide?

Is the time too close to a high-impact event in the economic calendar?

Does the open volume on a market mean that we don't want to allow a further trade?

Do we want to prevent pending orders without expiry?

Do we want to block trading entirely?

You register your widget or script (or even UDIX) as a trading validator by calling Framework.AddTradingValidator(). To deregister it, and stop receiving validation messages, you call Framework.RemoveTradingValidator().

Once you have called AddTradingValidator(), all trading requests throughout the platform get submitted for approval to your widget or script. This happens after a request has passed all Figaro's internal validity checks, but before the request is sent to the broker for execution.

Requests are sent to a function called Framework.OnTradeValidation(). This must return a synchronous response, in order not to delay trading unduly. You cannot look up some asynchronous data, such as the economic calendar, and then later send a response to the validation request once you have the data you need. To set up a check such as the economic calendar, you will typically request the calendar (asynchronously) while simultaneously registering as a validator. If you get a validation request before you have the calendar data to verify it, it is up to you whether you accept or reject the validation request.

OnTradeValidation() rejects a request by returning {status: "block"}, or specifies that the user must manually confirm it by returning {status: "confirm"}. Any other return value allows the request to go ahead. For example:

Framework.OnTradeValidation = function(Msg) {

if ( … some condition we don't like … ) {

// Block and reject the request

return {status: "block"};

} else if ( … some condition we're not sure about … ) {

// Show a confirmation dialog to the user (which may

// happen anyway, depending on other settings and controls)

return {status: "confirm"};

} else {

// Allow the trading action to proceed

return null; // or {status: "okay"}, or anything apart from status:"block"/"confirm"

}

};

If you return {status: "block"} then you can also add blockText into your response. This message is added to the standard rejection message to the user. For example:

return {status: "block", blockText: "Are you mad?"};

Similarly, if you return {status:"confirm"} then you can add confirmText to the response.

Your validator receives two pieces of information in the Msg message which is passed into OnTradeValidation():

Property

Description

fromWidgetType

Type of widget making the request. For example, you might choose to allow all requests from some known widget types such as the dealticket, because by definition the user has seen and manually actioned the request.

tradingJobs[]

List of trading actions which are being requested.

There are two things to note about the array of tradingJobs[]:

You cannot approve or reject individual requests within the array. You can only approve the whole batch or reject the whole batch.

What you receive in tradingJobs[] is a list of trading actions dis-aggregated into the low-level requests such as opening or closing individual orders. For example, if the user clicks Close > All in the built-in Figaro trade list widget, or a script uses the equivalent FLATTEN trading action, then what you will get in tradingJobs[] is not a single FLATTEN request but instead a separate entry in the array for each individual ticket which is going to be closed/deleted.

3.12Market depth and order book

Availability of market depth and order book data depends on the capabilities of the current logged-in trading account. They are not widely available.

In this context, "market depth" generally refers to institutional liquidity arranged by a broker, showing quotes below the best bid and offer in a market. "Order book" generally refers to traders' orders, on a peer-to-peer basis in an exchange. If the back-end platform provides an order book, it may be an aggregated view rather than all individual orders - e.g. price bands of 0.0010 with a totalled summary of the orders within each band.

3.12.1Market depth

The framework always responds to market depth requests, even if no depth data is available from the back-end system. What it will then send is simply changes in the current bid and ask, with no trading volume.

Where market depth is available, the back-end system may send either of the following:

Multiple quotes at each price (from different liquidity providers)

A single quote at each price

You request market depth using Framework.RequestMarketDepth(). This has one compulsory parameter and two optional parameters:

Parameter

Description

instrumentId

instrumentId of the market for which you want depth. Obviously compulsory.

requestId

An optional ID for your request, used in termination (see below). If you do not provide one, the framework will automatically allocate an ID, and report it in all responses. If you allocate your own ID, you are recommended to use FXB.utils.GenerateGuid().

callback

An optional asynchronous callback function which receives the depth. You can process market-depth messages in OnMessage(), or optionally choose to receive them in a specific asynchronous callback as well.

For example:

// Allow the framework to allocate an ID for the request, reported as the requestId in messages

Framework.RequestMarketDepth("EUR/USD", null, function (Msg) {

// Response containing market depth, also received in/via OnMessage()

// ID of the request, for use in termination, will be in Msg.requestId

});

You should terminate market depth requests when you no longer need them, using Framework.TerminateMarketDepth(). The function takes two parameters: the instrument ID, and the request ID allocated either by you or the framework.

Each market-depth update message has the following properties:

Property

Description

instrumentId

instrumentId of the market

requestId

The ID of the market-depth request, either provided by you in your call to RequestMarketDepth() or allocated automatically by the framework. Can be used to terminate requests.

initial

Boolean value indicating whether this the first message, or a subsequent update to the depth

changes

An array of changes to the market depth. For the first message, initial:true, this will list all the entries in the market depth. For subsequent messages, initial:false, it will list changes: the addition of new quotes, removal of quotes, modification of existing quotes.

depth

Object describing the full market depth, incorporating the latest changes. In other words, the framework gives you both the individual changes and also the resulting full book. You don't need to calculate the book yourself from the changes.

The changes object contains two arrays of changed quotes: asks[] and bids[]. Each item in these arrays can have the following properties:

asks[]/bids[] change

Description

id

An ID for the quote which is being added/removed/modified

state

The type of change to the quote: one of "new", "deleted", "changed". In the first message, initial:true, all the state values will be "new". After that, new quotes can be added, and existing quotes can be removed or modified. If a quote is being changed, then either or both of the price and volume may be different.

price

The price of the quote (e.g. 1.23456)

volume

The volume of the quote (e.g. 10000000)

The depth object contains the current full book following the changes; you don't need to calculate this yourself.

As noted above, the back-end system may have multiple quotes at each price (from different liquidity providers). The depth summary calculates both individual current quotes and also the aggregated volume at each price (which will be the same thing if the back-end system only issues aggregated quotes).

The depth object contains the following properties:

depth properties

Description

aggregated

Object containing asks[] and bids[] arrays containing total volume at each price. These are always sorted, by proximity to the best price - bids[] in descending order, asks[] in ascending order. Therefore, asks[0] and bids[0] are the best bid and offer. Each entry in the asks[] and bids[] arrays has a price and a volume. It also has a cumulative value: the total volume of this and all previous/better quotes in the array.

book

Object containing Dictionaries (not arrays) of the individual current bid and ask quotes. Each quote consists of an id, price, and volume. The key of each quote in its dictionary is the id.

bestAsk

Best ask price (same as aggregated.asks[0].price), or zero - see below

bestBid

Bid bed price (same as aggregated.bids[0].price) , or zero - see below

askVolume

Total volume of all ask quotes

bidVolume

Total volume of all bid quotes

A couple of final notes:

You need to be a little careful with this data. During unusual market conditions, or just very briefly during updates, it is possible for there to be no quotes on the bid side or ask side (or even both). Therefore, you cannot safely assume that aggregated.asks.length or aggregated.bids.length is greater than zero.

Internally within the broker's back-end system, it is possible for there to be different routes for market depth versus "trading" prices. It is not absolutely guaranteed that the bestBid and bestAsk in the market depth will match the current bid and ask of the FXB.Instrument.

3.12.2Order book

Order book - where available - is handled very similarly in the framework to market depth. This section mostly describes the differences rather than repeating identical information.

The key differences are as follows:

The order book may include entries on both sides of the current price - users' stop orders as well as limits - whereas market depth will only have entries at and "below" the current price.

The back-end system may list the order book as any of the following: every individual order, or aggregated totals of the orders at each price, or aggregated totals for bands of prices such as 0.0010. In the two latter cases the order-book messages from the framework will have a granularity property, such as 0.00001 or 0.0010.

The volumes in the order book may not be normal trading volumes; they may be a different kind of value such as percentages of the total open volume. You can only rely on the volume figures having meaning relative to each other; you cannot rely on each one having independent validity in relation to the market's contractSize.

Order book for a market must be requested, using Framework.RequestOrderBook(), and terminated when no longer needed, using Framework.TerminateOrderBook().

The parameters for RequestOrderBook() are the same as RequestMarketDepth(): an instrumentId, an optional request ID, and an optional message handler (instead of processing the messages in OnMessage).

You should terminate requests when you no longer need them, using Framework.TerminateOrderBook(). The function takes two parameters: the instrument ID, and the request ID allocated either by you or the framework.

The ORDER_BOOK response messages, in an asynchronous handler or in OnMessage(), are identical to the MARKET_DEPTH responses described above. The message contains the changes plus the full current book.

ORDER_BOOK responses have one further property, as mentioned above:

Property

Description

granularity

Level of aggregation applied by the back-end system. If granularity:null, then the back-end system should be providing the full list of orders (including multiple orders at the same price). Otherwise, the back-end system is combining orders into aggregated amounts at different price points.

3.13Economic calendar

The Figaro framework provides access to an economic calendar - unless the hasCalendar property of Framework.Environment indicates that no calendar is available in the current platform.

Widgets, scripts, and UDIXes can request the calendar using Framework.RequestCalendar(). This responds, asynchronously, with an object containing a calendarItems[] array. There can be further callbacks into the asynchronous handler function when items are added to the calendar, or when existing items are updated with new information. For example:

Framework.RequestCalendar(function (Msg) {

// This function can be called multiple times.

// There will be an initial asynchronous call with the current state of the calendar,

// potentially followed by updates.

// Msg.calendarItems[] is an array of new and/or updated items

});

Each item in the calendar array has the following properties. Note: there can be variation in the data depending on the source of the calendar. Only the ID, title, and timestamp are absolutely guaranteed to be available across all calendar providers.

Property

Description

id

Unique ID of the calendar event

title

Description of the event (e.g. NFP)

timestamp

Time of the event (in UTC milliseconds)

allday

Boolean indicating whether the event is an all-day event such as a holiday

impact

Expected market impact of the event: 0 = low, 1 = medium, 2 = high

currencies

Array of ISO currency codes related to the event. For example, currencies: ["USD", "JPY"]. Some calendar sources may use special identifiers such as "*" or "ALL" to indicate events which apply globally.

previous
forecast

actual

Where applicable (e.g. employment figures), the previous and forecast values for the event, plus the actual value as an update after the event. The format of these values is not standardised and depends on the calendar source; they may be either textual or numeric. Some calendar sources may not provide post-event updates with the actual values, and some sources may not have this data at all.

3.14The Figaro mail system

This section describes Figaro's internal mail system - not email. Widgets, scripts, and UDIXes can send email by using browser XMLHttpRequest to communicate with an email provider such as SendGrid or Amazon SES.

3.14.1Availability of the mail system

Some versions of the Figaro platform may not have a mail system. You can check availability of mail functionality by inspecting the hasMailSystem property of Framework.Environment.

3.14.2Overview of the mail system

Mail in Figaro is sent to devices, not to people or to trading accounts. Each device is identified for mail by an 8-character code such as DMZLXCVG. This ID is shown in the send-mail window in Figaro.

A widget, script, or UDIX can easily send a message to its own device - the current user - without needing to know the device's mail ID. But the following two things are entirely deliberate:

A widget, script, or UDIX cannot programmatically discover the mail ID of its own device

There is no way of discovering the ID of another device without that device's user looking it up, in the mail-sending dialog, and choosing to tell it to you

It is also possible to send mail into Figaro from outside the platform, from any environment which can make HTTP requests.

3.14.3Sending mail

A widget, script, or UDIX can send three types of message:

A local, transient message. These only exist within the running Figaro app, and are destroyed when the user logs out of their current trading account or reloads the app.

A remote message to the current device. These are stored permanently until the user chooses to delete them. The widget, script, or UDIX doesn't need to know the ID of the current device.

A remote message to another device. The widget, script, or UDIX must know the ID of the device it is sending to.

Remote messages are subject to a quota, regardless of whether they are being sent to the current device or another device. Your device's ability to send mail will be temporarily blocked if you try to send too many remote messages. If you are using the mailbox as an alerting mechanism, you should make sure that you don't do something such as entering a loop where you generate the same alert over and over again. (In extreme cases, this can even lead to mail from the device being permanently blocked.)

You send mail using Framework.SendMail(). This takes two parameters: a definition of the mail to send, and an asynchronous callback which receives success or failure. For example:

Framework.SendMail({

subject: "A new message"

}, function (Msg) {

if (Msg.success) {

// Mail successfully sent

} else {

// Mail failed. Error reason will be in Msg.error

}

});

The mail request can have the following properties. The subject is the only compulsory property:

Property

Description

subject

Subject of the message (max 200 characters)

body

Optional body content for the message. Limited to 4KB.

to

The ID of the device to send to. Only required when sending to another device. Can be omitted when sending mail to the current device's mailbox.

local

If local:true, identifies a local message as described above. If a local message is specified, any recipient to value is ignored.

priority

Not available on messages to other devices. Only available on messages to the current device, either locally or remotely. See below. Defaults to 0 if omitted.

noPush

By default, if the mail is not local and the receiving device is a mobile/tablet then the message will generate an iOS/Android push notification. You can turn this off by setting noPush:true (or any value for noPush, actually including noPush:false. It is the presence of a noPush key in the JSON which disables the push, not the key's value.)

The priority level - only available on messages to the current device - can be one of the following values between 0 and 3:

priority

Description

0

Normal priority

1

Message is marked in red in the mailbox

2

A new-mail icon is displayed in the platform's banner (until the message has been read)

3

The mail is forced to open immediately in the platform, without user interaction/request

The optional body of a message can be three things (subject to the 4KB limit):

Plain text

HTML - a full HTML document with <html> and </html> opening and closing nodes

The URL of a web page, subject to the usual browser security restrictions

An HTML document or web page is displayed in a sandboxed iframe, and has no framework access.

3.14.4Receiving mail

A widget, script, or UDIX can read the mailbox by using Framework.RequestMail(). This will issue an immediate (asynchronous) MAILBOX message to OnMessage(), followed by further MAILBOX messages whenever there are changes in the mailbox.

A MAILBOX message has a mail[] array listing all the messages currently in the system. It always lists all messages. On receipt of new mail, the MAILBOX message will contain the entire mailbox, not just the new message.

Widgets, scripts, and UDIXes can only see user-to-user messages. They cannot see messages issued by the broker. This is because those may contain sensitive information such as account passwords.

3.15Miscellaneous functions

This section covers any remaining functions which do not fall into any of the previous categories above.

3.15.1Generating downloads

You can trigger a download of data by using Framework.CreateDownload(). For example, Figaro's built-in Account History widget uses this to do a download of the history in CSV format.

In a web browser, CreateDownload() simply triggers a download of the file. In the mobile/tablet app, it integrates with the device's notification centre, with options for sending the file to another app, saving it in the phone's storage etc.

CreateDownload() takes a single description object as a parameter which must contain the following properties:

Property

Description

fileName

Name of file to generate

mimeType

MIME type such as text/csv

data

File data (as a string)

For example:

CreateDownload({

fileName: "mydata.csv",

mimeType: "text/csv",

data: "Header1,Header2,Header3\r\nData1,Data2,Data3\r\netc"

});

4Messages and event handlers

You can listen for changes in prices, the order list, account metrics etc using an OnMessage() handler. For example:

Framework.OnMessage = function(Msg) {

if (Msg.is(FXB.MessageTypes.PRICE)) {

… update a quote board

}

};

Some types of message such as changes to account metrics are issued automatically. Other types of message, such as prices or account history, must be specifically requested by your widget/script/UDIX.

By the time that message handlersare called, the framework's objects and collections have already been updated. For example, if you receive an ORDER_OPEN message, the new order(s) will already be present in the Framework.Orders collection.

As well as using the main OnMessage handler, you can also define individual handlers for some types of message. For example, you can define an OnPriceChange function. All price changes are then sent to that function as well as to the main OnMessage handler.

It's possible for your code to have multiple message handlers (each of which receives all messages). You can do the following as well as or instead of using OnMessage().

var handler1 = Framework.AddMessageHandler(function (Msg) { … a handler function});

var handler2 = Framework.AddMessageHandler(function (Msg) { … a second handler });

The return value from AddMessageHandler() is an ID which assists with the common Javascript problem of detaching anonymous functions. If you only need a message handler temporarily, you can store its ID and then later disconnect it using RemoveMessageHandler():

Framework.RemoveMessageHandler(handler1);

Note: you may observe that everything passes through OnMessage: initial load, results of trading actions etc. It's possible to handle the results of something like a trading action by watching OnMessage rather than supplying a callback function to SendOrder(). But there's generally no good reason to do that, and this section of the guide only covers the expected uses of OnMessage.

4.1Multiple updates in a single message

The framework can issue multiple updates in a single message. For example, a single message and a single call to OnMessage can contain changes to multiple prices and also updates to the order list.

Therefore, you should generally process messages along the following lines:

Framework.OnMessage = function(Msg) {

if (Msg.is(FXB.MessageTypes.PRICE)) {

… update a quote board

}

if (Msg.is([FXB.MessageTypes.ORDER_OPEN, FXB.MessageTypes.ORDER_CLOSE])) {

… update a trade list

}

};

The following - else-if instead of if-if - is not safe because a message may be both a price update and an order update, and the order update will be ignored by the code if the message is also a price update:

Framework.OnMessage = function(Msg) {

if (Msg.is(FXB.MessageTypes.PRICE)) {

… update a quote board

// NOT SAFE!

} else if (Msg.is([FXB.MessageTypes.ORDER_OPEN, FXB.MessageTypes.ORDER_CLOSE])) {

… update a trade list

}

};

4.2The FXB.Message object

The parameter which is passed to OnMessage() is an FXB.Message object. This is a Dictionary, because a message can contain multiple types of update.

In addition to the list of message-types in the message, which you can test using the Dictionary's is() or has(), there can be further properties in the object depending on the type(s) of message. For example, a PRICE message will contain a quotes[] array of new prices. These extra properties are described below.

4.3Types of message

The types of message which you can receive via OnMessage() are as follows, from the FXB.MessageTypes enumeration:

FXB.MessageTypes.

Description

PRICE

Update to one or more prices

ORDER_OPEN

New pending order or open trade

ORDER_CHANGE

Change to a pending order or open trade (including non-trading changes such as fluctuating profit on an open trade)

ORDER_CLOSE

Closure of an open trade or cancellation of a pending order

POSITION_CHANGE

Change to a net position

ACCOUNT_METRICS

Change to account metrics such as balance or margin usage

ACCOUNT_HISTORY

Initial load or subsequent change to the account history

MARKET_DEPTH

Update to market depth

ORDER_BOOK

Update to order book

MAILBOX

Event within Figaro's internal mail system

THEME

Change by the user to the software's graphical theme

CATEGORY_SETTINGS_CHANGE

Change to the category settings of your widget, script, or UDIX

WIDGET_CONTEXT_CHANGE

Change of context - synchronisation of markets and/or timeframes - in the Figaro page container

BROADCASTx

Range of messages, from BROADCAST1 to BROADCAST10, which widgets, scripts, or UDIXes can use to broadcast their existence to other components

GENERICx

Range of messages, from GENERIC1 to GENERIC10, which a widget, script, or UDIX can use to send a direct message to a component whose ID it knows

4.4FXB.MessageTypes.PRICE and OnPriceChange

You need to request messages for the prices you are interested in. You don't automatically receive notifications about all price changes.

Once you have requested price updates, there are two ways of receiving and processing them:

By using the main OnMessage handler, and listening for the PRICE message

By defining an OnPriceChange handler

There is a subtle difference between these two routes:

PRICE messages may contain other instruments, in addition to those you have requested, in their quotes[] array.

The OnPriceChange handler is guaranteed only to be called in relation to the market(s) you have requested.

4.4.1PRICE messages in OnMessage

Whenever there is a change to a requested price, the main OnMessage handler receives a PRICE message. For example:

Framework.OnMessage = function(Msg) {

if (Msg.is(FXB.MessageTypes.PRICE)) {

Msg.quotes[] contains a list of price changes

}

};

The FXB.Message object will contain a quotes[] array listing the changed prices. You may receive notifications for other markets in addition to the one(s) you have requested. For example, if you have only requested prices for a single market, you cannot rely on quotes.length being 1.

Each object in the quotes[] array has the following properties:

Property

Description

time

Time of the most recent quote

bid

Latest bid price

ask

Latest ask price

high

High of the current period. Availability depends on account features.

low

Low of the current period. Availability depends on account features.

change

Change amount during the current period. Availability depends on account features.

changePc

Change amount as a percentage. Availability depends on account features.

updateMarker

See below

You can react to the changed prices either by looking at the quotes[] array, or by looking at the prices in the Framework.Instrumentscollection which will already have been updated.

The updateMarker is a bit-mask of what is changing in a quote. This is designed to help you minimise the work in updating a user interface. For example, if the bit-mask contains FLAG_PRICE_BID but not FLAG_PRICE_ASK, then you know that you only need to update the display of the bid price. You don't need to make the browser to do extra work updating an ask price which hasn't changed.

The possible flags are members of the FXB.UpdateFlags enumeration:

FXB.UpdateFlags.

Description

BID

Bid has changed

ASK

Ask has changed

HIGH

High has changed

LOW

Low has changed

CHANGEONDAY

Change-on-day has changed

4.4.2OnPriceChange handler

Instead of using the main OnMessage handler, you may find it easier to watch price changes using an OnPriceChange handler. If defined, this is called by the framework with each individual price which is changing:

Framework.OnPriceChange = function(quote) {

// The quote object contains the same time, bid, ask etc properties listed above

};

4.5FXB.MessageTypes.ORDER_x and OnOrderX handlers

There are two ways of tracking changes in the order list (which includes both pending orders and open trades):

By using the main OnMessage handler to listen for the ORDER_OPEN, ORDER_CHANGE, and ORDER_CLOSE messages

By defining any or all of OnOrderOpen, OnOrderChange, and OnOrderClose handlers

4.5.1ORDER_x messages in OnMessage

The ORDER_OPEN, ORDER_CHANGE, and ORDER_CLOSE messages are issued on changes to the order list (which includes both pending orders and open trades).

ORDER_CHANGE is issued for changes in the current price and/or profit on an open trade, as well as trading actions such as moving its stop-loss.

The FXB.Message object will contain arrays listing the new/changed/closed orders:

FXB.MessageTypes.

Array

ORDER_OPEN

ordersOpened[]

ORDER_CHANGE

ordersChanged[]

ORDER_CLOSE

ordersClosed[]

A single message from the framework can contain multiple updates, and therefore the message may contain all of these arrays. For example:

Framework.OnMessage = function(Msg) {

if (Msg.is(FXB.MessageTypes.ORDER_OPEN)) {

… process changes in Msg.ordersOpened[]

}

if (Msg.is(FXB.MessageTypes.ORDER_CLOSE)) {

… process changes in Msg.ordersClosed[]

}

};

Each item in one of these arrays is simply an FXB.Order object describing the new/changed/closed order or trade.

You can react to the changes either by looking at the arrays of orders, or by looking at the Framework.Orderscollection which will already have been updated.

However, in addition to the standard members of FXB.Order, each item in the arrays also has an updateMarker. This is a bit-mask describing what has changed about the order. Particularly for changes rather than opens and closes, this is designed to help you minimise the work in updating a user interface, avoiding updates to HTML elements which don't need to be changed.

The possible flags are members of the FXB.UpdateFlags enumeration:

FXB.UpdateFlags.

Description

FLAG_ORDER_NEW

Order is new (should only exist in ordersOpened[] array)

FLAG_ORDER_TYPE

Change in orderType - for example, when logged in to a back-end platform where an order can change from pending to filled without changing ticket/reference number

FLAG_ORDER_VOLUME

Volume has changed, for example as a result of a partial close

FLAG_ORDER_PROFIT

Profit has changed

FLAG_ORDER_SLTP

Stop-loss and/or take-profit has changed

FLAG_ORDER_CURRENTPRICE

Current price (closePrice) has changed

FLAG_ORDER_OPENPRICE

Open price has changed - alteration to the entry price on a pending order

FLAG_ORDER_COMMENT

Order comment has changed

FLAG_ORDER_OTHER

Any other change to order properties

FLAG_ORDER_CLOSED

Order is closed/cancelled (should only exist in ordersClosed[] array)

4.5.2OnOrderX handlers

Instead of using the main OnMessage handler, you can also track changes in the order list using the functions OnOrderOpen, OnOrderChange, and OnOrderClose. (You don't have to define all three of these. If you are only interested in new opens, then you only need to define OnOrderOpen).

Each of these handlers receives as a parameter the individual order which is being opened, changed, or closed. For example:

Framework.OnOrderOpen = function(newOrder)

{

// newOrder is an FXB.Order describing the new trade or pending order

};

4.6FXB.MessageTypes.POSITION_CHANGE and OnPositionChange

You can listen for changes in the changes in the Framework.Positions list in two ways:

By using the main OnMessage handler to listen for the POSITION_CHANGE message

By defining an OnPositionChange handler

In both cases, position updates are not issued for changes in profit (or current price). They are only issued for changes in the net position caused by an order opening or closing. If you want to update a display of the profit on a net position, then you need to do something such as watching the market's price as a prompt telling you that the profit will have changed.

4.6.1POSITION_CHANGE message in OnMessage

The POSITION_CHANGE message is issued about changes in the Framework.Positions list.

The FXB.Message object will contain a positionsChanged[] array listing the FXB.Position objects which have changed. You can react to the changes either by looking at the positionsChanged[] array, or by looking at the Framework.Positions collection which will already have been updated.

4.6.2OnPositionChange handler

Instead of using the main OnMessage handler, you can also track changes in the position list by using an OnPositionChange handler. If defined, this is called by the framework for each change in a position:

Framework.OnPositionChange = function(position) {

// An FXB.Position update describing the change

};

4.7FXB.MessageTypes.ACCOUNT_METRICS and OnAccountMetrics

You can listen for changes in the metrics in Framework.Account - change in floating P/L, margin usage etc - in two ways:

By using the main OnMessage handler to listen for the ACCOUNT_METRICS message

By defining an OnAccountMetrics handler

4.7.1ACCOUNT_METRICS message in OnMessage

The ACCOUNT_METRICS message is issued about changes to the metrics in Framework.Account - change in floating P/L, margin usage etc.

The FXB.Message object will contain an accountMetrics object. The properties of Msg.accountMetrics correspond to the properties in the Framework.Account, but it only contains values which are changing. For example, for a change of floating P/L without a new closed trade which affects the balance, the accountMetrics will include a floatingPL property but not a balance property.

You can also inspect what has changed using an updateMarker in the accountMetrics. This is a bit-mask describing which metrics have changed. The possible flags are members of the FXB.UpdateFlags enumeration:

FXB.UpdateFlags.

Description

FLAG_ACCOUNT_FLOATINGPL

Change to floating P/L

FLAG_ACCOUNT_EQUITY

Change to equity (should always also imply FLAG_ACCOUNT_FLOATINGPL)

FLAG_ACCOUNT_BALANCE

Change to balance

FLAG_ACCOUNT_USEDMARGIN

Change to margin in use

FLAG_ACCOUNT_FREEMARGIN

Change to free margin

FLAG_ACCOUNT_CREDIT

Change to account credit

4.7.2OnAccountMetrics handler

Instead of using the main OnMessage handler, you can also track changes in the account metrics such as balance and floating P/L by using an OnAccountMetrics handler. If defined, this is called by the framework each time that any of the metrics changes:

Framework.OnAccountMetrics = function(accountMetrics) {

// Receives the same accountMetrics object as in OnMessage

};

The parameter to the OnAccountMetrics handler is the same value inside an ACCOUNT_METRICS message.

4.8FXB.MessageTypes.ACCOUNT_HISTORY

The ACCOUNT_HISTORY message is issued on initial load of the account history, followed by each addition of a new closed order or credit movement. You need to request account history; it is not issued automatically.

By the time that your message handler receives ACCOUNT_HISTORY, the Framework.TradeHistory collection will already have been updated with the changes.

You can inspect which transactions are being added by looking at the history[] array in the FXB.Message object. Like the Framework.TradeHistory collection, each item in the history[] array is an FXB.Order object describing the closed trade/order or credit movement.

4.9FXB.MessageTypes.MARKET_DEPTH

The MARKET_DEPTH message is sent in response to a request for market depth. It can be processed either in OnMessage() or using an asynchronous callback to RequestMarketDepth(). Market depth must be explicitly requested; it is not sent automatically.

4.10FXB.MessageTypes.ORDER_BOOK

The ORDER_BOOK message is sent in response to a request for order book. It can be processed either in OnMessage() or using an asynchronous callback to RequestOrderBook(). Order book must be explicitly requested; it is not sent automatically.

4.11FXB.MessageTypes.MAILBOX

The MAILBOX message is a notification of a change in Figaro's mail system - not just a new message, or deletions, but also a message being opened and treated as read.

MAILBOX messages must be specifically requested. They are not sent automatically by default.

MAILBOX messages contain a mail[] array. This always (re-)lists all messages in the mailbox, not just additions.

4.12FXB.MessageTypes.THEME

(This section is irrelevant to scripts and UDIXes because they have no user interface of their own. It is only applicable to widgets.)

The user can change the graphical theme of the software - e.g. from dark to light - while it is running, without a restart. Therefore, the theme can change while your widget is running.

The THEME message notifies you that this has happened. The FXB.Message object will contain a Theme object, but it is simplest to inspect the change just by using Framework.Theme.

4.13FXB.MessageTypes.CATEGORY_SETTINGS_CHANGE

The CATEGORY_SETTINGS_CHANGE message is received by all instances of your widget, script, or UDIX when one instance saves changes using Framework.SaveCategorySettings().

The message is sent to the instance which saved the change as well as to any other running instances of the widget. The message includes a categorySettings property containing the new settings, but these are also available via Framework.categorySettings.

4.14FXB.MessageTypes.WIDGET_CONTEXT_CHANGE

The WIDGET_CONTEXT_CHANGE message is received by a widget (only, not a script or UDIX) under the following circumstances:

The widget has been added to the Figaro page container

The user has chosen to synchronize the markets and/or timeframes on the page, and another widget changes the market or timeframe

Or, some component uses SetCurrentPageInstrument() or SetCurrentPageTimeframe()

See the section on handling context for more information.

4.15FXB.MessageTypes.BROADCASTx and FXB.MessageTypes.GENERICx

The message types from BROADCAST1 to BROADCAST10 and from GENERIC1 to GENERIC10 are designed for transient, ad hoc messaging between different widgets, scripts, and UDIXes.

BROADCASTx messages can be sent out without a specified recipient ID (a _to in the message). GENERICx messages require a recipient.

5Working with candle data

Note: the features described in this section can be used from any User Defined Indicator, without needing to give it Framework access and turning it into a UDIX. This section of the guide can also be treated as part of the UDI developer reference.

The section above about framework functions describes how RequestCandles() can be used to retrieve candle data from the framework - both historic candles and also optionally streaming updates to the candles as the current price changes.

Separate to that fundamental ability to collect candle data, the framework provides two resources making it easier to work with the candle data:

The FXB.CandleStore object

The FXB.ta library of technical analysis calculations

5.1The FXB.CandleStore object

The FXB.CandleStore object makes it easier to work with candle data:

It stores, updates, and indexes candles

It automatically calculates derived properties of each candle such as the high-low range and the median price

It detects changes such as the start of a new candle, and fires events which you can hook into as a simple way of detecting types of change in the store

It can automatically update a list of technical analysis calculations

For example, an automated trading algorithm might look broadly as follows:

// Set up a candle store

var myCandleStore = new FXB.CandleStore({

// Automatically calculate two moving averages

ta: [

new FXB.ta.EMA({period: 20}),

new FXB.ta.EMA({period: 50})

],

OnNewCandle: function() {

// Event which is fired each time a new candle starts.

// Compare the moving averages and potentially carry out some trading action.

}

});

// Use Framework.RequestCandles to load (streaming) data into the store, and set things moving

Framework.RequestCandles({instrumentId: "EUR/USD", timeframe: 3600}, myCandleStore);

5.1.1Creating a candle store

You can create a candle store simply by creating a new instance of the FXB.CandleStore class. For example:

// Create candle store

var myCandleStore = new FXB.CandleStore();

// Set up candle store

myCandleStore.OnLoad = function() { … };

… further initialisation, load of data etc

After creation, you can use LoadCandles() to load data into the store, assign event handlers, etc.

However, you can also initialise the candle store "inline", rather than setting it up later. For example, rather than creating a candle store and then assigning an OnLoad() handler, you can instead pass some or all properties in the initialisation of the store:

var myCandleStore = new FXB.CandleStore({

OnLoad: function() { … called on (re-)load },

OnNewCandle: function() { … called when a new candle starts },

etc

});

The full set of properties which you can optionally pass in the initialisation is as follows. You can also assign any other properties of your own (provided, obviously, that their names don't overlap with the candle store's own values):

Property

Description

OnCurrentCandleChange()

Event handler. See below.

OnNewCandle()

Event handler. See below.

OnLoad()

Event handler. See below.

OnLoadError()

Event handler. See below.

OnUpdate()

Event handler. See below.

candles[]

Array of candles to populate with. See below.

ta[]

Array of technical analysis calculations to populate with. See below.

oldestFirst

Changes the indexing of the store. See below.

5.1.2Loading data into the candle store

There are three ways of loading data into an FXB.CandleStore. Firstly, you can take the asynchronous response from RequestCandles() and pass the candles[] array from the response message into the LoadCandles() function in the FXB.CandleStore. For example:

Framework.$myCandleStore = new FXB.CandleStore();

var candleRequest = { … some candle request … };

Framework.RequestCandles(requestDef, function (Msg) {

if (Msg.candles) {

// Process both an initial response to the candle request

// and also streaming updates with changes and additions

Framework.$myCandleStore.LoadCandles(Msg.candles);

} else {

// No candles[] array. Some problem with the request.

}

});

LoadCandles() can be used both with the initial response to a candle request and also any subsequent streaming updates which contain modified or new candles.

In addition to setting up event handlers, you can detect changes in the store by looking at the return value from LoadCandles(). This can be one of the following values:

Return value

Description

-1

Load failed. Invalid candle data.

0

No change

1

Only the current, in-progress candle has changed

2

A new, latest candle has been added to the store. (As with the OnNewCandle() event, there may also have been a final update to what was previously the current candle.)

4

Initial load

6

Load of new candle data which both modifies the history and also adds new current or later candles

The second way of loading data into the store is to assign the candle store as the second parameter to RequestCandles(), instead of providing a callback function. For example:

// Create a candle store

Framework.$myCandleStore = new FXB.CandleStore();

var candleRequest = { … some candle request … };

// Pass the response(s) from RequestCandles() directly into the FXB.CandleStore,

// without using an asynchronous callback function or needing to process the messages manually:

Framework.RequestCandles(requestDef, Framework.$myCandleStore);

There are two special considerations here, resulting from the fact that you don't receive the response messages from RequestCandles().

The framework puts the ID of the candle request into the FXB.CandleStore as a candleRequestId property. If you need to terminate a streaming request, you can do so using Framework.TerminateCandleRequest(store.candleRequestId)

If the candle request fails, the framework fires an OnLoadError() event in the candle store.

The third way of loading candles into a store is to provide a candles[] array in the initialisation of the store. For example:

// Note: this method will only normally be suitable for static candle requests,

// not streaming requests where there are multiple callbacks

var candleRequest = { … some candle request … };

Framework.RequestCandles(requestDef, function (Msg) {

if (Msg.candles) {

// Create a store, populating it with the candles[] array

Framework.$myCandleStore = new FXB.CandleStore({

candles: Msg.candles,

… any other properties you want to assign

});

} else {

// No candles[] array. Some problem with the request.

}

});

You can load candle data of your own into an FXB.CandleStore as well as passing in candle data from the framework. To do this, you provide an array of candle objects, each of which - like the framework's own candle data - must contain ts, o, h, l, and c values (plus, optionally, v).

Finally, the FXB.CandleStore also has special support for User-Defined Indicators, which receive bar data as separate arrays, in data.barData.open[], data.barData.close[] etc, rather than as an array of candle objects. In the UDI.onCalculate() function you can pass the data parameter into a candle store, and that will combine the separate arrays into a list of candles. For example:

UDI.onCalculate = function(data, output) {

var cs = new FXB.CandleStore({candles: data}); // Can also use LoadCandles(data)

};

It's then possible to do things such as extracting derived prices from the store, such as GetValueArray("median"), or to pass the candle array into a bar-based technical analysis calculation.

5.1.3Events in the candle store

One of the key benefits of the candle store is that it fires events when candle data is loaded. This is particularly useful on streaming rather than static candle requests, because it helps you to differentiate between updates of the current candle versus the start of a new candle.

Event

Description / type of change

OnCurrentCandleChange()

Called when only the current, in-progress candle is changing

OnNewCandle()

Called when a new, later candle has been added to the store. What was previously the current candle may also have received a final update as part of the same change, without firing OnCurrentCandleChange()

OnLoad()

Any other type of change to the candle store. Mainly initial load, but can also be called if a block of additional old candles is later added to the store. In other words, OnLoad() is a notification that the candle data has fundamentally changed, and anything based on it needs to be recalculated.

OnLoadError()

Special case: called if you assign the candle store as the output for RequestCandles(), and there is a problem with your candle request

OnUpdate()

Called on any type of change to the candle store - (re-)load, new candle, update of current candle. Designed for callers which only need a notification that the store has changed, regardless of the type of change. The more specific handler such as OnNewCandle() or OnLoad() is called immediately before OnUpdate().

The candle store object is passed as a parameter to all event handlers. For example:

myCandleStore.OnLoad = function(candleStore) {

// Store can be accessed either via the myCandleStore variable

// or via the candleStore parameter to the event handler

};

You can assign event handlers after the candle store has been created. For example:

// Create candle store

var myCandleStore = new FXB.CandleStore();

// Assign a candle request directly into the store

Framework.RequestCandles({…}, myCandleStore);

// Assign event handlers

// Note: candle requests always return asynchronously, and therefore it's safe to assign

// the event handlers immediately after RequestCandles(), rather than doing so before.

myCandleStore.OnLoad = function(candleStore) { … };

myCandleStore.OnNewCandle = function(candleStore) { … };

You can also, instead or as well, provide the event handlers in the initialisation of the candle store. For example:

var myCandleStore = new FXB.CandleStore({

OnLoad: function() { … },

OnCurrentCandleChange: function() { … },

etc

});

5.1.4Reading data from the candle store

Loading data into the store fills a candles[] array, and you can read directly from this array:

myCandleStore.LoadCandles(…);

var currentClose = myCandleStore.candles[0].c;

However, it is not safe for you to modify the candles[] array in any way. If you need to do any sort of processing on the data, use GetCandleArray() instead as described below.

The store provides some simple properties for checking its contents:

Property

Description

length

Number of candles in the store (equivalent to candles.length)

hasData

Boolean indicating whether there are any candles in the store (length > 0)

By default, like the framework's array returned from RequestCandles(), the candles in a store are indexed newest-first. Item #0 in the array is the current, in-progress candle. Item #1 is the most recent complete candle. Item #[length-1] is the oldest candle etc.

This is generally the easiest and most processor-efficient way of working with candle data, but you can optionally invert the indexing by setting oldestFirst:true. This can be done either in the initialisation of the store, or at any later time by changing the oldestFirst property of the store.

If you set oldest-first mode, then the store's own candles[] array is unchanged, and continues to have the newest candle in #0. However, oldestFirst does alter the following functions which the store provides:

Function

Description

GetCandleArray()

Returns a copy of the candle array (using .slice(0), which means that it is safe for you to modify the result). The order of the array depends on oldestFirst.

GetCandle(idx)

Returns a specific zero-based candle, or null if the index is not valid. The oldestFirst property controls whether idx=0 means the most recent candle or the oldest candle etc.

GetValueArray(member)

Helper function which returns an array of a single member from all the candles. For example, GetValueArray("h") returns an array containing all the high prices, and GetValueArray("median") returns all the median prices.

In other words: setting oldestFirst is only meaningful if you then access the candle data using GetCandleArray() and GetCandle(), rather than reading the candles[] array directly. Setting oldestFirst on the store also affects the indexing of any technical analysis calculations in the store.

You can also look up candles by time:

Function

Description

GetByTime(tm, exact)

Finds the candle which starts with or contains the time tm (specified as a number of milliseconds).

If exact=true, then tm must exactly match a candle's start time or else GetByTime() returns null. If exact=false, then the following rules apply:

If tm is later than the start of the current candle - in other words, any date into the future - then GetByTime() returns the current candle.

If tm is earlier than the start of the earliest candle, then GetByTime() returns null.

Otherwise, GetByTime() returns the candle where tm is greater or equal to its start but before the start of the next candle.

GetByTime() has no notion of any timeframe which candles cover. For example, if you have hourly candles on a 24x5 market such as forex, and you supply a time over a weekend with exact=false, then what you will get back is the final candle on the preceding Friday. (You can obviously check whether a time actually falls within a candle by adding the timeframe of your data to the candle's start time.)

5.1.5Derived properties of each candle

When candles are loaded into the store, it automatically adds in derived properties for each candle such as the high-low range. These are available when you read data back from the store, using GetCandle() etc.

The derived candle properties which are added by the store are as follows:

Property

Description

range

High minus low

median

Average of high and low

typical

"Typical" price: (high + low + close) / 3

weighted

"Weighted" price: (high + low + close + close) / 4

ohlc4

Average of open + high + low + close

change

Close minus open

abschange

Absolute value of change; converted from negative to positive where applicable

5.1.6Technical analysis calculations in the candle store

A candle store lets you set a list of any technical analysis calculations from the FXB.ta library, and have these automatically updated when the candle data changes.

For example, you can initialise the candle store with a moving average and ATR. These calculations then automatically update whenever candles are loaded into the store (and before any event handlers are called):

var myCandleStore = new FXB.CandleStore({

// Automatically add a moving average and ATR

ta: [

new FXB.ta.EMA({period: 20, member: "h"}), // EMA of high (.h) price

new FXB.ta.ATR({period: 14}) // ATR

],

… further initialisation settings

You can set up technical analysis calculations in two ways. You can provide a ta[] array in the initialisation of the store, like the example above. It's also permissible to pass a single item rather than an array:

var myCandleStore = new FXB.CandleStore({

// Single TA object

ta: new FXB.ta.EMA({period: 20, member: "h"}), // EMA of high (.h) price

Or, at any time after creation of the store, you can use the AddTA() function:

myCandleStore.AddTA(new FXB.ta.RSI({period: 30}));

myCandleStore.AddTA(new FXB.ta.Stochastic({kPeriod: 5, dPeriod: 3, slowing: 3}));

Instead of providing an instance of the analysis class, such as new FXB.ta.ATR(), in the store's initialisation or in a call to AddTA(), you can instead pass a definition containing the parameters for the calculation plus an indicatorType value. The indicatorType should be the class within FXB.ta such as DEMA or ATR or Envelope. For example:

myCandleStore.AddTA({indicatorType: "Stochastic", kPeriod: 5, dPeriod: 3, slowing: 3});

// Has the same effect as…

myCandleStore.AddTA(new FXB.ta.Stochastic({kPeriod: 5, dPeriod: 3, slowing: 3}));

You can subsequently read the calculations using the candle store's ta[] array. The order of items within this is simply the order in which you added them. For example:

var currentRSI = myCandleStore.ta[0].GetCurrentValue();

var currentStochastic = myCandleStore.ta[1].GetCurrentValue();

You can add calculations, using AddTA(), after loading candle data as well as during the initial set-up of the store. And it's safe to modify the store's ta[] array directly to remove or even replace calculations.

As a service to make your code easier to read, there is a further way of addressing technical analysis calculations. When creating a calculation you can include an alias property, such "ema20":

myCandleStore.AddTA(new FXB.ta.EMA({period: 20, alias: "ema20"}));

You can then refer to calculations via their alias using a $ta() function in the candle store, making the code easier to read than referring to the calculation via a numeric index such as [1]. For example:

// Calculations can always be addressed via their numeric index, in order of creation…

var currentEma = myCandleStore.ta[1].GetCurrentValue();

// But the code becomes more readable if the access is given a name rather than a number…

var currentEma = myCandleStore.$ta("ema20").GetCurrentValue();

5.1.7Aggregation to higher timeframes

It is common to need multiple timeframes on a single market, such as both M30 and H1. Rather than making two separate candle requests, it can be more efficient to make a request only for the lower timeframe, such as M30, and then to create the higher timeframe(s), such as H1, by aggregating upwards.

Obvious note: the disadvantage of aggregation compared to a separate candle request is that you get fewer candles on the higher timeframe(s). If aggregating from M30 to H1, then N candles on M30 become N/2 candles on H1. An aggregation such as H1 to D1 causes a much larger reduction in data: the number of available daily candles is the number of hourly candles divided by 24.

You can create aggregated candles using the Aggregate() function. This creates a copy of a candle store, including any technical analysis calculations, and combines the candles into a higher timeframe. The new candle store can then subsequently be updated in the usual way, using LoadData(). For example:

// Request M30 candles

Framework.RequestCandles({"instrumentId": "EUR/USD", timeframe: 1800}, function(Msg) {

// If this is the first call, create a store for the M30 candles, including an ATR calculation

if (!Framework.$myM30) Framework.$myM30 = new FXB.CandleStore({

ta: [new FXB.ta.ATR({period: 14})]

});

// Load initial or subsequent candles into the M30 store

Framework.$myM30.LoadCandles(Msg.candles);

// If this is the first call, create an aggregated H1 store from the M30 store.

// The aggregated H1 store will automatically also have an ATR calculation.

if (!Framework.$myH1) {

// Creates H1 candles from the M30 initial candles which have just been loaded

Framework.$myH1 = Framework.$myM30.Aggregate(3600);

} else {

// If it's not the initial load of candles, then update the H1 store

// using the new M30 candles

Framework.$myH1.LoadCandles(Msg.candles);

}

});

As in the above example, Aggregate() requires a timeframe parameter for the aggregation, such as 3600 (=H1).

For timeframes between H2 and D1 inclusive, there is a further consideration: the time zone for the aggregation. Like the time zone when making candle requests, you may want to specify how the candles should be combined. Aggregate() can be given a time zone as a second parameter. For example:

// Aggregate into daily candles, using standard fx time (UTC+2/3 changing on US schedule)

cs.Aggregate(86400, {offset: 120, dstMode: 1});

If you don't provide a time zone parameter, then the default is UTC (and the setting is redundant and ignored if the timeframe is below H1 or above D1).

In a widget or script, you can get the user's standard time zone for charts using Framework.GetUserChartTimezone(). For example:

cs.Aggregate(86400, Framework.GetUserChartTimezone());

In a UDI, you can get the time zone of the indicator's chart from the context. For example:

cs.Aggregate(86400, data.context.timezone);

5.2FXB.ta technical analysis library

The framework provides an FXB.ta library of core technical analysis calculations: moving averages, oscillators such as MACD and Stochastic, RSI and ATR etc. For example:

// Create MACD calculation

var macd = new FXB.ta.MACD({fast: 12, slow: 26, signal: 9});

// Load some data

macd.LoadData( … );

// Get current value (item #0) of histogram

var histo = macd.GetHistogramValue(0);

5.2.1Calculation input: bar-based versus value-based calculations

Technical analysis calculations fall into two categories:

"Bar-based" calculations which can only be used with candle data, because they need multiple properties from each candle such as both the high and the low. Examples include ATR and the Stochastic Oscillator.

"Value-based" calculations which can be used with any array of values. For example, all types of moving average can be calculated on candle data, using a specific single value from each candle such as its close or high, but can also be calculated on any series of numbers.

For example, ATR must be calculated from candle data (where each item includes an o, h, l, and c):

var atr = new FXB.ta.ATR({period: 14});

atr.LoadData([{o:1.2342, h: 1.2347, l: 12341, c: 1.2343}, … ]);

Whereas a moving average can be calculated from any array of numbers, as well as on candle data:

// Calculate a moving average of a series of numbers

var ma1 = new FXB.ta.EMA({period: 5});

ma1.LoadData([2, 3, 14, 7, 5, 10, 12, 20, 13, 4, 8]);

// Calculate a moving average of the high of a series of candles

var ma2 = new FXB.ta.EMA({period: 10, member: "h"});

ma2.LoadData([{o:1.2342, h: 1.2347, l: 1.2341, c: 1.2343}, … ]);

This also means that a value-based calculation can be applied to any other technical analysis calculation. For example, you can calculate ATR, and then take the output and calculate Bollinger® Bands on those ATR results:

// Calculate ATR from candle data

var atr = new FXB.ta.ATR({period: 14});

atr.LoadData([{o:1.2342, h: 1.2347, l: 12341, c: 1.2343}, … ]);

// Calculate Bollinger Bands on the ATR results

var bands = new FXB.ta.Bands({period: 20});

bands.LoadData(atr.GetValueArray());

5.2.2Creating and initialising technical analysis calculations

You set up a technical analysis calculation by creating an instance of the corresponding class in the FXB.ta library. For example:

// Create an exponential moving average

var myEma = new FXB.ta.EMA({period: 20});

// Create an RSI calculation

var myRSI = new FXB.ta.RSI({period: 14});

All calculations require initialisation with parameters, such as a period value, and these are described below in relation to each individual calculation. (All parameters have defaults, but you will generally want to set them explicitly.)

You can include a data[] array in the initialisation of a calculation. This is simply a shortcut for creating the calculation and then calling LoadData() with the array:

var myEma = new FXB.ta.EMA({period: 20, data: [ … ]};

Parameters can be changed after a calculation has been created (rather than replacing the existing object with a new instance of the class). But changes do not take effect until/unless you completely re-load new data into the calculation. For example:

// Create EMA with period=20

var myEma = new FXB.ta.EMA({period: 20});

// Load data

myEma.LoadData( … );

// Change period parameter. Does not have any immediate effect.

myEma.period = 14;

// Values in the calculation do not change until after LoadData() is called again

myEma.LoadData( … );

5.2.2.1The member parameter, and derived prices

There is one special initialisation parameter, the member. This is applicable when (a) the calculation is value-based rather than bar-based, and (b) you load candle data into the calculation rather than giving it an array just of close prices, or just of highs. It is not applicable to candle-based calculations, or to value-based calculations where you load in an array of values rather than of candles.

The member tells the value-based calculation which value to use from each candle. It can be any of the following, and defaults to c (close price) if not explicitly set. Note that the candle data which you load into the calculation does not need to include derived properties such as the range or median. The calculation automatically works these out from the open/high/low/close if required:

member

Description

c

Close price

o

Open price

h

High price

l

Low price

range

High minus low

median

Average of high and low

typical

"Typical" price: (high + low + close) / 3

weighted

"Weighed" price: (high + low + close + close) / 4

ohlc4

Average of open, high, low, and close

change

Close minus open

abschange

Absolute value of change; converted from negative to positive where applicable

For example:

// Create an exponential moving average of the high prices of candles

var myEma = new FXB.ta.EMA({period: 20, member: "h"});

// Load candle data into the calculation (rather than an array just of the highs)

myEma.LoadData([{o:1.2342, h: 1.2347, l: 12341, c: 1.2343}, … ]);

5.2.3Loading data into a technical analysis calculation

All the technical analysis calculations are optimised so that they have two functions for loading data:

Property

Description

LoadData(array)

Loads or re-loads the full array of data for the calculation. By default, the array should be ordered so that it is newest-first: the most recent item is in #0 and the oldest item is in #length-1

UpdateCurrent(object)

Updates the value for the current bar/value only. You can also pass an array into UpdateCurrent(), but only the first value, [0], is then used.

In other words, LoadData() does a complete load or replacement of the data for the calculation, whereas UpdateCurrent() is a much faster function for doing updates when only the current value is changing. For example, using hourly candles, a full reload/recalculation is only required once per hour, and the thousands of ticks within an hour can be processed much more efficiently by passing just the new price data to UpdateCurrent().

Both LoadData() and UpdateCurrent() have boolean return values indicating whether the new data was successfully processed. Failure can be because your input data was invalid, for example attempting to load an array of numbers rather than of candles into a bar-based calculation. It can also be because you didn't supply enough data. For example, if you have a 30-period moving average, you must supply at least 30 values in the call to LoadData().

5.2.3.1Loading candle data into a calculation

As described above, calculations can be either bar-based or value-based. For bar-based calculations, what you pass to LoadData() can be one of two things:

An array of candles: objects containing an o, h, l, c, and v (optional, defaults to zero).

An object containing a barData object which in turn contains separate arrays for the open, close etc: open[], close[], etc. This option means that you can pass in the data object from a user-defined indicator's UDI.onCalculate() function.

For example, to load an array of candle objects:

myATR.LoadData([{o:1.2342, h: 1.2347, l: 12341, c: 1.2343}, … ]);

Or, to load in a barData object with the candle values in multiple separate arrays:

myATR.LoadData({

valueCount: 500, // Compulsory: number of items in the arrays

barData: {

open: [1.2342, 1.2340, 1.2345, …],

high: [ …],

low: [ … ],

close: [ … ],

volume: [ … ] // Volume is optional, and defaults to zero if omitted

}

});

Similarly, in a bar-based calculation, what you pass to UpdateCurrent() must also be a candle object, or an object containing barData:

// When the current candle changes, use UpdateCurrent() with the single current candle object

myATR.UpdateCurrent({o:1.2342, h: 1.2347, l: 12340, c: 1.2340});

With a barData object:

// In UpdateCurrent(), only the first entry in each array is used

myATR.UpdateCurrent({

barData: {

open: [1.2342, …],

high: [ …],

low: [ … ],

close: [ … ],

volume: [ … ] // Volume is optional, and defaults to zero if omitted

}

});

However, there is a further refinement. LoadData() understands the currentBarUpdateOnly value supplied by a UDI, and automatically turns LoadData() into a faster call to UpdateCurrent() where applicable. For example:

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.currentBarUpdateOnly is set then the calculation does a fast call to UpdateCurrent()

// rather than a full reload of all the data

UDI.$myATR.LoadData(data);

};

5.2.3.2Loading value data into a calculation

For value-based calculations, you can load in either a candle array or just any array of numbers. If you use a candle array, then you need to set a member value in the calculation's initialisation, specifying which candle property to use such as the median price (automatically derived from the high and low):

// Create an exponential moving average of the median prices of candles

var myEma = new FXB.ta.EMA({period: 20, member: "median"});

// Load candle data into the calculation

myEma.LoadData([{o:1.2342, h: 1.2347, l: 12341, c: 1.2343}, … ]);

Or you can just load into a value-based calculation an array of the median prices, or of any numeric values at all:

myEma.LoadData([1.2349, 1.2368, 1.2392, 1.2347, … ]);

When you are updating the current data for a value-based calculation, you can supply either a candle object or just the relevant property from the candle. For example:

myEma.UpdateCurrent({o:1.2342, h: 1.2347, l: 12341, c: 1.2343}); // Update with candle

myEma.UpdateCurrent(1.2344); // Update just with median price

5.2.3.3Loading data in the calculation's initialisation

Finally, there is another way of loading initial data into a technical analysis calculation: providing a data array/object in the calculation's initialisation. This is simply equivalent to setting up the calculation and then doing LoadData() on that same array/object:

var ma = new FXB.ta.EMA({

period: 30,

member: "median",

data: [{o:1.2342, h: 1.2347, l: 12341, c: 1.2343}, … ]

});

5.2.4Reading calculation output

All calculations have a primary output value, but some calculations produce other secondary values as well. For example, the Stochastic Oscillator generates not only a primary value but also a "signal" value (typically represented on a chart as a dotted or dashed line).

All classes in the FXB.ta library have some standard functions for reading their primary values:

Function

Description

GetValueArray()

Returns an array of all the primary output values

GetValue(idx)

Returns a single output value, using a zero-based index. By default, calculations are indexed newest-first, and #0 is the current, latest value of the calculation. Returns null if the index is not valid (< 0 or >= length).

GetCurrentValue()

Shortcut for GetValue(0)

GetLength()

Get the number of output values. Should always match the number of items in the array passed to LoadData(), unless LoadData() failed and returned false

length

Length property; alternative and equivalent to GetLength() function

HasData()

Shortcut for checking whether a calculation has been initialised. Equivalent to GetLength() > 0

hasData

Has-data property; alternative and equivalent to HasData() function

For example, testing whether two moving averages have crossed over since the previous bar:

// Create two moving averages for different periods and load the same data into them

var ma1 = new FXB.ta.EMA({period: 20});

var ma2 = new FXB.ta.EMA({period: 50});

ma1.Load(myDataArray);

ma2.Load(myDataArray);

// Compare the moving averages in the current bar versus the previous bar

if (ma1.GetValue(0) > ma2.GetValue(0) && ma1.GetValue(1) < ma2.GetValue(1)) {

// MA 1 has crossed above MA 2 during current bar (is now > and was <)

} else if (ma1.GetValue(0) < ma2.GetValue(0) && ma1.GetValue(1) > ma2.GetValue(1)) {

// MA 1 has crossed below MA 2 during current bar (is now < and was >)

} else {

// (No cross)

}

As described above, the number of output values will always be the same as the number of input values in the array passed to LoadData(). But some of the output values may be - usually, will be - null. For example, if you are calculating a 20-period simple moving average, no average can be calculated for the first 19 values. If you supply an array of 1000 input values, then GetLength() on the output will be 1000 but the oldest 19 values - from GetValue(981) to GetValue(999) - will be null.

For calculations which produce more than one output, the details are described below in relation to each calculation. But there is some standard functionality where a secondary value is returned via GetSignalArray(), GetSignalValue(), and GetCurrentSignalValue().

Function

Description

GetSignalArray()

Returns an array of all the secondary, "signal" output values

GetSignalValue(idx)

Returns a single signal value, using a zero-based index

GetCurrentSignalValue()

Equivalent to GetSignalValue(0)

Some calculations then have further functions for retrieving their output. For example, the Keltner and Bollinger band calculations have GetUpper(idx) and GetLower(idx) functions for returning their upper and lower band values.

5.2.5Indexing order and oldestFirst

As described in the previous section, the default for FXB.ta calculations is that output is indexed with newest values first: the current, latest result is #0. This is simply because a test such as GetValue(0) > GetValue(1) is a less cumbersome and more readable way of checking the current versus previous value than a test such as GetValue(length - 1) > GetValue(length - 2).

This also means that you should load data newest-first. For example:

// The 1.2349 value in array #0 should be the most recent value, not the oldest value

myEma.LoadData([1.2349, 1.2368, 1.2392, 1.2347, … ]);

However, if you prefer, all FXB.ta calculations can be changed so that they are indexed the other way round, with the oldest value in #0 and the newest value in length-1.

You do this by setting oldestFirst:true, either in the initialisation of the calculation or by changing the oldestFirst property at any later point. For example:

// Create a calculation with oldestFirst turned on

var myEma = new FXB.ta.EMA({period: 20, oldestFirst: true});

// Data should now be loaded oldest-first, not newest-first

myEma.LoadData([ …, …, …, 1.2347, 1.2392, 1.2368, 1.2349]);

// Because of oldestFirst, GetCurrentValue() is now equivalent to GetValue(length-1),

// not to GetValue(0)

var currentValue = myEma.GetCurrentValue();

At the risk of creating considerable potential confusion and bugs for yourself, it's possible to load data newest-first (because that is how the framework provides it by default) and then to change the indexing to oldest-first before reading the results:

var myEma = new FXB.ta.EMA({period: 20});

// oldestFirst is not yet turned on, and data should be loaded newest-first

myEma.LoadData([1.2349, 1.2368, 1.2392, 1.2347, … ]);

// Turn on oldestFirst, after loading

myEma.oldestFirst = true;

// Because of oldestFirst, the current latest item is in #length-1

var currentValue = myEma.GetValue(myEma.length - 1);

// And GetCurrentValue() is now equivalent to GetValue(length-1), not to GetValue(0)

var alsoCurrentValue = myEma.GetCurrentValue();

5.2.6Combining arrays

The FXB.ta library contains a helper function for combining the results from different arrays: FXB.ta.ArrayCombine(). For example, you can take an array of moving averages and an array of ATR values and add them together.

A simple example of ArrayCombine() is as follows:

// Subtract arr2 from arr1

var arr1 = [2, 4, 7, 8];

var arr2 = [5, 3, 7, 2];

var res = FXB.ta.ArrayCombine("subtract", arr1, arr2); // Returns [-3, 1, 0, 6]

ArrayCombine() takes three or more parameters: the operation to perform on the arrays, such as addition or subtraction, and two or more arrays to combine. The output of ArrayCombine() is an array of the combined results (or null if your inputs are not valid). If your input arrays have different lengths, then the length of the output is controlled by the first array you provide.

Valid operations to perform on the arrays are "add", "subtract", "multiply", "divide", "average", "minimum", "maximum", and "percent". (The "percent" mode divides the first array by the second array and multiplies by 100.)

Each "array" parameter can in fact be one of three things:

A simple array of values, as in the example above

A list of candles plus the value to use from each candle

A single fixed value

You define a candle input as follows - an object containing a candles[] array of the candle objects, plus a member defining which value to use from the candles.

{

member: "median", // Value to use from each candle, including derived properties

candles: [{o:1.2342, h: 1.2347, l: 12341, c: 1.2343}, …] // Array of candle data

}

For example, you can calculate the range - the difference between high and low - of an array of candles as follows:

// Array of candles to calculate from

var arrCandles = [{o:1.2342, h: 1.2347, l: 12341, c: 1.2343}, …];

// Subtract the low of each candle from the high of each candle - uses the same arrCandles

// for both parameters, but different members

var ranges = FXB.ta.ArrayCombine(

"subtract",

{member: "h", candles: arrCandles},

{member: "l", candles: arrCandles}

);

An input parameter can also be a {fixedValue: X}. For example, you can double all the values in an array as follows:

var arr1 = [2, 4, 7, 8];

var res = FXB.ta.ArrayCombine("multiply", arr1, {fixedValue: 2}); // Returns [4, 8, 14, 16]

Putting these examples together, you can do something such as calculating a moving average, calculating ATR, doubling the value of the ATR, and adding that to the moving average - which creates the upper band of a Keltner channel. For example:

// Calculate a moving average and ATR on an array of candles

var candles = [{o:1.2342, h: 1.2347, l: 12341, c: 1.2343}, …];

var ma = new FXB.ta.EMA({period: 20, data: candles});

var atr = new FXB.ta.ATR({period: 10, data: candles});

// Calculate double the ATR, using ArrayCombine()

var arrDoubleATR = FXB.ta.ArrayCombine("multiply", atr.GetValueArray(), {fixedValue: 2});

// Add double the ATR to the moving average, using ArrayCombine()

var arrKeltnerUpper = FXB.ta.ArrayCombine("add", ma.GetValueArray(), arrDoubleATR);

5.2.7Moving averages

The FXB.ta library provides several classes for creating different types of moving average, such as FXB.ta.EMA and FXB.ta.DEMA, listed below (and also widely used in the examples above).

However, the library also provides a way of creating moving-average calculations where the type of moving average is supplied as a variable, using the FXB.ta.CreateMovingAverage() function.

CreateMovingAverage() takes two parameters: the type of moving average, and the usual initialisation parameters which are then passed into the moving-average calculation in the normal way. For example:

var maType = "ema";

var parameters = {period: 30, member: "typical"};

// Create a type of moving average via the maType variable.

// Ends up being equivalent to: someMA = new FXB.ta.EMA(parameters);

var someMA = FXB.ta.CreateMovingAverage("ema", parameters);

Some calculations in the FXB.ta library use moving averages internally, and let you change the type of average by setting an initialisation parameter. For example, the Bollinger band calculation defaults to using a simple moving average but can be changed to using any other type of moving average. The FXB.ta.Bands calculation takes the parameter you provide, or its default, and does an internal call to CreateMovingAverage():

// Change the bands calculation to use a Hull MA instead of the default SMA

var bands = new FXB.ta.Bands({period: 20, deviations: 2, maType: "hull"});

The types of moving average are as follows. Each has both a numeric and a textual ID, and you can use either:

Numeric

Textual

FXB.ta.

Description

0

sma

SMA

Simple moving average

1

ema

EMA

Exponential moving average

4

vma

VMA

Variable moving average

5

vidya

VIDYA

Volatility Index Dynamic Average

7

wma

WMA

Weighted moving average

8

smma

SMMA

Smoothed moving average

9

hull

HullMA

Hull moving average

10

lsma

LSMA

Least-squares moving average

11

alma

ALMA

Arnaud Legoux moving average

12

dema

DEMA

Double exponential moving average

13

tema

TEMA

Triple exponential moving average

14

smaofsma

SMAofSMA

Simple moving average of a simple moving average

15

emaofema

EMAofEMA

Exponential moving average of an exponential moving average

16

mcginley

McGinleyMA

McGinley dynamic moving average

17

ama

AMA

Adaptive moving average

Giving another version of the example above, you can create a least-squares moving average in two ways:

var ma = new FXB.ta.LSMA({period: 20});

var ma = FXB.ta.CreateMovingAverage(10, {period: 20}); // 10 = "lsma" = FXB.ta.LSMA()

All moving averages need a period parameter in their initialisation - as widely used in various examples above.

Some of the types of moving average also require other parameters, though these have standard defaults and you may not want to change them or specify them explicitly:

Moving average

Extra parameters

ALMA

Uses offset and sigma parameters. The standard values are 0.85 and 6.

AMA

Uses fast and slow parameters for smoothing, defaulting to 2 and 10 respectively.

SMAofSMA

Uses a subPeriod parameter. The calculation does a moving average using the main period parameter, and then a moving average of that moving average using the subPeriod. If you do not provide a value for subPeriod, then it defaults to the same as period.

EMAofEMA

Same as SMAofSMA

VIDYA

Uses a cmoPeriod parameter, defaulting to the same as the main period

These extra parameters can be added to the ID of a moving average when creating an instance of the calculation via a variable. For example, the ID "alma:0.9:5" (or "11:0.9:5") means "ALMA, with an offset of 0.9 and sigma of 5". Similarly, "ama:5:15" (or "17:5:15") means "AMA, with a fast period of 5 and slow period of 15".

5.2.8List of technical analysis calculations

Currently available technical analysis calculations are as follows. This list may be extended in future releases of Figaro. Many other common calculations can be easily derived from the types here. For example, an "Awesome Oscillator" is simply the difference between the 5- and 34-period simple moving averages of the median price. It can be calculated using FXB.ta.MACD:

// The signal and smoothing values are irrelevant; only need/use the main "MACD" value for AO

var ao = new FXB.ta.MACD({fast: 5, slow: 34, member: "median", maType: "sma"});

The following list does not include moving averages, such FXB.ta.EMA or FXB.ta.HullMA. The moving averages are listed separately above.

The list shows the class within FXB.ta, such as FXB.ta.Slope(), and whether the calculation is bar-based or value-based.

Calculation class

Bar/value-based

Description

ADX

Bar

Directional movement system

AO

Value

Awesome Oscillator

Aroon

Bar

Aroon

AroonOsc

Bar

Aroob oscillator

ASI

Bar

Accumulative swing index

ATR

Bar

Average true range

Bands

Value

Bollinger® bands: a moving average plus/minus standard deviation

BearBull

Bar

Bear/bull power - one candle property minus an average of another candle property

CCI

Value

Commodity channel index

CFO

Value

Chande forecast oscillator

ChaikinVolatility

Bar

Chaikin Volatility

CMF

Bar

Chaikin Money Flow

CMO

Value

Chande momentum oscillator

COG

Value

Centre of gravity

CoppockCurve

Value

Coppock curve

DerivedPrice

Bar

Derived prices from candle data

DPO

Value

Detrended price oscillator

EFI

Bar

Elder force index

EFT

Value

Ehler Fisher transform

EMT

Bar

Elder market thermometer

EOM

Bar

EaseOfMovement

Envelope

Value

Envelopes around a moving average: the average plus/minus a fixed percentage

GRI

Bar

Gopalakrishnan range index

Highest

Value

Highest of the last N values

HV

Value

Historical volatility

IMI

Bar

Intraday momentum index

Keltner

Bar

Keltner channel (and STARC): moving average plus/minus ATR

KVO

Bar

Klinger volume oscillator

Lowest

Value

Lowest of the last N values

LRF

Value

Linear forecast

LRI

Value

Linear intercept

MACD

Value

Moving-average convergence/divergence: difference between two averages, with smoothing of that difference

MAOscillator

Value

Oscillator of a moving average

MassIndex

Bar

Mass index

MktFacilitation

Bar

Market facilitation index

Momentum

Value

Momentum oscillator

MoneyFlowIndex

Bar

Money Flow Index oscillator

Performance

Value

Performance index

PGO

Bar

Pretty Good oscillator

PO

Value

Price oscillator

PSAR

Bar

Parabolic stop and reverse

QStick

Bar

QStick

Rainbox

Value

Rainbow oscillator

Rank

Value

Ranking

RAVI

Value

RAVI

RSI

Value

Relative strength index

RVI

Bar

Relative vigor index

RWI

Bar

Random walk index

RSquared

Value

R-squared

Slope

Value

Slope of linear regression

Stdev

Value

Standard deviation

SMI

Bar

Stochastic momentum index

STC

Value

Schaff trend cycle

Stochastic

Bar/Value

Stochastic oscillator

StochRSI

Value

Stochastic RSI

Sum

Value

Sum of the last N items

SwingIndex

Bar

Swing index

TMF

Bar

Twiggs money flow

TRIX

Value

TRIX oscillator

TrueRange

Bar

True range

UO

Bar

Ultimate oscillator

VHF

Value

Vertical horizontal filter

VolumeIndex

Bar

Positive and negative volume index

WAD

Bar

Williams accumulation/distribution

WilliamsPctR

Bar

Williams %R

Accumulative Swing Index - FXB.ta.ASI

The FXB.ta.ASI class calculates accumulative swing index - simply the running total of the swing index. It is a bar-based calculation, and requires candle data as an input. Parameters are the same as for the swing index:

Parameter

Description

limit

Movement limit value, defaulting to 0.5

Note: because it is a running total, the value changes depending on the starting point/length of candle history. It is therefore mostly useful as a relative measure, not as an absolute value.

ADX - FXB.ta.ADX

The FXB.ta.ADX class calculates the directional movement system, ADX. It is a bar-based calculation, and requires candle data as an input. Parameters are as follows:

Parameter

Description

period

Rolling period for the calculation

The primary output value - GetValue() - of the calculation is the ADX. The calculation also provides the DI+ and DI- values:

Parameter

Description

GetPositiveValue(idx)

Gets the DI+ value at index #idx

GetNegativeValue(idx)

Gets the DI- value at index #idx

GetPositiveArray()

Returns an array of all DI+ values

GetNegativeArray()

Returns an array of all DI- values

Aroon - FXB.ta.Aroon

The FXB.ta.Aroon class calculates the Aroon indicator: a measure of the number of bars since the high and low of a period (expressed on a scale of 0 to 100). It is a bar-based calculation, and requires candle data as an input. Parameters are as follows:

Parameter

Description

period

Rolling period for the calculation

Note: the FXB.ta.Aroon is exceptional because the standard GetValue() function returns the value of the Aroon oscillator, not of the Aroon itself. To get the high and low values of the Aroon, use the following functions:

Parameter

Description

GetAroonUpArray()

Returns an array of all the high values

GetAroonDownArray()

Returns an array of all the low values

GetAroonUpValue(idx)

Returns the high value at bar index #idx

GetAroonDownValue(idx)

Returns the low value at bar index #idx

Aroon oscillator - FXB.ta.AroonOsc

The FXB.ta.AroonOsc class calculates the oscillator of the Aroon indicator: its high value minus its low value (leading to output in the range -100 to +100). It is a bar-based calculation, and requires candle data as an input. Parameters are as follows:

Parameter

Description

period

Rolling period for the calculation

Average True Range - FXB.ta.ATR

The FXB.ta.ATR class calculates average true range (a rolling average of true range). It is a bar-based calculation, and requires candle data as an input. Parameters are as follows:

Parameter

Description

period

Rolling period for the calculation

maType

Type of moving average. Defaults to a simple moving average (sma / 0).

Awesome Oscillator - FXB.ta.AO

The FXB.ta.AO class calculates the Awesome Oscillator: the difference between two moving averages (therefore similar to the main value of MACD). It is a value-based calculation, and can be applied to any series of values including the output of another FXB.ta calculation. For candle-based calculations, the default member for the Awesome Oscillator is the typical price, not the close price. Other parameters are as follows:

Parameter

Description

fast

Period for fast moving average (defaults to 5)

slow

Period for slow moving average (defaults to 34)

maType

Type of moving average for the fast and slow averages. Defaults to a simple moving average (sma / 0).

Bear/Bull power - FXB.ta.BearBull

The FXB.ta.BearBull class calculates one candle value minus an average of another candle value. It is normally used to calculate "bull power", the high minus an average of closes, or "bear power", the low minus an average of closes, but can be used with any pair of candle properties. It is a bar-based calculation, and requires candle data as an input. Parameters are as follows:

Parameter

Description

member

Member of each candle to use - normally either h for the high (for "bull power") or l for the low (for "bear power"), but can be any candle data including derived prices

averageOf

Member of each candle from which to calculate an average. Defaults to the close (c)

period

Rolling period for the average

maType

Type of moving average. Defaults to an exponential moving average (ema / 1).

Bollinger® bands - FXB.ta.Bands

The FXB.ta.Bands class calculates Bollinger bands: a moving average plus/minus a multiple of standard deviation. It is a value-based calculation, and can be applied to any series of values including the output of another FXB.ta calculation. Parameters are as follows:

Parameter

Description

period

Rolling period for the calculation

deviations

Multiple of standard deviation (typical value is 2)

maType

Type of moving average. Defaults to a simple moving average (sma / 0).

The primary output value - GetValue() - of the calculation is the moving average. The calculation also sets the signal value - GetSignalValue() - to the standard deviation. And the calculation provides some helper functions for getting the upper and lower bands:

Parameter

Description

GetUpper(idx)

Gets the upper band at index #idx: the main value plus the deviations parameter times the signal value

GetLower(idx)

Gets the lower band at index #idx: the main value minus the deviations parameter times the signal value

GetUpperArray()

Returns an array of all upper-band values

GetLowerArray()

Returns an array of all lower-band values

Centre of Gravity - FXB.ta.COG

The FXB.ta.COG class calculates Centre of Gravity. It is a value-based calculation, and can be applied to any series of values including the output of another FXB.ta calculation. Parameters are as follows:

Parameter

Description

period

Rolling period for the calculation

Commodity channel index - FXB.ta.CCI

The FXB.ta.CCI class calculates the commodity channel index. It is a value-based calculation, and can be applied to any series of values including the output of another FXB.ta calculation. Parameters are as follows:

Parameter

Description

period

Rolling period for the calculation

Chaikin Money Flow - FXB.ta.CMF

The FXB.ta.CMFclass calculates Chaikin money flow. It is a bar-based calculation, and requires candle data as an input. Parameters are as follows:

Parameter

Description

period

Rolling period for the calculation

Chaikin volatility - FXB.ta.ChaikinVolatility

The FXB.ta.ChaikinVolatility class calculates Chaikin volatility. It is a bar-based calculation, and requires candle data as an input. Parameters are as follows:

Parameter

Description

period

Rolling period for the calculation

maType

Type of moving average, for smoothing. Defaults to an exponential moving average (ema / 1).

smoothingPeriod

Smoothing period, for the moving average

Chande forecast oscillator - FXB.ta.CFO

The FXB.ta.CFO class calculates the Chande forecast oscillator. It is a value-based calculation, and can be applied to any series of values including the output of another FXB.ta calculation. Parameters are as follows:

Parameter

Description

period

Rolling period for the calculation

Chande momentum oscillator - FXB.ta.CMO

The FXB.ta.CMO class calculates the Chande momentum oscillator. It is a value-based calculation, and can be applied to any series of values including the output of another FXB.ta calculation. Parameters are as follows:

Parameter

Description

period

Rolling period for the calculation

Coppock curve - FXB.ta.CoppockCurve

The FXB.ta.CoppockCurve class calculates the Coppock curve. It is a value-based calculation, and can be applied to any series of values including the output of another FXB.ta calculation. Parameters are as follows:

Parameter

Description

period

Rolling period for the calculation

Derived price - FXB.ta.DerivedPrice

The FXB.ta.DerivedPrice simply returns a derived price such as "median" or "typical" from an array of candles. It is therefore a bar-based calculation, and it has no parameters apart from the derived member to return. For example, calculating a list of median prices from a candle array:

var medians = new FXB.ta.DerivedPrice({member: "median", data: [{o: 1.2456, …}, { … }]);

Detrended price oscillator - FXB.ta.DPO

The FXB.ta.DPO class calculates the Detrended Price Oscillator. It is a value-based calculation, and can be applied to any series of values including the output of another FXB.ta calculation. Parameters are as follows:

Parameter

Description

period

Rolling period for the calculation

maType

Type of moving average. Defaults to an exponential moving average (ema / 1).

Ease of movement - FXB.ta.EOM

The FXB.ta.EOM class calculates the ease of movement index. It is a bar-based calculation, and requires candle data as an input. Parameters are as follows:

Parameter

Description

period

Rolling period for the moving average

maType

Type of moving average. Defaults to an exponential moving average (ema / 1).

Ehler Fisher transform - FXB.ta.EFT

The FXB.ta.EFT class calculates the Ehler Fisher transform. It is a value-based calculation, and can be applied to any series of values including the output of another FXB.ta calculation. Parameters are as follows:

Parameter

Description

period

Rolling period for the calculation

Elder Market Thermometer - FXB.ta.EMT

The FXB.ta.EMT class calculates the Elder market thermometer, and a moving average of the thermometer. It is a bar-based calculation, and requires candle data as an input. Parameters are as follows:

Parameter

Description

period

Rolling period for the moving average

maType

Type of moving average. Defaults to an exponential moving average (ema / 1).

The primary output values - GetValue() etc - contain the thermometer. The signal values - GetSignalValue() etc - contain the moving average.

Elder Force Index - FXB.ta.EFI

The FXB.ta.EFI class calculates the Elder force index, and a moving average of the index. It is a bar-based calculation, and requires candle data as an input. Parameters are as follows:

Parameter

Description

period

Rolling period for the moving average

maType

Type of moving average. Defaults to an exponential moving average (ema / 1).

The primary output values - GetValue() etc - contain the force index. The signal values - GetSignalValue() etc - contain the moving average.

Envelope around a moving average - FXB.ta.Envelope

The FXB.ta.Envelope class calculates the "envelope" around a moving average: the average plus/minus a fixed percentage. It is a value-based calculation, and can be applied to any series of values including the output of another FXB.ta calculation. Parameters are as follows:

Parameter

Description

period

Rolling period for the calculation

bandPercent

Fixed percentage for the plus/minus envelope (defaults to 0.1 - in other words, addition/subtraction of the moving average x 0.001)

maType

Type of moving average. Defaults to a simple moving average (sma / 0).

The primary output value - GetValue() - of the calculation is the moving average. The calculation also sets the signal value - GetSignalValue() - to the size of the envelope: each moving average multiplied by the bandPercent divided by 100. And the calculation provides some helper functions for getting the upper and lower bands:

Parameter

Description

GetUpper(idx)

Gets the upper band at index #idx: the main value plus the deviations parameter times the signal value

GetLower(idx)

Gets the lower band at index #idx: the main value minus the deviations parameter times the signal value

GetUpperArray()

Returns an array of all upper-band values

GetLowerArray()

Returns an array of all lower-band values

Fractal bands - FXB.ta.FractalBands

The FXB.ta.FractalBands class calculates fractal bands: the high and low from the last confirmed fractal. It is a bar-based calculation, and requires candle data as an input. Parameters are as follows:

Parameter

Description

period

Number of bars required to form a fractal; the number either side of the mid-point. Defaults to 2, for a standard five-bar battern.

The calculation sets the primary output - GetValue() etc - to the high values. The signal values - GetSignalValue() etc - contain the low values.

The calculation only uses "confirmed" fractals, starting from the time at which a fractal was known to be confirmed. For example, with the standard period:2, when a new high forms that will only become the value of the band 3 bars later.

Gopalakrishnan range index - FXB.ta.GRI

The FXB.ta.GRI class calculates the Gopalakrishnan range index. It is a bar-based calculation, and requires candle data as an input. Parameters are as follows:

Parameter

Description

period

Rolling period for the calculation

Highest of the last N values - FXB.ta.Highest

The FXB.ta.Highest class calculates a rolling series of the highest of the last N values. It is a value-based calculation, and can be applied to any series of values including the output of another FXB.ta calculation. Parameters are as follows:

Parameter

Description

period

Rolling period for the calculation

In addition to putting the rolling highest values into the main output - GetValue() etc - the calculation also puts the relative offset of the highest value into the signal output - GetSignalValue(). For example, a signal value of 1 means that the highest value is the preceding one.

Historical Volatility - FXB.ta.HV

The FXB.ta.HV class calculates historical volatility: the standard deviation of the changes in a value, multiplied by a factor such as 260 to create an annualised figure. It is a value-based calculation, and can be applied to any series of values including the output of another FXB.ta calculation. Parameters are as follows:

Parameter

Description

period

Rolling period for the calculation

deviations

Number of standard deviations to calculate

factor

Factor (calculation uses the square root of this number)

Intraday momentum index - FXB.ta.IMI

The FXB.ta.IMI class calculates the intraday momentum index. It is a bar-based calculation, and requires candle data as an input. Parameters are as follows:

Parameter

Description

period

Rolling period for the calculation

Keltner channel and STARC - FXB.ta.Keltner

The FXB.ta.Keltner class calculates a Keltner channel: a moving average plus/minus ATR. Changing the default maType from EMA to SMA in effect turns the Keltner channel into a Stoller channel (STARC). It is a bar-based calculation (because it uses ATR), and requires candle data as an input. Parameters are as follows:

Parameter

Description

maPeriod

Period for the moving average which forms the centre of the channel (defaulting to 20)

maType

Type of moving average. Defaults to an exponential moving average (ema / 1).

atrPeriod

Period for the ATR calculation (doesn't have to be same as the moving-average period, and separately defaults to 10)

atrMultiple

Multiple of the ATR value for calculating the upper and lower bands (defaults to 2)

The primary output value - GetValue() - of the calculation is the moving average. The calculation also sets the signal value - GetSignalValue() - to the ATR value. And the calculation provides some helper functions for getting the upper and lower bands:

Parameter

Description

GetUpper(idx)

Gets the upper band at index #idx: the main value plus the atrMultiple parameter times the signal value

GetLower(idx)

Gets the lower band at index #idx: the main value minus the atrMultiple parameter times the signal value

GetUpperArray()

Returns an array of all upper-band values

GetLowerArray()

Returns an array of all lower-band values

Klinger volume oscillator - FXB.ta.KVO

The FXB.ta.KVO class calculates the Klinger volume oscillator. It is a bar-based calculation, and requires candle data as an input. Parameters are as follows:

Parameter

Description

fast

Period for fast moving average (e.g. 12)

slow

Period for slow moving average (e.g. 26)

signal

Signal/smoothing period (e.g. 9)

maType

Type of moving average for the fast and slow averages. Defaults to an exponential moving average (ema / 1).

smoothingType

Type of moving average for the smoothing of the differences. Defaults to a simple moving average (sma / 0).

The oscillator sets both primary and signal outputs.

Linear regression forecast - FXB.ta.LRF

The FXB.ta.LRF class calculates linear forecast. It is a value-based calculation, and can be applied to any series of values including the output of another FXB.ta calculation. Parameters are as follows:

Parameter

Description

period

Rolling period for the calculation

In addition to the primary output, the class also sets the signal values to the slope of the linear regression (same as FXB.ta.Slope).

Linear regression intercept - FXB.ta.LRI

The FXB.ta.LRI class calculates linear intercept. It is a value-based calculation, and can be applied to any series of values including the output of another FXB.ta calculation. Parameters are as follows:

Parameter

Description

period

Rolling period for the calculation

In addition to the primary output, the class also sets the signal values to the slope of the linear regression (same as FXB.ta.Slope).

Lowest of the last N values - FXB.ta.Lowest

The FXB.ta.Lowest class calculates a rolling series of the lowest of the last N values. It is a value-based calculation, and can be applied to any series of values including the output of another FXB.ta calculation. Parameters are as follows:

Parameter

Description

period

Rolling period for the calculation

In addition to putting the rolling lowest values into the main output - GetValue() etc - the calculation also puts the relative offset of the lowest value into the signal output - GetSignalValue(). For example, a signal value of 1 means that the lowest value is the preceding one.

MACD - FXB.ta.MACD

The FXB.ta.MACD class calculates moving average convergence/divergence. It is a value-based calculation, and can be applied to any series of values including the output of another FXB.ta calculation. Parameters are as follows:

Parameter

Description

fast

Period for fast moving average (e.g. 12)

slow

Period for slow moving average (e.g. 26)

signal

Signal/smoothing period (e.g. 9)

maType

Type of moving average for the fast and slow averages. Defaults to an exponential moving average (ema / 1).

smoothingType

Type of moving average for the smoothing of the differences. Defaults to a simple moving average (sma / 0).

The MACD has three outputs:

The primary value (GetValue): the difference between the fast and slow moving averages

The signal value (GetSignalValue): the smoothed moving average of the differences

"Histogram" (so named because that's how it's usually drawn on a chart): the difference minus the smoothing of the difference.

In addition to the usual functions for getting the primary and signal values, the FXB.ta.MACD has further helpers for getting the histogram values:

Parameter

Description

GetHistogramValue(idx)

Returns the histogram value at index #idx

GetHistogramArray()

Returns an array of all the histogram values, similar to GetValueArray() and GetSignalValueArray()

Market facilitation index - FXB.ta.MktFacilitation

The FXB.ta.MktFacilitation class calculates the market facilitiation index: the high-low range of a bar divided by volume. It is a bar-based calculation, and requires candle data as an input. The calculation has no parameters.

Mass index - FXB.ta.MassIndex

The FXB.ta.MassIndex class calculates mass index. It is a bar-based calculation, and requires candle data as an input. Parameters are as follows:

Parameter

Description

period

Period for totalling, typically 25

subperiod

Period for moving averages, typically 9

Momentum oscillator - FXB.ta.Momentum

The FXB.ta.Momentum class calculates the momentum oscillator: simply the change in value compared to N periods ago. It is a value-based calculation, and can be applied to any series of values including the output of another FXB.ta calculation. Parameters are as follows:

Parameter

Description

period

Rolling period for the moving average

Money Flow Index - FXB.ta.MoneyFlowIndex

The FXB.ta.MoneyFlowIndex class calculates the Money Flow index oscillator. It is a bar-based calculation, and requires candle data as an input. Parameters are as follows:

Parameter

Description

period

Period for totalling, typically 25

Moving average oscillator - FXB.ta.MAOscillator

The FXB.ta.MAOscillator class calculates the oscillator of a moving average: the change since the previous value. It is a value-based calculation, and can be applied to any series of values including the output of another FXB.ta calculation. Parameters are as follows:

Parameter

Description

period

Rolling period for the moving average

maType

Type of moving average

On-balance volume - FXB.ta.OBV

The FXB.ta.OBV class calculates on-balance volume: addition or subtraction of current volume to/from previous volume depending on whether a candle metric (usually, the close price) has risen or fallen. It is a bar-based calculation, and requires candle data as an input. Parameters are as follows:

Parameter

Description

member

Candle member to compare. Defaults to the close (c)

Note: the absolute current value of the OBV changes depending on the starting point/length of candle history. It is therefore mostly useful as a relative measure, not as an absolute value.

Parabolic stop and reverse - FXB.ta.PSAR

The FXB.ta.PSAR class calculates Parabolic Stop and Reverse. It is a bar-based calculation, and requires candle data as an input. Parameters are as follows:

Parameter

Description

minAF

Defaults to 0.02

maxAF

Defaults to 2

Performance - FXB.ta.Performance

The FXB.ta.Performance class calculates performance: the percentage change of each item compared to the initial starting value (not compared to each previous value). It is a value-based calculation, and can be applied to any series of values including the output of another FXB.ta calculation. The calculation has one optional parameter.

Parameter

Description

initial

If not zero (or null), the initial value to compare all the values to. If omitted, the first value in the series is used.

Pretty Good oscillator - FXB.ta.PGO

The FXB.ta.PGO class calculates the Pretty Good oscillator. It is a bar-based calculation, and requires candle data as an input. Parameters are as follows:

Parameter

Description

period

Rolling period for the calculation

Price Oscillator - FXB.ta.PO

The FXB.ta.PO class calculates the "price oscillator": the difference between fast and slow moving averages divided into the slow moving average. It is a value-based calculation, and can be applied to any series of values including the output of another FXB.ta calculation. Parameters are as follows:

Parameter

Description

fast

Period for fast moving average (e.g. 12)

slow

Period for slow moving average (e.g. 26)

maType

Type of moving average for the fast and slow averages. Defaults to an exponential moving average (ema / 1).

QStick - FXB.ta.QStick

The FXB.ta.QStick class calculates QStick - a moving average of the open-close change of each bar. It is a bar-based calculation, and requires candle data as an input. Parameters are as follows:

Parameter

Description

period

Rolling period for the calculation

Rainbow oscillator - FXB.ta.Rainbow

The FXB.ta.Rainbow class calculates the Rainbow oscillator. It is a value-based calculation, and can be applied to any series of values including the output of another FXB.ta calculation. Parameters are as follows:

Parameter

Description

period

Number of iterations for the rainbow

maType

Type of moving average. Defaults to a simple moving average (sma / 0).

Random Walk Index - FXB.ta.RWI

The FXB.ta.RWI class calculates the Random Walk index. It is a bar-based calculation, and requires candle data as an input. Parameters are as follows:

Parameter

Description

period

Rolling period for the calculation

The primary output value - GetValue() - of the calculation is the high values. The calculation puts the low values into the signal values - GetSignalValue() etc.

Rank - FXB.ta.Rank

The FXB.ta.Rank class ranks each value among the rolling group of the last N items, largest/highest first. It is a value-based calculation, and can be applied to any series of values including the output of another FXB.ta calculation. Parameters are as follows:

Parameter

Description

period

Rolling period for the calculation

mode

See below

The mode can be any of the following, defaulting to "percentile":

mode:X

Description

percentile

Percentile ranking. Larger values have higher rankings; the largest value in a series will have percentile rank=100

absolute

Absolute ranking. Larger values have higher rankings. For example, with period=14, the largest item in a series will have rank=14 (and the smallest will have rank=1).

quartile

Quartile ranking. Larger values have lower rankings; the largest item in a series will have quartile rank=1

decile

Decile ranking. Larger values have lower rankings; the largest item in a series will have decile rank=1

Rate of change - FXB.ta.ROC

The FXB.ta.ROC class calculates rate of change: the difference between the current value versus N bars ago, divided into the value N bars ago, times 100. It is a value-based calculation, and can be applied to any series of values including the output of another FXB.ta calculation. Parameters are as follows:

Parameter

Description

period

Rolling period for the calculation

RAVI - FXB.ta.RAVI

The FXB.ta.RAVI class calculates RAVI: very similar to the Price Oscillator except that the numerator in the calculation is the absolute difference between the averages rather than a directional positive/negative. It is a value-based calculation, and can be applied to any series of values including the output of another FXB.ta calculation. Parameters are as follows:

Parameter

Description

fast

Period for fast moving average (e.g. 12)

slow

Period for slow moving average (e.g. 26)

maType

Type of moving average for the fast and slow averages. Defaults to an exponential moving average (ema / 1).

Relative Strength Index - FXB.ta.RSI

The FXB.ta.RSI class calculates the Relative Strength Index oscillator. It is a value-based calculation, and can be applied to any series of values including the output of another FXB.ta calculation. Parameters are as follows:

Parameter

Description

period

Rolling period for the calculation

Relative Vigor Index - FXB.ta.RVI

The FXB.ta.RVI class calculates the Relative Vigor Index oscillator. It is a bar-based calculation, and requires candle data as an input. Parameters are as follows:

Parameter

Description

period

Rolling period for the calculation

The primary output value - GetValue() - of the calculation is the RVI. The calculation also sets the signal value - GetSignalValue() - to the smoothed average of the RVI.

R-squared - FXB.ta.RSquared

The FXB.ta.RSquared class calculates rolling R-squared of a series of values (comparing the values to their position index). It is a value-based calculation, and can be applied to any series of values including the output of another FXB.ta calculation. Parameters are as follows:

Parameter

Description

period

Rolling period for the calculation

Schaff trend cycle - FXB.ta.STC

The FXB.ta.STC class calculates the Schaff trend cycle. It is a value-based calculation, and can be applied to any series of values including the output of another FXB.ta calculation. Parameters are as follows:

Parameter

Description

fast

Period for fast moving average (e.g. 12)

slow

Period for slow moving average (e.g. 26)

smoothingPeriod

Smoothing period (e.g.109)

maType

Type of moving average for the fast and slow averages. Defaults to an exponential moving average (ema / 1).

Slope of linear regression - FXB.ta.Slope

The FXB.ta.Slope class calculates the slope of linear regression of the last N rolling items. It is a value-based calculation, and can be applied to any series of values including the output of another FXB.ta calculation. Parameters are as follows:

Parameter

Description

period

Rolling period for the calculation

Standard deviation - FXB.ta.Stdev

The FXB.ta.Stdev class calculates the standard deviation of the last N rolling items. It is a value-based calculation, and can be applied to any series of values including the output of another FXB.ta calculation. Parameters are as follows:

Parameter

Description

period

Rolling period for the calculation

deviations

Multiple of the standard deviation to return. Defaults to 1.

Stochastic momentum index - FXB.ta.SMI

The FXB.ta.SMI class calculates the Stochastic momentum index. It is a bar-based calculation, and requires candle data as an input. Parameters are as follows:

Parameter

Description

kPeriod

Period for the %K values (scanning bar high and lows)

kSmoothing

First smoothing period

kDoubleSmoothing

Second smoothing period

maType

Type of moving average for the smoothing. Defaults to a simple moving average (sma / 0).

dPeriod

Period for the %D signal values

maTypeD

Type of moving average for the %D signal values. Defaults to a simple moving average (sma / 0).

The primary output - GetValue() etc - is the %K values. The signal output - GetSignalValue() etc - contains the %D values.

Stochastic oscillator - FXB.ta.Stochastic

The FXB.ta.Stochastic class calculates the Stochastic oscillator. It is unusual because it can be either bar-based or value-based depending on the mode. Parameters are as follows:

Parameter

Description

mode

Type of calculation: either "hl" (high-low) or "value", defaulting to "hl". In high-low mode, the calculation is bar-based and requires candle data. In "value" mode, the calculation is value-based and can be calculated on any array of values.

kPeriod

Period for the %K values

dPeriod

Period for the %D values

slowing

Slowing/smoothing which is applied to the %K values

maType

Type of moving average for the smoothing. Defaults to a simple moving average (sma / 0).

The primary output - GetValue() etc - is the %K values. The signal output - GetSignalValue() etc - contains the %D values.

Stochastic RSI - FXB.ta.StochRSI

The FXB.ta.StochRSI class calculates the Stochastic oscillator of RSI values. It is a value-based calculation because RSI is value-based. Parameters and output are the same as for FXB.ta.Stochastic, with the addition of an extra parameter for specifying the RSI period:

Parameter

Description

kPeriod

Period for the %K values

dPeriod

Period for the %D values

slowing

Slowing/smoothing which is applied to the %K values

maType

Type of moving average for the smoothing. Defaults to a simple moving average (sma / 0).

rsiPeriod

Number of bars for the RSI calculation

Sum - FXB.ta.Sum

The FXB.ta.Sum class calculates a rolling total of the last N values (like a simple moving average, but without doing the division by the number of values). It is a value-based calculation, and can be applied to any series of values including the output of another FXB.ta calculation. Parameters are as follows:

Parameter

Description

period

Rolling period for the calculation

Swing index - FXB.ta.SwingIndex

The FXB.ta.SwingIndex class calculates the Swing index. It is a bar-based calculation, and requires candle data as an input. Parameters are as follows:

Parameter

Description

limit

Movement limit value, defaulting to 0.5

TRIX - FXB.ta.Trix

The FXB.ta.TRIX class calculates the TRIX Oscillator: the change in a triple-smoothed exponential moving average. It is a value-based calculation, and can be applied to any series of values including the output of another FXB.ta calculation. Parameters are as follows:

Parameter

Description

period

Rolling period for the calculation

True range - FXB.ta.TrueRange

The FXB.ta.TrueRange class calculates "true range": the maximum of the high-low range of the current bar, the current high minus the previous close, and the current low minus the previous close. It is a bar-based calculation, and requires candle data as an input. It has no parameters.

Twiggs Money Flow - FXB.ta.TMF

The FXB.ta.TMF class calculates Twiggs money flow. It is a bar-based calculation, and requires candle data as an input. Parameters are as follows:

Parameter

Description

period

Rolling period for the calculation

maType

Type of moving average. Defaults to an exponential moving average (ema / 1).

Ultimate oscillator - FXB.ta.UO

The FXB.ta.UO class calculates the Ultimate oscillator. It is a bar-based calculation, and requires candle data as an input. Parameters are as follows:

Parameter

Description

period

Rolling period for the calculation

Vertical horizontal filter - FXB.ta.VHF

The FXB.ta.VHF class calculates the vertical horizontal filter. It is a value-based calculation, and can be applied to any series of values including the output of another FXB.ta calculation. Parameters are as follows:

Parameter

Description

period

Rolling period for the calculation

Volume Index - FXB.ta.VolumeIndex

The FXB.ta.VolumeIndex class calculates positive and negative volume index. It is a bar-based calculation, and requires candle data as an input. Parameters are as follows:

Parameter

Description

member

Candle member. Defaults to the close (c)

The primary output values - GetValue() etc - are set to the positive volume index. The signal values - GetSignalValue() etc - are set to the negative volumed index.

Note: the absolute current value of the indexes changes depending on the starting point/length of candle history. It is therefore mostly useful as a relative measure, not as an absolute value.

Williams Accumulation Distribution - FXB.ta.WAD

The FXB.ta.WAD class calculates Williams accumulation/distribution. It is a bar-based calculation, and requires candle data as an input. The calculation has no parameters.

Note: the absolute current value of the WAD changes depending on the starting point/length of candle history. It is therefore mostly useful as a relative measure, not as an absolute value.

Williams %R - FXB.ta.WilliamsPctR

The FXB.ta.WilliamsPctR class calculates the Williams %R value. It is a bar-based calculation, and requires candle data as an input. Parameters are as follows:

Parameter

Description

period

Rolling period for the calculation

6Widget HTML and CSS reference

The section is only applicable to widgets, and describes standard HTML and CSS configuration in a widget's web page. It does not apply to scripts and UDIXes, which have no web page.

Standard behaviour is for a widget to include both the framework's Javascript and its CSS (in the page's <head>). Doing this means that the widget will automatically participate in the user's selected graphical theme for the software. The widget will inherit standard background and text colours, including live changes if the user selects a different theme.

However, this is not compulsory. A widget is free to ignore or override the framework's CSS, and use its own design.

6.1CSS reference

The best and simplest reference to the framework's CSS is the Developer Console of your web browser. This will show you the CSS variables which the framework declares (at :root level), such as --clr-text and --font-size-base.

The following sections contain some explanatory notes about the standard CSS, not a full itemisation of its variables and classes.

6.1.1Body classes

The framework sets some standard classes on the widget's <body> which may be useful as selectors in your own CSS (for example, changing some colours of your own based on BODY.Theme_light):

CSS class

Description

Theme_x

Identifier for the current graphical theme, such as Theme_standard or Theme_light

LTR or RTL

Identifies whether the user's selected language for the platform is written left-to-right or right-to-left. Sets CSS direction on the <body> (either direction:ltr or direction:rtl). The framework has some helper CSS classes to handle reading order.

BrowserPhoneScreen

Set by the framework if the device qualifies as a "phone-sized" screen. The framework's definition is if either the device width or the device height is less than 500px. Note that this is calculated using the size of the monitor (window.screen in Javascript), not the size of any individual browser frame in the software.

6.1.2Right-to-left reading order

The framework provides some helper CSS classes for handling reading order. The following classes invert, between left and right, if the user's selected language is right-to-left. For example, the definition of TEXTL becomes {text-align: right}.

CSS class

Description

TEXTL

text-align:left by default, inverting to right for right-to-left languages

TEXTR

text-align:right by default, inverting to left for right-to-left languages

FLOATL

float:left by default, inverting to right for right-to-left languages

FLOATR

float:right by default, inverting to left for right-to-left languages

TextDirection

direction:ltr by default, inverting to rtl for right-to-left languages

6.1.3Button CSS classes

There are some standard CSS classes for buttons. Note: in some future graphical themes of the software, the colour might maintain the general connotations of the class/type but not match the actual colour name.

CSS button class

Common purpose

btn-green

A button for accepting, saving, granting permission etc

btn-red

A button with a dangerous or destructive result: deleting something, carry out a dangerous action etc

btn-blue

A button for carrying out an action with no strong positive or negative connotation

btn-standard

A button with no associated action (other than e.g. closing a dialog)

btn-amber

Normally associated with a mild warning, without the full connotations of btn-red

btn-pink

(no common association)

6.1.4FontAwesome icons

The framework automatically includes the Font Awesome icons (v6), but does not include the standard Font Awesome CSS. The icons are imported into the framework's CSS as font-family:FontAwesome, and the framework's CSS includes a .FA class whose definition is {font-family:FontAwesome}. For example:

<!-- Display FontAwesome icon, inline, using the hex code in the form &#xAAAA; -->

<span class="FA">&#xf058;</span>

Or, using CSS to declare a class which displays an icon using :after:

.tickicon {

font-family: FontAwesome;

}

.tickicon:after {

content: "\f058";

}

6.2Standard HTML structure

Figaro's built-in widgets have little standard HTML structure because they display such a wide variety of information and functionality.

6.2.1Standard dialog structure

There is a standard HTML structure for dialogs, potentially relevant if your widget is displaying itself as its own dialog.

<div class="DialogStructure">

<div class="DialogTitle"></div>

<div class="DialogContent DialogContentIndent"></div>

<div class="DialogFooter"></div>

</div>

The DialogContent automatically expands and shrinks to fill the available space, and with overflow:auto. It is not compulsory for a dialog to have a title and/or footer. These elements can be omitted.

You may want to re-use the DialogStructure for things which are not dialogs. If a widget is not being displayed as a dialog, then the framework's standard CSS automatically hides the DialogTitle. You can force it to be visible by adding DialogTitleAlways: class="DialogTitle DialogTitleAlways".

Adding the class DialogTitleMini to the title reduces the font size and height of the title.

6.3Pop-up menus

The framework provides a standard mechanism for displaying pop-up menus. This has three advantages over a widget implementing its own menu HTML:

Standard look and feel with Figaro's built-in widgets

On small screens such as mobile phones, the framework automatically converts menus into a full-screen dialog (the MenuListDialog).

The framework's menus can extend outside the boundary of a widget's frame. If your widget is in a small frame, such as a small segment of a multi-widget layout in the Figaro page container, any menu which the widget displays itself will be confined to its small frame.

A widget can display a pop-up menu using Framework.CreatePopupMenu(). This takes three parameters:

Parameter

Description

menu

Definition of the menu (which can contain items with sub-menus)

eventOrNode

Either a node in the DOM, or a Javascript event object from which the framework uses the event.target. The menu is positioned relative to this node.

callback

Asynchronous callback function which receives the user's selection from the menu

For example:

// Add a click handler on someButton which displays a menu next to it

someButton.addEventListener("click", function(event) {

var menuDef = { … some menu definition … };

Framework.CreatePopupMenu(menuDef, event, function(Msg) {

// Receives the asynchronous result of the menu

});

});

The definition of a menu is the same as for Framework.MenuListDialog(), and described in detail in that section. The menu should have a title (only used on small screens when converted into a MenuListDialog), and an array of items[]. Each item can have an icon, custom colours, an array of sub-items etc.

The asynchronous callback function receives the user's selection from the menu, or cancellation. If there was a selection, then the Msg will contain an itemData which is simply the selected entry from the items[] array. For example:

Framework.CreatePopupMenu(menuDef, event, function(Msg) {

if (Msg.itemData) {

// User made a selection, and the original object from items[]

// is in Msg.itemData

} else {

// User dismissed the menu without making a selection

}

});

7FXB.utils and other helper functions

The framework contains some helper functions, mainly within the FXB.utils namespace. In particular, the FXB.utils.Dictionary() is extremely widely used within the framework.

7.1FXB.utils.Dictionary()

The FXB.utils.Dictionary class is a dictionary/hashtable which is extremely widely used within the Figaro framework. For example, the FXB.Instruments collection is a dictionary, and so is each FXB.Message which is sent to an OnMessage() handler.

The dictionary's properties and methods are as follows:

Object

Description

length

Property returning the number of items in the dictionary

set(key, object)

Sets a key/value pair in the dictionary

remove(key)

Removes a key/value pair from the dictionary. (Trying to remove a non-existent key is not an error; it just silently does nothing.)

get(key)

Returns a value from the dictionary based on its key, or null if the key is not present

has(key)

Tests whether a key exists in the dictionary

is(keyOrArray)

Can take either a single key as a parameter, or an array of keys. Passing a single key is simply equivalent to has(). Passing an array means "does the dictionary contain at least one of these keys?". In other words, using is() with an array does an is-any-of test.

keys()

Returns all the keys in the dictionary as an array

values()

Returns all the values in the dictionary as an array

clear()

Empties the dictionary

forEach(fn)

Iterator. See description below.

The dictionary's forEach() is similar to standard Javascript Array.forEach() except that it allows early exit, by returning a non-null from the iteration function. For example, you can scan the dictionary and terminate at the first match, avoiding unnecessary further iteration through the dictionary:

// See if there is any object in the dictionary which has some particular value

var bHasValueWithProperty = false;

myDictionary.forEach(function (key, object) {

if (object.someProperty == someValue) {

// Set flag

bHasValueWithProperty = true;

// Exit from iteration, and avoid unnecessary further looping

return true;

}

});

Any non-null return value which terminates the iteration is passed back as the return value from forEach(). Therefore, the following is possible:

// See if there is any object in the dictionary which has some particular value.

// On return, matchingObject will contain either null or the first matching item.

var matchingObject = myDictionary.forEach(function (key, object) {

if (object.someProperty == someValue) {

// Exit from iteration, and avoid unnecessary further looping.

// The non-null value becomes the return value from the forEach().

return object;

}

});

Note: it is safe to modify the contents of the dictionary during forEach() iteration. For example:

// Create a dictionary with 10 items

var x = new FXB.utils.Dictionary();

for (var i = 1; i <= 10; i++) x.set(i, i);

// Gets called 9 times because item 3 is removed on the first pass (where k = 1)

x.forEach (function (k, v) {

if (x.has(3)) x.remove(3); // Remove item 3 on first iteration

});

7.2FXB.utils.Clone()

FXB.utils.Clone() is a simple function for doing a deep clone of an object, but excluding any functions within the object (making it safe for serialisation). The current internal implementation of FXB.utils.Clone(x) is simply JSON.parse(JSON.stringify(x)), but this may change in future if a faster alternative becomes available.

7.3FXB.utils.IsEmpty()

Simple helper function which tests whether an object x is an empty object, {}. For example:

var bIsEmpty = FXB.utils.IsEmpty(someObject);

7.4FXB.utils.GenerateGuid()

FXB.utils.GenerateGuid() creates an ID (32 hexadecimal characters) which is guaranteed to be unique within the current browser session, and is not guaranteed but is overwhelmingly likely to be globally unique.

7.5FXB.utils.GenerateHash()

FXB.utils.GenerateHash(string) generates a hash of a string as a 32-bit integer. The hashing algorithm is subject to change, but is currently murmur32.

7.6$$()

For widgets only - because scripts and UDIXes are web workers, and have no HTML document - the framework creates a $$() short-hand for accessing DOM nodes. For example:

$$("btnSave").disabled = true;

$$() is simply a short-hand alternative to document.getElementById(x), though its full functionality is slightly broader. If there is no DOM node with ID x, then $$() goes on to try document.getElementsByClassName(x). There are three possible results from getElementsByClassName():

Empty array. $$() returns null.

Array with single item. $$() returns that single item (not the array).

Array with multiple items. $$() returns the array.

To put all that another way:

If there is a single node with class=x, then $$(x) is equivalent to document.getElementsByClassName(x)[0]

If there are multiple nodes with class=x, then $$(x) is equivalent to document.getElementsByClassName(x)

8Framework error codes

The following section describes the code values of an FXB.Result object - error codes in the framework. Almost all the error codes relate to trading requests.

All the values described in this section are in the FXB.ErrorCodes enumeration, for example FXB.ErrorCodes.OKAY.

8.1Success/no-failure values

There are three values which are treated by FXB.Result as isOkay=true and isError=false:

FXB.ErrorCodes.

Description

OKAY

No error; success

SUCCESS

Synonym for OKAY; same numeric value (zero)

CANCELLED

Action cancelled or dismissed by the user

NO_ACTION_REQUIRED

No action. For example, a trading request such as FLATTEN when there are no open orders or trades on the account.

All the remaining values represent error conditions, and the FXB.Result will report isOkay=false and isError=true.

8.2Not-yet-implemented errors

The following errors are not currently issued by the framework under any circumstances. But in future they may be issued if e.g. the implementation of some functionality is specific to each broker back-end platform, and Figaro has only completed the integration with some platforms:

FXB.ErrorCodes.

Description

NOTYETIMPLMENTED

Functionality has not yet been implemented.

TR_NOTYETIMPLEMENTED

More specifically, trading functionality which has not yet been implemented

8.3Failed internal validation of trading actions

The following error codes represent failed internal validation of trading actions - e.g. no trading volume specified - before trying to pass them to the broker's back-end system.

FXB.ErrorCodes.

Description

TR_INVALIDJOBDEFINITION

Invalid request definition for a trading job (generally invalid, rather than a problem such as TR_NOVOLUME)

TR_BADINSTRUMENT

Unrecognised or missing instrumentId

TR_BELOWMINIMUMVOLUME

Specified volume is below the minimum for the market

TR_CANTUSEBREAKEVENONPENDING

Attempt to set break-even exit on a pending order rather than an open trade

TR_TRADESLWRONGSIDEOFBID

Stop-loss on an open buy trade is the wrong side of the current (bid) price

TR_TRADESLWRONGSIDEOFASK

Stop-loss on an open sell trade is the wrong side of the current (ask) price

TR_TRADETPWRONGSIDEOFBID

Take-profit on an open buy trade is the wrong side of the current (bid) price

TR_TRADETPWRONGSIDEOFASK

Take-profit on an open sell trade is the wrong side of the current (ask) price

TR_ORDERSLWRONGSIDEOFBUYENTRY

Stop-loss on a pending buy order is the wrong side of the entry price for the order

TR_ORDERSLWRONGSIDEOFSELLENTRY

Stop-loss on a pending sell order is the wrong side of the entry price for the order

TR_ORDERTPWRONGSIDEOFBUYENTRY

Take-profit on a pending buy order is the wrong side of the entry price for the order

TR_ORDERTPWRONGSIDEOFSELLENTRY

Take-profit on a pending sell order is the wrong side of the entry price for the order

TR_ACCOUNTREADONLY

Access to the trading account is read-only and trading is not permitted (Account.accountReadOnly = true)

TR_UNEXPECTEDTRADINGACTION

Unexpected trading action, such as trying to close an order ID which identifies a credit record on the account rather than an order or trade

TR_INTERNALERROR

Unexpected internal trading error

TR_NOVOLUME

No volume specified on trading request

TR_NOENTRYPRICE

No entry price specified for a new pending order

TR_NEEDSLWITHRISKVOLUME

Risk-based trading volume, such as {cashRisk: x}, requires a stop-loss

TR_CANTHAVERISKVOLUMEANDSL

Can't set both the volume and stop-loss to cash/risk based values; one of them must be an absolute value rather than a cash-based value such as {equityPercent: 0.5}

TR_PARTIALCLOSENOEFFECT

Requested partial close will have effect. For example, a percentage-based partial close where the resulting volume is less than the minimum trading increment for the market.

TR_PARTIALCLOSEISFULLCLOSE

Requested partial close will execute a full close. (For example: volume is 10K, minimum trading increment is 1K; a request for a 95% close gets rounded up to 10K)

TR_PARTIALCLOSENOTMULTIPLE

Requested volume for a partial close is not a multiple of the minimum trading increment on the market (e.g. closing 5K on a market where the minimum volume is 10K)

TR_AMBIGUOUSENTRYOFFSET

Can't use price offsets for the entry where the tradingAction is BUY_PENDING or SELL_PENDING

TR_NOCANDLEDATAFORCOMMAND

Unable to get the candle data required for a trading action

TR_RRRWITHNOSTOPLOSS

Can't use an rrr value for the take-profit unless a stop-loss is specified.

8.4Trading requests blocked by user settings/preferences

The Figaro platform lets the user set options such as "don't allow trades and orders without a stop-loss" or "don't allow trades or orders which risk more than x% of equity" (based on the order's volume and stop-loss).

The following error codes represent breaches of the user's rules:

FXB.ErrorCodes.

Description

TR_TRADINGGLOBALLYDISABLED

User has disabled all trading in the platform's settings

TR_SETTINGSREQUIRESL

User's settings require that all orders and trades have a stop-loss

TR_SLTOOWIDEFORMAXRISK

Volume and stop-loss imply a cash risk which is beyond the user's specified maximum

TR_MINRISKTOOLARGE

Special case of the above: even the minimum trading volume on the market would, with the specified stop-loss, imply a cash risk which is beyond the user's specified maximum

TR_WIDGETTYPECANTTRADE

Your widget (category) has not been given permission to place trades

8.5Rejection by trading validators

There are two error codes relating to trading validators:

FXB.ErrorCodes.

Description

TR_VALIDATORREJECTION

Trading validator rejected the request ({status: "block"})

TR_VALIDATORTIMEOUT

Time-out while collecting a response from active trading validators

8.6Trading requests rejected by the broker platform

The following error codes describe scenarios where a trading request passed internal validation by the framework; was sent to the broker; but the broker rejected the request (or failed in some other way).

There are some error codes whose meaning overlaps, or which are very near duplicates (such as TR_MARKETMOVED and TR_REQUOTE). These reflect different classes of rejection in different back-end broker platforms.

FXB.ErrorCodes.

Description

TR_NOCONNECTION

No current connection to the back-end broker platform

TR_CLIENTSIDETIMEOUT

Time-out (client-side, implemented by the framework) while waiting for a response from the back-end broker platform

TR_BROKERERROR

General, uncategorised error reported by the back-end broker platform

TR_GENERALREJECTION

General, uncategorised rejection reported by the back-end broker platform

TR_ACCOUNTSUSPENDED

Trading account is reported as suspended

TR_ACCOUNTCLOSED

Trading account is reported as closed

TR_NOFUNDS

Too little margin to place the trade

TR_MARKETCLOSED

Market is currently closed

TR_TOOMANYPENDINGORDERS

Too many open pending orders

TR_TOOMANYPOSITIONS

Too many open positions

TR_TOOMUCHVOLUME

Too much open volume

TR_LONGTRADESONLY

Only long trades are permitted on this market

TR_SHORTTRADESONLY

Only short trades are permitted on this market

TR_CLOSETRADESONLY

The market is in close-only mode. New orders are not accepted.

TR_MARKETSUSPENDED

Trading on this market has been suspended by the broker

TR_VOLUMETOOSMALL

Requested trading volume is too small

TR_VOLUMETOOLARGE

Requested trading volume is too large

TR_VOLUMEINVALID

Requested trading volume is reported as not valid

TR_VOLUMEREJECTED

General rejection of the requested trading volume

TR_MANDATORYSTOP

Broker requires that the order has a stop-loss

TR_ALEADYCLOSED

Request to close an order or trade which is already closed

TR_ORDERLOCKED

Order/trade is currently locked by the broker and cannot be modified or closed

TR_ORDERMISSING

Request to modify an order or trade which does not exist

TR_TOOMANYREQUESTS

Broker rejected the request because the framework is making too many requests

TR_INVALIDEXPIRY

Invalid expiry option/value on an order

TR_HEDGINGNOTALLOWED

Order would create a hedged trade, and is not permitted by the account

TR_MUSTUSEFIFO

Orders must be processed (usually: specifically, closed) in FIFO order on a non-hedging account

TR_TOOCLOSETOMODIFY

Back-end system does not allow modification of the order because its entry/exit is too close to the current price

TR_PRICEINDICATIVE

Market price is indicative, and cannot be traded against

TR_INSTRUCTIONREJECTIONWITHCODE

Platform-specific order rejection

TR_REJECTIONWITHCODE

Platform-specific order rejection

TR_NOPRICEFORJOB

Unable to obtain a price for executing the order

TR_TRIGGERTOOCLOSETOMARKET

Price (typically, entry price on a pending order) is too close to the current market price

TR_AUTOTRADINGDISABLEDBYBROKER

MT4/5-specific: the order has been placed via an EA, and automated trading is disabled by the broker

TR_MT4ERROR130

MT4-specific: error #130, which can mean either that the entry price on a pending order is too close to the market price, or that the s/l or t/p on a trade is too close to the current market price, or that the s/l or t/p on a pending order is too close to the requested entry price

TR_NOTPENDING

Attempt to delete an ID which is not a pending order

TR_MARKETORDERSNOTALLOWED

At-market orders are not allowed on this market

TR_LIMITORDERSNOTALLOWED

Limit orders are not allowed on this market

TR_STOPORDERSNOTALLOWED

Stop orders are not allowed on this market

TR_STOPLIMITORDERSNOTALLOWED

Stop-limit orders are not allowed on this market

TR_SLNOTALLOWED

Stop-losses are not allowed on this market

TR_TPNOTALLOWED

Take-profits are not allowed on this market

TR_ACCOUNTNOTRADINGRIGHTS

Your account does not have trading rights. (Generally implies that the account is not allowed to trade, rather than being logged in with a read-only password.)

8.7Other (non-trading) errors

There is a small number of further error codes which do not relate to trading activity:

FXB.ErrorCodes.

Description

TR_BADCANDLEREQUEST

Invalid request for candle data

TR_MAILERROR

Problem with the mail system. Either no connection to the mail server, or an invalid send-mail request

9Script contexts

When you save a script in the Run Script widget, you can give it a "context". This makes it available in a specific place within the Figaro platform, rather than being part of the list of scripts for general use.

For example, you can set up a script so that it is included on the action menu for a trade or order (in the Trade List component, or from the chart marker for an open trade or pending order).

Scripts which have a context receive information about that context in their Framework.privateSettings, as described below.

9.1Quote Board script context

Scripts which are assigned to the Quote Board context are displayed under the widget's Tools menu. For example, you could create a script which doubles up any open position on markets currently listed in the quote board.

The script receives the following information in its Framework.privateSettings about the current contents of the quote board:

Member

Description

scriptContext

Set to "quoteboard"

listedInstruments[]

Array of all markets currently loaded into the quote board (potentially including markets filtered out, and not currently visible)

filteredInstruments[]

Array of all markets which meet any filtering, and are currently visible in the quote board

selectedInstruments[]

Array of markets which are currently selected by the user (and highlighted) in the quote board

9.2Trade List script context

Scripts which are assigned to the Trade List context are displayed under the widget's Tools menu. For example, you could create a script which calculates a technical indicator and closes any trade based on the indicator value for its market.

(Note: scripts can also be assigned to the Order Menu context. This causes them to be displayed under the All/Filtered Orders and Selected Orders sub-menus of the Tools menu.)

The script receives the following information in its Framework.privateSettings about the current contents of the trade list:

Member

Description

scriptContext

Set to "tradelist"

listMode

Selected mode for the list. One of "trades", "pending", or "all".

listedOrders[]

Array of all orders markets currently loaded into the trade list (which will match all the trades/orders currently existing on the account, depending on the value of the listMode)

filteredOrders[]

Array of all orders which meet any filtering, and are currently visible in the trade list

selectedOrders[]

Array of order which are currently selected by the user (and highlighted) in the trade list

9.3Chart script context

Scripts which are assigned to the Chart context are displayed under a chart's settings menu.

The script receives the following information in its Framework.privateSettings about the current chart:

Member

Description

scriptContext

Set to "chart"

instrumentId

ID of the market being displayed on the chart

timeframe

Timeframe being displayed on the chart

timezone

Selected time zone for the chart

9.4Order Menu script context

Scripts which are assigned to the Order Menu context are displayed in the following places within Figaro:

In the trade list widget, under the All/Filtered Orders and Selected Orders sub-menus of the Tools menu

In the pop-up action menu for an individual order in the trade list grid

In the pop-up action menu for an order from its marker on a trade chart

The script receives the following information in its Framework.privateSettings about the applicable orders. The orders[] array will contain a single ID if used from the action menu of a chart marker or individual entry in the trade list grid. The array may contain multiple orders if used from the Tools menu of the trade list.

Member

Description

scriptContext

Set to "ordermenu"

orders[]

Array of applicable orders/trades

10Sending mail into Figaro from outside the platform

It's possible to send mail into Figaro from outside the platform, from any environment which can make HTTP requests. If you are sending to a mobile device this also generates an iOS/Android push notification, and so you can receive alerts on your phone (or tablet) from external sources.

For example, you can send messages into Figaro from a huge range of sources, covering everything from a financial model in Microsoft Excel to an EA in the MT4 or MT5 trading platforms.

The examples in this guide use the general-purpose command-line tool curl. This is just a way of demonstrating that mail can be sent into Figaro from anything which can make HTTP requests. You can use the help material for curl to translate from these examples to any other environment which can make HTTP requests.

All mail in Figaro is sent to devices (not to people, or to trading accounts). You need three pieces of information in order to send mail into Figaro:

The mail ID of your device, which will be an 8-character code such as DMZLXCVG. You can see this by opening the new-mail window in Figaro, from the Mailbox widget.

Your sender password, which is a long alphanumeric string. You can see this by doing a long tap/click over your ID in the new-mail window. This expands the new-mail window to show extra information.

Your message server. This is also shown when you do the long-tap/click over the ID.

You send mail by making a POST request to the messaging server, with the request body being a block of JSON describing the message. For example, using curl:

curl --url "https://figaro-messaging.fxbluelabs.com/send?DMZLXCVG" --request "POST" --data-raw "{'password':'C9753B5FA8E5417BA064618AF65E2520', 'subject':'Test message'}"

To confirm, what this example is doing is making a POST request to a URL which consists of three parts:

The message server, such as https://figaro-messaging.fxbluelabs.com

The path /send

Your device ID as the request's querystring: ?DMZLXCVG

The body of the POST is then a block of JSON data. As a minimum, it must contain your sender password and a subject line for the message.

The values which you can include in the JSON body, describing the message, are as follows. The password and subject are compulsory. Everything else is optional.

Property

Description

password

The sending password for your device, as described above

subject

A subject line for the message (max 200 characters)

body

An optional body for the message (max 4KB)

to

Recipient ID, if sending a message to another device. If there is no to value, then the message is simply sent to your own device as identified by the request querystring.

priority

Priority for the message

attachment

Attachment data

noPush

Turns off any iOS/Android push notification

You can send mail to other people's devices as well as your own, by setting the to property. But it's deliberate that there is no way of discovering someone else's ID without them looking it up - in the Figaro new-mail window as described above - and telling it to you.

Messages to your own device can have priority. This is ignored if you use the to value to send a message to another device. Priorities are as follows:

Priority

Description

0

Normal priority

1

Message is marked in red in the Figaro mailbox

2

Figaro displays a red new-mail icon in its banner until the message has been read

3

Message is forced to open in the platform (after next log-in, if Figaro is not currently running)

The optional message body can be any of three things:

Plain text

HTML - a full HTML document with <html> and </html>

The URL of a web page. This must be an https:// page, not http://, and the page must not set the X-Frame-Options header.

For example, the following sends a priority 2 message with the www.fxblue.com website as the content:

curl --url "https://figaro-messaging.fxbluelabs.com/send?DMZLXCVG" --request "POST" --data-raw "{'password':'C9753B5FA8E5417BA064618AF65E2520', 'subject':'Test message', 'body':'https://www.fxblue.com', 'priority': 2}"

If you are sending to a mobile phone (or tablet), then Figaro mail generates iOS/Android push notifications by default. You can turn this off by setting 'noPush':true in the JSON (or any value for noPush - it is not actually treated as a boolean).

Mail into Figaro is subject to a quota - for example, a maximum of 60 requests per hour. Messages will be rejected if you try to send too many (including invalid requests such as those missing a subject line, or with a non-existent recipient).

The HTTP status code of the response from the server will be 200 if the message succeeds. If the message fails for any reason, then the status code will be 400. The body of the response from the server is JSON consisting of either one or two values:

Property

Description

success

True/false, indicating whether the message succeeded (duplicating the 200/400 status code of the response)

error

If success=false, a description of why the message failed

Like with messages sent from inside the Figaro platform in its new-mail window, you can also add attachment data. This works as follows:

You use the Data Transfer widget in Figaro to download an attachment (a .figarotransfer file)

You open the .figarotransfer file in any text editor

You add the attachment data (beginning AAAA…) into the message