Functions in the framework

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

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

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

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

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

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

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

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

3.1 Price requests

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

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

// Request EUR/USD

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

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

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

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

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

Framework.RequestPrices("EUR/USD");

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

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

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

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

});

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

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

3.2 Historic candle data

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

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

For example:

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

Framework.RequestCandles({

instrumentId: "EUR/USD",

timeframe: 3600

}, function (MsgCandles) {

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

});

There are three types of candle request:

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

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

· A request for the candles between start and end dates.

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

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

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

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

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

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

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

3.2.1 Candle response messages

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

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:

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

In a streaming request, the following will happen:

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

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

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

3.2.2 Terminating streaming requests

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

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

3.2.3 Time zones for candle requests

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

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

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

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

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

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: Sway.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 Sway Charts Pro platform for how candle data should be aggregated. If you do not explicitly set a timezone in your candle request, you will automatically get the user's preferred settings. And the default settings in Sway Charts Pro are not UTC; they're UTC+2 with USA daylight savings.

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

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

3.3 Trading

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 Sway.OrderTypes.FLATTEN. This closes all orders and trades on the account, rather than your code having to iterate though the orders in Framework.Orders and issue its own separate requests to close each one.

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

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

For example:

Framework.SendOrder({

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

tradingAction: Sway.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: Sway.OrderTypes.CLOSEPOSITION,

instrumentId: "EUR/USD"

},{

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

tradingAction: Sway.OrderTypes.BUY,

instrumentId: "GBP/USD",

volume: {lots: 0.05}

}

]}, function (Msg) {

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

}, {

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

confirm: true

});

3.3.1 Overview of trading requests

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

Useful note! The Run Script widget in the Sway Charts Pro 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 Sway.OrderTypes enumeration has values which are not applicable in the context of an Sway.Order. These extra values are described in the sections below.

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

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

Framework.SendOrder({

sl: {pips: 50},

volume: {equityPercent: 1},

3.3.2 New pending orders and trades

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

var request = {

tradingAction: Sway.OrderTypes.SELL_LIMIT,

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

instrumentId: "EUR/USD",

volume: {lots: 0.20}

};

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

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

3.3.2.1 Volume on order and trade requests

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

3.3.2.2 Open price on pending orders

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

3.3.2.3 Stop-loss on orders and trades

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

3.3.2.4 Take-profit on orders and trades

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

3.3.2.5 Trailing stop on orders and trades

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

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

3.3.2.6 Expiry on pending orders

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

3.3.2.7 Candle-based prices in order definitions

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

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

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

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

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: Sway.OrderTypes.BUY_LIMIT,

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

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

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

tp: {pips: 30},

3.3.3 Modifying open trades and pending orders

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

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

// (leaving all other settings unchanged)

Framework.SendOrder({

orderId: "O12345",

tradingAction: Sway.OrderTypes.CHANGE,

sl: {pips: 30}

}, function (MsgResult) {

});

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

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

Framework.SendOrder({

orderId: "T12345",

tradingAction: Sway.OrderTypes.CHANGE,

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

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

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

}, function (MsgResult) {

});

3.3.4 Closing individual trades and orders

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

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

// Delete pending order O12345

Framework.SendOrder({

tradingAction: Sway.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: Sway.OrderTypes.CLOSE,

orderId: "T12345"

}, function (MsgResult) {

});

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

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: Sway.OrderTypes.CLOSE,

orderId: "T12345",

volume: {percent: 75}

}, function (MsgResult) {

});

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

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

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

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

Framework.SendOrder({

tradingAction: Sway.OrderTypes.CLOSEPOSLOSERS,

instrumentId: "EUR/USD"

}, function (MsgResult) {

});

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

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

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

Framework.SendOrder({

tradingAction: Sway.OrderTypes.FLATTEN

}, function (MsgResult) {

});

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

3.3.7 Asynchronous result callback for SendOrder()

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

Framework.SendOrder({

… request definition

}, function (Msg) {

if (Msg.result.isError) {

// Trading request failed

} else {

// Trading request did not fail. However…

}

});

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

· Request fails. The Sway.Result will return isError=true

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

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

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

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

3.3.8 Optional settings for trading requests

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

3.3.9 Forcing confirmation of all subsequent trading

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

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

3.3.10 Validating trading requests without execution

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

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

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

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

Framework.ValidateOrder({

tradingAction: Sway.OrderTypes.SELL_LIMIT,

openPrice: {offset: 0.0020},

instrumentId: "EUR/USD",

volume: {lots: 0.20}

}, function (Msg) {

… result of validation

});

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

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

{

instrumentId: "EUR/USD",

tradingAction: Sway.OrderTypes.SELL_LIMIT,

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

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

}

3.4 Account history

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

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

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

3.5 Dialogs

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

3.5.1 Standard dialog behaviour and settings

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

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

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

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

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

3.5.2 Common information/alert/question dialogs

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

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

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

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

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

});

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

Framework.Ask({

title: "Really?",

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

buttons: [

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

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

]

}, function (Msg) {

});

3.5.2.1 Button styles for common dialogs

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

