org.yccheok.jstock.charting.TechnicalAnalysis.java Source code

Java tutorial

Introduction

Here is the source code for org.yccheok.jstock.charting.TechnicalAnalysis.java

Source

/*
 * JStock - Free Stock Market Software
 * Copyright (C) 2012 Yan Cheng CHEOK <yccheok@yahoo.com>
 *
 * 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, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

package org.yccheok.jstock.charting;

import com.tictactec.ta.lib.Core;
import com.tictactec.ta.lib.MInteger;
import java.util.Date;
import java.util.List;
import org.apache.commons.lang.ArrayUtils;
import org.jfree.data.time.Day;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeSeriesCollection;
import org.jfree.data.xy.XYDataset;
import org.yccheok.jstock.engine.Stock;

/**
 *
 * @author yccheok
 */
public class TechnicalAnalysis {

    /**
     * Returns the latest EMA.
     *
     * @param values list of raw data input
     * @param period the duration period
     * @return the latest EMA
     */
    public static Double createEMA(java.util.List<Double> values, int period) {
        if (period <= 0) {
            throw new java.lang.IllegalArgumentException("period must be greater than 0");
        }
        final int size = values.size();
        final Core core = new Core();
        final int allocationSize = size - core.emaLookback(period);
        if (allocationSize <= 0) {
            return null;
        }

        final double[] output = new double[allocationSize];
        final MInteger outBegIdx = new MInteger();
        final MInteger outNbElement = new MInteger();
        double[] _values = ArrayUtils.toPrimitive(values.toArray(new Double[0]));
        core.ema(0, values.size() - 1, _values, period, outBegIdx, outNbElement, output);

        return output[outNbElement.value - 1];
    }

    /**
     * Returns the latest MFI.
     *
     * @param highs list of high price
     * @param lows list of low price
     * @param closes list of close price
     * @param volumes list of volume
     * @param period the duration period
     * @return the latest MFI
     */
    public static Double createMFI(java.util.List<Double> highs, java.util.List<Double> lows,
            java.util.List<Double> closes,
            // TODO: CRITICAL LONG BUG REVISED NEEDED.
            java.util.List<Long> volumes, int period) {
        if (period <= 0) {
            throw new java.lang.IllegalArgumentException("period must be greater than 0");
        }
        if (highs.size() != lows.size() || highs.size() != closes.size() || highs.size() != volumes.size()) {
            throw new java.lang.IllegalArgumentException("input list must be same size");
        }

        final int size = highs.size();
        final Core core = new Core();
        final int allocationSize = size - core.mfiLookback(period);
        if (allocationSize <= 0) {
            return null;
        }
        final double[] output = new double[allocationSize];
        final MInteger outBegIdx = new MInteger();
        final MInteger outNbElement = new MInteger();
        double[] _highs = ArrayUtils.toPrimitive(highs.toArray(new Double[0]));
        double[] _lows = ArrayUtils.toPrimitive(lows.toArray(new Double[0]));
        double[] _closes = ArrayUtils.toPrimitive(closes.toArray(new Double[0]));
        long[] _volumes = ArrayUtils.toPrimitive(volumes.toArray(new Long[0]));

        double[] dv = new double[_volumes.length];
        // Do not use System.arraycopy.
        // It will cause java.lang.ArrayStoreException, due to type mismatch.
        for (int i = 0; i < dv.length; i++) {
            dv[i] = _volumes[i];
        }
        core.mfi(0, _highs.length - 1, _highs, _lows, _closes, dv, period, outBegIdx, outNbElement, output);

        return output[outNbElement.value - 1];
    }

    /**
     * Returns the latest RSI.
     *
     * @param values list of raw data input
     * @param period the duration period
     * @return the latest RSI
     */
    public static Double createRSI(java.util.List<Double> values, int period) {
        if (period <= 0) {
            throw new java.lang.IllegalArgumentException("period must be greater than 0");
        }
        final int size = values.size();
        final Core core = new Core();
        final int allocationSize = size - core.rsiLookback(period);
        if (allocationSize <= 0) {
            return null;
        }

        final double[] output = new double[allocationSize];
        final MInteger outBegIdx = new MInteger();
        final MInteger outNbElement = new MInteger();
        double[] _values = ArrayUtils.toPrimitive(values.toArray(new Double[0]));
        core.rsi(0, values.size() - 1, _values, period, outBegIdx, outNbElement, output);

        return output[outNbElement.value - 1];
    }

