簡單信號 - 新蠟燭線提示
任務
讓我們開發一個簡單信號,當我們選擇的時間框架內出現新的蠟燭線時,該信號可以顯示消息或發出提示音。假設屬於一根新蠟燭線的第一個價格點 (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 • русский • 中文 • 中文(繁體) |
---|