-- More information about this indicator can be found at: -- http://fxcodebase.com/code/viewtopic.php?f=17&t=1643 --+------------------------------------------------------------------+ --| Copyright © 2018, Gehtsoft USA LLC | --| http://fxcodebase.com | --+------------------------------------------------------------------+ --| Developed by : Mario Jemic | --| mario.jemic@gmail.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 | --+------------------------------------------------------------------+ -- Indicator profile initialization routine -- Defines indicator profile properties and indicator parameters -- TODO: Add minimal and maximal value of numeric parameters and default color of the streams function Init() indicator:name("Triangle"); indicator:description("Triangle"); indicator:requiredSource(core.Bar); indicator:type(core.Indicator); indicator.parameters:addString("k1", "Triangle", "Triangle Y/N" , "Yes"); indicator.parameters:addStringAlternative("k1", "Yes", "Triangle Yes" , "Yes"); indicator.parameters:addStringAlternative("k1", "No", "Triangle No", "No"); indicator.parameters:addString("k2", "Expanding Triangle", "Expanding Triangle Y/N" , "No"); indicator.parameters:addStringAlternative("k2", "Yes", "Expanding Triangle Yes" , "Yes"); indicator.parameters:addStringAlternative("k2", "No", "Expanding Triangle No" , "No"); indicator.parameters:addString("k5", "Wedges", "Up Wedges Y/N" , "No"); indicator.parameters:addStringAlternative("k5", "Yes", "Wedges Yes" , "Yes"); indicator.parameters:addStringAlternative("k5", "No", "Wedges No" , "No"); indicator.parameters:addString("k6", "Down Wedges", "Wedges Y/N" , "No"); indicator.parameters:addStringAlternative("k6", "Yes", "Wedges Yes" , "Yes"); indicator.parameters:addStringAlternative("k6", "No", "Wedges No" , "No"); indicator.parameters:addInteger("x", "Max. Base / Side Ratio", "Max. Base / Side Ratio", 5); indicator.parameters:addInteger("y", "Max. Side / Side Ratio", "Max. Side / Side Ratio", 5); indicator.parameters:addInteger("frame", "Maximum Triagle Lengt", "", 50); indicator.parameters:addInteger("Only", "Lookback Search Lengt", "", 100, 0, 1000); indicator.parameters:addColor("top_color", "Color of Top", "Color of Top", core.rgb(0, 255, 0)); indicator.parameters:addColor("bottom_color", "Color of Bottom", "Color of Bottom", core.rgb(0, 0, 255)); end -- Indicator instance initialization routine -- Processes indicator parameters and creates output streams -- TODO: Refine the first period calculation for each of the output streams. -- TODO: Calculate all constants, create instances all subsequent indicators and load all required libraries -- Parameters block local first = nil; local source = nil; local candlesize = nil; local k1=nil; local k2=nil; --local k3=nil; --local k4=nil; local k5=nil; local k6=nil; local base1=0; local base2=0; local Only; -- Streams block local dummy = nil; --Variable local upy = nil; local downy = nil; -- Routine function Prepare(nameOnly) Only= instance.parameters.Only; frame = instance.parameters.frame; k1 = instance.parameters.k1; k2 = instance.parameters.k2; k3 = instance.parameters.k3; k4 = instance.parameters.k4; k5 = instance.parameters.k5; k6 = instance.parameters.k6; base1 = instance.parameters.x; base2 = instance.parameters.y; source = instance.source; first = source:first(); local name = profile:id() .. "(" .. source:name() .. ", " .. frame .. ")"; instance:name(name); if (nameOnly) then return; end local s, e; s, e = core.getcandle(source:barSize(), core.now(), 0); -- remember size of the candle to calculate the date for the lines which ends after the end of the chart candlesize = e - s; upy = instance:addInternalStream(0, 0); downy = instance:addInternalStream(0, 0); -- add an extent to make the output streams longer than the source to be able draw into the future dummy = instance:addStream("D", core.Dot, name, "D", instance.parameters.top_color, first, 300); end local last_period = -1; -- update function Update(period) if period < first or not source:hasData(period) then return; end if period < source:size() - 1 - Only then return; end -- detect restart if last_period > period then core.host:execute("removeAll"); end last_period = period; if period >= 6 then fractal(period); end if period >= frame then Draw(period, frame); end end --distance between two points function Distance (x1,y1,x2,y2) return math.sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1)); end -- calculate fractals function fractal(p) upy[p] = 0; downy[p] = 0; curr = source.high[p - 2]; if (curr >= source.high[p - 4] and curr >= source.high[p - 3] and curr >= source.high[p - 1] and curr >= source.high[p]) then upy[p - 2] = source.high[p-2]; end curr = source.low[p - 2]; if (curr <= source.low[p - 4] and curr <= source.low[p - 3] and curr <= source.low[p - 1] and curr <= source.low[p]) then downy[p - 2] = source.low[p-2]; end upy[p - 1] = 0; downy[p - 1] = 0; upy[p] = 0; downy[p] = 0; end -- get line equation coefficients function getline(x1, y1, x2, y2) local a, b; a = ((y2 - y1) / (x2 - x1)); b = (y1 - a * x1); return a, b; end -- get line intersection point function intersect(a1, b1, a2, b2) local x, y; -- collinear if a1 == a2 then return nil, nil; end x = (b2 - b1) / (a1 - a2); y = a1 * x + b1; return x, y; end local line_id = 0; -- draws line based on two fractals and the point of the intersection function drawline(x1, y1, x2, y2, ix, iy, color) -- line's coordinates local lx1, lx2, ly1, ly2; -- case 1 -- intesection is before x1 if ix < x1 then lx1 = ix; ly1 = iy; lx2 = x2; ly2 = y2; -- intesection is between x1 and x2 elseif ix >= x1 and ix <= x2 then lx1 = x1; ly1 = y1; lx2 = x2; ly2 = y2; -- intesection is after x2 else lx1 = x1; ly1 = y1; lx2 = ix; ly2 = iy; end -- if line is out of the chart area - adjust it to the chart borders if lx1 < 0 or lx2 >= dummy:size() then local a, b; a, b = getline(lx1, ly1, lx2, ly2); if lx1 < 0 then lx1 = 0; end if lx2 >= dummy:size() then lx2 = dummy:size() - 1; end ly1 = a * lx1 + b; ly2 = a * lx2 + b; end -- get dates instead of number of the periods local date1, date2; date1 = source:date(lx1); if lx2 >= source:size() then date2 = source:date(source:size() - 1) + candlesize * (lx2 - source:size() + 1); else date2 = source:date(lx2); end core.host:execute("drawLine", line_id, date1, ly1, date2, ly2, color); line_id = line_id + 1; end -- find last two up and down fractals function Draw(period, frame) local i, f; -- x (period) and y (price) coordinates of two last fractals local hx1, hy1, hx2, hy2; local lx1, ly1, lx2, ly2; hy1 = nil; hy2 = nil; ly1 = nil; ly2 = nil; f = 0; for i = period, period - frame + 1, -1 do if upy[i] ~= 0 then if hy1 == nil then hy1 = upy[i]; hx1 = i; f = f + 1; elseif hy2 == nil then hy2 = upy[i]; hx2 = i; f = f + 1; end end if downy[i] ~= 0 then if ly1 == nil then ly1 = downy[i]; lx1 = i; f = f + 1; elseif ly2 == nil then ly2 = downy[i]; lx2 = i; f = f + 1; end end -- all four points are found if f == 4 then break; end end -- if all four points has been found if f == 4 then -- intersection point local ix, iy = nil; -- a and b coefficients of the y = a * x + b line quation -- x2 always < x1 local a1, b1, a2, b2; a1, b1 = getline(hx2, hy2, hx1, hy1); a2, b2 = getline(lx2, ly2, lx1, ly1); ix, iy = intersect(a1, b1, a2, b2); if ix == nil then -- collinear return ; else local ls = Distance (lx2,ly2,ix,iy); local bs= Distance (hx2,hy2,lx2,ly2); local hs = Distance (hx2,hy2,ix,iy); if ls/bs > base1 or hs/bs > base1 then return; end if bs > ls or bs > hs then return; end if ls/hs > base2 or hs/ls > base2 then return; end --Triangle if ix>hx1 and ix>lx1 and iy<=hy1 and iy >=ly1 and k1== "No" then return; end --Expanding Triangle if ix=ly1 and k2== "No" then return; end --Top -- if iy>hy1 and iy>ly1 and ix >hx1 and ixhx1 and ixhy1 and iy>ly1 and ix >hx1 and ix>lx1 and k5== "No" then return; end -- Down Wedges if iyhx1 and ix>lx1 and k6== "No" then return; end -- Expanding Down Wedges if iyhy1 and iy>ly1 and ix