Of the different ways to generate MultiCharts alerts, most are based on price action. But at least as helpful are using alerts for risk management, like firing an alert when there’s a possible data feed or internet connectivity issue. How to do that?

In this article:

Generating MultiCharts alerts when there’s no real-time data

We programmatically generate MultiCharts alerts with the Alert() keyword (PowerLanguage Keyword Reference, 2016), but for that we’ll need to enable the script’s alert setting. The alerts then appear as a notification window, audio alert, and/or email alert (MultiCharts Wiki, 2013), depending on how we’ve configured the alert settings.

While we usually generate alerts based on price action (like triggering alerts with a moving average crossover), another way to use alerts is to fire them when there’s a lack of price data. That way we’re notified when there’s a possible problem with the data feed or the computer’s internet connection.

Unfortunately, it’s currently not possible to programmatically determine whether there are issues with a data feed connection (see Henry MultiCharts, 2012). Luckily, with a workaround we can measure potential data feed issues indirectly. For that we track when the last real-time price update arrived. Now if that last price update happened a relatively long time ago (like 30 seconds), then we can be reasonably sure something is wrong with the data feed or internet connection.

This workaround is not a full solution because we’d still get false positives when the instrument trades with few price updates (like with after hours trading). When that happens to give a lot of unnecessary alerts, we can always only generate alerts during a certain time period to prevent alerts from being generated in the extended trading session.

Figuring out when the last data feed update happened

Successfully implementing the workaround has a couple of requirements. First, we need to access when the last price update happened. We get that value with the Q_Time_DT keyword, which returns the DateTime value from the chart’s Status Line (PowerLanguage Keyword Reference, 2016). That time, in turn, is when the last trade, bid, or ask update from the chart’s data feed was received.

A a nice feature of DateTime values is that we can perform calculations with them. That’s possible because the integer portion of a DateTime value indicates how many days have elapsed since January 1, 1900, whereas the decimal figure represent the portion of the day elapsed since midnight (PowerLanguage Keyword Reference, 2016).

Of course, we’ll need something to compare that data feed time with. We get the computer’s current time in DateTime format with the ComputerDateTime keyword (PowerLanguage Keyword Reference, 2016). But in order to correctly compare that keyword’s value with Q_Time_DT, both need to be in the same time zone.

To set the chart to the computer’s time zone, we right-click on one of the instrument’s price bars on the chart and select ‘Format [instrument name]…’. This brings up the ‘Format Instrument’ window, and there we go to the ‘Settings’ tab to set the ‘Time Zone’ option to ‘Local’:

Changing the time zone setting of an instrument in MultiCharts

Now that we have the time of when the feed last updated and the computer time, we can compare those two values each time the script calculates. However, MultiCharts scripts typically only calculate when a new tick is received (MultiCharts Wiki, 2016). But since we’re planning for a data feed issue, we cannot rely on incoming ticks to keep the script updated.

We address that by performing a periodic, time-based script calculation with RecalcLastBarAfter(). Inside that keyword’s parentheses we specify the number of seconds after which the keyword will trigger another script calculation on the last bar (MultiCharts Wiki, 2014a). Those periodic calculations affect the script in the same way as a new tick, and that keeps our script updated in the absence of real-time data.

With RecalcLastBarAfter() our script remains updated to generate timely alert messages. But when those time-based calculations trigger an alert repeatedly, the script becomes more an annoyance than a help. And so in the example below we’ll also look at keeping a certain number of seconds between consecutive alerts. That way we don’t get a flood of alert messages when there’s a possible data feed issue.

Optionally, the last thing we can address is generating an error when the script’s alerts are disabled. While not a requirement for correctly generating data feed alerts, the script’s alerts do serve an important purpose. With the RaiseRunTimeError() keyword we can generate an error message whenever we forget to enable the script’s alert setting (PowerLanguage Keyword Reference, 2016). Note that this keyword does turn off the script, which can be a bit annoying at times.

Example: generating alerts for possible data feed issues

In the example indicator below we generate an alert message whenever the chart’s data feed hasn’t been updated in 30 seconds, a time interval that we configure with the ‘DataFeed_Timeout’ input. The second input, ‘Time_Between_Alerts’, specifies the number of seconds between consecutive alerts. We use that alert to prevent a lot of alerts happening quickly after each other.

To track the data feed, each alert message shows how many seconds the feed has been inactive. The indicator also generates an error message with RaiseRunTimeError() whenever the ‘Enable Alerts’ option is disabled. If you dislike those error messages (they do turn off the script after all), you can safely remove that keyword and its associated if statement.

