简单信号 - 新蜡烛线提示
From FxCodeBaseWiki
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.bid 和 instance.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 • Русский • 中文 • 中文(繁體) |
|---|