/// <summary> /// Calculate Covariance. /// <see href="https://en.wikipedia.org/wiki/Covariance"/> /// </summary> /// <param name="series">this time series</param> /// <param name="other">other time series</param> /// <param name="n">number of bars</param> /// <param name="subSample">distance between bars</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>covariance time series</returns> public static ITimeSeries <double> Covariance(this ITimeSeries <double> series, ITimeSeries <double> other, int n, int subSample = 1, CacheId parentId = null, [CallerMemberName] string memberName = "", [CallerLineNumber] int lineNumber = 0) { var cacheId = new CacheId(parentId, memberName, lineNumber, series.GetHashCode(), other.GetHashCode(), n); var x = series; var y = other; return(IndicatorsBasic.BufferedLambda( (v) => { var avgX = Enumerable.Range(0, n) .Average(t => x[t * subSample]); var avgY = Enumerable.Range(0, n) .Average(t => y[t * subSample]); var covXY = Enumerable.Range(0, n) .Sum(t => (x[t * subSample] - avgX) * (y[t * subSample] - avgY)) / (n - 1.0); return covXY; }, 0.0, cacheId)); }
/// <summary> /// Calculate Pearson Correlation Coefficient. /// <see href="https://en.wikipedia.org/wiki/Pearson_correlation_coefficient"/> /// </summary> /// <param name="series">this time series</param> /// <param name="other">other time series</param> /// <param name="n">number of bars</param> /// <param name="subSample">distance between bars</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>correlation coefficient time series</returns> public static ITimeSeries <double> Correlation(this ITimeSeries <double> series, ITimeSeries <double> other, int n, int subSample = 1, CacheId parentId = null, [CallerMemberName] string memberName = "", [CallerLineNumber] int lineNumber = 0) { var cacheId = new CacheId(parentId, memberName, lineNumber, series.GetHashCode(), other.GetHashCode(), n); var x = series; var y = other; double sum = n / 2.0 * (n + 1.0); return(IndicatorsBasic.BufferedLambda( (v) => { var avgX = Enumerable.Range(0, n) .Average(t => x[t * subSample]); var avgY = Enumerable.Range(0, n) .Average(t => y[t * subSample]); var covXY = Enumerable.Range(0, n) .Sum(t => (x[t * subSample] - avgX) * (y[t * subSample] - avgY)) / n; var varX = Enumerable.Range(0, n) .Sum(t => Math.Pow(x[t * subSample] - avgX, 2.0)) / n; var varY = Enumerable.Range(0, n) .Sum(t => Math.Pow(y[t * subSample] - avgY, 2.0)) / n; var corr = covXY / Math.Max(1e-99, Math.Sqrt(varX) * Math.Sqrt(varY)); return corr; }, 0.0, cacheId)); }
/// <summary> /// Calculate Kaufman's Adaptive Moving Average, as described here: /// <see href="https://stockcharts.com/school/doku.php?id=chart_school:technical_indicators:kaufman_s_adaptive_moving_average"/> /// </summary> /// <param name="series">input time series</param> /// <param name="erPeriod">period for efficiency ratio</param> /// <param name="fastEma">period for fast EMA</param> /// <param name="slowEma">period for slow EMA</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>KAMA as time series</returns> public static ITimeSeries <double> KAMA(this ITimeSeries <double> series, int erPeriod = 10, int fastEma = 2, int slowEma = 30, CacheId parentId = null, [CallerMemberName] string memberName = "", [CallerLineNumber] int lineNumber = 0) { var cacheId = new CacheId(parentId, memberName, lineNumber, series.GetHashCode(), erPeriod, fastEma, slowEma); // TODO: we should be able to remove the try/ catch blocks here return(IndicatorsBasic.BufferedLambda( (v) => { try { double scFast = 2.0 / (1.0 + fastEma); double scSlow = 2.0 / (1.0 + slowEma); double change = Math.Abs(series[0] - series[erPeriod]); double volatility = Enumerable.Range(0, erPeriod) .Sum(t => Math.Abs(series[t] - series[t + 1])); double efficiencyRatio = change / Math.Max(1e-10, volatility); double smoothingConstant = Math.Pow(efficiencyRatio * (scFast - scSlow) + scSlow, 2); double r = smoothingConstant * (series[0] - v) + v; return double.IsNaN(r) ? 0.0 : r; } catch (Exception) { // we get here when we access bars too far in the past return series[0]; } }, series[0], cacheId)); }
/// <summary> /// Calculate Stochastic Oscillator, as described here: /// <see href="https://en.wikipedia.org/wiki/Stochastic_oscillator"/> /// </summary> /// <param name="series">input time series</param> /// <param name="n">oscillator period</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>Stochastic Oscillator as time series</returns> public static _StochasticOscillator StochasticOscillator(this ITimeSeries <double> series, int n = 14, CacheId parentId = null, [CallerMemberName] string memberName = "", [CallerLineNumber] int lineNumber = 0) { var cacheId = new CacheId(parentId, memberName, lineNumber, series.GetHashCode(), n); var container = Cache <_StochasticOscillator> .GetData( cacheId, () => new _StochasticOscillator()); double hh = series .Highest(n, cacheId)[0]; double ll = series .Lowest(n, cacheId)[0]; double price = series[0]; container.PercentK = IndicatorsBasic.BufferedLambda( v => 100.0 * (price - ll) / Math.Max(1e-10, hh - ll), 50.0, cacheId); container.PercentD = container.PercentK .SMA(3, cacheId); return(container); }
/// <summary> /// Normalize time series over number of bars; 1.0 being the average. /// </summary> /// <param name="series">input time series</param> /// <param name="n">normalizing period</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>normalized time series</returns> public static ITimeSeries <double> Normalize(this ITimeSeries <double> series, int n, CacheId parentId = null, [CallerMemberName] string memberName = "", [CallerLineNumber] int lineNumber = 0) { var cacheId = new CacheId(parentId, memberName, lineNumber, series.GetHashCode(), n); return(series.Divide(series.EMA(n, cacheId), cacheId)); }
/// <summary> /// Calculate logarithmic regression of time series. /// </summary> /// <param name="series">input time series</param> /// <param name="n">number of bars for regression</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>regression parameters as time series</returns> public static _Regression LogRegression(this ITimeSeries <double> series, int n, CacheId parentId = null, [CallerMemberName] string memberName = "", [CallerLineNumber] int lineNumber = 0) { var cacheId = new CacheId(parentId, memberName, lineNumber, series.GetHashCode(), n); return(series .Log(cacheId) .LinRegression(n, cacheId)); }
/// <summary> /// Cast time series to double /// </summary> /// <param name="series">input series of long</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>output series of double</returns> public static ITimeSeries <double> ToDouble(this ITimeSeries <long> series, CacheId parentId = null, [CallerMemberName] string memberName = "", [CallerLineNumber] int lineNumber = 0) { var cacheId = new CacheId(parentId, memberName, lineNumber, series.GetHashCode()); return(IndicatorsBasic.Lambda( (t) => (double)series[t], cacheId)); }
/// <summary> /// Calculate addition of time series and constant value. /// </summary> /// <param name="series">time series</param> /// <param name="constValue">constant value</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>time series + constant value</returns> public static ITimeSeries <double> Add(this ITimeSeries <double> series, double constValue, CacheId parentId = null, [CallerMemberName] string memberName = "", [CallerLineNumber] int lineNumber = 0) { var cacheId = new CacheId(parentId, memberName, lineNumber, series.GetHashCode(), constValue.GetHashCode()); return(IndicatorsBasic.Lambda( (t) => series[t] + constValue, cacheId)); }
/// <summary> /// Calculate historical volatility, based on log-returns. /// </summary> /// <param name="series">input time series</param> /// <param name="n">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>volatility as time series</returns> public static ITimeSeries <double> Volatility(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); return(series .LogReturn(cacheId) .StandardDeviation(n, cacheId)); }
/// <summary> /// Calculate subtraction of two time series. /// </summary> /// <param name="series1">time series #1</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> /// <param name="series2">time series #2</param> /// <returns>time series #1 - time series #2</returns> public static ITimeSeries <double> Subtract(this ITimeSeries <double> series1, ITimeSeries <double> series2, CacheId parentId = null, [CallerMemberName] string memberName = "", [CallerLineNumber] int lineNumber = 0) { var cacheId = new CacheId(parentId, memberName, lineNumber, series1.GetHashCode(), series2.GetHashCode()); return(IndicatorsBasic.Lambda( (t) => series1[t] - series2[t], cacheId)); }
/// <summary> /// Delay time series by number of bars. /// </summary> /// <param name="series">input time series</param> /// <param name="delay">delay 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>delayed time series</returns> public static ITimeSeries <double> Delay(this ITimeSeries <double> series, int delay, CacheId parentId = null, [CallerMemberName] string memberName = "", [CallerLineNumber] int lineNumber = 0) { var cacheId = new CacheId(parentId, memberName, lineNumber, series.GetHashCode(), delay); return(Lambda( (t) => series[t + delay], cacheId)); }
/// <summary> /// Calculate logarithmic return from the previous to the current value /// of the time series. /// </summary> /// <param name="series">input time series</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>logarithm of relative return</returns> public static ITimeSeries <double> LogReturn(this ITimeSeries <double> series, CacheId parentId = null, [CallerMemberName] string memberName = "", [CallerLineNumber] int lineNumber = 0) { var cacheId = new CacheId(parentId, memberName, lineNumber, series.GetHashCode()); return(Lambda( (t) => Math.Log(series[t] / series[t + 1]), cacheId)); }
/// <summary> /// Calculate range over the specified number of past bars. /// </summary> /// <param name="series">input time series</param> /// <param name="n">number of bars to search</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>range between highest and lowest value of past n bars</returns> public static ITimeSeries <double> Range(this ITimeSeries <double> series, int n, CacheId parentId = null, [CallerMemberName] string memberName = "", [CallerLineNumber] int lineNumber = 0) { var cacheId = new CacheId(parentId, memberName, lineNumber, series.GetHashCode(), n); return(series .Highest(n, cacheId) .Subtract(series .Lowest(n, cacheId), cacheId)); }
/// <summary> /// Calculate simple momentum: m = p[0] / p[n] - 1 /// </summary> /// <param name="series">input time series</param> /// <param name="n">number of bars for regression</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>simple momentum, as time series</returns> public static ITimeSeries <double> SimpleMomentum(this ITimeSeries <double> series, int n = 21, CacheId parentId = null, [CallerMemberName] string memberName = "", [CallerLineNumber] int lineNumber = 0) { var cacheId = new CacheId(parentId, memberName, lineNumber, series.GetHashCode(), n.GetHashCode()); return(IndicatorsBasic.BufferedLambda( prev => series[0] / series[n] - 1.0, 0.0, cacheId)); }
/// <summary> /// Calculate Simple Moving Average as described here: /// <see href="https://en.wikipedia.org/wiki/Moving_average#Simple_moving_average"/> /// </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>SMA time series</returns> public static ITimeSeries <double> SMA(this ITimeSeries <double> series, int n, CacheId parentId = null, [CallerMemberName] string memberName = "", [CallerLineNumber] int lineNumber = 0) { var cacheId = new CacheId(parentId, memberName, lineNumber, series.GetHashCode(), n); return(IndicatorsBasic.BufferedLambda( v => Enumerable.Range(0, n) .Sum(t => series[t]) / n, series[0], cacheId)); }
/// <summary> /// Calculate lowest value of the specified number of past bars. /// </summary> /// <param name="series">input time series</param> /// <param name="n">number of bars to search</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>lowest value of past n bars</returns> public static ITimeSeries <double> Lowest(this ITimeSeries <double> series, int n, CacheId parentId = null, [CallerMemberName] string memberName = "", [CallerLineNumber] int lineNumber = 0) { var cacheId = new CacheId(parentId, memberName, lineNumber, series.GetHashCode(), n); int N = Math.Max(1, n); return(BufferedLambda( (v) => Enumerable.Range(0, N).Min(t => series[t]), series[0], cacheId)); }
/// <summary> /// Calculate Capital Asset Pricing Model parameters. /// <see href="http://en.wikipedia.org/wiki/Capital_asset_pricing_model"/> /// This indicator uses an exponentially-weighted, incremental method of /// calculation, based on Tony Finch, which is very fast and efficient. /// /// </summary> /// <param name="series">input time series</param> /// <param name="benchmark">benchmark time series</param> /// <param name="n">length of observation window</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>container w/ CAPM parameters</returns> public static _CAPM CAPM(this ITimeSeries <double> series, ITimeSeries <double> benchmark, int n, CacheId parentId = null, [CallerMemberName] string memberName = "", [CallerLineNumber] int lineNumber = 0) { var cacheId = new CacheId(parentId, memberName, lineNumber, series.GetHashCode(), benchmark.GetHashCode(), n); var functor = Cache <CAPMFunctor> .GetData( cacheId, () => new CAPMFunctor(series, benchmark, n)); functor.Calc(); return(functor); }
/// <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 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 ITimeSeries <double> FastStandardDeviation(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 functor = Cache <FunctorStandardDeviation> .GetData( cacheId, () => new FunctorStandardDeviation(series, n)); functor.Calc(); return(functor); }
/// <summary> /// Calculate linear regression of time series. /// </summary> /// <param name="series">input time series</param> /// <param name="n">number of bars for regression</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>regression parameters as time series</returns> public static _Regression LinRegression(this ITimeSeries <double> series, int n, CacheId parentId = null, [CallerMemberName] string memberName = "", [CallerLineNumber] int lineNumber = 0) { var cacheId = new CacheId(parentId, memberName, lineNumber, series.GetHashCode(), n); var functor = Cache <LinRegressionFunctor> .GetData( cacheId, () => new LinRegressionFunctor(series, n)); functor.Calc(); return(functor); }
/// <summary> /// Return current drawdown in percent, as value between 0 and 1. /// </summary> /// <param name="series">input time series</param> /// <param name="n">length of observation window</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>drawdown as time series</returns> public static ITimeSeries <double> Drawdown(this ITimeSeries <double> series, int n, CacheId parentId = null, [CallerMemberName] string memberName = "", [CallerLineNumber] int lineNumber = 0) { var cacheId = new CacheId(parentId, memberName, lineNumber, series.GetHashCode(), n); // TODO: rewrite this, using buffered lambda, see MaxDrawdown return(IndicatorsBasic.Const(1.0, cacheId) .Subtract( series .Divide( series .Highest(n, cacheId), cacheId), cacheId)); }
/// <summary> /// Calculate return over maximum drawdown. /// </summary> /// <param name="series">input time series</param> /// <param name="n">length of observation window</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>RoMaD</returns> public static ITimeSeries <double> ReturnOnMaxDrawdown(this ITimeSeries <double> series, int n, CacheId parentId = null, [CallerMemberName] string memberName = "", [CallerLineNumber] int lineNumber = 0) { var cacheId = new CacheId(parentId, memberName, lineNumber, series.GetHashCode(), n); return(IndicatorsBasic.BufferedLambda( (p) => { double ret = series[0] / series[n] - 1.0; double dd = series.MaxDrawdown(n)[0]; return ret / Math.Max(1e-3, dd); }, 0.0, cacheId)); }
/// <summary> /// Calculate volatility estimate from recent trading range. /// </summary> /// <param name="series">input time series</param> /// <param name="n">length of calculation window</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>volatility as time series</returns> public static ITimeSeries <double> VolatilityFromRange(this ITimeSeries <double> series, int n, CacheId parentId = null, [CallerMemberName] string memberName = "", [CallerLineNumber] int lineNumber = 0) { var cacheId = new CacheId(parentId, memberName, lineNumber, series.GetHashCode(), n); return(IndicatorsBasic.BufferedLambda( (v) => { double hi = series.Highest(n)[0]; double lo = series.Lowest(n)[0]; return 0.80 * Math.Sqrt(1.0 / n) * Math.Log(hi / lo); }, 0.0, cacheId)); }
/// <summary> /// Calculate Williams %R, as described here: /// <see href="https://en.wikipedia.org/wiki/Williams_%25R"/> /// </summary> /// <param name="series">input time series</param> /// <param name="n">period</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>Williams %R as time series</returns> public static ITimeSeries <double> WilliamsPercentR(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); return(IndicatorsBasic.BufferedLambda( (v) => { double hh = series.Highest(n)[0]; double ll = series.Lowest(n)[0]; double price = series[0]; return -100.0 * (hh - price) / Math.Max(1e-10, hh - ll); }, -50.0, cacheId)); }
/// <summary> /// Calculate Exponential Moving Average, as described here: /// <see href="https://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average"/> /// </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>EMA time series</returns> public static ITimeSeries <double> EMA(this ITimeSeries <double> series, int n, CacheId parentId = null, [CallerMemberName] string memberName = "", [CallerLineNumber] int lineNumber = 0) { var cacheId = new CacheId(parentId, memberName, lineNumber, series.GetHashCode(), n); double alpha = 2.0 / (Math.Max(1, n) + 1); return(IndicatorsBasic.BufferedLambda( (v) => { double r = alpha * (series[0] - v) + v; return double.IsNaN(r) ? 0.0 : r; }, series[0], cacheId)); }
/// <summary> /// Calculate Ehlers' Zero Lag Exponential Moving Average, as described here: /// <see href="https://en.wikipedia.org/wiki/Zero_lag_exponential_moving_average"/> /// </summary> /// <param name="series">input time series</param> /// <param name="period">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>ZLEMA as time series</returns> public static ITimeSeries <double> ZLEMA(this ITimeSeries <double> series, int period, CacheId parentId = null, [CallerMemberName] string memberName = "", [CallerLineNumber] int lineNumber = 0) { var cacheId = new CacheId(parentId, memberName, lineNumber, series.GetHashCode(), period); int lag = (int)Math.Round((period - 1.0) / 2.0); return(series .Add( series .Subtract( series .Delay(lag, cacheId), cacheId), cacheId) .EMA(period, cacheId)); }
/// <summary> /// Calculate MACD, as described here: /// <see href="https://en.wikipedia.org/wiki/MACD"/> /// </summary> /// <param name="series">input time series</param> /// <param name="fast">fast EMA period</param> /// <param name="slow">slow EMA period</param> /// <param name="signal">signal line period</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></returns> public static _MACD MACD(this ITimeSeries <double> series, int fast = 12, int slow = 26, int signal = 9, CacheId parentId = null, [CallerMemberName] string memberName = "", [CallerLineNumber] int lineNumber = 0) { var cacheId = new CacheId(parentId, memberName, lineNumber, series.GetHashCode(), fast, slow, signal); var container = Cache <_MACD> .GetData( cacheId, () => new _MACD()); container.Fast = series.EMA(fast, cacheId); container.Slow = series.EMA(slow, cacheId); container.MACD = container.Fast.Subtract(container.Slow, cacheId); container.Signal = container.MACD.EMA(signal, cacheId); container.Divergence = container.MACD.Subtract(container.Signal, cacheId); return(container); }
/// <summary> /// Calculate Sharpe Ratio, as described here: /// <see href="https://en.wikipedia.org/wiki/Sharpe_ratio"/> /// </summary> /// <param name="series"></param> /// <param name="riskFreeRate"></param> /// <param name="n"></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></returns> public static ITimeSeries <double> SharpeRatio(this ITimeSeries <double> series, ITimeSeries <double> riskFreeRate, int n, CacheId parentId = null, [CallerMemberName] string memberName = "", [CallerLineNumber] int lineNumber = 0) { var cacheId = new CacheId(parentId, memberName, lineNumber, series.GetHashCode(), riskFreeRate.GetHashCode(), n); var excessReturn = series .Return(cacheId) .Subtract(riskFreeRate .Return(cacheId), cacheId); return(excessReturn .EMA(n, cacheId) .Divide(excessReturn .FastStandardDeviation(n, cacheId) .Max(IndicatorsBasic.Const(1e-10, cacheId), cacheId), cacheId)); }
/// <summary> /// Calculate Hull Moving Average, as described here: /// <see href="https://alanhull.com/hull-moving-average"/> /// </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>HMA time series</returns> public static ITimeSeries <double> HMA(this ITimeSeries <double> series, int n, CacheId parentId = null, [CallerMemberName] string memberName = "", [CallerLineNumber] int lineNumber = 0) { var cacheId = new CacheId(parentId, memberName, lineNumber, series.GetHashCode(), n); // http://www.incrediblecharts.com/indicators/weighted_moving_average.php // Integer(SquareRoot(Period)) WMA [2 x Integer(Period/2) WMA(Price) - Period WMA(Price)] int n2 = (int)Math.Round(n / 2.0); int n3 = (int)Math.Round(Math.Sqrt(n)); return(series .WMA(n2, cacheId) .Multiply(2.0, cacheId) .Subtract( series .WMA(n, cacheId), cacheId) .WMA(n3, cacheId)); }
/// <summary> /// Return maximum drawdown. /// </summary> /// <param name="series">input time series</param> /// <param name="n">length of observation window</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>maximum drawdown as time series</returns> public static ITimeSeries <double> MaxDrawdown(this ITimeSeries <double> series, int n, CacheId parentId = null, [CallerMemberName] string memberName = "", [CallerLineNumber] int lineNumber = 0) { var cacheId = new CacheId(parentId, memberName, lineNumber, series.GetHashCode(), n); return(IndicatorsBasic.BufferedLambda( (p) => { double highestHigh = 0.0; double maxDrawdown = 0.0; for (int t = n - 1; t >= 0; t--) { highestHigh = Math.Max(highestHigh, series[t]); maxDrawdown = Math.Max(maxDrawdown, 1.0 - series[t] / highestHigh); } return maxDrawdown; }, 0.0, cacheId)); }