blob: 6e4fd618a438647e8e2397e7963dae3ce5e91db6 [file] [log] [blame]
package io.prediction.examples.stock
import org.saddle._
import org.saddle.index.IndexTime
import scala.collection.immutable.HashMap
import breeze.linalg.DenseMatrix
import breeze.linalg.DenseVector
import com.github.nscala_time.time.Imports._
import scala.math
import math._
import nak.regress.LinearRegression
/**
* Base class for an indicator.
*
* All indicators should be defined as classes that extend
* this base class. See RSIIndicator as an example. These indicators can then
* be instantiated and passed into a StockStrategy class. Refer to tutorial
* for further explanation (found in the README.md file).
*/
@SerialVersionUID(100L)
abstract class BaseIndicator extends Serializable {
/** Calculates training series for a particular stock.
*
* @param logPrice series of logarithm of all prices for a particular stock.
* Logarithm values are recommended for more accurate results.
* @return the training series of the stock
*/
def getTraining(logPrice: Series[DateTime, Double]): Series[DateTime, Double]
/** Applies indicator on a window size of the value returned by
* getMinWindowSize() and returns the last value in the resulting series to
* be used for prediction in RegressionStrategy.
*
* @param logPrice series of logarithm of all prices for a particular stock
* @return the last value in the resulting series from the feature
* calculation
*/
def getOne(input: Series[DateTime, Double]): Double
/** Returns window size to be used in getOne()
*
* @return the window size
*/
def getMinWindowSize(): Int
}
/** Indicator that implements a relative strength index formula
*
* @constructor create an instance of an RSIIndicator
* @param period number of days to use for each of the 14 periods
* that are used in the RSI calculation
*/
class RSIIndicator(rsiPeriod: Int = 14) extends BaseIndicator {
private def getRet(dailyReturn: Series[DateTime, Double]) =
(dailyReturn - dailyReturn.shift(1)).fillNA(_ => 0.0)
def getMinWindowSize(): Int = rsiPeriod + 1
private def calcRS(logPrice: Series[DateTime, Double])
: Series[DateTime, Double] = {
//Positive and Negative Vecs
val posSeries = logPrice.mapValues[Double]((x: Double)
=> if (x > 0) x else 0)
val negSeries = logPrice.mapValues[Double]((x: Double)
=> if (x < 0) x else 0)
//Get the sum of positive/negative Frame
val avgPosSeries =
posSeries.rolling[Double] (rsiPeriod, (f: Series[DateTime,Double])
=> f.mean)
val avgNegSeries =
negSeries.rolling[Double] (rsiPeriod, (f: Series[DateTime,Double])
=> f.mean)
val rsSeries = avgPosSeries / avgNegSeries
rsSeries
}
// Computes RSI of price data over the defined training window time frame
def getTraining(logPrice: Series[DateTime, Double])
: Series[DateTime, Double] = {
val rsSeries = calcRS(getRet(logPrice))
val rsiSeries = rsSeries.mapValues[Double](
(x:Double) => 100 - ( 100 / (1 + x)))
// Fill in first 14 days offset with 50 to maintain results
rsiSeries.reindex(logPrice.rowIx).fillNA(_ => 50.0)
}
// Computes the RSI for the most recent time frame, returns single double
def getOne(logPrice: Series[DateTime, Double]): Double = {
getTraining(logPrice).last
}
}
/** Indicator that calcuate differences of closing prices
*
* @constructor create an instance of a ShiftsIndicator
* @param period number of days between any 2 closing prices to consider for
* calculating a return
*/
class ShiftsIndicator(period: Int) extends BaseIndicator {
private def getRet(logPrice: Series[DateTime, Double], frame: Int = period) =
(logPrice - logPrice.shift(frame)).fillNA(_ => 0.0)
def getMinWindowSize(): Int = period + 1
def getTraining(logPrice: Series[DateTime, Double])
: Series[DateTime, Double] = {
getRet(logPrice)
}
def getOne(logPrice: Series[DateTime, Double]): Double = {
getRet(logPrice).last
}
}