This indicator adapts to price movement in a new and unique way based on the rate of change of phase as measured by the Hilbert Transform Discriminator. It features fast attack average and a slow decay average. In conjunction with FAMA the lines only cross at major market reversals.
This indicator uses an alpha (α) value that is half of its corresponding MAMA indicator. This results in an indicator that is synchronized to MAMA, but with vertical movement that is not as great. Consequently, MAMA and FAMA do not cross unless there has been a major change in market direction.

Calculation:
FAMA = 0.5 * α * MAMA + ( 1 - 0.5 * α) * FAMAPrevious
α = FastLimit / DeltaPhase

Developer:
John Ehlers

                    
#region Namespaces using System; #endregion namespace ScriptCode { /// <summary> /// Indicator scripts are used for calculating a series of numerical values. /// /// This script can be used in several ways: /// (1) It can be used on a chart. /// (2) It can be used from another script. /// (3) It can be used as a script column in a watchlist. /// </summary> public partial class MyIndicator : IndicatorScriptBase // NEVER CHANGE THE CLASS NAME { #region Variables // Use for the underlying indicator on which to calculate the indicator script. private Indicator _indicator; // Use for the upper limit of the alpha value. private double _fastLimit; // Use for the lower limit of the alpha value. private double _slowLimit; // Use for the detrender series. private const int DETRENDER = 0; // Use for the period series. private const int PERIOD = 1; // Use for the smooth series. private const int SMOOTH = 2; // Use for the i1 series. private const int I1 = 3; // Use for the i2 series. private const int I2 = 4; // Use for the im series. private const int IM = 5; // Use for the q1 series. private const int Q1 = 6; // Use for the q2 series. private const int Q2 = 7; // Use for the re series. private const int RE = 8; // Use for the phase series. private const int PHASE = 9; // Use for the MAMA series. private const int MAMA = 10; #endregion #region OnInitialize /// <summary> /// This function accepts the user parameters for the script and is called when a new indicator instance is created. /// One of the parameters accepted by it must be that of a symbol or another script that is /// based on a symbol (drawing, indicator, pattern or signal). This symbol will be used as the underlying symbol for the indicator. /// /// The parameter values can be specified from the user interface (UI) or from another script, depending on usage. /// </summary> /// -------------------------------------------------------------------------------------------------- /// PLEASE USE THE SCRIPT WIZARD (CTRL+W) TO ADD, EDIT AND REMOVE THE SCRIPT PARAMETERS /// -------------------------------------------------------------------------------------------------- /// YOU MUST SET A PARAM TAG FOR EACH PARAMETER ACCEPTED BY THIS FUNCTION. /// ALL PARAM TAGS SHOULD BE SET IN THE 'OnInitialize' REGION, RIGHT ABOVE THE 'OnInitialize' FUNCTION. /// THE ORDER OF THE TAGS MUST MATCH THE ORDER OF THE ACTUAL PARAMETERS. /// REQUIRED ATTRIBUTES: /// (1) name: The exact parameter name. /// (2) type: The type of data to collect from the user: /// Set to "Integer" when the data type is 'int' /// Set to "IntegerArray" when the data type is 'int[]' /// Set to "DateTime" when the data type is 'long' /// Set to "DateTimeArray" when the data type is 'long[]' /// Set to "Boolean" when the data type is 'bool' /// Set to "BooleanArray" when the data type is 'bool[]' /// Set to "Double" when the data type is 'double' /// Set to "DoubleArray" when the data type is 'double[]' /// Set to "String" when the data type is 'string' /// Set to "StringArray" when the data type is 'string[]' /// Set to "Indicator" when the data type is 'Indicator' /// Set to "Pattern" when the data type is 'Pattern' /// Set to "Signal" when the data type is 'Signal' /// Set to "Drawing" when the data type is 'Drawing' /// Set to "Symbol" when the data type is 'int' representing a symbol index. /// OPTIONAL ATTRIBUTES: /// (3) default: The default parameter value is only valid when the type is Integer, Boolean, Double, String or an API Type. /// (4) min: The minimum parameter value is only valid when the type is Integer or Double. /// (5) max: The maximum parameter value is only valid when the type is Integer or Double. /// EXAMPLE: <param name="" type="" default="" min="" max="">Enter the parameter description here.</param> /// -------------------------------------------------------------------------------------------------- /// <param name="indicator" type="Indicator">Use for the underlying indicator on which to calculate the indicator script.</param> /// <param name="fastLimit" type="Double" default="0.5">Use for the upper limit of the alpha value.</param> /// <param name="slowLimit" type="Double" default="0.05">Use for the lower limit of the alpha value.</param> public void OnInitialize( Indicator indicator, double fastLimit, double slowLimit) { // Set the indicator. _indicator = indicator; // Set the fastLimit. _fastLimit = fastLimit; // Set the slowLimit. _slowLimit = slowLimit; // Create the internal series required for the FAMA calculation. IndicatorCreateSeries(11); } #endregion #region OnBarUpdate /// <summary> /// This function is used for calculating the indicator value for the latest bar (see the Indicator functions). /// </summary> /// <returns>The indicator value for the latest bar.</returns> public override double OnBarUpdate() { // Retrieve number of databars proccessed thus far. int CurrentBar = IndicatorValueCount(); // Sets initial 6 bar values and prevents calling out of bounds required values by smooth and detrender. if (CurrentBar < 6) { IndicatorSetSeriesValue(MAMA, 0, BarClose(SymbolIndex())); return BarClose(SymbolIndex()); } else if (CurrentBar == 6) { IndicatorSetSeriesValue(MAMA, 0, _indicator[0]); return BarClose(SymbolIndex()); } // Set Smooth IndicatorSetSeriesValue(SMOOTH, 0, ((4 * _indicator[0] + 3 * _indicator[1] + 2 * _indicator[2] + _indicator[3]) / 10)); // Set Detrender IndicatorSetSeriesValue(DETRENDER, 0, ((0.0962 * IndicatorGetSeriesValue(SMOOTH, 0) + 0.5769 * IndicatorGetSeriesValue(SMOOTH, 2) - 0.5769 * IndicatorGetSeriesValue(SMOOTH, 4) - 0.0962 * IndicatorGetSeriesValue(SMOOTH, 6)) * (0.075 * IndicatorGetSeriesValue(PERIOD, 1) + 0.54))); // Compute InPhase and Quadrature Components IndicatorSetSeriesValue(Q1, 0, (0.0962 * IndicatorGetSeriesValue(DETRENDER, 0) + 0.5769 * IndicatorGetSeriesValue(DETRENDER, 2) - 0.5769 * IndicatorGetSeriesValue(DETRENDER, 4) - 0.0962 * IndicatorGetSeriesValue(DETRENDER, 6)) * (0.075 * IndicatorGetSeriesValue(PERIOD, 1) + 0.54)); IndicatorSetSeriesValue(I1, 0, IndicatorGetSeriesValue(DETRENDER, 3)); // Advance phase of I1 and Q1 by 90 double jI = (0.0962 * IndicatorGetSeriesValue(I1, 0) + 0.5769 * IndicatorGetSeriesValue(I1, 2) - 0.5769 * IndicatorGetSeriesValue(I1, 4) - 0.0962 * IndicatorGetSeriesValue(I1, 6)) * (0.075 * IndicatorGetSeriesValue(PERIOD, 1) + 0.54); double jQ = (0.0962 * IndicatorGetSeriesValue(Q1, 0) + 0.5769 * IndicatorGetSeriesValue(Q1, 2) - 0.5769 * IndicatorGetSeriesValue(Q1, 4) - 0.0962 * IndicatorGetSeriesValue(Q1, 6)) * (0.075 * IndicatorGetSeriesValue(PERIOD, 1) + 0.54); // Phasor addition for 3 bar averaging IndicatorSetSeriesValue(I2, 0, (IndicatorGetSeriesValue(I1, 0) - jQ)); IndicatorSetSeriesValue(Q2, 0, (IndicatorGetSeriesValue(Q1, 0) + jI)); // Smooth the I and Q components before applying the discriminator IndicatorSetSeriesValue(I2, 0, (0.2 * IndicatorGetSeriesValue(I2, 0) + 0.8 * IndicatorGetSeriesValue(I2, 1))); IndicatorSetSeriesValue(Q2, 0, (0.2 * IndicatorGetSeriesValue(Q2, 0) + 0.8 * IndicatorGetSeriesValue(Q2, 1))); // Hilbert Transform Homodyne Discriminator IndicatorSetSeriesValue(RE, 0, (IndicatorGetSeriesValue(I2, 0) * IndicatorGetSeriesValue(I2, 1) + IndicatorGetSeriesValue(Q2, 0) * IndicatorGetSeriesValue(Q2, 1))); IndicatorSetSeriesValue(IM, 0, (IndicatorGetSeriesValue(I2, 0) * IndicatorGetSeriesValue(Q2, 1) - IndicatorGetSeriesValue(Q2, 0) * IndicatorGetSeriesValue(I2, 1))); IndicatorSetSeriesValue(RE, 0, (0.2 * IndicatorGetSeriesValue(RE, 0) + 0.8 * IndicatorGetSeriesValue(RE, 1))); IndicatorSetSeriesValue(IM, 0, (0.2 * IndicatorGetSeriesValue(IM, 0) + 0.8 * IndicatorGetSeriesValue(IM, 1))); // Compute Cycle Period if (IndicatorGetSeriesValue(IM, 0) != 0.0 && IndicatorGetSeriesValue(RE, 0) != 0.0) IndicatorSetSeriesValue(PERIOD, 0, (360 / ((180 / Math.PI) * Math.Atan(IndicatorGetSeriesValue(IM, 0) / IndicatorGetSeriesValue(RE, 0))))); if (IndicatorGetSeriesValue(PERIOD, 0) > 1.5 * (IndicatorGetSeriesValue(PERIOD, 1))) IndicatorSetSeriesValue(PERIOD, 0, (1.5 * IndicatorGetSeriesValue(PERIOD, 1))); if (IndicatorGetSeriesValue(PERIOD, 0) < 0.67 * (IndicatorGetSeriesValue(PERIOD, 1))) IndicatorSetSeriesValue(PERIOD, 0, (0.67 * IndicatorGetSeriesValue(PERIOD, 1))); if (IndicatorGetSeriesValue(PERIOD, 0) < 6) IndicatorSetSeriesValue(PERIOD, 0, 6); if (IndicatorGetSeriesValue(PERIOD, 0) > 50) IndicatorSetSeriesValue(PERIOD, 0, 50); IndicatorSetSeriesValue(PERIOD, 0, (0.2 * IndicatorGetSeriesValue(PERIOD, 0) + 0.8 * IndicatorGetSeriesValue(PERIOD, 1))); // Calculates phase value if (IndicatorGetSeriesValue(I1, 0) != 0.0) IndicatorSetSeriesValue(PHASE, 0, ((180 / Math.PI) * Math.Atan(IndicatorGetSeriesValue(Q1, 0) / IndicatorGetSeriesValue(I1, 0)))); // Delta phase calculates rate of change of the homodyne discriminator double deltaPhase = IndicatorGetSeriesValue(PHASE, 1) - IndicatorGetSeriesValue(PHASE, 0); if (deltaPhase < 1) deltaPhase = 1; // Calculate alpha for the current bar within range of the given limits double alpha = _fastLimit / deltaPhase; if (alpha < _slowLimit) alpha = _slowLimit; // Calculate the MAMA IndicatorSetSeriesValue(MAMA, 0, alpha * _indicator[0] + (1 - alpha) * IndicatorGetSeriesValue(MAMA, 1)); // Calculate the FAMA return (0.5 * alpha * IndicatorGetSeriesValue(MAMA, 0) + (1 - 0.5 * alpha) * this[0]); } #endregion #region OnChartSetup /// <summary> /// This function is used for setting up the indicator on the chart and registering its pens (see the IndicatorRegisterPen function). /// </summary> public override void OnChartSetup() { // Register a pen. IndicatorRegisterPen(0, "Pen", IQ_Color.ROYAL_BLUE, IQ_DashStyle.SOLID, 2); } #endregion #region OnSelectPen /// <summary> /// This function is used for selecting a registered indicator pen with which to color the latest indicator value. /// Call the IndicatorRegisterPen function from the OnChartSetup function in order to register an indicator pen. /// </summary> /// <returns>The indicator pen index to use for coloring the latest indicator value.</returns> public override byte OnSelectPen() { // Color the indicator value with the zero pen. return 0; } #endregion } }

The Algorithmic Trading Software for Hedge Funds and Quants