Note: The example code below can be used with indicators, functions, and signals (trading strategies). When using the code in a signal, do enable the IntrabarOrderGeneration attribute. That makes the signal also update by performing intra-bar calculations; otherwise, the script only calculates when the bar closes (MultiCharts Wiki, 2012).

The image below gives an example of the script’s alerts. After discussing the code we’ll take a closer look at the indicator’s behaviour and manual settings.

Data feed alerts generated by the MultiCharts example indicator

Inputs:
    DataFeed_Timeout(30),
    Time_Between_Alerts(30);

Variables:
    secondsSinceUpdate(0),
    secondsSinceAlert(0),
    IntrabarPersist timeLastAlert(0);

// Generate error when alerts are turned off
if (AlertEnabled = false) then
    RaiseRunTimeError("Note: the 'Enable Alerts' option of this script is disabled.");
    
// Calculate with a time-based recalculation and real-time data
if (GetAppInfo(aiCalcReason) = CalcReason_Timer) and 
    (GetAppInfo(aiRealTimeCalc) = 1) then begin

    // Determine how long the feed is inactive
    secondsSinceUpdate = (ComputerDateTime - Q_Time_DT) * 86400;
    
    // Calculate how long ago the previous alert was
    secondsSinceAlert = (ComputerDateTime - timeLastAlert) /
        ELTimeToDateTime_s(1);
    
    Plot1("Feed inactive: " + NumToStr(secondsSinceUpdate, 0) + "sec");
    
    // Generate the feed time out alert
    if (secondsSinceUpdate > DataFeed_Timeout) and
        (secondsSinceAlert > Time_Between_Alerts) then begin
    
        Alert("The feed hasn't updated in " + 
            NumToStr(secondsSinceUpdate, 0) + " seconds (" + 
            ExchListed + ":" + SymbolName + ").");
            
        timeLastAlert = ComputerDateTime;
    
    end;

end;

RecalcLastBarAfter(1);

We first create two input options: DataFeed_Timeout and Time_Between_Alerts. We use them to easily configure the script’s settings by hand, without having to edit and recompile the script’s code each time we want to change these values.

Then we create three variables:


Variables:
    secondsSinceUpdate(0),
    secondsSinceAlert(0),
    IntrabarPersist timeLastAlert(0);

These numerical variables have a default value of 0. We use the first two, secondsSinceUpdate and secondsSinceAlert, later on when calculating how many seconds ago the data feed updated and the last alert fired.

The timeLastAlert variable tracks when the last alert generated. By knowing that time, we can prevent alerts from firing too soon after each other. Since that variable needs to keep its value from one intra-bar script calculation to the next, we mark it as IntrabarPersist (PowerLanguage Keyword Reference, 2016). Without that keyword, the variable only remembers the values stored in it when the bar closes. With that keyword, the variable also remembers the values we assigned to it during an intra-bar script calculation.

Next we generate an error when the script’s alerts are disabled:


if (AlertEnabled = false) then
    RaiseRunTimeError("Note: the 'Enable Alerts' option of this script is disabled.");

Here we first check if the script’s alerts are enabled with AlertEnabled. That keyword returns true when the script’s ‘Enable Alerts’ option is enabled and false when that setting is turned off (PowerLanguage Keyword Reference, 2016).

When that keyword equals (=) false, we create an error message with RaiseRunTimeError() that also turns off the script (PowerLanguage Keyword Reference, 2016). Between the keyword’s parentheses we specify why the error happened, and that makes it look like:

Run-time error generated when the MultiCharts alerts are disabled

After that there’s an if statement that contains a large part of the indicator’s code:


if (GetAppInfo(aiCalcReason) = CalcReason_Timer) and 
    (GetAppInfo(aiRealTimeCalc) = 1) then begin

    // ...

end;

RecalcLastBarAfter(1);

The condition of this if statement evaluates two true/false expressions combined with the and logical keyword. That keyword returns true when the value on its left and the value on its right are both true. When the left, right, or both values are false, then the result combined by and is false too (PowerLanguage Keyword Reference, 2016).

The first expression uses the GetAppInfo() keyword with the aiCalcReason parameter, which makes the keyword return the reason of the current script calculation (PowerLanguage Keyword Reference, 2016). To see if the script currently calculates due to a periodic time-based calculation (which are triggered by the RecalcLastBarAfter() keyword), we evaluate whether GetAppInfo() returns a value that’s equal to (=) CalcReason_Timer. When that’s the case, the script currently calculates due to a time-based calculation.

