How to Start Using ForexConnect .NET API (Win32/Win64)

From FxCodeBaseWiki

Jump to: navigation, search

This article describes how to start using ForexConnect .NET 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 .NET Framework 2.0 and later on MS Windows 32-bit/64-bit version
Language: C# .NET
IDE: Microsoft Visual Studio 2005, 2008, 2010

ForexConnect .NET library usage with the Microsoft Silverlight application framework is not supported.
ForexConnect .NET library running using Mono is not supported.

Contents

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.
  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 C# project:

1. Configure the post-build event of your project to copy ForexConnect API libraries and supporting files to the folder where your program is built:
In the project Properties → Build Events → Post-Build event command line, add the following text:
copy "C:\Program Files\Candleworks\ForexConnectAPI\bin\*.*" "$(TargetDir)"
2. Add a reference to the fxcore2.dll .NET assembly to your project.
  • If you use .NET 4.0 then use the appropriate .NET fxcore2.dll assembly that can be found in the "C:\Program Files\Candleworks\ForexConnectAPI\bin\net\dotnet40\" folder.
  • If you use .NET 2.0 then use fxcore2.dll assembly from "C:\Program Files\Candleworks\ForexConnectAPI\bin\net\dotnet20\" folder.
3. Add the namespace fxcore2 to your code:
        using fxcore2;

Distribution

You must distribute your program with all binary libraries and supporting files from "C:\Program Files\Candleworks\ForexConnectAPI\bin". ForexConnect libraries and supporting files must be located in the folder where your application is installed.

Note that fxcore2.dll assembly must be placed in the the folder where your application is installed too.

ForexConnect .NET 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 .NET Event Handling Peculiarities

The ForexConnect .NET API already has implementations of IO2GSessionStatus and IO2GResponseListener interfaces to receive notifications of session status changes and data receiving. So you can use appropriate .NET events of the O2GSession object instead of implementing those interfaces by yourself. However you can implement them and use them to receive notifications from the session object after subscription if this is more suitable for you.

Note that all event handlers that you have implemented for O2GSession events are called in a separate thread. So you must keep in mind the following:

  1. You must always provide thread-safe access to all data stored in your application which is updated from your event handlers.
  2. You do not need to synchronize event handlers calls and do not need to think about the event handler 're-entering' because all events from API are already synchronized in one thread. Therefore events 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 the events handling.

Object Lifetime Management

You do not need any additional resource management for objects obtained using ForexConnect .NET API. However it is recommended to call the Dispose() method for all obtained objects which implement it. These calls allow you to free unused system resources more effectively. You can use the using statement for this purpose:

    using (O2GValueMap valuemap = factory.createValueMap())
    {
         //...
    }

Sample Application Using ForexConnect .NET API

Overview

This sample is a simple console application which uses ForexConnect .NET 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 MyApp.

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

Connecting with Trade Server

The main object of ForexConnect .NET API is a session object O2GSession in the fxcore2 namespace. This object represents a session of user's connection and can be created using a static method of the O2GTransport class:

    O2GTransport.createSession();

The O2GSession object notifies subscribers of all changes of the connection state through the following events:

or through the callback interface IO2GSessionStatus.

The data receiving notifications can be handled using the following O2GSession events:

or through the callback interface IO2GResponseListener.

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

  1. Create a session object.
  2. Implement handlers for session object events to receive notifications of session status changes.
  3. Call login() for the session and wait until the login proccess is completed.
  4. Process the received notifications of connection status changes in onSessionStatusChanged to manage the state of the login process.

See the following source code for details of implementation:

[Hide Source]
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using fxcore2;
 
namespace Sample
{
    class MyApp 
    {
        EventWaitHandle mSyncSessionEvent = null; //for synchronization with thread of calls of IO2GSessionStatus notification methods  
        O2GSession mSession; //store the session object reference        
        object csSessionStatus = new object(); //sync object for critical section to synchronize access to mSessionStatus    
 
        //constructor
        public MyApp()
        {
            mSyncSessionEvent = new EventWaitHandle(false, EventResetMode.AutoReset);                        
        }
        //destructor
        ~MyApp()
        {
            mSyncSessionEvent.Close();
        }
 