3.5.2.2 Callbacks from common dialogs

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

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

Framework.Ask(… , function(Msg) {

if (Msg.result) {

// Final result

} else {

// Initial creation/update

}

});

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

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

Framework.Ask(…, function(Msg) {

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

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

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

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

}

});

3.5.2.3 Manually destroying common dialogs

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

Framework.Ask({

title: "Are you sure?",

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

}, function (Msg) {

if (!Msg.result) {

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

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

// user has not answered.

setTimeout(function() {

Framework.DestroyDialog(Msg.widgetId);

}, 5000);

} else {

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

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

}

});

3.5.2.4 Floating, non-modal common dialogs

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

Framework.InfoMessage({

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

floating: true

});

3.5.3 Selecting a market or timeframe

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

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

Framework.SelectInstrument(function (instrumentId) {

if (instrumentId) {

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

} else {

// Cancellation of dialog

}

});

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

Framework.SelectTimeframe(function (timeframe) {

if (timeframe) {

} else {

// Cancellation of dialog

}

});

3.5.4 Asking for a text value

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

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

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

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

});

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

Framework.AskForText({

title: "Please enter your name",

heading: "What are you called?",

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

text: "", // Text to prepopulate with

}, function (result) {

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

});

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

3.5.5 Selecting from a list

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

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

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

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

Framework.SelectFromList([

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

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

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

], function (selection) {

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

});

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

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

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

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

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

Framework.SelectFromList({

title: "What shall we do?",

noCancel: true,

items: [

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

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

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

]

}, function (selection) {

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

});

3.5.5.1 Checkbox list

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

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

Framework.SelectFromList({

title: "Which do you want?",

checkboxes: true,

items: [

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

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

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

]

}, function (selection) {

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

});

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

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

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

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

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

Framework.MenuListDialog({

title: "My list",

items: [

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

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

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

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

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

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

]},

]

}, function (Msg) {

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

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

}

});

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

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

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

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

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

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

// User has made a selection

} else {

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

}

3.5.7 Displaying a settings dialog

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

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

Framework.AskSettings([

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

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

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

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

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

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

]}

], function (MsgResult) {

});

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

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

· An array of fields, as in the example above

· An object with a fields[] array

· An object with a sections[] array

· An object with a pages[] array

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

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

Framework.AskSettings({

sections: [

{

caption: "Section 1",

fields: [

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

]

}, {

caption: "Section 2",

fields: [

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

]

}

]

}, function (MsgResult) {

});

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

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

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

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

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

· Or, just a fields[] array

3.5.8 Custom HTML dialogs

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

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

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

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

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

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

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

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

html += "<script>";

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

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

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

html += "});"

html += "</script>";

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

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

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

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

Various things are happening here:

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

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

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

You can then create such a dialog as follows:

Framework.CreateHtmlDialog({

title: "My dialog",

html: html

}, function(Msg) {

if (Msg.result) {

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

clearInterval(Framework.$myTimer);

} else if (Msg.update) {

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

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

// Dialog is asking to be closed

Framework.DestroyDialog(Msg.widgetId);

} else {

// Some other message

}

} else {

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

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

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

// once per second.

setInterval(function() {

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

}, 1000);

}

});

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

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

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

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

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

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

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

3.5.9 Widget displaying itself as its own dialog

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

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

<html>

<head>…</head>

<body>

<div id="NormalContent">

<!-- Normal widget content -->

</div>

<div id="DialogContent">

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

</div>

</body>

</html>

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

Framework.OnLoad = function() {

// Check the mode. See below…

if (Framework.privateSettings.asDialog) {

// Dialog mode

// Hide the normal content and show the dialog content

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

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

// Do initialisation for dialog mode

} else {

// Normal mode

// Show the normal content and hide the dialog content

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

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

// Do normal initialisation

}

};

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

Framework.CreateDialog({

// Use own type as the type for the dialog

type: Framework.type,

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

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

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

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

settings: {

asDialog: true

}

}, function (Msg) {

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

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

});

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

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

// Create Sway.Result object

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

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

result.myData = {

someKey: someValue

};

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

Framework.SendDialogResult(result);

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

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

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

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

if (Msg.result) {

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

} else if (Msg.update) {

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

} else {

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

}

});

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

3.5.10 Displaying the platform's standard deal ticket

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

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

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

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

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

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

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

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

if (Msg.result) {

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

} else {

// Initial update, providing the ID of the dialog

}

});

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

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: Sway.OrderTypes.SELL,

instrumentId: "AUD/USD",

volume: 30000,

sl: {pips: 20}

},

handleExecution: false,

message: "This is my suggested trade"

}

}, function (Msg) {

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

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

} else {

// Initial update, or cancellation of the dialog

}

});

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

3.5.10.1 Selecting an order template

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

Framework.ChooseOrderTemplate(null, function(template) {

// Asynchronously receives the selected template, or null

if (template) {

// Potentially modify or add to the template

// …

// And then pass the template into the deal ticket

Framework.CreateDialog({

type: "dealticket",

settings: {

orderDefinition: template

}

});

}

});

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

