Simple Signal - New Candle Alert

From FxCodeBaseWiki

Jump to: navigation, search

Contents

Task

You can develop a simple signal which shows a message or plays a sound when a new candle of the chosen time frame appears. Assume that the candle appears when the first tick which belongs to a new candle, comes.

Writing Signal

Creating File

First, create an empty file:

  • Run Lua Editor.
  • Click on File->Strategy->New.
  • In the wizard form, click on Cancel.

There is no need for confusion over the term "strategy". A signal is a strategy which does not trade but only shows alerts.

A new strategy file is created. Now save it. The file name is a short name of the strategy (as MA_ADVISOR for the Sample moving average strategy). You can call your strategy NEW_CANDLE.

  • Click on File->Save As. Since you create the strategy, the editor opens the strategy folder of SDK. If it does not, please change the folder. You do your best to manage how the "File Open/Save" dialog functions which changes with every new Windows version but sometimes it isn't successful.
  • Enter NEW_CANDLE.lua in the file name and click on OK.

That's all, the file is created.

Introducing the Strategy

Introducing a strategy means providing all of the information which is required for showing the strategy in the list of strategies, asking you for the parameters, and so on.

All information used to “introduce” should be specified in the Init() function of the strategy, so write it. State the “user-friendly” name and description first.

function Init()
    strategy:name("Alert New Candle");
    strategy:description("The signal shows alerts when the first tick of a new candle appears");
end

Describe the parameters which should be asked. You need the following parameters:

The time frame of the candle to watch.

The time frame is just a code, for example, m1 means a 1-minute candle, H1 - a 1-hour candle, and so on. So, it is string data. Add a string parameter:

function Init()
    strategy:name("Alert New Candle");
    strategy:description("The signal shows alerts when the first tick of a new candle appears");
 
    strategy.parameters:addString("TF", "Time frame of candle", "", "H1");end

However, you don't want to care about all these codes. A better way is to choose the time frame from the list. You can create a list of parameter values using the addStringAlternative() method, as you did in GMA indicator but you can also use parameter flags. A parameter flag is a feature of Indicore which helps indicators and strategies provide the host application with a hint on how to handle the parameter. See:

function Init()
    strategy:name("Alert New Candle");
    strategy:description("The signal shows alerts when the first tick of a new candle appears");
 
    strategy.parameters:addString("TF", "Time frame of candle", "", "H1");
    strategy.parameters:setFlag("TF", core.FLAG_PERIODS);end

The host application knows that the parameter must be a list of time frames.

Ask the user if he/she wants to see the alert.

This is a simple Yes/No parameter. Such Yes/No values are called "Boolean" values, so create a boolean parameter:

function Init()
    ...
    strategy.parameters:addString("TF", "Time frame of candle", "", "H1");
    strategy.parameters:setFlag("TF", core.FLAG_PERIODS);
    strategy.parameters:addBoolean("ShowMessage", "Show message when new candle appears?", "", false);end

Ask the user if he/she wants to hear a sound.

This is a simple Yes/No parameter, as well.

function Init()
    ...
    strategy.parameters:addString("TF", "Time frame of candle", "", "H1");
    strategy.parameters:setFlag("TF", core.FLAG_PERIODS);
    strategy.parameters:addBoolean("ShowMessage", "Show message when new candle appears?", "", false);
    strategy.parameters:addBoolean("PlaySound", "Play Sound when new candle appears?", "", true);end

However, for the sound, you should also ask the file name of the sound and whether you want to have the sound played recurrently until you perform a certain action in the Trading Station. You should use the parameter flag trick again to let the application know that the file should be a sound file.

function Init()
    ...
    strategy.parameters:addString("TF", "Time frame of candle", "", "H1");
    strategy.parameters:setFlag("TF", core.FLAG_PERIODS);
    strategy.parameters:addBoolean("ShowMessage", "Show message when new candle appears?", "", false);
    strategy.parameters:addBoolean("PlaySound", "Play Sound when new candle appears?", "", true);
    strategy.parameters:addFile("Sound", "Sound file", "", "");    strategy.parameters:setFlag("Sound", core.FLAG_SOUND);    strategy.parameters:addBoolean("RecurrentSound", "Recurrent sound?", "", false);end

That's all. Add the parameter groups for easier navigation and the Init() method is ready:

