Geeks With Blogs
Josh Reuben
 

LMAX provides a .NET API for automating your financial trading strategy.

sign up & download here:http://www.lmaxtrader.co.uk/

If you can combine software engineering skills + numerical analysis skills + an understanding of financial markets --> the whole is greater than the sum of the parts. You can build products that make money, and you can save money by being a cross-domain expert.

http://en.wikipedia.org/wiki/Quantitative_analyst - see here to learn more about this lucrative field. A good place to start in learning quant programming is to Paul Wilmott's Introduction to Quantitative Finance - http://www.amazon.com/Paul-Wilmott-Introduces-Quantitative-Finance/dp/0470319585/ref=sr_1_3?ie=UTF8&qid=1310104602&sr=8-3 

 

Introduction
·        The LMAX .NET API is a wrapper over XML over HTTPS (REST) protocol.
·        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.
·        LMAX Trader is asynchronous - Data is received either through a Callback as the result of a Request or as an Event passed to an EventListener.
·        add a project reference to the LmaxClientLibrary.dll, and a 'using' statement for Com.Lmax.Api
To connect to LMAX Trader
·        construct an instance of LmaxApi with the HTTPS URI for LMAX Trader: "https://trade.lmaxtrader.com" for production or "https://testapi.lmaxtrader.com" for the test system.
_lmaxApi = new LmaxApi("https://testapi.lmaxtrader.com");
·        construct a LoginRequest – specify credentials + ProductType enum value: ProductType.CFD_LIVE (default) / ProductType.CFD_DEMO (to connect to the testapi system).
var loginRequest
= new LoginRequest("myusername", "mypassword", ProductType.CFD_DEMO);
_lmaxApi.Login(loginRequest, LoginCallback, FailureCallback("log in"));
create a LoginCallback – set the session,  setup listeners and create subscriptions.
    class MyTradingBot
    {
        private ISession _session;
 
        private void LoginCallback(ISession session)
        {
            Logger.Log("AccountId:" + _session.AccountDetails.AccountId);
            _session = session;
            // Register EventListeners, subscribe to data and start the session.
        }
 
        private static OnFailure FailureCallback(string failedFunction)
        {
            return failureResponse => Console.Error.WriteLine("Failed to " + failedFunction + " due to: " + failureResponse.Message);
        }
 
        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 will accept different arguments depending upon the request made. However, failure callbacks will always contain a FailureResponse object.
Subscribing to Market Data
·        Within the LoginCallback method, register an order book event listener ISession.MarketDataChanged, then use the session 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 callback 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.
·        Once all of the listeners and subscriptions are setup then the ISession.Start call starts the connection to the asynchronous event stream. It should be noted that this call will block until the session is 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).
    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();
        }
    }
Placing Orders
·        The LMAX Trader platform supports a number of different order types and we are continually adding more.
·        Placing an order is just another case of constructing a request and waiting for a Callback. When an order is placed, the web server only validates & parses the request and sends it asynchronously to the broker. Obtaining the results of the order placement involves subscribing to execution reports.
·        The request objects for Market and Limit orders are called MarketOrderSpecification and LimitOrderSpecification. 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<long> _newOrders = new List<long>();
        private readonly List<long> _pendingOrders = new List<long>();
        private readonly List<Order> _placedOrders = new List<Order>();
 
        //Sample order specifications for market and limit 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 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))
            {
                _placedOrders.Add(execution.Order);
            }
        }
 
        public void PlaceMarketOrder(long instrumentId, long 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, OrderPlacementFailureCallback(instructionId, "place market order"));
        }
 
        public void PlaceSellLimitOrder(long instrumentId, decimal sellPrice, long 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"));
        }
 
        private void PlaceOrderSuccess(long 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(long 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));
 
        }
    }
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<long> _pendingOrders = new List<long>();
        private readonly List<long> _workingOrders = new List<long>();
 
        public void CancelAllOrders()
        {
            CancelOrders(_pendingOrders);
            CancelOrders(_workingOrders);
        }
 
       public void CancelOrders(List<long> instructionIds)
        {
            foreach (long originalInstructionId in instructionIds)
            {
                long cancelRequestInstructionId = generateNextInstructionId();
                _session.CancelOrder(new CancelOrderRequest(_instrumentId, originalInstructionId, cancelRequestInstructionId),
                        instructionId => Console.WriteLine("Cancel order instruction placed: {0:d}", instructionId),
                        failureResponse => Console.WriteLine("Failed to cancel order: %s%n", failureResponse));
            }
        }
    }
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));
 
        }
    }
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);
                               }
                           }
                       };
        }
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.
        }
    }
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<long> _newOrders = new List<long>();
        private readonly List<long> _pendingOrders = new List<long>();
 
        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, long 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, long 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(long 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(long 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 specified stop. to remove one of the stops (e.g. stop loss) you need to re-specify the other one if required (e.g. stop profit).
    class MyTradingBot
    {
        private readonly List<long> _amendInstructions = new List<long>();
 
        private ISession _session;
 
        public void AmendStops(long instrumentId, long originalInstructionId, long instructionId,
                           decimal stopLossOffset, decimal stopProfitOffset)
        {
            _session.AmendStops(new AmendStopLossProfitRequest(instrumentId, originalInstructionId, instructionId,
                                                     stopLossOffset, stopProfitOffset), AmendSuccess, AmendFailure);
        }
 
        public void RemoveStopLoss(long instrumentId, long originalInstructionId, long 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(long 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.
Subscribing to Account State changes
·        the API enables subscription to Account State changes. This 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.
    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
Requesting Account State
·        As well as subscribing to account state changes 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();
        }
 
        private void SubscriptionCallback()
        {
            _session.RequestAccountState(new AccountStateRequest(),
                () => Console.WriteLine("AccountStateRequest sent"),
                failureResponse => Console.Error.WriteLine("AccountStateRequest failed"));
        }
    }
 
Posted on Friday, July 8, 2011 8:58 AM Quant Programming | Back to top


Comments on this post: LMAX .NET Trading API Walkthrough

# re: LMAX .NET Trading API Walkthrough
Requesting Gravatar...
Hi,

I have a question about the session and multithreading.

At the moment i use Java API... i think it's the same that .net api.

Do you think that a possible to send order (market) in parallel in the same session (or create another with same account) ?

Because at the moment if i send 2 orders market, i need wait return execution of first order to send the 2nd order... :(

Thanks for you help.

Regards
Left by Ludo on Aug 03, 2013 12:39 AM

# re: LMAX .NET Trading API Walkthrough
Requesting Gravatar...
It is somehow confusing. I guess, I needed more time to study this. - Dr. Thomas Devlin
Left by Mike Abbott on Jan 04, 2017 10:03 AM

# re: LMAX .NET Trading API Walkthrough
Requesting Gravatar...
Hi there,

Does anyone knows the DNS LIve server and live TargetCompId: ?

I only have the demo ones

Market Data:-

DNS: fix-marketdata.london-demo.lmax.com
Port: 443 (SSL)
TargetCompId: LMXBDM
Left by Marcelo on Sep 20, 2017 3:23 AM

Your comment:
 (will show your gravatar)


Copyright © JoshReuben | Powered by: GeeksWithBlogs.net