Framework.ChooseOrderTemplate(null, function(template) {

// Asynchronously receives the selected template, or null

if (template) {

// Put the required market into the template

template.instrumentId = <some market ID>;

// And then pass the template into the deal ticket

Framework.SendOrder(template, function (MsgResult) {

// … handle asynchronous success or failure

});

}

});

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

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

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

if (template == "default") {

// User clicked the Default button.

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

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

Framework.CreateDialog({

type: "dealticket"

});

} else if (template) {

// User selected a template

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

Framework.CreateDialog({

type: "dealticket",

settings: {

orderDefinition: template

}

});

} else {

// (User cancelled the dialog)

}

});

3.5.11 Notifying the user by generating mail

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

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

3.5.12 Toast

"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 Sway Charts Pro app itself if the user turns on options in the Notifications section of the app's settings.

3.5.12.1 Creating toast

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

Framework.CreateToast({

title: "Finished!",

text: "The task has been completed",

icon: "f05a"

});

The standard properties of the toast definition are as follows:

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

Framework.CreateToast({

title: "Finished!",

text: "The task has been completed",

icon: "f05a"

}, function (ToastMsg) {

if (ToastMsg.clicked) {

// User has clicked on the toast

} else {

// The toast is being destroyed

}

});

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

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

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

3.5.12.2 Dialogs from toast clicks

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

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

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

An example of a dialog definition:

Framework.CreateToast({

title: "Something happened",

text: "The text of the toast",

dialog: {

type: "infodialog",

settings: {

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

}

}

});

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

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

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

3.5.12.3 Destroying toast

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

· The user clicks on it

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

There can be two exceptions to this lifespan:

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

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

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

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

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

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

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

· Using the toastId in the click callback function

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

Framework.CreateToast({

title: "Permanent…",

text: "Stays on screen until manually destroyed",

noDestroyOnClick: true,

lifespan: 999999999

}, function (ToastClickMsg) {

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

and then …

Framework.DestroyToast(ToastClickMsg.toastId);

});

3.6 Settings and state storage

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

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

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

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

3.6.1 Types of framework storage: private and category

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

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

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

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

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

3.6.2 Loading and saving category settings

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

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

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

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

});

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

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

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

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

Framework.SaveCategorySettings({

instrumentId: "EUR/USD",

timeframe: 3600,

visualOptions: {

color: "blue"

}

});

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

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

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

· But you don't act on the change immediately.

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

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

3.6.3 Loading and saving private settings

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

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

Framework.SavePrivateSettings({

instrumentId: "EUR/USD",

timeframe: 3600,

visualOptions: {

color: "blue"

}

});

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

3.6.4 Combining category settings and private settings

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

· If privateSettings are not empty, use those

· Load category settings

· If categorySettings are then not empty, use those

· Otherwise, use defaults

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

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

For example, using Sway.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 (!Sway.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 (Sway.utils.IsEmpty(Framework.$myState)) {

Framework.$myState = { … some defaults … };

}

// And, finally, do UI initialisation based on the settings or defaults

Framework.$UpdateMyUI();

});

};

3.6.5 Widget state (for new windows etc)

The user can choose to take any widget in the Sway Charts Pro page container and pop it out into a new browser window. You can copy the settings from the existing widget into the new window using a mechanism very similar to, and often related to, privateSettings.

You need to handle Framework.OnGetState() which the framework calls in order to ask you, "Do you have any stored settings which you want to pass into the new window?". For example:

Framework.OnGetState = function()

{

return {

instrumentId: "EUR/USD",

timeframe: 3600,

visualOptions: {

color: "blue"

}

};

}

The new pop-up browser window will receive this stored state in its Framework.privateSettings.

As a result, it's very common for a widget's storage of private settings and its handling of OnGetState() to be linked. For example:

· The widget uses OnGetState() as a general function for building a representation of its current settings.

· When saving private settings as a result of a user change, the widget calls its own OnGetState() function.

For example:

// Build some representation of the widget's state. This is returned both internally

// and to the framework when opening a new window.

Framework.OnGetState = function() {

var settings = { … };

return settings;

};

// Private internal function which our widget calls whenever the user makes a change

// and new settings need to be saved. We simply re-use OnGetState() as a way

// of getting the current state.

Framework.$SaveMySettings = function() {

Framework.SavePrivateSettings(Framework.OnGetState());

}

Note: OnGetState() is also the mechanism which allows a widget to keep running even if the user navigates to a different page of the app.

3.6.6 Saving both private and category settings

It is very common for a widget's private and category settings to be the same thing. For example, if you make a change in Sway Charts Pro's standard quote board such as switching the view between grid and deal tickets then that doesn't alter other open quote boards – they ignore CATEGORY_SETTINGS_CHANGE – but the most recent changes do become the default for all new future instances of the quote board.

In other words, on a change of settings, it's very common for a widget to do the following:

var newState = Framework.OnGetState(); // See example above

Framework.SavePrivateSettings(newState); // Private settings for this instance

Framework.SaveCategorySettings(newState); // Use same settings as defaults for new instances