function Init()
    strategy:name("Alert New Candle");
    strategy:description("The signal shows alerts when the first tick of a new candle appears");
 
    strategy.parameters:addGroup("Parameters");
    strategy.parameters:addString("TF", "Time frame of candle", "", "H1");
    strategy.parameters:setFlag("TF", core.FLAG_PERIODS);
 
    strategy.parameters:addGroup("Alerts");
    strategy.parameters:addBoolean("ShowMessage", "Show message when new candle appears?", "", false);
    strategy.parameters:addBoolean("PlaySound", "Play Sound when new candle appears?", "", true);
    strategy.parameters:addFile("Sound", "Sound file", "", "");
    strategy.parameters:setFlag("Sound", core.FLAG_SOUND);
    strategy.parameters:addBoolean("RecurrentSound", "Recurrent sound?", "", false);
end

The host application knows how to show your strategy in the list of all strategies and how to ask for the parameters when you want to run the strategy.

Please note that you do not have any parameters for the instrument. You may assume that any signal or strategy is always applied on one and only one instrument, however, they can use any number of instruments). It is activated every time a new tick of that instrument comes. So, the host application asks you for the instrument to apply the strategy on anyway and you don't need to specify such parameter explicitly.

Starting Strategy

Create the Prepare() method of the strategy. This method is called:

  • When you change the parameters of the strategy while the strategy is being created. In this case, the strategy is only expected to check the parameters and set the name of the instance.
  • Before starting the strategy. In this case, the strategy is expected to prepare all the data for further execution.

You can start with the first task. You should check the parameters and create the name of the strategy.

You have only one parameter to be checked. You should NOT choose the tick time frame, otherwise, you are to have an alert every time a new tick appears. It's possible to receive an alert several times in one second.

function Prepare(onlyName)
    assert(instance.parameters.TF ~= "t1", "Please choose non-tick time frame");
end

Create the name of the strategy instance. It is recommended that the name should include the strategy short name, the name of the instrument and all significant parameters. If only the name is requested (the first case of calling the Prepare() method), you should return to avoid needless operations executed when the strategy is prepared for the start.

function Prepare(onlyName)
    assert(instance.parameters.TF ~= "t1", "Please choose non-tick time frame");
 
    local name;
    name = profile:id() .. "(" .. instance.bid:instrument() .. "," .. instance.parameters.TF .. ")";
    instance:name(name);
    if onlyName then
        return ;
    end
end

In fact, you need nothing at this point and may continue. However, return to the Prepare () function later, when you find what else you should do here.

Updating the Strategy

The strategy should also have the Update() function which is called every time a new tick of the chosen time frame comes. Unlike the indicator's Update() function, the strategy has no parameters for update because the function is called only when a new tick appears. It is not called for any historical data.

To access ticks, you can use instance.bid and instance.ask collections. The last element of these collections is the latest bid and ask prices.

So, on every tick you should:

  • If it is the first tick that you receive, find the end of the candle the tick belongs to.
  • Otherwise, if the tick does not belong the candle, show all the alerts and calculate the end of the new candle.

To keep the end of the candle between calls of the Update() function, simply use a global variable:

local startOfCandle = nil;
 
function Update()
    local s;
 
    -- get candle
    s = core.getcandle(instance.parameters.TF, 
                       instance.bid:date(instance.bid:size() - 1),
                       core.host:execute("getTradingDayOffset"), 
                       core.host:execute("getTradingWeekOffset"));
 
    if startOfCandle == nil then
        startOfCandle = s;
    elseif s > startOfCandle then
        startOfCandle = s;
        -- TBD: Signal
    end
end

At first glance, nothing may seem to be wrong with this code. However, take a closer look at the code.

First, you have three operations:

  • Pretty heavy getcandle call.
  • Calling host for trading day.
  • Calling host for trading week.

And all of them are executed every tick.

You can optimize this code.

First of all, the trading day and trading week parameters cannot be changed any time. You can get them just once, in the Prepare() method:

local tradingDayOffset, tradingWeekOffset;
 
function Prepare(onlyName)
    ...
    tradingDayOffset = core.host:execute("getTradingDayOffset");
    tradingWeekOffset = core.host:execute("getTradingWeekOffset");
end
 
function Update()
    ...
    s = core.getcandle(TF, instance.bid:date(NOW), tradingDayOffset, tradingWeekOffset);
    ...
