function Init() { indicator.name("AO Divergence"); indicator.description(""); indicator.requiredSource(core.Bar); indicator.type(core.Oscillator); indicator.parameters.addInteger("AO_Short", "Period of short MA for AO", "", 5); indicator.parameters.addInteger("AO_Long", "Period of long MA for AO", "", 35); indicator.parameters.addBoolean("I", "Indicator mode", "Keep true value to display labels && lines. Set this parameter to false when the indicator is used in another indicator.", true); indicator.parameters.addColor("D_color", "Color of Divergence line", "", core.rgb(0, 155, 255)); indicator.parameters.addColor("UP_color", "Color of Uptrend", "", core.rgb(255, 0, 0)); indicator.parameters.addColor("DN_color", "Color of Downtend", "", core.rgb(0, 255, 0)); } var I; var DD; var UP_color; var DN_color; var first; var source = null; var D = null; var UP = null; var DN = null; var AO = null; var lineid = null; function Prepare() { I = instance.parameters.I; DD = instance.parameters.DD; UP_color = instance.parameters.UP_color; DN_color = instance.parameters.DN_color; source = instance.source; AO = core.indicators.create("AO", source, instance.parameters.AO_Short,instance.parameters.AO_Long); first = AO.DATA.first(); var name = profile.id() + "(" + source.name() + ", " + instance.parameters.AO_Short + ", " + instance.parameters.AO_Long + ")"; instance.name(name); D = instance.addStream("AO", core.Line, name + ".AO", "AO", instance.parameters.D_color, first, -1); if (I) { UP = instance.createTextOutput ("Up", "Up", "Wingdings", 10, core.H_Center, core.V_Top, instance.parameters.UP_color, -1); DN = instance.createTextOutput ("Dn", "Dn", "Wingdings", 10, core.H_Center, core.V_Bottom, instance.parameters.DN_color, -1); } else { UP = instance.addStream("UP", core.Bar, name + ".UP", "UP", instance.parameters.D_color, first, -1); DN = instance.addStream("DN", core.Bar, name + ".DN", "DN", instance.parameters.D_color, first, -1); } } var pperiod = null; var pperiod1 = null; var line_id = 0; function Update(period, mode) { if (pperiod != null && pperiod > period) { core.host.execute("removeAll"); } pperiod = period; if (pperiod1 != null && pperiod1 == source.serial(period)) { return ; } pperiod1 = source.serial(period) period = period - 1; AO.update(mode); if (period >= first) { D[period] = AO.DATA[period]; if (period >= first + 2) { processBullish(period - 2); processBearish(period - 2); } } } function processBullish(period) { if (isTrough(period)) { var curr, prev; curr = period; prev = prevTrough(period); if (prev != null) { if (AO.DATA[curr] > AO.DATA[prev] && source.low[curr] < source.low[prev]) { if (I) { DN.set(curr, AO.DATA[curr], "\225", "Classic bullish"); line_id = line_id + 1; core.host.execute("drawLine", line_id, source.date(prev), AO.DATA[prev], source.date(curr), AO.DATA[curr], DN_color); } else { DN[period] = curr - prev; } } else if (AO.DATA[curr] < AO.DATA[prev] && source.low[curr] > source.low[prev]) { if (I) { DN.set(curr, AO.DATA[curr], "\225", "Reversal bullish"); line_id = line_id + 1; core.host.execute("drawLine", line_id, source.date(prev), AO.DATA[prev], source.date(curr), AO.DATA[curr], DN_color); } else { DN[period] = -(curr - prev); } } } } } function isTrough(period) { var i; if (AO.DATA[period] < 0 && AO.DATA[period] < AO.DATA[period - 1] && AO.DATA[period] < AO.DATA[period + 1]) { for (i = period - 1; i>=first; i--) { if (AO.DATA[i] > 0) { return (true); } else if (AO.DATA[period] > AO.DATA[i]) { return (false); } } } return (false); } function prevTrough(period) { var i; for (i = period - 5; i>=first; i--) { if (AO.DATA[i] <= AO.DATA[i - 1] && AO.DATA[i] < AO.DATA[i - 2] && AO.DATA[i] <= AO.DATA[i + 1] && AO.DATA[i] < AO.DATA[i + 2]) { return (i); } } return (null); } function processBearish(period) { if (isPeak(period)) { var curr, prev; curr = period; prev = prevPeak(period); if (prev != null) { if (AO.DATA[curr] < AO.DATA[prev] && source.high[curr] > source.high[prev]) { if (I) { UP.set(curr, AO.DATA[curr], "\226", "Classic bearish"); line_id = line_id + 1; core.host.execute("drawLine", line_id, source.date(prev), AO.DATA[prev], source.date(curr), AO.DATA[curr], UP_color); } else { UP[period] = curr - prev; } } else if (AO.DATA[curr] > AO.DATA[prev] && source.high[curr] < source.high[prev]) { if (I) { UP.set(curr, AO.DATA[curr], "\226", "Reversal bearish"); line_id = line_id + 1; core.host.execute("drawLine", line_id, source.date(prev), AO.DATA[prev], source.date(curr), AO.DATA[curr], UP_color); } else { UP[period] = -(curr - prev); } } } } } function isPeak(period) { var i; if (AO.DATA[period] > 0 && AO.DATA[period] > AO.DATA[period - 1] && AO.DATA[period] > AO.DATA[period + 1]) { for (i = period - 1; i>=first; i--) { if (AO.DATA[i] < 0) { return (true); } else if (AO.DATA[period] < AO.DATA[i]) { return (false); } } } return (false); } function prevPeak(period) { var i; for (i = period - 5; i>=first; i--) { if (AO.DATA[i] >= AO.DATA[i - 1] && AO.DATA[i] > AO.DATA[i - 2] && AO.DATA[i] >= AO.DATA[i + 1] && AO.DATA[i] > AO.DATA[i + 2]) { return (i); } } return (null); }