This can be made slightly more efficient by combining the two saves into a single call to Framework.SaveSettings(). This takes two parameters: private settings first, category settings second. The above example would then become:

var newState = Framework.OnGetState();

Framework.SaveSettings(newState, newState);

3.7 Interacting with the Sway Charts Pro page container and environment

3.7.1 Handling context – synchronization in the page container

This section is only applicable to widgets. A script or UDIX cannot (directly) be part of the Sway Charts Pro page container.

If your widget offers a selection of market or timeframe, you may want to respect "context" where the user can choose to synchronize the markets and/or timeframes on a page. Doing this consists of three parts:

· Reading any context when your widget starts up

· Handling changes of context while your widget is running

· Sending changes of context to the page container if the user changes the market or timeframe selection from inside your widget

Any existing context on start-up is provided by the Framework.context property. If present, this may contain one or both of an instrumentId and a timeframe. The typical way of handling this is to inspect it and, if present, to use it to override any private or category settings which the widget has.

The example above about widget settings has the following code:

... code which examines private and category settings, and builds Framework.$myState …

// And, finally, do UI initialisation based on the settings or defaults

Framework.$UpdateMyUI();

A typical way of handling context would be, as a final stage after loading stored settings or using defaults, to overwrite any market or timeframe selection if the page container is providing context. For example:

... code which examines private and category settings, and builds Framework.$myState …

// The addition:

// If the page container has instrument context, overwrite whatever settings we have built up

if (Framework.context && Framework.context.instrumentId) {

Framework.$myState.instrumentId = Framework.context.instrumentId;

}

// And, finally, do UI initialisation based on the settings or defaults

Framework.$UpdateMyUI();

There may also be changes in context while your widget is running, as a result of the user changing the market and/or timeframe selection in another widget. You receive these changes as a WIDGET_CONTEXT_CHANGE message, and you would typically handle it the same way as the user changing the selection within your own widget.

Finally, if the user changes the market and/or timeframe selection within your own widget, using your own UI, you broadcast that change to any other widgets in the same page as yours. You do this using Framework.ReportContext(). For example:

Framework.ReportContext({

instrumentId: <new market selection>,

timeframe: <new timeframe selection>

});

3.7.2 Changing the market or timeframe of the current page

Any widget, script, or even UDIX can change the context of the current page in the Sway Charts Pro container without being part of the current page, using the functions Framework.SetCurrentPageInstrument() and Framework.SetCurrentPageTimeframe().

For example, the following script sets up a "slideshow", cycling the current page between different markets every 5 seconds until the script is terminated by the user:

var instrumentList = ["EUR/USD", "USD/JPY", "GBP/USD", "USD/CHF"];

var currentIndex = -1;

setInterval(function() {

currentIndex++;

if (currentIndex >= instrumentList.length) currentIndex = 0;

Framework.SetCurrentPageInstrument(instrumentList[currentIndex]);

}, 5000);

3.7.3 Changing the current page displayed in Sway Charts Pro

You can change the current page which is being displayed in Sway Charts Pro using Framework.NavigateToPage().

The parameter for this function is the user-controlled name of the page, such as "Quote board", or "My new page". There is no mechanism for saying "go to the quote board, whatever the user has called the page", or "go to the first/any page which contains the quote board widget".

3.7.4 Chart actions

Sway Charts Pro provides functions which allow any widget, script, or UDIX to make changes either to all open charts, or just to the active chart (the one which the user last clicked on). This is done using Framework.ChartChange().

The Run Script widget in Sway Charts Pro serves as a full and interactive guide to the available chart actions. You can get information on how to build chart-change code by using the options in the Run Script widget.

3.8 Communicating between widgets/scripts/UDIXes

Different instances of a widget, script, or UDIX can share saved "category" settings as described above, with notifications when the shared settings change.

It is also possible for different widgets, scripts, and UDIXes to send messages to each other without permanent data storage. This comes in three parts.

· Every widget, script, or UDIX has an ID, Framework.id.

· A component which is interested in talking to another component sends out a broadcast message.

· The other component(s) receive the broadcast message, in OnMessage(), and can see the sender's ID. They can now send a direct message back. On receipt of a direct message (or a broadcast message), the original sender can now see the recipient's ID, and send further direct messages.

You send messages using Framework.PostMessage(). The only compulsory parameter is an Sway.Message object, which you can construct as follows. The initialisation must have a msgType property, and can then contain any other data that you want.

The framework has special message types for widgets to exchange ad hoc data. There are BROADCASTx messages which can be sent without knowing a recipient's ID, and GENERICx messages which can be used for direct, peer-to-peer communication.

For example:

var myMessage = new Sway.Message({

msgType: Sway.MessageTypes.BROADCAST1, // A msgType is compulsory for Sway.Message

myAction: "Want to chat?",

anyOtherData: {

moreData: 231,

},

someProperty: someValue

});

Framework.PostMessage(myMessage);

Broadcast messages are sent to all other widgets, scripts, and UDIXes in the platform. Those can then listen for broadcasts in OnMessage(), and choose to respond. For example:

