Algorithmic Trading

MOVING FROM RESEARCH TO TRADING

I have written recently about the comparative advantages of different programming languages in the context of research and trading (see here).  My sense of it is that there is no single “ideal” programming language – the best strategy is to pick an appropriate tool for the job and there are usually several reasonable choices one could make.

If you are engaged in econometrics research, you might choose a package like RATS, Eviews, Gauss, or Prof. James Davidson’s excellent and inexpensive TSM, which I have used for many years and can recommend highly. For a latency-sensitive high frequency trading application, you will probably want to use something like C++, or possibly a 3rd party algo system like Apama or Tethys. But for algorithmic trading systems of intermediate frequency the choice appears almost unlimited.

Matlab AlgoThe problem with retail trading tools like TradeStation, Multicharts, or Amibroker, is that they are designed primarily for single-asset strategies.  That may be ok for futures trading,where more often than not the focus is on a single underlying, but in equities the opposite is true. Using one of these products to develop and implement a pairs trading strategy is a stretch.   As for portfolio analytics – forget it.

This is where more general, high level languages like R, Matlab or Mathematica come in:  their greater power and flexibility is handling large, multivariate data sets makes it much more straightforward to develop portfolio strategies. And they can often bridge the gap between R&D and implementation quite easily:  code that was used in the research stage can often be quickly re-tooled to work in a production version of the system.  As for production systems, there is now a significant cottage industry of traders who use Matlab in algo trading.  R has a similar following (see here).

In addition to parallelizing the code (for use with the Parallel Computing Toolbox) to speed up the research phase, you might also want to implement a hybrid system by re-coding the slower routines in C++, to create a mex file (for details see here). Matlab’s Profiler is a useful tool for identifying code bottlenecks.  In a recent piece of research in which I was evaluating over 30,000,000 cointegrated portfolios, I discovered to my surprise that the main code bottleneck was the multiple calls to Matlab’s std function, a problem easily fixed with a few lines of C++ code.  The resulting hybrid program executed at more than twice the speed – important when your run time might be several hours, or even days.

HOOKING UP THE EXECUTION PLATFORM

The main challenge for developers using generic tools like Mathematica, Matlab or R is the implementation stage of the project. Providing connectivity to brokerage/execution platforms never seemed high on the list of priorities for Wolfram or Mathworks and things are similarly hit or miss with R.

Belatedly, Mathematica now offers a link to Bloomberg via its Finance Platform.  Matlab, meanwhile, offers a Trading Toolbox, which supposedly offers connectivity , not only to Bloomberg, but also Interactive Brokers and Trading Technologies, amongst other platforms.  Unfortunately, the toolbox interface to IB appears to rely on outdated 1990s ActiveX technology, which is flakey at best.  In tests, I was unable to make progress past the ‘not connected’ error message.

At that point I turned to Yair Altman’s  IB-Matlab product.  Happily, this uses IB’s Java api, which is a great deal more robust than the ActiveX platform.  It’s been some time since I last used IB-Matlab and was pleased to see that Yair has been very busy over the intervening period, building the capabilities of the system and providing very comprehensive documentation for it.  With Yair’s help, it took me no time at all to get up and running and within a day or two the system was executing orders flawlessly in IB’s TWS.  The relatively few snags I ran into were almost all due to IB’s extremely terse error messaging, which often gives almost no clue as to what the issue might be.  Fortunately, Yair is very generous with his time in providing support to his users and his responses to me questions were fast and detailed.

EXECUTION ALGOS

With intermediate  systems trading at frequencies of, say, 5-minutes to daily, one has a choice to make as regards execution.  Given that the strategy is not very latency sensitive, it is certainly conceivable to develop one’s own execution algos in Matlab.  However, platforms like TWS are equipped with native algos, not only from IB, but also other providers like Credit Suisse and JefAD Algofries.

Actually, I have found several of IB’s own algos such as Scaletrader and Accumulate/Distribute to be very effective. Certainly IB seems very proud of them – IB CEO Thomas Peterffy has patented at least one of them. Accumulate/Distribute, for instance, is quite sophisticated, allowing the user to randomize and slice the size and interval between individual orders, use passive or aggressive order types, and pause execution on a news alert, or when the price falls below a moving average, or outside a specified range.

