Start Using ForexConnect API (C++ Win32/Win64)

From FxCodeBaseWiki
Jump to: navigation, search


This article describes how to start using ForexConnect API. Here you can find the API basics explained and step-by-step instructions on creating a working sample of a simple trading application.

Platform: Microsoft Windows x86, x64
Language: C++
IDE: Microsoft Visual Studio 2005, 2008, 2010


Getting and Installing ForexConnect API Libraries

  1. Download the last version of ForexConnect API:
    • If you have a 32-bit version of Microsoft Windows, download 32-bit version of ForexConnect API.
    • If you have a 64-bit version of Microsoft Windows, download 64-bit build of ForexConnect API.
    See the System Properties dialog for information about your system version.
    Note that if your system is Windows XP and you do not see "x64 Edition" listed in the System Properties dialog, then you are running a 32-bit version of Windows XP.
    To get the latest version of the API, please visit the Download page.
  2. Start the installer and then follow instructions of the setup wizard.
Further we will suppose that ForexConnect API is installed in C:\Program Files\Candleworks\ForexConnectAPI\.

Using ForexConnect API with Microsoft Visual Studio

You should make the following changes in your project Properties:

  1. Configure the post-build event of your project to copy ForexConnect API libraries to the folder where your program is built:
    In Configuration Properties → Build Events → Post-Build Event → Command Line, add the following text:
    copy "C:\Program Files\Candleworks\ForexConnectAPI\bin\*.dll" "$(TargetDir)"
    As an alternative, you can add "C:\Program Files\Candleworks\ForexConnectAPI\bin\" to the value of the system variable Path.
  2. Add a reference to ForexConnect library to the Linker parameters:
    1. In Configuration Properties → Linker → General → Additional Library Directories, add
      "C:\Program Files\CandleWorks\ForexConnectAPI\lib"
    2. In Configuration Properties → Linker → Input → Additional Dependencies, add
      Order2Go2.lib - if you use ForexConnect 1.1.2 version or earlier
      or
      ForexConnect.lib - if you use ForexConnect 1.1.3 version or higher.
  3. Specify the include subfolder, which contains header files, in Configuration Properties → C\C++ → General → Additional Include Directories:
    "C:\Program Files\CandleWorks\ForexConnectAPI\include"
  4. Setup Configuration Properties → General → Character Set = Not Set.
  5. Add the following lines to the file stdafx.h:
  • If you use ForexConnect 1.1.2 version or earlier:
    #include <windows.h>
    #include "Order2Go2.h"
  • If you use ForexConnect 1.1.3 version or higher:
    #include <windows.h>
    #include "ForexConnect.h"

Distribution

You must distribute your program with all binary libraries from "C:\Program Files\Candleworks\ForexConnectAPI\bin". For your application to work, ForexConnect libraries should be located in the folder where your application is installed, or the path to the libraries should be in the value of the system variable Path.

ForexConnect API Features

Event Driven Architecture

All APIs used by ForexConnect are asynchronous, so you will have to implement an event-driven architecture in your code.

An event-driven architecture is a software architecture pattern that manages the behavior of production, detection and consumption of events as well as the responses they evoke. In this context, an event should be treated as some value or message that can be identified within an ongoing stream of monitored inputs, such as specific conditions or signals or something else.

Event-driven architectures usually consist of event producers and event consumers. Event consumers subscribe to some event manager, and event producers publish to this manager. When the manager receives an event from a producer, it forwards this event to all registered consumers or stores the event for later forwarding.

An event handler is a callback routine that operates asynchronously and handles inputs received into a program (events). In this context, an event is some meaningful element of application information from an underlying development framework, usually from a graphical user interface (GUI) toolkit or some kind of input routine. On the GUI side, for example, events include key strokes, mouse activity, action selections, or timer expirations. On the input side, events include opening or closing files and data streams, reading data and so on.

Event handling is the receipt of an event at some event handler from an event producer and subsequent processes.

