/// <summary>
        /// Determines if the thrust displaced 3x3 is sufficiently smooth.
        /// </summary>
        /// <param name="frameIndex">The _chart index of the first frame of the newly discovered thrust.</param>
        /// <param name="direction">The direction of the thrust.</param>
        /// <returns>True if the thrust 3x3 has 2 or more dips. False if the thrust 3x3 has less than 2 dips.</returns>
        protected virtual bool Displaced3x3HasDoubleDip(int frameIndex, EPatternDirection direction)
        {
            bool result = false;

            List <Frame> thrustFrames = _chart.Frames.Skip(frameIndex).ToList();

            bool doubleDipFound = false;

            for (int i = 0; (i + 2) < thrustFrames.Count; i++)
            {
                Frame      frameA = thrustFrames[i], frameB = thrustFrames[i + 1], frameC = thrustFrames[i + 2];
                IIndicator displaced3x3A = frameA.Indicators.First(k => k.Type == IndicatorType.DisplacedMovingAverage && k.ParentOrdinal == 0);
                IIndicator displaced3x3B = frameB.Indicators.First(k => k.Type == IndicatorType.DisplacedMovingAverage && k.ParentOrdinal == 0);
                IIndicator displaced3x3C = frameC.Indicators.First(k => k.Type == IndicatorType.DisplacedMovingAverage && k.ParentOrdinal == 0);

                if (direction == EPatternDirection.Up)
                {
                    doubleDipFound = displaced3x3A.Value > displaced3x3B.Value && displaced3x3B.Value > displaced3x3C.Value;
                }
                else
                {
                    doubleDipFound = displaced3x3A.Value < displaced3x3B.Value && displaced3x3B.Value < displaced3x3C.Value;
                }

                if (doubleDipFound)
                {
                    return(true);
                }
            }

            return(result);
        }
        /// <summary>
        /// Composes a thrust object
        /// </summary>
        /// <param name="reactionFrame">Reaction frame of the thrust</param>
        /// <param name="direction">Direction of the thrust</param>
        /// <returns></returns>
        protected virtual Thrust BuildThrust(Frame reactionFrame, EPatternDirection direction)
        {
            // create the thrust
            Thrust thrust = new Thrust()
            {
                Direction   = direction,
                Instrument  = _chart.Instrument,
                Granularity = _chart.Granularity,
                SignalTime  = reactionFrame.Bar.time,
                SignalPrice = direction == EPatternDirection.Up ? reactionFrame.Bar.lowMid : reactionFrame.Bar.highMid,
                Side        = direction == EPatternDirection.Up ? MACC.Constants.SignalSide.Buy : MACC.Constants.SignalSide.Sell
            };

            return(thrust);
        }
        protected virtual int GetCheckAcrossIndex(Frame reactionFrame, EPatternDirection direction)
        {
            int checkAcrossIndex = _chart.Frames.IndexOf(reactionFrame);

            while (checkAcrossIndex < _chart.Frames.Count)
            {
                Frame      frame        = _chart.Frames[checkAcrossIndex];
                IIndicator displaced3x3 = frame.Indicators.First(k => k.Type == IndicatorType.DisplacedMovingAverage && k.ParentOrdinal == 0);

                if (direction == EPatternDirection.Up && frame.Bar.lowMid >= displaced3x3.Value)
                {
                    break;
                }
                else if (direction == EPatternDirection.Down && frame.Bar.highMid <= displaced3x3.Value)
                {
                    break;
                }

                checkAcrossIndex++;
            }

            return(checkAcrossIndex);
        }
        /*
         *  upThrust
         *  traverse >= 10 bars
         *  >= 7 bars have low bid above the 3x3
         *  >= 5 consecutive bars must have low bid above the 3x3
         *  sequence may not start with > 3 bars with low bid below 3x3
         *  if (any of?) top 3 bars of sequence have low bid below 3x3 then lowest low bid of those below the 3x3 must be > .386 ask price
         *  thrust range must be >= (.06 x thrust ReactionPrice)
         *  MACD histogram value of all thrust frames must be > 0
         *
         *  downThrust
         *  traverse >= 10 bars
         *  >= 7 bars have low bid above the 3x3
         *  >= 5 consecutive bars must have low bid above the 3x3
         *  sequence may not start with > 3 bars with low bid below 3x3
         *  if (any of?) top 3 bars of sequence have low bid below 3x3 then lowest low bid of those below the 3x3 must be > .386 ask price
         *  thrust range must be >= (.06 x thrust ReactionPrice)
         *  MACD histogram value of all thrust frames must be > 0
         *  above or below the 3x3?
         */
        #endregion

        /// <summary>
        /// Searches the chart for a thrust pattern.
        /// </summary>
        /// <param name="chart">Chart object containing at least 20 frames</param>
        /// <returns>A tradeable thrust object, if found.</returns>
        protected override Thrust DetectThrust()
        {
            Thrust thrust = null;

            if (_chart.Frames.Count >= 20)
            {
                Frame frame;

                bool  upThrustFound = false, downThrustFound = false;
                short above3x3Count = 0, above3x3Links = 0, below3x3Count = 0, below3x3Links = 0, across3x3Count = 0;

                Func <short> sequentialAbove3x3Count = () => { return(above3x3Links > 0 ? (short)(above3x3Links + 1) : (short)0); };
                Func <short> sequentialBelow3x3Count = () => { return(below3x3Links > 0 ? (short)(below3x3Links + 1) : (short)0); };

                AboveBelowDMA        previousCandleSide = AboveBelowDMA.Unknown, currentCandleSide = AboveBelowDMA.Unknown;
                List <AboveBelowDMA> leaderSides = new List <AboveBelowDMA>();

                // only need to sniff 20 frames to find thrust
                // if not in 20 frames, whatever thrust exists has yielded to a consolidation period .. no good
                short loopCount = 0;
                while (loopCount < 20)
                {
                    loopCount++;
                    int i = _chart.Frames.Count - loopCount;
                    frame = _chart.Frames[i];

                    IPriceBar  candle       = frame.Bar;
                    IIndicator displaced3x3 = frame.Indicators.First(k => k.Type == IndicatorType.DisplacedMovingAverage && k.ParentOrdinal == 0);

                    if (candle.lowMid >= displaced3x3.Value)
                    {
                        above3x3Count++;

                        if (previousCandleSide == AboveBelowDMA.Above)
                        {
                            above3x3Links++;
                        }

                        currentCandleSide = previousCandleSide = AboveBelowDMA.Above;
                    }
                    else if (candle.highMid <= displaced3x3.Value)
                    {
                        below3x3Count++;

                        if (previousCandleSide == AboveBelowDMA.Below)
                        {
                            below3x3Links++;
                        }

                        currentCandleSide = previousCandleSide = AboveBelowDMA.Below;
                    }
                    else
                    {
                        across3x3Count++;
                        currentCandleSide = previousCandleSide = AboveBelowDMA.Across;
                    }

                    // save the first 3 bar sides
                    if (leaderSides.Count < 3)
                    {
                        leaderSides.Add(currentCandleSide);
                    }

                    // start thrust parsing once 10 bars have been examined
                    if (loopCount > 9)
                    {
                        upThrustFound = sequentialAbove3x3Count() >= 6 && sequentialBelow3x3Count() <= 3 &&
                                        CheckLeaders(leaderSides, AboveBelowDMA.Above) && !Displaced3x3HasDoubleDip(i, EPatternDirection.Up);

                        downThrustFound = sequentialBelow3x3Count() >= 6 && sequentialAbove3x3Count() <= 3 &&
                                          CheckLeaders(leaderSides, AboveBelowDMA.Below) && !Displaced3x3HasDoubleDip(i, EPatternDirection.Down);

                        if (upThrustFound || downThrustFound)
                        {
                            EPatternDirection direction     = upThrustFound ? EPatternDirection.Up : EPatternDirection.Down;
                            Frame             reactionFrame = FindReactionFrame(frame, i, direction);

                            if (reactionFrame != null)
                            {
                                int           checkAcrossIndex = GetCheckAcrossIndex(reactionFrame, direction);
                                AboveBelowDMA checkAcrossSide  = upThrustFound ? AboveBelowDMA.Above : AboveBelowDMA.Below;

                                if (CheckAcrossFrames(checkAcrossIndex, checkAcrossSide))
                                {
                                    thrust = BuildThrust(reactionFrame, direction);
                                }
                            }

                            break;
                        }
                    }
                }
            }

            return(thrust);
        }
        /*
         *  upThrust
         *  traverse >= 10 bars
         *  >= 7 bars have low bid above the 3x3
         *  >= 5 consecutive bars must have low bid above the 3x3
         *  sequence may not start with > 3 bars with low bid below 3x3
         *  if (any of?) top 3 bars of sequence have low bid below 3x3 then lowest low bid of those below the 3x3 must be > .386 ask price
         *  thrust range must be >= (.06 x thrust ReactionPrice)
         *  MACD histogram value of all thrust frames must be > 0
         *
         *  downThrust
         *  traverse >= 10 bars
         *  >= 7 bars have low bid above the 3x3
         *  >= 5 consecutive bars must have low bid above the 3x3
         *  sequence may not start with > 3 bars with low bid below 3x3
         *  if (any of?) top 3 bars of sequence have low bid below 3x3 then lowest low bid of those below the 3x3 must be > .386 ask price
         *  thrust range must be >= (.06 x thrust ReactionPrice)
         *  MACD histogram value of all thrust frames must be > 0
         *  above or below the 3x3?
         */
        #endregion

        /// <summary>
        /// Searches the chart for a thrust pattern.
        /// </summary>
        /// <param name="chart">Chart object containing at least 20 frames</param>
        /// <returns>A tradeable thrust object, if found.</returns>
        protected override Thrust DetectThrust()
        {
            Thrust thrust = null;

            if (_chart.Frames.Count >= 20)
            {
                Frame frame;

                bool  upThrustFound = false, downThrustFound = false;
                short above3x3Count = 0, below3x3Count = 0, across3x3Count = 0;

                AboveBelowDMA        currentCandleSide = AboveBelowDMA.Unknown;
                List <AboveBelowDMA> leaderSides = new List <AboveBelowDMA>();
                List <Frame>         acrossFrames = new List <Frame>();

                // only need to sniff 20 frames to find thrust
                // if not in 20 frames, whatever thrust exists has yielded to a consolidation period .. no good
                for (int loopCount = 1; true; loopCount++)
                {
                    int i = _chart.Frames.Count - loopCount;
                    frame = _chart.Frames[i];

                    IPriceBar  candle       = frame.Bar;
                    IIndicator displaced3x3 = frame.Indicators.First(k => k.Type == IndicatorType.DisplacedMovingAverage && k.ParentOrdinal == 0);

                    if (candle.lowMid >= displaced3x3.Value)
                    {
                        above3x3Count++;
                        currentCandleSide = AboveBelowDMA.Above;
                    }
                    else if (candle.highMid <= displaced3x3.Value)
                    {
                        below3x3Count++;
                        currentCandleSide = AboveBelowDMA.Below;
                    }
                    else
                    {
                        across3x3Count++;
                        currentCandleSide = AboveBelowDMA.Across;
                        acrossFrames.Add(frame);
                    }

                    // save the first 3 bar sides
                    if (leaderSides.Count < 3)
                    {
                        leaderSides.Add(currentCandleSide);
                    }

                    // start thrust parsing once 10 bars have been examined
                    if (loopCount == 7)
                    {
                        upThrustFound = above3x3Count >= 5 && below3x3Count == 0 && CheckLeaders(leaderSides, AboveBelowDMA.Above) && CheckAcrossFrames(acrossFrames, AboveBelowDMA.Above);

                        downThrustFound = below3x3Count >= 5 && above3x3Count == 0 && CheckLeaders(leaderSides, AboveBelowDMA.Below) && CheckAcrossFrames(acrossFrames, AboveBelowDMA.Below);

                        if (upThrustFound || downThrustFound)
                        {
                            EPatternDirection direction     = upThrustFound ? EPatternDirection.Up : EPatternDirection.Down;
                            Frame             reactionFrame = FindReactionFrame(frame, i, direction);

                            if (reactionFrame != null)
                            {
                                int           checkAcrossIndex = GetCheckAcrossIndex(reactionFrame, direction);
                                AboveBelowDMA checkAcrossSide  = upThrustFound ? AboveBelowDMA.Above : AboveBelowDMA.Below;

                                if (CheckAcrossFrames(checkAcrossIndex, checkAcrossSide))
                                {
                                    thrust = BuildThrust(reactionFrame, direction);
                                }
                            }
                        }

                        break;
                    }
                }
            }

            return(thrust);
        }
