/// <summary>True if a break-out to the 'high' side is detected</summary> private bool IsBreakOutInternal(Monic trend, List <Peak> peaks, bool high) { // A break-out is when the latest candle is significantly above the upper trend line // or below the lower trend line and showing signs of going further. Also, the preceding candles // must be below the trend line. // No trend, no break-out if (trend == null) { return(false); } // The latest candle must be in the break-out direction var sign = high ? +1 : -1; var latest = Instrument.Latest; if (latest.Sign != sign) { return(false); } // The price must be beyond the trend by a significant amount var price_threshold = trend.F(0.0) + sign * Instrument.MCS; if (Math.Sign(latest.Close - price_threshold) != sign) { return(false); } // Only the latest few candles can be beyond the trend line // and all must be in the direction of the break out. if (peaks[0].Index < -2) { // Allow the last two candles to be part of the break out foreach (var c in Instrument.CandleRange(peaks[0].Index, -2)) { // If more than half the candle is beyond the trend line, not a breakout var ratio = sign * Instrument.Compare(c, trend, false); if (ratio > 0) { return(false); } } } return(true); }
/// <summary>Determine a measure of trend strength</summary> private double TrendStrength(Monic trend, List <Peak> peaks, bool high) { if (trend == null) { return(0.0); } // Trend strength has to be a measure of how often price approached the trend line // and bounced off. Candles miles away from the trend line don't count, only consider // candles that span or are within a tolerance range of the trend line. var above = 0.0; var below = 0.0; var count = 0; var threshold = ConfirmTrend * Instrument.MCS; foreach (var c in Instrument.CandleRange(peaks.Back().Index, Instrument.IdxLast - WindowSize)) { var p = trend.F(c.Index + Instrument.IdxFirst); if (c.High < p - threshold) { continue; } if (c.Low > p + threshold) { continue; } above += Math.Max(0, c.High - p); below += Math.Max(0, p - c.Low); ++count; } // There must be some candles contributing var total = above + below; if (total == 0) { return(0.0); } // Return the proportion of above to below var strength = (high ? +1 : -1) * (below - above) / total; // Weight the strength based on the number of candles that contribute var weighted_count = Maths.Sigmoid(count, 6); return(strength * weighted_count); }
[Test] public void FromLinearRegression() { { var pts = new v2[] { new v2(0, 15), new v2(1, 13), new v2(2, 10), new v2(3, 7), new v2(4, 4), new v2(5, 1), }; var m = Monic.FromLinearRegression(pts); //Assert.True(Math_.FEql(m.A, 0.689393937587738)); //Assert.True(Math_.FEql(m.B, -6.10151338577271)); } { var pts = new v2[] { new v2(0, 15), new v2(1, 13), new v2(2, 10), new v2(3, 7), new v2(4, 4), new v2(5, 1), new v2(6, 5), new v2(7, 8), new v2(8, 13), new v2(9, 19), }; var q = Quadratic.FromLinearRegression(pts); Assert.True(Math_.FEql((float)q.A, 0.689394f)); Assert.True(Math_.FEql((float)q.B, -6.10151672f)); Assert.True(Math_.FEql((float)q.C, 17.3090973f)); } }
/// <summary>Return a polynomial approximation of the indicator values or null if no decent approximation could be made. 'order' is polynomial order (i.e. 1 or 2)</summary> public Extrapolation Extrapolate(int order, int history_count, Idx?idx_ = null, int series = 0) { var idx = idx_ ?? IdxLast; // Get the points to fit too var range = Instrument.IndexRange(idx - history_count, idx); var points = range .Where(x => Maths.IsFinite(Source[series][(int)(x - IdxFirst)])) .Select(x => new v2((float)x, (float)Source[series][(int)(x - IdxFirst)])) .ToArray(); // Require a minimum number of points if (points.Length <= order) { return(null); } // Create a curve using linear regression var curve = (IPolynomial)null; switch (order) { default: throw new Exception("Unsupported polynomial order"); case 2: curve = Quadratic.FromLinearRegression(points); break; case 1: curve = Monic.FromLinearRegression(points); break; } // Measure the confidence in the fit. // Map the error range to [0,+1], where > 0.5 is "good" var rms = Math.Sqrt(points.Sum(x => Maths.Sqr(x.y - curve.F(x.x)))); var conf = 1.0 - Maths.Sigmoid(rms, Instrument.MCS * 0.2); return(new Extrapolation(curve, conf)); }
// Notes: // - price is trending up if the lows are getting higher // - price is trending down if the highs are getting lower // - trend is unknown otherwise // - Break outs can be detected by comparing two sets of price peaks /// <summary>Find the highs and lows of the price</summary> /// <param name="instr">The instrument to find peaks in</param> /// <param name="iend">The last candle, i.e. look backwards from here</param> public PricePeaks(Instrument instr, Idx iend, int window_size = 5) { Instrument = instr; WindowSize = window_size; ConfirmTrend = 0.5; Beg = iend; End = iend; Highs = new List <Peak>(); Lows = new List <Peak>(); #region Find peaks { var threshold = ConfirmTrend * Instrument.MCS; var corr_hi = new Correlation(); var corr_lo = new Correlation(); // The last high/low encountered var hi = (Peak)null; var lo = (Peak)null; var done_hi = false; var done_lo = false; // Iterate through the peaks foreach (var pk in FindPeaks(iend)) { // Save the first peak as it might be a break out if (FirstPeak == null) { FirstPeak = pk; continue; } var last = pk.High ? hi : lo; var peaks = pk.High ? Highs : Lows; var corr = pk.High ? corr_hi : corr_lo; var trend = pk.High ? TrendHigh : TrendLow; var done = pk.High ? done_hi : done_lo; // First peak encountered? if (last == null) { // Just save the peak corr.Add(pk.Index, pk.Price); peaks.Add(pk); } // The trend has not been broken else if (!done) { // Second peak encountered if (trend == null) { // Form a trend line between the peaks if (pk.High) { TrendHigh = Monic.FromPoints(pk.Index, pk.Price, last.Index, last.Price); } else { TrendLow = Monic.FromPoints(pk.Index, pk.Price, last.Index, last.Price); } corr.Add(pk.Index, pk.Price); peaks.Add(pk); } // 3+ peak encountered, confirm trend strength else { // Get the predicted value from the trend line var p = trend.F(pk.Index); if (Math.Abs(p - pk.Price) < threshold) { // If within tolerance, the trend is confirmed corr.Add(pk.Index, pk.Price); if (pk.High) { TrendHigh = corr.LinearRegression; } else { TrendLow = corr.LinearRegression; } peaks.Add(pk); } else { if (pk.High) { done_hi = true; } else { done_lo = true; } // Otherwise, if the trend does not have 3 points, it is rejected if (peaks.Count < 3) { if (pk.High) { TrendHigh = null; } else { TrendLow = null; } } } } } // Save the peak as last if (pk.High) { hi = pk; } else { lo = pk; } // If the high and low trends are done, break the loop if (done_hi && done_lo) { break; } } } #endregion }