/// <summary> /// Calculate standard deviation, based on exponentially weighted filters. This is an /// incremental calculation, based on Tony Finch, which is very fast and efficient. /// </summary> /// <param name="series">input time series</param> /// <param name="n">filtering length</param> /// <param name="parentId">caller cache id, optional</param> /// <param name="memberName">caller's member name, optional</param> /// <param name="lineNumber">caller line number, optional</param> /// <returns>variance as time series</returns> public static _SemiDeviation SemiDeviation(this ITimeSeries <double> series, int n = 10, CacheId parentId = null, [CallerMemberName] string memberName = "", [CallerLineNumber] int lineNumber = 0) { var cacheId = new CacheId(parentId, memberName, lineNumber, series.GetHashCode(), n); var container = Cache <_SemiDeviation> .GetData( cacheId, () => new _SemiDeviation()); container.Average = series.SMA(n); container.Downside = IndicatorsBasic.BufferedLambda( v => { var downSeries = Enumerable.Range(0, n) .Where(t => series[t] < container.Average[0]); if (downSeries.Count() == 0) { return(0.0); } else { return(Math.Sqrt(downSeries .Average(t => Math.Pow(series[t] - container.Average[0], 2.0)))); } }, 0.0, cacheId); container.Upside = IndicatorsBasic.BufferedLambda( v => { var upSeries = Enumerable.Range(0, n) .Where(t => series[t] > container.Average[0]); if (upSeries.Count() == 0) { return(0.0); } else { return(Math.Sqrt(upSeries .Average(t => Math.Pow(series[t] - container.Average[0], 2.0)))); } }, 0.0, cacheId); return(container); }
/// <summary> /// Calculate Bollinger Bands, as described here: /// <see href="https://traderhq.com/ultimate-guide-to-bollinger-bands/"/>. /// </summary> /// <param name="series">input time series</param> /// <param name="n">length of calculation</param> /// <param name="stdev">width of bands</param> /// <param name="parentId">caller cache id, optional</param> /// <param name="memberName">caller's member name, optional</param> /// <param name="lineNumber">caller line number, optional</param> /// <returns>Bollinger Band time series</returns> public static _BollingerBands BollingerBands(this ITimeSeries <double> series, int n = 20, double stdev = 2.0, CacheId parentId = null, [CallerMemberName] string memberName = "", [CallerLineNumber] int lineNumber = 0) { var cacheId = new CacheId(parentId, memberName, lineNumber, series.GetHashCode(), n, stdev.GetHashCode()); var container = Cache <_BollingerBands> .GetData( cacheId, () => new _BollingerBands()); var stdevSeries = series.StandardDeviation(n, cacheId).Multiply(stdev, cacheId); container.Middle = series.SMA(n, cacheId); container.Upper = container.Middle.Add(stdevSeries, cacheId); container.Lower = container.Middle.Subtract(stdevSeries, cacheId); container.PercentB = IndicatorsBasic.BufferedLambda( prev => (series[0] - container.Lower[0]) / Math.Max(1e-10, container.Upper[0] - container.Lower[0]), 0.0, cacheId); return(container); }
/// <summary> /// Calculate Commodity Channel Index of input time series, as described here: /// <see href="https://en.wikipedia.org/wiki/Commodity_channel_index"/> /// </summary> /// <param name="series">input time series</param> /// <param name="n">averaging length</param> /// <param name="parentId">caller cache id, optional</param> /// <param name="memberName">caller's member name, optional</param> /// <param name="lineNumber">caller line number, optional</param> /// <returns>CCI time series</returns> public static ITimeSeries <double> CCI(this ITimeSeries <double> series, int n = 20, CacheId parentId = null, [CallerMemberName] string memberName = "", [CallerLineNumber] int lineNumber = 0) { var cacheId = new CacheId(parentId, memberName, lineNumber, series.GetHashCode(), n); return(IndicatorsBasic.BufferedLambda( (v) => { ITimeSeries <double> delta = series .Subtract( series .SMA(n, cacheId), cacheId); ITimeSeries <double> meanDeviation = delta .AbsValue(cacheId) .SMA(n, cacheId); return delta[0] / Math.Max(1e-10, 0.015 * meanDeviation[0]); }, 0.5, cacheId)); }