How to Use Async Methods in VBA

From FxCodeBaseWiki
Jump to: navigation, search

Introduction

When you call some Order2Go trading method to create an order, you actually send a request to create an order to the server. If you use a regular (synchronous) method, then after sending a request you can't continue any operations until the order is processed and the server returns the appropriate data.

For example, when you call the OpenTrade3 method, the method sends order parameters to the server and waits for a response from the server, it will return an error or an appropriate OrderID.

In case of a slow Internet connection or a large number of orders in Order2Go tables, the application will "hang" keeping control of the thread until a response from the server is received. Unless the server responds quickly, this creates a problem in a single threaded environment in which Order2Go must be used (Order2Go API should not be used in a multithreaded environment).

Asynchronous methods can be an effective tool to solve this problem. These methods allow applications to place orders and continue any operations without having to wait for a server response and blocking the thread.

This article discusses how to use asynchronous methods in VBA (Visual Basic for Application) projects.

To learn how to start creating your Order2Go application in VBA, please read Start using Order2Go in VBA.

How to Use Asynchronous Methods

When you use asynchronous trading methods, you will not get back an OrderID, but only a RequestID. Once your order is processed and the server returns the result of execution (an error or an appropriate OrderID), you will get a notification (event) from the server.

So, for proper usage of asynchronous methods, you should correctly process events in your application. Order2Go provides two models of event processing: ActiveX event handling and pending events. Please read about differences in the models in the on-line Order2Go documentation Events Processing.

It is recommended to use pending events in VBA (rather than ActiveX event handling) to improve performance of your application.

Enable Pending Events Mode

After creating core and trading desk objects but before login, you should enable pending events. You can choose which particular kinds of events you want to receive.

The example below demonstrates how to enable server-only events and make Order2Go not send events for calculated data such as trade P/L and updates of summary rows:

    Set core = CreateObject("Order2Go.Core")
    Set desk = core.CreateTradeDesk("trader")
    'enable server-only events to reduce the load
    'this method makes Order2Go not send events for calculated data such as trade P/L and
    'updates of summary rows
    Call desk.SetTableEventsFilter("orders", desk.EVENTSFILTER_SERVER)
    Call desk.SetTableEventsFilter("trades", desk.EVENTSFILTER_SERVER)
    Call desk.SetTableEventsFilter("closed trades", desk.EVENTSFILTER_SERVER)
    
    
    'enable pending table events
    'now events are collected in an internal queue until they are requested
    desk.EnablePendingEvents (desk.EventAdd + desk.EventChange + desk.EventRemove + desk.EventSessionStatusChange + desk.EventAsyncRequestResult)

Call Asynchronous Method

All trading methods and Order2Go operations are available in two forms: synchronous and asynchronous.

The example below sends a request to create a buy open order to the server using the asynchronous method OpenTradeAsync and saves the received RequestID:

        'get all information to send a proper market order
        Set table = desk.FindMainTable("accounts")
        Set account = table.Rows.Item(1)
        Set deskProps = desk.TradingSettingsProvider
        
        'send an asynchronous request to open a minimal buy position
        'for EUR/USD for the first of the exiting accounts
        'at the best market price
        'with no stops or limits attached
        Call desk.OpenTradeAsync(account.CellValue("AccountID"), "EUR/USD", True, deskProps.GetBaseUnitSize("EUR/USD", account.CellValue("AccountID")), 0, "", 0, 0, 0, 0, 0, varRequestId)
        requestId = CStr(varRequestId)
        log ("request " + requestId + " sent")

Pump Messages and Process Events

When the pending events mode is enabled, Order2Go collects events instead of sending them to all subscribed ActiveX controls. To get a collection of all accumulated events, you can call TradeDeskAut.GetPendingEvents. The events returned by this method will be purged from the queue. Then you can enumerate this collection and get information about each particular event. For example, you can check whether an event is a result of creating a position by an open order.

So when you use asynchronous methods and would like to know when some request is completed, you continually monitor and process events. In case of environments like VBA, continual processing of events implemented as a loop can cause hanging of your application from the user's point of view. You should call DoEvents from time to time to pass control to the operating system to process messages. Generally DoEvents is required when you run a long loop and want to consume messages (usually related to the UI). All you need is to put a DoEvents call (or call the wait method for a short period such as 0.25 second) all the time before another loop is started. This will "pump" the messages.

The example below waits until the result of request execution appears either in the closed or open trades table. It implements event processing with usage of DoEvents:

        
        'wait until the request execution result appears
        'either in the closed or open trades
        stop2 = False
        DoEvents
        Do While Not stop2
            
            If gLog.Cells(1, 3).Value = "y" Then
                stop2 = True
            End If
            
            Set deskEvents = desk.GetPendingEvents
            
            For Each deskEvent In deskEvents
                DoEvents
                
                ' table change event
                If deskEvent.Kind = deskEvent.KIND_ADD Or _
                    deskEvent.Kind = deskEvent.KIND_CHANGE Or _
                    deskEvent.Kind = deskEvent.KIND_REMOVE Then
                    
                    'closed trades table has been changed
                    If deskEvent.TableType = "closed trades" Then
                        Set parser = desk.GetParser
                        Call parser.ParseEventRow(deskEvent.ExtInfo, deskEvent.TableType)
                        'if our order was used to open a trade
                        'and the trade has been closed (e.g. using TS or by a stop order)
                        If CStr(parser.GetValue("OpenOrderReqID")) = requestId Then
                           log ("a trade was created and then was closed externally")
                           stop2 = True
                        End If
                        
                        'if our order was used to close a trade
                        If CStr(parser.GetValue("CloseOrderReqID")) = requestId Then
                           log ("the request closed an opposite trade")
                           stop2 = True
                        End If
                    End If
                    
                    'trades table event
                    If deskEvent.TableType = "trades" Then
                        Set parser = desk.GetParser
                        Call parser.ParseEventRow(deskEvent.ExtInfo, deskEvent.TableType)
                        
                        If CStr(parser.GetValue("OpenOrderReqID")) = requestId Then
                            log ("the request opened a new trade, wait until the trade is closed")
                        End If
                    End If
                End If
                
                'our request failed
                If deskEvent.Kind = deskEvent.KIND_REQUESTFAILED Then
                    If deskEvent.requestId = requestId Then
                        log ("request " + requestId + " failed")
                        stop2 = True
                    End If
                End If
                
                'connection lost?
                If deskEvent.Kind = deskEvent.KIND_SESSIONSTAUSCHANGE Then
                    If deskEvent.ExtInfo = "Disconnected" Then
                        log ("connection lost")
                        stop2 = True
                    End If
                End If
            Next
        Loop

Disable Pending Events Mode

Order2Go does not know whether you are interested in events and will keep them until you either clear or get pending events. In case you do not do this for a long time, these accumulated events can cause memory overflow. So if you don't need to get events anymore, you should disable pending events as shown in the example below:

    'disable pending events
    desk.EnablePendingEvents (0)

Sample Application

You can download a simple application which logs in asynchronously, creates one long trade for EUR/USD and then waits until the trade is closed externally (for example, from TS or by S or L orders): File:AsyncDemoVBA.zip

This Article in Other Languages

Language: English  • español