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:
Parameter | Description |
requestDef | Details of the candle request: market, timeframe etc |
callback | Callback function which asynchronously receives the requested data (and then further updates depending on the type of request) |
For example:
// Request latest candles on EUR/USD H1 (a default number of candles filled in by the framework)
Framework.RequestCandles({
instrumentId: "EUR/USD",
timeframe: 3600
}, function (MsgCandles) {
// Function which asynchronously receives the candle data from the framework
});
There are three types of candle request:
· The most common: a request for the last N candles up to the current time, e.g. in order to display a trading chart.
· A request for the N candles up to a historic point in time, e.g. in order to add further history when a user scrolls back in time on a chart
· A request for the candles between start and end dates.
All requests must include an instrumentId and a timeframe (as in the above example). These are the only compulsory properties in a request.
The list of available timeframes depends on the account and trading back-end which 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:
Property | Description |
instrumentId | ID of the Sway.Instrument for which you want data |
timeframe | Chart timeframe, such as 3600 |
count | Number of candles (not applicable if startDate is provided) |
startDate | Start date for historic request. If provided, must also set endDate. |
endDate | End date for historic request |
noStreaming | Optional flag which can be used to turn off streaming on requests. (Automatic if a request has an endDate.) |
requestId | Optional ID for the request, repeated back in messages to the callback function. If not provided, the framework will allocate one automatically. If you want to allocate your own ID, you are recommended to use Sway.utils.GenerateGuid(). |
timezone | Optional time zone settings for the request. See below. |
convertTimes | Option to do conversion of UTC times before returning the data. See below. |
Note: the Promise version of the function, pRequestCandles(), always sets noStreaming:true because a Promise cannot settle more than once; it cannot receive multiple callbacks with streaming data.
3.2.1 Candle response messages
The framework's response into your callback function will be a message object containing the following properties:
Property | Description |
result | Sway.Result object describing whether your request succeeded |
requestId | ID of the candle request, either allocated automatically or set by you in your request definition. This can be used to terminate streaming requests. |
candles[] | If your request was successful, an array of initial or changed candles |
timezone | Time zone used for the candle request. See below. Either repeats a timezone which you specified in your request or, if none, contains the user's default time zone for charts (Framework. GetUserChartTimezone). |
For example:
Framework.RequestCandles(requestDef, function (MsgCandles) {
if (MsgCandles.result.isOkay) {
// MsgCandles will contain a candles[] array
} else {
// Some problem with the request; see MsgCandles.result.code
}
});
Each item in a candle array has the following properties:
Candle property | Description |
i | Unique time-based index value for the candle. Not necessarily the same as ts. You should use i rather than ts to store/index and update candles. |
ts | Start time of the candle – see notes below about time zones. Depending on the back-end system, this is not necessarily the exact start of a timeframe-based period. For example, if you have requested hourly data, then it is possible when connected to some back-end systems for the ts to be e.g. 10:02, if that was the first price movement in the hour, rather than 10:00. |
o | Open price |
h | High price |
l | Low price |
c | Close price (current price for current candle) |
v | Volume. Not available on all platforms. |
The candles[] array is always sorted by time (ts), in descending order – most recent first.
In a streaming request, the following will happen:
· Your callback function will receive an initial call with an array of, say, 1000 candles
· Each time the price changes, the current candle will update, and your callback function will receive a candles[] array containing a single item: the update to the current candle.
· When the price changes and enters a new time period, you will receive a candles[] array containing an item with an i (and ts) which you have not previously received.
3.2.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:
Property | Description |
offset | Base offset in minutes from UTC, e.g. 120 or -300. Note: although this offset lets you specify a number of minutes, all existing back-end platforms require that any offset for candle requests is a number of hours. Therefore, offset should be a multiple of 60. |
dstMode | Daylight savings mode. One of the values of the Sway.DSTModes enumeration: NONE, USA, EUROPE, AUSTRALIA |
For example, the most common request for forex analysis looks like this:
Framework.RequestCandles({
instrumentId: "EUR/USD",
timeframe: 86400, // D1,
timezone: {
// 2 hours ahead of UTC, with USA daylight savings.
// Therefore, constant 7 hours ahead of New York time.
// Market open at 17:00 NYC on Sunday is reported as a candle ts of 00:00
offset: 120,
dstMode: 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:
Parameter | Description |
request | Definition of an individual request, or an array of requests |
callback | Callback function which asynchronously receives success of the request or (first) failure |
settings | Optional settings controlling how the request is processed. For example, the default is that the framework will automatically display an error message if a trading request fails, but you can use the settings to say that the framework should not display its own error dialog, and that you will do this yourself. |
For example:
Framework.SendOrder({
// Buy 10K (0.10 lots) EUR/USD
tradingAction: 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:
Example | Description |
volume: 10000 | Standard request, defining volume in terms of cash/units |
volume: {lots: 0.1} | Framework converts the specified lots quantity to cash/units using the contractSize of the Sway.Instrument |
volume: {equityPercent: 0.5} | Can only be used in combination with a stop-loss. The framework calculates the trade volume such that the cash value of hitting the stop-loss is (a maximum of) the specified percentage of current account equity |
In other words, the framework lets you send a request saying "stop-loss at 50 pips and risking 1% of account equity" without needing to do all the accompanying calculations yourself in your own code:
Framework.SendOrder({
sl: {pips: 50},
volume: {equityPercent: 1},
…
3.3.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:
Example | Description |
instrumentId | instrumentId of the Sway.Instrument to trade. Always compulsory. |
volume | Volume to trade. Always compulsory. |
openPrice | Open price for a pending order. Compulsory on pending orders; ignored on trades. For stop-limit orders, the openPrice is the limit price. |
triggerPrice | Trigger (stop) price for stop-limit orders. Compulsory on stop-limit orders; ignored on all other types. |
sl | Stop-loss to set on the order/trade. Always optional. |
tp | Take-profit to set on the order/trade. Always optional. |
trail | Trailing stop to apply to the order/trade. Always optional, and not necessarily available. Requires that the broker account supports trailing stops. |
expiry | Optional expiration on pending orders. Ignored on trades. |
comment | Textual comment to set on the order/trade, if supported by the back-end platform. |
magicNumber | MT4/5-style "magic number" to set on the order/trade, if supported by the back-end platform. |
3.3.2.1 Volume on order and trade requests
The volume can be specified in any of the following ways:
Volume | Description |
number | Simple numeric value in cash/units (not lots), such as 10000 |
{notional: x} | Same as a simple number: value in cash/units (not lots), such as 10000 |
{lots: x} | Value in lots, such as 0.10, which the framework converts using the market's contractSize |
{poundPerPoint: x} | Converted by the framework so that cash value of a movement by the market's pipSize is (a maximum of) the specified amount. Despite the property name ("pound"), deriving from UK spread-bet accounts, the value x is interpreted using whatever is the account's deposit currency. For example: trading EUR/USD on a GBP account, with pipSize of 0.00010. Each movement of the 0.0001 pipSize will gain or lose USD 10 (with contractSize of 100,000). If, on the GBP account, you set {poundPerPoint: 1}, then the framework will calculate the volume which, rounding down, leads to each 0.0001 movement gaining or losing £1. Calculated volume will be around 7000 (0.07 lots), depending on the current GBP-USD exchange rate. |
{cashRisk: x} | Requires a stop-loss on the order/trade. The framework calculates the volume which, in combination with the specified stop-loss, leads to a maximum loss of no more than x. |
{equityPercent: x} | Requires a stop-loss on the order/trade. The framework calculates the volume which, in combination with the specified stop-loss, leads to a maximum loss of no more than x% of current account equity. |
{balancePercent: x} | Requires a stop-loss on the order/trade. The framework calculates the volume which, in combination with the specified stop-loss, leads to a maximum loss of no more than x% of current account balance. |
3.3.2.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.)
openPrice: | Description |
number | Fixed price such as 1.23456 |
{price: x} | Same as a simple number: fixed price such as 1.23456 |
{offset: x} | Offset from the current market price, such as 0.0020. The value is directional depending on the tradingAction. For a sell-limit or buy-stop, it is added to the current price. For a sell-stop or buy-limit, it is subtracted from the current price. |
{pips: x} | Offset from the current market price in pips, such as 20. Converted by the framework using the market's pipSize. The value is directional depending on the tradingAction. |
{bar: barDef} | See the description of candle-based prices below |
3.3.2.3 Stop-loss on orders and trades
The sl on a pending order or trade can be specified using any of the following:
sl: | Description |
number | Fixed price such as 1.23456 (zero or omitted for no stop-loss) |
{price: x} | Same as a simple number: fixed price such as 1.23456 |
{offset: x} | Offset from the current market price, such as 0.0020. The value is directional depending on the tradingAction: subtracted from the current price for buy orders, added for sells. |
{pips: x} | Offset from the current market price in pips, such as 20. Converted by the framework using the market's pipSize. The value is directional depending on the tradingAction. |
{bar: barDef} | See the description of candle-based prices below |
{cashRisk: x} | Calculates the stop-loss so that, based on the volume, it equates to a cash value no more than x (in the account's deposit currency). Must be used with a fixed volume; cannot be used with a volume which is also variable/derived such as volume:{cashRisk:x} |
{equityPercent:x} | Calculates the stop-loss so that, based on the volume, it equates to no more than x% of the current account equity. Must be used with a fixed volume; cannot be used with a volume which is also variable/derived such as volume:{cashRisk:x} |
{balancePercent: x} | Calculates the stop-loss so that, based on the volume, it equates to no more than x% of the current account balance. Must be used with a fixed volume; cannot be used with a volume which is also variable/derived such as volume:{cashRisk:x} |
3.3.2.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:
tp: | Description |
{rrr: x} | Calculates the take-profit so that it is rrr times the distance of the stop-loss. For example, if rrr:2 then the take-profit will be twice as far from entry as the stop-loss (and represent twice the cash profit/loss). Must be used in conjunction with an sl; the sl cannot be zero/omitted. |
3.3.2.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:
trail: | Description |
number | Price offset, such as 0.0020 |
{pips: x} | Offset in pips, such as 20. Converted by the framework using the market's pipSize. |
3.3.2.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.)
expiry: | Description |
number | An absolute expiry time (UTC, milliseconds) |
"day" (expiry: "day") | End-of-day. The time zone for the "day" is taken from the user's settings for the Sway Charts Pro platform. |
{seconds: x} | Number of seconds from current time |
{minutes: x} | Number of minutes from current time |
{hours: x} | Number of hours from current time |
3.3.2.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.
Bar-based price | Description |
index | Zero-based candle index. Zero means "the current candle"; 1 means "the last complete candle" etc. |
price | The price to use from the candle: one of o, h, l, c |
offsetPips | Optional offset in pips to add (note: always add) to the candle price |
For example, the following request specifies the following:
· Buy-limit pending order
· Open price at the high of yesterday's D1 candle
· Stop-loss at the low of yesterday's D1 candle, minus 5 pips
· Take-profit at 30 pips from the current price (not candle-based)
Framework.SendOrder({
tradingAction: 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:
expiry: | Description |
number | Simple numeric value in cash/units, not lots, to close, such as 10000. (If this exceeds the trade volume, the framework will simply close the trade entirely rather than reporting an error.) |
{lots: x} | Portion to close in lots, converted by the framework to cash/units using the market's contractSize |
{percent: x} | Close the specified percentage of the trade, always rounding up to the nearest permitted trading increment. See the example below. |
The following request closes 75% of trade T12345. This is subject to the trading minimums and increments for the market. For example, if T12345 is a position of 3000 cash/units, and the minimum/increment volume for orders in this market is 1000, then the following instruction will be interpreted as a complete close. The framework always rounds up when doing a close. It will calculate 75% of 3000 = 2250, and round that up to the nearest permitted increment of 1000… which is a complete close of all 3000 cash/units.
// Close 75% of trade T12345
Framework.SendOrder({
tradingAction: 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) {
…
});
Sway.OrderTypes. | Description |
CLOSEPOSITION | Closes all open trades and pending orders on the market |
CLOSEPOSTRADES | Closes open trades on the market, but not pending orders |
CLOSEPOSPENDING | Closes pending orders, but not open trades |
CLOSEPOSWINNERS | Closes profitable open trades (netProfit > 0) |
CLOSEPOSLOSERS | Closes loss-making open trades (netProfit < 0) |
CLOSEPOSLONG | Closes all long trades and pending orders |
CLOSEPOSSHORT | Closes all short trades and pending orders |
HEDGEPOSITION | If supported by the account, hedges the current position by placing an open trade in the opposite direction for the current open volume. For example, you are currently short 20,000 EUR/USD this will open a buy trade for 20,000. Not allowed if the account is netting/non-hedging. |
DOUBLEPOSITION | Doubles the current position. For example, if you are currently (net) short 20,000 EUR/USD this will open a new sell trade for a further 20,000 |
REVERSEPOSITION | Reverses the current position by placing a new trade for the opposite volume, without closing the current open trade(s). For example, if you are currently short 20,000 EUR/USD this will open a buy trade for 40,000. Not allowed if the account is netting/non-hedging. |
CLOSEANDREVERSE | Like REVERSEPOSITION, but closes the current open trade(s) rather than just opening a new trade in the opposite direction, and is therefore permitted on netting/non-hedging accounts. For example, if you are currently short 20,000 EUR/USD this will close the existing open trade(s) and open a new buy trade for 20,000. |
3.3.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:
Sway.OrderTypes. | Description |
FLATTEN | Closes all open trades and pending orders on all markets |
FLATTENTRADES | Closes all open trades on all markets, but not pending orders |
FLATTENPENDING | Deletes all pending orders on all markets, but not open trades |
FLATTENWINNERS | Closes all profitable open trades on all markets (netProfit > 0) |
FLATTENLOSERS | Closes all loss-making open trades on all markets (netProfit < 0) |
FLATTENLONG | Closes all long trades and pending orders on all markets |
FLATTENSHORT | Closes all short trades and pending orders on all markets |
3.3.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:
Property | Description |
confirm | The user can control, in Sway Charts Pro's settings, whether each widget has its trading activity confirmed or not. If the user has turned confirmation off for your widget you can turn it on setting confirm:true. But you can't do the opposite. You cannot turn off confirmation if the user has turned it on. |
showResultDialog | Controls whether the framework displays a message about success/failure of the request. By default, the framework will display an error message for failed requests but nothing for successful requests. If you want to hide the framework's own message and display your own confirmation, then you can set showResultDialog:false (or showResultDialog:""). Other possible values for showResultDialog are "all", meaning "show a message for successes as well as failures", and "noaction" meaning "don't show a message for a request which was successfully carried out, but do show a message for a request which didn't require any action". |
fifo | Boolean which can be used to force non-hedging behaviour on hedging accounts (irrelevant and ignored on netting accounts). For example, if fifo:true and the user is long EUR/USD, then placing a sell market order for EUR/USD will close (or partially close) the existing long ticket rather than opening a new one. If omitted, this defaults to the user-controllable setting in the Sway Charts Pro platform. |
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:
Property | Description |
result | An Sway.Result object. If validation succeeded then isOkay will be true. If validation failed, then isError will be true, and the code will be one of the framework's error codes. |
tradingJobs[] | If successful, an array of the individual trading jobs after validation |
For example, after validation of the above request, tradingJobs[] will have a single entry such as the following:
{
instrumentId: "EUR/USD",
tradingAction: 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:
Function | Description |
keepAfterParent | Defaults to false, causing a dialog to be destroyed by the framework automatically if its creator terminates. Can be changed to true, giving the dialog independent life. |
noCancel | Defaults to false. If set to true, the user cannot dismiss a dialog by clicking outside it. They must respond to one of the dialog's buttons. |
noResize | Prevents a dialog from being resizable by the user |
windowWidth | Overrides the standard width of the dialog. The value can be specified in pixels, e.g. "450px", or as a % of the browser window size, e.g. "75%". (Ignored on small screens such as mobile phones, where dialogs are always made full-screen.) |
windowHeight | Overrides the standard height of the dialog. Same options as windowWidth. |
Note: you must set both windowWidth and windowHeight, or neither. If you only set one of the properties, it is ignored.
In some cases, you don't just get a final result from a dialog. You also get an initial callback which gives you the ID of the dialog. This makes it possible for you to destroy the dialog, using Framework.DestroyDialog(), before the user responds. There is an example of this below in relation to a simple Ask() message.
3.5.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:
Function | Description |
Ask() | Asks the user a question |
ErrorMessage() | Displays a message styled as an error |
SuccessMessage() | Displays a message styled as success |
AlertMessage() | Displays a message styled as an alert/notification |
InfoDialog() | Displays a message styled as information |
Each of these functions takes two parameters: a block of settings, and a callback function which receives the dialog result. For example:
Framework.Ask("Are you sure?", function(Msg) {
…
});
The settings can either be a simple string, as in the above example, or an object with properties. For example:
Framework.Ask({
title: "Really?",
text: "Are you sure you want to do this?",
buttons: [
{text: "Yes, definitely", value: "Yup", className: "btn-red"},
{text: "On second thoughts…", value: "No"}
]
}, function (Msg) {
…
});
3.5.2.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:
Button property | Description |
text | Text to display in the button |
value | Value to pass back to your asynchronous handler if the button is clicked |
className | Optional CSS class for the button. Common options are btn-red, btn-blue, btn-green, btn-pink, btn-amber, btn-standard |
3.5.2.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:
Property | Description |
title | Title for the dialog |
heading | Optional heading to display above the text field |
needValue | If true, the Save button is disabled unless the user enters a value. Note: if you set needValue but not the standard noCancel, then the user cannot click the Save button without entering a value, but can cancel the dialog. |
3.5.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.
Property | Description |
text | Text to display for the item |
items:[] | Optional array of sub-items, as in the example above. The menu can have any level of nesting: sub-menus which have sub-menus which have sub-menus etc. |
disabled | If true, the item cannot be selected, and is visually styled as disabled |
unselectable | If true, the item cannot be selected, but without being visually styled as disabled |
separator | If true, the framework displays a separator bar between this item and the one before it. |
separatorAfter | If true, the framework displays a separator bar between this item and the one after it. |
icon | Icon to display next to the item. This can be the hexadecimal code of any Font Awesome icon, prefaced with x. For example, xf34e to display an alarm-clock icon. |
checkbox | If not null, the item is given either a ticked (checkbox:false) or unticked (checkbox:true) checkbox icon. If both checkbox and icon are set, then icon takes precedence. |
checked | If not null, the item is given either a unfilled (checked:false) or filled (checked:true) radio-button icon. If both checked and icon are set, then icon takes precedence. |
color | Overrides the text colour using a normal CSS colour value such as "red" or "#FF0000" |
iconColor | Overrides the icon colour using a normal CSS colour value such as "red" or "#FF0000" |
backgroundColor | Overrides the background colour for the item's row using a normal CSS colour value such as "red" or "#FF0000" |
Each entry in the array can also have any number of other properties for your own reference, such as id, value, action etc. When an item is selected, the full data for the item, including any extra properties such as these, is passed into your asynchronous callback function (as Msg.result.itemData).
Your callback function is called twice: once with initialisation data which gives you the ID of the dialog, and a second time with the result of the dialog: either the user's selection, or cancellation.
The final result callback has a result member, an 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:
Property | Description |
id | Optional, but helps to identify the field when you receive the results back from the dialog |
caption | Label to display next to the field |
type | Type of field: one of text, textarea, yesno, list, int, float, color, time, email, instrumentId, timeframe |
value | Initial value to pre-populate into the field: · For a list, this should be the k value of one of the options · For a chart timeframe, this should be the usual ID; a number of seconds. · For a color, this should be a CSS color value such as "darkolivegreen" or "#FF0000" · For a time, this should be a string in the form hh:ss such as "14:18" |
options | Only used for a list field. As in the above example, an array of objects which have k and v properties (key, and text to display to the user) |
The configuration which you pass into AskSettings() can be any of the following:
· An array of fields, as in the example above
· An object with a fields[] array
· An object with a sections[] array
· An object with a pages[] array
If you pass an object rather than a bare array, then you can set a title plus standard dialog options such as noCancel.
Passing an object with a sections[] array lets you break up a large set of fields into multiple sections, to make it easier for the user to understand. For example:
Framework.AskSettings({
sections: [
{
caption: "Section 1",
fields: [
{type: "int", caption: "A number field", value: 17},
]
}, {
caption: "Section 2",
fields: [
{type: "int", caption: "Another number field", value: 46},
]
}
]
}, function (MsgResult) {
…
});
Your settings can even be split up into multiple pages; a pages[] array. Each page must have a caption and a sections[] array.
What you get back in your asynchronous callback function depends on the input you provide. The Msg which is passed to the callback is always an 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:
Property | Description |
url | If you supply a url, that remote address is loaded into the dialog (instead of an HTML string). Allows you to host the content for a dialog on an external server rather than building it inside your widget/script/UDIX. Like all other resources, such a page must be hosted at an https:// address. |
noTitle | If noTitle:true is set then the dialog is not given a title bar. Your HTML/url content takes up the whole dialog window. |
noStandardCss | By default the framework will insert its own CSS into your HTML (not applicable if you use a url), in order to make the styling consistent with the rest of Sway Charts Pro. You can prevent this using noStandardCss:true. |
buttons | You can optionally give the dialog standard buttons, in a footer, in exactly the same way as for dialogs such as Ask(). These buttons will close your dialog in the usual way, sending back a Msg.result containing the value of the clicked button. |
3.5.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:
Property | Description |
instrumentId | Initial instrument to set in the dialog |
orderDefinition | A complete order definition to pre-populate into the dialog. The possible properties of orderDefinition are simply the same as passing a request to SendOrder(). If both are present, orderDefinition takes precedence over instrumentId. You can pass a wider range of orderDefinition values into the trading-action than into the dealticket. The dealticket can only pre-populate with an instruction to open a new trade or order, whereas the trading-action can pre-populate with anything which can be passed to SendOrder(). |
handleExecution | Defaults to true. If set to false, the dialog returns an orderDefinition to you, in its result, rather than executing the trading action itself. |
message | A brief message to display at the top of the dialog |
For example:
// Display a suggested trade, with an accompanying message, and get the
// user's selection back from the dealticket rather than having the order
// executed by the dealticket
Framework.CreateDialog({
type: "dealticket",
settings: {
orderDefinition: {
tradingAction: 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:
Property | Description |
allowInstrumentChange | Defaults to true. If false, prevents the user from changing the initial market (which you set using instrumentId or orderDefinition) |
noPosition | If true, the dealticket does not display the current-position indicator and the secondary panel for carrying out actions on the whole position. Turning off allowInstrumentChange automatically turns on noPosition, and means that the deal ticket can only be used for placing new orders, not for closing existing orders on the selected market. |
fullMode | By default the dealticket selects between its "simple" and "full" modes depending on whether the pre-populated or default order definition can be represented in simple mode. You can force the deal-ticket to open in full mode by setting fullMode:true |
noToolsIcon | Can be used to turn off the settings icon in the dealticket, preventing a switch between simple and full mode, and preventing access to saved order templates |
3.5.10.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:
Property | Description |
showNone | Adds a "None" button to the selection dialog. Allows you to distinguish between the user cancelling the dialog versus saying "I don't want to use a stored template". If the button is clicked then the return value from ChooseOrderTemplate() is the string "none" (rather than a template object). |
showDefault | Similar to showNone, but with different styling. Displays a "Default" button rather than a "None" button, and returns the string "default". |
There is no functional difference between showNone and showDefault. Choosing to use the term "Default" is slightly clearer to the user that, if nothing is explicitly pre-populated into the deal ticket, then the deal ticket will automatically select any user-defined default template. For example:
Framework.ChooseOrderTemplate({showDefault: true}, function(template) {
if (template == "default") {
// User clicked the Default button.
// Display the deal ticket. A blank or omitted orderDefinition will mean
// that the deal ticket automatically selects any user-defined default
Framework.CreateDialog({
type: "dealticket"
});
} else if (template) {
// User selected a template
// Pre-populate the selected template into the deal ticket
Framework.CreateDialog({
type: "dealticket",
settings: {
orderDefinition: template
}
});
} else {
// (User cancelled the dialog)
}
});
3.5.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:
Property | Description |
text | Body text of the notification |
title | Title for the notification. (Not compulsory.) |
icon | Hexadecimal code of an icon in the FontAwesome set, such as "f05a" for an info icon. (Not compulsory.) |
Framework.CreateToast() can have a callback function. This is called if the user clicks on the toast notification and/or when the toast is destroyed. For example:
Framework.CreateToast({
title: "Finished!",
text: "The task has been completed",
icon: "f05a"
}, function (ToastMsg) {
if (ToastMsg.clicked) {
// User has clicked on the toast
} else {
// The toast is being destroyed
}
});
The ToastMsg which is passed to the callback function has the following properties:
Property | Description |
clicked | Indicates whether the message is the result of a click. If not, then it means that the toast is being destroyed and no further messages will be sent. |
toastId | ID assigned to the toast |
toast | Definition of the toast, as originally passed in to CreateToast() |
By default, a toast notification is destroyed if the user clicks on it. Therefore, unless you modify the standard behaviour using the properties described below, if the user clicks on the toast then there will be two messages: a click message with clicked:true and then a destruction message with clicked:false.
Other properties which you can use to modify the toast are as follows:
Property | Description |
lifespan | Lifespan of the toast in seconds, overriding the app's default |
toastId | Optional ID to assign to the toast, so that the toast has a known ID which can be manually destroyed. Callers are recommended to use Sway.utils.GenerateGuid(). |
noDestroyOnClick | Prevents the toast being automatically destroyed when the user clicks on it. The caller can destroy it manually (rather than waiting for its natural expiry) by implementing a click callback and using that to destroy the toast. |
toastCreationGroup | Ignored if blank/null. Suppresses creation of this new toast if there are any existing items with the same toastCreationGroup value. |
toastReplacementGroup | Ignored if blank/null. The opposite of toastCreationGroup. If there are any existing items with the same toastReplacementGroup value, they are destroyed (and replaced by this new toast). |
toastClickGroup | Ignored if blank/null. Automatically destroys all the items with the same toastClickGroup value when any one of them is clicked on. |
toastClass | Optional display class for the toast, changing its formatting. Currently available values are ToastSuccess and ToastError. |
dialog | Optional description of a dialog to display in response to a click on the toast. See below. |
3.5.12.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:
Property | Description |
offset | Base offset in minutes from UTC, e.g. 120 (UTC+2) or -300 (UTC-5) |
dstMode | Daylight savings schedule. One of the values of the Sway.DSTModes enumeration: NONE, USA, EUROPE, AUSTRALIA |
For example, the following defines a base offset 2 hours ahead of UTC, changing into daylight savings (UTC+3) on the USA schedule:
timezone: {
offset: 120,
dstMode: 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"
Option property | Description |
digits | Number of decimal places to include (with rounding). Defaults to 2 if neither digits nor max_digits is specified |
max_digits | Allows a variable number of decimal places, as required, up to a maximum of max_digits. For example, if max_digits=2 then the results of the formatting will be as follows: 123 = "123" 123.1 = "123.1" 123.456 = "123.46" |
roundUp001 | Designed for use with percentages where, even if the rounding is mathematically accurate, you don't want to display a value as 0.0 or 100.0 unless it is exactly 0 or 100. If roundUp001 true and digits>0, value which would be rounded down to 0 or up to 100 will instead be displayed as the minimum "increment" such as 0.01 or 99.99 |
thousands | Display numbers in millions or thousands as xM or xK, with the fractional amount of thousands/millions controlled by digits or max_digits. For example, with {thousands: true, max_digits: 1}: 1456 = "1.5K" 1000000 = "1M" |
show_plus | Add a + as a prefix to positive numbers, typically used in relation to profit/loss amounts, or percentages which can be either positive or negative |
zero | If the number is exactly zero, and options.zero is not null, then the function returns the value of options.zero – for example, an empty string, or a placeholder such as - |
thousands_separator | Automatically set based on user’s choice of language/region |
decimal_separator | Automatically set based on user’s choice of language/region |
prefix | Any prefix (such as a currency symbol) to add to the start of the text |
suffix | Any suffix to add to the end of the text |
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:
Property | Description |
granularity | Level of aggregation applied by the back-end system. If granularity:null, then the back-end system should be providing the full list of orders (including multiple orders at the same price). Otherwise, the back-end system is combining orders into aggregated amounts at different price points. |
3.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:
Parameter | Description |
volume | Volume to format – always, as standard in the framework, an amount in cash/units, not lots |
instrument | Sway.Instrument object to use for the price formatting, or the instrumentId of an instrument. Only used in "lots" mode, and optional if you use the options to force "cash" mode. |
options | See below |
FormatVolume() is again a wrapper around FormatNumber(). You can use the full features of FormatNumber(), but typically you will only want to use the following properties in the options:
Option property | Description |
mode | Defaults to the user’s preference in the Sway Charts Pro settings. Can be overridden to either "lots" or "volume". |
append_lots | Ignored in "volume" mode. In "lots" mode, returns the text as "X lots" rather than just "X". |
show_plus | Same as for FormatNumber(): adds a + at the beginning of positive numbers. Typically used for values such as position net-volume where the value can be either positive or negative |
3.10.5 Framework.FormatDate()
FormatDate() formats a date, by default in the format yyyy/mm/dd hh:mm:ss
Parameter | Description |
datetime | Date/time to format. Although the framework's standard representation of date/times is milliseconds, you are allowed to pass a Javascript Date object into this function. |
options | Options for formatting the date |
The possible options for FormatDate() are as follows:
Option | Description |
format | String describing the format for the date. If omitted, or none of the items below, then the format is yyyy/mm/dd hh:mm:ss. · "dateonly": formats as yyyy/mm/dd, i.e. no time component · "yyyymm": formats as yyyy/mm, i.e. no day or time · "timeonly": formats as hh:mm:ss, i.e. no date component · "timehhmm": formats as hh:mm, i.e. no date component and no seconds · "datehhmm": formats as yyyy/mm/dd hh:mm, i.e. a full date-time, but without the seconds · "textdate": language specific, but in English formats as dddd d mmmm yyyy, e.g. Saturday 12 December 2020 (no time component) |
localtime | By default, FormatDate() uses the UTC properties of the date: getUTCHours(), getUTCMonth() etc. Setting localtime:true tells it instead to use getHours(), getMonth() etc – which therefore converts the date/time from UTC to the user's local time zone |
date_separator | Can be used to override the separator between the parts of the date. Defaults to / |
time_separator | Can be used to override the separator between the parts of the time. Defaults to : |
3.10.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.
Parameter | Description |
target | Either a DOM node into which to put the price, or the ID of a node. If target is a string, then the framework does $$(target). |
instrument | The market to use for formatting the price. Can be specified either as an Sway.Instrument object or the instrumentId of a market |
price | The price to format |
options | Optional. Number-formatting options which are ultimately passed to FormatNumber(). For example, to suppress thousands-separators, set thousands_separator to a blank string |
For example:
<!-- Note: the element which is the target of DisplayPrice() must be empty on the first call -->
<span id="bid"></span>
…
var market = Framework.Instruments.get("EUR/USD");
Framework.DisplayPrice("bid", market, market.bid);
3.10.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():
Property | Description |
fromWidgetType | Type of widget making the request. For example, you might choose to allow all requests from some known widget types such as the dealticket, because by definition the user has seen and manually actioned the request. |
tradingJobs[] | List of trading actions which are being requested. |
There are two things to note about the array of tradingJobs[]:
· You cannot approve or reject individual requests within the array. You can only approve the whole batch or reject the whole batch.
· What you receive in tradingJobs[] is a list of trading actions dis-aggregated into the low-level requests such as opening or closing individual orders. For example, if the user clicks Close > All in the built-in 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:
Parameter | Description |
instrumentId | instrumentId of the market for which you want depth. Obviously compulsory. |
requestId | An optional ID for your request, used in termination (see below). If you do not provide one, the framework will automatically allocate an ID, and report it in all responses. If you allocate your own ID, you are recommended to use Sway.utils.GenerateGuid(). |
callback | An optional asynchronous callback function which receives the depth. You can process market-depth messages in OnMessage(), or optionally choose to receive them in a specific asynchronous callback as well. |
For example:
// Allow the framework to allocate an ID for the request, reported as the requestId in messages
Framework.RequestMarketDepth("EUR/USD", null, function (Msg) {
// Response containing market depth, also received in/via OnMessage()
// ID of the request, for use in termination, will be in Msg.requestId
});
You should terminate market depth requests when you no longer need them, using Framework.TerminateMarketDepth(). The function takes two parameters: the instrument ID, and the request ID allocated either by you or the framework.
Each market-depth update message has the following properties:
Property | Description |
instrumentId | instrumentId of the market |
requestId | The ID of the market-depth request, either provided by you in your call to RequestMarketDepth() or allocated automatically by the framework. Can be used to terminate requests. |
initial | Boolean value indicating whether this the first message, or a subsequent update to the depth |
changes | An array of changes to the market depth. For the first message, initial:true, this will list all the entries in the market depth. For subsequent messages, initial:false, it will list changes: the addition of new quotes, removal of quotes, modification of existing quotes. |
depth | Object describing the full market depth, incorporating the latest changes. In other words, the framework gives you both the individual changes and also the resulting full book. You don't need to calculate the book yourself from the changes. |
The changes object contains two arrays of changed quotes: asks[] and bids[]. Each item in these arrays can have the following properties:
asks[]/bids[] change | Description |
id | An ID for the quote which is being added/removed/modified |
state | The type of change to the quote: one of "new", "deleted", "changed". In the first message, initial:true, all the state values will be "new". After that, new quotes can be added, and existing quotes can be removed or modified. If a quote is being changed, then either or both of the price and volume may be different. |
price | The price of the quote (e.g. 1.23456) |
volume | The volume of the quote (e.g. 10000000) |
The depth object contains the current full book following the changes; you don't need to calculate this yourself.
As noted above, the back-end system may have multiple quotes at each price (from different liquidity providers). The depth summary calculates both individual current quotes and also the aggregated volume at each price (which will be the same thing if the back-end system only issues aggregated quotes).
The depth object contains the following properties:
depth properties | Description |
aggregated | Object containing asks[] and bids[] arrays containing total volume at each price. These are always sorted, by proximity to the best price – bids[] in descending order, asks[] in ascending order. Therefore, asks[0] and bids[0] are the best bid and offer. Each entry in the asks[] and bids[] arrays has a price and a volume. It also has a cumulative value: the total volume of this and all previous/better quotes in the array. |
book | Object containing Dictionaries (not arrays) of the individual current bid and ask quotes. Each quote consists of an id, price, and volume. The key of each quote in its dictionary is the id. |
bestAsk | Best ask price (same as aggregated.asks[0].price), or zero – see below |
bestBid | Bid bed price (same as aggregated.bids[0].price) , or zero – see below |
askVolume | Total volume of all ask quotes |
bidVolume | Total volume of all bid quotes |
A couple of final notes:
· You need to be a little careful with this data. During unusual market conditions, or just very briefly during updates, it is possible for there to be no quotes on the bid side or ask side (or even both). Therefore, you cannot safely assume that aggregated.asks.length or aggregated.bids.length is greater than zero.
· Internally within the broker's back-end system, it is possible for there to be different routes for market depth versus "trading" prices. It is not absolutely guaranteed that the bestBid and bestAsk in the market depth will match the current bid and ask of the 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.
Property | Description |
id | Unique ID of the calendar event |
title | Description of the event (e.g. NFP) |
timestamp | Time of the event (in UTC milliseconds) |
allday | Boolean indicating whether the event is an all-day event such as a holiday |
impact | Expected market impact of the event: 0 = low, 1 = medium, 2 = high |
currencies | Array of ISO currency codes related to the event. For example, currencies: ["USD", "JPY"]. Some calendar sources may use special identifiers such as "*" or "ALL" to indicate events which apply globally. |
previous forecast actual | Where applicable (e.g. employment figures), the previous and forecast values for the event, plus the actual value as an update after the event. The format of these values is not standardised and depends on the calendar source; they may be either textual or numeric. Some calendar sources may not provide post-event updates with the actual values, and some sources may not have this data at all. |
3.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:
Property | Description |
subject | Subject of the message (max 200 characters) |
body | Optional body content for the message. Limited to 4KB. |
to | The ID of the device to send to. Only required when sending to another device. Can be omitted when sending mail to the current device's mailbox. |
local | If local:true, identifies a local message as described above. If a local message is specified, any recipient to value is ignored. |
priority | Not available on messages to other devices. Only available on messages to the current device, either locally or remotely. See below. Defaults to 0 if omitted. |
noPush | By default, if the mail is not local and the receiving device is a mobile/tablet then the message will generate an iOS/Android push notification. You can turn this off by setting noPush:true (or any value for noPush, actually including noPush:false. It is the presence of a noPush key in the JSON which disables the push, not the key's value.) |
The priority level – only available on messages to the current device – can be one of the following values between 0 and 3:
priority | Description |
0 | Normal priority |
1 | Message is marked in red in the mailbox |
2 | A new-mail icon is displayed in the platform's banner (until the message has been read) |
3 | The mail is forced to open immediately in the platform, without user interaction/request |
The optional body of a message can be three things (subject to the 4KB limit):
· Plain text
· HTML – a full HTML document with <html> and </html> opening and closing nodes
· The URL of a web page, subject to the usual browser security restrictions
An HTML document or web page is displayed in a sandboxed iframe, and has no framework access.
3.14.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:
Property | Description |
fileName | Name of file to generate |
mimeType | MIME type such as text/csv |
data | File data (as a string) |
For example:
CreateDownload({
fileName: "mydata.csv",
mimeType: "text/csv",
data: "Header1,Header2,Header3\r\nData1,Data2,Data3\r\netc"
});
Last updated