pls have a look at this example of using core API. This is another price deviation line but it is a line drawn in two colors for values >0 or <0.
It is hugely based on MarkersDemo, I just changed some things and neither do I think it handles many aliases correctly so please use over no more than one.
If launched as a project, it should be in RunConfigurations -> ClassPath tab -> UserEntries
Code: Select all
package velox.api.layer1.simpledemo.devmarkers;
import java.awt.Color;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
import velox.api.layer1.Layer1ApiAdminAdapter;
import velox.api.layer1.Layer1ApiFinishable;
import velox.api.layer1.Layer1ApiInstrumentListener;
import velox.api.layer1.Layer1ApiProvider;
import velox.api.layer1.Layer1CustomPanelsGetter;
import velox.api.layer1.annotations.Layer1ApiVersion;
import velox.api.layer1.annotations.Layer1ApiVersionValue;
import velox.api.layer1.annotations.Layer1Attachable;
import velox.api.layer1.annotations.Layer1StrategyName;
import velox.api.layer1.common.ListenableHelper;
import velox.api.layer1.common.Log;
import velox.api.layer1.data.BalanceInfo;
import velox.api.layer1.data.ExecutionInfo;
import velox.api.layer1.data.InstrumentInfo;
import velox.api.layer1.data.MarketMode;
import velox.api.layer1.data.OrderInfoUpdate;
import velox.api.layer1.data.StatusInfo;
import velox.api.layer1.data.TradeInfo;
import velox.api.layer1.layers.strategies.interfaces.CalculatedResultListener;
import velox.api.layer1.layers.strategies.interfaces.CustomEventAggregatble;
import velox.api.layer1.layers.strategies.interfaces.CustomGeneratedEvent;
import velox.api.layer1.layers.strategies.interfaces.CustomGeneratedEventAliased;
import velox.api.layer1.layers.strategies.interfaces.InvalidateInterface;
import velox.api.layer1.layers.strategies.interfaces.Layer1IndicatorColorInterface;
import velox.api.layer1.layers.strategies.interfaces.OnlineCalculatable;
import velox.api.layer1.layers.strategies.interfaces.OnlineValueCalculatorAdapter;
import velox.api.layer1.messages.GeneratedEventInfo;
import velox.api.layer1.messages.Layer1ApiUserMessageAddStrategyUpdateGenerator;
import velox.api.layer1.messages.UserMessageLayersChainCreatedTargeted;
import velox.api.layer1.messages.indicators.DataStructureInterface;
import velox.api.layer1.messages.indicators.DataStructureInterface.TreeResponseInterval;
import velox.api.layer1.messages.indicators.IndicatorColorInterface;
import velox.api.layer1.messages.indicators.IndicatorColorScheme;
import velox.api.layer1.messages.indicators.IndicatorLineStyle;
import velox.api.layer1.messages.indicators.Layer1ApiDataInterfaceRequestMessage;
import velox.api.layer1.messages.indicators.Layer1ApiUserMessageModifyIndicator;
import velox.api.layer1.messages.indicators.SettingsAccess;
import velox.api.layer1.messages.indicators.StrategyUpdateGenerator;
import velox.api.layer1.settings.Layer1ConfigSettingsInterface;
import velox.api.layer1.messages.indicators.Layer1ApiUserMessageModifyIndicator.GraphType;
import velox.colors.ColorsChangedListener;
import velox.gui.StrategyPanel;
import velox.gui.colors.ColorsConfigItem;
@Layer1Attachable
@Layer1StrategyName("Markers demo Devi")
@Layer1ApiVersion(Layer1ApiVersionValue.VERSION2)
public class Layer1ApiMarkersDeviDemo implements
Layer1ApiFinishable,
Layer1ApiAdminAdapter,
Layer1ApiInstrumentListener, OnlineCalculatable,
Layer1CustomPanelsGetter,
Layer1ConfigSettingsInterface,
Layer1IndicatorColorInterface {
public static class CustomTradePriceEvent implements CustomGeneratedEvent {
private static final long serialVersionUID = 1L;
private final long time;
public double lastPrice;
public CustomTradePriceEvent(long time, double lastPrice) {
this.time = time;
this.lastPrice = lastPrice;
}
@Override
public long getTime() {
return time;
}
@Override
public Object clone() {
return new CustomTradePriceEvent(time, lastPrice);
}
@Override
public String toString() {
return "[" + lastPrice + "(V)]";
}
}
public static class CustomTradePriceAggregationEvent implements CustomGeneratedEvent {
private static final long serialVersionUID = 1L;
private final long time;
public double lastPrice;
public CustomTradePriceAggregationEvent(long time, double lastPrice) {
this.time = time;
this.lastPrice = lastPrice;
}
@Override
public long getTime() {
return time;
}
@Override
public Object clone() {
return new CustomTradePriceAggregationEvent(time, lastPrice);
}
@Override
public String toString() {
return "[" + lastPrice + "(A)]";
}
}
public static final CustomEventAggregatble CUSTOM_TRADE_EVENTS_AGGREGATOR = new CustomEventAggregatble() {
@Override
public CustomGeneratedEvent getInitialValue(long t) {
return new CustomTradePriceAggregationEvent(t, Double.NaN);
}
@Override
public void aggregateAggregationWithValue(CustomGeneratedEvent aggregation, CustomGeneratedEvent value) {
CustomTradePriceAggregationEvent aggregationEvent = (CustomTradePriceAggregationEvent) aggregation;
CustomTradePriceEvent valueEvent = (CustomTradePriceEvent) value;
if (Double.isNaN(aggregationEvent.lastPrice) || !Double.isNaN(valueEvent.lastPrice)) {
aggregationEvent.lastPrice = valueEvent.lastPrice;
}
}
@Override
public void aggregateAggregationWithAggregation(CustomGeneratedEvent aggregation1,
CustomGeneratedEvent aggregation2) {
CustomTradePriceAggregationEvent aggregationEvent1 = (CustomTradePriceAggregationEvent) aggregation1;
CustomTradePriceAggregationEvent aggregationEvent2 = (CustomTradePriceAggregationEvent) aggregation2;
if (Double.isNaN(aggregationEvent1.lastPrice) || !Double.isNaN(aggregationEvent2.lastPrice)) {
aggregationEvent1.lastPrice = aggregationEvent2.lastPrice;
}
}
};
private static final String INDICATOR_NAME = "Deviations";
private static final String COLOR_NAME_UPPER = "Color Upper";
private static final Color UPPER_DEFAULT_COLOR = Color.GREEN;
private static final String COLOR_NAME_LOWER = "Color Lower";
private static final Color LOWER_DEFAULT_COLOR = Color.RED;
private static final String TREE_NAME = "Custom Events Tree";
private static final double DEVIATION = 20.0;
private static final int TRADES_NUMBER = 10;
private Layer1ApiProvider provider;
private Map<String, MarkersDemoSettings> settingsMap = new HashMap<>();
private SettingsAccess settingsAccess;
private Map<String, String> indicatorsFullNameToUserName = new HashMap<>();
private Map<String, String> indicatorsUserNameToFullName = new HashMap<>();
private Map<String, InvalidateInterface> invalidateInterfaceMap = new ConcurrentHashMap<>();
private DataStructureInterface dataStructureInterface;
private Object locker = new Object();
private double lastIndValue;
public Layer1ApiMarkersDeviDemo(Layer1ApiProvider provider) {
this.provider = provider;
ListenableHelper.addListeners(provider, this);
}
@Override
public void finish() {
synchronized (indicatorsFullNameToUserName) {
for (String userName: indicatorsFullNameToUserName.values()) {
provider.sendUserMessage(new Layer1ApiUserMessageModifyIndicator(Layer1ApiMarkersDeviDemo.class, userName, false));
provider.sendUserMessage(getGeneratorMessage(false));
}
}
invalidateInterfaceMap.clear();
}
private Layer1ApiUserMessageModifyIndicator getUserMessageAdd(String userName,
IndicatorLineStyle lineStyle, boolean isAddWidget) {
return new Layer1ApiUserMessageModifyIndicator(Layer1ApiMarkersDeviDemo.class, userName, true,
new IndicatorColorScheme() {
@Override
public ColorDescription[] getColors() {
return new ColorDescription[] {
new ColorDescription(Layer1ApiMarkersDeviDemo.class, COLOR_NAME_UPPER, UPPER_DEFAULT_COLOR, false),
new ColorDescription(Layer1ApiMarkersDeviDemo.class, COLOR_NAME_LOWER, LOWER_DEFAULT_COLOR, false)
};
}
@Override
public String getColorFor(Double value) {
if (value < 0.0) {
return COLOR_NAME_LOWER;
} else {
return COLOR_NAME_UPPER;
}
}
@Override
public ColorIntervalResponse getColorIntervalsList(double valueFrom, double valueTo) {
if (valueTo <= 0) {
return new ColorIntervalResponse(new String[]{COLOR_NAME_LOWER}, new double[]{});
} else if (valueFrom >= 0) {
return new ColorIntervalResponse(new String[]{COLOR_NAME_UPPER}, new double[]{});
}
return new ColorIntervalResponse(new String[]{COLOR_NAME_LOWER, COLOR_NAME_UPPER}, new double[]{0.0D});
}
}, Layer1ApiMarkersDeviDemo.this, lineStyle, Color.white, Color.black, null,
null, null, null, null, GraphType.BOTTOM, isAddWidget, false, null, this, null);
}
@Override
public void onUserMessage(Object data) {
if (data.getClass() == UserMessageLayersChainCreatedTargeted.class) {
UserMessageLayersChainCreatedTargeted message = (UserMessageLayersChainCreatedTargeted) data;
if (message.targetClass == getClass()) {
provider.sendUserMessage(new Layer1ApiDataInterfaceRequestMessage(dataStructureInterface -> this.dataStructureInterface = dataStructureInterface));
addIndicator(INDICATOR_NAME);
Layer1ApiUserMessageAddStrategyUpdateGenerator message1 = getGeneratorMessage(true);
provider.sendUserMessage(message1);
}
}
}
@Override
public void onInstrumentAdded(String alias, InstrumentInfo instrumentInfo) {
}
@Override
public void onInstrumentRemoved(String alias) {
}
@Override
public void onInstrumentNotFound(String symbol, String exchange, String type) {
}
@Override
public void onInstrumentAlreadySubscribed(String symbol, String exchange, String type) {
}
@Override
public void calculateValuesInRange(String indicatorName, String alias, long t0, long intervalWidth, int intervalsNumber,
CalculatedResultListener listener) {
String userName = indicatorsFullNameToUserName.get(indicatorName);
switch (userName) {
case INDICATOR_NAME: {
List<TreeResponseInterval> result = dataStructureInterface.get(Layer1ApiMarkersDeviDemo.class, TREE_NAME,
t0, intervalWidth, intervalsNumber, alias, new Class<?>[] { CustomTradePriceEvent.class });
double lastPrice = getValueFromEvent(result.get(0));
for (int i = 1; i <= intervalsNumber; ++i) {
double price = getValueFromEvent(result.get(i));
if (!Double.isNaN(price)) {
lastPrice = price;
}
listener.provideResponse(lastPrice);
}
listener.setCompleted();
break;
}
default:
throw new IllegalArgumentException("Unknown indicator name " + indicatorName);
}
}
@Override
public OnlineValueCalculatorAdapter createOnlineValueCalculator(String indicatorName, String indicatorAlias, long time,
Consumer<Object> listener, InvalidateInterface invalidateInterface) {
String userName = indicatorsFullNameToUserName.get(indicatorName);
invalidateInterfaceMap.put(userName, invalidateInterface);
TreeResponseInterval result = dataStructureInterface.get(Layer1ApiMarkersDeviDemo.class, TREE_NAME, time, indicatorAlias, new Class<?>[] {CustomTradePriceEvent.class});
final double startValue = getValueFromEvent(result);
switch (userName) {
case INDICATOR_NAME:
return new OnlineValueCalculatorAdapter() {
private double lastValue = Double.isNaN(startValue) ? 0 : startValue;
@Override
public void onTrade(String alias, double price, int size, TradeInfo tradeInfo) {
if (alias.equals(indicatorAlias)) {
if (alias.equals(indicatorAlias)) {
lastValue = Double.isNaN(lastValue) ? price : (lastValue + price) / 2.;
listener.accept(lastIndValue);
}
}
}
};
default:
throw new IllegalArgumentException("Unknown indicator name " + indicatorName);
}
}
@Override
public StrategyPanel[] getCustomGuiFor(String alias, String indicatorName) {
StrategyPanel panel = new StrategyPanel("Colors", new GridBagLayout());
panel.setLayout(new GridBagLayout());
GridBagConstraints gbConst;
IndicatorColorInterface indicatorColorInterface = new IndicatorColorInterface() {
@Override
public void set(String name, Color color) {
setColor(alias, name, color);
}
@Override
public Color getOrDefault(String name, Color defaultValue) {
Color color = getSettingsFor(alias).getColor(name);
return color == null ? defaultValue : color;
}
@Override
public void addColorChangeListener(ColorsChangedListener listener) {
}
};
ColorsConfigItem configItemUpper = new ColorsConfigItem(COLOR_NAME_UPPER, COLOR_NAME_UPPER, true,
UPPER_DEFAULT_COLOR, indicatorColorInterface, new ColorsChangedListener() {
@Override
public void onColorsChanged() {
InvalidateInterface invalidaInterface = invalidateInterfaceMap.get(INDICATOR_NAME);
if (invalidaInterface != null) {
invalidaInterface.invalidate();
}
}
});
gbConst = new GridBagConstraints();
gbConst.gridx = 0;
gbConst.gridy = 0;
gbConst.weightx = 1;
gbConst.insets = new Insets(5, 5, 5, 5);
gbConst.fill = GridBagConstraints.HORIZONTAL;
panel.add(configItemUpper, gbConst);
ColorsConfigItem configItemLower = new ColorsConfigItem(COLOR_NAME_LOWER, COLOR_NAME_LOWER, true,
LOWER_DEFAULT_COLOR, indicatorColorInterface, new ColorsChangedListener() {
@Override
public void onColorsChanged() {
InvalidateInterface invalidaInterface = invalidateInterfaceMap.get(INDICATOR_NAME);
if (invalidaInterface != null) {
invalidaInterface.invalidate();
}
}
});
gbConst = new GridBagConstraints();
gbConst.gridx = 0;
gbConst.gridy = 1;
gbConst.weightx = 1;
gbConst.insets = new Insets(5, 5, 5, 5);
gbConst.fill = GridBagConstraints.HORIZONTAL;
panel.add(configItemLower, gbConst);
return new StrategyPanel[] {panel};
}
public void addIndicator(String userName) {
Layer1ApiUserMessageModifyIndicator message = null;
switch (userName) {
case INDICATOR_NAME:
message = getUserMessageAdd(userName, IndicatorLineStyle.SHORT_DASHES_WIDE_LEFT_NARROW_RIGHT, true);
break;
default:
Log.warn("Unknwon name for marker indicator: " + userName);
break;
}
if (message != null) {
synchronized (indicatorsFullNameToUserName) {
indicatorsFullNameToUserName.put(message.fullName, message.userName);
indicatorsUserNameToFullName.put(message.userName, message.fullName);
}
provider.sendUserMessage(message);
}
}
@Override
public void acceptSettingsInterface(SettingsAccess settingsAccess) {
this.settingsAccess = settingsAccess;
}
private MarkersDemoSettings getSettingsFor(String alias) {
synchronized (locker) {
MarkersDemoSettings settings = settingsMap.get(alias);
if (settings == null) {
settings = (MarkersDemoSettings) settingsAccess.getSettings(alias, INDICATOR_NAME, MarkersDemoSettings.class);
settingsMap.put(alias, settings);
}
return settings;
}
}
protected void settingsChanged(String settingsAlias, MarkersDemoSettings settingsObject) {
synchronized (locker) {
settingsAccess.setSettings(settingsAlias, INDICATOR_NAME, settingsObject, settingsObject.getClass());
}
}
@Override
public void setColor(String alias, String name, Color color) {
MarkersDemoSettings settings = getSettingsFor(alias);
settings.setColor(name, color);
settingsChanged(alias, settings);
}
@Override
public Color getColor(String alias, String name) {
Color color = getSettingsFor(alias).getColor(name);
if (color == null) {
switch (name) {
case COLOR_NAME_UPPER:
color = UPPER_DEFAULT_COLOR;
break;
case COLOR_NAME_LOWER:
color = LOWER_DEFAULT_COLOR;
break;
default:
Log.warn("Layer1ApiMarkersDemo: unknown color name " + name);
color = Color.WHITE;
break;
}
}
return color;
}
@Override
public void addColorChangeListener(ColorsChangedListener listener) {
// every one of our colors is modified only from one place
}
private Layer1ApiUserMessageAddStrategyUpdateGenerator getGeneratorMessage(boolean isAdd) {
Layer1ApiUserMessageAddStrategyUpdateGenerator generator = new Layer1ApiUserMessageAddStrategyUpdateGenerator(Layer1ApiMarkersDeviDemo.class, TREE_NAME, isAdd, true, new StrategyUpdateGenerator() {
private Consumer<CustomGeneratedEventAliased> consumer;
private long time = 0;
private double initialPrice = Double.NaN;
boolean upper = false;
int uni = 0;
@Override
public void setGeneratedEventsConsumer(Consumer<CustomGeneratedEventAliased> consumer) {
this.consumer = consumer;
}
@Override
public Consumer<CustomGeneratedEventAliased> getGeneratedEventsConsumer() {
return consumer;
}
@Override
public void onStatus(StatusInfo statusInfo) {
}
@Override
public void onOrderUpdated(OrderInfoUpdate orderInfoUpdate) {
}
@Override
public void onOrderExecuted(ExecutionInfo executionInfo) {
}
@Override
public void onBalance(BalanceInfo balanceInfo) {
}
@Override
public void onTrade(String alias, double price, int size, TradeInfo tradeInfo) {
if (Double.isNaN(initialPrice)) {
initialPrice = price;
}
if (!Double.isNaN(price)) {
uni++;
if (uni % TRADES_NUMBER == 0) {
upper = !upper;
}
}
double add = upper ? +DEVIATION : -DEVIATION;
double lastValue = initialPrice - price + add;
Layer1ApiMarkersDeviDemo.this.lastIndValue = lastValue;
this.consumer.accept(
new CustomGeneratedEventAliased(new CustomTradePriceEvent(time, lastValue), alias));
}
@Override
public void onMarketMode(String alias, MarketMode marketMode) {
}
@Override
public void onDepth(String alias, boolean isBid, int price, int size) {
}
@Override
public void onInstrumentAdded(String alias, InstrumentInfo instrumentInfo) {
}
@Override
public void onInstrumentRemoved(String alias) {
}
@Override
public void onInstrumentNotFound(String symbol, String exchange, String type) {
}
@Override
public void onInstrumentAlreadySubscribed(String symbol, String exchange, String type) {
}
@Override
public void onUserMessage(Object data) {
}
@Override
public void setTime(long time) {
this.time = time;
}
}, new GeneratedEventInfo[] {new GeneratedEventInfo(CustomTradePriceEvent.class, CustomTradePriceAggregationEvent.class, CUSTOM_TRADE_EVENTS_AGGREGATOR)});
return generator;
}
private double getValueFromEvent(TreeResponseInterval treeResponseInterval) {
Object object = treeResponseInterval.events.get(CustomTradePriceEvent.class.toString());
if (object != null) {
return ((CustomTradePriceAggregationEvent) object).lastPrice;
} else {
return Double.NaN;
}
}
}