--+------------------------------------------------------------------+ --| 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() indicator:name("OM order test"); indicator:description(""); indicator.parameters:addString("Account", "Account", "", ""); indicator.parameters:setFlag("Account", core.FLAG_ACCOUNT); indicator.parameters:addBoolean("is_need_limit", "Use limit", "", false); indicator.parameters:addBoolean("peg_limit", "Use peg limit", "", true); indicator.parameters:addDouble("limit_pips", "Limit in pips", "", 30, 0, 10000); indicator.parameters:addString("limit_type", "Limit type", "", "O"); indicator.parameters:addStringAlternative("limit_type", "O", "", "O"); indicator.parameters:addStringAlternative("limit_type", "M", "", "M"); indicator.parameters:addBoolean("is_need_stop", "Use stop", "", false); indicator.parameters:addBoolean("peg_stop", "Use peg stop", "", true); indicator.parameters:addDouble("stop_pips", "Stop in pips", "", 30, 0, 10000); indicator.parameters:addString("stop_type", "Stop type", "", "O"); indicator.parameters:addStringAlternative("stop_type", "O", "", "O"); indicator.parameters:addStringAlternative("stop_type", "M", "", "M"); indicator.parameters:addBoolean("is_need_trailing", "Set trailing stop", "", true); indicator.parameters:addBoolean("is_need_dynamic_trailing", "Dynamic trailing mode", "", false); indicator.parameters:addInteger("trailing_stop", "Trailing stop", "", 10, 1, 10000); indicator.parameters:addString("output_file", "Output file", "", ""); indicator:requiredSource(core.Bar); end local Amount; local Offer; local Account; local PointSize; local Digits; local QTXT; local TRADES_UPDATE = 1; function Prepare() instance:name("OM order test"); Offer = core.host:findTable("offers"):find("Instrument", instance.source:instrument()).OfferID; PointSize = core.host:findTable("offers"):find("Instrument", instance.source:instrument()).PointSize; Digits = core.host:findTable("offers"):find("Instrument", instance.source:instrument()).Digits; Account = instance.parameters.Account; Amount = core.host:execute("getTradingProperty", "minQuantity", instance.source:instrument(), Account); QTXT = "create_orders_OM_test"; core.host:execute("subscribeTradeEvents", TRADES_UPDATE, "trades"); end local started = false; local rate; local stop_rate; local limit_rate; local trailing; local buy_sell = "B"; local is_buy = true; function Update() if not started then started = true; rate = round(instance.source.low[NOW] + 50 * PointSize, Digits); local command = createTrueMarketOrderCommand(Offer, Account, buy_sell, Amount, "IOC", QTXT); if instance.parameters.is_need_limit then if instance.parameters.peg_limit then addPipLimitToOrder(command, is_buy, instance.parameters.limit_pips, instance.parameters.limit_type); else limit_rate = instance.source.low[NOW] + instance.parameters.limit_pips * instance.source.low:pipSize(); attachLimit(command, limit_rate); end end if instance.parameters.is_need_stop then trailing = getTrailingStop(instance.parameters.is_need_trailing, instance.parameters.is_need_dynamic_trailing, instance.parameters.trailing_stop); if instance.parameters.peg_stop then addPipStopToOrder(command, is_buy, trailing, instance.parameters.stop_pips, instance.parameters.stop_type); else stop_rate = instance.source.low[NOW] + instance.parameters.stop_pips * instance.source.low:pipSize(); attachStop(command, stop_rate, trailing); end end local success, msg = terminal:execute(100, command); if not success then onTestFailed("Error on data prepare: " .. msg); end end end function getOrderSLRateType(order) if order == nil then return 0; end return order.TypeSL; end function checkResult(position) if position.BS ~= buy_sell then onTestFailed("Wrong position side"); elseif position.OfferID ~= Offer then onTestFailed("Wrong offer id"); elseif position.AmountK * 1000 ~= Amount then onTestFailed("Wrong amount"); else local can_close = core.host:execute("getTradingProperty", "canCreateMarketClose", position.Instrument, position.AccountID); local stop_order = core.host:findTable("orders"):find("OrderID", position.StopOrderID); local limit_order = core.host:findTable("orders"):find("OrderID", position.LimitOrderID); local stop_rate_type = getSLRateType(instance.parameters.is_need_stop, instance.parameters.stop_pips, instance.parameters.stop_type); local limit_rate_type = getSLRateType(instance.parameters.is_need_limit, instance.parameters.limit_pips, instance.parameters.limit_type); if can_close then stop_order_rate_type = getOrderSLRateType(stop_order); if stop_order_rate_type ~= stop_rate_type then onTestFailed("Wrong type stop position field. Expected " .. tostring(stop_rate_type) .. " but got " .. tostring(stop_order_rate_type)); return; end limit_order_rate_type = getOrderSLRateType(limit_order); if limit_order_rate_type ~= limit_rate_type then onTestFailed("Wrong type limit position field. Expected " .. tostring(limit_rate_type) .. " but got " .. tostring(limit_order_rate_type)); return; end else if stop_order ~= nil then onTestFailed("Wrong type stop position field (it shouldn't be set)"); return; end if limit_order ~= nil then onTestFailed("Wrong type limit position field (it shouldn't be set)"); return; end end if instance.parameters.is_need_stop then local stop_order; local stop_order_type; if can_close then if position.StopOrderID == nil or position.StopOrderID == "" then onTestFailed("Stop order ID should be filled"); return; end stop_order = core.host:findTable("orders"):find("OrderID", position.StopOrderID); stop_order_type = "S"; if not instance.parameters.peg_stop and position.Stop ~= stop_rate then onTestFailed("Wrong stop rate (in the position)"); return; end else if position.StopOrderID ~= nil and position.StopOrderID ~= "" then onTestFailed("Stop order ID shouldn't be filled"); return; end if instance.parameters.is_need_trailing then stop_order_type = "STE"; else stop_order_type = "SE"; end stop_order = findELSOrder(stop_order_type, QTXT); end if stop_order == nil then onTestFailed("Stop not found"); return; end if stop_order.Type ~= stop_order_type then onTestFailed("Wrong stop type"); return; end if stop_order.TypeSL ~= stop_rate_type then onTestFailed("Wrong stop rate type"); return; end if instance.parameters.is_need_trailing and stop_order.TrlMinMove ~= trailing then onTestFailed("Wrong stop trailing"); return; end if not instance.parameters.peg_stop and stop_order.Rate ~= stop_rate then onTestFailed("Wrong stop rate"); return; end end if instance.parameters.is_need_limit then local limit_order; local limit_order_type; if can_close then if position.LimitOrderID == nil or position.LimitOrderID == "" then onTestFailed("Limit order ID should be filled"); return; end limit_order = core.host:findTable("orders"):find("OrderID", position.LimitOrderID); limit_order_type = "L"; if not instance.parameters.peg_limit and position.Limit ~= limit_rate then onTestFailed("Wrong limit rate (in the position)"); return; end else if position.LimitOrderID ~= nil and position.LimitOrderID ~= "" then onTestFailed("Limit order ID shouldn't be filled"); return; end limit_order_type = "LE"; limit_order = findELSOrder(limit_order_type, QTXT); end if limit_order == nil then onTestFailed("Limit not found"); return; end if limit_order.Type ~= limit_order_type then onTestFailed("Wrong limit type"); return; end if limit_order.TypeSL ~= limit_rate_type then onTestFailed("Wrong limit rate type"); return; end if not instance.parameters.peg_limit and limit_order.Rate ~= limit_rate then onTestFailed("Wrong limit rate"); return; end end onTestSucceed(); end end function AsyncOperationFinished(cookie, success, message) if cookie == 100 then if not success then onTestFailed(message); end elseif cookie == TRADES_UPDATE then local position = findPosition(QTXT); if position ~= nil then checkResult(position); end end end ------------------------------------------------------------------------------- -- common ------------------------------------------------------------------------------- -- Attach Stop Orders to the Command -- http://www.fxcodebase.com/documents/IndicoreSDK/web-content.html?key=TradingCommandsCreateOrder_A.html function attachStop(valuemap, rateStop, trailStepStop) valuemap.RateStop = rateStop; valuemap.TrailStepStop = trailStepStop; end function attachPeggedStop(valuemap, pegTypeStop, pegPriceOffsetPipsStop, trailStepStop) valuemap.PegTypeStop = pegTypeStop; valuemap.PegPriceOffsetPipsStop = pegPriceOffsetPipsStop; valuemap.TrailStepStop = trailStepStop; end function getTrailingStop(is_need_trailing, is_need_dynamic_trailing, trailing_stop) if is_need_trailing then if is_need_dynamic_trailing then return 1; else return trailing_stop; end end return nil; end function addPipStopToOrder(order, is_buy, trail_stop, stop_pips, stop_type) local stop; if is_buy then stop = -stop_pips; else stop = stop_pips; end attachPeggedStop(order, stop_type, stop, trail_stop); end -- Attach Limit Orders to the Command -- http://www.fxcodebase.com/documents/IndicoreSDK/web-content.html?key=TradingCommandsCreateOrder_A.html function attachLimit(valuemap, rateLimit) valuemap.RateLimit = rateLimit; end function attachPeggedLimit(valuemap, pegTypeLimit, pegPriceOffsetPipsLimit) valuemap.PegTypeLimit = pegTypeLimit; valuemap.PegPriceOffsetPipsLimit = pegPriceOffsetPipsLimit; end function addPipLimitToOrder(order, is_buy, limit_pips, limit_type) local limit; if is_buy then limit = limit_pips; else limit = -limit_pips; end attachPeggedLimit(order, limit_type, limit); end -- Creates a true market order command -- http://www.fxcodebase.com/documents/IndicoreSDK/web-content.html?key=TradingCommandsCreateOrder_OM.html function createTrueMarketOrderCommand(offerId, accountId, buySell, amount, gtc, customId) local valuemap = core.valuemap(); valuemap.Command = "CreateOrder"; valuemap.OrderType = "OM"; valuemap.OfferID = offerId; valuemap.AcctID = accountId; valuemap.BuySell = buySell; valuemap.Quantity = amount; valuemap.GTC = gtc; valuemap.CustomID = customId; return valuemap; end function getSLRateType(use, pegged, open_or_market) if not use then return 0; end if not pegged then return 1; end if open_or_market == "O" then return 2; else return 3; end end function findELSOrder(order_type, qtxt) local enum = core.host:findTable("orders"):enumerator(); local row = enum:next(); while (row ~= nil) do if row.QTXT == qtxt and row.ContingencyType == 3 and row.Type == order_type then return row; end row = enum:next(); end return nil; end -- finds a position by custom id function findPosition(customID) local enum = core.host:findTable("trades"):enumerator(); local row = enum:next(); while (row ~= nil) do if row.QTXT == customID then return row; end row = enum:next(); end return nil; end -- Rounds the number to the specified precision (number of digits after the point) function round(num, digits) if digits and digits > 0 then local mult = 10 ^ digits return math.floor(num * mult + 0.5) / mult; end return math.floor(num + 0.5); end function writeToLog(str) if instance.parameters.output_file == "" then core.host:trace(str); else local f; while (f == nil) do f = io.open(instance.parameters.output_file, "a"); end f:write(str .. "\n"); f:close(); end end function onTestSucceed() writeToLog("[OK][" .. QTXT .. "]"); writeToLog("[DONE]"); --core.host:execute("stop"); end function onTestFailed(errorMessage) writeToLog("[FAIL][" .. QTXT .. "] Error: " .. tostring(errorMessage)); writeToLog("[DONE]"); --core.host:execute("stop"); end function onTestError(errorMessage) writeToLog("[ERR][" .. QTXT .. "] " .. tostring(errorMessage)); writeToLog("[DONE]"); --core.host:execute("stop"); end