0

I am trying to setup the Interactive Brokers API on Ubuntu (18.04). I have installed both the IB Gateway, which is used for communicating with exchanges, as well as other API software for developing trading algorithms in Java, C++, C# and Python. (Which you can find here). The API is written in both Java and C++, and as stated prior it offers support for both. However when attempting to compile an example from their source code there is an error in the EReader.cpp file. I have dealt with several other C++ errors in their code however this one I cannot figure out. Here is the code:

#include "StdAfx.h"
#include "shared_ptr.h"
#include "Contract.h"
#include "EDecoder.h"
#include "EMutex.h"
#include "EReader.h"
#include "EClientSocket.h"
#include "EPosixClientSocketPlatform.h"
#include "EReaderSignal.h"
#include "EMessage.h"
#include "DefaultEWrapper.h"

#define IN_BUF_SIZE_DEFAULT 8192

static DefaultEWrapper defaultWrapper;

EReader::EReader(EClientSocket *clientSocket, EReaderSignal *signal)
: processMsgsDecoder_(clientSocket->EClient::serverVersion(), clientSocket->getWrapper(), clientSocket) {
    m_isAlive = true;
    m_pClientSocket = clientSocket;       
    m_pEReaderSignal = signal;
    m_needsWriteSelect = false;
    m_nMaxBufSize = IN_BUF_SIZE_DEFAULT;
    m_buf.reserve(IN_BUF_SIZE_DEFAULT);
}

EReader::~EReader(void) {
m_isAlive = false;

#if defined(IB_WIN32)
WaitForSingleObject(m_hReadThread, INFINITE);
#endif
}

void EReader::checkClient() {
m_needsWriteSelect = !m_pClientSocket->getTransport()- 
isOutBufferEmpty();
}

void EReader::start() {
#if defined(IB_POSIX)
pthread_t thread;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create( &thread, &attr, readToQueueThread, this );
pthread_attr_destroy(&attr);
#elif defined(IB_WIN32)
m_hReadThread = CreateThread(0, 0, readToQueueThread, this, 0, 0);
#else
#   error "Not implemented on this platform"
#endif
}

#if defined(IB_POSIX)
void * EReader::readToQueueThread(void * lpParam)
#elif defined(IB_WIN32)
DWORD WINAPI EReader::readToQueueThread(LPVOID lpParam)
#else
#   error "Not implemented on this platform"
#endif
{
EReader *pThis = reinterpret_cast<EReader *>(lpParam);

pThis->readToQueue();
return 0;
}

void EReader::readToQueue() {
EMessage *msg = 0;

while (m_isAlive) {
    if (m_buf.size() == 0 && !processNonBlockingSelect() && m_pClientSocket->isSocketOK())
        continue;

    if (!putMessageToQueue())
        break;
}

m_pClientSocket->handleSocketError();
m_pEReaderSignal->issueSignal(); //letting client know that socket was closed
}

bool EReader::putMessageToQueue() {
EMessage *msg = 0;

if (m_pClientSocket->isSocketOK())
    msg = readSingleMsg();

if (msg == 0)
    return false;

m_csMsgQueue.Enter();
m_msgQueue.push_back(ibapi::shared_ptr<EMessage>(msg));
m_csMsgQueue.Leave();
m_pEReaderSignal->issueSignal();

return true;
}

bool EReader::processNonBlockingSelect() {
fd_set readSet, writeSet, errorSet;
struct timeval tval;

tval.tv_usec = 100 * 1000; //100 ms
tval.tv_sec = 0;

if( m_pClientSocket->fd() >= 0 ) {

    FD_ZERO( &readSet);
    errorSet = writeSet = readSet;

    FD_SET( m_pClientSocket->fd(), &readSet);

    if (m_needsWriteSelect)
        FD_SET( m_pClientSocket->fd(), &writeSet);

    FD_SET( m_pClientSocket->fd(), &errorSet);

    int ret = select( m_pClientSocket->fd() + 1, &readSet, &writeSet, &errorSet, &tval);

    if( ret == 0) { // timeout
        return false;
    }

    if( ret < 0) {  // error
        m_pClientSocket->eDisconnect();
        return false;
    }

    if( m_pClientSocket->fd() < 0)
        return false;

    if( FD_ISSET( m_pClientSocket->fd(), &errorSet)) {
        // error on socket
        m_pClientSocket->onError();
    }

    if( m_pClientSocket->fd() < 0)
        return false;

    if( FD_ISSET( m_pClientSocket->fd(), &writeSet)) {
        // socket is ready for writing
        onSend();
    }

    if( m_pClientSocket->fd() < 0)
        return false;

    if( FD_ISSET( m_pClientSocket->fd(), &readSet)) {
        // socket is ready for reading
        onReceive();
    }

    return true;
}

return false;
}

void EReader::onSend() {
m_pEReaderSignal->issueSignal();
}

void EReader::onReceive() {
int nOffset = m_buf.size();

m_buf.resize(m_nMaxBufSize);

int nRes = m_pClientSocket->receive(m_buf.data() + nOffset, m_buf.size() - nOffset);

if (nRes <= 0)
    return;

m_buf.resize(nRes + nOffset);   
}

bool EReader::bufferedRead(char *buf, int size) {
while (size > 0) {
    while (m_buf.size() < size && m_buf.size() < m_nMaxBufSize)
        if (!processNonBlockingSelect() && !m_pClientSocket->isSocketOK())
            return false;

    int nBytes = (std::min<unsigned int>)(m_nMaxBufSize, size);



    std::copy(m_buf.begin(), m_buf.begin() + nBytes, buf);
    std::copy(m_buf.begin() + nBytes, m_buf.end(), m_buf.begin());
    m_buf.resize(m_buf.size() - nBytes);

    size -= nBytes;
    buf += nBytes;
}

return true;
}

