Sei sulla pagina 1di 9

The LMAX .

NET API Tutorial

Page 1 of 9

The LMAX .NET API


Contents
1. Key Concepts 2. Notes On Asynchronous Systems 3. Lets Get Started! - Logging In 4. Subscribing to Market Data 5. Placing Orders 6. Cancelling Orders 7. Handling Rejects 8. Handling Errors on Requests 9. Handling Errors on the Event Stream 10. Handling Session Disconnection 11. Using and Amending Stops 12. Subscribing to Other Events 13. Requesting Account State 14. Retrieve Security Definitions 15. Retrieve Historic Market Data 16. Protocol Version Checking 17. Order and Execution Tracking

1. Key Concepts
The LMAX .NET API is a thin .NET layer over our existing XML over HTTPS (REST) protocol. The goal of the API is to provide a simplified programming model for .NET-based clients connecting to the LMAX Trader platform. The design of the API contains 5 key concepts: Requests, Callbacks, Events, EventListeners, and the Session. Requests and Callbacks are used when making requests to LMAX Trader, e.g. placing orders or subscribing to order book events. EventListeners are used to propagate Events that are received asynchronously from LMAX Trader, e.g. market data or execution reports. The final concept is the Session which is the core interface used to send requests to LMAX Trader or register EventListeners.

2. Notes On Asynchronous Systems


The protocol used to communicate with LMAX Trader (and therefore the API that sits on top of it) is fundamentally asynchronous. Data is returned from the API through either a Callback as the result of a Request or as an Event passed to an EventListener. For this reason, you may notice that almost all of the methods in the API have void return values. Anyone who has done UI programming, e.g. using Swing or GWT, will find the LMAX API's asynchronous programming model very natural. Programmers who haven't done so may find this programming model counter-intuitive at first, but with a little practice it should become second nature. It may be necessary to become familiar with some slightly more advanced .NET features such as anonymous delegates.

3. Lets Get Started! - Logging In


To make the initial connection to LMAX Trader, first you need to construct an instance of LmaxApi. LmaxApi only requires a single argument which is the HTTPS URI for LMAX Trader. This would be "https://api.lmaxtrader.com" for production or "https://testapi.lmaxtrader.com" for the test system. Don't forget to add both a project reference to the LmaxClientLibrary.dll, and also the 'using' statements you need in each class file
using Com.Lmax.Api; // ... lines omitted public static void Main(string[] args) { string url = "https://testapi.lmaxtrader.com"; LmaxApi lmaxApi = new LmaxApi(url); }