        public O2GSessionStatusCode Status
        {
            get
            {
                O2GSessionStatusCode status;
                lock (csSessionStatus)
                {
                    status = mSessionStatus;
                }
                return status;
            }
            private set 
            {
                lock (csSessionStatus)
                {
                    mSessionStatus = value;
                }
            }
        }
        O2GSessionStatusCode mSessionStatus; //store current connection status
 
        //Session status events handlers
        void mSession_SessionStatusChanged(object sender, SessionStatusEventArgs e)
        {
            lock (csSessionStatus)
            {
                mSessionStatus = e.SessionStatus;
            }
 
            switch (e.SessionStatus)
            {
                case O2GSessionStatusCode.Connected:
                case O2GSessionStatusCode.Disconnected:
                    mSyncSessionEvent.Set();
                    break;
            }
        }
 
        void mSession_LoginFailed(object sender, LoginFailedEventArgs e)
        {
            lock (csSessionStatus)
            {
                mSessionStatus = O2GSessionStatusCode.Disconnected;
            }
 
            mSyncSessionEvent.Set();
        }
 
        //create session, connect and make other preparations
        public bool run()
        {
            try
            {
                mSession = O2GTransport.createSession(); //create IO2GSession object	        
                //subscribe to session status events
                mSession.LoginFailed += new EventHandler<LoginFailedEventArgs>(mSession_LoginFailed);
                mSession.SessionStatusChanged += new EventHandler<SessionStatusEventArgs>(mSession_SessionStatusChanged);
 
                //Please specify valid username and password
                mSession.login("username", "password", "http://www.fxcorporate.com/Hosts.jsp", "Demo");
 
                //Waiting for result of async login           
                if (mSyncSessionEvent.WaitOne(5000) &&
                   this.Status != O2GSessionStatusCode.Connected)
                {
                    return false;
                }
 
 
                return true;
            }
            catch (Exception)
            {
                this.stop();
                throw;
            }
        }
 
 
        //log out and stop
        public void stop()
        {
            if (mSession != null)
            {
                mSession.logout();
                mSyncSessionEvent.WaitOne(5000); //wait for logout completion during 5 seconds
 
                mSession.LoginFailed -= new EventHandler<LoginFailedEventArgs>(mSession_LoginFailed);
                mSession.SessionStatusChanged -= new EventHandler<SessionStatusEventArgs>(mSession_SessionStatusChanged);
 
                mSession.Dispose();
                mSession = null;//to avoid second time stop
                mSessionStatus = O2GSessionStatusCode.Disconnected; //for getStatus()
            }
        }
    }     
}

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

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 onSessionStatusChanged event handler. There are following common steps to process the TradingSessionRequested status:
2.1. Retrieve the trading session list from the O2GSession 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 setTradingSession().
            [Show Source]
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 the price update for a certain instrument.

To receive notifications of request responses or server objects state changes, you must implement and subscribe event handlers to the appropriate events of the O2G2Session instance.

For this, modify the MyApp class to handle these events:

[Hide Source]
    class MyApp 
    { 
        EventWaitHandle mSyncResponseEvent = null; //for synchronization of main thread and handler thread             //...
 
 
        //constructor
        public MyApp()
        {
            //..
            mSyncResponseEvent = new EventWaitHandle(false, EventResetMode.AutoReset);
        }
        //destructor
        ~MyApp()
        {
            //..
            mSyncResponseEvent.Close();
        }
        void stop()
        {
             if(mSession != NULL) 
             {
                 //...
                 mSession.RequestCompleted -= new EventHandler<RequestCompletedEventArgs>(mSession_RequestCompleted);
                 mSession.RequestFailed -= new EventHandler<RequestFailedEventArgs>(mSession_RequestFailed);
                 mSession.TablesUpdates -= new EventHandler<TablesUpdatesEventArgs>(mSession_TablesUpdates);
                 //...
             }   
        } 
 
        //...
        void mSession_TablesUpdates(object sender, TablesUpdatesEventArgs e)
        {      
        }
 
        void mSession_RequestFailed(object sender, RequestFailedEventArgs e)
        {
        }
 
        void mSession_RequestCompleted(object sender, RequestCompletedEventArgs e)
        {            
        }
 
        void run()
        {
             //... after Login is successful
             mSession.RequestCompleted += new EventHandler<RequestCompletedEventArgs>(mSession_RequestCompleted);
             mSession.RequestFailed += new EventHandler<RequestFailedEventArgs>(mSession_RequestFailed);
             mSession.TablesUpdates += new EventHandler<TablesUpdatesEventArgs>(mSession_TablesUpdates);
             //...
        }
    }            