With this requirement of a time-based calculation, the code inside the if statement doesn’t execute for other calculation reasons (like a price update). This makes the script a tiny bit more efficient (since the if statement’s code doesn’t run every time). It also makes the code inside the if statement run more consistent (based on when the time-based calculations happen).

The second expression evaluated in the if statement’s condition also uses GetAppInfo(), but now with the aiRealTimeCalc parameter. With that, the GetAppInfo() keyword returns 1 when the script calculates with real-time data (PowerLanguage Keyword Reference, 2016). With this we ensure that the script doesn’t generate unnecessary data feed alerts when the instrument has closed.

Note that, just below the if statement, RecalcLastBarAfter() periodically recalculates the script (PowerLanguage Keyword Reference, 2016). By placing that keyword outside the if statement, it executes regardless of whether there’s real-time data or what triggered the current script calculation. By always executing RecalcLastBarAfter() with a value of 1 between its parentheses, the keyword keeps the script up to date with a time-based recalculation every second.

When both expressions are true, the code inside the if statement executes. That code first calculates the number of seconds since the last data feed update and the most recent alert:


secondsSinceUpdate = (ComputerDateTime - Q_Time_DT) * 86400;

// Calculate how long ago the previous alert was
secondsSinceAlert = (ComputerDateTime - timeLastAlert) /
    ELTimeToDateTime_s(1);

To set the secondsSinceUpdate variable to how long the data feed is inactive, we subtract the time of the last trade, bid, or ask update (Q_Time_DT) from the computer’s current time (ComputerDateTime). Since both Q_Time_DT and ComputerDateTime are DateTime values, subtracting them results in a DateTime value. To convert that to a seconds amount, we need to multiply it with 86,400 (the number of seconds there are in a day).

Then we set the secondsSinceAlert variable to how many seconds passed since the last alert. For that we subtract the last alert’s time (timeLastAlert) from the current computer time (ComputerDateTime). With those values being DateTime values too, we again get a DateTime difference. Here we translate that value to the number of seconds with the ELTimeToDateTime_s() keyword with the parameter value of 1 (PowerLanguage Keyword Reference, 2016).

To track the data feed’s quickness, we then plot how long the feed has been inactive:


Plot1("Feed inactive: " + NumToStr(secondsSinceUpdate, 0) + "sec");

While the keywords for plotting (such as Plot1()) typically show numerical values on the chart, they can display string (text) values too (PowerLanguage Keyword Reference, 2016). As we’ll see in the images below, those plotted string values appear in the chart’s Status Line (the area in the top of the chart, with information such as the symbol name and resolution).

The string displayed by Plot1() combines static text (like "Feed inactive: ") and a numerical value with the + operator. That number is how long the data feed has been inactive (secondsSinceUpdate), and by plotting that we see on the chart how active the data feed currently is (and can then use that information to possibly adjust the value of the DataFeed_Timeout input option).

We convert that amount of seconds (secondsSinceUpdate) to a text with NumToStr(), a keyword that requires two parameters: the number to convert to text and how many decimals that number should get (PowerLanguage Keyword Reference, 2016). Here we have it show secondsSinceUpdate with 0 decimals.

Next we generate alerts to notify of possible data feed issues:


if (secondsSinceUpdate > DataFeed_Timeout) and
    (secondsSinceAlert > Time_Between_Alerts) then begin

    Alert("The feed hasn't updated in " + 
        NumToStr(secondsSinceUpdate, 0) + " seconds (" + 
        ExchListed + ":" + SymbolName + ").");
        
    timeLastAlert = ComputerDateTime;

end;

There are two expressions in this if statement’s condition. Since we use and to combine them, both have to be true before the code inside the if statement executes.

The first true/false expression checks if the number of seconds since the last data feed update (secondsSinceUpdate) is greater than (>) the DataFeed_Timeout input option (which has a default value of 30). To prevent alerts quickly after each other, the second expression requires that the number of seconds since the last alert (secondsSinceAlert) is greater than (>) the Time_Between_Alerts input (that also has a standard value of 30).

And so when the data feed hasn’t been updated in 30 seconds and an alert hasn’t fired in the same time period, then the code inside the if statement executes. That code begins with Alert() to generate an alert programmatically (PowerLanguage Keyword Reference, 2016). Inside that keyword’s parentheses, we construct an alert message with several components.