    // Moving Average Convergence/Divergence Fix 12/26
    public static MACD.Result createMACDFix(List<Double> values, int period) {
        if (period <= 0) {
            throw new java.lang.IllegalArgumentException("period must be greater than 0");
        }
        final int size = values.size();
        final Core core = new Core();
        final int allocationSize = size - core.macdFixLookback(period);
        if (allocationSize <= 0) {
            return null;
        }

        final double[] outMACD = new double[allocationSize];
        final double[] outMACDSignal = new double[allocationSize];
        final double[] outMACDHist = new double[allocationSize];
        final MInteger outBegIdx = new MInteger();
        final MInteger outNbElement = new MInteger();
        double[] _values = ArrayUtils.toPrimitive(values.toArray(new Double[0]));

        core.macdFix(0, values.size() - 1, _values, period, outBegIdx, outNbElement, outMACD, outMACDSignal,
                outMACDHist);

        return MACD.Result.newInstance(outMACD[outNbElement.value - 1], outMACDSignal[outNbElement.value - 1],
                outMACDHist[outNbElement.value - 1]);
    }

    /**
     * Returns SMA time series for charting purpose.
     *
     * @param chartDatas list of chart data
     * @param name name for the time series
     * @param period the duration period
     * @return SMA time series for charting purpose
     */
    public static TimeSeries createSMA(List<ChartData> chartDatas, String name, int period) {
        if (period <= 0) {
            throw new java.lang.IllegalArgumentException("period must be greater than 0");
        }

        final TimeSeries series = new TimeSeries(name);
        final int num = chartDatas.size();

        final Core core = new Core();
        final int allocationSize = num - core.smaLookback(period);
        if (allocationSize <= 0) {
            return series;
        }
        final double[] last = new double[num];
        // Fill up last array.
        for (int i = 0; i < num; i++) {
            last[i] = chartDatas.get(i).lastPrice;
        }

        final double[] output = new double[allocationSize];
        final MInteger outBegIdx = new MInteger();
        final MInteger outNbElement = new MInteger();

        core.sma(0, last.length - 1, last, period, outBegIdx, outNbElement, output);

        for (int i = 0; i < outNbElement.value; i++) {
            series.add(new Day(new Date(chartDatas.get(i + outBegIdx.value).timestamp)), output[i]);
        }

        return series;
    }

    public static MACD.ChartResult createMACD(List<ChartData> chartDatas, String name, MACD.Period period) {
        final int num = chartDatas.size();
        final Core core = new Core();
        final int allocationSize = num - core.macdLookback(period.fastPeriod, period.slowPeriod, period.period);
        if (allocationSize <= 0) {
            return null;
        }

        final double[] last = new double[num];
        // Fill up last array.
        for (int i = 0; i < num; i++) {
            last[i] = chartDatas.get(i).lastPrice;
        }

        final double[] outMACD = new double[allocationSize];
        final double[] outMACDSignal = new double[allocationSize];
        final double[] outMACDHist = new double[allocationSize];
        final MInteger outBegIdx = new MInteger();
        final MInteger outNbElement = new MInteger();

        core.macd(0, last.length - 1, last, period.fastPeriod, period.slowPeriod, period.period, outBegIdx,
                outNbElement, outMACD, outMACDSignal, outMACDHist);

        final TimeSeries macdTimeSeries = new TimeSeries(name);
        final TimeSeries macdSignalTimeSeries = new TimeSeries(name);
        final TimeSeries macdHistTimeSeries = new TimeSeries(name);

        for (int i = 0; i < outNbElement.value; i++) {
            Day day = new Day(new Date(chartDatas.get(i + outBegIdx.value).timestamp));
            macdTimeSeries.add(day, outMACD[i]);
            macdSignalTimeSeries.add(day, outMACDSignal[i]);
            macdHistTimeSeries.add(day, outMACDHist[i]);
        }

        return MACD.ChartResult.newInstance(new TimeSeriesCollection(macdTimeSeries),
                new TimeSeriesCollection(macdSignalTimeSeries), new TimeSeriesCollection(macdHistTimeSeries));
    }

    /**
     * Returns EMA time series for charting purpose.
     *
     * @param chartDatas list of chart data
     * @param name name for the time series
     * @param period the duration period
     * @return EMA time series for charting purpose
     */
    public static TimeSeries createEMA(List<ChartData> chartDatas, String name, int period) {
        if (period <= 0) {
            throw new java.lang.IllegalArgumentException("period must be greater than 0");
        }

        final TimeSeries series = new TimeSeries(name);
        final int num = chartDatas.size();

        final Core core = new Core();
        final int allocationSize = num - core.emaLookback(period);
        if (allocationSize <= 0) {
            return series;
        }
        final double[] last = new double[num];
        // Fill up last array.
        for (int i = 0; i < num; i++) {
            last[i] = chartDatas.get(i).lastPrice;
        }

        final double[] output = new double[allocationSize];
        final MInteger outBegIdx = new MInteger();
        final MInteger outNbElement = new MInteger();

        core.ema(0, last.length - 1, last, period, outBegIdx, outNbElement, output);

        for (int i = 0; i < outNbElement.value; i++) {
            series.add(new Day(new Date(chartDatas.get(i + outBegIdx.value).timestamp)), output[i]);
        }

        return series;
    }