</syntaxhighligth>


Request Current Prices

Depending on 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 the isTableLoadedByDefault() method of the O2GLoginRules instance.
2. If they are loaded, get the already received offers response object using the getTableRefeshResponse(Offers) method of the O2GLoginRules instance.
This response object can be processed to extract offers data. To get offers reader for reading the response data, do the following:
  1. Get the response reader factory using the getResponseReaderFactory() method of the session object instance.
  2. Create a reader using the createOffersTableReader() method of the O2GResponseReaderFactory instance.
3. If offers were not received on login, send a request for the offers table explicitly using the sendRequest() method of the session object.
To create the appropriate request, do the following:
  1. Get the request factory using the getRequestFactory() method of the session object.
  2. Create request using the createRefreshTableRequest() method of O2GRequestFactory instance.

To request the current prices, add the following source code to the run() method of our sample after login processing:

[Hide Source]
 
            //.. 
            O2GLoginRules loginRules = mSession.getLoginRules();
            bool bLoaded = false;
            using (O2GRequestFactory factory = mSession.getRequestFactory()) //create request factory            
            {
                bLoaded = loginRules.isTableLoadedByDefault(O2GTableType.Offers);
                if (bLoaded)
                {
                    using (O2GResponse response = loginRules.getTableRefeshResponse(O2GTableType.Offers))
                    {
                        mSession_RequestCompleted(this, new RequestCompletedEventArgs(null, response)); //process already received offers			          
                    }
                }
                else //explicitly request Offers
                {
                    using (O2GRequest refreshOffers = factory.createRefreshTableRequest(O2GTableType.Offers)) //create response object 
                    {
                        mSession.sendRequest(refreshOffers);
                        if (!mSyncResponseEvent.WaitOne(5000)) //waiting for response
                            return false;
                    }
                }
            //..

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 course, 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, response processing is the same when offers data is received from the O2GLoginRules object and when we explicitly retrieve offers data. So to process the response object received from O2GLoginRules, you can directly call the implemented event handler of the RequestCompleted event of the session object.

Receiving Price Data

As a call of sendRequest() is asynchronous, to receive a response with price data, you need to implement the event handler for RequestCompleted event of session object. 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. Get the O2GOffersTableResponseReader response reader using O2GResponseReaderFactory to get price data from the response object.
  3. Process all rows in the Offers table using the reader.

You must provide thread-safe access to the offers stored in your application.

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

[Hide Source]
 
        //..
        public double EURUSDAsk
        {
            get
            {
                double v;
                lock (csOffers)
                {
                    v = mEURUSDAsk;
                }
                return v;
            }
            private set
            {
                lock (csOffers)
                {
                    mEURUSDAsk = value;
                }
            }
        }
        private double mEURUSDAsk;
 
        public double EURUSDBid
        {
            get
            {
                double v;
                lock (csOffers)
                {
                    v = mEURUSDBid;
                }
                return v;
            }
            private set
            {
                lock (csOffers)
                {
                    mEURUSDBid = value;
                }
            }
        }
        private double mEURUSDBid;    
 
 
        //constructor
        public CMyApp()
        {
            //..
            InitializeCriticalSection(&csOffers);
            mEURUSDBid = 0;
            mEURUSDAsk = 0;
        }                 
        //..
 
 
        void mSession_RequestCompleted(object sender, RequestCompletedEventArgs e)
        {
            using (O2GResponseReaderFactory pFactory = mSession.getResponseReaderFactory())
            {
                if (e.Response.Type == O2GResponseType.GetOffers)
                {
                    using (O2GOffersTableResponseReader pOffersReader = pFactory.createOffersTableReader(e.Response))
                    {
                        for (int i = 0; i < pOffersReader.Count; i++)
                        {
                            using (O2GOfferRow offer = pOffersReader.getRow(i))
                            {
                                if (offer.Instrument.Equals("EUR/USD"))
                                {
                                    EURUSDAsk = offer.Ask;
                                    EURUSDBid = offer.Bid;
                                    Console.WriteLine("EUR/USD: Bid={0} Ask={1}", offer.Bid, offer.Ask);
                                }
                            }
                        }
                    }
                    mSyncResponseEvent.Set(); //signal for code that is waiting for response
                }
            }
        }

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 RequestComplete event 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

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 MyApp to handle the EUR/USD price update:

Processing of the Offers table update includes the following steps:

1. Get O2GResponseReaderFactory from the session object.
2. Get a O2GTablesUpdatesReader reader using the factory via createTablesUpdatesReader method.
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 operation type.
4. To process the change, use the getOfferRow() method of the reader to retrieve an object of the O2GOfferRow type:
        O2GOfferRow 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 MyClass to handle the EUR/USD price updating:

[Hide Source]
 
        void mSession_TablesUpdates(object sender, TablesUpdatesEventArgs e)
        {
            using (O2GResponseReaderFactory pFactory = mSession.getResponseReaderFactory())
            {
                using (O2GTablesUpdatesReader pReader = pFactory.createTablesUpdatesReader(e.Response))
                {
                    for (int i = 0; i < pReader.Count; i++)
                    {
                        if (pReader.getUpdateTable(i) == O2GTableType.Offers)
                        {
                            if (pReader.getUpdateType(i) == O2GTableUpdateType.Update)
                            {
                                using (O2GOfferRow offer = pReader.getOfferRow(i))
                                {
                                    if (offer.Instrument.Equals("EUR/USD"))
                                    {
                                        if (EURUSDBid != 0 && EURUSDAsk != 0)  //Process only after mEURUSDBid and mEURUSDAsk are initialized with values in onRequestComplete
                                        {
                                            EURUSDAsk = offer.Ask;
                                            EURUSDBid = offer.Bid;
                                            Console.WriteLine("EUR/USD: Bid={0} Ask={1}", offer.Bid, offer.Ask);
                                        }
                                    }
                                }
                            }
                        }
                    }
                }  
            }
        }

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 O2GRequestFactory instance to create O2GValueMap 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 O2GRequest object using O2GRequestFactory 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 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.

[Show Source]

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 MyApp 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:

[Hide Source]
        public void createOrder(bool bIsBuy)
        {
            using (O2GRequestFactory factory = mSession.getRequestFactory())
            {
                using (O2GValueMap valuemap = factory.createValueMap())
                {
                    valuemap.setString(O2GRequestParamsEnum.Command, Constants.Commands.CreateOrder);
                    valuemap.setString(O2GRequestParamsEnum.OrderType, Constants.Orders.TrueMarketOpen);
                    valuemap.setString(O2GRequestParamsEnum.AccountID, mAccount);            // The identifier of the account the order should be placed for.
                    valuemap.setString(O2GRequestParamsEnum.OfferID, "1");                // The identifier of the instrument the order should be placed for.
                    valuemap.setString(O2GRequestParamsEnum.BuySell, bIsBuy ? Constants.Buy : Constants.Sell);
                    valuemap.setInt(O2GRequestParamsEnum.Amount, 100000);                    // The quantity of the instrument to be bought or sold.
                    valuemap.setString(O2GRequestParamsEnum.CustomID, "TrueMarketOrder");    // The custom identifier of the order.
 
                    using (O2GRequest request = factory.createOrderRequest(valuemap))
                    {
                        if (request == null)
                            Console.WriteLine(factory.getLastError());
                        else
                            mSession.sendRequest(request);
                    }
                }
            }
        }

Note that there are some useful helper classes in the fxcore2.Constants namespace for filling the valueMap object with order parameters:

  • Constants.Commands
  • Constants.Order

and etc.

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

    request.RequestId;

Retrieving Orders Table

All information about states of existing orders 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 createRefreshTableRequestByAccount() of the O2GRequestFactory instance.
  4. Handle the request response in an event handler of the RequestComplete event.
  5. Handle the Orders table update in an event handler of the TablesUpdates event.

To simplify our sample, we take the stored account ID and use it in the example of the Orders table retrieving.

[Hide Source]
        public bool run()
        {
                    //..                    
                    bLoaded = loginRules.isTableLoadedByDefault(O2GTableType.Orders);
                    if (bLoaded)
                    {
                        using (O2GResponse response = loginRules.getTableRefeshResponse(O2GTableType.Orders))
                        {
                            mSession_RequestCompleted(this, new RequestCompletedEventArgs(null, response));  //process orders			          
                        }
                    }
                    else //explicit request the Accounts
                    {
                        using (O2GRequest refreshOrders = factory.createRefreshTableRequestByAccount(O2GTableType.Orders, mAccount)) //create  request object 
                        {
                            mSession.sendRequest(refreshOrders);
                            if (!mSyncResponseEvent.WaitOne(5000)) //waiting for response
                                return false;
                        }
                    }
                    //..
        }
 
        //...
        void mSession_RequestCompleted(object sender, RequestCompletedEventArgs e)
        {
            using (O2GResponseReaderFactory pFactory = mSession.getResponseReaderFactory())
            {
                //..
                if (e.Response.Type == O2GResponseType.GetOrders)
                {
                    using (O2GOrdersTableResponseReader pOrdersReader = pFactory.createOrdersTableReader(e.Response))
                    {
                        for (int i = 0; i < pOrdersReader.Count; i++)
                        {
                            using (O2GOrderRow order = pOrdersReader.getRow(i))
                            {
                                DateTime st = order.StatusTime;
                                Console.WriteLine("Order: ID={0} Rate={1} BuySell={2} Status={3} StatusTime={4}",
                                                                    order.OrderID,
                                                                    order.Rate,
                                                                    order.BuySell,
                                                                    order.Status,
                                                                    st.ToString("yyyy-MM-dd hh:mm"));
                            }
                        }
                    }
                    mSyncResponseEvent.Set(); //signal for code that is waiting for response
                }
                //..
            }
        } 
 
        //...
 
        void mSession_TablesUpdates(object sender, TablesUpdatesEventArgs e)
        {
            using (O2GResponseReaderFactory pFactory = mSession.getResponseReaderFactory())
            {
                using (O2GTablesUpdatesReader pReader = pFactory.createTablesUpdatesReader(e.Response))
                {
                    for (int i = 0; i < pReader.Count; i++)
                    {
                        //..
                        if (pReader.getUpdateTable(i) == O2GTableType.Orders)
                        {
                            using (O2GOrderRow order = pReader.getOrderRow(i))
                            {
                                DateTime st = order.StatusTime;
                                Console.WriteLine("Order updated: ID={0} Rate={1} BuySell={2} Status={3} StatusTime={4}",
                                                                    order.OrderID,
                                                                    order.Rate,
                                                                    order.BuySell,
                                                                    order.Status,
                                                                    st.ToString("yyyy-MM-dd hh:mm")
                                                                    );
                            }
                        }
                        //..
                    }
                }
            }
        }

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.

Finalizing Application. Logout

As you can see, the stop() method of our MyApp class frees all used system resources and unsubscribes all event handlers from session events to stop receiving notifications. You should call logout() before termination of your application. Also you should call the Dispose() method of the session instance before _tmain() returns control.

[Show Source]

Handling Request Errors

When an error occurs during asynchronous execution of a request, the event handler of the RequestFailed event is invoked. In our sample, we handle the error by putting the error description in the console output and by stopping the application:

        void mSession_RequestFailed(object sender, RequestFailedEventArgs e)
        {
            Console.WriteLine(e.Error);
            stop();
        }

Launching Sample

By now our sample can log in, 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 MyApp class. For this, do the following:

  1. Inside the main function of our console application, create an instance of our MyApp class.
  2. Call the run() method of the instance to log in 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.
    class Program
    {
        static void Main(string[] args)
        {
            MyApp app = new MyApp();
            if (app.run()) //waiting for login completion and making other preparations
            {
                char cKey;
                while (true)
                {
                    cKey = (char)Console.Read();
                    if (app.Status == O2GSessionStatusCode.Disconnected) //in case of error after async, call createOrder
                        break;
 
                    if (cKey == 'q')
                    {
                        app.stop();
                        break;
                    }
                    else if (cKey == 'b')
                        app.createOrder(true);
                    else if (cKey == 's')
                        app.createOrder(false);
                }
            }
        }
    }

The run() method of the sample prepares the application for trading. After the preparation is made, 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 Net.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 • Русский • 中文 • ‪中文(繁體)‬
Personal tools