Page 1 of 1
Extend BM DOM
Posted: Mon Apr 06, 2020 1:19 am
by blk
Hi,
Is there a way to extend BM DOM ? I would like to add extra columns and populate it via api.
If this is not possible, is this something you have on roadmap?
Thanks,
Re: Extend BM DOM
Posted: Wed Apr 08, 2020 11:01 am
by Andry API support
Currently there's no way to populate columns via API. The only way to automate populating columns (Cloud Notes type columns) is to create a server which can report (for any column cell) text, color text and background color. The least refresh interval that can be set from gui is 1 min which is sometimes too long but a server can override it. Let me know if you're interested in code examples. If not, I'll move the topic to Feature Requests.
Re: Extend BM DOM
Posted: Wed Apr 08, 2020 12:53 pm
by blk
Hi Andrey,
Can you show me an example of how to override the 1 min refresh interval for cloud notes?
You can move Adding Custom DOM columns and populating via API to features request.
Thanks
Re: Extend BM DOM
Posted: Thu Apr 09, 2020 9:30 am
by Andry API support
Hi blk,
this should work for latest 7.1 releases.
Cloud notes download url (change the port number if you need to):
notes server example (the field responsible to store a refresh interval is refreshDelayMillis):
Code: Select all
package layer1modules;
import java.awt.Color;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicBoolean;
import velox.api.layer1.Layer1ApiProvider;
import velox.api.layer1.annotations.Layer1ApiVersion;
import velox.api.layer1.annotations.Layer1ApiVersionValue;
import velox.api.layer1.annotations.Layer1SimpleAttachable;
import velox.api.layer1.annotations.Layer1StrategyName;
import velox.api.layer1.common.Log;
import velox.api.layer1.data.InstrumentInfo;
import velox.api.layer1.data.TradeInfo;
import velox.api.layer1.layers.utils.OrderBook;
import velox.api.layer1.simplified.Api;
import velox.api.layer1.simplified.CustomModule;
import velox.api.layer1.simplified.DepthDataListener;
import velox.api.layer1.simplified.Indicator;
import velox.api.layer1.simplified.InitialState;
import velox.api.layer1.simplified.TradeDataListener;
@Layer1SimpleAttachable
@Layer1StrategyName("notes server 8089")
@Layer1ApiVersion(Layer1ApiVersionValue.VERSION2)
public class SimplifiedNotesServer8089 implements CustomModule, TradeDataListener, DepthDataListener
{
protected int port = 8089;
protected Indicator lastTradeIndicator;
protected static final String NOTES_REFRESH_HEADER_NAME = "X-Bookmap-Cloud-Notes-Refresh-Delay-Millis";
protected static final String header = "\"Symbol\",\"Price Level\",\"Note\",\"Foreground Color\",\"Background Color\","
+ "\"Text Alignment\",\"Notification Enabled\",\"Sound Notification Enabled\",\"Notification Is Repeatable\","
+ "\"Delay Before Repeating\",\"Subscribing Offset\",\"Notification Sound\"\r\n";
protected static final String newLine="\r\n";
@SuppressWarnings("unused")
protected Layer1ApiProvider provider;
protected OrderBook orderBook = new OrderBook();
protected int refreshDelayMillis = 500;
protected String alias;
protected double pips;
protected int prevBestBid;
protected AtomicBoolean enabled = new AtomicBoolean(true);
protected ServerSocket serverSocket;
protected final ExecutorService readNotesExecutor = Executors.newSingleThreadExecutor();
protected final ExecutorService incomingBboExecutor = Executors.newSingleThreadExecutor();
protected NoteUnit actualBbo;
protected final Object unitLock = new Object();
volatile boolean isShuttingDown;
protected int count;
@Override
public void initialize(String alias, InstrumentInfo info, Api api, InitialState initialState) {
this.orderBook = new OrderBook();
this.pips = info.pips;
this.alias = alias;
startServer();
}
@Override
public void stop() {
Thread th = new Thread(() -> {
isShuttingDown = true;
incomingBboExecutor.shutdownNow();
enabled.set(false);
try {
serverSocket.close();
} catch (Exception e) {
e.printStackTrace();
}
readNotesExecutor.shutdownNow();
});
th.start();
}
@Override
public void onTrade(double price, int size, TradeInfo tradeInfo) {
}
@Override
public void onDepth(boolean isBid, int price, int size) {
if (alias.equals(this.alias)) {
orderBook.onUpdate(isBid, price, size);
int bestBid = orderBook.getBestBidPriceOrNone();
if (bestBid != Integer.MAX_VALUE && bestBid != prevBestBid) {
onBestBid(bestBid, (int) orderBook.getSizeFor(true, bestBid));
}
}
}
public void onBestBid(int bidPrice, int bidSize) {
if (bidPrice == 0) return;
Runnable runnable = () -> {
NoteUnit unit = new NoteUnit();
unit.setSymbol(alias);
unit.setPriceLevel((double)(bidPrice * pips));
unit.setForegroundColor(Color.decode("#ffffff"));
unit.setBackgroundColor(Color.decode("#00cc7a"));
synchronized (unitLock) {
actualBbo = unit;
}
};
if (!isShuttingDown) {
incomingBboExecutor.execute(runnable);
}
}
protected void startServer() {
Thread server = new Thread(() -> {
try (ServerSocket socket = new ServerSocket(port)) {
serverSocket = socket;
while (enabled.get()) {
Socket connection = socket.accept();
try {
BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
// read first line of request
String request = in.readLine();
if (request == null) {
continue;
}
Runnable runnable = () -> {
try {
OutputStream out = new BufferedOutputStream(connection.getOutputStream());
if (request.startsWith("GET")) {
StringBuilder sb = new StringBuilder();
sb.append(header);
synchronized (unitLock) {
if (actualBbo != null) {
actualBbo.setNote(count++ + " Bbo is " + String.valueOf(actualBbo.getPriceLevel()));
sb.append(actualBbo.getCsvLine());
for (int i = -5; i < 6; i++) {
if (i == 0) continue;
NoteUnit unit = new NoteUnit();
unit.setSymbol(alias);
unit.setPriceLevel(actualBbo.getPriceLevel() + (double)i * pips);
unit.setNote(String.valueOf(unit.getPriceLevel()));
unit.setForegroundColor(Color.decode("#ffffff"));
if (i < 0) {
unit.setBackgroundColor(Color.decode("#00cc7a"));
} else {
unit.setBackgroundColor(Color.decode("#cc3300"));
}
sb.append(unit.getCsvLine());
}
} else {
NoteUnit blankUnit = new NoteUnit();
blankUnit.setSymbol(alias);
blankUnit.setPriceLevel(0.0);
sb.append(blankUnit.getCsvLine());
}
}
String response = sb.toString();
// Log.info(response);
PrintStream printStream = new PrintStream(out);
printStream.print("HTTP/1.0 200 OK" + newLine + "Content-Type: text/plain" + newLine
+ "Date: " + new Date() + newLine
+ NOTES_REFRESH_HEADER_NAME + ": " + String.valueOf(refreshDelayMillis) + newLine
+ "Content-length: "
+ response.length() + newLine + newLine + response);
// Log.info("count is " + (count -1));
printStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
};
if (!isShuttingDown) {
readNotesExecutor.execute(runnable);
}
} catch (Exception e) {
Log.info("Exception0 for alias " + alias + "", e);
}
}
} catch (Exception e) {
Log.info("Exception1 for alias " + alias + "", e);
}
});
server.start();
}
}
an auxiliary class:
Code: Select all
package layer1modules;
import java.awt.Color;
import java.io.File;
import java.lang.reflect.Field;
public class NoteUnit {
enum TextAlignment {
right, left, center;
}
private String symbol;
private Double priceLevel;
private String note;
private Color foregroundColor;
private Color backgroundColor;
private TextAlignment alignment;
private Boolean notificationEnabled;
private Boolean soundNotificationEnabled;
private Boolean notificationIsRepeatable;
private Integer delayBeforeRepeating;
private Integer subscribingOffset;
private File notificationSound;
private boolean isNew;
public String getSymbol() {
return symbol;
}
public void setSymbol(String symbol) {
this.symbol = symbol;
}
public Double getPriceLevel() {
return priceLevel;
}
public void setPriceLevel(Double priceLevel) {
this.priceLevel = priceLevel;
}
public String getNote() {
return note;
}
public void setNote(String note) {
this.note = note;
}
public Color getForegroundColor() {
return foregroundColor;
}
public void setForegroundColor(Color foregroundColor) {
this.foregroundColor = foregroundColor;
}
public Color getBackgroundColor() {
return backgroundColor;
}
public void setBackgroundColor(Color backgroundColor) {
this.backgroundColor = backgroundColor;
}
public TextAlignment getAlignment() {
return alignment;
}
public void setAlignment(TextAlignment alignment) {
this.alignment = alignment;
}
public Boolean getNotificationEnabled() {
return notificationEnabled;
}
public void setNotificationEnabled(Boolean notificationEnabled) {
this.notificationEnabled = notificationEnabled;
}
public Boolean getSoundNotificationEnabled() {
return soundNotificationEnabled;
}
public void setSoundNotificationEnabled(Boolean soundNotificationEnabled) {
this.soundNotificationEnabled = soundNotificationEnabled;
}
public Boolean getNotificationIsRepeatable() {
return notificationIsRepeatable;
}
public void setNotificationIsRepeatable(Boolean notificationIsRepeatable) {
this.notificationIsRepeatable = notificationIsRepeatable;
}
public Integer getDelayBeforeRepeating() {
return delayBeforeRepeating;
}
public void setDelayBeforeRepeating(Integer delayBeforeRepeating) {
this.delayBeforeRepeating = delayBeforeRepeating;
}
public Integer getSubscribingOffset() {
return subscribingOffset;
}
public void setSubscribingOffset(Integer subscribingOffset) {
this.subscribingOffset = subscribingOffset;
}
public File getNotificationSound() {
return notificationSound;
}
public void setNotificationSound(File notificationSound) {
this.notificationSound = notificationSound;
}
public boolean isNew() {
return isNew;
}
public void setNew(boolean isNew) {
this.isNew = isNew;
}
public String getCsvLine() {
Field[] fields = getClass().getDeclaredFields();
StringBuilder sb = new StringBuilder();
for (Field field : fields) {
if (field.getName().equals("isNew")) continue;
sb.append("\"");
try {
Object object = field.get(this);
String objectAsText;
if (object instanceof Color) {
Color color = (Color) object;
objectAsText = String.format("#%02x%02x%02x", color.getRed(), color.getGreen(), color.getBlue());
} else {
objectAsText = object == null ? "" : String.valueOf(object);
}
sb.append(objectAsText);
} catch (IllegalArgumentException | IllegalAccessException e) {
e.printStackTrace();
}
sb.append("\",");
}
sb.deleteCharAt(sb.length() - 1);
sb.append("\n");
return sb.toString();
}
}
build.gradle Bookmap libs dependencies(change according to your Bookmap release number):
Code: Select all
repositories {
mavenCentral()
maven {
url "http://maven.bookmap.com/maven2/releases/"
}
}
dependencies {
compileOnly group: 'com.bookmap.api', name: 'api-core', version: '7.1.0.31';
compileOnly group: 'com.bookmap.api', name: 'api-simplified', version: '7.1.0.31';
Re: Extend BM DOM
Posted: Thu Apr 09, 2020 12:49 pm
by blk
Thanks Andrey, appreciate your reply and code samples
!