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
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:
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
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
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
Scripts and UDIXes have automatic access to the framework. A widget needs to include the following references in its HTML (usually in the
<script src="https://figaro.fxbluelabs.com/scripts/widget-js"></script>
<link rel="stylesheet" type="text/css" href="https://figaro.fxbluelabs.com/css/widget-css"/>
Scripts and UDIXes automatically have a
<script>
// Create an instance of the framework
var Framework = new FXB.Framework();
For consistency across widgets, scripts, and UDIXes, this document refers to widgets having access to the framework by creating a new FXB.Framework() object:
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
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
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.
Widgets, scripts, and UDIXes have normal access to the browser's Javascript environment. For example, they can use
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
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
You can use standard features of your browser to inspect and debug your code. For example, you can insert the
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
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
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()
});
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
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 -
The existence of Promises also means that it's possible to use Javascript
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();
Scripts run forever until they call
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.
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.
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
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
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
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
…
};
};
Widgets, scripts, and UDIXes can use the standard features of the browser environment to collect external data. For example, you can use
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
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).
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.
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
Regardless of the back-end system which it is logged into, the framework represents all trading volumes, such as the
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).
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
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.
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.
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 |
List of all available markets for the current trading account. | |
List of all open trades and pending orders (potentially multiple open trades for each market unless the account is netting/non-hedging) | |
List of open trades aggregated into a single long/short position for each market | |
List of historic trades and credit movements on the account. This collection is empty by default and must be explicitly requested using Framework.RequestAccountHistory() |
The
Object | Description |
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. | |
Contains a persistent identifier for the client's browser/app | |
Contains functions for translating variables into the user's currently selected language | |
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 |
Standard response/error object |
The framework provides the following properties. All of these are read-only.
Property | Description |
RELEASE | |
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. |
The framework provides a list of all the markets available on the current trading account. The
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
As a result,
Does your key match an instrument's
For fx markets, does your key match the base currency + quote currency? For example, if you pass in "EURUSD" (and the
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
Addition of
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.
Helper function which returns an array of all instruments with
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
The
The properties of the
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^- |
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 |
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 |
midPrice * | Mid price: ( |
minLot * | Minimum trade volume, in lots: |
maxLot * | Maximum trade volume, in lots: |
lotStep * | Minimum trade increment, in lots: |
tickValue * | Cash value, in the account's deposit currency, of a change in price by the |
Figaro does its own categorisation of markets (again looking to standardise across different platforms). Every
| 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 |
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().
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
On a spread-bet account, the
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 |
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 |
The
To watch for changes in the order list, you can use an OnMessage() handler.
In addition to standard
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,
The framework uses the
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
Property | Description |
orderId | ID of the order (usually but not necessarily numeric) |
reference | Client-facing reference number for the order |
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). |
The
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
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
The
Stop-limit orders are not widely available. The account features indicate whether the account offers them.
The framework provides a range of functions for helping to work with
var oppositeType = FXB.OrderTypes.Invert(
FXB.OrderTypes. | Helper function |
IsLong(x) | Tests whether the |
IsShort(x) | Tests whether the |
IsMarket(x) | Tests whether the |
IsPending(x) | Tests whether the |
IsLimit(x) | Tests whether the |
IsStop(x) | Tests whether the |
IsStopLimit(x) | Tests whether the |
IsCredit(x) | Tests whether the |
Invert(x) | Inverts the order-type. For example, |
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
(If the trading account is singleNetPosition:true, then the
The
However, the collection provides the following helper function for working with individual markets:
Instrument.Positions. | Description |
getOrEmpty(x) | Extends the standard |
To watch for changes in positions and their profit, you can use an OnMessage() handler.
An
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
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. |
The
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
The
Different accounts can have different capabilities - for example, availability of trailing stops or market depth. These are described by the Account.features.
The
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 |
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 |
defaultInstrumentId | Suggested |
A block of sub-properties describing the capabilities of the account |
Different accounts can have different capabilities. The features available on the current account are described by its
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 |
chartTimeframes[] | Array of chart timeframes for which candle data is available. For example, |
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, |
dayHighLow | Boolean. Indicates that |
dayChange | Boolean. Indicates that |
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. |
The
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
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
This
The
The
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 |
The functions in
Function | Description |
Translates language variables in HTML | |
Translates a single language variable | |
Translates an error result into the equivalent text | |
Translates a chart timeframe, such as 3600, into corresponding text | |
Translates a market category ID into corresponding text |
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().
The framework supports a mechanism where language variables are included in HTML in curly brackets such as {SetMarket}, and Javascript can then use
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
Framework.OnLoad = function() {
this.Translation.TranslateDOM();
… and other initialisation actions …
};
The
// 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,
You can provide values for these placeholders by supplying an array as an optional second parameter to
// 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
var maxAllowedMsg = Framework.$T("MaxSelectMarkets", [20]);
The
Framework.SendOrder(def, function (MsgResult) {
if (MsgResult.result.isOkay) {
// Succeeded
} else {
// Failed. Display error message…
var errorText = Framework.Translation.TranslateError(MsgResult.result);
…
}
});
The
var txt = Framework.Translation.TranslateTimeframe(86400); // = "D1"
The
var market = Framework.Instruments.getOrBlank(someInstrumentId);
var categoryText = Framework.Translation.TranslateInstrumentCategory(market.categoryId);
(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
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
Results/responses to actions are widely represented within the framework using the
An
Property | Description |
code | Error (or success) code from |
isOkay | Derived boolean property indicating whether the |
isError | Derived boolean property indicating whether the |
An error
Depending on the nature of the response, the
For example, in the response from the standard
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)
}
});
The External Widget example in Figaro and some of the code in this guide assigns extra ad-hoc functions and properties into the
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
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
The callback is dropped from the function's parameter list (and replaced by returning a Promise). For example,
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.
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
// 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
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
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
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
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
You can specify a start date and/or end date for the candle request using
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
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 | Start date for historic request. If provided, must also set |
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 |
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,
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 |
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 | 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 |
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
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
When the price changes and enters a new time period, you will receive a
You can terminate a streaming candle request using
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
Like all times in the framework, candle
For timeframes above H1, you can set a
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
You can specify aggregation in your candle request using a
Property | Description |
offset | Base offset in minutes from UTC, e.g. 120 or -300. Note: although this |
dstMode | Daylight savings mode. One of the values of the |
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
In other words: if you do not set your own
However, in your request you can also optionally specify
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
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
Parameter | Description |
Definition of an individual request, or an array of requests | |
Callback function which asynchronously receives success of the request or (first) failure | |
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 |
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
});
The request definition which you submit as a parameter to
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
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 |
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},
…
To open a new pending order or trade (market order), you set the
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:
Compulsory and optional request properties when opening a new trade or order are as follows:
Example | Description |
instrumentId | |
Volume to trade. Always compulsory. | |
Open price for a pending order. Compulsory on pending orders; ignored on trades. For stop-limit orders, the | |
Trigger (stop) price for stop-limit orders. Compulsory on stop-limit orders; ignored on all other types. | |
Stop-loss to set on the order/trade. Always optional. | |
Take-profit to set on the order/trade. Always optional. | |
Trailing stop to apply to the order/trade. Always optional, and not necessarily available. Requires that the broker account supports trailing stops. | |
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. |
The
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 |
{poundPerPoint: x} | Converted by the framework so that cash value of a movement by the market's |
{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. |
The
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 |
{pips: x} | Offset from the current market price in pips, such as 20. Converted by the framework using the market's |
{bar: barDef} | See the description of candle-based prices below |
The
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 |
{pips: x} | Offset from the current market price in pips, such as 20. Converted by the framework using the market's |
{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 |
{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 |
{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 |
The
tp: | Description |
{rrr: x} | Calculates the take-profit so that it is |
Trailing stops are not necessarily supported; this depends on the capabilities of the broker account and its back-end trading platform. The
If supported, the
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 |
The
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 |
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
The definition of the openPrice, sl, and/or tp can then include the following values. The
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 |
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},
…
You can modify a pending order or open trade using
// 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 (
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) {
…
});
You can close an open trade or delete a pending order by using
Deletion of a pending order is simple because it can have no properties other than the
// 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
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 |
{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) {
…
});
The following
For the following actions, an
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 ( |
CLOSEPOSLOSERS | Closes loss-making open trades ( |
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 |
The following
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
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 ( |
FLATTENLOSERS | Closes all loss-making open trades on all markets ( |
FLATTENLONG | Closes all long trades and pending orders on all markets |
FLATTENSHORT | Closes all short trades and pending orders on all markets |
The callback function which you provide as a parameter to
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
Request is carried out and succeeds. The
Request requires no action - for example, asking to do a
Your request is subject to user confirmation, and the user cancels the request before it is started. The
In other words,
A trading instruction through
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 |
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 |
fifo | Boolean which can be used to force non-hedging behaviour on hedging accounts (irrelevant and ignored on netting accounts). For example, if |
A widget, script, or UDIX can use the function
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
You can use
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
Framework.ValidateOrder({
tradingAction: FXB.OrderTypes.SELL_LIMIT,
openPrice: {offset: 0.0020},
instrumentId: "EUR/USD",
volume: {lots: 0.20}
}, function (Msg) {
… result of validation
});
The
Property | Description |
result | An FXB.Result object. If validation succeeded then |
tradingJobs[] | If successful, an array of the individual trading jobs after validation |
For example, after validation of the above request,
{
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
}
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
There can be a long delay between using
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.
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
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
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 |
Note: you must set both
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
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) {
…
});
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 |
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
Framework.Ask(… , function(Msg) {
if (Msg.result) {
// Final result
} else {
// Initial creation/update
}
});
The final result,
The standard
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.
}
});
As described above, the initial update message from the dialog gives you its ID, in
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()
}
});
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
Framework.InfoMessage({
text: "Must be explicitly closed. Not destroyed if the user clicks outside it",
floating: true
});
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
You can ask the user to select a market using
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(function (timeframe) {
if (timeframe) {
…
} else {
// Cancellation of dialog
}
});
You can ask the user for a text value using
The simple usage is equivalent to the browser modal
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
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 |
The framework provides a
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()
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
An array of objects, like the example above. Each object must have a
Full dialog initialisation, including the ability to set standard properties such as noCancel, containing an
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
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
});
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
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
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
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({
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
Each entry in the
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 |
unselectable | If |
separator | If |
separatorAfter | If |
icon | Icon to display next to the item. This can be the hexadecimal code of any Font Awesome icon, prefaced with |
checkbox | If not null, the item is given either a ticked ( |
checked | If not null, the item is given either a unfilled ( |
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
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
if (Msg.result && Msg.result.itemData) {
// User has made a selection
} else {
// Initial update message, or user cancelled the dialog without response
}
The framework provides an
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 |
value | Initial value to pre-populate into the field: For a chart For a For a |
options | Only used for a |
The configuration which you pass into
An array of fields, as in the example above
An object with a
An object with a
An object with a
If you pass an object rather than a bare array, then you can set a
Passing an object with a
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
What you get back in your asynchronous callback function depends on the input you provide. The
If the user saves the settings, then you will receive one of the following inside the
A
A
Or, just a
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
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
As an example, take the following widget/script/UDIX code which builds a string containing an HTML document, including a
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
There is a button in the page which uses
The Javascript in the HTML sets up a listener on
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
If the
If the
If the
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
Property | Description |
url | If you supply a |
noTitle | If |
noStandardCss | By default the framework will insert its own CSS into your HTML (not applicable if you use a |
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 |
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
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
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
The dialog can send a result back to the main widget using
// 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
The dialog can also send interim updates back to the creator, using
The main widget receives information from or about the dialog in its asynchronous callback function for
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
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
The
You can open either window, with no pre-population, using very simple calls to
Framework.CreateDialog({type: "dealticket"});
…
Framework.CreateDialog({type: "trading-action"});
In both cases,
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
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 |
handleExecution | Defaults to true. If set to false, the dialog returns an |
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
Property | Description |
allowInstrumentChange | Defaults to true. If false, prevents the user from changing the initial market (which you set using |
noPosition | If true, the |
fullMode | By default the |
noToolsIcon | Can be used to turn off the settings icon in the |
You can ask the user to select a saved order template using
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
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
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 |
showDefault | Similar to |
There is no functional difference between
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)
}
});
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.
"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.
A widget, script, or UDIX can create its own toast pop-ups using
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 |
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
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 |
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
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 |
toastReplacementGroup | Ignored if blank/null. The opposite of |
toastClickGroup | Ignored if blank/null. Automatically destroys all the items with the same |
toastClass | Optional display class for the toast, changing its formatting. Currently available values are |
dialog | Optional description of a dialog to display in response to a click on the toast. See below. |
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
Or, provide a
An example of a
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
The
The
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
There can be two exceptions to this lifespan:
The toast is given
And/or, the toast is given a long
In either of these circumstances, the caller may want to remove the toast manually, using
// Note: no callback. DestroyToast() is fire-and-forget.
Framework.DestroyToast("1234-5678-abcd");
The ID of the toast, for passing to
By providing an explicit
Using the
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);
});
A widget, script, or UDIX can use standard browser features such as
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.
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.
You can load category settings using
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
You must use
You should regard the ID which you use in
You can save category settings by using
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
But you don't act on the change immediately.
Instead, you wait for the
However, one final note: you should not use
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({
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
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
If
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
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();
});
};
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,
You need to handle
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
As a result, it's very common for a widget's storage of private settings and its handling of
The widget uses
When saving private settings as a result of a user change, the widget calls its own
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:
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
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
var newState = Framework.OnGetState();
Framework.SaveSettings(newState, newState);
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
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({
instrumentId: <new market selection>,
timeframe: <new timeframe selection>
});
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
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);
You can change the current page which is being displayed in Figaro using
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".
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
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.
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
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().
Each component in the chart's dock area has a title, and by default the platform will use the
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
Chart widgets can receive the chart's bar data by defining a
Changes to the current in-progress candle can be received using a
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
}
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
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
Like the UDI functions, you can pass a callback to
If you set
Messages sent by HTML which you create are sent to a
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
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
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
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
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
var myResponse = new FXB.Message({
msgType: FXB.MessageTypes.GENERIC1,
myData: "some communication"
});
Framework.Respond(myResponse, someOriginalMessage);
(If you are using
The one thing to note is that an asynchronous callback for
In other words: an asynchronous callback on
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.
The framework uses the following properties to define a
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 |
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.
The
It is therefore almost entirely equivalent to
If the framework detects a problem with the local clock,
The function
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
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
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.
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
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.
The framework has a variety of functions for doing standard formatting of numbers, dates, prices etc.
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 |
max_digits | Allows a variable number of decimal places, as required, up to a maximum of 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 |
thousands | Display numbers in millions or thousands as xM or xK, with the fractional amount of thousands/millions controlled by 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 |
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 |
Because lot-size conversion is market-specific, the
var market = Framework.Instruments.get(instrumentId);
var text = Framework.FormatVolume(20000, market, {append_lots:true});
// For example, text = "0.20 lots"
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 |
options | See below |
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 |
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
Option | Description |
format | String describing the format for the date. If omitted, or none of the items below, then the format is "dateonly": formats as "yyyymm": formats as "timeonly": formats as "timehhmm": formats as "datehhmm": formats as "textdate": language specific, but in English formats as |
localtime | By default, |
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 : |
Note: the DOM node which receives the output must be empty on first use of
The parameters for
Parameter | Description |
target | Either a DOM node into which to put the price, or the ID of a node. If |
instrument | The market to use for formatting the price. Can be specified either as an FXB.Instrument object or the |
price | The price to format |
options | Optional. Number-formatting options which are ultimately passed to FormatNumber(). For example, to suppress thousands-separators, set |
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);
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}
});
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
Once you have called
Requests are sent to a function called
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
return {status: "block", blockText: "Are you mad?"};
Similarly, if you return
Your validator receives two pieces of information in the
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 |
tradingJobs[] | List of trading actions which are being requested. |
There are two things to note about the array of
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
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.
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
Parameter | Description |
instrumentId | |
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
Each market-depth update message has the following properties:
Property | Description |
instrumentId | |
requestId | The ID of the market-depth request, either provided by you in your call to |
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, |
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
| Description |
id | An ID for the quote which is being added/removed/modified |
state | The type of change to the quote: one of |
price | The price of the quote (e.g. 1.23456) |
volume | The volume of the quote (e.g. 10000000) |
The
As noted above, the back-end system may have multiple quotes at each price (from different liquidity providers). The
The depth object contains the following properties:
| Description |
aggregated | Object containing |
book | Object containing Dictionaries (not arrays) of the individual current bid and ask quotes. Each quote consists of an |
bestAsk | Best ask price (same as |
bestBid | Bid bed price (same as |
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
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
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
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
Order book for a market must be requested, using
The parameters for
You should terminate requests when you no longer need them, using
The
Property | Description |
granularity | Level of aggregation applied by the back-end system. If |
The Figaro framework provides access to an economic calendar - unless the
Widgets, scripts, and UDIXes can request the calendar using
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, |
previous 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. |
This section describes Figaro's internal mail system - not email. Widgets, scripts, and UDIXes can send email by using browser
Some versions of the Figaro platform may not have a mail system. You can check availability of mail functionality by inspecting the
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.
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({
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
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 |
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 |
The
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
Plain text
HTML - a full HTML document with
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.
A widget, script, or UDIX can read the mailbox by using
A
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.
This section covers any remaining functions which do not fall into any of the previous categories above.
You can trigger a download of data by using
In a web browser,
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"
});
You can listen for changes in prices, the order list, account metrics etc using an
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
As well as using the main
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
var handler1 = Framework.AddMessageHandler(function (Msg) { … a handler function});
var handler2 = Framework.AddMessageHandler(function (Msg) { … a second handler });
The return value from
Framework.RemoveMessageHandler(handler1);
Note: you may observe that everything passes through
The framework can issue multiple updates in a single message. For example, a single message and a single call to
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 -
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
}
};
The parameter which is passed to
In addition to the list of message-types in the message, which you can test using the
The types of message which you can receive via
FXB.MessageTypes. | Description |
Update to one or more prices | |
New pending order or open trade | |
Change to a pending order or open trade (including non-trading changes such as fluctuating profit on an open trade) | |
Closure of an open trade or cancellation of a pending order | |
Change to a net position | |
Change to account metrics such as balance or margin usage | |
Initial load or subsequent change to the account history | |
Update to market depth | |
Update to order book | |
Event within Figaro's internal mail system | |
Change by the user to the software's graphical theme | |
Change to the category settings of your widget, script, or UDIX | |
Change of context - synchronisation of markets and/or timeframes - in the Figaro page container | |
Range of messages, from BROADCAST1 to BROADCAST10, which widgets, scripts, or UDIXes can use to broadcast their existence to other components | |
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 |
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
By defining an OnPriceChange handler
There is a subtle difference between these two routes:
The OnPriceChange handler is guaranteed only to be called in relation to the market(s) you have requested.
Whenever there is a change to a requested price, the main
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
Each object in the
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
The
The possible flags are members of the
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 |
Instead of using the main
Framework.OnPriceChange = function(quote) {
// The quote object contains the same time, bid, ask etc properties listed above
};
There are two ways of tracking changes in the order list (which includes both pending orders and open trades):
By using the main
By defining any or all of OnOrderOpen, OnOrderChange, and OnOrderClose handlers
The
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
The possible flags are members of the
FXB.UpdateFlags. | Description |
FLAG_ORDER_NEW | Order is new (should only exist in |
FLAG_ORDER_TYPE | Change in |
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 ( |
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 |
Instead of using the main
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
};
You can listen for changes in the changes in the Framework.Positions list in two ways:
By using the main
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.
The
The FXB.Message object will contain a
Instead of using the main
Framework.OnPositionChange = function(position) {
// An FXB.Position update describing the change
};
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
By defining an OnAccountMetrics handler
The
The FXB.Message object will contain an
You can also inspect what has changed using an
FXB.UpdateFlags. | Description |
FLAG_ACCOUNT_FLOATINGPL | Change to floating P/L |
FLAG_ACCOUNT_EQUITY | Change to equity (should always also imply |
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 |
Instead of using the main
Framework.OnAccountMetrics = function(accountMetrics) {
// Receives the same accountMetrics object as in OnMessage
};
The parameter to the
The
By the time that your message handler receives
You can inspect which transactions are being added by looking at the
The
The
The
(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
The
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
The
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.
The message types from
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
The
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);
You can create a candle store simply by creating a new instance of the
// 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. |
There are three ways of loading data into an
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.
}
});
In addition to setting up event handlers, you can detect changes in the store by looking at the return value from
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
// 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
The framework puts the ID of the candle request into the
If the candle request fails, the framework fires an
The third way of loading candles into a store is to provide a
// 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
Finally, the
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
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 |
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, |
OnLoadError() | Special case: called if you assign the candle store as the output for |
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 |
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
});
Loading data into the store fills a
myCandleStore.LoadCandles(…);
var currentClose = myCandleStore.candles[0].c;
However, it is not safe for you to modify the
The store provides some simple properties for checking its contents:
Property | Description |
length | Number of candles in the store (equivalent to |
hasData | Boolean indicating whether there are any candles in the store ( |
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
If you set oldest-first mode, then the store's own
Function | Description |
GetCandleArray() | Returns a copy of the candle array (using |
GetCandle(idx) | Returns a specific zero-based candle, or null if the index is not valid. The |
GetValueArray(member) | Helper function which returns an array of a single member from all the candles. For example, |
In other words: setting
You can also look up candles by time:
Function | Description |
GetByTime(tm, exact) | Finds the candle which starts with or contains the time |
If
If
If
Otherwise,
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
The derived candle properties which are added by the store are as follows:
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 |
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
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
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
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
var currentRSI = myCandleStore.ta[0].GetCurrentValue();
var currentStochastic = myCandleStore.ta[1].GetCurrentValue();
You can add calculations, using
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
myCandleStore.AddTA(new FXB.ta.EMA({period: 20, alias: "ema20"}));
You can then refer to calculations via their alias using a
// 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();
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
// 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,
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 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
cs.Aggregate(86400,
In a UDI, you can get the time zone of the indicator's chart from the
cs.Aggregate(86400, data.context.timezone);
The framework provides an
// 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);
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
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());
You set up a technical analysis calculation by creating an instance of the corresponding class in the
// 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
You can include a
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( … );
There is one special initialisation parameter, the
The
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}, … ]);
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 |
In other words,
Both
As described above, calculations can be either bar-based or value-based. For bar-based calculations, what you pass to
An array of candles: objects containing an
An object containing a
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
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
// 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
// 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.
UDI.onCalculate = function(data, output) {
// Create an ATR calculation if we don't already have one
if (!UDI.$myATR) UDI.$myATR = new FXB.ta.ATR({period: 14});
// Pass the data into the ATR calculation.
// If data.
UDI.$myATR.LoadData(data);
…
};
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
// 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
Finally, there is another way of loading initial data into a technical analysis calculation: providing a
var ma = new FXB.ta.EMA({
period: 30,
member: "median",
data: [{o:1.2342, h: 1.2347, l: 12341, c: 1.2343}, … ]
});
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
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 |
GetLength() | Get the number of output values. Should always match the number of items in the array passed to |
length | Length property; alternative and equivalent to |
HasData() | Shortcut for checking whether a calculation has been initialised. Equivalent to |
hasData | Has-data property; alternative and equivalent to |
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
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
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 |
Some calculations then have further functions for retrieving their output. For example, the Keltner and Bollinger band calculations have
As described in the previous section, the default for
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
You do this by setting
// 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();
The
A simple example of
// 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]
Valid operations to perform on the arrays are
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
{
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
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);
The
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
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
// 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
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 |
AMA | Uses |
SMAofSMA | Uses a |
EMAofEMA | Same as |
VIDYA | Uses a |
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".
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
// 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
The list shows the class within
Calculation class | Description | |
Bar | Directional movement system | |
Value | Awesome Oscillator | |
Bar | Aroon | |
Bar | Aroob oscillator | |
Bar | Accumulative swing index | |
Bar | Average true range | |
Value | Bollinger® bands: a moving average plus/minus standard deviation | |
Bar | Bear/bull power - one candle property minus an average of another candle property | |
Value | Commodity channel index | |
Value | Chande forecast oscillator | |
Bar | Chaikin Volatility | |
Bar | Chaikin Money Flow | |
Value | Chande momentum oscillator | |
Value | Centre of gravity | |
Value | Coppock curve | |
Bar | Derived prices from candle data | |
Value | Detrended price oscillator | |
Bar | Elder force index | |
Value | Ehler Fisher transform | |
Bar | Elder market thermometer | |
Bar | EaseOfMovement | |
Value | Envelopes around a moving average: the average plus/minus a fixed percentage | |
Bar | Gopalakrishnan range index | |
Value | Highest of the last N values | |
Value | Historical volatility | |
Bar | Intraday momentum index | |
Bar | Keltner channel (and STARC): moving average plus/minus ATR | |
Bar | Klinger volume oscillator | |
Value | Lowest of the last N values | |
Value | Linear forecast | |
Value | Linear intercept | |
Value | Moving-average convergence/divergence: difference between two averages, with smoothing of that difference | |
Value | Oscillator of a moving average | |
Bar | Mass index | |
Bar | Market facilitation index | |
Value | Momentum oscillator | |
Bar | Money Flow Index oscillator | |
Value | Performance index | |
Bar | Pretty Good oscillator | |
Value | Price oscillator | |
Bar | Parabolic stop and reverse | |
Bar | QStick | |
Value | Rainbow oscillator | |
Value | Ranking | |
Value | RAVI | |
Value | Relative strength index | |
Bar | Relative vigor index | |
Bar | Random walk index | |
Value | R-squared | |
Value | Slope of linear regression | |
Value | Standard deviation | |
Bar | Stochastic momentum index | |
Value | Schaff trend cycle | |
Bar/Value | Stochastic oscillator | |
Value | Stochastic RSI | |
Value | Sum of the last N items | |
Bar | Swing index | |
Bar | Twiggs money flow | |
Value | TRIX oscillator | |
Bar | True range | |
Bar | Ultimate oscillator | |
Value | Vertical horizontal filter | |
Bar | Positive and negative volume index | |
Bar | Williams accumulation/distribution | |
Bar | Williams %R |
The
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.
The
Parameter | Description |
period | Rolling period for the calculation |
The primary output value -
Parameter | Description |
GetPositiveValue(idx) | Gets the DI+ value at index |
GetNegativeValue(idx) | Gets the DI- value at index |
GetPositiveArray() | Returns an array of all DI+ values |
GetNegativeArray() | Returns an array of all DI- values |
The
Parameter | Description |
period | Rolling period for the calculation |
Note: the
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 # |
GetAroonDownValue(idx) | Returns the low value at bar index # |
The
Parameter | Description |
period | Rolling period for the calculation |
The
Parameter | Description |
period | Rolling period for the calculation |
maType | Type of moving average. Defaults to a simple moving average (sma / 0). |
The
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). |
The
Parameter | Description |
member | Member of each candle to use - normally either |
averageOf | Member of each candle from which to calculate an average. Defaults to the close ( |
period | Rolling period for the average |
maType | Type of moving average. Defaults to an exponential moving average (ema / 1). |
The
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 -
Parameter | Description |
GetUpper(idx) | Gets the upper band at index |
GetLower(idx) | Gets the lower band at index |
GetUpperArray() | Returns an array of all upper-band values |
GetLowerArray() | Returns an array of all lower-band values |
The
Parameter | Description |
period | Rolling period for the calculation |
The
Parameter | Description |
period | Rolling period for the calculation |
The
Parameter | Description |
period | Rolling period for the calculation |
The
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 |
The
Parameter | Description |
period | Rolling period for the calculation |
The
Parameter | Description |
period | Rolling period for the calculation |
The
Parameter | Description |
period | Rolling period for the calculation |
The
var medians = new FXB.ta.DerivedPrice({member: "median", data: [{o: 1.2456, …}, { … }]);
The
Parameter | Description |
period | Rolling period for the calculation |
maType | Type of moving average. Defaults to an exponential moving average (ema / 1). |
The
Parameter | Description |
period | Rolling period for the moving average |
maType | Type of moving average. Defaults to an exponential moving average (ema / 1). |
The
Parameter | Description |
period | Rolling period for the calculation |
The
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 -
The
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 -
The
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 -
Parameter | Description |
GetUpper(idx) | Gets the upper band at index |
GetLower(idx) | Gets the lower band at index |
GetUpperArray() | Returns an array of all upper-band values |
GetLowerArray() | Returns an array of all lower-band values |
The
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 -
The calculation only uses "confirmed" fractals, starting from the time at which a fractal was known to be confirmed. For example, with the standard
The
Parameter | Description |
period | Rolling period for the calculation |
The
Parameter | Description |
period | Rolling period for the calculation |
In addition to putting the rolling highest values into the main output -
The
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) |
The
Parameter | Description |
period | Rolling period for the calculation |
The
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 -
Parameter | Description |
GetUpper(idx) | Gets the upper band at index |
GetLower(idx) | Gets the lower band at index |
GetUpperArray() | Returns an array of all upper-band values |
GetLowerArray() | Returns an array of all lower-band values |
The
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.
The
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).
The
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).
The
Parameter | Description |
period | Rolling period for the calculation |
In addition to putting the rolling lowest values into the main output -
The
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 (
The signal value (
"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
Parameter | Description |
GetHistogramValue(idx) | Returns the histogram value at index |
GetHistogramArray() | Returns an array of all the histogram values, similar to |
The
The
Parameter | Description |
period | Period for totalling, typically 25 |
subperiod | Period for moving averages, typically 9 |
The
Parameter | Description |
period | Rolling period for the moving average |
The
Parameter | Description |
period | Period for totalling, typically 25 |
The
Parameter | Description |
period | Rolling period for the moving average |
maType |
The
Description | |
member | Candle member to compare. Defaults to the close ( |
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.
The
Parameter | Description |
minAF | Defaults to 0.02 |
maxAF | Defaults to 2 |
The
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. |
The
Parameter | Description |
period | Rolling period for the calculation |
The
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). |
The
Parameter | Description |
period | Rolling period for the calculation |
The
Parameter | Description |
period | Number of iterations for the rainbow |
maType | Type of moving average. Defaults to a simple moving average (sma / 0). |
The
Parameter | Description |
period | Rolling period for the calculation |
The primary output value -
The
Parameter | Description |
period | Rolling period for the calculation |
mode | See below |
The
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 |
The
Parameter | Description |
period | Rolling period for the calculation |
The
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). |
The
Parameter | Description |
period | Rolling period for the calculation |
The
Parameter | Description |
period | Rolling period for the calculation |
The primary output value -
The
Parameter | Description |
period | Rolling period for the calculation |
The
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). |
The
Parameter | Description |
period | Rolling period for the calculation |
The
Parameter | Description |
period | Rolling period for the calculation |
deviations | Multiple of the standard deviation to return. Defaults to 1. |
The
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 -
The
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 -
The
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 |
The
Parameter | Description |
period | Rolling period for the calculation |
The
Parameter | Description |
limit | Movement limit value, defaulting to 0.5 |
The
Parameter | Description |
period | Rolling period for the calculation |
The
The
Parameter | Description |
period | Rolling period for the calculation |
maType | Type of moving average. Defaults to an exponential moving average (ema / 1). |
The
Parameter | Description |
period | Rolling period for the calculation |
The
Parameter | Description |
period | Rolling period for the calculation |
The
Parameter | Description |
member | Candle member. Defaults to the close ( |
The primary output values -
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.
The
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.
The
Parameter | Description |
period | Rolling period for the calculation |
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
However, this is not compulsory. A widget is free to ignore or override the framework's CSS, and use its own design.
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
The following sections contain some explanatory notes about the standard CSS, not a full itemisation of its variables and classes.
The framework sets some standard classes on the widget's
CSS class | Description |
Theme_x | Identifier for the current graphical theme, such as |
LTR or RTL | Identifies whether the user's selected language for the platform is written left-to-right or right-to-left. Sets CSS |
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 ( |
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
CSS class | Description |
TEXTL | |
TEXTR | |
FLOATL | |
FLOATR | |
TextDirection | |
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-pink | (no common association) |
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
<!-- Display FontAwesome icon, inline, using the hex code in the form ꪪ -->
<span class="FA"></span>
Or, using CSS to declare a class which displays an icon using
.tickicon {
font-family: FontAwesome;
}
.tickicon:after {
content: "\f058";
}
Figaro's built-in widgets have little standard HTML structure because they display such a wide variety of information and functionality.
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
You may want to re-use the
Adding the class
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
Parameter | Description |
menu | Definition of the menu (which can contain items with sub-menus) |
eventOrNode | Either a node in the DOM, or a Javascript |
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
The asynchronous callback function receives the user's selection from the menu, or cancellation. If there was a selection, then the
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
}
});
The framework contains some helper functions, mainly within the
The
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 |
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
// 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
// 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
// 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
});
Simple helper function which tests whether an object x is an empty object,
var bIsEmpty = FXB.utils.IsEmpty(someObject);
For widgets only - because scripts and UDIXes are web workers, and have no HTML document - the framework creates a
$$("btnSave").disabled = true;
Empty array.
Array with single item.
Array with multiple items.
To put all that another way:
If there is a single node with
If there are multiple nodes with
The following section describes the
All the values described in this section are in the
There are three values which are treated by FXB.Result as
| Description |
OKAY | No error; success |
SUCCESS | Synonym for |
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
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:
| Description |
NOTYETIMPLMENTED | Functionality has not yet been implemented. |
TR_NOTYETIMPLEMENTED | More specifically, trading functionality which has not yet been implemented |
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.
| Description |
TR_INVALIDJOBDEFINITION | Invalid request definition for a trading job (generally invalid, rather than a problem such as |
TR_BADINSTRUMENT | Unrecognised or missing |
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 |
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_ |
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. |
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:
| 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 |
There are two error codes relating to trading validators:
| Description |
TR_VALIDATORREJECTION | Trading validator rejected the request ( |
TR_VALIDATORTIMEOUT | Time-out while collecting a response from active trading validators |
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
| 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.) |
There is a small number of further error codes which do not relate to trading activity:
| 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 |
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
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
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 |
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
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 |
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 |
Scripts which are assigned to the Chart context are displayed under a chart's settings menu.
The script receives the following information in its
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 |
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
Member | Description |
scriptContext | Set to "ordermenu" |
orders[] | Array of applicable orders/trades |
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
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 --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
Your device ID as the request's querystring:
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
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 |
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
Messages to your own device can have priority. This is ignored if you use the
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
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
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 |
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
You open the
You add the attachment data (beginning