简单信号 - 新蜡烛线提示

From FxCodeBaseWiki

Jump to: navigation, search

Contents

任务

让我们开发一个简单信号,当我们选择的时间框架内出现新的蜡烛线时,该信号可以显示消息或发出提示音。假设属于一根新蜡烛线的第一个分笔成交点 (tick) 出现时,即代表出现新蜡烛线。

编写信号

建立文件

首先,建立一个空文件:

  • 运行 Lua 编辑器 (Lua Editor)。
  • 单击 File(文件)->Strategy(策略)->New(新建)
  • 在向导表单内,单击 Cancel(取消),我们将从头编写策略!

不要被“策略”这个术语所迷惑。信号是指不进行交易,只显示提示的策略。

新的策略文件已建立。现在保存该文件。文件名使用策略的简称(例如,示例中的移动平均线策略命名为 MA_ADVISOR)。我们的策略可以称为 NEW_CANDLE。

  • 单击 File(文件)->Save As(另存为)。因为我们已经建立了策略,编辑器将打开 SDK 的策略文件夹。如果没有打开,请您更改文件夹。在每个新的 Windows 版本中,“文件打开/保存”对话框的行为都会发生变化,虽然我们已经尽力应对此情况,但有时仍然不太成功。
  • 在文件名中输入 NEW_CANDLE.lua,然后单击 OK(确定)

相关介绍就是这些,文件已建立。

引入策略

引入策略是指提供所需的所有信息,以便在策略列表中显示策略,向用户请求参数,等等。

所有“引入”信息必须在策略的 Init() 函数中指定,所以我们来编写该函数。首先,定义“简单易懂”的名称和说明。

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

现在说明必须请求的参数。我们需要以下参数:

要监视的蜡烛线的时间框架

时间框架就是一个代码,例如,m1 代表 1 分钟蜡烛线,H1 代表 1 小时蜡烛线,等等。所以,它属于字符串数据。添加一个字符串参数:

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

然而,我们不想让用户关注上述所有代码。更好的方法是让用户从列表中选择时间框架。我们可以使用 addStringAlternative() 方法建立参数值列表,此操作与我们对 GMA 指标进行的操作一样,不过我们可以使用参数标志。参数标志是 Indicore 的一个功能,可帮助指标和策略向主应用程序提供关于如何处理参数的线索。我们来看看:

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

现在主应用程序就会知道该参数一定是时间框架的列表。

询问用户是否想看到提示。

这是一个简单的 Yes/No(是/否)参数。这类 Yes/No(是/否)值被称为布尔值,因此建立一个布尔参数:

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

询问用户是否想听到提示音。

这也是一个简单的 Yes/No(是/否)参数。

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

但是对于提示音,我们还必须询问声音文件的名称,以及用户是否想一直重复播放提示音,直到他/她在交易平台中执行了特定指令。请注意,我们需要再次使用参数标志,使应用程序知道该文件是声音文件。

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

相关介绍就是这些。为了更方便地进行导航,可以添加参数组,Init() 方法已经就绪:

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

现在,主应用程序知道如何在所有策略的列表中显示我们的策略,以及当用户想要运行策略时如何请求参数。

请注意,我们没有任何商品参数。我们假设任何信号或策略都会应用于一种商品,且仅应用于该商品(不过,它们可以使用任意数量的商品),而且每当该商品产生新的分笔成交点时,信号或策略就会被激活。因此,主应用程序无论如何都会询问用户需要对哪种商品应用策略,我们无需明确指定此类参数。

启动策略

现在,建立策略的 Prepare() 方法:出现以下情况时,即调用该方法:

  • 当策略正在建立时,用户改变策略的参数。在这种情况下,策略需检查参数,并设定实例的名称。
  • 启动策略之前。在这种情况下,策略需要准备所有供将来执行的数据。

让我们先开始第一个任务。我们必须检查策略并建立策略名称。

我们只需要检查一个参数。用户不得将 tick(分笔成交点)选作时间框架,否则,每次出现新的分笔成交点时,用户都会收到提示。可能一秒钟之内就会出现好多次。

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

现在建立策略实例的名称。建议该名称包含策略的简称、商品名称和所有重要参数。如果只要求名称(调用 Prepare() 方法的第一种情况),我们必须返回,以免在策略准备开始运行时执行不必要的操作。

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

事实上,此时我们无需执行其他任何操作,只要继续即可。不过,如果稍后我们发现还需要在此执行某些操作,我们将返回 Prepare() 函数。

更新策略

策略还必须具有 Update() 函数,每当选择的时间框架内出现新的分笔成交点,就会调用该函数。不同于指标的 Update() 函数,策略没有更新参数,因为只有当一个的分笔成交点出现时,才会调用该函数。不会为了任何历史数据调用该函数。

要访问分笔成交点,我们可以使用 instance.bidinstance.ask 集合。这些集合的最后一个元素就是最新的卖出价和买进价。

因此,对于每个分笔成交点,我们必须:

  • 如果它是我们接收到的第一个分笔成交点,找到它所属的蜡烛线的结束点。
  • 如果分笔成交点不属于该蜡烛线,显示所有提示并计算新蜡烛线的结束点。

若要保存在两次调用 Update() 函数之间出现的蜡烛线结束点,只需使用一个全局变量:

local startOfCandle = nil;
 
function Update()
    local s;
 
    -- 获取蜡烛线
    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;
        -- 待定:信号
    end
end

这个代码有什么问题?乍看之下似乎没有问题。但再仔细看一下这个代码。

首先,我们有三项操作:

  • 非常频繁的 getcandle 调用。
  • 向宿主程序调用交易日信息。
  • 向宿主程序调用交易周信息。

所有操作都是对每个分笔成交点执行的。

让我们优化此代码。

首先,交易日和交易周参数在任何时候都无法更改。我们可以在 Prepare() 方法中一次获取它们。

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

然后,我们再仔细看一下 core.getcandle 函数。它传回蜡烛线的开始点和结束点(或者下一根蜡烛线的开始点)。因此,我们可以对每根蜡烛线的结束点只进行一次计算,然后等待下一根蜡烛线的分笔成交点:

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

好多了。现在让我们添加提示和提示音以满足用户的这类请求。使用 terminal 表来显示提示。请注意对 core.host:execute("convertTime") 的调用。价格集合传回的日期和时间总是美国东部标准时间。用户可以在交易平台中设定另一个时区。因此,为了让蜡烛线的日期与用户在交易平台中看到的日期保持一致,我们必须将美国东部标准时间转换为当前选择的交易平台时区。

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

相关介绍就是这些。

最终版

请参见以下完整的策略源代码:

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


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