Framework.OnMessage = function(Msg) {

if (Msg.is(Sway.MessageTypes.BROADCAST1)) {

// Is this a message which we understand?

if (Msg.myAction && Msg.myAction == "Want to chat?") {

// Send a response

var response = new Sway.Message({

msgType: Sway.MessageTypes.GENERIC1,

_to: Msg._from, // The _from of the Msg gives us the sender's ID

myResponse: "Yes, I want to chat"

});

Framework.PostMessage(response);

}

}

};

When a component receives a message, the _from property tells it the sender's ID. This gives it the ability to construct a direct message back to the sender, by setting the _to property in a response message – as in the example above.

The original sender could then listen for acceptance of its original broadcast as follows:

Framework.OnMessage = function(Msg) {

if (Msg.is(Sway.MessageTypes.GENERIC1)) {

// Is this a message we understand?

if (Msg.myResponse && Msg.myResponse == "Yes, I want to chat") {

// The recipient of broadcast knows this sender's ID,

// and this sender now knows the responder's ID because

// it is the _from of this response message

}

}

};

Once a direct connection is established, there is a further mechanism which can simplify the sender/response code. The Framework.PostMessage() function has an optional second parameter: an asynchronous callback which receives responses. This is called if a recipient of a message uses Framework.Respond().

For example, once they have each other's IDs, one component can send a message to another component as follows:

Framework.PostMessage(myNewMessage, function (Msg) {

// Callback triggered by the recipient using Respond().

// The response message also passes through OnMessage(), but handling it "inline"

// like this will often be simpler and cleaner.

});

The recipient triggers the response callback in the sender by using Framework.Respond() instead of Framework.PostMessage(). The Respond() function takes two parameters: the new message to send back, and the message to respond to. For example:

var myResponse = new Sway.Message({

msgType: Sway.MessageTypes.GENERIC1,

myData: "some communication"

});

Framework.Respond(myResponse, someOriginalMessage);

(If you are using Respond(), you don't need to set an explicit _to on the message you are sending. Respond() fills this in for you.)

The one thing to note is that an asynchronous callback for PostMessage() is only called once (or not at all, if nothing responds). If you use an asynchronous callback with a broadcast message, then it will only receive the first response, and be terminated after that. If there are second and subsequent responses from other components, then those will be ignored (unless also separately handled in OnMessage).

In other words: an asynchronous callback on PostMessage() is great for direct, peer-to-peer messages, but generally not suitable for broadcast messages unless you are specifically only expecting a single response.

3.9 Time conversion functions

All times in the framework – such as the openTime of an order or the ts of a candle – are UTC. The framework provides a variety of functions for helping with time conversion.

Note: all the parameters and return values for the framework's date functions are numbers (of milliseconds), not Javascript Date objects.

3.9.1 Time zone definitions

The framework uses the following properties to define a timezone:

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: Sway.DSTModes.USA

}

Note: this is a time zone widely used in forex trading, but not a setting you will ever see on a normal clock. It is widely referred to as "central European time" or "Cyprus time" (because many fx brokers are based there), but that is not what it is. It is 2/3 hours ahead of UTC, but it changes on the USA daylight-savings schedule, not the European schedule. It will be the same as Cyprus time for most of the year, but it will be different during the periods when the USA is in daylight-savings but Europe is not.

3.9.2 Getting the current time – Framework.Now()

The Framework.Now() function returns current UTC time, in milliseconds, according to the computer clock.

It is therefore almost entirely equivalent to (new Date()).valueOf(). However, the framework includes a time check with the Sway Charts Pro server, and watches for computer clocks which are materially wrong (current threshold: >30 seconds wrong). One of the most common issues it picks up is a clock which the user has changed to local time without adjusting the computer's time zone. The result is that the computer clock shows the "correct" local time to the user, but its calculation of UTC is hours out.

If the framework detects a problem with the local clock, Framework.Now() will return an adjusted time, correcting for errors in the local clock.

3.9.2 Converting between UTC and a different time zone

The function Framework.AdjustUTCToZone() converts from UTC to a different time zone. It takes two parameters: a UTC time in milliseconds and a timezone description. For example:

var someDate = new Date(Date.UTC(2020, 6, 1, 0, 0, 0)); // July 1st 2020, midnight UTC

var newYork = Framework.AdjustUTCToZone(someDate.valueOf(), {

offset: -300, /* USA east coast at UTC-5 with daylight savings */

dstMode: Sway.DSTModes.USA

});

This will return 1593547200000. Javascript has no way of specifying that a Date object belongs to a particular zone. What AdjustUTCToZone() has to do is to return a date/time where the UTC functions such as getUTCHours() will give the adjusted time.

To put it another way, 1593547200000 represents June 30th 8pm UTC – the original time adjusted backwards by 4 hours (not 5 hours, because the USA was in daylight savings on the specified date). For example:

var date = new Date(1593547200000);

var hours = date.getUTCHours(); // Returns 20

var dayOfMonth = date.getUTCDate(); // Returns 30

var month = date.getUTCMonth(); // Returns 5 (June)

