package org.chartsy.main.data; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.List; import org.chartsy.main.utils.SerialVersion; /** * * @author viorel.gheba */ public class Dataset implements Serializable { private static final long serialVersionUID = SerialVersion.APPVERSION; private List<DataItem> data; public Dataset() { this.data = new ArrayList<DataItem>(); } public Dataset(List<DataItem> list) { data = list; } public boolean isNull() { return (data == null); } public boolean isEmpty() { return data.isEmpty(); } public void sort() { Collections.sort(data); } public int getItemsCount() { return data.size(); } public int getLastIndex() { int index = data.size() - 1; return index < 0 ? 0 : index; } public double getDiffValue() { double lastClose = getCloseAt(getLastIndex()); double prevClose = getCloseAt(getLastIndex() - 1); double diff = lastClose - prevClose; return diff; } public double getPercentValue() { double lastClose = getCloseAt(getLastIndex()); double prevClose = getCloseAt(getLastIndex() - 1); double percent = ((lastClose - prevClose) / prevClose) * 100; return percent; } public List<DataItem> getDataItems() { return data; } public DataItem getDataItem(int index) { if (index < 0 || index >= data.size()) return null; return data.get(index); } public void setDataItem(int index, DataItem item) { if (index < 0 || index >= data.size()) return; data.set(index, item); } public void addDataItem(DataItem item) { data.add(item); } public long[] getTimeValues() { long[] values = new long[data.size()]; for (int i = 0; i < data.size(); i++) if (data.get(i) != null) values[i] = data.get(i).getTime(); return values; } public Date[] getDateValues() { Date[] values = new Date[data.size()]; for (int i = 0; i < data.size(); i++) if (data.get(i) != null) values[i] = data.get(i).getDate(); return values; } public double[] getOpenValues() { double[] values = new double[data.size()]; for (int i = 0; i < data.size(); i++) if (data.get(i) != null) values[i] = data.get(i).getOpen(); return values; } public double[] getHighValues() { double[] values = new double[data.size()]; for (int i = 0; i < data.size(); i++) if (data.get(i) != null) values[i] = data.get(i).getHigh(); return values; } public double[] getLowValues() { double[] values = new double[data.size()]; for (int i = 0; i < data.size(); i++) if (data.get(i) != null) values[i] = data.get(i).getLow(); return values; } public double[] getCloseValues() { double[] values = new double[data.size()]; for (int i = 0; i < data.size(); i++) if (data.get(i) != null) values[i] = data.get(i).getClose(); return values; } public double[] getVolumeValues() { double[] values = new double[data.size()]; for (int i = 0; i < data.size(); i++) if (data.get(i) != null) values[i] = data.get(i).getVolume(); return values; } public long getTimeAt(int index) { if (index < 0 || index > data.size()) { return 0; } return data.get(index) != null ? data.get(index).getTime() : 0; } public void setTimeAt(int index, long value) { if (index < 0 || index > data.size()) { return; } if (data.get(index) == null) { return; } data.get(index).setTime(value); } public Date getDateAt(int index) { if (index < 0 || index > data.size()) { return null; } return data.get(index).getDate(); } public double getOpenAt(int index) { if (index < 0 || index > data.size()) { return 0; } return data.get(index) != null ? data.get(index).getOpen() : 0; } public void setOpenAt(int index, double value) { if (index < 0 || index > data.size()) { return; } data.get(index).setOpen(value); } public double getHighAt(int index) { if (index < 0 || index > data.size()) { return 0; } return data.get(index) != null ? data.get(index).getHigh() : 0; } public void setHighAt(int index, double value) { if (index < 0 || index > data.size()) { return; } data.get(index).setHigh(value); } public double getLowAt(int index) { if (index < 0 || index > data.size()) { return 0; } return data.get(index) != null ? data.get(index).getLow() : 0; } public void setLowAt(int index, double value) { if (index < 0 || index > data.size()) { return; } data.get(index).setLow(value); } public double getCloseAt(int index) { if (index < 0 || index > data.size()) { return 0; } return data.get(index) != null ? data.get(index).getClose() : 0; } public void setCloseAt(int index, double value) { if (index < 0 || index > data.size()) { return; } data.get(index).setClose(value); } public double getVolumeAt(int index) { if (index < 0 || index > data.size()) { return 0; } return data.get(index) != null ? data.get(index).getVolume() : 0; } public void setVolumeAt(int index, double value) { if (index < 0 || index > data.size()) { return; } data.get(index).setVolume(value); } public DataItem getLastDataItem() { return data.get(getLastIndex()); } public long getLastTime() { return data.get(getLastIndex()) != null ? data.get(getLastIndex()).getTime() : 0; } public Date getLastDate() { return data.get(getLastIndex()) != null ? data.get(getLastIndex()).getDate() : null; } public double getLastOpen() { return data.get(getLastIndex()) != null ? data.get(getLastIndex()).getOpen() : 0; } public double getLastHigh() { return data.get(getLastIndex()) != null ? data.get(getLastIndex()).getHigh() : 0; } public double getLastLow() { return data.get(getLastIndex()) != null ? data.get(getLastIndex()).getLow() : 0; } public double getLastClose() { return data.get(getLastIndex()) != null ? data.get(getLastIndex()).getClose() : 0; } public double getLastVolume() { return data.get(getLastIndex()) != null ? data.get(getLastIndex()).getVolume() : 0; } public double getPriceAt(int index, String price) { int p = getPrice(price); return getPriceAt(index, p); } public double getPriceAt(int index, int price) { switch (price) { case OPEN_PRICE: return getOpenAt(index); case HIGH_PRICE: return getHighAt(index); case LOW_PRICE: return getLowAt(index); case CLOSE_PRICE: return getCloseAt(index); case VOLUME_PRICE: return getVolumeAt(index); } return 0; } public double getLastPrice(String price) { int p = getPrice(price); return getLastPrice(p); } public double getLastPrice(int price) { switch (price) { case OPEN_PRICE: return getLastOpen(); case HIGH_PRICE: return getLastHigh(); case LOW_PRICE: return getLastLow(); case CLOSE_PRICE: return getLastClose(); case VOLUME_PRICE: return getLastVolume(); } return 0; } public double getMin() { return getMin(LOW_PRICE); } public double getMin(String price) { int p = getPrice(price); return getMin(p); } public double getMin(int price) { List<DataItem> list = getDataItems(); double value = Double.MAX_VALUE; switch (price) { case OPEN_PRICE: for (DataItem item : list) { if (item != null && value > item.getOpen()) value = item.getOpen(); } return value; case HIGH_PRICE: for (DataItem item : list) { if (item != null && value > item.getHigh()) value = item.getHigh(); } return value; case LOW_PRICE: for (DataItem item : list) { if (item != null && value > item.getLow()) value = item.getLow(); } return value; case CLOSE_PRICE: for (DataItem item : list) { if (item != null && value > item.getClose()) value = item.getClose(); } return value; case VOLUME_PRICE: for (DataItem item : list) { if (item != null && value > item.getVolume()) value = item.getVolume(); } return value; } return 0; } public double getMinNotZero() { return getMinNotZero(LOW_PRICE); } public double getMinNotZero(String price) { int p = getPrice(price); return getMinNotZero(p); } public double getMinNotZero(int price) { List<DataItem> list = getDataItems(); double value = Double.MAX_VALUE; switch (price) { case OPEN_PRICE: for (DataItem item : list) { if (item != null && item.getOpen() != 0 && value > item.getOpen()) value = item.getOpen(); } return value; case HIGH_PRICE: for (DataItem item : list) { if (item != null && item.getHigh() != 0 && value > item.getHigh()) value = item.getHigh(); } return value; case LOW_PRICE: for (DataItem item : list) { if (item != null && item.getLow() != 0 && value > item.getLow()) value = item.getLow(); } return value; case CLOSE_PRICE: for (DataItem item : list) { if (item != null && item.getClose() != 0 && value > item.getClose()) value = item.getClose(); } return value; case VOLUME_PRICE: for (DataItem item : list) { if (item != null && item.getVolume() != 0 && value > item.getVolume()) value = item.getVolume(); } return value; } return 0; } public double getMax() { return getMax(HIGH_PRICE); } public double getMax(String price) { int p = getPrice(price); return getMax(p); } public double getMax(int price) { List<DataItem> list = getDataItems(); double value = Double.MIN_VALUE; switch (price) { case OPEN_PRICE: for (DataItem item : list) { if (item != null && value < item.getOpen()) value = item.getOpen(); } return value; case HIGH_PRICE: for (DataItem item : list) { if (item != null && value < item.getHigh()) value = item.getHigh(); } return value; case LOW_PRICE: for (DataItem item : list) { if (item != null && value < item.getLow()) value = item.getLow(); } return value; case CLOSE_PRICE: for (DataItem item : list) { if (item != null && value < item.getClose()) value = item.getClose(); } return value; case VOLUME_PRICE: for (DataItem item : list) { if (item != null && value < item.getVolume()) value = item.getVolume(); } return value; } return 0; } public double getMaxNotZero() { return getMaxNotZero(HIGH_PRICE); } public double getMaxNotZero(String price) { int p = getPrice(price); return getMaxNotZero(p); } public double getMaxNotZero(int price) { List<DataItem> list = getDataItems(); double value = Double.MIN_VALUE; switch (price) { case OPEN_PRICE: for (DataItem item : list) { if (item != null && item.getOpen() != 0 && value < item.getOpen()) value = item.getOpen(); } return value; case HIGH_PRICE: for (DataItem item : list) { if (item != null && item.getHigh() != 0 && value < item.getHigh()) value = item.getHigh(); } return value; case LOW_PRICE: for (DataItem item : list) { if (item != null && item.getLow() != 0 && value < item.getLow()) value = item.getLow(); } return value; case CLOSE_PRICE: for (DataItem item : list) { if (item != null && item.getClose() != 0 && value < item.getClose()) value = item.getClose(); } return value; case VOLUME_PRICE: for (DataItem item : list) { if (item != null && item.getVolume() != 0 && value < item.getVolume()) value = item.getVolume(); } return value; } return 0; } public List<DataItem> getVisibleItems(int period, int end) { List<DataItem> list = new ArrayList<DataItem>(); for (int i = 0; i < period; i++) { int j = end - period + i; if (j < getItemsCount() && j >= 0) { list.add(data.get(j)); } } return list; } public DataItem[] getVisibleItemsArray(int period, int end) { DataItem[] list = new DataItem[period]; for (int i = 0; i < period; i++) { int j = end - period + i; if (j < getItemsCount() && j >= 0) { list[i] = data.get(j); } } return list; } public Dataset getVisibleDataset(int period, int end) { return new Dataset(getVisibleItems(period, end)); } public static Dataset EMPTY(int count) { List<DataItem> list = new ArrayList<DataItem>(); for (int i = 0; i < count; i++) { list.add(i, null); } return new Dataset(list); } public static Dataset CONST(Dataset d, double ct) { if (d == null) { return null; } int count = d.getItemsCount(); Dataset result = EMPTY(count); for (int i = 0; i < count; i++) { result.setDataItem(i, new DataItem(d.getTimeAt(i), ct)); } return result; } public static Dataset LOG(Dataset d) { if (d == null) { return null; } int count = d.getItemsCount(); Dataset result = EMPTY(count); for (int i = 0; i < count; i++) { if (d.getDataItem(i) != null) { double open = Math.log10(d.getOpenAt(i)); double high = Math.log10(d.getHighAt(i)); double low = Math.log10(d.getLowAt(i)); double close = Math.log10(d.getCloseAt(i)); double volume = Math.log10(d.getVolumeAt(i)); result.setDataItem(i, new DataItem(d.getTimeAt(i), open, high, low, close, volume)); } } return result; } public static Dataset SUM(Dataset d1, Dataset d2) { if (d1 == null || d2 == null) { return null; } int count = d1.getItemsCount(); Dataset result = EMPTY(count); for (int i = 0; i < count; i++) { result.setDataItem(i, new DataItem( d1.getTimeAt(i), d1.getOpenAt(i) + d2.getOpenAt(i), d1.getHighAt(i) + d2.getHighAt(i), d1.getLowAt(i) + d2.getLowAt(i), d1.getCloseAt(i) + d2.getCloseAt(i), d1.getVolumeAt(i) + d2.getVolumeAt(i))); } return result; } public static Dataset DIFF(Dataset d1, Dataset d2) { if (d1 == null || d2 == null) { return null; } int count = d1.getItemsCount(); Dataset result = EMPTY(count); for (int i = 0; i < count; i++) { result.setDataItem(i, new DataItem( d1.getTimeAt(i), d1.getOpenAt(i) - d2.getOpenAt(i), d1.getHighAt(i) - d2.getHighAt(i), d1.getLowAt(i) - d2.getLowAt(i), d1.getCloseAt(i) - d2.getCloseAt(i), d1.getVolumeAt(i) - d2.getVolumeAt(i))); } return result; } public static Dataset MULTIPLY(Dataset d, double value) { if (d == null) { return null; } int count = d.getItemsCount(); Dataset result = EMPTY(count); for (int i = 0; i < count; i++) { result.setDataItem(i, new DataItem( d.getTimeAt(i), d.getOpenAt(i) * value, d.getHighAt(i) * value, d.getLowAt(i) * value, d.getCloseAt(i) * value, d.getVolumeAt(i) * value)); } return result; } public static Dataset DIV(Dataset d, double value) { if (d == null) { return null; } int count = d.getItemsCount(); Dataset result = EMPTY(count); for (int i = 0; i < count; i++) { result.setDataItem(i, new DataItem( d.getTimeAt(i), d.getOpenAt(i) / value, d.getHighAt(i) / value, d.getLowAt(i) / value, d.getCloseAt(i) / value, d.getVolumeAt(i) / value)); } return result; } public static Dataset HMA(Dataset dataset, int period) { if (dataset == null) { return null; } Dataset wma_n2 = Dataset.WMA(dataset, period / 2); Dataset wma_n = Dataset.WMA(dataset, period); Dataset two_wma_n2 = Dataset.MULTIPLY(wma_n2, 2); Dataset diff = Dataset.DIFF(two_wma_n2, wma_n); Dataset result = Dataset.WMA(diff, (int) Math.sqrt(period)); return result; } public static Dataset SMA(Dataset dataset, int period) { if (dataset == null) { return null; } int count = dataset.getItemsCount(); Dataset result = Dataset.EMPTY(count); int j = 0; for (j = 0; j < count && (dataset.getDataItem(j) == null); j++) { result.setDataItem(j, null); } for (int i = j + period - 1; i < count; i++) { long time = dataset.getTimeAt(i); double open = 0; double high = 0; double low = 0; double close = 0; double volume = 0; for (int k = 0; k < period; k++) { open += dataset.getOpenAt(i - k); high += dataset.getHighAt(i - k); low += dataset.getLowAt(i - k); close += dataset.getCloseAt(i - k); volume += dataset.getVolumeAt(i - k); } open /= (double) period; high /= (double) period; low /= (double) period; close /= (double) period; volume /= (double) period; result.setDataItem(i, new DataItem(time, open, high, low, close, volume)); } return result; } public static Dataset EMA(Dataset dataset, int period) { if (dataset == null) { return null; } int count = dataset.getItemsCount(); Dataset result = Dataset.EMPTY(count); int j = 0; for (j = 0; j < count && (dataset.getDataItem(j) == null); j++) { result.setDataItem(j, null); } double open = 0; double high = 0; double low = 0; double close = 0; double volume = 0; for (int i = j; i < period + j && i < count; i++) { open += dataset.getOpenAt(i); high += dataset.getHighAt(i); low += dataset.getLowAt(i); close += dataset.getCloseAt(i); volume += dataset.getVolumeAt(i); if (i == period + j - 1) { open /= (double) period; high /= (double) period; low /= (double) period; close /= (double) period; volume /= (double) period; result.setDataItem(i, new DataItem(dataset.getTimeAt(i), open, high, low, close, volume)); } else { result.setDataItem(i, null); } } double k = 2 / ((double) (period + 1)); for (int i = period + j; i < dataset.getItemsCount(); i++) { open = (dataset.getOpenAt(i) - open) * k + open; high = (dataset.getHighAt(i) - high) * k + high; low = (dataset.getLowAt(i) - low) * k + low; close = (dataset.getCloseAt(i) - close) * k + close; volume = (dataset.getVolumeAt(i) - volume) * k + volume; result.setDataItem(i, new DataItem(dataset.getTimeAt(i), open, high, low, close, volume)); } return result; } /* * Wilder does not use the standard exponential moving average formula. * * Indicators affected are: * Average True Range * Directional Movement System * Relative Strength Index * Twiggs Money Flow developed by Colin Twiggs using Wilder's moving average formula. * * http://www.incrediblecharts.com/indicators/wilder_moving_average.php * http://user42.tuxfamily.org/chart/manual/Exponential-Moving-Average.html */ public static Dataset EMAWilder(Dataset dataset, int period) { int classic_ema_period = 2*period - 1; return Dataset.EMA(dataset, classic_ema_period); } public static Dataset WMA(Dataset dataset, int period) { if (dataset == null) { return null; } int count = dataset.getItemsCount(); Dataset result = Dataset.EMPTY(count); double denominator = ((double) period * ((double) period + 1)) / 2; int j = 0; for (j = 0; j < count && (dataset.getDataItem(j) == null); j++) { result.setDataItem(j, null); } for (int i = j; i < period + j; i++) { result.setDataItem(i, null); } for (int i = period + j; i < count; i++) { double open = 0; double high = 0; double low = 0; double close = 0; double volume = 0; for (int k = i - period; k < i; k++) { open += (period - i + k + 1) * dataset.getOpenAt(k); high += (period - i + k + 1) * dataset.getHighAt(k); low += (period - i + k + 1) * dataset.getLowAt(k); close += (period - i + k + 1) * dataset.getCloseAt(k); volume += (period - i + k + 1) * dataset.getVolumeAt(k); } open /= denominator; high /= denominator; low /= denominator; close /= denominator; volume /= denominator; result.setDataItem(i, new DataItem(dataset.getTimeAt(i), open, high, low, close, volume)); } return result; } public static Dataset TEMA(Dataset dataset, int period) { if (dataset == null) { return null; } int count = dataset.getItemsCount(); Dataset result = Dataset.EMPTY(dataset.getItemsCount()); Dataset ema1 = EMA(dataset, period); Dataset ema2 = EMA(ema1, period); Dataset ema3 = EMA(ema2, period); int j = 0; for (j = 0; j < count && (dataset.getDataItem(j) == null || ema1.getDataItem(j) == null || ema2.getDataItem(j) == null || ema3.getDataItem(j) == null); j++) { result.setDataItem(j, null); } for (int i = j; i < count; i++) { double open = 0; double high = 0; double low = 0; double close = 0; double volume = 0; if (ema1.getCloseAt(i) != 0 && ema2.getCloseAt(i) != 0 && ema3.getCloseAt(i) != 0) { open = 3 * ema1.getOpenAt(i) - 3 * ema2.getOpenAt(i) + ema3.getOpenAt(i); high = 3 * ema1.getHighAt(i) - 3 * ema2.getHighAt(i) + ema3.getHighAt(i); low = 3 * ema1.getLowAt(i) - 3 * ema2.getLowAt(i) + ema3.getLowAt(i); close = 3 * ema1.getCloseAt(i) - 3 * ema2.getCloseAt(i) + ema3.getCloseAt(i); volume = 3 * ema1.getVolumeAt(i) - 3 * ema2.getVolumeAt(i) + ema3.getVolumeAt(i); } result.setDataItem(i, new DataItem(dataset.getTimeAt(i), open, high, low, close, volume)); } return result; } public static Dataset[] ADX(Dataset dataset, int period) { if (dataset == null) { return null; } int count = dataset.getItemsCount(); Dataset pdi = Dataset.EMPTY(count); Dataset mdi = Dataset.EMPTY(count); Dataset dx = Dataset.EMPTY(count); Dataset tr = Dataset.EMPTY(count); Dataset hmhp = Dataset.EMPTY(count); Dataset lmlp = Dataset.EMPTY(count); for (int i = 1; i < count; i++) { long time = dataset.getTimeAt(i); double tr0 = dataset.getHighAt(i) - dataset.getCloseAt(i - 1); double tr1 = Math.abs(dataset.getHighAt(i) - dataset.getCloseAt(i - 1)); tr0 = Math.max(tr0, tr1); tr1 = Math.abs(dataset.getLowAt(i) - dataset.getCloseAt(i - 1)); tr.setDataItem(i, new DataItem(time, Math.max(tr0, tr1))); if (dataset.getHighAt(i) <= dataset.getHighAt(i - 1) && dataset.getLowAt(i) < dataset.getLowAt(i - 1)) { lmlp.setDataItem(i, new DataItem(time, dataset.getLowAt(i - 1) - dataset.getLowAt(i))); } else if (dataset.getHighAt(i) > dataset.getHighAt(i - 1) && dataset.getLowAt(i) >= dataset.getLowAt(i - 1)) { hmhp.setDataItem(i, new DataItem(time, dataset.getHighAt(i) - dataset.getHighAt(i - 1))); } else { double tempH = Math.abs(dataset.getHighAt(i) - dataset.getHighAt(i - 1)); double tempL = Math.abs(dataset.getLowAt(i) - dataset.getLowAt(i - 1)); if (tempH > tempL) { hmhp.setDataItem(i, new DataItem(time, tempH)); } else { lmlp.setDataItem(i, new DataItem(time, tempL)); } } } Dataset strDS = Dataset.EMA(tr, period); Dataset shmhpDS = Dataset.EMA(hmhp, period); Dataset slmlpDS = Dataset.EMA(lmlp, period); for (int i = period; i < count; i++) { long time = dataset.getTimeAt(i); double curPDI = 0; double curMDI = 0; if (strDS.getDataItem(i) != null) { if (strDS.getCloseAt(i) != 0) { curPDI = (shmhpDS.getCloseAt(i) / strDS.getCloseAt(i)) * 100; curMDI = (slmlpDS.getCloseAt(i) / strDS.getCloseAt(i)) * 100; } } pdi.setDataItem(i, new DataItem(time, curPDI)); mdi.setDataItem(i, new DataItem(time, curMDI)); if (curPDI + curMDI != 0) { dx.setDataItem(i, new DataItem(time, (Math.abs(curPDI - curMDI) / (curPDI + curMDI) * 100))); } } Dataset adx = Dataset.EMA(dx, period); Dataset[] result = new Dataset[3]; result[0] = pdi; result[1] = mdi; result[2] = adx; return result; } public static int getPrice(String price) { for (int i = 0; i < LIST.length; i++) if (price.equals(LIST[i])) return i; return 3; // Default Close } public static final int OPEN_PRICE = 0; public static final int HIGH_PRICE = 1; public static final int LOW_PRICE = 2; public static final int CLOSE_PRICE = 3; public static final int VOLUME_PRICE = 4; public static final String OPEN = "Open"; public static final String HIGH = "High"; public static final String LOW = "Low"; public static final String CLOSE = "Close"; public static final String VOLUME = "Volume"; public static final String[] LIST = new String[] { OPEN, HIGH, LOW, CLOSE }; }