EMessage * EReader::readSingleMsg() {
if (m_pClientSocket->usingV100Plus()) {
    int msgSize;

    if (!bufferedRead((char *)&msgSize, sizeof(msgSize)))
        return 0;

    msgSize = htonl(msgSize);

    if (msgSize <= 0 || msgSize > MAX_MSG_LEN)
        return 0;

    std::vector<char> buf = std::vector<char>(msgSize);

    if (!bufferedRead(buf.data(), buf.size()))
        return 0;

    return new EMessage(buf);
}
else {
    const char *pBegin = 0;
    const char *pEnd = 0;
    int msgSize = 0;

    while (msgSize == 0)
    {
        if (m_buf.size() >= m_nMaxBufSize * 3/4) 
            m_nMaxBufSize *= 2;

        if (!processNonBlockingSelect() && !m_pClientSocket->isSocketOK())
            return 0;

        pBegin = m_buf.data();
        pEnd = pBegin + m_buf.size();
        msgSize = EDecoder(m_pClientSocket->EClient::serverVersion(), &defaultWrapper).parseAndProcessMsg(pBegin, pEnd);
    }

    std::vector<char> msgData(msgSize);

    if (!bufferedRead(msgData.data(), msgSize))
        return 0;

    if (m_buf.size() < IN_BUF_SIZE_DEFAULT && m_buf.capacity() > IN_BUF_SIZE_DEFAULT)
    {
        m_buf.resize(m_nMaxBufSize = IN_BUF_SIZE_DEFAULT);
        m_buf.shrink_to_fit();
    }

    EMessage * msg = new EMessage(msgData);

    return msg;
}
}

ibapi::shared_ptr<EMessage> EReader::getMsg(void) {
m_csMsgQueue.Enter();

if (m_msgQueue.size() == 0) {
    m_csMsgQueue.Leave();

    return ibapi::shared_ptr<EMessage>();
}

ibapi::shared_ptr<EMessage> msg = m_msgQueue.front();

m_msgQueue.pop_front();
m_csMsgQueue.Leave();

return msg;
}


void EReader::processMsgs(void) {
m_pClientSocket->onSend();
checkClient();

ibapi::shared_ptr<EMessage> msg = getMsg();

if (!msg.get())
    return;

const char *pBegin = msg->begin();

while (processMsgsDecoder_.parseAndProcessMsg(pBegin, msg->end()) > 0) 
{
    msg = getMsg();

    if (!msg.get())
        break;

    pBegin = msg->begin();
} 
}

The error I get it is the following: error

error: 'min' was not declared in this scope  int nBytes = 
min(m_nMaxBuffSize, size);

I have had to do other things such as editing other source code and makefiles, I am stuck here. Any insight would be appreciated.

ofey73
  • 155
  • 3
  • 12

1 Answers1

1

In my version 973 source at that line I have

int nBytes = (std::min<unsigned int>)(m_nMaxBufSize, size);

Make sure you are using the latest version. The problem may be an example of what happens here Why is "using namespace std" considered bad practice?

brian
  • 10,619
  • 4
  • 21
  • 79
  • However now, the compilation process went further but got stuck on not being able to find a header file in the Main.cpp. Couldn't find the Windows.h file and it was included in the main, I cannot seem to find Windows.h anywhere in the directory with all the IBKR stuff. Did you deal with this problem when setting it up? – ofey73 Jul 24 '18 at 19:15
  • I didn't use C++, neither should you. I didn't downvote your question but someone did since it was an obvious error for any C++ programmer. Also, windows.h is a very basic header file that every windows program needs. I can only conclude it's your first C++ program and your build environment isn't properly set up. Your first C++ project shouldn't involve all your money! If you're familiar with java or python, try those instead. The C++ api has always been complained about by others with much more experience. – brian Jul 24 '18 at 19:40
  • 1
    Yeah there definitely seems to be a lot of stuff wrong with it. Even things as blatantly obvious as cases (yes upper and lower case) with paths and directoriess (e.g., ../../../source/cppclient vs ../../../source/CppClient). Prior to me going into the makefile and changing that, it wouldn’t compile (duh). Yes I am pretty new to C++, (although not my first program) I’d say I’m good with C, Python and R. Thanks. – ofey73 Jul 24 '18 at 19:51
  • And if it requires the Windows.h header, can it only be built exclusively _on_ _Windows_? – ofey73 Jul 24 '18 at 19:59
  • You can search on how to use MinGW to cross compile for windows on linux. That will have the windows.h file. – brian Jul 24 '18 at 20:19
  • to be honest the Java API doesn’t look too hot either. For example, in the ApiDemo.java file they return an array list as opposed to a list. Plus INSTANCE is a final const not a mutable instance, dunno why all the variables are final, the static register() call is kinda silly as well... why not call init() for the purposes of clarity? It has member variables initialising each other, which can be problematic. It seems pretty bad actually. – ofey73 Aug 02 '18 at 01:55
  • 1
    I personally wouldn't use the ApiDemo way of making a program. You can see it uses ib.controller classes which are weird. The TestJavaClient program is much simpler IMO. I only ever import com.ib.client.* for my programs. – brian Aug 03 '18 at 00:18