/// <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); }
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); }