It's their implementation.
1) Retrieve their latest API JAR, version 9.72.05, using Maven:
<!-- https://mvnrepository.com/artifact/com.interactivebrokers.tws/tws-api -->
<dependency>
<groupId>com.interactivebrokers.tws</groupId>
<artifactId>tws-api</artifactId>
<version>9.72.05</version>
</dependency>
* Side note: I've been using an older version of their API, which was *still* 9.72.05. Then, at some point, using the same code on my side, I've downloaded the latest version of their JAR and surprise: it didn't compile any more! So they've not just updated / fixed their implementation, but changed the API, *without* changing the API version. This seems broken to me.
I can send you a saved version of the same 9.72.05 JAR with a different API contract if you don't believe me. Anyways.
2) Go to your local Maven repository at ~/.m2/repository/com/interactivebrokers/tws/tws-api/9.72.05
Use IntelliJ to disassemble the tws-api-9.72.05.jar and navigate to the IbReader class.
Take a look at the private EMessage readSingleMessage() throws IOException method.
3) It does this, and it's broken:
if(this.m_iBufLen == 0) {
this.m_iBufLen = this.m_clientSocket.read(this.m_iBuf, this.m_iBufLen, this.m_iBuf.length - this.m_iBufLen);
}
msgSize = this.m_iBufLen > 0?this.m_threadReadDecoder.processMsg(new EMessage(this.m_iBuf)):0;
this.m_iBufLen -= msgSize;
Where the buffer I'm talking about is declared as private byte[] m_iBuf = new byte[8192];
I fixed it temporarily by declaring it as
private byte[] m_iBuf = new byte[262144]; // 256 Kb ought to be enough
4) What's the problem?
Well, a socket read may return any number of bytes. The read data may contain a full message, a lot of messages or, and here's the problem, only part of a message. Say the message is [CONTRACT_END] but the socket read only reads [CONT] in one go. The call to threadReadDecoder.processMsg() will fail, as it doesn't have a full message to decode, so returned msgSize will be zero. Then, it fails to subtract it from the buffer size and consequently, it doesn't read the remaining [RACT_END].
Something along these lines anyways. The good thing with Java is that you can look at their code and fix it yourself. The bad is that you see what crap they write.
1) Retrieve their latest API JAR, version 9.72.05, using Maven:
<!-- https://mvnrepository.com/artifact/com.interactivebrokers.tws/tws-api -->
<dependency>
<groupId>com.interactivebrokers.tws</groupId>
<artifactId>tws-api</artifactId>
<version>9.72.05</version>
</dependency>
* Side note: I've been using an older version of their API, which was *still* 9.72.05. Then, at some point, using the same code on my side, I've downloaded the latest version of their JAR and surprise: it didn't compile any more! So they've not just updated / fixed their implementation, but changed the API, *without* changing the API version. This seems broken to me.
I can send you a saved version of the same 9.72.05 JAR with a different API contract if you don't believe me. Anyways.
2) Go to your local Maven repository at ~/.m2/repository/com/interactivebrokers/tws/tws-api/9.72.05
Use IntelliJ to disassemble the tws-api-9.72.05.jar and navigate to the IbReader class.
Take a look at the private EMessage readSingleMessage() throws IOException method.
3) It does this, and it's broken:
if(this.m_iBufLen == 0) {
this.m_iBufLen = this.m_clientSocket.read(this.m_iBuf, this.m_iBufLen, this.m_iBuf.length - this.m_iBufLen);
}
msgSize = this.m_iBufLen > 0?this.m_threadReadDecoder.processMsg(new EMessage(this.m_iBuf)):0;
this.m_iBufLen -= msgSize;
Where the buffer I'm talking about is declared as private byte[] m_iBuf = new byte[8192];
I fixed it temporarily by declaring it as
private byte[] m_iBuf = new byte[262144]; // 256 Kb ought to be enough
4) What's the problem?
Well, a socket read may return any number of bytes. The read data may contain a full message, a lot of messages or, and here's the problem, only part of a message. Say the message is [CONTRACT_END] but the socket read only reads [CONT] in one go. The call to threadReadDecoder.processMsg() will fail, as it doesn't have a full message to decode, so returned msgSize will be zero. Then, it fails to subtract it from the buffer size and consequently, it doesn't read the remaining [RACT_END].
Something along these lines anyways. The good thing with Java is that you can look at their code and fix it yourself. The bad is that you see what crap they write.