end

Then, take a closer look at the core.getcandle function. It returns both the start and the end of the candle (or the beginning of the next candle). So, you can just calculate the end of each candle once and then wait for the tick in the next candle:

local endOfCandle = nil;
 
function Update()
    local s, e;
 
    if endOfCandle == nil then
        s, e = core.getcandle(TF, instance.bid:date(NOW), tradingDayOffset, tradingWeekOffset);
        endOfCandle = e;
    elseif instance.bid:date(NOW) >= endOfCandle then
        s, e = core.getcandle(TF, instance.bid:date(NOW), tradingDayOffset, tradingWeekOffset);
        endOfCandle = e;
        ...
    end
end

This is much better. You can now add the alerts and sounds, in the event that you requested them. Use the terminal table to show the alerts. Pay attention to the core.host:execute("convertTime") call. The date and time returned by the price collections are always in EST time zone. You can set another time zone in Trading Station, so, to have the date of the candle match the date that you see in Trading Station, you should convert the EST time zone into the currently chosen Trading Station time zone.

function Update()
    ...
    elseif instance.bid:date(NOW) >= endOfCandle then
    ...
        if instance.parameters.ShowMessage then
            local message;
            message = string.format("Candle of %s/%s (%s) is started", 
                                     instance.bid:instrument(), TF, 
                                     core.formatDate(core.host:execute("convertTime", core.TZ_EST, core.TZ_TS, s)));
            terminal:alertMessage(instance.bid:instrument(), instance.bid[NOW], message, instance.bid:date(NOW));
        end
 
        if instance.parameters.PlaySound then
            terminal:alertSound(instance.parameters.Sound, instance.parameters.RecurrentSound);
        end
 
    end
end

That's all.

Final Version

Please see the complete source code of your strategy below:

function Init()
    strategy:name("Alert New Candle");
    strategy:description("The signal shows alerts when the first tick of a new candle appears");
 
    strategy.parameters:addGroup("Parameters");
    strategy.parameters:addString("TF", "Time frame of candle", "", "H1");
    strategy.parameters:setFlag("TF", core.FLAG_PERIODS);
 
    strategy.parameters:addGroup("Alerts");
    strategy.parameters:addBoolean("ShowMessage", "Show message when new candle appears?", "", false);
    strategy.parameters:addBoolean("PlaySound", "Play Sound when new candle appears?", "", true);
    strategy.parameters:addFile("Sound", "Sound file", "", "");
    strategy.parameters:setFlag("Sound", core.FLAG_SOUND);
    strategy.parameters:addBoolean("RecurrentSound", "Recurrent sound?", "", false);
end
 
local name;
local TF;
local tradingDayOffset, tradingWeekOffset;
 
function Prepare(onlyName)
    assert(instance.parameters.TF ~= "t1", "Please choose non-tick time frame");
 
    name = profile:id() .. "(" .. instance.bid:instrument() .. "," .. instance.parameters.TF .. ")";
    instance:name(name);
    if onlyName then
        return ;
    end
 
    tradingDayOffset = core.host:execute("getTradingDayOffset");
    tradingWeekOffset = core.host:execute("getTradingWeekOffset");
    TF = instance.parameters.TF;
end
 
local endOfCandle = nil;
 
function Update()
    local s, e;
 
    if endOfCandle == nil then
        s, e = core.getcandle(TF, instance.bid:date(NOW), tradingDayOffset, tradingWeekOffset);
        endOfCandle = e;
    elseif instance.bid:date(NOW) >= endOfCandle then
        s, e = core.getcandle(TF, instance.bid:date(NOW), tradingDayOffset, tradingWeekOffset);
        endOfCandle = e;
        if instance.parameters.ShowMessage then
            local message;
            message = string.format("Candle of %s/%s (%s) is started", instance.bid:instrument(), TF, core.formatDate(core.host:execute("convertTime", core.TZ_EST, core.TZ_TS, s)));
            terminal:alertMessage(instance.bid:instrument(), instance.bid[NOW], message, instance.bid:date(NOW));
        end
 
        if instance.parameters.PlaySound then
            terminal:alertSound(instance.parameters.Sound, instance.parameters.RecurrentSound);
        end
    end
end

This Article in Other Languages

Language: English  • Español • Français • Русский • 中文 • ‪中文(繁體)‬
Personal tools