-- More information about this indicator can be found at: -- http://fxcodebase.com/code/viewtopic.php?f=31&t=70198 --+------------------------------------------------------------------+ --| Copyright © 2020, Gehtsoft USA LLC | --| http://fxcodebase.com | --+------------------------------------------------------------------+ --| Support our efforts by donating | --| Paypal: https://goo.gl/9Rj74e | --+------------------------------------------------------------------+ --| Developed by : Mario Jemic | --| mario.jemic@gmail.com | --| https://AppliedMachineLearning.systems | --| Patreon : https://goo.gl/GdXWeN | --+------------------------------------------------------------------+ local main_source; local actions = {}; local TriggerSource, PivotSource local Throttle local Pips = {} local CustomID local CalcMode local O_PIVOT = 1 local O_CAM = 2 local O_WOOD = 3 local O_FIB = 4 local O_FLOOR = 5 local O_FIBR = 6 local PeriodDate local P local S1 local S2 local S3 local S4 local R1 local R2 local R3 local R4 local MS01, MS12, MS23, MS34, MR01, MR12, MR23, MR34 local add_log, log_file; local headers = {}; local labels = { "R4", "R4", "MR34", "MR34", "R3", "R3", "MR23", "MR23", "R2", "R2", "MR12", "MR12", "R1", "R1", "MR01", "MR01", "P", "P", "MS01", "MS01", "S1", "S1", "MS12", "MS12", "S2", "S2", "MS23", "MS23", "S3", "S3", "MS34", "MS34", "S4", "S4" }; local check_margin; function MargingPass() if not check_margin then return true; end local Amount = instance.parameters:getInteger("Amount"); local account = core.host:findTable("accounts"):find("AccountID", instance.parameters.Account) local MMR = core.host:execute("getTradingProperty", "MMR", instance.bid:instrument(), instance.parameters.Account) local full_margin = account.UsableMargin + account.UsedMargin; return (account.UsableMargin - MMR * Amount) / full_margin >= (instance.parameters.min_magrin / 100.0); end function CalcLevels(ref, period) if (period - 1 < ref:first()) then -- not enough data return end if (ref:serial(period) == INDEX) then -- already calculated; return end -- if we are daily or less, ignore saturdays. if (ref:barSize() ~= "M1" and ref:barSize() ~= "W1") then offset = core.host:execute("getTradingDayOffset") if (core.isnontrading(ref:date(period), offset)) then return end end local prev_i = period INDEX = ref:serial(period) -- calculate the pivot level if CalcMode == O_PIVOT or CalcMode == O_FIB or CalcMode == O_FLOOR then P = (ref.high[prev_i] + ref.close[prev_i] + ref.low[prev_i]) / 3 elseif CalcMode == O_CAM then P = ref.close[prev_i] elseif CalcMode == O_FIBR then P = (ref.high[prev_i] + ref.low[prev_i]) / 2 elseif CalcMode == O_WOOD then local open if (prev_i == ref:size() - 1) then -- for a live day take close as open of the next period open = ref.open[prev_i] else open = ref.open[prev_i + 1] end P = (ref.high[prev_i] + ref.low[prev_i] + open * 2) / 4 end local h, l, r, p h = ref.high[prev_i] l = ref.low[prev_i] r = h - l p = P local values = {}; values["P"] = P; -- calculate the other levels if CalcMode == O_PIVOT then values["R4"] = p + r * 3 values["R3"] = p + r * 2 values["R2"] = p + r values["R1"] = p * 2 - l values["S1"] = p * 2 - h values["S2"] = p - r values["S3"] = p - r * 2 values["S4"] = p - r * 3 elseif CalcMode == O_CAM then values["R4"] = p + r * 1.1 / 2 values["R3"] = p + r * 1.1 / 4 values["R2"] = p + r * 1.1 / 6 values["R1"] = p + r * 1.1 / 12 values["S1"] = p - r * 1.1 / 12 values["S2"] = p - r * 1.1 / 6 values["S3"] = p - r * 1.1 / 4 values["S4"] = p - r * 1.1 / 2 elseif CalcMode == O_WOOD then values["R4"] = h + (2 * (p - l) + r) values["R3"] = h + 2 * (p - l) values["R2"] = p + r values["R1"] = p * 2 - l values["S1"] = p * 2 - h values["S2"] = p - r values["S3"] = l - 2 * (h - p) values["S4"] = l - (r + 2 * (h - p)) elseif CalcMode == O_FIB then values["R4"] = p + 1.618 * (h - l) values["R3"] = p + 1 * (h - l) values["R2"] = p + 0.618 * (h - l) values["R1"] = p + 0.382 * (h - l) values["S1"] = p - 0.382 * (h - l) values["S2"] = p - 0.618 * (h - l) values["S3"] = p - 1 * (h - l) values["S4"] = p - 1.618 * (h - l) elseif CalcMode == O_FLOOR then values["R4"] = 0 values["R3"] = h + (p - l) * 2 values["R2"] = p + r values["R1"] = p * 2 - l values["S1"] = p * 2 - h values["S2"] = p - r values["S3"] = l - (h - p) * 2 values["S4"] = 0 elseif CalcMode == O_FIBR then values["R4"] = l + (h - l) * 1.272 values["R3"] = l + (h - l) * 1.0 values["R2"] = l + (h - l) * 0.764 values["R1"] = l + (h - l) * 0.618 values["S1"] = l + (h - l) * 0.382 values["S2"] = l + (h - l) * 0.236 values["S3"] = l + (h - l) * 0.0 values["S4"] = l + (h - l) * -0.272 end -- calculate mid points values["MR01"] = 0.5 * (values["P"] + values["R1"]) values["MR12"] = 0.5 * (values["R1"] + values["R2"]) values["MR23"] = 0.5 * (values["R2"] + values["R3"]) values["MR34"] = 0.5 * (values["R3"] + values["R4"]) values["MS01"] = 0.5 * (values["P"] + values["S1"]) values["MS12"] = 0.5 * (values["S1"] + values["S2"]) values["MS23"] = 0.5 * (values["S2"] + values["S3"]) values["MS34"] = 0.5 * (values["S3"] + values["S4"]) for i, level in ipairs(actions) do level.value = values[level.label] + level.pips * PivotSource:pipSize(); level.stop_value = values[level.stop]; level.limit_value = values[level.limit]; end end local lastTradeTime = 0 local throttleCount = 0 function CheckThrottle() if (Throttle > 0) then -- if we have to check the throttle. local now = core.host:execute("getServerTime") local diff = now - lastTradeTime if (diff > 1) then -- throttle exceeded, reset. lastTradeTime = math.floor(now) throttleCount = 1 return true end -- we are still within the throttle window. Make sure we haven't exceeded the throttle. if (throttleCount < Throttle) then throttleCount = throttleCount + 1 return true end -- hit the throttle already. return false end -- throttle disabled. return true end -- START OF USER DEFINED SECTION local EntryActions = {}; local ExitActions = {}; -- START OF USER DEFINED SECTION local STRATEGY_NAME = "Highly Adaptable Pivot Strategy"; function CreateParameters() strategy.parameters:addInteger( "Throttle", "Position Throttle", "The max number of positions that can be opened within each day. Zero for unlimited.", 0, 0, 1000 ) strategy.parameters:addGroup("Pivot") strategy.parameters:addString("PivotTF", "Pivot Time frame", "", "D1") strategy.parameters:setFlag("PivotTF", core.FLAG_PERIODS) strategy.parameters:addString("CalcMode", "Calculation mode", "The mode of pivot calculation.", "Pivot") strategy.parameters:addStringAlternative("CalcMode", "Classic Pivot", "", "Pivot") strategy.parameters:addStringAlternative("CalcMode", "Camarilla", "", "Camarilla") strategy.parameters:addStringAlternative("CalcMode", "Woodie", "", "Woodie") strategy.parameters:addStringAlternative("CalcMode", "Fibonacci", "", "Fibonacci") strategy.parameters:addStringAlternative("CalcMode", "Floor", "", "Floor") strategy.parameters:addStringAlternative("CalcMode", "Fibonacci Retracement", "", "FibonacciR") strategy.parameters:addGroup("Actions") AddLevel(1, "R4") AddLevel(2, "R4 #2") AddLevel(3, "MR34") AddLevel(4, "MR34 #2") AddLevel(5, "R3") AddLevel(6, "R3 #2") AddLevel(7, "MR23") AddLevel(8, "MR23 #2") AddLevel(9, "R2") AddLevel(10, "R2 #2") AddLevel(11, "MR12") AddLevel(12, "MR12 #2") AddLevel(13, "R1") AddLevel(14, "R1 #2") AddLevel(15, "MR01") AddLevel(16, "MR01 #2") AddLevel(17, "P") AddLevel(18, "P #2") AddLevel(19, "MS01") AddLevel(20, "MS01 #2") AddLevel(21, "S1") AddLevel(22, "S1 #2") AddLevel(23, "MS12") AddLevel(24, "MS12 #2") AddLevel(25, "S2") AddLevel(26, "S2 #2") AddLevel(27, "MS23") AddLevel(28, "MS23 #2") AddLevel(29, "S3") AddLevel(30, "S3 #2") AddLevel(31, "MS34") AddLevel(32, "MS34 #2") AddLevel(33, "S4") AddLevel(34, "S4 #2") strategy.parameters:addBoolean("check_margin", "Perform margin care", "", true); strategy.parameters:addDouble("min_magrin", "Min margin, %", "", 0) end function AddOption(name) strategy.parameters:addStringAlternative(name, "R4", "", "R4"); strategy.parameters:addStringAlternative(name, "MR34", "", "MR34"); strategy.parameters:addStringAlternative(name, "R3", "", "R3"); strategy.parameters:addStringAlternative(name, "MR23", "", "MR23"); strategy.parameters:addStringAlternative(name, "R2", "", "R2"); strategy.parameters:addStringAlternative(name, "MR12", "", "MR12"); strategy.parameters:addStringAlternative(name, "R1", "", "R1"); strategy.parameters:addStringAlternative(name, "MR01", "", "MR01"); strategy.parameters:addStringAlternative(name, "P", "", "P"); strategy.parameters:addStringAlternative(name, "MS01", "", "MS01"); strategy.parameters:addStringAlternative(name, "S1", "", "S1"); strategy.parameters:addStringAlternative(name, "MS12", "", "MS12"); strategy.parameters:addStringAlternative(name, "S2", "", "S2"); strategy.parameters:addStringAlternative(name, "MS23", "", "MS23"); strategy.parameters:addStringAlternative(name, "S3", "", "S3"); strategy.parameters:addStringAlternative(name, "MS34", "", "MS34"); strategy.parameters:addStringAlternative(name, "S4", "", "S4"); end function AddLevel(Number, Desc) strategy.parameters:addString("Action" .. Number, Desc .. " Action", "", "NO") strategy.parameters:addStringAlternative("Action" .. Number, "No Action", "", "NO") strategy.parameters:addStringAlternative("Action" .. Number, "Sell", "", "SELL") strategy.parameters:addStringAlternative("Action" .. Number, "Buy", "", "BUY") strategy.parameters:addStringAlternative("Action" .. Number, "Close Position", "", "CLOSE") strategy.parameters:addStringAlternative("Action" .. Number, "Alert", "", "Alert") strategy.parameters:addDouble("Pips" .. Number, Desc .. " Pips", "", 0, -1000.0, 1000.0) strategy.parameters:addString("stop" .. Number, Desc .. " Stop", "", "no"); strategy.parameters:addStringAlternative("stop" .. Number, "No Stop", "", "no"); AddOption("stop" .. Number) strategy.parameters:addString("limit" .. Number, Desc .. " Limit", "", "no"); strategy.parameters:addStringAlternative("limit" .. Number, "No Limit", "", "no"); AddOption("limit" .. Number) end function CreateEntryIndicators(source) Throttle = instance.parameters.Throttle; check_margin = instance.parameters.check_margin; if instance.parameters.CalcMode == "Pivot" then CalcMode = O_PIVOT elseif instance.parameters.CalcMode == "Camarilla" then CalcMode = O_CAM elseif instance.parameters.CalcMode == "Woodie" then CalcMode = O_WOOD elseif instance.parameters.CalcMode == "Fibonacci" then CalcMode = O_FIB elseif instance.parameters.CalcMode == "Floor" then CalcMode = O_FLOOR elseif instance.parameters.CalcMode == "FibonacciR" then CalcMode = O_FIBR else assert(false, "Unknown calculation mode: " .. instance.parameters.CalcMode) end assert(instance.parameters.PivotTF ~= "t1", "The pivot time frame must not be tick") PivotSource = ExtSubscribe(3, nil, instance.parameters.PivotTF, instance.parameters.Type == "Bid", "bar") PipSize = PivotSource.close:pipSize() for i = 1, 34 do actions[i] = { pips = instance.parameters:getDouble("Pips" .. i) * PipSize; stop = instance.parameters:getString("stop" .. i); limit = instance.parameters:getString("limit" .. i); label = labels[i]; IsPass = function (period, action) return action.value ~= nil and core.crosses(main_source.close, action.value, period) and CheckThrottle() end }; end end function UpdateIndicators() CalcLevels(PivotSource, PivotSource:size() - 2); end -- END OF USER DEFINED SECTION function AddAction(id, name) strategy.parameters:addString("Action" .. id, name, "", "NO") strategy.parameters:addStringAlternative("Action" .. id, "No Action", "", "NO") strategy.parameters:addStringAlternative("Action" .. id, "Sell", "", "SELL") strategy.parameters:addStringAlternative("Action" .. id, "Buy", "", "BUY") strategy.parameters:addStringAlternative("Action" .. id, "Close Position", "", "CLOSE") end function Init() strategy:name(STRATEGY_NAME); strategy:description(""); strategy:type(core.Both); strategy:setTag("NonOptimizableParameters", "StartTime,StopTime,ToTime,signaler_ToTime,signaler_show_alert,signaler_play_soundsignaler_sound_file,signaler_recurrent_sound,signaler_send_email,signaler_email,signaler_show_popup,signaler_debug_alert,use_advanced_alert,advanced_alert_key"); strategy.parameters:addBoolean("type", "Price Type", "", true); strategy.parameters:setFlag("type", core.FLAG_BIDASK); strategy.parameters:addString("timeframe", "Timeframe", "", "m1"); strategy.parameters:setFlag("timeframe", core.FLAG_PERIODS); strategy.parameters:addBoolean("AllowTrade", "Allow strategy to trade", "", false); 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:addString("entry_execution_type", "Execution Type", "Once per bar close or on every tick", "Live"); strategy.parameters:addStringAlternative("entry_execution_type", "End of Turn", "", "EndOfTurn"); strategy.parameters:addStringAlternative("entry_execution_type", "Live", "", "Live"); strategy.parameters:addString("Account", "Account to trade on", "", ""); strategy.parameters:setFlag("Account", core.FLAG_ACCOUNT); strategy.parameters:addString("amount_type", "Amount Units", "", "lots"); strategy.parameters:addStringAlternative("amount_type", "Lots", "", "lots"); strategy.parameters:addStringAlternative("amount_type", "% of equity", "", "equity"); strategy.parameters:addDouble("Amount", "Trade Amount", "", 1, 1, 1000000); strategy.parameters:addGroup("Money Management"); strategy.parameters:addBoolean("use_position_limit", "Use Position limit", "", true); strategy.parameters:addInteger("position_limit", "Limit", "", 1); strategy.parameters:addBoolean("close_on_opposite", "Close on opposite", "", true); strategy.parameters:addString("custom_id", "Custom ID", "", STRATEGY_NAME); CreateParameters(); strategy.parameters:addGroup("Trading time"); 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("use_mandatory_closing", "Use Mandatory Closing", "", false); strategy.parameters:addString("mandatory_closing_exit_time", "Mandatory Closing Time", "", "23:59:59"); strategy.parameters:addInteger("mandatory_closing_valid_interval", "Valid Interval for Operation, in second", "", 60); strategy.parameters:addGroup("Alerts"); strategy.parameters:addInteger("signaler_ToTime", "Convert the date to", "", 6) strategy.parameters:addIntegerAlternative("signaler_ToTime", "EST", "", 1) strategy.parameters:addIntegerAlternative("signaler_ToTime", "UTC", "", 2) strategy.parameters:addIntegerAlternative("signaler_ToTime", "Local", "", 3) strategy.parameters:addIntegerAlternative("signaler_ToTime", "Server", "", 4) strategy.parameters:addIntegerAlternative("signaler_ToTime", "Financial", "", 5) strategy.parameters:addIntegerAlternative("signaler_ToTime", "Display", "", 6) strategy.parameters:addBoolean("signaler_show_alert", "Show Alert", "", true); strategy.parameters:addBoolean("signaler_play_sound", "Play Sound", "", false); strategy.parameters:addFile("signaler_sound_file", "Sound File", "", ""); strategy.parameters:setFlag("signaler_sound_file", core.FLAG_SOUND); strategy.parameters:addBoolean("signaler_recurrent_sound", "Recurrent Sound", "", true); strategy.parameters:addBoolean("signaler_send_email", "Send Email", "", false); strategy.parameters:addString("signaler_email", "Email", "", ""); strategy.parameters:setFlag("signaler_email", core.FLAG_EMAIL); end local MAIN_SOURCE_ID = 1; local TICK_SOURCE_ID = 2; local MANDATORY_CLOSE_TIMER_ID = 3; local entry_source_id; local base_size, offer_id, Account, Amount, amount_type, AllowTrade, close_on_opposite, custom_id, AllowedSide; local use_stop, stop_pips, use_limit, limit_pips, entry_execution_type, use_trailing, trailing, use_position_limit, position_limit; local _show_alert, _sound_file, _recurrent_sound, _email; local _ToTime, OpenTime, CloseTime; local use_mandatory_closing, exit_time; function Prepare(nameOnly) local name = profile:id() .. "(" .. instance.bid:name() .. ")"; instance:name(name); if nameOnly then return; end use_mandatory_closing = instance.parameters.use_mandatory_closing; use_position_limit = instance.parameters.use_position_limit; position_limit = instance.parameters.position_limit; use_trailing = instance.parameters.use_trailing; trailing = instance.parameters.trailing; AllowedSide = instance.parameters.AllowedSide; entry_execution_type = instance.parameters.entry_execution_type; limit_pips = instance.parameters.limit_pips; use_limit = instance.parameters.use_limit; use_stop = instance.parameters.use_stop; stop_pips = instance.parameters.stop_pips; AllowTrade = instance.parameters.AllowTrade; Account = instance.parameters.Account; Amount = instance.parameters.Amount; amount_type = instance.parameters.amount_type; close_on_opposite = instance.parameters.close_on_opposite; custom_id = instance.parameters.custom_id; main_source = ExtSubscribe(MAIN_SOURCE_ID, nil, instance.parameters.timeframe, instance.parameters.type, "bar") if entry_execution_type == "Live" then tick_source = ExtSubscribe(TICK_SOURCE_ID, nil, "t1", instance.parameters.type, "bar"); entry_source_id = TICK_SOURCE_ID; else entry_source_id = MAIN_SOURCE_ID; end CreateEntryIndicators(main_source); base_size = core.host:execute("getTradingProperty", "baseUnitSize", instance.bid:instrument(), Account); offer_id = core.host:findTable("offers"):find("Instrument", instance.bid:instrument()).OfferID; 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"); _ToTime = instance.parameters.signaler_ToTime 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 if instance.parameters.signaler_play_sound then _sound_file = instance.parameters.signaler_sound_file; assert(_sound_file ~= "", "Sound file must be chosen"); end _show_alert = instance.parameters.signaler_show_alert; _recurrent_sound = instance.parameters.signaler_recurrent_sound; if instance.parameters.signaler_send_email then _email = instance.parameters.signaler_email; assert(_email ~= "", "E-mail address must be specified"); end if use_mandatory_closing then exit_time, valid = ParseTime(instance.parameters.mandatory_closing_exit_time); assert(valid, "Time " .. instance.parameters.mandatory_closing_exit_time .. " is invalid"); core.host:execute("setTimer", MANDATORY_CLOSE_TIMER_ID, math.max(instance.parameters.mandatory_closing_valid_interval / 2, 1)); end 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 true; end 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 local last_entry, last_exit; function DoBuy(stop, limit) local now = core.host:execute("convertTime", core.TZ_EST, _ToTime, core.host:execute("getServerTime")); now = now - math.floor(now); if not InRange(now, OpenTime, CloseTime) then return; end if last_entry ~= main_source:date(NOW) and not PositionsLimitHit() then if AllowTrade then if close_on_opposite then CloseTrades("S"); end OpenTrade("B", stop, limit); end Signal("Entry long", main_source); last_entry = main_source:date(NOW); end end function DoSell(stop, limit) local now = core.host:execute("convertTime", core.TZ_EST, _ToTime, core.host:execute("getServerTime")); now = now - math.floor(now); if not InRange(now, OpenTime, CloseTime) then return; end if last_entry ~= main_source:date(NOW) and not PositionsLimitHit() then if AllowTrade then if close_on_opposite then CloseTrades("B"); end OpenTrade("S", stop, limit); end Signal("Entry short", main_source); last_entry = main_source:date(NOW); end end function DoClose() if last_exit ~= main_source:date(NOW) then if AllowTrade then CloseTrades("B"); CloseTrades("S"); end Signal("Exit", main_source); last_exit = main_source:date(NOW); end end function ExtUpdate(id, source, period) if use_mandatory_closing and core.host.Trading:getTradingProperty("isSimulation") then DoMandatoryClosing(); end if id ~= entry_source_id then return; end local entry_period; if entry_execution_type == "Live" then entry_period = main_source:size() - 1; else entry_period = period; end UpdateIndicators(); for i, action in ipairs(actions) do if action.IsPass(entry_period, action) then local actionType = instance.parameters:getString("Action" .. i); if actionType == "SELL" then DoSell(action.stop_value, action.limit_value); elseif actionType == "BUY" then DoBuy(action.stop_value, action.limit_value); elseif actionType == "CLOSE" then DoClose(); end end end end function DoMandatoryClosing() if not use_mandatory_closing then return; end local now = core.host:execute("convertTime", core.TZ_EST, _ToTime, core.host:execute("getServerTime")); now = now - math.floor(now); if InRange(now, exit_time, exit_time + (instance.parameters.mandatory_closing_valid_interval / 86400.0)) then CloseTrades("B"); CloseTrades("S"); DeleteOrders(); end end function PositionsLimitHit() if not use_position_limit then return false; end local enum = core.host:findTable("trades"):enumerator(); local row = enum:next(); local count = 0; while row ~= nil do if row.BS == side and row.Instrument == main_source:instrument() and (row.QTXT == custom_id or custom_id == "") then count = count + 1; end row = enum:next(); end local enum = core.host:findTable("orders"):enumerator(); local row = enum:next(); while row ~= nil do if row.BS == side and row.Instrument == main_source:instrument() and (row.QTXT == custom_id or custom_id == "") then count = count + 1; end row = enum:next(); end return count >= position_limit; end function DeleteOrders() local enum = core.host:findTable("orders"):enumerator() local row = enum:next() while row ~= nil do if row.AccountID == Account and row.Instrument == main_source:instrument() then local valuemap = core.valuemap() valuemap.Command = "DeleteOrder" valuemap.OrderID = row.OrderID success, msg = terminal:execute(4, valuemap) if not (success) then terminal:alertMessage( instance.bid:instrument(), instance.bid[NOW], "Failed delete order " .. row.OrderID .. ":" .. msg, instance.bid:date(NOW) ) end end row = enum:next() end end function CloseTrades(side) local enum = core.host:findTable("trades"):enumerator(); local row = enum:next(); while row ~= nil do if row.BS == side and row.Instrument == main_source:instrument() and (row.QTXT == custom_id or custom_id == "") then CloseTrade(row); end row = enum:next(); end end function ExtAsyncOperationFinished(cookie, success, message, message1, message2) if cookie == MANDATORY_CLOSE_TIMER_ID then DoMandatoryClosing(); end end function OpenTrade(side, stop, limit) if AllowedSide ~= "Both" then if AllowedSide == "Buy" and side == "S" then return; end if AllowedSide == "Sell" and side == "B" then return; end end local valuemap = core.valuemap(); valuemap.OrderType = "OM"; valuemap.OfferID = offer_id; valuemap.AcctID = Account; if amount_type == "lots" then valuemap.Quantity = Amount * base_size; else local equity = core.host:findTable("accounts"):find("AccountID", valuemap.AcctID).Equity; local used_equity = equity * Amount / 100.0; local emr = core.host:getTradingProperty("EMR", instance.bid:instrument(), valuemap.AcctID); valuemap.Quantity = math.floor(used_equity / emr) * base_size; end valuemap.BuySell = side; valuemap.CustomID = custom_id; valuemap.RateStop = stop; valuemap.RateLimit = limit; local success, msg = terminal:execute(4, valuemap); end function CloseTrade(trade) local valuemap = core.valuemap(); valuemap.BuySell = trade.BS == "B" and "S" or "B"; valuemap.OrderType = "CM"; valuemap.OfferID = trade.OfferID; valuemap.AcctID = trade.AccountID; valuemap.TradeID = trade.TradeID; valuemap.Quantity = trade.Lot; local success, msg = terminal:execute(2, valuemap); end function FormatEmail(source, period, message) --format email subject local subject = message .. "(" .. source:instrument() .. ")"; --format email text local delim = "\013\010"; local signalDescr = "Signal: " .. (STRATEGY_NAME or ""); local symbolDescr = "Symbol: " .. source:instrument(); local messageDescr = "Message: " .. message; local ttime = core.dateToTable(core.host:execute("convertTime", core.TZ_EST, _ToTime, source:date(period))); local dateDescr = string.format("Time: %02i/%02i %02i:%02i", ttime.month, ttime.day, ttime.hour, ttime.min); local priceDescr = "Price: " .. source[period]; local text = "You have received this message because the following signal alert was received:" .. delim .. signalDescr .. delim .. symbolDescr .. delim .. messageDescr .. delim .. dateDescr .. delim .. priceDescr; return subject, text; end function Signal(message, source) if source == nil then if instance.source ~= nil then source = instance.source; elseif instance.bid ~= nil then source = instance.bid; else local pane = core.host.Window.CurrentPane; source = pane.Data:getStream(0); end end if _show_alert then terminal:alertMessage(source:instrument(), source[NOW], message, source:date(NOW)); end if _sound_file ~= nil then terminal:alertSound(_sound_file, _recurrent_sound); end if _email ~= nil then terminal:alertEmail(_email, profile:id().. " : " .. message, FormatEmail(source, NOW, message)); end end dofile(core.app_path() .. "\\strategies\\standard\\include\\helper.lua");