--+------------------------------------------------------------------+ --| Copyright © 2016, Gehtsoft USA LLC | --| http://fxcodebase.com | --+------------------------------------------------------------------+ --| Support our efforts by donating | --| Paypal: https://goo.gl/9Rj74e | --| BitCoin : 15VCJTLaz12Amr7adHSBtL9v8XomURo9RF | --+------------------------------------------------------------------+ --| Developed by : Mario Jemic | --| mario.jemic@gmail.com | --+------------------------------------------------------------------+ function Init() --The strategy profile initialization strategy:name("Close ALL Strategy"); strategy:description(""); strategy:setTag("NonOptimizableParameters", "Email,SendEmail,SoundFile,RecurrentSound,PlaySound, ShowAlert"); strategy.parameters:addGroup("Calculation"); strategy.parameters:addInteger("Profit", "Profit (in pips)", "", 100); CreateTradingParameters(); end function CreateTradingParameters() strategy.parameters:addGroup("Trading Parameters"); strategy.parameters:addBoolean("AllowTrade", "Allow strategy to trade", "", false); strategy.parameters:setFlag("AllowTrade", core.FLAG_ALLOW_TRADE); strategy.parameters:addString("Account", "Account to trade on", "", ""); strategy.parameters:setFlag("Account", core.FLAG_ACCOUNT); strategy.parameters:addGroup("Alerts"); strategy.parameters:addBoolean("ShowAlert", "ShowAlert", "", true); strategy.parameters:addBoolean("PlaySound", "Play Sound", "", false); strategy.parameters:addFile("SoundFile", "Sound File", "", ""); strategy.parameters:setFlag("SoundFile", core.FLAG_SOUND); strategy.parameters:addBoolean("RecurrentSound", "Recurrent Sound", "", true); strategy.parameters:addBoolean("SendEmail", "Send Email", "", false); strategy.parameters:addString("Email", "Email", "", ""); strategy.parameters:setFlag("Email", core.FLAG_EMAIL); strategy.parameters:addGroup("Time Parameters"); strategy.parameters:addString("StartTime", "Start Time for Trading", "", "00:00:00"); strategy.parameters:addString("StopTime", "Stop Time for Trading", "", "24:00:00"); end local OpenTime, CloseTime; local SoundFile = nil; local RecurrentSound = false; local AllowTrade; local CanClose; local ShowAlert; local Email; local SendEmail; local OpenTime, CloseTime; local Profit; function Prepare( nameOnly) local name; name = profile:id() ; instance:name(name); Profit=instance.parameters.Profit; PrepareTrading(); if nameOnly then return ; end TickSource = ExtSubscribe(1, nil, "t1", instance.parameters.Type == "Bid", "close"); local valid; OpenTime, valid = ParseTime(instance.parameters.StartTime); assert(valid, "Time " .. instance.parameters.StartTime .. " is invalid"); CloseTime, valid = ParseTime(instance.parameters.StopTime); assert(valid, "Time " .. instance.parameters.StopTime .. " is invalid"); end -- NG: create a function to parse time function ParseTime(time) local Pos = string.find(time, ":"); local h = tonumber(string.sub(time, 1, Pos - 1)); time = string.sub(time, Pos + 1); Pos = string.find(time, ":"); local m = tonumber(string.sub(time, 1, Pos - 1)); local s = tonumber(string.sub(time, Pos + 1)); return (h / 24.0 + m / 1440.0 + s / 86400.0), -- time in ole format ((h >= 0 and h < 24 and m >= 0 and m < 60 and s >= 0 and s < 60) or (h == 24 and m == 0 and s == 0)); -- validity flag end function PrepareTrading() local PlaySound = instance.parameters.PlaySound; if PlaySound then SoundFile = instance.parameters.SoundFile; else SoundFile = nil; end assert(not(PlaySound) or (PlaySound and SoundFile ~= ""), "Sound file must be chosen"); ShowAlert = instance.parameters.ShowAlert; RecurrentSound = instance.parameters.RecurrentSound; SendEmail = instance.parameters.SendEmail; if SendEmail then Email = instance.parameters.Email; else Email = nil; end assert(not(SendEmail) or (SendEmail and Email ~= ""), "E-mail address must be specified"); AllowTrade = instance.parameters.AllowTrade; Account = instance.parameters.Account; CanClose = core.host:execute("getTradingProperty", "canCreateMarketClose", instance.bid:instrument(), Account); end function ExtUpdate(id, source, period) -- The method called every time when a new bid or ask price appears. now = core.host:execute("getServerTime"); -- get only time now = now - math.floor(now); -- check whether the time is in the exit time period if AllowTrade then if not(checkReady("trades")) or not(checkReady("orders")) then return ; end else return; end if id ~= 1 then return; end if not(now >= OpenTime and now <= CloseTime) then return ; end if tradesProfit() < Profit then return; end exitPositions(); exitEntryPositions(); end function exitEntryPositions() local enum, row; enum = core.host:findTable("orders"):enumerator(); row = enum:next(); while (row ~= nil) do if row.FixStatus == "W" then local valuemap = core.valuemap(); valuemap.Command = "DeleteOrder"; valuemap.OrderID = row.OrderID; success, message = terminal:execute(200, valuemap); if not(success) then terminal:alertMessage(instance.bid:instrument(), instance.bid[NOW], "Failed delete order " .. message, instance.bid:date(NOW)); end end row = enum:next(); end end -- NG: Introduce async function for timer/monitoring for the order results function ExtAsyncOperationFinished(cookie, success, message) if cookie == 200 and not success then terminal:alertMessage(instance.bid:instrument(), instance.bid[instance.bid:size() - 1], "Failed to delete order" .. message, instance.bid:date(instance.bid:size() - 1)); elseif cookie == 201 and not success then terminal:alertMessage(instance.bid:instrument(), instance.bid[instance.bid:size() - 1], "Failed to close position" .. message, instance.bid:date(instance.bid:size() - 1)); end end --===========================================================================-- -- TRADING UTILITY FUNCTIONS -- --============================================================================-- function Signal (Label) if ShowAlert then terminal:alertMessage(instance.bid:instrument(), instance.bid[NOW], Label, instance.bid:date(NOW)); end if SoundFile ~= nil then terminal:alertSound(SoundFile, RecurrentSound); end if Email ~= nil then terminal:alertEmail(Email, Label, profile:id() .. "(" .. instance.bid:instrument() .. ")" .. instance.bid[NOW]..", " .. Label..", " .. instance.bid:date(NOW)); end end function checkReady(table) local rc; if Account == "TESTACC_ID" then -- run under debugger/simulator rc = true; else rc = core.host:execute("isTableFilled", table); end return rc; end function tradesProfit() local enum, row; local RETURN = 0; enum = core.host:findTable("trades"):enumerator(); row = enum:next(); while row ~= nil do if row.AccountID == Account then RETURN = RETURN + row.PL; end row = enum:next(); end return RETURN; end -- exit from the specified trade using the direction as a key function exitPositions() -- we have to loop through to exit all trades in each direction instead -- of using the net qty flag because we may be running multiple strategies on the same account. local enum, row; local found = false; enum = core.host:findTable("trades"):enumerator(); row = enum:next(); while (not found) and (row ~= nil) do -- for every trade for this instance. if row.AccountID == Account then exitTrade(row); end row = enum:next(); end end -- exit from the specified direction function exitTrade(tradeRow) if not(AllowTrade) then return true; end local valuemap, success, msg; valuemap = core.valuemap(); -- switch the direction since the order must be in oppsite direction if tradeRow.BS == "B" then BuySell = "S"; else BuySell = "B"; end valuemap.OrderType = "CM"; valuemap.OfferID = tradeRow.OfferID; valuemap.AcctID = Account; if (CanClose) then -- Non-FIFO can close each trade independantly. valuemap.TradeID = tradeRow.TradeID; valuemap.Quantity = tradeRow.Lot; else -- FIFO. valuemap.NetQtyFlag = "Y"; -- this forces all trades to close in the opposite direction. end valuemap.BuySell = BuySell; valuemap.CustomID = tradeRow.QTXT; success, msg = terminal:execute(201, valuemap); if not(success) then terminal:alertMessage(instance.bid:instrument(), instance.bid[instance.bid:size() - 1], "Close order failed" .. msg, instance.bid:date(instance.bid:size() - 1)); end return true; end dofile(core.app_path() .. "\\strategies\\standard\\include\\helper.lua");