ConcurrentSkipListMap firstKey() throws NoSuchElementException even though it contains data

I wrote a small application that receives data from a web socket, which I store in static ConcurrentSkipListMap. The application initially creates a new thread where it runs infinitely while loop calling ConcurrentSkipListMap.firstKey(). After a while, this call throws a NoSuchElementException, even though the ConcurrentSkipListMap contains data. break point in catch block Example of my application:

I have cacher class that contains websocket implementation and NavigableMap init:

package solvethat.net.triobot.Example;

import com.binance.api.client.BinanceApiCallback;
import com.binance.api.client.BinanceApiClientFactory;
import com.binance.api.client.domain.event.DepthEvent;
import com.binance.api.client.domain.market.OrderBook;
import com.binance.api.client.domain.market.OrderBookEntry;

import java.math.BigDecimal;
import java.util.Comparator;
import java.util.List;
import java.util.NavigableMap;
import java.util.concurrent.ConcurrentSkipListMap;

public class AskCacher {

private long updateId;
private final BinanceApiClientFactory factory;

public AskCacher() {
    factory = BinanceApiClientFactory.newInstance();
    initAsks();
    runWebsocket();
}

/**
 * Init data getting order book snapshot
 */
private void initAsks() {
    try {
        OrderBook orderBook = factory.newRestClient().getOrderBook("btcusdt".toUpperCase(), 10);
        updateId = orderBook.getLastUpdateId();
        NavigableMap<Double, Double> asks = new ConcurrentSkipListMap<>(Comparator.naturalOrder());
        for (OrderBookEntry ask : orderBook.getAsks()) {
            asks.put(Double.parseDouble(ask.getPrice()), Double.parseDouble(ask.getQty()));
        }
        StaticData.ask = asks;
    } catch (Exception e) {
        System.err.println(e.getMessage());
    }
}

private void runWebsocket() {
    factory.newWebSocketClient().onDepthEvent("btcusdt", new BinanceApiCallback<>() {
        /**
         * Set ask price and call analysis method
         */
        @Override
        public void onResponse(DepthEvent depthEvent) {
            if (depthEvent.getFinalUpdateId() > updateId) {
                updateId = depthEvent.getFinalUpdateId();
                updateOrderBook(depthEvent.getAsks());
            }
        }

        /**
         * Just print err message
         */
        @Override
        public void onFailure(final Throwable cause) {
            System.err.println(cause.getMessage());
        }
    });
}

/**
 * Updates an order book (asks) with a delta received from the server.
 * Whenever the qty specified is ZERO, it means the price should was removed from the order book.
 */
private void updateOrderBook(List<OrderBookEntry> orderBookDeltas) {
    for (OrderBookEntry orderBookDelta : orderBookDeltas) {
        Double price = Double.parseDouble(orderBookDelta.getPrice());
        BigDecimal qty = new BigDecimal(orderBookDelta.getQty());
        if (qty.compareTo(BigDecimal.ZERO) == 0) {
            // qty=0 means remove this level
            StaticData.ask.remove(price);
        } else {
            StaticData.ask.put(price, Double.parseDouble(orderBookDelta.getQty()));
        }
    }

    // Print best ask to see if cacher is alive
    System.out.println("btc-usdt best ask: " + StaticData.ask.firstKey());

    // Edit map length
    if (StaticData.ask.size() > 10) {
        StaticData.ask.tailMap((Double) StaticData.ask.keySet().toArray()[10], true).clear();
    }
}}

Then infinite loop:

package solvethat.net.triobot.Example;
public class InfiniteLoop {

public void loopProcess() {
    Analyzer analyzer = new Analyzer();
    while (true) {
        analyzer.analyze(StaticData.ask.firstEntry());
    }
}}

And analyzer class:

package solvethat.net.triobot.Example;

import java.util.Map;

public class Analyzer {

    public void analyze(Map.Entry<Double, Double> entry) {

        StaticData.AnalyzeObject analyzeObject = new StaticData.AnalyzeObject();
        analyzeObject.setBestAsk(entry.getKey());

        if (analyzeObject.getBestAsk() > 50000) {
            System.out.println("It is a good price!!");
        }
    }

}

Static data model:

package solvethat.net.triobot.Example;

import java.util.NavigableMap;

public class StaticData {
    public static NavigableMap<Double, Double> ask;

    public static class AnalyzeObject {

        double bestAsk;

        public double getBestAsk() {
            return bestAsk;
        }

        public void setBestAsk(double bestAsk) {
            this.bestAsk = bestAsk;
        }
    }
}

Main class for example run:

package solvethat.net.triobot.Example;

public class Main {
    public static void main(String[] arguments) {
        new AskCacher();
        new Thread(new InfiniteLoop()::loopProcess).start();
    }
}

The example only shows how the application is composed, but I was not able to use it to raise an error but I opened my repo as public: https://github.com/Sick-E/TrioBot

Can anyone please help me? Thank you. Tomas

Upvotes: 0

Views: 216

Answers (1)

Alexander Pavlov
Alexander Pavlov

Reputation: 2220

You can replace your code with something like that (no exception handling is required)

Optional.ofNullable(trio.getThirdPair().getBids().firstEntry())
  .map(Map.Entry::getKey)
  .ifPresent(trio.getTrioAnalysis()::setBidThird);

Upvotes: 0

Related Questions