There is much to be said for using algos native to the execution platform rather than reinventing the wheel, providing the cost is reasonable. So, while it is perfectly feasible to build execution algos in Matlab, it typically isn’t necessary – in most cases standard algos will suffice.

There are exceptions, of course.  IB doesn’t offer the  kind of basket-trading capabilities REDIthat are available in advanced algo platforms like Tethys or RediPlus.  In those systems, for example, you can set the level of long/short imbalance in the portfolio that you are willing to tolerate and the algo will speed up or slow down execution of trades in individual components of the basket to maintain the dollar imbalance within that tolerance.  You can also manage the sector risk dynamically during execution.

Those kind of advanced capabilities don’t come cheap and you wont find them at IB, or any other retail platform. If you need that kind of functionality, for example, because you are trading a long/short equity portfolio within a universe of 200-300 names, your best option is probably to switch to a different execution platform.  Otherwise you will need to code a custom algo in your language of choice.

For many quantitative strategies, (at least the low frequency ones) IB’s standard algos are often good enough.  The Accumulate/Distribute algo, for instance, will show a visual representation of the progress of the execution of individuals legs of a pairs trade, and it is easy enough to identify a potential imbalance and adjust the algo parameters in real time. If you are only trading pairs, or small portfolios of cointegrated securities, it probably isn’t worthwhile to develop the sophisticated logic that would be required to handle the adjustment of the execution of individual legs of a trade in a fully automated way.  A large portfolio would be a different matter, however.

MATLAB EXAMPLE

I thought it might be instructive to take a look at how you might implement the execution of a strategy in Matlab, using IB algos. In the Matlab code fragment below, the (2 x nTickers) array tradeActions contains, in the first row, the action we wish to take (1 = BUY, -1 = SELL, -2 = SELL SHORT) and in the second row the (absolute value of) the number of shares we wish to trade for tickers i =1:nTickers. We break each order up into hundred lots and odd lots, routing the former via IB’s Accumulate/Distribute algo and the latter as passive REL orders (note that A/D  will typically randomize the timing of each sub-order, while REL orders are posted directly into the market). The Matlab function AccumulateDistribute implements the most important features of IB’s A/D algo, including random size and time slicing of the order.  Orders are submitted as passive REL orders with zero offset (so they will sit on the current bid or ask) – obviously you would typically want to consider allowing some non-zero offset for less liquid securities.  It is not hard to envisage how one might further enhance the algo to monitor the progress of the execution and speed up or slow down certain orders accordingly.

MatlabA couple of IB api “gotchas” to be aware of:

(i) IB requires unique and monotonically increasing orderIds for each order. One way to do this, suggested by Yair, is to use orderId = round((now-735000)*3e5);  This fails when you are submitting a number of orders sequentially at high speed (say in a for loop), where the time increments are sub-second, so you need to pass the orderID back and force a minimal increment, as I have in the code below.

(ii) It is very important to specify the primary exchange of each security:  securities with identical tickers can be found trading on different exchanges.  Failing to specify the primary exchange in such a case will result in IB rejecting the order with a typically cryptic api message.

for i = 1:nTickers
if tradeActions(1,i) ~= 0
nShares = abs(tradeActions(2,i));
hundredLots = 100*floor(nShares /100);
oddLots = nShares-hundredLots;
currentTicker = cell2mat(tickers(i));
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Handle round hundred lots in Accumulate Distribute algo
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
if hundredLots > 0
orderId=AccumulateDistribute(ibConnectionObject, AccountId, currentTicker,…
tradeActions(1,i), hundredLots, 0, executionTime, timeBetweenOrders, orderId);
nOrders = nOrders +1;
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Handle excess odd lots as REL orders
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
if oddLots > 0
orderId=RELOrder(ibConnectionObject, AccountId, currentTicker,…
tradeActions(1,i), oddLots, orderId);
nOrders = nOrders +1;
end
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%function orderId = AccumulateDistribute(ibConnectionObject,AccountId, …
Ticker, Action, Qty, timeZone, executionTime, timeBetweenOrders, orderId)%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%if nargin < 9
orderId = round((now-735000)*3e5);
endif nargin < 8
timeBetweenOrders = 11; %secs
end
if nargin < 7
executionTime = 300; %secs to execute order
endif nargin < 6
timeZone = 0;
endif timeZone == 0
Zone = ‘ EST’;
elseif timeZone == 5
Zone = ‘ GMT’;
endswitch Action
case 1
orderAction = ‘BUY’;
case -1
orderAction = ‘SELL’;
case -2
orderAction = ‘SELL’; %’SSHORT’;
end