You can do the conversion in the opposite direction using Framework.AdjustZoneToUTC(). You pass in a number of milliseconds which represents a date/time in another zone, and the function adjusts that to UTC. For example:

var newYork = 1593547200000;

var utcTime= Framework.AdjustZoneToUTC(newYork, {

offset: -300, /* USA at a UTC-5 with daylight savings */

dstMode: Sway.DSTModes.USA

});

This reverses the process in the example above and returns 1593561600000 – July 1st 00:00 UTC.

3.9.3 The user's time zone preferences

The settings in Sway Charts Pro let the user set two different default time zones: one for charts, and one for general purposes such as open and close times in the trade list. These will normally be the same value, but don't have to be.

You can get the user's preferences using Framework.GetUserChartTimezone() and Framework.GetUserGeneralTimezone(). These both return timezone definitions, like the examples above.

For example, you can convert a UTC date/time to the user's general time zone using:

var userTime = Framework.AdjustUTCToZone(someUtc, Framework.GetUserGeneralTimezone());

It's up to you whether you respect the user's preferences and convert a date before displaying it – and it may not always be appropriate to do so, if you are dealing with something where the user will expect to see their local time despite their preferred settings for the platform.

3.10 Helper functions for formatting values

The framework has a variety of functions for doing standard formatting of numbers, dates, prices etc.

3.10.1 Framework.FormatNumber()

FormatNumber() is a general-purpose function for formatting numbers. It has two parameters: the number to convert, and a block of options. For example:

var text = Framework.FormatNumber(1234.5678, {digits: 3, show_plus: true})

// With standard settings, returns "+1,234.568"

amework.

The ORDER_BOOK response messages, in an asynchronous handler or in OnMessage(), are identical to the MARKET_DEPTH responses described above. The message contains the changes plus the full current book.

ORDER_BOOK responses have one further property, as mentioned above:

3.10.2 Framework.FormatCash()

FormatCash() formats a currency amount. It is a simple bare wrapper around FormatNumber(), and accepts the same options as FormatNumber(). It is intended to be used with digits:null rather than a specific number, and then automatically selects a number of digits to use based on the account deposit currency. For example, on a JPY account it will convert the value at 0DP.

3.10.3 Framework.FormatPercent()

FormatPercent() formats a percentage, appending a % symbol. It is a simple bare wrapper around FormatNumber(), and accepts the same options as FormatNumber(). The position of the % symbol depends on the user's selected language for the Sway Charts Pro platform: prepended for Japanese; appended for everything else.

3.10.4 Framework.FormatVolume()

FormatVolume() formats a trading volume. By default, it reads the user's preferences in the Sway Charts Pro settings about whether volumes should be displayed as cash/units or lots. For example, volume of 20000 may be formatted as either "0.20 lots" or "20K".

Because lot-size conversion is market-specific, the FormatVolume() function requires an Sway.Instrument. For example:

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

var text = Framework.FormatVolume(20000, market, {append_lots:true});

// For example, text = "0.20 lots"

FormatVolume() has three parameters:

FormatVolume() is again a wrapper around FormatNumber(). You can use the full features of FormatNumber(), but typically you will only want to use the following properties in the options:

3.10.5 Framework.FormatDate()

FormatDate() formats a date, by default in the format yyyy/mm/dd hh:mm:ss

The possible options for FormatDate() are as follows:

3.10.6 Framework.DisplayPrice()

DisplayPrice() formats a price into an HTML element, rather than returning text. This is because it implements Sway Charts Pro's standard formatting of prices such as 1.23456 which requires HTML representation, with specific CSS classes, rather than plain text. (Therefore, DisplayPrice() is only relevant to a widget, not to a script or UDIX.)

Note: the DOM node which receives the output must be empty on first use of DisplayPrice().

The parameters for DisplayPrice() are as follows.

For example:

<!-- Note: the element which is the target of DisplayPrice() must be empty on the first call -->

<span id="bid"></span>

var market = Framework.Instruments.get("EUR/USD");

Framework.DisplayPrice("bid", market, market.bid);

3.10.7 Generating a textual description of an order or trading action

Framework.DescribeOrderOrTemplate() turns an Sway.Order or a trading request into a textual description such as "Buy 0.20 lots EUR/USD". Note: the resulting text is automatically run through the Translation system and will be adjusted to the user's selected language.

For example:

// Generate a caption for trade with orderId T12345

var tradeCaption = Framework.DescribeOrderOrTemplate(Framework.Orders.get("T12345"))

// Generate a caption for a trading request to sell 0.20 lots EUR/USD

var requestCaption = Framework.DescribeOrderOrTemplate({

tradingAction: Sway.OrderTypes.SELL,

instrumentId: "EUR/USD",

volume: {lots: 0.2}

});

3.11 Trading validators

Trading validators are not – currently – used by any built-in part of the Sway Charts Pro 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 Sway Charts Pro, and has the option to block them. The idea is that a trading validator can implement custom checks on trading activity such as the following:

· Is the spread on a market too wide?

· Is the time too close to a high-impact event in the economic calendar?

· Does the open volume on a market mean that we don't want to allow a further trade?

