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,
Extend BM DOM
-
- Posts: 548
- Joined: Mon Jul 09, 2018 11:18 am
- Has thanked: 25 times
- Been thanked: 85 times
Re: Extend BM DOM
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
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
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
-
- Posts: 548
- Joined: Mon Jul 09, 2018 11:18 am
- Has thanked: 25 times
- Been thanked: 85 times
Re: Extend BM DOM
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):
an auxiliary class:
build.gradle Bookmap libs dependencies(change according to your Bookmap release number):
this should work for latest 7.1 releases.
Cloud notes download url (change the port number if you need to):
Code: Select all
http://127.0.0.1:8089
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';