The processes involved in event handling include:

  • Identifying where an event should be forwarded;
  • Making the forward;
  • Receiving the forwarded event;
  • Taking some kind of appropriate action in response, such as writing to a log, sending an error or recovery routine or sending a message;
  • The event handler may ultimately forward the event to an event consumer.

The benefit of event-driven architectures is that they enable arbitrarily large collections of consumers and producers, along with some number of managers, to exchange ongoing status and response information. They are also usually fairly responsive to events as they occur, and work well in unpredictable and asynchronous communication environments.

ForexConnect Event Handling Peculiarities

You will receive notifications of events via IO2GSessionStatus and IO2GResponseListener interfaces. You have to develop your own classes that implement IO2GSessionStatus and IO2GResponseListener and pass them as callback interfaces in subscription methods of the IO2GSession object for API events notifications.

Note that all methods (event handlers) of your implementation of the IO2GSessionStatus and IO2GResponseListener callback interfaces are called by the ForexConnect library in a separate thread. So you must keep the following in mind:

  1. You must always provide thread-safe access to all data stored in your application that is updated from your event handlers.
  2. You do not need to synchronize event handlers calls between themselves and do not need to think about "re-entering" an event handler because all events from API are already synchronized in one thread. Therefore, event handlers are called in sequence.
  3. You should handle each event as fast as possible because they are synchronized in the "handlers" thread by the ForexConnect library. You can run your own threads to accelerate events handling.

Object Lifetime Management

ForexConnect API provides its own mechanism of lifetime management for API objects. This mechanism is based on counting object references. All objects, which can be created or taken using API function, implement the IAddRef interface. This interface has two methods: addRef() and release(). When you use these objects you must follow certain rules:

  • When you get an API object as a result of an API function call, you get this object with an internal counter of references equal to 1. So you must call the release() method to decrease the value of its usage counter before the variable storing the object reference is out of the visibility scope. You can use the helper template class O2G2Ptr<IAddReff> to wrap and store reference to ForexConnect API object in local variable with static allocated memory inside your function. Then, when the function execution is finished, release() is called from the destructor of the O2G2Ptr<IAddReff> instance.
    Example:
    void myFunc()
    {
        IO2GSession* pSession = CO2GTransport::createSession();
        //...
        pSession->release();
    }
or
    void myFunc()
    {
        O2G2Ptr<IO2GSession> pSession = CO2GTransport::createSession();
        //...
    }
  • When you implement a virtual method (event handler) of your class which inherits the IO2GSessionStatus listener or IO2GResponseListener interface, you must not call release() for objects that are taken as parameters of these methods. Such objects are created and released in the caller of your event handler.
  • When you save an API object reference in a class-level variable for further usage outside of the scope of the function where it was taken, you must call addRef() immediately after the object reference has been assigned to this class-level variable.
    Note: You must call release() for the object assigned to the variable before reassigning a new object to the variable. Of course, you must call release() for the stored object in the class destructor.
    The same rules are applied when an object is stored as an array element or a list item, or in any other structure that cannot call release() for a stored object by itself. In this case the code that manages this structure is responsible for calling release() for API objects stored in the structure.

IAddRef Implementation

When you implement listeners inherited from IO2GSessionStatus or IO2GResponseListener, you must implement the methods of the IAddRef interface too, because IO2GSessionStatus and IO2GResponseListener are inherited from the IAddRef interface.

#include "stdafx.h"
#include <iostream>
class CMyResponseListener : IO2GResponseListener
{
private:    
    LONG dwRef; //reference counter for IAddRef implementation
public: 
    //ctor
    CMyApp()
    {  
        dwRef = 1; //initial counter = 1   
    }    

    //descructor
    ~CMyApp()
    {   
    }

    //IO2GResponseListener
    void onRequestCompleted(const char * requestId, IO2GResponse  *response = 0)
    {
    }
    void onRequestFailed(const char *requestId , const char *error)
    {
    }
    void onTablesUpdates(IO2GResponse *tablesUpdates)
    {      
    }

    //IAddRef implementation
    long addRef() 
    { 
        return InterlockedIncrement(&dwRef); //thread-safe increment of counter
    }
    long release() 
    {
        LONG dwRes = InterlockedDecrement(&dwRef); //thread-safe decrement of counter
        if (dwRes == 0) //when counter is 0 then terminate this instance
            delete this;
        return dwRes;
    }    
};

