-- More information about this indicator can be found at: -- http://fxcodebase.com/code/viewtopic.php?f=31&t=4562 -- Id: 6581 --+------------------------------------------------------------------+ --| Copyright © 2016, Gehtsoft USA LLC | --| http://fxcodebase.com | --+------------------------------------------------------------------+ --| Support our efforts by donating | --| Paypal: https://goo.gl/9Rj74e | --| BitCoin : 15VCJTLaz12Amr7adHSBtL9v8XomURo9RF | --+------------------------------------------------------------------+ -- Intoduce the strategy to the host application (for example, Marketscope). -- The function is called once when the host application initially loads the strategy. function Init() -- User-friendly name and the description strategy:name("Highly adaptable CCI Strategy"); strategy:description(""); strategy:setTag("NonOptimizableParameters", "Email,SendEmail,SoundFile,RecurrentSound,PlaySound, ShowAlert"); -- Fast and slow moving average parameters strategy.parameters:addGroup("Selector"); strategy.parameters:addString("Action".. 1, "Overbought Level CrossOver Action", "", "NO"); strategy.parameters:addStringAlternative("Action".. 1, "No Action", "", "NO"); strategy.parameters:addStringAlternative("Action".. 1, "Sell", "", "SELL"); strategy.parameters:addStringAlternative("Action".. 1, "Buy", "", "BUY"); strategy.parameters:addStringAlternative("Action".. 1, "Close Position", "", "CLOSE"); strategy.parameters:addString("Action".. 2, "Overbought Level CrossUnder Action", "", "NO"); strategy.parameters:addStringAlternative("Action".. 2, "No Action", "", "NO"); strategy.parameters:addStringAlternative("Action".. 2, "Sell", "", "SELL"); strategy.parameters:addStringAlternative("Action".. 2, "Buy", "", "BUY"); strategy.parameters:addStringAlternative("Action".. 2, "Close Position", "", "CLOSE"); strategy.parameters:addString("Action".. 3, "Zero Line CrossOver Action", "", "NO"); strategy.parameters:addStringAlternative("Action".. 3, "No Action", "", "NO"); strategy.parameters:addStringAlternative("Action".. 3, "Sell", "", "SELL"); strategy.parameters:addStringAlternative("Action".. 3, "Buy", "", "BUY"); strategy.parameters:addStringAlternative("Action".. 3, "Close Position", "", "CLOSE"); strategy.parameters:addString("Action".. 4, "Zero Line CrossUnder Action", "", "NO"); strategy.parameters:addStringAlternative("Action".. 4, "No Action", "", "NO"); strategy.parameters:addStringAlternative("Action".. 4, "Sell", "", "SELL"); strategy.parameters:addStringAlternative("Action".. 4, "Buy", "", "BUY"); strategy.parameters:addStringAlternative("Action".. 4, "Close Position", "", "CLOSE"); strategy.parameters:addString("Action".. 5, "Oversold Level CrossOver Action", "", "NO"); strategy.parameters:addStringAlternative("Action".. 5, "No Action", "", "NO"); strategy.parameters:addStringAlternative("Action".. 5, "Sell", "", "SELL"); strategy.parameters:addStringAlternative("Action".. 5, "Buy", "", "BUY"); strategy.parameters:addStringAlternative("Action".. 5, "Close Position", "", "CLOSE"); strategy.parameters:addString("Action".. 6, "Oversold Level CrossUnder Action", "", "NO"); strategy.parameters:addStringAlternative("Action".. 6, "No Action", "", "NO"); strategy.parameters:addStringAlternative("Action".. 6, "Sell", "", "SELL"); strategy.parameters:addStringAlternative("Action".. 6, "Buy", "", "BUY"); strategy.parameters:addStringAlternative("Action".. 6, "Close Position", "", "CLOSE"); strategy.parameters:addGroup("CCI Parameter"); strategy.parameters:addInteger("CCIP", "Indicator periods", "", 14, 2, 200); strategy.parameters:addGroup("Levels"); strategy.parameters:addInteger("OB", "Overbought Level", "", 100); strategy.parameters:addInteger("OS", " Oversold Level", "", -100); -- Price subscription parameters (bid or ask price, time frame) strategy.parameters:addGroup("Price"); strategy.parameters:addString("PT", "Price Type", "", "Bid"); strategy.parameters:addStringAlternative("PT", "Bid", "", "Bid"); strategy.parameters:addStringAlternative("PT", "Ask", "", "Ask"); strategy.parameters:addString("TF", "Time Frame", "", "m1"); strategy.parameters:setFlag("TF", core.FLAG_PERIODS); strategy.parameters:addGroup("Trading Parameters"); strategy.parameters:addBoolean("AllowTrade", "Allow strategy to trade", "", false); strategy.parameters:setFlag("AllowTrade", core.FLAG_ALLOW_TRADE); strategy.parameters:addBoolean("AllowMultiple", "Allow Multiple Positions in the same direction", "", true); strategy.parameters:addInteger("MaxTrades", "Max open positions (0 == unlimited)", "", 0, 0, 100); strategy.parameters:addString("SIDE", "Allow Short/Long/Both Positions", "", "BOTH"); strategy.parameters:addStringAlternative("SIDE", "Both", "", "BOTH"); strategy.parameters:addStringAlternative("SIDE", "Short", "", "SHORT"); strategy.parameters:addStringAlternative("SIDE", "Long", "", "LONG"); 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, 100); 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("Notification"); strategy.parameters:addBoolean("ShowAlert", "Show Alert", "", false); strategy.parameters:addBoolean("PlaySound", "Play Sound", "", false); strategy.parameters:addBoolean("RecurSound", "Recurrent Sound", "", false); strategy.parameters:addFile("SoundFile", "Sound File", "", ""); strategy.parameters:setFlag("SoundFile", core.FLAG_SOUND); strategy.parameters:addBoolean("SendEmail", "Send Email", "", false); strategy.parameters:addString("Email", "Email", "", ""); strategy.parameters:setFlag("Email", core.FLAG_EMAIL); end -- The global variables local BAR = nil; -- the price history we subscribed for local first; -- the index of the oldest period where we can check whether moving averages has been crossed local PlaySound; local RecurrentSound; local SoundFile; local Email; local SendEmail; local AllowTrade; local Account; local Amount; local BaseSize; local SetLimit; local Limit; local SetStop; local Stop; local TrailingStop; local Offer; local CanClose; local MaxTrades; local SIDE; local AllowMultiple; local OB,OS; local CCI; local Action={}; -- Prepare all the data. -- The function is called once when the strategy is about to be started. function Prepare(nameOnly) -- check moving average parameters OB=instance.parameters.OB; OS= instance.parameters.OS; SIDE= instance.parameters.SIDE; AllowMultiple= instance.parameters.AllowMultiple; MaxTrades = instance.parameters.MaxTrades; local name; name = profile:id() .. ", " .. instance.bid:instrument(); CCIP= instance.parameters.CCIP; local i; for i = 1, 6, 1 do Action[i]= instance.parameters:getString ("Action" .. i); end ShowAlert = instance.parameters.ShowAlert; if ShowAlert then PlaySound = instance.parameters.PlaySound; if PlaySound then RecurrentSound = instance.parameters.RecurSound; SoundFile = instance.parameters.SoundFile; else SoundFile = nil; end assert(not(PlaySound) or (PlaySound and SoundFile ~= ""), "Sound file must be specified"); 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"); end assert(instance.parameters.TF ~= "t1", "The strategy cannot be applied on ticks."); -- check whether the strategy is allowed to trade 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 -- name the indicator instance:name(name); -- setup the signal. pay attention, we pass "ShowAlert" (value initially taken from the instance.parameters.ShowAlert) -- here, so, we don't check whether alerts are requested anymore. -- and finally subscribe for the ticks of the instrument the user initially chosen to run the strategy for to -- have our strategy activated once. BAR = ExtSubscribe(1, nil, instance.parameters.TF, instance.parameters.PT == "Bid", "bar"); CCI = core.indicators:create("CCI", BAR, CCIP); first= CCI.DATA:first(); if nameOnly then return ; end end -- the function is called every time when any subscribed price is changed. For tick subscribtions the function is called -- for every tick, for the bar subscribtions the function is called when the candle is closed (in other words, when -- the first tick of the next candle appears). function ExtUpdate(id, source, period) if AllowTrade then if not(checkReady("trades")) or not(checkReady("orders")) then return ; end end if period < first +1 then return; end CCI:update(core.UpdateLast); if core.crossesOver(CCI.DATA, OB, period)then ACTION(1); elseif core.crossesUnder(CCI.DATA, OB, period)then ACTION(2); elseif core.crossesOver(CCI.DATA, 0, period) then ACTION(3); elseif core.crossesUnder(CCI.DATA, 0, period) then ACTION(4); elseif core.crossesOver(CCI.DATA, OS, period) then ACTION(5); elseif core.crossesUnder(CCI.DATA, OS, period) then ACTION(6); end end function ACTION (Flag) if Action[Flag] == "NO" then return; elseif Action[Flag] == "BUY" then if haveTrades('B') and not AllowMultiple then if haveTrades('S') then exit('S'); Signal ("Close Short"); end return; end if SIDE == "SHORT" then if haveTrades('S') then exit('S'); Signal ("Close Short"); end return; end if AllowTrade then if haveTrades('S') then exit('S'); Signal ("Close Short"); end enter('B'); Signal ("Open Long"); else Signal ("Close Short"); Signal ("Open Long"); end elseif Action[Flag] == "SELL" then if haveTrades('S') and not AllowMultiple then if haveTrades('B') then exit('B'); Signal ("Close Long"); end return; end if SIDE == "LONG" then if haveTrades('B') then exit('B'); Signal ("Close Long"); end return; end if AllowTrade then if haveTrades('B') then exit('B'); Signal ("Close Long"); end enter('S'); Signal ("Open Short"); else Signal ("Close Long"); Signal ("Open Short"); end elseif Action[Flag] == "CLOSE" then if AllowTrade then if haveTrades('B') then exit('B'); Signal ("Close Long"); end if haveTrades('S') then exit('S'); Signal ("Close Short"); end else Signal ("Close All"); end 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 tradesCount(BuySell) local enum, row; local count = 0; enum = core.host:findTable("trades"):enumerator(); row = enum:next(); while count == 0 and 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(100, 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");