/*
This file is part of jTotus.
jTotus 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 3 of the License, or
(at your option) any later version.
jTotus 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 jTotus. If not, see <http://www.gnu.org/licenses/>.
*/
package org.jlucrum.realtime.indicators;
import org.jlucrum.realtime.eventtypes.StockTick;
import org.jlucrum.realtime.ranalyzer.Rexecutor;
import com.tictactec.ta.lib.Core;
import com.tictactec.ta.lib.MInteger;
import com.tictactec.ta.lib.RetCode;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.rosuda.REngine.REXP;
import org.rosuda.REngine.REXPMismatchException;
import org.rosuda.REngine.REngineException;
import org.rosuda.REngine.RList;
import org.rosuda.REngine.Rserve.RserveException;
/**
*
* @author Evgeni Kappinen
*/
public class SimpleTechnicalIndicators extends StockIndicator<StockTick> {
public static DataTypes dataTypes;
public enum DataTypes {
VOLUME,
LATESTPRICE,
LATESTBUY,
LATESTSELL,
LATESTHIGH,
LATESTLOW,
REVENUE
}
public SimpleTechnicalIndicators() {
super();
}
public double VOLUME(int i) {
return super.getTick(i).getVolume();
}
public double LATESTPRICE(int i) {
return super.getTick(i).getLatestPrice();
}
public double LATESTHIGH(int i) {
return super.getTick(i).getLatestHighest();
}
public double LATESTLOW(int i) {
return super.getTick(i).getLatestLowest();
}
// Volume Rate of Change (VROC)
// Source: http://www.mysmp.com/technical-analysis/volume-rate-of-change.html
public double vroc(int iIndex, int n) {
if (iIndex - n <= 0) {
return 0;
}
return ((VOLUME(iIndex) - VOLUME(iIndex - n)) / VOLUME(iIndex - n)) * 100;
}
public double vrocMultPrice(int iIndex, int n) {
if (iIndex - n <= 0) {
return 0;
}
double volume = ((VOLUME(iIndex) - VOLUME(iIndex - n)) / VOLUME(iIndex - n)) * 100;
double curPrice = ((LATESTPRICE(iIndex) - LATESTPRICE(iIndex - n)) / LATESTPRICE(iIndex - n)) * 100;
return (volume * curPrice);
}
public static double vrocVolume(double ithVoluome, double iNthVoluome) {
return ((ithVoluome - iNthVoluome) / iNthVoluome) * 100;
}
// Price and Volume Trend, for dayily data
// Source: http://en.wikipedia.org/wiki/Volume_Price_Trend
public double vptRecursive(int ithIndex) {
if (ithIndex - 1 < 0) {
return 0.0d;
}
return ((LATESTPRICE(ithIndex) - LATESTPRICE(ithIndex - 1)) / LATESTPRICE(ithIndex - 1)) / VOLUME(ithIndex) + vptRecursive(ithIndex - 1);
}
//Accumulation/distribution index
//Source: http://en.wikipedia.org/wiki/Accumulation/distribution_index
public double accdistIndexRecursive(int ithIndex) {
if (ithIndex - 1 < 0) {
return 0.0d;
}
double clv = ((LATESTPRICE(ithIndex) - LATESTLOW(ithIndex))
- (LATESTHIGH(ithIndex) - LATESTPRICE(ithIndex)))
/ (LATESTHIGH(ithIndex) - LATESTLOW(ithIndex));
double accdist = accdistIndexRecursive(ithIndex - 1) + VOLUME(ithIndex)*clv;
return accdist;
}
public double accdistIndexRecursiveVroc(int ithIndex, int n) {
if (ithIndex - 1 < 0) {
return 0.0d;
}
double clv = ((LATESTPRICE(ithIndex) - LATESTLOW(ithIndex))
- (LATESTHIGH(ithIndex) - LATESTPRICE(ithIndex)))
/ (LATESTHIGH(ithIndex) - LATESTLOW(ithIndex));
double accdist = accdistIndexRecursiveVroc(ithIndex - 1, n) + (vrocMultPrice(ithIndex, n))*clv;
return accdist;
}
public double[] RSI(double[] input,
MInteger outBegIdxDec,
MInteger outNbElementDec,
int decRSIPeriod) {
int intput_size = input.length - 1;
final Core core = new Core();
final int allocationSizeDecision = intput_size - core.rsiLookback(decRSIPeriod);
if (allocationSizeDecision <= 0) {
System.err.printf("No data for period (%d)\n", allocationSizeDecision);
return null;
}
double[] outputDec = new double[allocationSizeDecision];
RetCode decCode = core.rsi(0, intput_size - 1,
input, decRSIPeriod,
outBegIdxDec, outNbElementDec,
outputDec);
if (decCode.compareTo(RetCode.Success) != 0) {
//Error return empty method results
throw new java.lang.IllegalStateException("RSI failed:" + decRSIPeriod
+ " Begin:" + outBegIdxDec.value
+ " NumElem:" + outNbElementDec.value + "\n");
}
return outputDec;
}
private static Rexecutor rexec = null;
public static double correlation(double a[], double b[]) {
if (rexec==null) {
rexec = new Rexecutor();
}
double cor = 0.0;
try {
rexec.getConnection().assign("a", a);
rexec.getConnection().assign("b", b);
RList list = rexec.getConnection()
.eval("cor.test(log(a), log(b), method=\"spearman\")")
.asList();
cor = list.at("estimate").asDouble();
// cor = Math.pow(cor, 2);
} catch (REXPMismatchException ex) {
Logger.getLogger(SimpleTechnicalIndicators.class.getName()).log(Level.SEVERE, null, ex);
} catch (REngineException ex) {
Logger.getLogger(SimpleTechnicalIndicators.class.getName()).log(Level.SEVERE, null, ex);
}
return cor;
}
public static double[] crossCorrelation(double a[], double b[], String aName, String bName) {
int maxLag;
if (rexec == null) {
rexec = new Rexecutor();
}
double []cor = new double[2];
try {
rexec.getConnection().assign("a", a);
rexec.getConnection().assign("b", b);
maxLag = Math.max(30 , a.length / 5 );
//estimate
cor[0] = rexec.getConnection().eval("jpeg('crossCor"+aName+"_"+bName+".jpg');"
+ "ab.ccf <- ccf(diff(a),diff(b), lag.max = "+maxLag+", main=\""+aName+" and "+bName+"\");"
+ "dev.off();"
+ "max(ab.ccf$acf)").asDouble();
//lag
cor[1] = rexec.getConnection().eval("ab.indx <- which(ab.ccf$acf==max(ab.ccf$acf));"
+ "ab.ccf$lag[ab.indx]").asDouble();
System.out.printf("Cross-Correlation estimate:%f with lag:%f\n", cor[0], cor[1]);
} catch (REXPMismatchException ex) {
Logger.getLogger(SimpleTechnicalIndicators.class.getName()).log(Level.SEVERE, null, ex);
} catch (REngineException ex) {
Logger.getLogger(SimpleTechnicalIndicators.class.getName()).log(Level.SEVERE, null, ex);
}
return cor;
}
//TODO:
//http://en.wikipedia.org/wiki/Rate_of_change_%28technical_analysis%29
//http://tadoc.org/indicator/ADOSC.htm
//http://stockcharts.com/help/doku.php?id=chart_school:technical_indicators:force_index
public static void plotAB(double a[], double b[]) {
if (rexec == null) {
rexec = new Rexecutor();
}
try {
rexec.getConnection().assign("a", a);
rexec.getConnection().assign("b", b);
rexec.getConnection().eval("plot(scale(a), type=\"l\", col=1, xlim=c(0,"+a.length+"), ylim=c(-5,5));");
rexec.getConnection().eval("lines(scale(b), col=2);");
// + "lines(b, col=\"red\");");
} catch (REngineException ex) {
Logger.getLogger(SimpleTechnicalIndicators.class.getName()).log(Level.SEVERE, null, ex);
}
}
public static void plot(double a[], double b[]) {
if (rexec == null) {
rexec = new Rexecutor();
}
try {
rexec.getConnection().assign("a", a);
rexec.getConnection().assign("b", b);
rexec.getConnection().eval("plot(a, b, type=\"l\", col=1);");
// + "lines(b, col=\"red\");");
} catch (REngineException ex) {
Logger.getLogger(SimpleTechnicalIndicators.class.getName()).log(Level.SEVERE, null, ex);
}
}
public static double[] autocorrelation(double a[]) {
if (rexec == null) {
rexec = new Rexecutor();
}
double []cor = null;
try {
cor[0] = execute("a<-%d; a.ccf<-acf(a, lag=100);max(a.ccf$acf)", a).asDouble();
//lag
cor[1] = execute("a.indx <- which(a.ccf$acf==max(a.ccf$acf));"
+ "a.ccf$lag[a.indx]").asDouble();
System.out.printf("Cross-Correlation estimate:%f with lag:%f\n", cor[0], cor[1]);
// cor = Math.pow(cor, 2);
} catch (REXPMismatchException ex) {
Logger.getLogger(SimpleTechnicalIndicators.class.getName()).log(Level.SEVERE, null, ex);
} catch (REngineException ex) {
Logger.getLogger(SimpleTechnicalIndicators.class.getName()).log(Level.SEVERE, null, ex);
}
return cor;
}
//FIXME: (y <- c(3,3,)) assigments
//FIXME: y = c(1,2);
private static final Pattern patternForAssign = Pattern.compile("([\\w]+)[\\s]*<-[\\s]*%d[\\s]*;");
private static String [] paramParser(String cmds) {
ArrayList<String> listOfCmds = new ArrayList<String>();
StringBuilder cmd = new StringBuilder();
boolean startToSkip = false;
char []cmdsAsChar = cmds.toCharArray();
for (char c : cmdsAsChar) {
if (c == '\'') {
startToSkip = !startToSkip;
cmd.append(c);
} else if (c == ';') {
if (startToSkip) {
cmd.append(c);
} else {
cmd.append(c);
listOfCmds.add(cmd.toString());
cmd = new StringBuilder();
}
} else {
cmd.append(c);
}
}
listOfCmds.add(cmd.toString());
return (String[]) listOfCmds.toArray(new String[listOfCmds.size()]);
}
public static REXP execute(final String command, double[]...b) throws REngineException, REXPMismatchException {
REXP retValue = null;
int counter = 0;
if (rexec == null) {
rexec = new Rexecutor();
}
String[] cmds = paramParser(command);
for (String cmd : cmds) {
Matcher m = patternForAssign.matcher(cmd);
if (m.find()) {
String name = m.group(1);
System.out.printf("Assigning:%s\n", name);
rexec.getConnection().assign(name, b[counter++]);
} else {
retValue = rexec.getConnection().eval(cmd);
}
}
return retValue;
}
//TODO:
//http://en.wikipedia.org/wiki/Rate_of_change_%28technical_analysis%29
//http://tadoc.org/indicator/ADOSC.htm
//http://stockcharts.com/help/doku.php?id=chart_school:technical_indicators:force_index
}