Price Conversion
This guide covers price conversion in Bookmap add-ons, explaining the relationship between Level 1 (raw) prices and human-readable prices, and how to use the pips value correctly.
Price Formats in Bookmapโ
Bookmap uses two internal price representations:
| Format | Description | Example (ES futures) |
|---|---|---|
| Level 1 (Raw) | Internal normalized representation | 160790 |
| Human-Readable | Actual price traders see | 4019.75 |
The Pips Valueโ
The pips value from InstrumentInfo represents the minimum price increment for an instrument.
Obtaining Pipsโ
private double pips;
@Override
public void initialize(String alias, InstrumentInfo info, Api api, InitialState initialState) {
this.pips = info.pips;
}
Conversion Formulasโ
| Conversion | Formula | Use Case |
|---|---|---|
| Raw รขโ โ Human-Readable | price * pips | UI display, logging, volume profile keys |
| Human-Readable รขโ โ Raw | price / pips | indicator.addPoint(), chart plotting |
Critical Rule for Indicatorsโ
indicator.addPoint(value) expects Level 1 format (raw price), NOT human-readable price.
This is the most common source of bugs where indicator lines appear off-chart or at incorrect levels.
Wrong Approachโ
@Override
public void onTrade(double price, int size, TradeInfo tradeInfo) {
double displayPrice = price * pips; // Human-readable: 6086.50
// WRONG - indicator will plot at wrong level (off-screen)
myIndicator.addPoint(displayPrice);
}
Correct Approachโ
@Override
public void onTrade(double price, int size, TradeInfo tradeInfo) {
double rawPrice = price; // Level 1 format
double displayPrice = price * pips; // Human-readable for UI
// CORRECT - use raw price for indicator
myIndicator.addPoint(rawPrice);
}
Common Scenariosโ
Scenario: Trade Price to Indicatorโ
The price parameter in onTrade() is already in Level 1 format.
@Override
public void onTrade(double price, int size, TradeInfo tradeInfo) {
// price is already Level 1 - use directly for indicators
tradeIndicator.addPoint(price);
// Convert for display purposes
double displayPrice = price * pips;
Log.info("Trade at: " + displayPrice);
}
Scenario: Order Price to Indicatorโ
Order prices from OrderInfoUpdate and ExecutionInfo need conversion.
@Override
public void onOrderUpdated(OrderInfoUpdate orderInfoUpdate) {
// limitPrice is raw format, convert for indicator
double levelForIndicator = orderInfoUpdate.limitPrice / pips;
lastOrderLimitPrice.addPoint(levelForIndicator);
}
@Override
public void onOrderExecuted(ExecutionInfo executionInfo) {
// execution price needs same conversion
double levelForIndicator = executionInfo.price / pips;
lastExecutionPrice.addPoint(levelForIndicator);
}
Scenario: Calculated Metrics to Indicatorโ
When you calculate metrics (POC, High, Low) using human-readable prices, convert back for indicators.
// Store metrics in human-readable format for UI
private double pocPrice = 6086.50;
private double highPrice = 6090.25;
private void updateIndicators() {
// Convert back to Level 1 for indicator plotting
if (!Double.isNaN(pocPrice)) {
pocIndicator.addPoint(pocPrice / pips);
}
if (!Double.isNaN(highPrice)) {
highIndicator.addPoint(highPrice / pips);
}
}
Scenario: BBO Level Comparisonโ
BBO prices from onBbo() are already level numbers.
@Override
public void onBbo(int bidPrice, int bidSize, int askPrice, int askSize) {
// bidPrice and askPrice are level numbers
for (OrderInfo order : activeLimitOrders.values()) {
// Convert order's raw price to level for comparison
double orderLevel = order.limitPrice / pips;
boolean nearBid = bidPrice - orderLevel <= CANCEL_DISTANCE;
boolean nearAsk = orderLevel - askPrice <= CANCEL_DISTANCE;
}
}
Complete Exampleโ
@Layer1SimpleAttachable
@Layer1StrategyName("Price Level Demo")
@Layer1ApiVersion(Layer1ApiVersionValue.VERSION2)
public class PriceLevelDemo implements CustomModule, TradeDataListener {
private double pips;
private Indicator highIndicator;
private Indicator lowIndicator;
// Store as human-readable for display
private double highPrice = Double.MIN_VALUE;
private double lowPrice = Double.MAX_VALUE;
@Override
public void initialize(String alias, InstrumentInfo info, Api api, InitialState initialState) {
this.pips = info.pips;
highIndicator = api.registerIndicator("High", GraphType.PRIMARY);
lowIndicator = api.registerIndicator("Low", GraphType.PRIMARY);
}
@Override
public void onTrade(double price, int size, TradeInfo tradeInfo) {
double displayPrice = price * pips; // Human-readable
// Track high/low in human-readable format
if (displayPrice > highPrice) {
highPrice = displayPrice;
}
if (displayPrice < lowPrice) {
lowPrice = displayPrice;
}
// Convert BACK to Level 1 for indicators
highIndicator.addPoint(highPrice / pips);
lowIndicator.addPoint(lowPrice / pips);
}
@Override
public void stop() {}
}
Dual Storage Patternโ
For complex add-ons, store both formats:
// For indicator plotting (Level 1)
private double highRaw = Double.NaN;
// For UI display (human-readable)
private double highDisplay = Double.NaN;
private void updateHigh(double rawPrice) {
double displayPrice = rawPrice * pips;
if (Double.isNaN(highDisplay) || displayPrice > highDisplay) {
highDisplay = displayPrice;
highRaw = rawPrice;
}
}
private void updateIndicators() {
if (!Double.isNaN(highRaw)) {
highIndicator.addPoint(highRaw);
}
}
Helper Methodsโ
Centralize conversions for clarity:
private double toDisplayPrice(double rawPrice) {
return rawPrice * pips;
}
private double toRawPrice(double displayPrice) {
return displayPrice / pips;
}
Common Pitfallsโ
Pitfall: Indicator Lines Off-Chartโ
Symptom: Indicators are enabled but no lines visible on chart
Cause: Passing human-readable price to addPoint() instead of Level 1
Solution: Divide human-readable price by pips before calling addPoint()
Pitfall: Volume Profile Precisionโ
Symptom: Price levels not matching exactly
Cause: Using raw prices for volume profile map keys
Solution: Use human-readable prices for volume profile keys to maintain decimal precision
// Use human-readable for better precision in maps
volumeProfile.merge(displayPrice, (long) size, Long::sum);
See Alsoโ
- Data Listeners - TradeDataListener and price handling
- Api Interface - Registering and updating indicators
- Javadoc: InstrumentInfo
- Javadoc: Indicator