· Do we want to prevent pending orders without expiry?

· Do we want to block trading entirely?

You register your widget or script (or even UDIX) as a trading validator by calling Framework.AddTradingValidator(). To deregister it, and stop receiving validation messages, you call Framework.RemoveTradingValidator().

Once you have called AddTradingValidator(), all trading requests throughout the platform get submitted for approval to your widget or script. This happens after a request has passed all Sway Charts Pro's internal validity checks, but before the request is sent to the broker for execution.

Requests are sent to a function called Framework.OnTradeValidation(). This must return a synchronous response, in order not to delay trading unduly. You cannot look up some asynchronous data, such as the economic calendar, and then later send a response to the validation request once you have the data you need. To set up a check such as the economic calendar, you will typically request the calendar (asynchronously) while simultaneously registering as a validator. If you get a validation request before you have the calendar data to verify it, it is up to you whether you accept or reject the validation request.

OnTradeValidation() rejects a request by returning {status: "block"}, or specifies that the user must manually confirm it by returning {status: "confirm"}. Any other return value allows the request to go ahead. For example:

Framework.OnTradeValidation = function(Msg) {

if ( … some condition we don't like … ) {

// Block and reject the request

return {status: "block"};

} else if ( … some condition we're not sure about … ) {

// Show a confirmation dialog to the user (which may

// happen anyway, depending on other settings and controls)

return {status: "confirm"};

} else {

// Allow the trading action to proceed

return null; // or {status: "okay"}, or anything apart from status:"block"/"confirm"

}

};

If you return {status: "block"} then you can also add blockText into your response. This message is added to the standard rejection message to the user. For example:

return {status: "block", blockText: "Are you mad?"};

Similarly, if you return {status:"confirm"} then you can add confirmText to the response.

Your validator receives two pieces of information in the Msg message which is passed into OnTradeValidation():

There are two things to note about the array of tradingJobs[]:

· You cannot approve or reject individual requests within the array. You can only approve the whole batch or reject the whole batch.

· What you receive in tradingJobs[] is a list of trading actions dis-aggregated into the low-level requests such as opening or closing individual orders. For example, if the user clicks Close > All in the built-in Sway Charts Pro trade list widget, or a script uses the equivalent FLATTEN trading action, then what you will get in tradingJobs[] is not a single FLATTEN request but instead a separate entry in the array for each individual ticket which is going to be closed/deleted.

3.12 Market depth and order book

Availability of market depth and order book data depends on the capabilities of the current logged-in trading account. They are not widely available.

In this context, "market depth" generally refers to institutional liquidity arranged by a broker, showing quotes below the best bid and offer in a market. "Order book" generally refers to traders' orders, on a peer-to-peer basis in an exchange. If the back-end platform provides an order book, it may be an aggregated view rather than all individual orders – e.g. price bands of 0.0010 with a totalled summary of the orders within each band.

3.12.1 Market depth

The framework always responds to market depth requests, even if no depth data is available from the back-end system. What it will then send is simply changes in the current bid and ask, with no trading volume.

Where market depth is available, the back-end system may send either of the following:

· Multiple quotes at each price (from different liquidity providers)

· A single quote at each price

You request market depth using Framework.RequestMarketDepth(). This has one compulsory parameter and two optional parameters:

For example:

// Allow the framework to allocate an ID for the request, reported as the requestId in messages

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

// Response containing market depth, also received in/via OnMessage()

// ID of the request, for use in termination, will be in Msg.requestId

});

You should terminate market depth requests when you no longer need them, using Framework.TerminateMarketDepth(). The function takes two parameters: the instrument ID, and the request ID allocated either by you or the framework.

Each market-depth update message has the following properties:

The changes object contains two arrays of changed quotes: asks[] and bids[]. Each item in these arrays can have the following properties:

The depth object contains the current full book following the changes; you don't need to calculate this yourself.

As noted above, the back-end system may have multiple quotes at each price (from different liquidity providers). The depth summary calculates both individual current quotes and also the aggregated volume at each price (which will be the same thing if the back-end system only issues aggregated quotes).

The depth object contains the following properties:

A couple of final notes:

· You need to be a little careful with this data. During unusual market conditions, or just very briefly during updates, it is possible for there to be no quotes on the bid side or ask side (or even both). Therefore, you cannot safely assume that aggregated.asks.length or aggregated.bids.length is greater than zero.

· Internally within the broker's back-end system, it is possible for there to be different routes for market depth versus "trading" prices. It is not absolutely guaranteed that the bestBid and bestAsk in the market depth will match the current bid and ask of the Sway.Instrument.

3.12.2 Order book

Order book – where available – is handled very similarly in the framework to market depth. This section mostly describes the differences rather than repeating identical information.

The key differences are as follows:

· The order book may include entries on both sides of the current price – users' stop orders as well as limits – whereas market depth will only have entries at and "below" the current price.

· The back-end system may list the order book as any of the following: every individual order, or aggregated totals of the orders at each price, or aggregated totals for bands of prices such as 0.0010. In the two latter cases the order-book messages from the framework will have a granularity property, such as 0.00001 or 0.0010.

