To use the Interactive Brokers (IB) TWS API, I need to write a class that implements EWrapper
. I am having trouble properly disconnecting my socket in that class, even when using the built-in eDisconnect()
method provided by IB.
Whenever I write a class that implements EWrapper
, calling eDisconnect()
on the EClientSocket
instance throws an Exception
(which is passed to error(Exception e)
...more details below). I would like to be able to disconnect properly, so that I can connect and disconnect serially within the same program.
Consider the following example:
public class ConnectionTest {
public static void main(String[] args) {
// Create Contract
Contract contract = new Contract();
contract.symbol("IBM");
contract.secType("STK");
contract.exchange("NYSE");
TWSCounter twsCounter = new TWSCounter(); // Class I wrote that simply keeps track of ID request numbers
APIClient apiClient = new APIClient(twsCounter);
apiClient.connect();
apiClient.reqContractDetails(contract);
//apiClient.disconnect(); // commented out for first example
}
}
public class APIClient implements EWrapper {
private TWSCounter twsCounter;
private EClientSocket clientSocket = null;
private EJavaSignal signal = new EJavaSignal();
private EReader reader;
// Constructor
public APIClient(TWSCounter twsCounter) {
this.twsCounter = twsCounter;
}
public void connect() {
// Create a new EClientSocket object
clientSocket = new EClientSocket (this, signal);
// Connect to the TWS
clientSocket.eConnect("127.0.0.1", 7497, 0);
// Pause here for connection to complete
try {
while (! (clientSocket.isConnected()));
}
catch (Exception e) {
e.printStackTrace();
System.err.println("Error trying to connect to TWS API");
System.exit(1);
}
reader = new EReader(clientSocket, signal);
reader.start();
// Based on IB sample code in Test.java
new Thread(() -> {
while (clientSocket.isConnected()) {
signal.waitForSignal();
try {
reader.processMsgs();
} catch(Exception e) {
error(e);
System.err.println("Error while trying reader.processMsgs()");
}
}
}).start();
}
public void disconnect() {
clientSocket.eDisconnect();
}
public void reqContractDetails(Contract contract) {
clientSocket.reqContractDetails(twsCounter.getNextNumber(), contract);
}
@Override
public void contractDetails(int reqId, ContractDetails contractDetails) {
System.out.println("conID: " + contractDetails.conid());
}
// More methods...
When I run ConnectionTest
, I correctly get the output, conID: 8314
. The program continues to run indefinitely, however, until I terminate it manually in eclipse by pushing the 'stop' button.
If I "uncomment" apiClient.disconnect()
at the end of ConnectionTest
, then the program disconnects, however an exception is thrown.
How one chooses to override the error methods in the APIClient affects what happens. If they are not overridden, the process terminates silently, despite the Exception
.
If, however, public void error(Exception e)
is overridden as follows, we get the following stack trace:
public void error(Exception e) {
e.printStackTrace();
}
java.net.SocketException: Socket closed
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.socketRead(Unknown Source)
at java.net.SocketInputStream.read(Unknown Source)
at java.net.SocketInputStream.read(Unknown Source)
at java.net.SocketInputStream.read(Unknown Source)
at java.io.DataInputStream.readInt(Unknown Source)
at com.ib.client.EClientSocket.readInt(EClientSocket.java:233)
at com.ib.client.EReader.readSingleMessage(EReader.java:119)
at com.ib.client.EReader.putMessageToQueue(EReader.java:79)
at com.ib.client.EReader.run(EReader.java:57)
I believe issue is that the EReader
(EReader
extends Thread
) calls putMessageToQueue()
even after the socket is disconnected. From EReader
:
@Override
public void run() {
try {
// loop until thread is terminated
while (!isInterrupted()) {
if (!putMessageToQueue())
break;
}
}
catch ( Exception ex ) {
//if (parent().isConnected()) {
if( ex instanceof EOFException ) {
eWrapper().error(EClientErrors.NO_VALID_ID, EClientErrors.BAD_LENGTH.code(),
EClientErrors.BAD_LENGTH.msg() + " " + ex.getMessage());
}
else {
eWrapper().error( ex);
}
parent().eDisconnect();
//}
}
m_signal.issueSignal();
}
Modifying the APIClient.disconnect()
method to interrupt the EReader
, doesn't seem to help however:
public void disconnect() {
reader.interrupt();
clientSocket.eDisconnect();
}
For the most part, I've been able to get by without solving this issue properly. What I would really like to be able to do, however, is connect and disconnect serially, as in this simplified example:
public class ConnectionTest {
public static void main(String[] args) {
// Create Contracts
Contract contract = new Contract();
contract.symbol("IBM");
contract.secType("STK");
contract.exchange("NYSE");
Contract contract2 = new Contract();
contract2.symbol("KO");
contract2.secType("STK");
contract2.exchange("NYSE");
TWSCounter twsCounter = new TWSCounter();
APIClient apiClient = new APIClient(twsCounter);
apiClient.connect();
apiClient.reqContractDetails(contract);
try {
Thread.sleep(1000);
} catch (Exception e){
System.err.println("Couldn't sleep");
System.exit(1);
}
apiClient.disconnect();
try {
Thread.sleep(1000);
} catch (Exception e){
System.err.println("Couldn't sleep");
}
apiClient.connect();
apiClient.reqContractDetails(contract2);
try {
Thread.sleep(1000);
} catch (Exception e){
System.err.println("Couldn't sleep");
}
apiClient.disconnect();
}
}
Running this, as is, will sometimes correctly give conID: 8314
and conID: 8894
along with two stack traces the same as above, but will also sometimes give conID: 8314
and Connection Message: -1, 507, Bad Message Length null
. Error message 507 is:
Bad Message Length (Java-only)
Indicates EOF exception was caught while reading from the socket. This can occur if there is an attempt >to connect to TWS with a client ID that is already in use, or if TWS is locked, closes, or breaks the >connection. It should be handled by the client application and used to indicate that the socket
connection is not valid.
IB Message Codes
For what it's worth, I've been completely frustrated by this problem off and on for two years. (I'm not a professional programmer.) I've searched for solutions online and even paid a programmer to help me, all to no avail (although I have learned much more java in the process). I've also downloaded the latest version of the IB API (v 9.76). The same problem occurs with multiple versions of the IB API.
question from:
https://stackoverflow.com/questions/65896041/ib-tws-api-has-a-socketexception-when-calling-edisconnect-on-the-eclientsocket