componentSize = 200*(1+floor(Qty/1000));
componentSize=min(componentSize,Qty);
componentSize = max(100, componentSize);
componentSize =min(1000, componentSize);

import com.ib.client.TagValue;

m_contract = ibConnectionObject.createContract(”,”,”,0,”,”,”,”);
m_contract.m_symbol = Ticker;
m_contract.m_secType = ‘STK’;
m_contract.m_exchange = ‘SMART’;
m_contract.m_primaryExch = ‘ARCA’;
m_contract.m_currency = ‘USD’;
m_contract.m_localSymbol = Ticker;

m_algoParams = java.util.Vector;

startTime = now + 5/24; % A/D apears to treat all times as GMT!
startTime = [datestr(startTime, ‘yyyymmdd’), ‘-‘,datestr(startTime, ‘HH:MM:SS’), Zone];
endTime = now + 5/24 + executionTime / (60*60*24);
endTime = [datestr(now, ‘yyyymmdd’), ‘-‘,datestr(endTime, ‘HH:MM:SS’) Zone];

% Set parameters for Accumulate/Distribute Algo
m_algoParams.add( TagValue(‘componentSize’, num2str(componentSize)));
m_algoParams.add( TagValue(‘timeBetweenOrders’, num2str(timeBetweenOrders)));
m_algoParams.add( TagValue(‘randomizeTime20’, ‘1’));
m_algoParams.add( TagValue(‘randomizeSize55’, ‘1’));
m_algoParams.add( TagValue(‘giveUp’, ‘0’));
m_algoParams.add( TagValue(‘catchUp’, ‘1’));
m_algoParams.add( TagValue(‘waitForFill’, ‘1’));
m_algoParams.add( TagValue(‘startTime’, startTime));
m_algoParams.add( TagValue(‘endTime’, endTime));

% Now create the order, using algoParams
m_order = ibConnectionObject.createOrder(…
orderAction, Qty, ‘REL’, 0, 0, ”, …
”, 0, ”, ”, realmax, 0, false);
m_order.m_account = AccountId;
m_order.m_action = orderAction;
m_order.m_totalQuantity = Qty;
m_order.m_orderType = ‘REL’;
m_order.m_algoStrategy = ‘AD’;
m_order.m_tif = ‘DAY’;
m_order.m_algoParams = m_algoParams;
m_order.m_transmit = false;

ibConnectionObject.placeOrder(orderId, m_contract, m_order);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

function orderId = RELOrder(ibConnectionObject,AccountId,
Ticker, Action, Qty, orderId)

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

if nargin < 6

orderId = round((now-735000)*3e5);

end

 

switch Action

case 1

orderAction = ‘BUY’;

case -1

orderAction = ‘SELL’;

case -2

orderAction = ‘SELL’; %’SSHORT’;

end

import com.ib.client.TagValue;

m_contract = ibConnectionObject.createContract(”,”,”,0,”,”,”,”);

m_contract.m_symbol = Ticker;

m_contract.m_secType = ‘STK’;

m_contract.m_exchange = ‘SMART’;

m_contract.m_primaryExch = ‘ARCA’;

m_contract.m_currency = ‘USD’;

m_contract.m_localSymbol = Ticker;

 

% Now create the order, using algoParams

m_order = ibConnectionObject.createOrder(…

orderAction, Qty, ‘REL’, 0, 0, ”, …

”, 0, ”, ”, realmax, 0, false);

m_order.m_account = AccountId;

m_order.m_action = orderAction;

m_order.m_totalQuantity = Qty;

m_order.m_orderType = ‘REL’;

m_order.m_tif = ‘DAY’;

m_order.m_transmit = false;

ibConnectionObject.placeOrder(orderId, m_contract, m_order);

orderId = orderId + 1;