· The volumes in the order book may not be normal trading volumes; they may be a different kind of value such as percentages of the total open volume. You can only rely on the volume figures having meaning relative to each other; you cannot rely on each one having independent validity in relation to the market's contractSize.

Order book for a market must be requested, using Framework.RequestOrderBook(), and terminated when no longer needed, using Framework.TerminateOrderBook().

The parameters for RequestOrderBook() are the same as RequestMarketDepth(): an instrumentId, an optional request ID, and an optional message handler (instead of processing the messages in OnMessage).

You should terminate requests when you no longer need them, using Framework.TerminateOrderBook(). The function takes two parameters: the instrument ID, and the request ID allocated either by you or the fr

3.13 Economic calendar

The Sway Charts Pro framework provides access to an economic calendar – unless the hasCalendar property of Framework.Environment indicates that no calendar is available in the current platform.

Widgets, scripts, and UDIXes can request the calendar using Framework.RequestCalendar(). This responds, asynchronously, with an object containing a calendarItems[] array. There can be further callbacks into the asynchronous handler function when items are added to the calendar, or when existing items are updated with new information. For example:

Framework.RequestCalendar(function (Msg) {

// This function can be called multiple times.

// There will be an initial asynchronous call with the current state of the calendar,

// potentially followed by updates.

// Msg.calendarItems[] is an array of new and/or updated items

});

Each item in the calendar array has the following properties. Note: there can be variation in the data depending on the source of the calendar. Only the ID, title, and timestamp are absolutely guaranteed to be available across all calendar providers.

3.14 The Sway Charts Pro mail system

This section describes Sway Charts Pro's internal mail system – not email. Widgets, scripts, and UDIXes can send email by using browser XMLHttpRequest to communicate with an email provider such as SendGrid or Amazon SES.

3.14.1 Availability of the mail system

Some versions of the Sway Charts Pro platform may not have a mail system. You can check availability of mail functionality by inspecting the hasMailSystem property of Framework.Environment.

3.14.2 Overview of the mail system

Mail in Sway Charts Pro 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 Sway Charts Pro.

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 Sway Charts Pro from outside the platform, from any environment which can make HTTP requests.

3.14.3 Sending mail

A widget, script, or UDIX can send three types of message:

· A local, transient message. These only exist within the running Sway Charts Pro app, and are destroyed when the user logs out of their current trading account or reloads the app.

· A remote message to the current device. These are stored permanently until the user chooses to delete them. The widget, script, or UDIX doesn't need to know the ID of the current device.

· A remote message to another device. The widget, script, or UDIX must know the ID of the device it is sending to.

Remote messages are subject to a quota, regardless of whether they are being sent to the current device or another device. Your device's ability to send mail will be temporarily blocked if you try to send too many remote messages. If you are using the mailbox as an alerting mechanism, you should make sure that you don't do something such as entering a loop where you generate the same alert over and over again. (In extreme cases, this can even lead to mail from the device being permanently blocked.)

You send mail using Framework.SendMail(). This takes two parameters: a definition of the mail to send, and an asynchronous callback which receives success or failure. For example:

Framework.SendMail({

subject: "A new message"

}, function (Msg) {

if (Msg.success) {

// Mail successfully sent

} else {

// Mail failed. Error reason will be in Msg.error

}

});

The mail request can have the following properties. The subject is the only compulsory property:

The priority level – only available on messages to the current device – can be one of the following values between 0 and 3:

The optional body of a message can be three things (subject to the 4KB limit):

· Plain text

· HTML – a full HTML document with <html> and </html> opening and closing nodes

· The URL of a web page, subject to the usual browser security restrictions

An HTML document or web page is displayed in a sandboxed iframe, and has no framework access.

3.14.4 Receiving mail

A widget, script, or UDIX can read the mailbox by using Framework.RequestMail(). This will issue an immediate (asynchronous) MAILBOX message to OnMessage(), followed by further MAILBOX messages whenever there are changes in the mailbox.

A MAILBOX message has a mail[] array listing all the messages currently in the system. It always lists all messages. On receipt of new mail, the MAILBOX message will contain the entire mailbox, not just the new message.

Widgets, scripts, and UDIXes can only see user-to-user messages. They cannot see messages issued by the broker. This is because those may contain sensitive information such as account passwords.

3.15 Miscellaneous functions

This section covers any remaining functions which do not fall into any of the previous categories above.

3.15.1 Generating downloads

You can trigger a download of data by using Framework.CreateDownload(). For example, Sway Charts Pro's built-in Account History widget uses this to do a download of the history in CSV format.

In a web browser, CreateDownload() simply triggers a download of the file. In the mobile/tablet app, it integrates with the device's notification centre, with options for sending the file to another app, saving it in the phone's storage etc.

CreateDownload() takes a single description object as a parameter which must contain the following properties:

For example:

CreateDownload({

fileName: "mydata.csv",

mimeType: "text/csv",

data: "Header1,Header2,Header3\r\nData1,Data2,Data3\r\netc"

});

Last updated