Once the LmaxApi instance has been created, the next thing to do is construct a LoginRequest. LoginRequest requires a username and password as part of the constructor. There is an optional third parameter called ProductType which, if left out, defaults to ProductType.CFD_LIVE for connecting to production, but may be set to ProductType.CFD_DEMO to connect to the testapi system.
public static void Main(string[] args) { // ... lines omitted. LoginRequest loginRequest = new LoginRequest("myusername", "mypassword", ProductType.CFD_DEMO); lmaxApi.Login(loginRequest, LoginCallback, FailureCallback("log in")); }

The final step of the login process is to create a LoginCallback. The OnLogin(ISession session) Callback delegate should be considered the "entry point" of the client application. It is from within this Callback where the application should setup listeners and create subscriptions to the information that it is interested in. It is the point where the Session is provided to the application. A reference to the session should be captured, and held onto for later use. A common pattern is to use a delegate from inside the main application instance.
class MyTradingBot { private ISession _session; private void LoginCallback(ISession session) { Console.WriteLine("Logged in, account ID: " + session.AccountDetails.AccountId); _session = session; // Register EventListeners, subscribe to data and start the session. // More detail on this later. } private static OnFailure FailureCallback(string failedFunction) { return failureResponse => Console.Error.WriteLine("Failed to " + failedFunction + " due to: " + failureResponse.Message); }

file:///C:/Users/msischka.GAZPROMUK/AppData/Local/Temp/wz93be/LmaxNetCli...

19/09/2013

The LMAX .NET API Tutorial

Page 2 of 9

public static void Main(string[] args) { MyTradingBot myTradingBot = new MyTradingBot(); LmaxApi lmaxApi = new LmaxApi("https://testapi.lmaxtrader.com"); lmaxApi.Login(new LoginRequest("myusername", "mypassword", ProductType.CFD_DEMO), myTradingBot.LoginCallback, FailureCallback("log in")); } }

Success callbacks such as the LoginCallback above may accept different arguments depending upon the request made. However, failure callbacks will always contain a FailureResponse object. This is covered in more detail in the section on Handling Errors on Requests.

4. Subscribing to Market Data


Now that the application has successfully logged in, it's time to do something useful - subscribing to some market data. The login call introduced the first two concepts of Requests and Callbacks. Subscribing to market data involves the remaining three concepts: Events, EventListeners and the Session. Within the LoginCallback method, the application should first register an order book EventListener, then use the session to send a request to subscribe to the market data for a specific order book. Order book subscriptions are made on a per instrument id basis. Whenever a price change is pushed out to the client, the delegate method that is the listener for market data will be called, passing in an OrderBookEvent that will contain the current market data for a specific order book. The most common pattern is to make the main trading application class implement the OrderBookEventListener interface. Once all of the listeners and subscriptions are setup then the application needs to start the session. The start call is very important as it starts the connection to the asynchronous event stream. It should be noted that this call will block until the session is deliberately stopped - typically when the application wants to shutdown. The LMAX .NET API does not use multiple threads internally. However, it does not prevent multi-threaded application being written using the API. The session can be safely used across multiple threads (it will prevent start being called twice on the same session). The mechanism for finding instrumentIds is covered in the section on Retrieving Security Definitions. For the purposes of these examples we will assume that the instrumentId is known ahead of time.
class MyTradingBot { private const long GBP_USD_INSTRUMENT_ID = 4001; public void MarketDataUpdate(OrderBookEvent orderBookEvent) { Console.WriteLine("Market data: {0}", orderBookEvent); } public void LoginCallback(ISession session) { session.MarketDataChanged += MarketDataUpdate; session.Subscribe(new OrderBookSubscriptionRequest(GBP_USD_INSTRUMENT_ID), () => Console.WriteLine("Successful subscription"), failureResponse => Console.Error.WriteLine("Failed to subscribe: {0}", failureResponse)); session.Start(); } }

5. Placing Orders
The LMAX Trader platform supports a number of different order types and we are continually adding more. This section will just cover the basics of Market, Limit and Stop orders. Placing an order is very simple. It is just another case of constructing a request and waiting for a Callback. However, obtaining information as to what happened as a result of placing the order is not as simple. When an order is placed, the web server only parses the request, does some basic validation before sending it asynchronously to the broker. Obtaining the results of the order placement involves subscribing to another type of data, in this case execution reports. The request objects for Market, Limit and Stop orders are called MarketOrderSpecification, LimitOrderSpecification and StopOrderSpecification. To differentiate between buy orders and sell orders the LMAX Trader uses signed quantities. To place a buy use a positive quantity to place a sell use a negative one. When placing an order, an instructionId must be provided. This should be retained so that it can be used to cancel or amend the order later.
class MyTradingBot { private readonly List<string> _newOrders = new List<string>(); private readonly List<string> _pendingOrders = new List<string>(); private readonly List<Order.Order> _placedOrders = new List<Order.Order>(); //Sample order specifications for market, limit and stop buy-orders, with //hard-coded instruction ids private readonly MarketOrderSpecification _marketOrderSpecification = new MarketOrderSpecification("1", 4001, +2m, TimeInForce.ImmediateOrCancel); private readonly LimitOrderSpecification _limitOrderSpecification = new LimitOrderSpecification("2", 4002, 1.62m, +2m, TimeInForce.GoodForDay); private readonly StopOrderSpecification _stopOrderSpecification = new StopOrderSpecification("3", 4003, 1.62m, +2m, TimeInForce.GoodForDay); private ISession _session; public void MarketDataUpdate(OrderBookEvent orderBookEvent) { if (shouldTradeGivenCurrentMarketData(orderBookEvent)) { PlaceMarketOrder(orderBookEvent.InstrumentId, generateNextInstructionId()); decimal sellPrice = calculateSellPrice(orderBookEvent); PlaceSellLimitOrder(orderBookEvent.InstrumentId, sellPrice, generateNextInstructionId()); } } public void ExecutionEventListener(Execution execution) { if (_newOrders.Remove(execution.Order.InstructionId)) {

file:///C:/Users/msischka.GAZPROMUK/AppData/Local/Temp/wz93be/LmaxNetCli...

19/09/2013

The LMAX .NET API Tutorial

Page 3 of 9

_placedOrders.Add(execution.Order); } } public void PlaceMarketOrder(long instrumentId, string instructionId) { // Place a market order to buy - note that we can re-use an // order specification to place multiple orders but the instructionId // must be reset each time a new order is placed _marketOrderSpecification.InstrumentId = instrumentId; _marketOrderSpecification.InstructionId = instructionId; _newOrders.Add(instructionId); } public void PlaceSellLimitOrder(long instrumentId, decimal sellPrice, string instructionId) { // Place a limit order to sell. _limitOrderSpecification.InstrumentId = instrumentId; _limitOrderSpecification.Price = sellPrice; _limitOrderSpecification.Quantity = -2.0m;// Negative to indicate sell _limitOrderSpecification.InstructionId = instructionId; _newOrders.Add(instructionId); _session.PlaceLimitOrder(_limitOrderSpecification, PlaceOrderSuccess, OrderPlacementFailureCallback(instructionId, "place limit order")); } public void PlaceStopOrder(long instrumentId, decimal buyStopPrice, string instructionId) { // Place a limit order to sell. _stopOrderSpecification.InstrumentId = instrumentId; _stopOrderSpecification.StopPrice = buyStopPrice; _limitOrderSpecification.Quantity = 2.0m; // Positive to indicate buy _limitOrderSpecification.InstructionId = instructionId; _newOrders.Add(instructionId); _session.PlaceStopOrder(_stopOrderSpecification, PlaceOrderSuccess, OrderPlacementFailureCallback(instructionId, "place stop order")); } private void PlaceOrderSuccess(string placeOrderInstructionId) { // note - this will be the same instructionId as the one passed to the API, // it confirms this success is related to that specific place order request //move from "new" to "pending" to show the order was successfully placed _newOrders.Remove(placeOrderInstructionId); _pendingOrders.Add(placeOrderInstructionId); } private OnFailure OrderPlacementFailureCallback(string instructionId, string failedFunction) { return failureResponse => { _newOrders.Remove(instructionId); Console.Error.WriteLine("Failed to " + failedFunction + " due to: " + failureResponse.Message); }; } public void LoginCallback(ISession session) { // ... lines omitted. session.OrderExecuted += ExecutionEventListener; session.Subscribe(new ExecutionSubscriptionRequest(), () => Console.WriteLine("Successful subscription"), failureResponse => Console.Error.WriteLine("Failed to subscribe: {0}", failureResponse)); } }

_session.PlaceMarketOrder(_marketOrderSpecification, PlaceOrderSuccess, OrderPlacementFailureCallback(instructionId, "place market order"))

6. Cancelling Orders
Cancelling orders is very similar to placing orders, and shares the same Callback interface. A CancelOrderRequest requires the instrumentId and the instructionId ("originalInstructionId") of the original order that is now being cancelled. On a successful placement of CancelOrderRequest, the instructionId of the CancelOrderRequest is returned.
class MyTradingBot { private Session _session; private readonly long _instrumentId; // ..... private readonly List<string> _pendingOrders = new List<string>(); private readonly List<string> _workingOrders = new List<string>(); public void CancelAllOrders() { CancelOrders(_pendingOrders); CancelOrders(_workingOrders); } public void CancelOrders(List<string> instructionIds) { foreach (string originalInstructionId in instructionIds) { string cancelRequestInstructionId = generateNextInstructionId(); _session.CancelOrder(new CancelOrderRequest(_instrumentId, originalInstructionId, cancelRequestInstructionId), instructionId => Console.WriteLine("Cancel order instruction placed: %s", instructionId), failureResponse => Console.WriteLine("Failed to cancel order: %s%n", failureResponse)); } }

file:///C:/Users/msischka.GAZPROMUK/AppData/Local/Temp/wz93be/LmaxNetCli...

19/09/2013

The LMAX .NET API Tutorial

Page 4 of 9

7. Handling Rejects
Because the LMAX Trader platform is asynchronous it is not possible for all classes of order failures to be handled in the Failure Callback. A Request may be syntactically valid, but could be rejected at some point later by the Broker or the MTF. Possible reasons for rejections could include: EXPOSURE_CHECK_FAILURE or INSUFFICIENT_LIQUIDITY (full set available in instructionRejected-event.rng). These events are returned in the same way that executions are returned, via the event stream. So to be notified of rejection events it is necessary to register an InstructionRejected listener with the session. There is no specific subscription for instruction rejects, using ExecutionSubscriptionRequest will also subscribe to instruction rejected events.
class MyTradingBot { private void FailOnInstructionRejected(InstructionRejectedEvent instructionRejected) { Console.WriteLine("Rejection received: {0}", instructionRejected); } public void LoginCallback(ISession session) { // ... lines omitted. session.InstructionRejected += FailOnInstructionRejected; //should be subscribe to this once only, and it will fire the instruction reject and execution listeners session.Subscribe(new ExecutionSubscriptionRequest(), () => Console.WriteLine("Successful subscription"), failureResponse => Console.Error.WriteLine("Failed to subscribe: {0}", failureResponse)); } }

8. Handling Errors on Requests


The failure Callback can be invoked in two different scenarios, and the FailureResponse has a flag called IsSystemFailure to differentiate between them. The first is application failures. This is where the request has been successfully received and responded to by the LMAX Trader, however the system has detected that there is an error in the data, e.g. the price is negative or the quantity is zero. For this scenario, IsSystemFailure will be false, as the failure is not the result of the Request encountering a system issue. The flag will be set to true if some sort of system error was encountered while processing the request. This could include IOExceptions as a result of a network connectivity problem or a SAXParseException due to some corrupt data. If the FailureResponse was the result of an exception, the exception can be obtained by calling Exception on the FailureResponse. Not all system failures will be the result of an exception, e.g. if the response to the HTTP result was not an "200 OK" then a system failure will be generated. The actual response code will be in the Message part of the FailureResponse.
public OnFailure FailureCallback(string failedFunction) { return failureResponse => { if (!failureResponse.IsSystemFailure) { Console.Error.WriteLine("Data Error - Message: {0}, Description: {1}", failureResponse.Message, failureResponse.Description); } else { Exception e = failureResponse.Exception; if (null != e) { Console.Error.WriteLine(e.StackTrace); } else { Console.Error.WriteLine("System Error - Message: {0}, Description: {1}", failureResponse.Message, failureResponse.Description); } } }; }

9. Handling Errors on the Event Stream


The other place that errors can occur, specifically system errors, is on the event stream. By default these errors are hidden - if a failure occurs on the event stream (e.g. the stream is disconnected) it will automatically retry the connection. However, if an API client requires notification of an error that has occurred, it is possible to add an EventListener that will be called back any time an exception occurs on the event stream. The sole parameter passed to the listener registered with EventStreamFailed, is the Exception that occurred. The API client can make a decision about how it would like to proceed. If the API client would like to reconnect to the event stream, then no action is necessary, though it may wish to log the exception. However, if the client would like to shutdown, then the stop method on Session can be used to shutdown the API client. The stop method should only be used when the client actually wants to shutdown. It is possible to start and stop the session multiple times, but it is not recommended practice.
class MyTradingBot { private ISession _session; public void NotifyStreamFailure(Exception exception) { if (ShouldClientExit(exception)) { _session.Stop(); } } public void LoginCallback(Session session) { _session = session; session.EventStreamFailed += NotifyStreamFailure; session.Start(); // This method will exit when session.stop is called. } }

file:///C:/Users/msischka.GAZPROMUK/AppData/Local/Temp/wz93be/LmaxNetCli...

19/09/2013

The LMAX .NET API Tutorial

Page 5 of 9

10. Handling Session Disconnection


It is possible for the LMAX Exchange to log you out and disconnect your session. This may happen if there have not been any requests on the session for a period (usually around 15 minutes), or if your account has been locked. Timeouts can be avoided by making heartbeat requests every 5 minutes or so. HeartbeatClient.cs in the CSharpClientLibrarySamples project demonstrates how to subscribe, request and process heartbeat requests. If your session has been disconnected, reconnecting the session is not possible, so this does not happen automatically as it does for stream failures. A new session must be created by logging in again. In order to be notified of your session being disconnected, you must set a EventStreamSessionDisconnected delegate. An implementation might be to attempt to log in again, or exit the program.
class MyTradingBot { private ISession _session; private int _reconnectCount; public void NotifySessionDisconnect() { if (++_reconnectCount <= 3) { Console.WriteLine("Session disconnected - attempting to log in again (attempt " + _reconnectCount + ")"); _lmaxApi.login(...); } else { Console.WriteLine("Session disconnected - aborting after too many reconnect attempts"); } } public void LoginCallback(Session session) { _session = session; session.EventStreamSessionDisconnected += NotifySessionDisconnect; session.Start(); // This method will exit when session.stop is called. } }

11. Using and Amending Stops


The LMAX Trader platform supports stop loss and stop profit offsets for both limit and market orders. Stops can be specified either in the constructor for the relevant Limit or Market order specification, or by using the properties StopLossPriceOffset and StopProfitPriceOffset.
class MyTradingBot { private readonly List<string> _newOrders = new List<string>(); private readonly List<string> _pendingOrders = new List<string>(); private readonly MarketOrderSpecification _marketOrderSpecification = new MarketOrderSpecification(0, 100, 2.0m, TimeInForce.ImmediateOrCancel, 0.1m, 0.2m); private readonly LimitOrderSpecification _limitOrderSpecification = new LimitOrderSpecification(1, 101, 1.0m, 0.0m, TimeInForce.GoodForDay); private ISession _session; public void PlaceMarketOrder(long instrumentId, string instructionId) { // Place a market order to buy, note that we can re-use an // order specification to place multiple orders but the instructionId // must be reset each time a new order is placed _marketOrderSpecification.InstrumentId = instrumentId; _marketOrderSpecification.InstructionId = instructionId; _newOrders.Add(instructionId); _session.PlaceMarketOrder(_marketOrderSpecification, PlaceOrderSuccess, PlaceOrderFailure(instructionId, "place market order")); } public void PlaceSellLimitOrder(long instrumentId, decimal sellPrice, string instructionId) { // Place a limit order to sell. _limitOrderSpecification.InstrumentId = instrumentId; _limitOrderSpecification.Price = sellPrice; _limitOrderSpecification.Quantity = -2.0m;// Negative to indicate sell _limitOrderSpecification.StopLossPriceOffset = 0.2m; _limitOrderSpecification.InstructionId = instructionId; _newOrders.Add(instructionId); _session.PlaceLimitOrder(_limitOrderSpecification, PlaceOrderSuccess, PlaceOrderFailure(instructionId, "place sell limit order")); } private void PlaceOrderSuccess(string placeOrderInstructionId) { // note - this will be the same instructionId passed on the place order call, // it confirms this success is related to that specific place order request //move from "new" to "pending" to show the order was successfully placed _newOrders.Remove(placeOrderInstructionId); _pendingOrders.Add(placeOrderInstructionId); } private OnFailure PlaceOrderFailure(string instructionId, string failedFunction) { return failureResponse => { _newOrders.Remove(instructionId); Console.Error.WriteLine("Failed to " + failedFunction + " due to: " + failureResponse.Message); }; } }

Stops can also be amended after the order was placed, using the AmendStopLossProfitRequest. An important point to remember is that a null will remove a previously

file:///C:/Users/msischka.GAZPROMUK/AppData/Local/Temp/wz93be/LmaxNetCli...

19/09/2013

The LMAX .NET API Tutorial

Page 6 of 9

specified stop. So, if you only want to remove one of the stops (e.g. stop loss) you should remember to respecify the other one if required (e.g. stop profit).
class MyTradingBot { private readonly List<string> _amendInstructions = new List<string>(); private ISession _session; public void AmendStops(long instrumentId, string originalInstructionId, string instructionId, decimal stopLossOffset, decimal stopProfitOffset) { _session.AmendStops(new AmendStopLossProfitRequest(instrumentId, originalInstructionId, instructionId, stopLossOffset, stopProfitOffset), AmendSuccess, AmendFailure); } public void RemoveStopLoss(long instrumentId, string originalInstructionId, string instructionId, decimal oldStopProfitOffset) { // Use the oldStopProfitOffset to retain the stop profit for the order. _session.AmendStops(new AmendStopLossProfitRequest(instrumentId, originalInstructionId, instructionId, null, oldStopProfitOffset), AmendSuccess, AmendFailure); } private void AmendSuccess(string amendRequestInstructionId) { _amendInstructions.Add(amendRequestInstructionId); } private void AmendFailure(FailureResponse failureResponse) { Console.WriteLine("Failed to amend stop: {0}", failureResponse); } }

Amending a stop may fail - for example, if the stop has already fired.

12. Subscribing to Other Events


As well as being able to subscribe to market data, the API allows you to subscribe to other types of event. These are all described further on the Session interface. In each case, you need to add an event listener, then subscribe to the event. Account State: These allows you to keep track of your current balance, funds available to trade, etc. Whenever an account state change is pushed out to the client, the delegate registered as the AccountStateUpdated listener will be called, passing in an AccountStateEvent that will contain the latest details for your account. Execution: These describe executions on your orders, i.e. fills. Order Book Status: These describe order book status changes like the start of day Open event, end of day Close event, and other statii like Suspended. Order Events: These describe changes to your orders, e.g. if you add a stop loss to an order. Position Events: These describe overall position changes, which happen on fills, cancels, etc. Order Book Events: These describe market price changes (this is a high volume event stream). For example, subscribing to AccountState events:
class MyTradingBot { public void OnAccountStateEvent(AccountStateEvent accountStateEvent) { Console.WriteLine("Account state: {0}", accountStateEvent); } public void LoginCallback(ISession session) { session.AccountStateUpdated += OnAccountStateEvent; session.Subscribe(new AccountSubscriptionRequest(), () => Console.WriteLine("Successful subscription"), failureResponse => Console.Error.WriteLine("Failed to subscribe: {0}", failureResponse)); session.Start(); } }

NB for simplicity the above example does not include a market data subscription, but it is perfectly permissible for a client to subscribe for both types of events at once

13. Requesting Account State


As well as subscribing to account state changes as they happen, the API allows you to request the current account state. This will result in an AccountStateEvent being issued immediately. To receive the event you need to have subscribed to AccountStateEvents as described above.
class MyTradingBotAccountState { private ISession _session; public void OnAccountStateEvent(AccountStateEvent accountStateEvent) { Console.WriteLine("Account state: {0}", accountStateEvent); } public void LoginCallback(ISession session) { _session = session; _session.AccountStateUpdated += OnAccountStateEvent; session.Subscribe(new AccountSubscriptionRequest(), SubscriptionCallback, failureResponse => Console.Error.WriteLine("Failed to subscribe: {0}", failureResponse)); _session.Start(); }

file:///C:/Users/msischka.GAZPROMUK/AppData/Local/Temp/wz93be/LmaxNetCli...

19/09/2013

The LMAX .NET API Tutorial

Page 7 of 9

private void SubscriptionCallback() { _session.RequestAccountState(new AccountStateRequest(), () => Console.WriteLine("AccountStateRequest sent"), failureResponse => Console.Error.WriteLine("AccountStateRequest failed")); } }

14. Retrieve Security Definitions


The API allows you to retrieve security definitions. To do this create a SearchRequest and provide an implementation of OnSearchResponse. There are 2 main forms of the query string:
z z

To find a specific instrument the "id: (instrumentId)" form can be used. To do a general search, use a term such as "CURRENCY", which will find all of the currency instruments. A search term like "UK" will find all of the instruments that have "UK" in the name.

On a successful call the results will be returned in a List<Instrument> containing the first 25 results, ordered alphabetically by name. If there are more results, the parameter hasMoreResults will be set to true. To retrieve the next 25 instruments do another search, passing the id from the last instrument as the offsetInstrumentId of this new search. For example:
class MyTradingBotAccountState { private ISession _session; public void LoginCallback(ISession session) { string query = ""; // see above for how to do a more specific search long offsetInstrumentId = 0; // see above for more details on this offset parameter _session = session; session.SearchInstruments(new SearchRequest(query, offsetInstrumentId), SearchCallback, failureResponse => Console.Error.WriteLine("Failed to subscribe: {0}", failureResponse)); _session.Start(); } private void SearchCallback(List<Instrument> instruments, bool hasMoreResults) { Console.WriteLine("Instruments Retrieved: {0}", instruments); if(hasMoreResults) { Console.WriteLine("To continue retrieving all instruments please start next search from: {0}", instruments.get(instruments.size() } } }

15. Retrieving Historic Market Data


In addition to subscribing to real-time market data, the API provides a mechanism for retrieving historic market data. Two types of historic market data are supported:
z z

TopOfBookHistoricMarketDataRequest AggregateHistoricMarketDataRequest

- Best bid & ask tick data. - Aggregated price & volume data by day or minute.

The data is delivered as a gzip-compressed CSV file. The following steps are required to receive historic market data: 1. 2. 3. 4. Implement and register a historic market data delegate Subscribe to historic market data events Make historic market data requests Retrieve URLs within an authenticated session

The code snipets below are extracted from the class HistoricMarketDataRequester in the API samples. 15.1. Implement and register a historic market data delegate To receive historic market data, you must first implement a delegate matching the signature of LmaxApi.OnHistoricMarketDataEvent and register the delegate with the session:
// Implement the delegate private void OnHistoricMarketData(string instructionId, List uris) { // do something with the URIs... see section 15.4 for a sample implementation } // Register the delegate _session.HistoricMarketDataReceived += OnHistoricMarketData;

15.2. Subscribe to historic market data events Use the standard _session.Subscribe() mechanism to subscribe to historic market data requests:
_session.Subscribe(new HistoricMarketDataSubscriptionRequest(), () => Console.WriteLine("Successful subscription"), failureResponse => Console.Error.WriteLine("Failed to subscribe: {0}", failureResponse));

As with other subscriptions, you can Subscribe before you call _session.Start(). 15.3. Make historic market data requests

file:///C:/Users/msischka.GAZPROMUK/AppData/Local/Temp/wz93be/LmaxNetCli...

19/09/2013

The LMAX .NET API Tutorial

Page 8 of 9

To request historic market data for a specific instrument and date range, create an instance of TopOfBookHistoricMarketDataRequest or AggregateHistoricMarketDataRequest and call the requestHistoricMarketData method on the session:
_session.RequestHistoricMarketData(new AggregateHistoricMarketDataRequest(instructionId, instrumentId, DateTime.Parse("2011-05-11"), DateTime.Parse("2011-06-13"), Resolution.Day, Format.Csv, Option.Bid), () => Console.WriteLine("Successful request"), failureResponse => Console.Error.WriteLine("Failed request: {0}", failureResponse));

15.4. Retrieve URLs within an authenticated session When the data is ready, the delegate you registered in step 15.1 will receive an asynchronous message with a list of URLs. The asynchronous message also includes the instructionId you included with the request. The URLs must be retrieved using an authenticated connection. You can use the session's OpenUri() method to open each URL. The files at the URLs are compressed with gzip. The code snippet below shows how to retrieve the URL and print out the uncompressed data:
private void OnHistoricMarketData(string instructionId, List uris) { foreach (var uri in uris) { _session.OpenUri(uri, OnUriResponse, FailureCallback("open uri")); } } private static void OnUriResponse(Uri uri, BinaryReader reader) { using (var stream = new GZipStream(reader.BaseStream, CompressionMode.Decompress)) { const int size = 1024; var buffer = new byte[size]; var numBytes = stream.Read(buffer, 0, size); while (numBytes > 0) { Console.Write(Encoding.UTF8.GetString(buffer, 0, numBytes)); numBytes = stream.Read(buffer, 0, size); } } }

16. Protocol Version Checking By default, the LMAX .NET API checks that the current protocol version used by the LMAX Trader Platform matches the version that the LMAX .NET API was built for. This strict checking can be disabled when constructing the LoginRequest by supplying false as the fourth parameter:
LoginRequest loginRequest = new LoginRequest("myusername", "mypassword", ProductType.CFD_DEMO, false);

17. Order and Execution Tracking


When it comes to tracking orders and executions the LMAX .NET API does not behave in exactly the same way that FIX does. This means it can be a little bit tricky to recognise the last Execution event corresponding to a given Order event. For example, if an individual order with a quantity of 30 aggressively matches multiple price points (quantity of 10 at each) on the exchange, a fix user would expect would expect:
MsgType(35)=8, MsgType(35)=8, MsgType(35)=8, MsgType(35)=8, ExecType(150)=New(0), CumQty(14)=0 ExecType(150)=Trade(F), CumQty(14)=10 ExecType(150)=Trade(F), CumQty(14)=20 ExecType(150)=Trade(F), CumQty(14)=30

Since our API is based upon our XML protocol the information that we can return on the API is restricted to what is included in the XML messages. For a similar scenario our XML protocol only emits a single order event at the end of the matching cycle, therefore the data output would be:
<order> <timeInForce>ImmediateOrCancel</timeInForce> <instructionId>1733844027851145216</instructionId> <originalInstructionId>1733844027851145216</originalInstructionId> <orderId>AAK8oAAAAAAAAAAF</orderId> <accountId>1393236922</accountId> <instrumentId>179361</instrumentId> <quantity>30</quantity> <matchedQuantity>30</matchedQuantity> <matchedCost>22.1</matchedCost> <cancelledQuantity>0</cancelledQuantity> <timestamp>2011-12-22T10:09:25</timestamp> <orderType>STOP_COMPOUND_MARKET</orderType> <openQuantity>20</openQuantity> <openCost>22.1</openCost> <cumulativeCost>22.1</cumulativeCost> <commission>0</commission> <stopReferencePrice>111</stopReferencePrice> <stopLossOffset /> <stopProfitOffset /> <executions> <executionId>3</executionId> <execution> <price>110</price> <quantity>10</quantity> </execution> <execution> <price>111</price> <quantity>10</quantity>

file:///C:/Users/msischka.GAZPROMUK/AppData/Local/Temp/wz93be/LmaxNetCli...

19/09/2013

The LMAX .NET API Tutorial

Page 9 of 9

</execution> <execution> <price>112</price> <quantity>10</quantity> </execution> </executions> </order>

This means that the .NET API does not have the information about the individual filled quantities as a result of each execution, only the final state of the order. This can make it a little tricky to find which Execution event represents the end of the matching cycle. The information seen by a user of the .NET API for the same scenario would be:
Quantity = 10, Order.FilledQuantity = 30 Quantity = 10, Order.FilledQuantity = 30 Quantity = 10, Order.FilledQuantity = 30

However, it is possible to use some of the additional events that are available on the .NET API to derive the same behaviour. By listening to both Execution and Order events we can track the cumulative quantities as we go. It does require a little bit more state management by the client, but the logic is fairly straight forward:
class ExecutionTracking { private private private private private ISession _session; Dictionary<string, Order> _lastOrderStateByInstructionId = new Dictionary<string,Order>(); Order _currentOrder; decimal _currentFilledQuantity; decimal _currentCancelledQuantity;

public ExecutionTracking(ISession session) { _session = session; } private void OnOrder(Order order) { Order previousOrderState; if (_lastOrderStateByInstructionId.TryGetValue(order.InstructionId, out previousOrderState)) { // Track the current filled/cancelled quantity as a delta between this // order event and the previous one of the same order. _currentFilledQuantity = previousOrderState.FilledQuantity; _currentCancelledQuantity = previousOrderState.CancelledQuantity; } else { // The is the first order event for this order, so start from zero. _currentFilledQuantity = 0; _currentCancelledQuantity = 0; } _currentOrder = order; } private void OnExecution(Execution execution) { // As we receive the executions for the order increment the quantities for each execution _currentFilledQuantity += execution.Quantity; _currentCancelledQuantity += execution.CancelledQuantity; // Once our per execution tracking of the order matches the totals on the // order itself, we've found the last execution for a given order event. if (_currentOrder.FilledQuantity == _currentFilledQuantity && _currentOrder.CancelledQuantity == _currentCancelledQuantity) { Console.Out.WriteLine("Last Execution: " + execution + " for order: " + _currentOrder); if (isComplete(execution.Order)) { // The order has completed, all quantity is filled or cancelled, so remove the order from _lastOrderStateByInstructionId.Remove(_currentOrder.InstructionId); } else { // Track the order event for the next match _lastOrderStateByInstructionId.Add(_currentOrder.InstructionId, _currentOrder); } _currentOrder = null; } } private Boolean isComplete(Order order) { decimal completedQuantity = order.FilledQuantity + order.CancelledQuantity; return order.Quantity == completedQuantity; } public void Subscribe() { _session.OrderChanged += OnOrder; _session.OrderExecuted += OnExecution; _session.Subscribe(new ExecutionSubscriptionRequest(), SubscriptionSuccessCallback, SubscriptionFailureCallback); } }

file:///C:/Users/msischka.GAZPROMUK/AppData/Local/Temp/wz93be/LmaxNetCli...

19/09/2013

Potrebbero piacerti anche