using System; using System.Collections.Generic; namespace TSLab.Script.Handlers { // ReSharper disable InconsistentNaming public class BaseAMA { [HandlerParameter(true, "0.5", Min = "0.1", Max = "1.0", Step = "0.1")] public double FastLimit { get; set; } [HandlerParameter(true, "0.05", Min = "0.01", Max = "0.1", Step = "0.01")] public double SlowLimit { get; set; } protected IList Execute(IList Price, bool isMama) { var count = Price.Count; var Smooth = new double[count]; var Detrender = new double[count]; var I1 = new double[count]; var Q1 = new double[count]; var I2 = new double[count]; var Q2 = new double[count]; var Period = new double[count]; var Phase = new double[count]; var Re = new double[count]; var Im = new double[count]; var MAMA = new double[count]; var FAMA = new double[count]; for (int i = 0; i < 6; i++) { MAMA[i] = FAMA[i] = Price[i]; } for (int i = 6; i < count; i++) { Smooth[i] = (4 * Price[i] + 3 * Price[i - 1] + 2 * Price[i - 2] + Price[i - 3]) / 10; Detrender[i] = (.0962 * Smooth[i] + .5769 * Smooth[i - 2] - .5769 * Smooth[i - 4] - .0962 * Smooth[i - 6]) * (.075 * Period[i - 1] + .54); //Compute InPhase and Quadrature components Q1[i] = (.0962 * Detrender[i] + .5769 * Detrender[i - 2] - .5769 * Detrender[i - 4] - .0962 * Detrender[i - 6]) * (.075 * Period[i - 1] + .54); I1[i] = Detrender[i - 3]; //Advance the phase of I1 and Q1 by 90 degrees var jI = (.0962 * I1[i] + .5769 * I1[i - 2] - .5769 * I1[i - 4] - .0962 * I1[i - 6]) * (.075 * Period[i - 1] + .54); var jQ = (.0962 * Q1[i] + .5769 * Q1[i - 2] - .5769 * Q1[i - 4] - .0962 * Q1[i - 6]) * (.075 * Period[i - 1] + .54); //Phasor addition for 3 bar averaging) I2[i] = I1[i] - jQ; Q2[i] = Q1[i] + jI; //Smooth the I and Q components before applying the discriminator I2[i] = .2 * I2[i] + .8 * I2[i - 1]; Q2[i] = .2 * Q2[i] + .8 * Q2[i - 1]; //Homodyne Discriminator Re[i] = I2[i] * I2[i - 1] + Q2[i] * Q2[i - 1]; Im[i] = I2[i] * Q2[i - 1] - Q2[i] * I2[i - 1]; Re[i] = .2 * Re[i] + .8 * Re[i - 1]; Im[i] = .2 * Im[i] + .8 * Im[i - 1]; if (Im[i] != 0 && Re[i] != 0) { Period[i] = 2 * Math.PI / Math.Atan2(Im[i], Re[i]); } if (Period[i] > 1.5 * Period[i - 1]) { Period[i] = 1.5 * Period[i - 1]; } if (Period[i] < .67 * Period[i - 1]) { Period[i] = .67 * Period[i - 1]; } if (Period[i] < 6) { Period[i] = 6; } if (Period[i] > 50) { Period[i] = 50; } Period[i] = .2 * Period[i] + .8 * Period[i - 1]; //SmoothPeriod = .33*Period[i] + .67*SmoothPeriod[i-1]; if (I1[i] != 0) { Phase[i] = Math.Atan2(Q1[i], I1[i]) * 180 / Math.PI; } var DeltaPhase = Phase[i - 1] - Phase[i]; if (DeltaPhase < 1) { DeltaPhase = 1; } var alpha = FastLimit / DeltaPhase; if (alpha < SlowLimit) { alpha = SlowLimit; } MAMA[i] = alpha * Price[i] + (1 - alpha) * MAMA[i - 1]; FAMA[i] = .5 * alpha * MAMA[i] + (1 - .5 * alpha) * FAMA[i - 1]; } return isMama ? MAMA : FAMA; } } //[HandlerCategory("Indicators")] public class MAMA : BaseAMA, IDouble2DoubleHandler { public IList Execute(IList source) { return Execute(source, true); } } //[HandlerCategory("Indicators")] public class FAMA : BaseAMA, IDouble2DoubleHandler { public IList Execute(IList source) { return Execute(source, false); } } // ReSharper restore InconsistentNaming }