package org.chartsy.macd;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.Stroke;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import org.chartsy.main.ChartFrame;
import org.chartsy.main.chart.Indicator;
import org.chartsy.main.data.DataItem;
import org.chartsy.main.data.Dataset;
import org.chartsy.main.utils.DefaultPainter;
import org.chartsy.main.utils.Range;
import org.chartsy.main.utils.SerialVersion;
import org.openide.nodes.AbstractNode;
/**
*
* @author viorel.gheba
*/
public class MACD
extends Indicator
{
private static final long serialVersionUID = SerialVersion.APPVERSION;
public static final String MACD = "macd";
public static final String SIGNAL = "signal";
public static final String HISTOGRAM = "histogram";
private Color histogramColor = null;
private IndicatorProperties properties;
public MACD()
{
super();
properties = new IndicatorProperties();
}
public String getName()
{ return "MACD"; }
public String getLabel()
{ return properties.getLabel() + " (" + properties.getFast() + ", " + properties.getSlow() + ", " + properties.getSmooth() + ")"; }
public String getPaintedLabel(ChartFrame cf){ return getLabel(); }
public Indicator newInstance(){ return new MACD(); }
public LinkedHashMap getHTML(ChartFrame cf, int i)
{
LinkedHashMap ht = new LinkedHashMap();
DecimalFormat df = new DecimalFormat("#,##0.00");
double[] values = getValues(cf, i);
String[] labels = {"Histogram:", "Signal:", "MACD:"};
ht.put(getLabel(), " ");
if (values.length > 0)
{
Color[] colors = getColors();
colors[0] = values[0] > 0 ? properties.getHistogramPositiveColor() : properties.getHistogramNegativeColor();
for (int j = 0; j < values.length; j++)
{
ht.put(getFontHTML(colors[j], labels[j]), getFontHTML(colors[j], df.format(values[j])));
}
}
return ht;
}
@Override
public Range getRange(ChartFrame cf)
{
Range range = super.getRange(cf);
double d = Math.max(Math.abs(range.getLowerBound()), Math.abs(range.getUpperBound()));
return new Range(-1 * d, d);
}
public void paint(Graphics2D g, ChartFrame cf, Rectangle bounds)
{
Dataset macd = visibleDataset(cf, MACD);
Dataset signal = visibleDataset(cf, SIGNAL);
Dataset histogram = visibleDataset(cf, HISTOGRAM);
if (histogram != null && signal != null && macd != null)
{
if (maximized)
{
Range range = getRange(cf);
DefaultPainter.histogram(g, cf, range, bounds, histogram, properties.getHistogramPositiveColor(), properties.getHistogramNegativeColor()); // paint the histogram
DefaultPainter.line(g, cf, range, bounds, signal, properties.getSignalColor(), properties.getSignalStroke()); // paint the signal
DefaultPainter.line(g, cf, range, bounds, macd, properties.getMacdColor(), properties.getMacdStroke()); // paint the MACD
}
}
}
public void calculate()
{
Dataset initial = getDataset();
if (initial != null && !initial.isEmpty())
{
int fast = properties.getFast();
int slow = properties.getSlow();
int smooth = properties.getSmooth();
Dataset fastEMA = Dataset.EMA(initial, fast);
Dataset slowEMA = Dataset.EMA(initial, slow);
Dataset macd = getMACD(fastEMA, slowEMA, slow);
addDataset(MACD, macd);
Dataset signal = getSignal(macd, slow, smooth);
addDataset(SIGNAL, signal);
Dataset MACDHistogram = getMACDHistogram(macd, signal);
addDataset(HISTOGRAM, MACDHistogram);
}
}
public boolean hasZeroLine(){ return true; }
public boolean getZeroLineVisibility(){ return properties.getZeroLineVisibility(); }
public Color getZeroLineColor(){ return properties.getZeroLineColor(); }
public Stroke getZeroLineStroke(){ return properties.getZeroLineStroke(); }
public boolean hasDelimiters(){ return false; }
public boolean getDelimitersVisibility(){ return false; }
public double[] getDelimitersValues(){ return new double[] {}; }
public Color getDelimitersColor(){ return null; }
public Stroke getDelimitersStroke(){ return null; }
public Color[] getColors()
{ return new Color[] {histogramColor!=null ? histogramColor : properties.getHistogramPositiveColor(), properties.getSignalColor(), properties.getMacdColor()}; }
public double[] getValues(ChartFrame cf)
{
Dataset macd = visibleDataset(cf, MACD);
Dataset signal = visibleDataset(cf, SIGNAL);
Dataset histogram = visibleDataset(cf, HISTOGRAM);
if (macd != null && signal != null && histogram != null)
{
int i = histogram.getLastIndex();
double[] values = new double[3];
values[0] = histogram.getDataItem(i) != null ? histogram.getCloseAt(i) : 0;
values[1] = signal.getDataItem(i) != null ? signal.getCloseAt(i) : 0;
values[2] = macd.getDataItem(i) != null ? macd.getCloseAt(i) : 0;
if (histogram.getDataItem(i) != null)
histogramColor = histogram.getCloseAt(i) > 0 ? properties.getHistogramPositiveColor() : properties.getHistogramNegativeColor();
return values;
}
return new double[] {};
}
public double[] getValues(ChartFrame cf, int i)
{
Dataset macd = visibleDataset(cf, MACD);
Dataset signal = visibleDataset(cf, SIGNAL);
Dataset histogram = visibleDataset(cf, HISTOGRAM);
if (macd != null && signal != null && histogram != null)
{
double[] values = new double[3];
values[0] = histogram.getDataItem(i) != null ? histogram.getCloseAt(i) : 0;
values[1] = signal.getDataItem(i) != null ? signal.getCloseAt(i) : 0;
values[2] = macd.getDataItem(i) != null ? macd.getCloseAt(i) : 0;
if (histogram.getDataItem(i) != null)
histogramColor = histogram.getCloseAt(i) > 0 ? properties.getHistogramPositiveColor() : properties.getHistogramNegativeColor();
return values;
}
return new double[] {};
}
public boolean getMarkerVisibility(){ return properties.getMarker(); }
public AbstractNode getNode(){ return new IndicatorNode(properties); }
@Override
public Double[] getPriceValues(ChartFrame cf)
{
List<Double> list = new ArrayList<Double>();
Range range = getRange(cf);
Double value;
if (range.getUpperBound() >= 1)
{
int step = (int) (range.getUpperBound() / 3) + 1;
for (int i = step; i <= range.getUpperBound(); i+=step)
{
value = new Double(i);
list.add(value);
value = new Double(-1 * i);
list.add(value);
}
}
else
{
double step = (range.getUpperBound() > 0.5) ? 0.25 : 0.15;
for (double i = step; i <= range.getUpperBound(); i+=step)
{
value = new Double(i);
list.add(value);
value = new Double(-1 * i);
list.add(value);
}
}
return list.toArray(new Double[list.size()]);
}
private Dataset getMACD(final Dataset fastEMA, final Dataset slowEMA, final int slow)
{
int count = fastEMA.getItemsCount();
Dataset result = Dataset.EMPTY(count);
for (int i = slow; i < count; i++)
{
double diff = fastEMA.getCloseAt(i) - slowEMA.getCloseAt(i);
result.setDataItem(i, new DataItem(fastEMA.getTimeAt(i), diff));
}
return result;
}
private Dataset getSignal(final Dataset MACD, final int slow, final int smooth)
{
int count = MACD.getItemsCount();
Dataset result = Dataset.EMPTY(count);
double close = 0;
for (int i = slow; i < slow + smooth; i++)
close += MACD.getCloseAt(i);
close /= smooth;
for (int i = (slow + smooth); i < count; i++)
{
double close2 = (2 * (MACD.getCloseAt(i) - close))/(1 + smooth) + close;
result.setDataItem(i, new DataItem(MACD.getTimeAt(i), close));
close = close2;
}
return result;
}
private Dataset getMACDHistogram(final Dataset MACD, final Dataset signal)
{
int count = MACD.getItemsCount();
Dataset result = Dataset.EMPTY(count);
for (int i = 0; i < count; i++)
{
if (signal.getDataItem(i) != null && MACD.getDataItem(i) != null)
{
double diff = MACD.getCloseAt(i) - signal.getCloseAt(i);
result.setDataItem(i, new DataItem(MACD.getTimeAt(i), diff));
}
}
return result;
}
}