    /**
     * Returns CCI XYDataset for charting purpose.
     *
     * @param chartDatas list of chart data
     * @param name name for the XYDataset
     * @param period the duration period
     * @return CCI XYDataset for charting purpose
     */
    public static XYDataset createCCI(List<ChartData> chartDatas, String name, int period) {
        if (period <= 0) {
            throw new java.lang.IllegalArgumentException("period must be greater than 0");
        }

        final TimeSeries series = new TimeSeries(name);
        final int num = chartDatas.size();

        final Core core = new Core();
        final int allocationSize = num - core.cciLookback(period);
        if (allocationSize <= 0) {
            return new TimeSeriesCollection(series);
        }

        final double[] high = new double[num];
        final double[] low = new double[num];
        final double[] close = new double[num];
        // Fill up last array.
        for (int i = 0; i < num; i++) {
            high[i] = chartDatas.get(i).highPrice;
            low[i] = chartDatas.get(i).lowPrice;
            close[i] = chartDatas.get(i).lastPrice;
        }

        final double[] output = new double[allocationSize];
        final MInteger outBegIdx = new MInteger();
        final MInteger outNbElement = new MInteger();

        core.cci(0, num - 1, high, low, close, period, outBegIdx, outNbElement, output);

        for (int i = 0; i < outNbElement.value; i++) {
            series.add(new Day(new Date(chartDatas.get(i + outBegIdx.value).timestamp)), output[i]);
        }

        return new TimeSeriesCollection(series);
    }

    /**
     * Returns RSI XYDataset for charting purpose.
     *
     * @param chartDatas list of chart data
     * @param name name for the XYDataset
     * @param period the duration period
     * @return RSI XYDataset for charting purpose
     */
    public static XYDataset createRSI(List<ChartData> chartDatas, String name, int period) {
        if (period <= 0) {
            throw new java.lang.IllegalArgumentException("period must be greater than 0");
        }

        final TimeSeries series = new TimeSeries(name);
        final int num = chartDatas.size();

        final Core core = new Core();
        final int allocationSize = num - core.rsiLookback(period);
        if (allocationSize <= 0) {
            return new TimeSeriesCollection(series);
        }

        final double[] last = new double[num];
        // Fill up last array.
        for (int i = 0; i < num; i++) {
            last[i] = chartDatas.get(i).lastPrice;
        }

        final double[] output = new double[allocationSize];
        final MInteger outBegIdx = new MInteger();
        final MInteger outNbElement = new MInteger();

        core.rsi(0, last.length - 1, last, period, outBegIdx, outNbElement, output);

        for (int i = 0; i < outNbElement.value; i++) {
            series.add(new Day(new Date(chartDatas.get(i + outBegIdx.value).timestamp)), output[i]);
        }

        return new TimeSeriesCollection(series);
    }

    /**
     * Returns MFI XYDataset for charting purpose.
     *
     * @param chartDatas list of chart data
     * @param name name for the XYDataset
     * @param period the duration period
     * @return MFI XYDataset for charting purpose
     */
    public static XYDataset createMFI(List<ChartData> chartDatas, String name, int period) {
        if (period <= 0) {
            throw new java.lang.IllegalArgumentException("period must be greater than 0");
        }

        final TimeSeries series = new TimeSeries(name);
        final int num = chartDatas.size();

        final Core core = new Core();
        final int allocationSize = num - core.mfiLookback(period);
        if (allocationSize <= 0) {
            return new TimeSeriesCollection(series);
        }

        final double[] high = new double[num];
        final double[] low = new double[num];
        final double[] close = new double[num];
        final double[] volume = new double[num];
        // Fill up last array.
        for (int i = 0; i < num; i++) {
            high[i] = chartDatas.get(i).highPrice;
            low[i] = chartDatas.get(i).lowPrice;
            close[i] = chartDatas.get(i).lastPrice;
            volume[i] = chartDatas.get(i).volume;
        }

        final double[] output = new double[allocationSize];
        final MInteger outBegIdx = new MInteger();
        final MInteger outNbElement = new MInteger();

        core.mfi(0, num - 1, high, low, close, volume, period, outBegIdx, outNbElement, output);

        for (int i = 0; i < outNbElement.value; i++) {
            series.add(new Day(new Date(chartDatas.get(i + outBegIdx.value).timestamp)), output[i]);
        }

        return new TimeSeriesCollection(series);
    }

    /**
     * Returns typical price of the stock.
     * 
     * @param stock the stock
     * @return typical price of the stock
     */
    public static double getTypicalPrice(Stock stock) {
        return (stock.getHighPrice() + stock.getLowPrice() + stock.getLastPrice()) / 3.0;
    }
}