Sample Application Using ForexConnect API

Overview

This sample is a simple console application which uses ForexConnect API. This application has the following features:

  1. Connecting to a trade server using the predefined user credentials
  2. Retrieving prices for EUR/USD
  3. Retrieving the accounts table for the user
  4. Creating an Open Market Order for EUR/USD when you input 'b' (buy) or 's'(sell)
  5. Retrieving the orders table and receiving notifications of updates in this table
  6. Finishing the application execution when you input 'q' (quit)

To simplify the sample, the whole application logic is implemented in one class CMyApp.

You can download the whole source code of the sample here: File:ForexConnect Sample Win.zip

Connecting with Trade Server

The main object of ForexConnect API is a session object that implements the IO2GSession interface. This object represents a session of user's connection and can be created using a static method of the CO2GTransport class:

    CO2GTransport::createSession();

The IO2GSession object notifies subscribers of all changes of the connection state via callback interface IO2GSessionStatus , and notifies of responses received from the server or of subscribed data via callback interface IO2GResponseListener.

To connect to a trade server using ForexConnect API, do the following:

  1. Implement the IO2GSessionStatus interface to receive notifications of changes of the connection state.
  2. Create a session object.
  3. Subscribe the implemented IO2GSessionStatus listener to notifications from the session object.
  4. Call login() for the session and wait for the login result.
  5. Explore the state of the login by receiving a notification in the method IO2GSessionStatus::onSessionStatusChanged(O2GSessionStatusCode status).
  6. Before shutting your application down, clean up all used API objects: unsubscribe the IO2GSessionStatus listener and call release() for the session object to decrease the reference counter.

See the following source code for implementation details: '"`UNIQ--toggledisplay-0000000C-QINU`"'

Note that we wait for a notification of the login completion because the login() call is asynchronous. For this, we use a special synchronization signal. When IO2GSessionStatus::onSessionStatusChanged() is called, the signal is set to resume the thread execution after WaitingForSingleObject() in the run() method.

Also note that when you have subscribed to notifications, the object reference counter is increased for the IO2GSessionStatus listener. So you must unsubscribe the listeners to decrease their reference counter.

Login with Choosing Trading Session

When a user account has several trading sessions, then login is a multiple step process:

1. Call the login() method of the session object with a username, password, server URL, and database name specified.
2. Process the received status TradingSessionRequested in the method IO2GSessionStatus::onSessionStatusChanged(O2GSessionStatusCode status). There are following common steps to process the TradingSessionRequested status:
2.1. Retrieve the trading session list from the IO2GSession object using the getTradingSessionDescriptors() method.
2.2. Provide a choice of the trading session for the user.
2.3. Request a secret PIN from the user.
2.4. Set the specified trading session ID and PIN using IO2GSession::setTradingSession().

            '"`UNIQ--toggledisplay-0000000D-QINU`"'

3. If setTradingSession is successful, then the Connected status is received in the onSessionStatusChanged() event handler.

Managing Prices

Managing of prices includes the following steps:

  1. Check if there is price data automatically requested during login.
    • If yes, go to step 2.
    • If no, send a request for the current prices for all instruments.
  2. Handle the response.
  3. Handle price update for a certain instrument.

Note that to receive any notifications of request responses or server objects state changes, you must implement the IO2GResponseListener interface and subscribe this listener to events notifications.

So, modify the CMyApp class to implement the IO2GResponseListener interface by itself and subscribe to notifications of response receiving in the run() method: '"`UNIQ--toggledisplay-0000000E-QINU`"'


Request Current Prices

Depending on the settings of the trade server, the current prices of all instruments either can be automatically received during the login process, or you can explicitly request this data from the trade server. So, to get the current prices you should do the following actions:

