I added Squeeze dots to the zero line (BB inside KC: Squeeze on, red, BB outside KC: Squeeze off, green):
package ColorMACD;
import com.motivewave.platform.sdk.common.DataContext;
import com.motivewave.platform.sdk.common.Defaults;
import com.motivewave.platform.sdk.common.Enums;
import com.motivewave.platform.sdk.common.Inputs;
import com.motivewave.platform.sdk.common.LineInfo;
import com.motivewave.platform.sdk.common.Util;
import com.motivewave.platform.sdk.common.desc.*;
import com.motivewave.platform.sdk.study.Study;
import com.motivewave.platform.sdk.study.StudyHeader;
import java.awt.Color;
@StudyHeader(
namespace = "com.mycompany",
id = "ColorMACD",
name = "My Color MACD",
desc = "MACD with user-configurable 4-color histogram.",
menu = "My Studies",
overlay = false
)
public class ColorMACD extends Study {
enum Values { MACD, SIGNAL, HIST }
final static String HIST_IND = "histInd";
// User-configurable histogram color keys
final static String COLOR_RISE_ABOVE = "riseAboveZeroColor";
final static String COLOR_FALL_ABOVE = "fallAboveZeroColor";
final static String COLOR_RISE_BELOW = "riseBelowZeroColor";
final static String COLOR_FALL_BELOW = "fallBelowZeroColor";
@Override
public void initialize(Defaults defaults) {
var sd = createSD();
var tab = sd.addTab("General");
var grp = tab.addGroup("Inputs");
grp.addRow(new InputDescriptor(Inputs.INPUT, "Input", Enums.BarInput.CLOSE));
grp.addRow(new IntegerDescriptor(Inputs.PERIOD, "Period 1", 12, 1, 9999, 1));
grp.addRow(new IntegerDescriptor(Inputs.PERIOD2, "Period 2", 26, 1, 9999, 1));
grp.addRow(new IntegerDescriptor(Inputs.SIGNAL_PERIOD, "Signal Period", 9, 1, 9999, 1));
tab = sd.addTab("Display");
grp = tab.addGroup("Paths");
grp.addRow(new PathDescriptor(Inputs.PATH, "MACD Path", defaults.getLineColor(), 1.5f, null, true, false, true));
grp.addRow(new PathDescriptor(Inputs.SIGNAL_PATH, "Signal Path", defaults.getRed(), 1.0f, null, true, false, true));
grp.addRow(new BarDescriptor(Inputs.BAR, "Default Bar Color", defaults.getBarColor(), true, true));
grp = tab.addGroup("Histogram Colors");
grp.addRow(new ColorDescriptor(COLOR_RISE_ABOVE, "Rising Above Zero", Color.GREEN));
grp.addRow(new ColorDescriptor(COLOR_FALL_ABOVE, "Falling Above Zero", new Color(0, 128, 0)));
grp.addRow(new ColorDescriptor(COLOR_RISE_BELOW, "Rising Below Zero", Color.CYAN));
grp.addRow(new ColorDescriptor(COLOR_FALL_BELOW, "Falling Below Zero", Color.RED));
grp = tab.addGroup("Indicators");
grp.addRow(new IndicatorDescriptor(Inputs.IND, "MACD Ind", null, null, false, true, true));
grp.addRow(new IndicatorDescriptor(Inputs.SIGNAL_IND, "Signal Ind", defaults.getRed(), null, false, false, true));
grp.addRow(new IndicatorDescriptor(HIST_IND, "Hist Ind", defaults.getBarColor(), null, false, false, true));
var desc = createRD();
desc.setMinTick(0.0001);
desc.setLabelSettings(Inputs.INPUT, Inputs.PERIOD, Inputs.PERIOD2, Inputs.SIGNAL_PERIOD);
desc.exportValue(new ValueDescriptor(Values.MACD, "MACD", new String[]{Inputs.INPUT, Inputs.PERIOD, Inputs.PERIOD2}));
desc.exportValue(new ValueDescriptor(Values.SIGNAL, "MACD Signal", new String[]{Inputs.SIGNAL_PERIOD}));
desc.exportValue(new ValueDescriptor(Values.HIST, "MACD Histogram", new String[]{Inputs.PERIOD, Inputs.PERIOD2, Inputs.SIGNAL_PERIOD}));
desc.declarePath(Values.MACD, Inputs.PATH);
desc.declarePath(Values.SIGNAL, Inputs.SIGNAL_PATH);
desc.declareBars(Values.HIST, Inputs.BAR);
desc.declareIndicator(Values.MACD, Inputs.IND);
desc.declareIndicator(Values.SIGNAL, Inputs.SIGNAL_IND);
desc.declareIndicator(Values.HIST, HIST_IND);
desc.setRangeKeys(Values.MACD, Values.SIGNAL, Values.HIST);
desc.addHorizontalLine(new LineInfo(0, null, 1.0f, new float[]{3, 3}));
}
@Override
protected void calculate(int index, DataContext ctx) {
int period1 = getSettings().getInteger(Inputs.PERIOD);
int period2 = getSettings().getInteger(Inputs.PERIOD2);
int period = Util.max(period1, period2);
if (index < period) return;
Object input = getSettings().getInput(Inputs.INPUT);
var series = ctx.getDataSeries();
Double ma1 = series.ema(index, period1, input);
Double ma2 = series.ema(index, period2, input);
if (ma1 == null || ma2 == null) return;
double macd = ma1 - ma2;
series.setDouble(index, Values.MACD, macd);
int signalPeriod = getSettings().getInteger(Inputs.SIGNAL_PERIOD);
if (index < period + signalPeriod) return;
Double signal = series.sma(index, signalPeriod, Values.MACD);
if (signal == null) return;
series.setDouble(index, Values.SIGNAL, signal);
double hist = macd - signal;
series.setDouble(index, Values.HIST, hist);
Double prevHist = series.getDouble(index - 1, Values.HIST);
boolean isRising = prevHist != null && hist > prevHist;
// Use user-selected colors
Color riseAbove = getSettings().getColor(COLOR_RISE_ABOVE);
Color fallAbove = getSettings().getColor(COLOR_FALL_ABOVE);
Color riseBelow = getSettings().getColor(COLOR_RISE_BELOW);
Color fallBelow = getSettings().getColor(COLOR_FALL_BELOW);
Color barColor;
if (hist >= 0) {
barColor = isRising ? riseAbove : fallAbove;
} else {
barColor = isRising ? riseBelow : fallBelow;
}
series.setBarColor(index, Values.HIST, barColor);
series.setComplete(index);
}
}