Skip to main content

OnlineCalculatable Implementation Guide

This guide covers implementing the OnlineCalculatable interface for indicators that support both historical scrollback and real-time updates.

Overviewโ€‹

Two Computation Modesโ€‹

The OnlineCalculatable interface requires supporting two distinct calculation modes:

ModeMethodWhen Used
Historical RangecalculateValuesInRange()Scrollback, replay, initial load
Real-time UpdatescreateOnlineValueCalculator()Live data as it arrives

Why Both Are Neededโ€‹

  • Historical: User scrolls back, replays data, or initially loads the chart
  • Real-time: Live data streams in during trading

Indicator Registrationโ€‹

Using the Builder Patternโ€‹

Layer1ApiUserMessageModifyIndicator message = Layer1ApiUserMessageModifyIndicator
.builder(MyStrategy.class, indicatorName)
.setIsAdd(true)
.setGraphType(GraphType.PRIMARY)
.setOnlineCalculatable(this)
.setIndicatorColorScheme(colorScheme)
.setIndicatorLineStyle(IndicatorLineStyle.NONE)
.build();

indicatorsFullNameToUserName.put(message.fullName, message.userName);
provider.sendUserMessage(message);

Name Managementโ€‹

private Map<String, String> indicatorsFullNameToUserName = new HashMap<>();
private Map<String, String> indicatorsUserNameToFullName = new HashMap<>();

// After registration
indicatorsFullNameToUserName.put(message.fullName, message.userName);
indicatorsUserNameToFullName.put(message.userName, message.fullName);

calculateValuesInRange Implementationโ€‹

Method Signatureโ€‹

void calculateValuesInRange(
String indicatorName, // fullName of the indicator
String indicatorAlias, // Instrument alias
long t0, // Start time (nanoseconds)
long intervalWidth, // Interval duration (nanoseconds)
int intervalsNumber, // Number of intervals
CalculatedResultListener listener
)

Complete Exampleโ€‹

@Override
public void calculateValuesInRange(String indicatorName, String alias,
long t0, long intervalWidth, int intervalsNumber, CalculatedResultListener listener) {

if (dataStructureInterface == null) {
listener.setCompleted();
return;
}

String userName = indicatorsFullNameToUserName.get(indicatorName);

// Request trade data
ArrayList<TreeResponseInterval> data = dataStructureInterface.get(
t0, intervalWidth, intervalsNumber, alias,
new StandardEvents[] { StandardEvents.TRADE }
);

// Get initial price from snapshot (element 0)
TradeAggregationEvent snapshot = (TradeAggregationEvent) data.get(0)
.events.get(StandardEvents.TRADE.toString());
double lastPrice = snapshot.lastPrice;

// Process each interval (1 through N)
for (int i = 1; i <= intervalsNumber; ++i) {
TradeAggregationEvent trades = (TradeAggregationEvent) data.get(i)
.events.get(StandardEvents.TRADE.toString());

if (!Double.isNaN(trades.lastPrice)) {
lastPrice = trades.lastPrice;
}

// Return marker or value
if (!trades.askAggressorMap.isEmpty() || !trades.bidAggressorMap.isEmpty()) {
listener.provideResponse(new Marker(lastPrice, -8, -8, tradeIcon));
} else {
listener.provideResponse(lastPrice);
}
}

listener.setCompleted();
}

Key Rulesโ€‹

  1. Provide exactly intervalsNumber responses
  2. Skip element 0 (it's the snapshot)
  3. Always call listener.setCompleted()
  4. Check listener.isCancelled() for expensive calculations

createOnlineValueCalculator Implementationโ€‹

Method Signatureโ€‹

OnlineValueCalculatorAdapter createOnlineValueCalculator(
String indicatorName,
String indicatorAlias,
long time,
Consumer<Object> listener,
InvalidateInterface invalidateInterface
)

Complete Exampleโ€‹

@Override
public OnlineValueCalculatorAdapter createOnlineValueCalculator(
String indicatorName, String indicatorAlias, long time,
Consumer<Object> listener, InvalidateInterface invalidateInterface) {

String userName = indicatorsFullNameToUserName.get(indicatorName);
invalidateInterfaceMap.put(userName, invalidateInterface);

return new OnlineValueCalculatorAdapter() {
@Override
public void onTrade(String alias, double price, int size, TradeInfo tradeInfo) {
if (alias.equals(indicatorAlias)) {
listener.accept(new Marker(price, -8, -8, tradeIcon));
}
}

@Override
public void onIntervalWidth(long intervalWidth) {
// Handle zoom changes
}
};
}

OnlineValueCalculatorAdapter Callbacksโ€‹

CallbackUse Case
onTrade()Trade-based indicators
onDepth()Depth-based indicators
onOrderUpdated()Order tracking
onOrderExecuted()Execution markers
onUserMessage()Custom events
onIntervalWidth()Zoom level changes
onRealTimeDataStart()Historical รขโ€ โ€™ live transition

InvalidateInterface Usageโ€‹

Store the interface for later recalculation:

private Map<String, InvalidateInterface> invalidateInterfaceMap = new ConcurrentHashMap<>();

// In createOnlineValueCalculator
invalidateInterfaceMap.put(userName, invalidateInterface);

// When settings change
public void onSettingsChanged() {
InvalidateInterface ii = invalidateInterfaceMap.get(INDICATOR_NAME);
if (ii != null) {
ii.invalidate();
}
}

Response Typesโ€‹

TypeDescription
DoubleLine value
Double.NaNNo value (gap)
MarkerSingle icon
List<Marker>Multiple icons
DataCoordinateMarkerData-bound graphic

Cross-Referencesโ€‹