----------------------------------------------------------- -- zig_output_channel_strat ----------------------------------------------------------- local _gSubscription = {}; local _gUpdatePeriods = {}; local _gLastTime; ----------------------------------------------------------- -- Standard strategy init handler for marketscope strategy ----------------------------------------------------------- function Init() strategy:name("zig_output_channel_strat"); strategy:type(core.Both); strategy.parameters:addString("Account", "Account", "", ""); strategy.parameters:setFlag("Account", core.FLAG_ACCOUNT); strategy.parameters:addBoolean("AllowTrade", "Allow 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:addBoolean("AllowMultiplePositions", "Allow multiple positions", "", true); strategy.parameters:addDouble("take_profit", "take Profit", "", 2); strategy.parameters:addInteger("max_position_oneway", "max Position Oneway", "", 10); strategy.parameters:addInteger("lots", "lots", "", 1); strategy.parameters:addInteger("bb_m15_period", "bb M15 Period", "", 35); strategy.parameters:addInteger("bb_m1_period", "bb M1 Period", "", 20); strategy.parameters:addDouble("bb_m15_deviation", "bb M15 Deviation", "", 1.61803); strategy.parameters:addDouble("bb_m1_deviation", "bb M1 Deviation", "", 2); strategy.parameters:addGroup("zig_m1_bid_1 parameters") strategy.parameters:addInteger("zig_m1_bid_1_Depth", "Depth", "the minimal amount of bars where there will not be the second maximum", 21); strategy.parameters:addInteger("zig_m1_bid_1_Deviation", "Deviation", "Distance in pips to eliminate the second maximum in the last Depth periods", 8); strategy.parameters:addInteger("zig_m1_bid_1_Backstep", "Backstep", "The minimal amount of bars between maximums/minimums", 5); strategy.parameters:addInteger("zig_m1_bid_1_Period", "Period", "Period", 1); strategy.parameters:addGroup("zig_m1_ask_1 parameters") strategy.parameters:addInteger("zig_m1_ask_1_Depth", "Depth", "the minimal amount of bars where there will not be the second maximum", 21); strategy.parameters:addInteger("zig_m1_ask_1_Deviation", "Deviation", "Distance in pips to eliminate the second maximum in the last Depth periods", 8); strategy.parameters:addInteger("zig_m1_ask_1_Backstep", "Backstep", "The minimal amount of bars between maximums/minimums", 5); strategy.parameters:addInteger("zig_m1_ask_1_Period", "Period", "Period", 1); strategy.parameters:addGroup("BB_m1_bid parameters") strategy.parameters:addBoolean("BB_m1_bid_HideAve", "Hide average line", "Defines whether the BB average line is hidden.", false); strategy.parameters:addGroup("BB_m1_ask parameters") strategy.parameters:addBoolean("BB_m1_ask_HideAve", "Hide average line", "Defines whether the BB average line is hidden.", false); strategy.parameters:addGroup("BB_m15_bid parameters") strategy.parameters:addBoolean("BB_m15_bid_HideAve", "Hide average line", "Defines whether the BB average line is hidden.", false); strategy.parameters:addGroup("BB_m15_ask parameters") strategy.parameters:addBoolean("BB_m15_ask_HideAve", "Hide average line", "Defines whether the BB average line is hidden.", false); strategy.parameters:addGroup("zig_m1_bid_2 parameters") strategy.parameters:addInteger("zig_m1_bid_2_Depth", "Depth", "the minimal amount of bars where there will not be the second maximum", 21); strategy.parameters:addInteger("zig_m1_bid_2_Deviation", "Deviation", "Distance in pips to eliminate the second maximum in the last Depth periods", 8); strategy.parameters:addInteger("zig_m1_bid_2_Backstep", "Backstep", "The minimal amount of bars between maximums/minimums", 5); strategy.parameters:addInteger("zig_m1_bid_2_Period", "Period", "Period", 2); strategy.parameters:addGroup("zig_m1_ask_2 parameters") strategy.parameters:addInteger("zig_m1_ask_2_Depth", "Depth", "the minimal amount of bars where there will not be the second maximum", 21); strategy.parameters:addInteger("zig_m1_ask_2_Deviation", "Deviation", "Distance in pips to eliminate the second maximum in the last Depth periods", 8); strategy.parameters:addInteger("zig_m1_ask_2_Backstep", "Backstep", "The minimal amount of bars between maximums/minimums", 5); strategy.parameters:addInteger("zig_m1_ask_2_Period", "Period", "Period", 2); end local mCID= "FXSW_STRATEGY"; local mAccount; local mLotSize; local mAllowTrade = false; local mAllowedSide = "Both"; local mPlaySound = false; local mReccurentSound = false; local mSendEmail = false; local mShowAlert = false; local mEmail; local mAllowMultiplePositions = true; local t1 = "t1"; local m1 = "m1"; local m15 = "m15"; local symbol; local sourcem1_bid; local id_sourcem1_bid = 101; local sourcem1_ask; local id_sourcem1_ask = 102; local sourcet1_bid; local id_sourcet1_bid = 103; local sourcet1_ask; local id_sourcet1_ask = 104; local sourcem15_bid; local id_sourcem15_bid = 105; local sourcem15_ask; local id_sourcem15_ask = 106; local take_profit; local max_position_oneway; local lots; local bb_m15_period; local bb_m1_period; local bb_m15_deviation; local bb_m1_deviation; local zig_m1_bid_1_Depth; local zig_m1_bid_1_Deviation; local zig_m1_bid_1_Backstep; local zig_m1_bid_1_Period; local zig_m1_bid_1; local zig_m1_ask_1_Depth; local zig_m1_ask_1_Deviation; local zig_m1_ask_1_Backstep; local zig_m1_ask_1_Period; local zig_m1_ask_1; local BB_m1_bid_N; local BB_m1_bid_Dev; local BB_m1_bid_HideAve; local BB_m1_bid; local BB_m1_ask_N; local BB_m1_ask_Dev; local BB_m1_ask_HideAve; local BB_m1_ask; local BB_m15_bid_N; local BB_m15_bid_Dev; local BB_m15_bid_HideAve; local BB_m15_bid; local BB_m15_ask_N; local BB_m15_ask_Dev; local BB_m15_ask_HideAve; local BB_m15_ask; local zig_m1_bid_2_Depth; local zig_m1_bid_2_Deviation; local zig_m1_bid_2_Backstep; local zig_m1_bid_2_Period; local zig_m1_bid_2; local zig_m1_ask_2_Depth; local zig_m1_ask_2_Deviation; local zig_m1_ask_2_Backstep; local zig_m1_ask_2_Period; local zig_m1_ask_2; local zig_mid_top_1 = 0; local zig_mid_bottom_1 = 0; local mid_tick = 0; local mid_m1_open = 0; local BB_m1_top = 0; local BB_m1_bottom = 0; local BB_m15_top = 0; local BB_m15_bottom = 0; local sell_conditon_1 = false; local sell_condition_2 = false; local buy_conditon_1 = false; local buy_condition_2 = false; local zig_mid_top_2 = 0; local zig_mid_bottom_2 = 0; ----------------------------------------------------------- -- Standard prepare handler for marketscope strategy ----------------------------------------------------------- function Prepare(onlyName) -- collect parameters mAccount = instance.parameters.Account; mLotSize = core.host:execute("getTradingProperty", "baseUnitSize", instance.bid:instrument(), mAccount); mAllowTrade = instance.parameters.AllowTrade; mAllowedSide = instance.parameters.AllowedSide; mAllowMultiplePositions = instance.parameters.AllowMultiplePositions; symbol = instance.bid:instrument(); take_profit = instance.parameters.take_profit; max_position_oneway = instance.parameters.max_position_oneway; lots = instance.parameters.lots; bb_m15_period = instance.parameters.bb_m15_period; bb_m1_period = instance.parameters.bb_m1_period; bb_m15_deviation = instance.parameters.bb_m15_deviation; bb_m1_deviation = instance.parameters.bb_m1_deviation; zig_m1_bid_1_Depth = instance.parameters.zig_m1_bid_1_Depth; zig_m1_bid_1_Deviation = instance.parameters.zig_m1_bid_1_Deviation; zig_m1_bid_1_Backstep = instance.parameters.zig_m1_bid_1_Backstep; zig_m1_bid_1_Period = instance.parameters.zig_m1_bid_1_Period; zig_m1_ask_1_Depth = instance.parameters.zig_m1_ask_1_Depth; zig_m1_ask_1_Deviation = instance.parameters.zig_m1_ask_1_Deviation; zig_m1_ask_1_Backstep = instance.parameters.zig_m1_ask_1_Backstep; zig_m1_ask_1_Period = instance.parameters.zig_m1_ask_1_Period; BB_m1_bid_HideAve = instance.parameters.BB_m1_bid_HideAve; BB_m1_ask_HideAve = instance.parameters.BB_m1_ask_HideAve; BB_m15_bid_HideAve = instance.parameters.BB_m15_bid_HideAve; BB_m15_ask_HideAve = instance.parameters.BB_m15_ask_HideAve; zig_m1_bid_2_Depth = instance.parameters.zig_m1_bid_2_Depth; zig_m1_bid_2_Deviation = instance.parameters.zig_m1_bid_2_Deviation; zig_m1_bid_2_Backstep = instance.parameters.zig_m1_bid_2_Backstep; zig_m1_bid_2_Period = instance.parameters.zig_m1_bid_2_Period; zig_m1_ask_2_Depth = instance.parameters.zig_m1_ask_2_Depth; zig_m1_ask_2_Deviation = instance.parameters.zig_m1_ask_2_Deviation; zig_m1_ask_2_Backstep = instance.parameters.zig_m1_ask_2_Backstep; zig_m1_ask_2_Period = instance.parameters.zig_m1_ask_2_Period; --set name instance:name(profile:id() .. "(" .. instance.bid:instrument() .. "(" .. "" .. "))"); if onlyName then return; end --datasources sourcem1_bid = ExtSubscribe(id_sourcem1_bid, symbol, m1, true, "bar"); sourcem1_ask = ExtSubscribe(id_sourcem1_ask, symbol, m1, false, "bar"); sourcet1_bid = ExtSubscribe(id_sourcet1_bid, symbol, t1, true, "bar"); sourcet1_ask = ExtSubscribe(id_sourcet1_ask, symbol, t1, false, "bar"); sourcem15_bid = ExtSubscribe(id_sourcem15_bid, symbol, m15, true, "bar"); sourcem15_ask = ExtSubscribe(id_sourcem15_ask, symbol, m15, false, "bar"); --indicators zig_m1_bid_1 = core.indicators:create("ZIGZAG CHANNEL WITH OUTPUT", sourcem1_bid, zig_m1_bid_1_Depth, zig_m1_bid_1_Deviation, zig_m1_bid_1_Backstep, zig_m1_bid_1_Period, 65280, 255, 1, 1, 65280, 255, 1, 1); _gUpdatePeriods[zig_m1_bid_1.DATA] = _gUpdatePeriods[sourcem1_bid]; zig_m1_ask_1 = core.indicators:create("ZIGZAG CHANNEL WITH OUTPUT", sourcem1_ask, zig_m1_ask_1_Depth, zig_m1_ask_1_Deviation, zig_m1_ask_1_Backstep, zig_m1_ask_1_Period, 65280, 255, 1, 1, 65280, 255, 1, 1); _gUpdatePeriods[zig_m1_ask_1.DATA] = _gUpdatePeriods[sourcem1_ask]; BB_m1_bid = core.indicators:create("BB", sourcem1_bid.close, bb_m1_period, bb_m1_deviation, 255, 1, 1, BB_m1_bid_HideAve, 16711680, 1, 1); _gUpdatePeriods[BB_m1_bid.DATA] = _gUpdatePeriods[sourcem1_bid.close]; BB_m1_ask = core.indicators:create("BB", sourcem1_ask.close, bb_m1_period, bb_m1_deviation, 255, 1, 1, BB_m1_ask_HideAve, 16711680, 1, 1); _gUpdatePeriods[BB_m1_ask.DATA] = _gUpdatePeriods[sourcem1_ask.close]; BB_m15_bid = core.indicators:create("BB", sourcem15_bid.close, bb_m15_period, bb_m15_deviation, 255, 1, 1, BB_m15_bid_HideAve, 16711680, 1, 1); _gUpdatePeriods[BB_m15_bid.DATA] = _gUpdatePeriods[sourcem15_bid.close]; BB_m15_ask = core.indicators:create("BB", sourcem15_ask.close, bb_m15_period, bb_m15_deviation, 255, 1, 1, BB_m15_ask_HideAve, 16711680, 1, 1); _gUpdatePeriods[BB_m15_ask.DATA] = _gUpdatePeriods[sourcem15_ask.close]; zig_m1_bid_2 = core.indicators:create("ZIGZAG CHANNEL WITH OUTPUT", sourcem1_bid, zig_m1_bid_2_Depth, zig_m1_bid_2_Deviation, zig_m1_bid_2_Backstep, zig_m1_bid_2_Period, 65280, 255, 1, 1, 65280, 255, 1, 1); _gUpdatePeriods[zig_m1_bid_2.DATA] = _gUpdatePeriods[sourcem1_bid]; zig_m1_ask_2 = core.indicators:create("ZIGZAG CHANNEL WITH OUTPUT", sourcem1_ask, zig_m1_ask_2_Depth, zig_m1_ask_2_Deviation, zig_m1_ask_2_Backstep, zig_m1_ask_2_Period, 65280, 255, 1, 1, 65280, 255, 1, 1); _gUpdatePeriods[zig_m1_ask_2.DATA] = _gUpdatePeriods[sourcem1_ask]; end ----------------------------------------------------------- -- 'Event handler' that is called when a datasource is updated ----------------------------------------------------------- function ExtUpdate(id, updatedSource, period) if not checkReady("trades") or not checkReady("summary") then return; end -- update indicators values zig_m1_bid_1:update(core.UpdateLast); zig_m1_ask_1:update(core.UpdateLast); BB_m1_bid:update(core.UpdateLast); BB_m1_ask:update(core.UpdateLast); BB_m15_bid:update(core.UpdateLast); BB_m15_ask:update(core.UpdateLast); zig_m1_bid_2:update(core.UpdateLast); zig_m1_ask_2:update(core.UpdateLast); -- update expressions if id == id_sourcet1_bid then --Updates handler of 'sourcet1_bid' datasource ('t1' timeframe) --Check that all data of used datasources is available if canCalculate( zig_m1_ask_1.Top , zig_m1_ask_1.Top:size() - 1 ) and canCalculate( zig_m1_bid_1.Top , zig_m1_bid_1.Top:size() - 1 ) then zig_mid_top_1 = (zig_m1_ask_1.Top[getClosedPeriod(zig_m1_ask_1.Top, zig_m1_ask_1.Top:size() - 1) ] + zig_m1_bid_1.Top[getClosedPeriod(zig_m1_bid_1.Top, zig_m1_bid_1.Top:size() - 1) ] / 2); end --Check that all data of used datasources is available if canCalculate( zig_m1_ask_1.Bottom , zig_m1_ask_1.Bottom:size() - 1 ) and canCalculate( zig_m1_bid_1.Bottom , zig_m1_bid_1.Bottom:size() - 1 ) then zig_mid_bottom_1 = (zig_m1_ask_1.Bottom[getClosedPeriod(zig_m1_ask_1.Bottom, zig_m1_ask_1.Bottom:size() - 1) ] + zig_m1_bid_1.Bottom[getClosedPeriod(zig_m1_bid_1.Bottom, zig_m1_bid_1.Bottom:size() - 1) ] / 2); end --Check that all data of used datasources is available if canCalculate( sourcet1_ask , getClosedPeriod(sourcet1_ask, sourcet1_ask:size() - 1) ) and canCalculate( sourcet1_bid , getClosedPeriod(sourcet1_bid, sourcet1_bid:size() - 1) ) then mid_tick = (sourcet1_ask[getClosedPeriod(sourcet1_ask, sourcet1_ask:size() - 1) ] + sourcet1_bid[getClosedPeriod(sourcet1_bid, sourcet1_bid:size() - 1) ] / 2); end --Check that all data of used datasources is available if canCalculate( sourcem1_ask.open , getClosedPeriod(sourcem1_ask.open, sourcem1_ask.open:size() - 1) ) and canCalculate( sourcem1_bid.open , getClosedPeriod(sourcem1_bid.open, sourcem1_bid.open:size() - 1) ) then mid_m1_open = (sourcem1_ask.open[getClosedPeriod(sourcem1_ask.open, sourcem1_ask.open:size() - 1) ] + sourcem1_bid.open[getClosedPeriod(sourcem1_bid.open, sourcem1_bid.open:size() - 1) ] / 2); end --Check that all data of used datasources is available if canCalculate( BB_m1_bid.TL , BB_m1_bid.TL:size() - 1 ) and canCalculate( BB_m1_ask.TL , BB_m1_ask.TL:size() - 1 ) then BB_m1_top = (BB_m1_bid.TL[getClosedPeriod(BB_m1_bid.TL, BB_m1_bid.TL:size() - 1) ] + BB_m1_ask.TL[getClosedPeriod(BB_m1_ask.TL, BB_m1_ask.TL:size() - 1) ] / 2); end --Check that all data of used datasources is available if canCalculate( BB_m1_bid.BL , BB_m1_bid.BL:size() - 1 ) and canCalculate( BB_m1_ask.BL , BB_m1_ask.BL:size() - 1 ) then BB_m1_bottom = (BB_m1_bid.BL[getClosedPeriod(BB_m1_bid.BL, BB_m1_bid.BL:size() - 1) ] + BB_m1_ask.BL[getClosedPeriod(BB_m1_ask.BL, BB_m1_ask.BL:size() - 1) ] / 2); end --Check that all data of used datasources is available if canCalculate( BB_m15_ask.TL , BB_m15_ask.TL:size() - 1 ) and canCalculate( BB_m15_bid.TL , BB_m15_bid.TL:size() - 1 ) then BB_m15_top = (BB_m15_ask.TL[getClosedPeriod(BB_m15_ask.TL, BB_m15_ask.TL:size() - 1) ] + BB_m15_bid.TL[getClosedPeriod(BB_m15_bid.TL, BB_m15_bid.TL:size() - 1) ] / 2); end --Check that all data of used datasources is available if canCalculate( BB_m15_ask.BL , BB_m15_ask.BL:size() - 1 ) and canCalculate( BB_m15_bid.BL , BB_m15_bid.BL:size() - 1 ) then BB_m15_bottom = (BB_m15_ask.BL[getClosedPeriod(BB_m15_ask.BL, BB_m15_ask.BL:size() - 1) ] + BB_m15_bid.BL[getClosedPeriod(BB_m15_bid.BL, BB_m15_bid.BL:size() - 1) ] / 2); end sell_conditon_1 = mid_m1_open < BB_m1_top and mid_m1_open < BB_m15_top; sell_condition_2 = mid_tick >= BB_m1_top and mid_tick >= BB_m15_top and (mid_tick >= zig_mid_top_1 or mid_tick >= zig_mid_bottom_1 or mid_tick >= zig_mid_top_2 or mid_tick >= zig_mid_bottom_2); buy_conditon_1 = mid_m1_open > BB_m1_bottom and mid_m1_open > BB_m15_bottom; buy_condition_2 = mid_tick <= BB_m1_bottom and mid_tick <= BB_m15_bottom and (mid_tick <= zig_mid_bottom_1 or mid_tick <= zig_mid_top_1 or mid_tick <= zig_mid_bottom_2 or mid_tick <= zig_mid_top_2); --Check that all data of used datasources is available if canCalculate( zig_m1_ask_2.Top , zig_m1_ask_2.Top:size() - 1 ) and canCalculate( zig_m1_bid_2.Top , zig_m1_bid_2.Top:size() - 1 ) then zig_mid_top_2 = (zig_m1_ask_2.Top[getClosedPeriod(zig_m1_ask_2.Top, zig_m1_ask_2.Top:size() - 1) ] + zig_m1_bid_2.Top[getClosedPeriod(zig_m1_bid_2.Top, zig_m1_bid_2.Top:size() - 1) ] / 2); end --Check that all data of used datasources is available if canCalculate( zig_m1_ask_2.Bottom , zig_m1_ask_2.Bottom:size() - 1 ) and canCalculate( zig_m1_bid_2.Bottom , zig_m1_bid_2.Bottom:size() - 1 ) then zig_mid_bottom_2 = (zig_m1_ask_2.Bottom[getClosedPeriod(zig_m1_ask_2.Bottom, zig_m1_ask_2.Bottom:size() - 1) ] + zig_m1_bid_2.Bottom[getClosedPeriod(zig_m1_bid_2.Bottom, zig_m1_bid_2.Bottom:size() - 1) ] / 2); end end -- processing of Activation points if id==id_sourcet1_bid then --Updates handler of 'sourcet1_bid' datasource ('t1' timeframe) --'buying' activation point logic if countLongPositions() <= max_position_oneway and buy_conditon_1 and buy_condition_2 then if mAllowTrade then createTrueMarketOrder("B", lots, symbol, 0, false, 0); end end --'selling' activation point logic if countShortPositions() <= max_position_oneway and sell_conditon_1 and sell_condition_2 then if mAllowTrade then createTrueMarketOrder("S", lots, symbol, 0, false, 0); end end --'close_buy' activation point logic if (getBuyNetPL(symbol) > take_profit and mid_tick >= BB_m1_top) or sell_condition_2 then if mAllowTrade then close("B", core.host:findTable("offers"):find("Instrument", symbol).OfferID); end end --'close_sell' activation point logic if (getSellNetPL(symbol) > take_profit and mid_tick <= BB_m1_bottom) or buy_condition_2 then if mAllowTrade then close("S", core.host:findTable("offers"):find("Instrument", symbol).OfferID); end end end end function checkReady(tableName) return core.host:execute("isTableFilled", tableName); end ----------------------------------------------------------- --Enters to the market -- side: B - BUY or S - SELL -- amount: order amount -- instrumentName: instrument of order -- stop: 0, 1 or greater value -- isTrailingStop: true/false -- limit: 0, 1 or greater value ----------------------------------------------------------- function createTrueMarketOrder(side, amount, instrumentName, stop, isTrailingStop, limit) if not mAllowTrade then return; end local offerId = core.host:findTable("offers"):find("Instrument", instrumentName).OfferID; if not (mAllowedSide == "Both" or (mAllowedSide == "Buy" and side == "B") or (mAllowedSide == "Sell" and side == "S")) then return; end if not mAllowMultiplePositions then if (side == 'B' and countLongPositions(instrumentName) > 0) then return; elseif (side == 'S' and countShortPositions(instrumentName) > 0) then return; end end local valuemap; valuemap = core.valuemap(); valuemap.Command = "CreateOrder"; valuemap.OrderType = "OM"; valuemap.OfferID = offerId; valuemap.AcctID = mAccount; valuemap.GTC = "FOK"; --Fill or Kill order to avoid partial execution valuemap.Quantity = amount * mLotSize; valuemap.BuySell = side; valuemap.CustomID = mCID; if stop >= 1 then valuemap.PegTypeStop = "M"; if side == "B" then valuemap.PegPriceOffsetPipsStop = - stop; else valuemap.PegPriceOffsetPipsStop = stop; end if isTrailingStop then valuemap.TrailStepStop = 1; end end if limit >= 1 then valuemap.PegTypeLimit = "M"; if side == "B" then valuemap.PegPriceOffsetPipsLimit = limit; else valuemap.PegPriceOffsetPipsLimit = -limit; end end if (not canClose(instrumentName)) and (stop >= 1 or limit >= 1) then valuemap.EntryLimitStop = 'Y' end success, msg = terminal:execute(200, valuemap); assert(success, msg); end ----------------------------------------------------------- -- closes all positions of the specified direction (B for buy, S for sell) ----------------------------------------------------------- function canClose(instrumentName) return core.host:execute("getTradingProperty", "canCreateMarketClose", instrumentName, mAccount); end function close(side, offer) local enum, row, valuemap; enum = core.host:findTable("trades"):enumerator(); while true do row = enum:next(); if row == nil then break; end if row.AccountID == mAccount and row.OfferID == offer and row.BS == side and row.QTXT == mCID then -- if trade has to be closed if canClose(row.Instrument) then -- create a close market order when hedging is allowed valuemap = core.valuemap(); valuemap.OrderType = "CM"; valuemap.OfferID = offer; valuemap.AcctID = mAccount; valuemap.Quantity = row.Lot; valuemap.TradeID = row.TradeID; valuemap.CustomID = mCID; if row.BS == "B" then valuemap.BuySell = "S"; else valuemap.BuySell = "B"; end success, msg = terminal:execute(200, valuemap); assert(success, msg); else -- create an opposite market order when FIFO valuemap = core.valuemap(); valuemap.OrderType = "OM"; valuemap.OfferID = offer; valuemap.AcctID = mAccount; valuemap.Quantity = row.Lot; valuemap.CustomID = mCID; if row.BS == "B" then valuemap.BuySell = "S"; else valuemap.BuySell = "B"; end success, msg = terminal:execute(200, valuemap); assert(success, msg); end end end end ----------------------------------------------------------- --Handle command execution result ----------------------------------------------------------- function ExtAsyncOperationFinished(cookie, success, message) if cookie == 1 then loaded = true; elseif cookie == 200 then assert(success, message); end end ----------------------------------------------------------- -- Helper functions ----------------------------------------------------------- function getOppositeSide(side) if(side == "B") then return "S"; else return "B"; end end function getDSPeriod(ds, updatedSource, period) local p; if ds:isBar() then p = core.findDate(ds.open, updatedSource:date(period), false); else p = core.findDate(ds, updatedSource:date(period), false); end if (p > ds:size() - 1) then p = ds:size() - 1; elseif (p < ds:first()) then p = ds:first(); end return p; end ----------------------------------------------------------- -- Allow to calculate last closed bar period for ds datasource -- which has not tick frequency updates ----------------------------------------------------------- function getClosedPeriod(ds, supposedPeriod) --Check if datasource lastdate is closed on updatePeriod or shift supposedPeriod to -1 if _gUpdatePeriods[ds] == 't1' or _gUpdatePeriods[ds] == nil then return supposedPeriod; else return supposedPeriod - 1; end end ----------------------------------------------------------- --Helper functions to wrap the table's method call into the simple function call ----------------------------------------------------------- function streamSize(stream) return stream:size(); end function streamHasData(stream, period) return stream:hasData(period); end function canCalculate(stream, period) return (period >= 0) and (period > stream:first()) and streamHasData(stream, period); end ----------------------------------------------------------- -- Helper functions to be sure that you work with a tick stream ----------------------------------------------------------- function getTickStreamOfPriceType(stream, priceType) if stream:isBar() then if priceType == "open" then return stream.open; elseif priceType == "high" then return stream.high; elseif priceType == "low" then return stream.low; elseif priceType == "close" then return stream.close; elseif priceType == "typical" then return stream.typical; elseif priceType == "weighted" then return stream.weighted; elseif priceType == "volume" then return stream.volume; else return stream.close; end else return stream; end end function selectStream(safeStream, subStream) if safeStream:isBar() then return subStream; else return safeStream; end end --------------------------------------------------------- -- Subscription for updates by datasource timeframe --------------------------------------------------------- -- subscribe for the price data function ExtSubscribe(id, instrument, period, bid, type) local sub = {}; if instrument == nil and period == "t1" then if bid then sub.stream = instance.bid; else sub.stream = instance.ask; end sub.tick = true; sub.loaded = true; sub.lastSerial = -1; _gSubscription[id] = sub; elseif instrument == nil then sub.stream = core.host:execute("getHistory", id, instance.bid:instrument(), period, 0, 0, bid); sub.tick = false; sub.loaded = false; sub.lastSerial = -1; _gSubscription[id] = sub; else sub.stream = core.host:execute("getHistory", id, instrument, period, 0, 0, bid); sub.tick = (period == "t1"); sub.loaded = false; sub.lastSerial = -1; _gSubscription[id] = sub; end _gUpdatePeriods[sub.stream] = period; if sub.tick then return sub.stream; else if type == "open" then _gUpdatePeriods[sub.stream.open] = period; return sub.stream.open; elseif type == "high" then _gUpdatePeriods[sub.stream.high] = period; return sub.stream.high; elseif type == "low" then _gUpdatePeriods[sub.stream.low] = period; return sub.stream.low; elseif type == "close" then _gUpdatePeriods[sub.stream.close] = period; return sub.stream.close; elseif type == "bar" then _gUpdatePeriods[sub.stream.open] = period; _gUpdatePeriods[sub.stream.high] = period; _gUpdatePeriods[sub.stream.low] = period; _gUpdatePeriods[sub.stream.close] = period; _gUpdatePeriods[sub.stream.median] = period; _gUpdatePeriods[sub.stream.typical] = period; _gUpdatePeriods[sub.stream.volume] = period; _gUpdatePeriods[sub.stream.weighted] = period; return sub.stream; else assert(false, type .. " is unknown"); end end end function AsyncOperationFinished(cookie, success, message) local sub; sub = _gSubscription[cookie]; if sub ~= nil then sub.loaded = true; if sub.stream:size() > 1 then sub.lastSerial = sub.stream:serial(sub.stream:size() - 1); end else -- unknown cookie if ExtAsyncOperationFinished ~= nil then ExtAsyncOperationFinished(cookie, success, message) end end end function Update() if instance.bid:size() > 0 then _gLastTime = instance.bid:date(instance.bid:size() - 1); end for k, v in pairs(_gSubscription) do if v.loaded and v.stream:size() > 1 then local s = v.stream:serial(v.stream:size() - 1); local p; if s ~= v.lastSerial then if v.tick then p = v.stream:size() - 1; -- the last tick else p = v.stream:size() - 2; -- the previous candle end ExtUpdate(k, v.stream, p); v.lastSerial = s; end end end end --------------------------------------------------------- -- Additional functions --------------------------------------------------------- local mAccountRow = nil; local mSummaries = {}; function checkAccountRow() if mAccountRow == nil then mAccountRow = core.host:findTable("accounts"):find("AccountID", mAccount); else mAccountRow:refresh(); end end function checkSummaryRow(sInstrument) local sOfferID, summaryIter, summaryRow; if mSummaries[sInstrument] ~= nil then --try refresh if not (mSummaries[sInstrument]:refresh()) then mSummaries[sInstrument] = nil; end end --re-read all cache if mSummaries[sInstrument] == nil then sOfferID = core.host:findTable("offers"):find("Instrument", sInstrument).OfferID; summaryIter = core.host:findTable("summary"):enumerator(); summaryRow = summaryIter:next(); while summaryRow ~= nil do if summaryRow.OfferID == sOfferID then mSummaries[sInstrument] = summaryRow; break; end summaryRow = summaryIter:next(); end end end function getEquity() checkAccountRow(); return mAccountRow.Equity; end function getBalance() checkAccountRow(); return mAccountRow.Balance; end function getProfit() checkAccountRow(); return mAccountRow.Equity - mAccountRow.Balance; end function getAmountK(sInstrument) local res; checkSummaryRow(sInstrument); res = mSummaries[sInstrument]; if res == nil then return 0; else return res.AmountK; end end function getGrossPL(sInstrument) local res; checkSummaryRow(sInstrument); res = mSummaries[sInstrument]; if res == nil then return 0; else return res.GrossPL; end end function getNetPL(sInstrument) local res; checkSummaryRow(sInstrument); res = mSummaries[sInstrument]; if res == nil then return 0; else return res.NetPL; end end function getSellAmountK(sInstrument) local res; checkSummaryRow(sInstrument); res = mSummaries[sInstrument]; if res == nil then return 0; else return res.SellAmountK; end end function getBuyAmountK(sInstrument) local res; checkSummaryRow(sInstrument); res = mSummaries[sInstrument]; if res == nil then return 0; else return res.BuyAmountK; end end function getBuyNetPLPip(sInstrument) local res; checkSummaryRow(sInstrument); res = mSummaries[sInstrument]; if res == nil then return 0; else return res.BuyNetPLPip; end end function getSellNetPLPip(sInstrument) local res; checkSummaryRow(sInstrument); res = mSummaries[sInstrument]; if res == nil then return 0; else return res.SellNetPLPip; end end function getBuyNetPL(sInstrument) local res; checkSummaryRow(sInstrument); res = mSummaries[sInstrument]; if res == nil then return 0; else return res.BuyNetPL; end end function getSellNetPL(sInstrument) local res; checkSummaryRow(sInstrument); res = mSummaries[sInstrument]; if res == nil then return 0; else return res.SellNetPL; end end function countPositions(sInstrument) local tradesIter, tradeRow, count, sOfferID; count = 0; if sInstrument ~= nil then sOfferID = core.host:findTable("offers"):find("Instrument", sInstrument).OfferID; end tradesIter = core.host:findTable("trades"):enumerator(); tradeRow = tradesIter:next(); while tradeRow ~= nil do if (sInstrument == nil or tradeRow.OfferID == sOfferID) then count = count + 1; end tradeRow = tradesIter:next(); end return count; end function countLongPositions(sInstrument) local tradesIter, tradeRow, count, sOfferID; count = 0; if sInstrument ~= nil then sOfferID = core.host:findTable("offers"):find("Instrument", sInstrument).OfferID; end tradesIter = core.host:findTable("trades"):enumerator(); tradeRow = tradesIter:next(); while tradeRow ~= nil do if ((sInstrument==nil or tradeRow.OfferID == sOfferID) and tradeRow.BS == "B") then count = count + 1; end tradeRow = tradesIter:next(); end return count; end function countShortPositions(sInstrument) local tradesIter, tradeRow, count, sOfferID; count = 0; if sInstrument ~= nil then sOfferID = core.host:findTable("offers"):find("Instrument", sInstrument).OfferID; end tradesIter = core.host:findTable("trades"):enumerator(); tradeRow = tradesIter:next(); while tradeRow ~= nil do if ((sInstrument == nil or tradeRow.OfferID == sOfferID) and tradeRow.BS == "S") then count = count + 1; end tradeRow = tradesIter:next(); end return count; end function getLastUpdateTime() if (_gLastTime == nil) then return 0; else return _gLastTime; end end function time(hours, minutes, seconds) local dtLast; dtLast = core.dateToTable(_gLastTime); if seconds == nil then seconds = 0; end return core.datetime(dtLast.year, dtLast.month, dtLast.day, hours, minutes, seconds); end function isValidDate(checkDate) if (checkDate < 1) then return false; else return true; end end function parseTime(sTime) local iDelimHMPos = string.find(sTime, ":"); local h = tonumber(string.sub(sTime, 1, iDelimHMPos - 1)); local sTimeTile = string.sub(sTime, iDelimHMPos + 1); local iDelimMSPos = string.find(sTimeTile, ":"); local m, s; s = 0; if iDelimMSPos == nil then m = tonumber(sTimeTile); else m = tonumber(string.sub(sTimeTile, 1, iDelimMSPos - 1)); s = tonumber(string.sub(sTimeTile, iDelimMSPos + 1)); end return time(h, m, s); end function getPipSize(sInstrument) return core.host:findTable("offers"):find("Instrument", sInstrument).PointSize; end function getUsableMarginPercent() checkAccountRow(); return (mAccountRow.UsableMargin / mAccountRow.Equity) * 100; end