1. Check if there are price offers received on login using IO2GLoginRules::isTableLoadedByDefault().
2. If they are loaded, get the already received offers response object using IO2GLoginRules::getTableRefeshResponse(Offers).
This response object can be processed to extract offers data. To create offers reader for the response, use the IO2GSession::getResponseReaderFactory() and the IO2GResponseReaderFactory::createOffersTableReader method.
3. If offers were not received on login, send the offers table request explicitly using IO2GSession::sendRequest(). To create the appropriate request, use IO2GSession::getRequestFactory() and the IO2GRequestFactory::createRefreshTableRequest(Offers) method.

To request the current prices, add the following source code to the run() method of our sample after login processing: '"`UNIQ--toggledisplay-0000000F-QINU`"'

In the sample, we wait for receiving the request response using a synchronization signal. So we "catch" the moment to start monitoring of price changing for EUR/USD from. Of cause, we should set this signal when price data is received.

In our sample a "trick" is used to avoid code duplication. As you will see further, the response processing is the same for the case when offers data is received from the IO2GLoginRules object and for the case when we explicitly retrieve offers data. So to process the response object received from IO2GLoginRules you can directly call the event handler ResponseListener::onRequestCompleted(null, response).

Receiving Price Data

As a call of sendRequest() is asynchronous, to receive a response with price data you need to implement the ResponseListener::onRequestCompleted(null, response) event handler. As this event handler is used to receive notifications of responses of all requests, you should do the following:

1. Check if the response type is GetOffers.
2. Create the IO2GOffersTableResponseReader response reader using IO2GResponseReaderFactory to get price data from the response object.
3. Process all rows in the Offers table using the reader.

Do not forget that you must manage references counters of all obtained objects.
Also you must provide thread-safe access to the stored offers.

See the source code below for a sample of how to handle receiving of the response. To store the current prices for EUR/USD, the appropriate variables are defined and thread-safe access to them is implemented.

'"`UNIQ--toggledisplay-00000010-QINU`"' A synchronization signal is set when receiving of offers data is completed. This "trick" allows waiting for receiving the current prices in the main thread after sending the request.

Our sample application handles the onRequestComplete notification and extracts the EUR/USD bid and ask price. The received ask and bid prices are stored in the mEURUSDBid and mEURUSDAsk class-level variables, thread-safe methods to read and change these variables are implemented.

Receiving Offers Updates

Processing of the Offers table update includes the following steps:

1. Get a response reader factory from the session object.
2. Create a special reader for table updates using the factory.
3. Make a loop to enumerate each element of the updates list because the received data can contain updates for any type of objects, not just for the Offers table. So you need to check each item in the updates list for the required table type and update the operation type.
4. To process the change, use the appropriate method of the reader to retrieve an object of a particular type:
        O2G2Ptr<IO2GOfferRow> offer = pReader->getOfferRow(i);

Note that the notification is received in a separate thread, therefore you must use thread-safe reading and updating of the variables that store the received data.

Add the following code to CMyClass to handle the EUR/USD price update: '"`UNIQ--toggledisplay-00000013-QINU`"' In our sample, we process EUR/USD price updates only and store their last values in the mEURUSDBid and mEURUSDAsk variables. We have implemented thread-safe methods to access these variables.

Order Creation

To create an order, do the following:

1. Make sure that you have at the least a user account ID and an offer ID that are required to create an order. If not, request them at first.
2. Use an IO2GRequestFactory object to create IO2GValueMap to specify the order parameters.
3. Fill the valuemap with the necessary parameters to create a particular type of order
Please see ForexConnectAPI SDK for details about parameters of commands for creating orders.
4. Create an IO2GRequestFactory::IO2GRequest object for the filled valuemap.
5. Start execution of the request.
6. Receive the request response to be sure that the request execution is successful.


As we need an Account ID for our sample, let's retrieve the Accounts table at the first. Retrieving data of the Accounts table is similar to retrieving data of the Offers table. To simplify the sample, get the first account from the user account list and store it in a class-level variable for further usage. '"`UNIQ--toggledisplay-00000014-QINU`"'

We wait for the response to avoid order creation before the account Id is retrieved.

