R/MACD.R

#
#   TTR: Technical Trading Rules
#
#   Copyright (C) 2007-2013  Joshua M. Ulrich
#
#   This program is free software: you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation, either version 2 of the License, or
#   (at your option) any later version.
#
#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
#
#   You should have received a copy of the GNU General Public License
#   along with this program.  If not, see <http://www.gnu.org/licenses/>.
#

#'MACD Oscillator
#'
#'The MACD was developed by Gerald Appel and is probably the most popular price
#'oscillator.  The MACD function documented in this page compares a fast moving
#'average (MA) of a series with a slow MA of the same series.  It can be used
#'as a generic oscillator for any univariate series, not only price.
#'
#'The MACD function either subtracts the fast MA from the slow MA, or finds the
#'rate of change between the fast MA and the slow MA.
#'
#'@param x Object that is coercible to xts or matrix; usually price, but can be
#'volume, etc.
#'@param nFast Number of periods for fast moving average.
#'@param nSlow Number of periods for slow moving average.
#'@param nSig Number of periods for signal moving average.
#'@param maType Either:
#' \enumerate{
#'   \item A function or a string naming the function to be called.
#'   \item A \emph{list} with the first component like (1) above, and
#'     additional parameters specified as \emph{named} components.
#'     See Examples.
#' }
#'@param percent logical; if \code{TRUE}, the percentage difference between the
#'fast and slow moving averages is returned, otherwise the difference between
#'the respective averages is returned.
#'@param \dots Other arguments to be passed to the \code{maType} function in
#'case (1) above.
#'@return A object of the same class as \code{x} or a matrix (if \code{try.xts}
#'fails) containing the columns:
#' \describe{
#'  \item{ macd }{ The price (volume, etc.) oscillator. }
#'  \item{ signal }{ The oscillator signal line (a moving average of the oscillator). }
#' }
#'@note The MACD is a special case of the general oscillator applied to price.
#'The MACD can be used as a general oscillator applied to any series. Time
#'periods for the MACD are often given as 26 and 12, but the original formula
#'used exponential constants of 0.075 and 0.15, which are closer to
#'25.6667 and 12.3333 periods.
#'@author Joshua Ulrich
#'@seealso See \code{\link{EMA}}, \code{\link{SMA}}, etc. for moving average
#'options; and note Warning section.
#'@references The following site(s) were used to code/document this
#'indicator:
#'\cr Moving Average Convergence/Divergence (MACD):\cr
#'\url{https://www.fmlabs.com/reference/MACD.htm}\cr
#'\url{https://www.metastock.com/Customer/Resources/TAAZ/?p=66}\cr
#'\url{https://www.linnsoft.com/techind/macd}\cr
#'\url{https://school.stockcharts.com/doku.php?id=technical_indicators:moving_average_convergence_divergence_macd}\cr
#'\cr Price Oscillator:\cr
#'\url{https://www.fmlabs.com/reference/PriceOscillator.htm}\cr
#'\url{https://www.fmlabs.com/reference/PriceOscillatorPct.htm}\cr
#'\url{https://www.metastock.com/Customer/Resources/TAAZ/?p=94}\cr
#'\url{https://school.stockcharts.com/doku.php?id=technical_indicators:price_oscillators_ppo}\cr
#'\cr Volume Oscillator:\cr
#'\url{https://www.fmlabs.com/reference/PVO.htm}\cr
#'\url{https://www.metastock.com/Customer/Resources/TAAZ/?p=122}\cr
#'@keywords ts
#'@examples
#'
#' data(ttrc)
#'
#' macd  <- MACD( ttrc[,"Close"], 12, 26, 9, maType="EMA" )
#' macd2 <- MACD( ttrc[,"Close"], 12, 26, 9,
#'          maType=list(list(SMA), list(EMA, wilder=TRUE), list(SMA)) )
#'
"MACD" <-
function(x, nFast=12, nSlow=26, nSig=9, maType, percent=TRUE, ...) {

  # Oscillators

  # WISHLIST:
  # Add capability to allow 'ma.slow' and 'ma.fast' to be vectors
  # containing MAs, which would allow the oscillator to be constructed
  # using MAs of different prices.

  # Default MA
  if(missing(maType)) {
    maType <- 'EMA'
  }

  # Case of two different 'maType's for both MAs.
  if( is.list(maType) ) {

    # Make sure maType is a list of lists
    maTypeInfo <- sapply(maType,is.list)
    if( !(all(maTypeInfo) && length(maTypeInfo) == 3) ) {
      stop("If \'maType\' is a list, you must specify\n ",
      "*three* MAs (see Examples section of ?MACD)")
    }

    # If MA function has 'n' arg, see if it's populated in maType;
    # if it isn't, populate it with function's formal 'n'
    if( !is.null( formals(maType[[1]][[1]])$n ) && is.null( maType[[1]]$n ) ) {
      maType[[1]]$n <- nFast
    }
    if( !is.null( formals(maType[[2]][[1]])$n ) && is.null( maType[[2]]$n ) ) {
      maType[[2]]$n <- nSlow
    }
    if( !is.null( formals(maType[[3]][[1]])$n ) && is.null( maType[[3]]$n ) ) {
      maType[[3]]$n <- nSig
    }
    
    mavg.fast <- do.call( maType[[1]][[1]], c( list(x), maType[[1]][-1] ) )
    mavg.slow <- do.call( maType[[2]][[1]], c( list(x), maType[[2]][-1] ) )

  }
  
  # Case of one 'maType' for both MAs.
  else {
  
    mavg.fast <- do.call( maType, c( list(x), list(n=nFast, ...) ) )
    mavg.slow <- do.call( maType, c( list(x), list(n=nSlow, ...) ) )

  }
  
  if(percent) {
    macd <- 100 * ( mavg.fast / mavg.slow - 1 )
  } else {
    macd <- mavg.fast - mavg.slow
  }

  if( is.list(maType) ) {
    signal <- do.call( maType[[3]][[1]], c( list( macd ), maType[[3]][-1] ) )
  } else
    signal <- do.call( maType, c( list( macd ), list(n=nSig, ...) ) )

  result <- cbind( macd, signal )
  colnames(result) <- c( "macd", "signal" )
  
  return( result )
}

Try the TTR package in your browser

Any scripts or data that you put into this service are public.

TTR documentation built on Sept. 1, 2020, 5:08 p.m.