Exemple #6
0
 public Pattern()
 {
     _direction = EPatternDirection.None;
 }
        /// <summary>
        /// Finds the reaction frame of a detected thrust
        /// </summary>
        /// <param name="pilotFrame">Frame at which a valid thrust was confirmed</param>
        /// <param name="frameIndex">The _chart index of the pilot frame</param>
        /// <param name="direction">The direction of the confirmed thrust</param>
        /// <returns>The reaction frame of the thrust</returns>
        protected virtual Frame FindReactionFrame(Frame pilotFrame, int frameIndex, EPatternDirection direction)
        {
            #region reaction frame logic
            /// for up thrust
            ///   tentative rxn frame is the one with lowBid < 3x3 and 2 preceding candles has a higher chained lowBid
            ///   do a forward search of the chart bars to confirm the rxn frame has lowest lowBid
            ///   the confirmed reaction frame is the one with the lowest lowBid
            /// for down thrust
            ///   tentative rxn frame is the one with highBid > 3x3 and 2 preceding candles has a lower chained highBid
            ///   do a forward search of the chart bars to confirm the rxn frame has highest highBid
            ///   the confirmed reaction frame is the one with the highest highBid

            /// do not sweat the breakouts from consolidation (see type G in notes)
            /// these should yield deeper rxn at worst meaning a missed trade
            /// the timekill of the trade will mop these up
            #endregion

            // find reaction candle
            Frame     horizonFrame          = pilotFrame;
            IPriceBar horizonCandle         = horizonFrame.Bar;
            bool      horizonFrameConfirmed = false;

            while (!horizonFrameConfirmed)
            {
                Frame      precedingFrame    = _chart.Frames[frameIndex - 1];
                IPriceBar  precedingCandle01 = _chart.Frames[frameIndex - 1].Bar;
                IPriceBar  precedingCandle02 = _chart.Frames[frameIndex - 2].Bar;
                IIndicator horizon3x3        = horizonFrame.Indicators.First(k => k.Type == IndicatorType.DisplacedMovingAverage);

                if (direction == EPatternDirection.Up)
                {
                    if (horizonCandle.lowMid < horizon3x3.Value &&
                        precedingCandle01.lowMid > horizonCandle.lowMid &&
                        precedingCandle02.lowMid > precedingCandle01.lowMid)
                    {
                        horizonFrameConfirmed = true;
                    }
                }

                if (direction == EPatternDirection.Down)
                {
                    if (horizonCandle.highMid > horizon3x3.Value &&
                        precedingCandle01.highMid < horizonCandle.highMid &&
                        precedingCandle02.highMid < precedingCandle01.highMid)
                    {
                        horizonFrameConfirmed = true;
                    }
                }

                if (!horizonFrameConfirmed)
                {
                    horizonFrame  = precedingFrame;
                    horizonCandle = precedingCandle01;

                    frameIndex--;
                }
            }

            // forward search to confirm rxn frame
            Frame reactionFrame = horizonFrame;
            for (int i = frameIndex; i < _chart.Frames.Count; i++)
            {
                Frame currentFrame = _chart.Frames[i];

                if (direction == EPatternDirection.Up)
                {
                    if (currentFrame.Bar.lowMid <= reactionFrame.Bar.lowMid)
                    {
                        reactionFrame = currentFrame;
                    }
                }
                else
                {
                    if (currentFrame.Bar.highMid >= reactionFrame.Bar.highMid)
                    {
                        reactionFrame = currentFrame;
                    }
                }
            }

            return(reactionFrame);
        }