The best practice is encapsulation of the order creation logic into a separate method of the CMyApp class. Also you should handle the response to the order creation request in the onRequestCompleted event handler to make sure that the order is created.

See the following source code that creates a buy or sell order for the EUR/USD instrument with 100K amount: '"`UNIQ--toggledisplay-00000015-QINU`"'

Note that there are some useful "constants" for filling the valueMap object with order parameters:

  • O2G2::Commands
  • O2G2::Order

and etc.

You can keep the request ID to process the response of a particular request only:

    request->GetRequestId();

Retrieving Orders Table

All information about existing orders states can be retrieved from the Orders table.

ForexConnect API allows retrieving the Orders table only for a specified account. So you need to do the following:

  1. Retrieve the Accounts table.
  2. Wait until Accounts data is received.
  3. Retrieve the Orders table for each received account using IO2GRequestFactory::createRefreshTableRequestByAccount().
  4. Handle the request response in onRequestComplete().
  5. Handle the Orders table update in onTablesUpdates().

To simplify our sample, we take the stored account ID and use it in the example of the Orders table retrieving. '"`UNIQ--toggledisplay-00000018-QINU`"'

Note that if you store orders data, you must provide thread-safe access to this data and correct management of reference counters for the stored objects. In our sample orders data is not stored, so synchronization is not implemented.

Note that the date and time as a part of the received data is in the "VARIANTTIME" format. So use the VariantTimeToSystemTime Windows function to convert this data into the SystemTime structure.

Finalizing Application. Logout

As you can see, the CMyApp::stop() method frees all used system resources and unsubscribes the IO2GSesssionStatus and IO2GResponseListener listeners to stop receiving notifications and to decrease reference counters of the listeners. You should call logout() before termination of your application. All API objects that are stored on the class-level and all our class instances which implement the IAddRef interface must be freed using a call of the release() method. Note that CMyApp implements IAddRef, therefore release() must be called for it before _tmain() returns control. '"`UNIQ--toggledisplay-00000019-QINU`"'

Handling Request Errors

When an error occurs during asynchronous execution of a request, the handler IO2GResponseListener::onRequestFailed is invoked.

In our sample, we handle the error by putting the error description in the console output and by stopping the application:

    void onRequestFailed(const char *requestId , const char *error)
    {
        std::cout << error;
        stop();
    }

Launching Sample

By now our sample can login, retrieve EUR/USD price changes, show information about existing orders, and has a method to create an Open Market Order.

To launch the sample on this stage, we need to implement usage of our CMyApp class. For this, do the following:

  1. Inside the main function of our console application, create an instance of our CMyApp class.
  2. Call the run() method of the instance to login to the trade server and start receiving updates of EUR/USD.
  3. Provide reading of user's input to execute commands:
    • when ‘b’ is input, create a buy market order;
    • when ‘s’ is input, create a sell market order;
    • when ‘q’ is input, quit the application.
int _tmain(int argc, _TCHAR* argv[])
{
    CMyApp* app = new CMyApp();
    if(app->run()) //this call return only after login and completng other preparatory actions
    {
        char c='\0';
        while(true)
        {
            std::cin >> c; 
            if(app->getStatus() == IO2GSessionStatus::Disconnected) //in case of error after async call of createOrder
                break;
                
            if(c == 'q')
            {
                app->stop();
                break;
            }
            else if(c == 'b')
                app->createOrder(true); 
            else if(c == 's')
                app->createOrder(false);
        }        
    }
    app->release();
    return 0;
}

The run() method of the sample prepares the application for trading. After the preparation is done, it returns true if all is good.
As you can see, this method calls all API functions asynchronously but waits for their responses using special synchronization objects. Note that this is not an effective method of using ForexConnectAPI, but it is easy for understanding.

Do not forget to specify a valid username and password for a call of the login() method.

Now you can build and run the sample. If you have any problem with building the sample, please compare it with the whole source code of the sample: File:ForexConnect Sample Win.zip.

What's Next?

For detailed information about all API classes and their methods, please see ForexConnectAPI SDK.


This Article in Other Languages

Language: English  • español • français • русский • 中文 • 中文(繁體)‎