JavaScript API
Introduction
Bookmap's JavaScript API (or JS-API) is a framework for the development of Bookmap add-ons in JavaScript (JS). It's a wrapper (aka proxy) on top of Bookmap's Simplified Java API and thus supports all its features. Its additional functionality is described in JS-API javadoc. JS API is itself a Java add-on and provided as a jar file (click to download it).
Quick start
The two example scripts below are independent self sufficient add-ons for Bookmap. You can run them either individually, or together (as the Multiple scripts section shows)
CVD (cumulative volume delta) draws a line on the bottom panel
let cvdLine = api.registerIndicator('CVD', GraphType.BOTTOM, 0)
let cvd = 0
api.addTradeDataListeners((price, size, info) => cvdLine.addPoint(cvd += info.isBidAggressor ? size : -size))
VWAP (volume-weighted average price) draws a line on the heatmap chart.
let vwapLine = api.registerIndicator('VWAP', GraphType.PRIMARY, NaN)
let cumSize = 0, cumPriceSize = 0 // cumulative values for higher efficiency
api.addTradeDataListeners((price, size, info) => vwapLine.addPoint((cumPriceSize += price * size) / (cumSize += size)))
That's it. There is no data aggregation, throttling, or delays. Here is the expected result:
For each indicator line JS API creates its settings UI, each labeled as it was named by the script that created it:
You can subscribe to the updates of full market depth or entire order-by-order data such as CME MBO data (if provided by the market data vendor). TBA: examples
Note: if you are not familiar with lambda expression, scroll down for a more verbose but simpler implementation of the same add-ons CVD and VWAP.
Prerequisites
- Bookmap 7.2 or above
- JS-API jar file. Download it here
- Either Free or Pro license for JS API. The Free license is automatically enabled to all Bookmap users.
Examples: CVD and VWAP
This section shows how to create basic Hello World-like indicators using JS-API. If you fail to create your indicator using this guide, please let us know at which step the problem was (or still is). These CVD and VWAP script examples are identical to those in Quick Start section, but written with a simpler (though more verbose coding style)
CVD indicator
Let's create a JS add-on which draws CVD (Cumulative Volume Delta) line on the bottom panel chart
- Copy and paste the code below into a text file and save it* with path =
C:\Bookmap\addons\js\hello-cvd.js
. - Launch Bookmap, choose an instrument, and click Settings -> Configure add-ons, click ADD, select the JS-API jar file you downloaded earlier, choose
JS-API+History
and enable it - Insert the path above into the text field and click on Reload button. You should see a CVD line on the bottom panel chart
let indicator = api.registerIndicator('CVD', GraphType.BOTTOM, 0)
let cvd = 0
function onTrade(price, size, info) {
cvd += info.isBidAggressor ? size : -size
indicator.addPoint(cvd)
}
api.addTradeDataListeners(onTrade)
(*) Caution: In general, you can save the script files anywhere, with or without any extension (e.g., .txt), and use any text editor. However, make sure your editor saves these files with standard UTF-8 encoding
VWAP indicator
Repeat the previous process but with new JS code: a VWAP indicator line in the bottom panel
let indicator1 = api.registerIndicator('VWAP', GraphType.PRIMARY, NaN)
let cumSize = 0
let cumPriceSize = 0
function onTrade1(price, size, info) {
cumSize += size
cumPriceSize += price * size
let vwap = cumPriceSize / cumSize
indicator1.addPoint(vwap)
}
api.addTradeDataListeners(onTrade1)
Multiple scripts
We can add multiple* script files using semicolon as a delimiter. The scripts may be one of the other's dependency, or completely independent scripts. To see how it works, add both JS files to the script path(s) field as following: ..\addons\js\hello-cvd.js; ..\addons\js\hello-vwap.js
as shown above. See more details about multiple scripts in the Q&A section.
(*) Caution: When using multiple scripts, be aware that they are executed in the same context, and in the same order as written in the path field. This means the two scripts are evaluated as if they appear in a single file. You should avoid naming variables with the same name. This is the reason for using indicator1
and onTrade1
in the hello-vwap.js
script. Future updates of the API may include the ability to run scripts in separate execution contexts.
FAQ
Here is are common questions about the JS API.
What is the relation between Bookmap Java API and this JS API?
JS API is a wrapper around Java Simplified API, which is part of Bookmap.
Which IDE (development environment) should I use for editing JS files?
You don’t really need an IDE. For most purposes you’ll only take advantage of syntax highlighting, but any decent text editor has such capability. Examples: Notepad++, Sublime Text, Atom.
What is the purpose of JS API? Is it going to replace Java API?
Main purpose is to allow you to get started quickly without having to set up an IDE and JDK. You can have the first functional prototype for small & simple add-ons significantly faster using JS AP. Another purpose is to give access to Bookmap API to a larger community of developers, including all Web developers. However, it’s not going to replace Java API and it is not recommended to develop large add-ons in JavaScript. Yes, you can get away with it, but JS as a language is not very well suited for larger projects unless you are using some external compiler and code refactoring tools (e.g. good luck renaming a field that’s used in 100 different places without a sufficiently good IDE, and even then it would be a gamble), which would level the complexity of Java anyway..
Which is the additional functionality, provided by JS API?
It’s covered by JS API javadoc
Which JavaScript engine runs the scripts?
It’s Nashorn.
Is JS fast enough for processing market data? Any benchmarks compared to Java?
For the most part - it is fast enough. In most cases it’s slightly slower than Java, but not by much (less than 2x slower). (TBA: actual benchmarks). It’s important to understand that benchmarks above are running code isolated from anything else and do that sequentially - in reality code will be slower due to the fact memory will be more fragmented and most of the data will not be in the CPU cache. This might affect JavaScript somewhat more than pure Java, but also not by much. Most add-ons don’t really perform any heavy computations and for those even 10x difference (which isn’t the case) wouldn’t be noticeable.
Can I load / import external JS file?
Here is a quick example:
load("foo.js"); // loads script from file "foo.js" from current directory
load("http://www.example.com/t.js"); // loads script file from given URL
For detailed information refer to Nashorn extensions documentation. However, please be aware of associated risks described in the next section
Is it safe to load external 3rd party JS code or a jar file?
It's not safe at all! It is VERY important to understand that running JS add-on that is either obfuscated or you don't fully understand carries the same risk as running any untrusted code on your computer. Any arbitrary harmful code can be packaged into a js file and be executed once you invoke it with "load" command. Say by claiming that this add-on computes vwap and loads some 3rd party dependencies. Those could be real dependencies - just for computation. And then you get a targeted or a "zero day" attack (because different JS files can be returned to different users). Or even simpler: downloading js files via HTTP opens up a new attack vector: just MITM (man-in-the-middle) the connection and spoof the js file. Not hard to do, if you are physically in the same network. HTTP offers no protection from that.
How JS script can use Java objects and classes
There are multiple ways to use Java classes. Here are some examples using Java.type() method:
var JArray = Java.type("int[]"); // import Java type
var arr = new JArray(10); // create 10 element int array
var TreeMap = Java.type("java.util.TreeMap");
var treeMap = new TreeMap();
Alternatively, using Packages:
var GsonBuilder = Packages.com.google.gson.GsonBuilder
var gson = new GsonBuilder().serializeNulls().serializeSpecialFloatingPointValues().setPrettyPrinting().create()
Log.info(gson.toJson(java.util.Arrays.asList(3, 'items', null, new java.util.HashMap())))
Expected result (see in Bookmap log file) is:
20210219 15:17:09.380(UTC) INFO: [
3,
"items",
null,
{}
]
Should I convert incoming Java objects to JS objects and vice versa?
It’s not necessary. If your code works with whatever object you send to Java, it means no further adaptation needed. On the JS side you can access all fields and methods of Java objects while JS engine has no clue about their purpose. Consider the callback onTrade()
when using addConsolidatedTradeDataListener()
subscription:
api.addConsolidatedTradeDataListener((price, size, tradeInfo) => {/* some processing */})
The tradeInfo object here is an extension of TradeInfo
provided by Java API and has 2 additional fields
public class TradeInfoConsolidated extends TradeInfo {
public boolean isStop = false;
public final List<Executed> executedOrders;
}
What is the “api“ object in the examples?
The “api” object is injected into JS context by the JS API. It’s an instance of js.api.JsApi
class, which extends velox.api.layer1.simplified.Api
class
Where are alias, instrumentInfo, and initialState object that are provided in Java API via initialize()?
These objects are provided as fields of JS API. Get them as following:
let alias = api.getAlias()
let instrumentInfo = api.getInstrumentInfo()
let initialState = api.getInitialState()
What Java classes are directly accessible to the JS script?
For convenience, JS API preloads the following classes and packages making them directly accessible:
velox.api.layer1.messages.indicators.Layer1ApiUserMessageModifyIndicator.GraphType
velox.api.layer1.common.Log
velox.api.layer1.simplified
velox.api.layer1.data
js.api
In addition, Nashorn makes a short-cut for the package java and others. Refer to Nashorn documentation for more details.
Can I load / import external jar file?
Well… While technically the answer is “yes”, if you need to do that you’d probably be better off just using Java API. If you are still interested - you can use java ClassLoader directly from JavaScript, but that would be essentially just a way to write Java code in Javascript-ish syntax. Still, after you have loaded a class using ClassLoader you can use it like you would use any of built-in Bookmap or Java classes.
Which JS version syntax is supported by the JS engine?
Nashorn implements ECMAScript 5.1 specification, but also partly implements ECMAScript 6.
Is arrow function syntax supported by the JS engine?
Arrow function isn’t supported by Nashorn. However, JS API pre-processes script to replace any occurrence of arrow function by an equivalent expression acceptable by Nashorn. This conversion seems to be reliable on test cases it was tested, but there is no guarantee that these test cases cover the full scope of the syntax.
What is the exact syntax for running multiple scripts together?
The canonical syntax is absolute paths, separated by semicolon, for instance:
C:\addons\addon1.js;C:\addons\addon2.js;
However, the following modifications are also acceptable:
- Spaces around each path
- Relative paths, for instance
..\addons\js\addon.js
. Note that Bookmap’s default working directory (in Windows) is:C:\Bookmap\Config
- Double quotes around paths
- Both
“/”
and“\”
path separators are accepted in Windows
Where can I share my JS indicator with other Bookmap users?
On Bookmap forum (TBA: link)
Can I offer my indicator as a JS file on Bookmap marketplace?
Not yet because the licensing mechanism for JS add-ons is not yet developed