First is a static text ("The feed hasn't updated in "). Then we use NumToStr() and secondsSinceUpdate to display how long ago the data feed was updated. For a more helpful alert message, we also include the instrument’s exchange and symbol name. We get those with the ExchListed and SymbolName keywords (PowerLanguage Keyword Reference, 2016).

Then we update the timeLastAlert variable to the computer’s date and time (ComputerDateTime). This way we can, the next time the script calculates, determine how long ago the previous alert happened. That allows us to keep a certain number of seconds between alerts.

Example: triggering MultiCharts alerts for a sluggish data feed

Now let’s see how the above example indicator performs. First, let add the script to the chart without enabling alerts. It then generates an error message like:

Example of the error message when the alert are disabled

To prevent that error, we need to enable the script’s alerts. And to have the indicator generate a data feed alert at any time, we need to set the ‘Alert Conditions Check’ option to ‘Every Tick’:

Configuring the alert settings of the MultiCharts indicator

In the same ‘Format Study’ window where we configure the alert settings there are also other options we have to set. With the ‘Update on every tick’ setting, which we find in the ‘Properties’ tab, the script performs intra-bar calculations instead of computing once per bar, when the bar closes (MultiCharts Wiki, 2014b). With that option turned on, the script can generate a data feed alert at any time.

Configuring a MultiCharts indicator to update with every tick

In the ‘Format Study’ window we can also configure the input options in the ‘Inputs’ tab:

Configuring the input options of the MultiCharts script

Now when we add the script to the chart with its alerts enabled, it shows in the chart’s Status Line how long the data feed has been inactive:

Example chart with the indicator added

If an indicator’s value doesn’t appear in the Status Line, right-click somewhere on the Status Line and choose ‘Format Status Line’. This opens a window where components of the Status Line can be enabled or disabled. Here make sure that the ‘Study Values’ option is enabled:

Formatting the Status Line in MultiCharts

The alerts that the indicator generates look like this (here I’ve set the data feed timeout to 15 seconds):

Example alerts when there's a data feed timeout in MultiCharts

See generating an alert when automated trade execution is turned off for another idea of how alerts can help with risk management.

Summary

When the script’s ‘Enable Alerts’ option is enabled, we can generate alerts with the Alert() keyword. Currently, there’s no way to programmatically determine whether a data feed has connectivity problems. What we can do is track how many seconds ago the last data feed update happened, and generate alerts based on that. The time of the last trade, bid, or ask update in the chart’s time zone is returned by the Q_Time_DT keyword. The ComputerDateTime keyword, on the other hand, returns the computer’s current time. When the chart has the same time zone as the computer, we can subtract those two values from each other. That results in a DateTime time interval. We can convert such a DateTime value to seconds by multiplying it with 86400 or dividing with ELTimeToDateTime_s(1). Should the data feed indeed stop or disconnect, then the lack of price updates will also stop the script from calculating. We can prevent that by periodically recalculating the script with the RecalcLastBarAfter() keyword. That way the script keeps up to date, independent of whether there are incoming price ticks or not.

Learn more:


References

Henry MultiCharts (2012, January 31). Loss of Broker Connectivity Alert. Retrieved on June 23, 2016, from http://www.multicharts.com/discussion/viewtopic.php?f=5&t=9890#p47214

MultiCharts Wiki (2012, August 31). IntrabarOrderGeneration. Retrieved on June 23, 2016, from https://www.multicharts.com/trading-software/index.php/IntraBarOrderGeneration

MultiCharts Wiki (2014a, September 3). RecalcLastBarAfter. Retrieved on June 23, 2016, from https://www.multicharts.com/trading-software/index.php/RecalcLastBarAfter

MultiCharts Wiki (2014b, September 15). Indicator settings. Retrieved on June 24, 2016, from http://www.multicharts.com/trading-software/index.php/Indicator_Settings

MultiCharts Wiki (2013, May 10). Using Alerts. Retrieved on June 11, 2016, from https://www.multicharts.com/trading-software/index.php/Using_Alerts

MultiCharts Wiki (2016, February 22). How Scripts Work. Retrieved on June 23, 2016, from http://www.multicharts.com/trading-software/index.php/How_Scripts_Work

PowerLanguage Keyword Reference (2016). Retrieved on May 25, 2016, from http://www.multicharts.com/trading-software/images/c/c6/PowerLanguage_Keyword_Reference.pdf