-- Id: 7764 -- More information about this indicator can be found at: -- http://fxcodebase.com --+------------------------------------------------------------------+ --| Copyright © 2018, Gehtsoft USA LLC | --| http://fxcodebase.com | --+------------------------------------------------------------------+ --| Developed by : Mario Jemic | --| mario.jemic@gmail.com | --+------------------------------------------------------------------+ --| Support our efforts by donating | --| Paypal: https://goo.gl/9Rj74e | --+------------------------------------------------------------------+ --| Patreon : https://goo.gl/GdXWeN | --| BitCoin : 15VCJTLaz12Amr7adHSBtL9v8XomURo9RF | --| BitCoin Cash: 1BEtS465S3Su438Kc58h2sqvVvHK9Mijtg | --| Ethereum : 0x8C110cD61538fb6d7A2B47858F0c0AaBd663068D | --| LiteCoin : LLU8PSY2vsq7B9kRELLZQcKf5nJQrdeqwD | --+------------------------------------------------------------------+ local BAR = true function Init() --The strategy profile initialization strategy:name("TTC TDI STRATEGY") strategy:description("") strategy:setTag("Version", "2") strategy:setTag("NonOptimizableParameters", "ShowAlert,PlaySound,SoundFile,RecurrentSound,SendMail,Email") strategy.parameters:addGroup("Price") strategy.parameters:addString("Type", "Price Type", "", "Bid") strategy.parameters:addStringAlternative("Type", "Bid", "", "Bid") strategy.parameters:addStringAlternative("Type", "Ask", "", "Ask") strategy.parameters:addString("TF", "Time frame", "", "m5") strategy.parameters:setFlag("TF", core.FLAG_PERIODS) strategy.parameters:addGroup("Trix Trend Cycle") strategy.parameters:addInteger("TrixPeriod", "Period", "", 4) strategy.parameters:addDouble("SL", "Sell Level", "", 90) strategy.parameters:addDouble("BL", "Buy Level", "", 10) strategy.parameters:addGroup("Traders Dynamic Index Indicator") strategy.parameters:addString("Price", "Price Source", "", "close") strategy.parameters:addStringAlternative("Price", "OPEN", "", "open") strategy.parameters:addStringAlternative("Price", "HIGH", "", "high") strategy.parameters:addStringAlternative("Price", "LOW", "", "low") strategy.parameters:addStringAlternative("Price", "CLOSE", "", "close") strategy.parameters:addStringAlternative("Price", "MEDIAN", "", "median") strategy.parameters:addStringAlternative("Price", "TYPICAL", "", "typical") strategy.parameters:addStringAlternative("Price", "WEIGHTED", "", "weighted") strategy.parameters:addInteger("RSI_N", "RSI Periods", "Recommended values are in 8-25 range", 13, 2, 1000) strategy.parameters:addInteger( "VB_N", "Volatility Band", "Number of periods to find volatility band. Recommended value is 20-40", 34, 2, 1000 ) strategy.parameters:addDouble("VB_W", "Volatility Band Width", "", 1.6185, 0, 100) strategy.parameters:addInteger("RSI_P_N", "RSI Price Line Periods", "", 2, 1, 1000) strategy.parameters:addString("RSI_P_M", "RSI Price Line Smoothing Method", "", "MVA") strategy.parameters:addStringAlternative("RSI_P_M", "MVA(SMA)", "", "MVA") strategy.parameters:addStringAlternative("RSI_P_M", "EMA", "", "EMA") strategy.parameters:addStringAlternative("RSI_P_M", "LWMA", "", "LWMA") strategy.parameters:addStringAlternative("RSI_P_M", "LSMA(Regression)", "", "REGRESSION") strategy.parameters:addStringAlternative("RSI_P_M", "SMMA", "", "SMMA") strategy.parameters:addStringAlternative("RSI_P_M", "WMA(Wilders)", "", "WMA") strategy.parameters:addStringAlternative("RSI_P_M", "KAMA(Kaufman)", "", "KAMA") strategy.parameters:addInteger("TS_N", "Trade Signal Line Periods", "", 7, 1, 1000) strategy.parameters:addString("TS_M", "Trade Signal Line Smoothing Method", "", "MVA") strategy.parameters:addStringAlternative("TS_M", "MVA(SMA)", "", "MVA") strategy.parameters:addStringAlternative("TS_M", "EMA", "", "EMA") strategy.parameters:addStringAlternative("TS_M", "LWMA", "", "LWMA") strategy.parameters:addStringAlternative("TS_M", "LSMA(Regression)", "", "REGRESSION") strategy.parameters:addStringAlternative("TS_M", "SMMA", "", "SMMA") strategy.parameters:addStringAlternative("TS_M", "WMA(Wilders)", "", "WMA") strategy.parameters:addStringAlternative("TS_M", "KAMA(Kaufman)", "", "KAMA") --BUY ENTRY LEVEL : 35 --BUY EXIT LEVEL : 68 --SELL ENTRY LEVEL: 65 --SELL EXIT LEVEL: 32 strategy.parameters:addInteger("BE", "Buy Entry Level", "", 35) strategy.parameters:addInteger("EB", "Buy Exit Level", "", 68) strategy.parameters:addInteger("SE", "Sell Level", "", 65) strategy.parameters:addInteger("ES", "Sell Exit Level", "", 32) strategy.parameters:addGroup("Optinal Exit") strategy.parameters:addBoolean("EXIT", "Use Optinal Exit", "", true) strategy.parameters:addGroup("Strategy Parameters") strategy.parameters:addString("Direction", "Type of signal", "", "direct") strategy.parameters:addStringAlternative("Direction", "direct", "", "direct") strategy.parameters:addStringAlternative("Direction", "reverse", "", "reverse") CreateTradingParameters() strategy.parameters:addGroup("Time Parameters") strategy.parameters:addInteger("ToTime", "Convert the date to", "", 6) strategy.parameters:addIntegerAlternative("ToTime", "EST", "", 1) strategy.parameters:addIntegerAlternative("ToTime", "UTC", "", 2) strategy.parameters:addIntegerAlternative("ToTime", "Local", "", 3) strategy.parameters:addIntegerAlternative("ToTime", "Server", "", 4) strategy.parameters:addIntegerAlternative("ToTime", "Financial", "", 5) strategy.parameters:addIntegerAlternative("ToTime", "Display", "", 6) strategy.parameters:addString("StartTime", "Start Time for Trading", "", "00:00:00") strategy.parameters:addString("StopTime", "Stop Time for Trading", "", "24:00:00") strategy.parameters:addBoolean("UseMandatoryClosing", "Use Mandatory Closing", "", false) strategy.parameters:addString("ExitTime", "Mandatory Closing Time", "", "23:59:00") strategy.parameters:addInteger("ValidInterval", "Valid interval for operation in second", "", 60) end function CreateTradingParameters() strategy.parameters:addGroup("Trading Parameters") strategy.parameters:addBoolean("AllowTrade", "Allow strategy to trade", "", false) -- NG: optimizer/backtester hint strategy.parameters:setFlag("AllowTrade", core.FLAG_ALLOW_TRADE) strategy.parameters:addString( "ALLOWEDSIDE", "Allowed side", "Allowed side for trading or signaling, can be Sell, Buy or Both", "Both" ) strategy.parameters:addStringAlternative("ALLOWEDSIDE", "Both", "", "Both") strategy.parameters:addStringAlternative("ALLOWEDSIDE", "Buy", "", "Buy") strategy.parameters:addStringAlternative("ALLOWEDSIDE", "Sell", "", "Sell") strategy.parameters:addBoolean("AllowMultiple", "Allow Multiple", "", true) strategy.parameters:addString("Account", "Account to trade on", "", "") strategy.parameters:setFlag("Account", core.FLAG_ACCOUNT) strategy.parameters:addInteger("Amount", "Trade Amount in Lots", "", 1, 1, 1000000) strategy.parameters:addBoolean("SetLimit", "Set Limit Orders", "", false) strategy.parameters:addInteger("Limit", "Limit Order in pips", "", 30, 1, 10000) strategy.parameters:addBoolean("SetStop", "Set Stop Orders", "", false) strategy.parameters:addInteger("Stop", "Stop Order in pips", "", 30, 1, 10000) strategy.parameters:addBoolean("TrailingStop", "Trailing stop order", "", false) 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) end local Source local SoundFile = nil local RecurrentSound = false local ALLOWEDSIDE local AllowMultiple local AllowTrade local Offer local CanClose local Account local Amount local SetLimit local Limit local SetStop local Stop local TrailingStop local ShowAlert local Email local SendEmail local BaseSize local BL, SL local TrixPeriod local BE, SE local EB, ES local Indicator = {} local Short = {} local Source local RSI_N, VB_N, VB_W, RSI_P_N, RSI_P_M, TS_N, TS_M local Direction local EXIT local first local Price local OpenTime, CloseTime, ExitTime local ValidInterval, UseMandatoryClosing local ToTime -- function Prepare(nameOnly) Price = instance.parameters.Price BL = instance.parameters.BL SL = instance.parameters.SL BE = instance.parameters.BE SE = instance.parameters.SE RSI_N = instance.parameters.RSI_N VB_N = instance.parameters.VB_N VB_W = instance.parameters.VB_W RSI_P_N = instance.parameters.RSI_P_N RSI_P_M = instance.parameters.RSI_P_M TS_N = instance.parameters.TS_N TS_M = instance.parameters.TS_M EXIT = instance.parameters.EXIT EB = instance.parameters.EB ES = instance.parameters.ES TrixPeriod = instance.parameters.TrixPeriod -- NG: replace string comparison everytime in future. Direction = instance.parameters.Direction == "direct" -- NG: check TF1/TF2 instead of TF assert(instance.parameters.TF ~= "t1", "The time frame must not be tick") local name name = profile:id() .. "( " .. instance.bid:name() local i name = name assert(core.indicators:findIndicator("TTC") ~= nil, "Please, download and install TTC.LUA indicator") assert( core.indicators:findIndicator("TRADERSDYNAMICINDEX") ~= nil, "Please, download and install TRADERSDYNAMICINDEX.LUA indicator" ) name = name .. " )" instance:name(name) PrepareTrading() if nameOnly then return end Source = ExtSubscribe(1, nil, instance.parameters.TF, instance.parameters.Type == "Bid", "bar") Indicator[1] = core.indicators:create("TTC", Source, TrixPeriod) Short["TTC"] = Indicator[1]:getStream(0) Indicator[2] = core.indicators:create("TRADERSDYNAMICINDEX", Source[Price], RSI_N, VB_N, VB_W, RSI_P_N, RSI_P_M, TS_N, TS_M) Short[1] = Indicator[2]:getStream(0) Short[2] = Indicator[2]:getStream(1) Short[3] = Indicator[2]:getStream(2) Short[4] = Indicator[2]:getStream(3) Short[5] = Indicator[2]:getStream(4) first = math.max( Short["TTC"]:first(), Short[1]:first(), Short[2]:first(), Short[3]:first(), Short[4]:first(), Short[5]:first() ) ToTime = instance.parameters.ToTime ValidInterval = instance.parameters.ValidInterval UseMandatoryClosing = instance.parameters.UseMandatoryClosing if ToTime == 1 then ToTime = core.TZ_EST elseif ToTime == 2 then ToTime = core.TZ_UTC elseif ToTime == 3 then ToTime = core.TZ_LOCAL elseif ToTime == 4 then ToTime = core.TZ_SERVER elseif ToTime == 5 then ToTime = core.TZ_FINANCIAL elseif ToTime == 6 then ToTime = core.TZ_TS end 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") ExitTime, valid = ParseTime(instance.parameters.ExitTime) assert(valid, "Time " .. instance.parameters.ExitTime .. " is invalid") if UseMandatoryClosing then core.host:execute("setTimer", 100, math.max(ValidInterval / 2, 1)) end end function PrepareTrading() AllowMultiple = instance.parameters.AllowMultiple ALLOWEDSIDE = instance.parameters.ALLOWEDSIDE 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 if AllowTrade then Account = instance.parameters.Account Amount = instance.parameters.Amount BaseSize = core.host:execute("getTradingProperty", "baseUnitSize", instance.bid:instrument(), Account) Offer = core.host:findTable("offers"):find("Instrument", instance.bid:instrument()).OfferID CanClose = core.host:execute("getTradingProperty", "canCreateMarketClose", instance.bid:instrument(), Account) SetLimit = instance.parameters.SetLimit Limit = instance.parameters.Limit SetStop = instance.parameters.SetStop Stop = instance.parameters.Stop TrailingStop = instance.parameters.TrailingStop end end function ReleaseInstance() core.host:execute("killTimer", 100) end function ParseTime(time) local pos = string.find(time, ":"); if pos == nil then return nil, false; end local h = tonumber(string.sub(time, 1, pos - 1)); time = string.sub(time, pos + 1); pos = string.find(time, ":"); if pos == nil then return nil, false; end 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 InRange(now, openTime, closeTime) if openTime < closeTime then return now >= openTime and now <= closeTime; end if openTime > closeTime then return now > openTime or now < closeTime; end return now == openTime; end function ExtUpdate(id, source, period) -- The method called every time when a new bid or ask price appears. if AllowTrade then if not (checkReady("trades")) or not (checkReady("orders")) then return end end if id ~= 1 then return end Indicator[1]:update(core.UpdateLast) Indicator[2]:update(core.UpdateLast) if period < first + 1 then return end now = core.host:execute("getServerTime") now = core.host:execute("convertTime", core.TZ_EST, ToTime, now) -- get only time now = now - math.floor(now) if not InRange(now, OpenTime, CloseTime) then return end --[[ BUYING CONDITIONS: 1. PRICE LINE OF TDI > BUY ENTRY LEVEL (35) AND ok 2. PRICE LINE OF TDI > TRD SIG OF TDI ok 3. TRD SIG OF TDI> MKT BASE OF TDI ok 4. PRICE LINE OF TDI< BUY EXIT LEVEL(68) 5. PRICE LINE OF TDI >VOL BAND LOW 6. PRICE LINE OF TDI < VOL BAND HIGH ]] if core.crossesOver(Short["TTC"], BL, period) and Short[1][period] > BE and Short[1][period] > Short[5][period] and Short[5][period] > Short[4][period] and Short[1][period] < EB and Short[1][period] > Short[3][period] and Short[1][period] < Short[2][period] then --[[ SELLING CONDITIONS: 1. PRICE LINE OF TDI < SELL ENTRY LEVEL (65) AND 2. PRICE LINE OF TDI < TRD SIG OF TDI 3. TRD SIG OF TDI < MKT BASE OF TDI 4. PRICE LINE OF TDI> SELL EXIT LEVEL(32) 5. PRICE LINE OF TDI >VOL BAND LOW 6. PRICE LINE OF TDI < VOL BAND HIGH]] if Direction then BUY() else SELL() end elseif core.crossesUnder(Short["TTC"], SL, period) and Short[1][period] < SE and Short[1][period] < Short[5][period] and Short[5][period] < Short[4][period] and Short[1][period] > ES and Short[1][period] > Short[3][period] and Short[1][period] < Short[2][period] then if Direction then SELL() else BUY() end end if EXIT then if core.crossesUnder(Short["TTC"], SL, period) then if Direction then if haveTrades("B") then exit("B") Signal("Close Long") end else if haveTrades("S") then exit("S") Signal("Close Short") end end end if core.crossesOver(Short["TTC"], BL, period) then if Direction then if haveTrades("S") then exit("S") Signal("Close Short") end else if haveTrades("B") then exit("B") Signal("Close Long") end end end end end -- NG: Introduce async function for timer/monitoring for the order results function ExtAsyncOperationFinished(cookie, success, message) if cookie == 100 then -- timer if UseMandatoryClosing and AllowTrade then now = core.host:execute("getServerTime") now = core.host:execute("convertTime", core.TZ_EST, ToTime, now) -- get only time now = now - math.floor(now) -- check whether the time is in the exit time period if now >= ExitTime and now < ExitTime + (ValidInterval / 86400.0) then if not checkReady("trades") then return end if haveTrades("B") then exit("B") Signal("Close Long") end if haveTrades("S") then exit("S") Signal("Close Short") end end end elseif cookie == 200 and not success then terminal:alertMessage( instance.bid:instrument(), instance.bid[instance.bid:size() - 1], "Open order failed" .. 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], "Close order failed" .. message, instance.bid:date(instance.bid:size() - 1) ) end end --===========================================================================-- -- TRADING UTILITY FUNCTIONS -- --============================================================================-- function BUY() if AllowTrade then if haveTrades("B") and not AllowMultiple then if haveTrades("S") then exit("S") Signal("Close Short") end return end if ALLOWEDSIDE == "Sell" then if haveTrades("S") then exit("S") Signal("Close Short") end return end if haveTrades("S") then exit("S") Signal("Close Short") end enter("B") Signal("Open Long") elseif ShowAlert then Signal("Up Trend") end end function SELL() if AllowTrade then if haveTrades("S") and not AllowMultiple then if haveTrades("B") then exit("B") Signal("Close Long") end return end if ALLOWEDSIDE == "Buy" then if haveTrades("B") then exit("B") Signal("Close Long") end return end if haveTrades("B") then exit("B") Signal("Close Long") end enter("S") Signal("Open Short") else Signal("Down Trend") end end 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) return core.host:execute("isTableFilled", table) end function tradesCount(BuySell) local enum, row local count = 0 enum = core.host:findTable("trades"):enumerator() row = enum:next() -- NG: to get the true count we must NOT stop when count is not a zero or -- the function will return 1 or 0 only and will work as "haveTrades" -- while count == 0 and row ~= nil do while row ~= nil do if row.AccountID == Account and row.OfferID == Offer and (row.BS == BuySell or BuySell == nil) then count = count + 1 end row = enum:next() end return count end function haveTrades(BuySell) local enum, row local found = false enum = core.host:findTable("trades"):enumerator() row = enum:next() while (not found) and (row ~= nil) do if row.AccountID == Account and row.OfferID == Offer and (row.BS == BuySell or BuySell == nil) then found = true end row = enum:next() end return found end -- enter into the specified direction function enter(BuySell) if not (AllowTrade) then return true end -- do not enter if position in the -- specified direction already exists if tradesCount(BuySell) > 0 and not AllowMultiple then return true end local valuemap, success, msg valuemap = core.valuemap() valuemap.OrderType = "OM" valuemap.OfferID = Offer valuemap.AcctID = Account valuemap.Quantity = Amount * BaseSize valuemap.BuySell = BuySell -- add stop/limit valuemap.PegTypeStop = "O" if SetStop then if BuySell == "B" then valuemap.PegPriceOffsetPipsStop = -Stop else valuemap.PegPriceOffsetPipsStop = Stop end end if TrailingStop then valuemap.TrailStepStop = 1 end valuemap.PegTypeLimit = "O" if SetLimit then if BuySell == "B" then valuemap.PegPriceOffsetPipsLimit = Limit else valuemap.PegPriceOffsetPipsLimit = -Limit end end if (not CanClose) then valuemap.EntryLimitStop = "Y" end success, msg = terminal:execute(200, valuemap) if not (success) then terminal:alertMessage( instance.bid:instrument(), instance.bid[instance.bid:size() - 1], "Open order failed" .. msg, instance.bid:date(instance.bid:size() - 1) ) return false end return true end -- exit from the specified direction function exit(BuySell, use_net) if not (AllowTrade) then return true end if use_net == true then local valuemap, success, msg if tradesCount(BuySell) > 0 then valuemap = core.valuemap() -- switch the direction since the order must be in oppsite direction if BuySell == "B" then BuySell = "S" else BuySell = "B" end valuemap.OrderType = "CM" valuemap.OfferID = Offer valuemap.AcctID = Account valuemap.NetQtyFlag = "Y" valuemap.BuySell = BuySell success, msg = terminal:execute(101, valuemap) if not(success) then terminal:alertMessage( instance.bid:instrument(), instance.bid[instance.bid:size() - 1], "Open order failed"..msg, instance.bid:date(instance.bid:size() - 1) ) return false end return true end else local enum = core.host:findTable("trades"):enumerator(); local row = enum:next(); while row ~= nil do if row.BS == BuySell and row.OfferID == Offer then local valuemap = core.valuemap(); valuemap.BuySell = row.BS == "B" and "S" or "B"; valuemap.OrderType = "CM"; valuemap.OfferID = row.OfferID; valuemap.AcctID = row.AccountID; valuemap.TradeID = row.TradeID; valuemap.Quantity = row.Lot; local success, msg = terminal:execute(101, valuemap); end row = enum: next(); end end return false end dofile(core.app_path() .. "\\strategies\\standard\\include\\helper.lua")