Petersoj / IQFeed4j

A Java API for the high quality, market-data-vendor "DTN IQFeed" https://iqfeed.net
https://petersoj.github.io/IQFeed4j/
MIT License
13 stars 8 forks source link

Could not handle SummaryUpdate message: Invalid value for MinuteOfHour 99 #7

Closed subes closed 2 years ago

subes commented 2 years ago
2022-02-23 01:15:00.237 [ |invesdwin Level 1 F] ERROR n.j.iqfeed4j.feed.streaming.level1.Level1Feed.handleSummaryU - Could not handle SummaryUpdate message!
net.jacobpeterson.iqfeed4j.util.csv.mapper.CSVMappingException: Error mapping at index 1 with offset 1!
        at net.jacobpeterson.iqfeed4j.util.csv.mapper.index.IndexCSVMapper.map(IndexCSVMapper.java:130)
        at net.jacobpeterson.iqfeed4j.feed.streaming.level1.Level1Feed.handleSummaryUpdateMessage(Level1Feed.java:642)
        at net.jacobpeterson.iqfeed4j.feed.streaming.level1.Level1Feed.onMessageReceived(Level1Feed.java:494)
        at net.jacobpeterson.iqfeed4j.feed.AbstractFeed.run(AbstractFeed.java:226)
        at java.base/java.lang.Thread.run(Thread.java:829)
Caused by: java.time.format.DateTimeParseException: Text '99:99:99.139000' could not be parsed: Invalid value for MinuteOfHour (valid values 0 - 59): 99
        at java.base/java.time.format.DateTimeFormatter.createError(DateTimeFormatter.java:2017)
        at java.base/java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1952)
        at java.base/java.time.LocalTime.parse(LocalTime.java:463)
        at net.jacobpeterson.iqfeed4j.util.csv.mapper.AbstractCSVMapper$DateTimeConverters.lambda$static$7(AbstractCSVMapper.java:124)
        at net.jacobpeterson.iqfeed4j.util.csv.mapper.CSVMapping.lambda$new$0(CSVMapping.java:25)
        at net.jacobpeterson.iqfeed4j.util.csv.mapper.CSVMapping.apply(CSVMapping.java:46)
        at net.jacobpeterson.iqfeed4j.util.csv.mapper.index.IndexCSVMapper.map(IndexCSVMapper.java:128)
        ... 4 common frames omitted
Caused by: java.time.DateTimeException: Invalid value for MinuteOfHour (valid values 0 - 59): 99
        at java.base/java.time.temporal.ValueRange.checkValidIntValue(ValueRange.java:330)
        at java.base/java.time.temporal.ChronoField.checkValidIntValue(ChronoField.java:736)
        at java.base/java.time.format.Parsed.resolveTime(Parsed.java:555)
        at java.base/java.time.format.Parsed.resolveTimeFields(Parsed.java:480)
        at java.base/java.time.format.Parsed.resolveFields(Parsed.java:267)
        at java.base/java.time.format.Parsed.resolve(Parsed.java:253)
        at java.base/java.time.format.DateTimeParseContext.toResolved(DateTimeParseContext.java:331)
        at java.base/java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:2052)
        at java.base/java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1948)
        ... 9 common frames omitted
subes commented 2 years ago

Though it seems the error can be ignored since it only gets logged:

private void handleSummaryUpdateMessage(String[] csv, Level1MessageType messageType) {
        try {
            SummaryUpdate summaryUpdate = summaryUpdateCSVMapper.map(csv, 1);
            FeedMessageListener<SummaryUpdate> listener =
                    summaryUpdateListenersOfSymbols.get(summaryUpdate.getSymbol());
            if (listener == null) {
                LOGGER.error("Received SummaryUpdate, but no listener for symbol {} exists!",
                        summaryUpdate.getSymbol());
            } else {
                switch (messageType) {
                    case SUMMARY_MESSAGE:
                        summaryUpdate.setType(SummaryUpdate.Type.SUMMARY);
                        break;
                    case UPDATE_MESSAGE:
                        summaryUpdate.setType(SummaryUpdate.Type.UPDATE);
                        break;
                }

                listener.onMessageReceived(summaryUpdate);
            }
        } catch (Exception exception) {
            LOGGER.error("Could not handle SummaryUpdate message!", exception);
        }
    }
Petersoj commented 2 years ago

@subes yes I've experienced this issue on the Level 1 Feed before (it's extremely rare though). I'm not sure why IQFeed decides to send trade data with 99:99:99.139000 as the time of a SummaryUpdate (as shown in your given stacktrace). I purposefully made that handleSummaryUpdateMessage method to only print the exception instead of throw it to the listener in the event that some data couldn't be mapped. So worse-case scenario, your trading algo/application doesn't receive a trade with an invalid timestamp, but perhaps that's actually a good thing (so invalid timestamps don't throw exceptions on the feed's thread and abruptly terminate the level 1 feed). I'm closing this issue as you can just ignore that exception. I would recommend contacting IQFeed developer support if it keeps happening (I have not seen this happen to my trading application in a long time and I am listening to level 1 data for over 1500 high-market-cap tickers, so perhaps this issue is localized to small-cap tickers?).

subes commented 2 years ago

Thanks for the info. This happens on XG# or XG#C (thus fdax). I also only had this a few times in the log. Might be helpful to have an option to log invalid messages so one knows how the complete invalid message looked like? Though I guess iqfeed.exe might have a debug log option? Though would be ideal if the exception contained the message in a string format.

Petersoj commented 2 years ago

IQFeed4j does indeed have an option to log all Level 1 messages that are received. You can do so by setting the net.jacobpeterson.iqfeed4j.feed.streaming.level1.Level1Feed logger level to TRACE using your logger implementation. Yes you can also log all messages in iqfeed.exe.