-- Id: 14792 --+------------------------------------------------------------------+ --| Copyright © 2019, Gehtsoft USA LLC | --| http://fxcodebase.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 | --+------------------------------------------------------------------+ function Init() strategy:name("Breakeven All"); strategy:description(""); 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("Period", "Timeframe", "", "m1"); strategy.parameters:setFlag("Period", core.FLAG_PERIODS); strategy.parameters:addGroup("Settings"); strategy.parameters:addDouble("Profit1", "Min Profit 1 (in pips)", "", 30.0); strategy.parameters:addDouble("Profit2", "Min Profit 2 (in pips)", "", 50.0); strategy.parameters:addDouble("Profit3", "Min Profit 3 (in pips)", "", 75.0); strategy.parameters:addDouble("Profit4", "Min Profit 4 (in pips)", "", 100.0); strategy.parameters:addDouble("Profit5", "Min Profit 5 (in pips)", "", 150.0); strategy.parameters:addDouble("Profit6", "Min Profit 6 (in pips)", "", 200.0); strategy.parameters:addDouble("Gap", "Lead Gap (in pips)","", 25); strategy.parameters:addBoolean("SetTrailing", "Set Trailing", "If enabled, will set the stop to Trailing.", false); strategy.parameters:addBoolean("AllowTrade", "Allow strategy to trade", "", true); strategy.parameters:setFlag("AllowTrade", core.FLAG_ALLOW_TRADE); strategy.parameters:addString("Account", "Account", "Account to monitor open positions for.", ""); strategy.parameters:setFlag("Account", core.FLAG_ACCOUNT); end local stopOrder = nil; -- the identifier of the stop order local Source = nil; local minChange; local Profit1, Profit2, Profit3, Profit4, Profit5, Profit6; local gap; local first; local SetTrailing; local Parameters={}; local CanClose = true; local CanHedge, CanStop, CanNetStop; local Offer, Account; function Prepare(onlyName) local name; gap = instance.parameters.Gap; Profit1= instance.parameters.Profit1; Profit2= instance.parameters.Profit2; Profit3= instance.parameters.Profit3; Profit4= instance.parameters.Profit4; Profit5= instance.parameters.Profit5; Profit6= instance.parameters.Profit6; SetTrailing = instance.parameters.SetTrailing; name = profile:id() .. "(" .. instance.bid:instrument() .. "[" .. instance.parameters.Period .. "], " .. Profit1 .. ", ".. Profit2 .. ", ".. Profit3 .. ", ".. Profit4 .. ", ".. Profit5 .. ", ".. Profit6 .. ", " .. gap .. ")"; Account = instance.parameters.Account; Offer = core.host:findTable("offers"):find("Instrument", instance.bid:instrument()).OfferID; CanClose = core.host:execute("getTradingProperty", "canCreateMarketClose", instance.bid:instrument(), Account); CanHedge = core.host:findTable("accounts"):find("AccountID", Account).Hedging; CanStop = core.host:execute("getTradingProperty", "canCreateStopLimit", instance.bid:instrument(), Account); CanNetStop = core.host:execute("getTradingProperty", "canCreateNetStopLimit", instance.bid:instrument(), Account); instance:name(name); if onlyName then return ; end minChange = math.pow(10, -instance.bid:getPrecision()); ExtSetupSignal(name .. ":", true); Source = ExtSubscribe(1, nil, instance.parameters.Period, instance.parameters.Type == "Bid", "bar"); first = Source:first()-1; end function MarketOrder(BuySell, SetStop, SetLimit) local valuemap, success, msg; valuemap = core.valuemap(); valuemap.Command = "CreateOrder"; valuemap.OrderType = "OM"; valuemap.OfferID = Offer; valuemap.AcctID = Account; valuemap.Quantity = 10000; valuemap.BuySell = BuySell; -- add stop/limit valuemap.PegTypeStop = "O"; if SetStop then if BuySell == "B" then valuemap.PegPriceOffsetPipsStop = -100; else valuemap.PegPriceOffsetPipsStop = 100; end end if TrailingStop then valuemap.TrailStepStop = 1; end valuemap.PegTypeLimit = "O"; if SetLimit then if BuySell == "B" then valuemap.PegPriceOffsetPipsLimit = 50; else valuemap.PegPriceOffsetPipsLimit = -50; end end if (not CanClose) then valuemap.EntryLimitStop = "Y"; end local subject; local price; if BuySell == "B" then price = instance.ask[NOW]; subject = " Enter BUY " .. valuemap.Quantity .. "@" .. string.format("%.5f",price); else price = instance.bid[NOW]; subject = " Enter SELL " .. valuemap.Quantity .. "@" .. string.format("%.5f",price); end -- format a nice string for the email and the logs. local strEmail = subject .. " Bid: " .. string.format("%.5f",instance.bid[NOW]) .. " Ask: " .. string.format("%.5f",instance.ask[NOW]) .. " (" .. string.format("%.1f",(instance.ask[NOW] - instance.bid[NOW])/instance.ask:pipSize()) .. ")"; if valuemap.PegPriceOffsetPipsLimit ~= nil then strEmail = strEmail .. " TakeProfit (50)"; end if valuemap.PegPriceOffsetPipsStop ~= nil then strEmail = strEmail .. " Stop: (100)"; end ExtTrace("TRADE: " .. strEmail); terminal:execute(200, valuemap); return true; end function CreateNewStop(tradeRow, stopValue) -- ======================================================================= -- CREATE NEW ORDER -- -- ======================================================================= ExtTrace("Creating new stop for " .. tradeRow.BS .. ":" .. tradeRow.TradeID .. " oldValue '" .. tradeRow.Stop .. "' newValue " .. stopValue .. " open " .. tradeRow.Open); valuemap = core.valuemap(); valuemap.Command = "CreateOrder"; valuemap.OfferID = Offer; valuemap.Rate = stopValue; if tradeRow.BS == "B" then valuemap.BuySell = "S"; else valuemap.BuySell = "B"; end ExtTrace("CanClose " .. tostring(CanClose) .. " CanHedge " .. tostring(CanHedge) .. " CanStop " .. tostring(CanStop) .. " CanNetStop " .. tostring(CanNetStop)); if CanClose then valuemap.OrderType = "S"; valuemap.AcctID = tradeRow.AccountID; valuemap.TradeID = tradeRow.TradeID; valuemap.Quantity = tradeRow.Lot; else valuemap.OrderType = "SE" valuemap.AcctID = Account; -- if (CanHedge == "Y") then -- valuemap.NetQtyFlag = "Y" -- else -- We want to create a specific stop order per trade so we do not want to use NetQtyFlag. valuemap.NetQtyFlag = "N" valuemap.Quantity = tradeRow.Lot; -- end valuemap.CustomID = "MOO" .. tradeRow.TradeID; end if SetTrailing then valuemap.TrailUpdatePips = 1; end success, msg = terminal:execute(800, valuemap); if not(success) then ExtTrace("Failed create stop " .. msg); terminal:alertMessage(instance.bid:instrument(), instance.bid[NOW], "Failed create stop " .. msg, instance.bid:date(NOW)); else requestId = core.parseCsv(msg)[0] end end function UpdateExistingStop(tradeRow, stopValue) if stopValue > 0 and stopValue ~= currentStop then ExtTrace("Adjusting stop for " .. tradeRow.BS .. ":" .. tradeRow.TradeID .. " oldValue " .. tradeRow.Stop .. " newValue " .. stopValue .. " open " .. tradeRow.Open); -- this stop order needs to be adjusted to break-even point. valuemap = core.valuemap(); valuemap.Command = "EditOrder"; valuemap.OrderID = tradeRow.StopOrderID; valuemap.AcctID = tradeRow.AccountID; valuemap.Rate = stopValue; if SetTrailing then valuemap.TrailUpdatePips = 1; end success, msg = terminal:execute(900, valuemap); if not(success) then done = true; terminal:alertMessage(instance.bid:instrument(), instance.bid[NOW], "Failed change stop " .. tradeRow.TradeID .. " " .. msg, instance.bid:date(NOW)); end end end local TRADE = false; function ExtUpdate(id, source, period) if id ~= 1 then return; end if period < first then return; end if not(checkReady("trades")) or not(checkReady("orders")) or not instance.parameters.AllowTrade then return ; end -- FIFO Disabled (CanClose) -- 2013/08/11 19:03:00 Trade ID TRD0000001B OpenOrderReqID RQY00000019 StopOrderID '' LimitOrderId '' -- 2013/08/11 19:03:00 Trade ID TRD0000001F OpenOrderReqID RQY0000001D StopOrderID 'ORD00000021' LimitOrderId '' -- 2013/08/11 19:03:00 Order ID ORD00000021 RequestID RQY0000001D TradeID TRD0000001F NetQuantity false Type S BS S Stage C -- 2013/08/11 19:03:00 OrderRate 1.32271 IsEntryOrder false TypeSL 1 TypeStop 0 TypeLimit 0 OrderIdPrimary -- FIFO Enabled (Not CanClose) -- 2013/08/11 19:03:00 Trade ID TRD0000001B OpenOrderReqID RQY00000019 StopOrderID '' LimitOrderId '' -- 2013/08/11 19:03:00 Trade ID TRD0000001F OpenOrderReqID RQY0000001D StopOrderID '' LimitOrderId '' -- 2013/08/11 19:03:00 Order ID ORD00000023 RequestID RQY00000022 TradeID TRD00000024 NetQuantity false Type SE BS S Stage O -- 2013/08/11 19:03:00 OrderRate 1.32271 IsEntryOrder true TypeSL 1 TypeStop 0 TypeLimit 0 OrderIdPrimary -- first find a trade to update local enum, tradeRow; enum = core.host:findTable("trades"):enumerator(); tradeRow = enum:next(); while (tradeRow ~= nil) do local openPrice = tradeRow.Open; local bidPrice = instance.bid[NOW]; local askPrice = instance.ask[NOW]; local stopValue = 0; if tradeRow.AccountID == Account and tradeRow.OfferID == Offer and tradeRow.PL > Profit6 and string.sub(tradeRow.QTXT, 1, 3) ~= "MOO" then -- ok we have found a trade and it's profit is greater than the configured profit. -- check that it doesn't have a stop order set, or if it does, the stop is less than if (tradeRow.IsBuy) then stopValue = openPrice + (gap * instance.bid:pipSize()); elseif (not tradeRow.IsBuy) then stopValue = openPrice - (gap * instance.bid:pipSize()); end elseif tradeRow.AccountID == Account and tradeRow.OfferID == Offer and tradeRow.PL > Profit5 and string.sub(tradeRow.QTXT, 1, 3) ~= "MOO" then if (tradeRow.IsBuy) then stopValue = openPrice + (gap * instance.bid:pipSize()); elseif (not tradeRow.IsBuy) then stopValue = openPrice - (gap * instance.bid:pipSize()); end elseif tradeRow.AccountID == Account and tradeRow.OfferID == Offer and tradeRow.PL > Profit4 and string.sub(tradeRow.QTXT, 1, 3) ~= "MOO" then if (tradeRow.IsBuy) then stopValue = openPrice + (gap * instance.bid:pipSize()); elseif (not tradeRow.IsBuy) then stopValue = openPrice - (gap * instance.bid:pipSize()); end elseif tradeRow.AccountID == Account and tradeRow.OfferID == Offer and tradeRow.PL > Profit3 and string.sub(tradeRow.QTXT, 1, 3) ~= "MOO" then if (tradeRow.IsBuy) then stopValue = openPrice + (gap * instance.bid:pipSize()); elseif (not tradeRow.IsBuy) then stopValue = openPrice - (gap * instance.bid:pipSize()); end elseif tradeRow.AccountID == Account and tradeRow.OfferID == Offer and tradeRow.PL > Profit2 and string.sub(tradeRow.QTXT, 1, 3) ~= "MOO" then if (tradeRow.IsBuy) then stopValue = openPrice + (gap * instance.bid:pipSize()); elseif (not tradeRow.IsBuy) then stopValue = openPrice - (gap * instance.bid:pipSize()); end elseif tradeRow.AccountID == Account and tradeRow.OfferID == Offer and tradeRow.PL > Profit1 and string.sub(tradeRow.QTXT, 1, 3) ~= "MOO" then if (tradeRow.IsBuy) then stopValue = openPrice + (gap * instance.bid:pipSize()); elseif (not tradeRow.IsBuy) then stopValue = openPrice - (gap * instance.bid:pipSize()); end end -- ExtTrace("Trade ID " .. tradeRow.TradeID .. " OpenOrderReqID " .. tradeRow.OpenOrderReqID .. " OpenOrderID " .. tradeRow.OpenOrderID -- .. " StopOrderID '" .. tradeRow.StopOrderID .. "' LimitOrderId '" .. tradeRow.LimitOrderID .. "' Lots '" .. tradeRow.Lot .. " QTXT " .. tradeRow.QTXT); -- ExtTrace("OpenPrice " .. openPrice .. " Stop '" .. tradeRow.Stop .. "' stopValue " .. stopValue .. " gap " .. gap); if CanClose then if (tradeRow.StopOrderID ~= "" and tradeRow.StopOrderID ~= nil) then -- if we already have a stop, make sure the newStopRate is sufficiently different to the existing StopRate if math.abs(stopValue - tradeRow.Stop) > minChange then UpdateExistingStop(tradeRow, stopValue); end else -- create a new stop order. CreateNewStop(tradeRow, stopValue); end else -- search for a stopOrder with the QTXT set appropriately local orderId; local orderEnum, orderRow; orderEnum = core.host:findTable("orders"):enumerator(); orderRow = orderEnum:next(); local found = false; local uniqueID = "MOO" .. tradeRow.TradeID; while (orderRow ~= nil) do -- ExtTrace("Order ID " .. orderRow.OrderID .. " RequestID " .. orderRow.RequestID .. " TradeID " .. orderRow.TradeID .. -- " NetQuantity " .. tostring(orderRow.NetQuantity) .. " Lots " .. orderRow.Lot .. " Type " .. orderRow.Type .. " BS " .. orderRow.BS .. " Stage " .. orderRow.Stage); -- ExtTrace("OrderRate " .. orderRow.Rate .. " IsEntryOrder " .. tostring(orderRow.IsEntryOrder) .. " TypeSL " .. orderRow.TypeSL .. -- " TypeStop " .. orderRow.TypeStop .. " TypeLimit " .. orderRow.TypeLimit .. " OrderIdPrimary '" -- .. orderRow.OrderIdPrimary .. "' ContingencyID '" .. orderRow.ContingencyID .. "' QTXT " .. orderRow.QTXT .. -- " TrlMinMove " .. orderRow.TrlMinMove .. " Distance " .. orderRow.Distance); if orderRow.OfferID == Offer and orderRow.AccountID == Account and orderRow.QTXT == uniqueID then found = true; break; end orderRow = orderEnum:next(); end if (not found) then -- if found, ignore it -- if not found, assume there isn't a stop entry order for this trade, create a new one with the correct QTXT CreateNewStop(tradeRow, stopValue); end end end tradeRow = enum:next(); end function ExtAsyncOperationFinished(id, success, message) if id == 800 then if not(success) then ExtTrace("Failed create stop order " .. message); terminal:alertMessage(instance.bid:instrument(), instance.bid[NOW], "Failed create stop order " .. message , instance.bid:date(NOW)); end elseif id == 900 then if not(success) then ExtTrace("Failed adjust stop order " .. message); terminal:alertMessage(instance.bid:instrument(), instance.bid[NOW], "Failed adjust stop order " .. message , instance.bid:date(NOW)); end end end function checkReady(table) return core.host:execute("isTableFilled", table); end -- --------------------------------------------------------- -- Trace helper function -- --------------------------------------------------------- function ExtTrace(message) local theDate = core.dateToTable(core.host:execute("convertTime", 1, 4, math.max(instance.bid:date(instance.bid:size() - 1), instance.ask:date(instance.ask:size() - 1)))); core.host:trace(string.format("%d/%02d/%02d %02d:%02d:%02d ", theDate.year, theDate.month, theDate.day, theDate.hour, theDate.min, theDate.sec) .. " " .. message); end dofile(core.app_path() .. "\\strategies\\standard\\include\\helper.lua");