pairs portfolio
People have asked me how I go about implementing a strategy in Stratbox. While I’ve illustrated a good number of strategies running in Stratbox in these pages, I’ve never walked through a non-trivial example from conception, through design, implementation and iteration. Today we’ll go through a reasonably complex example in total (I’ll provide source) detail.
The example I’ve chosen is, I think, very nice because it’s a portfolio-oriented strategy, which is pretty much the only kind I care to explore; it’s also based around the concept of pairs trading, which is something which most can easily relate to; and finally, it’s already public domain and yet almost certainly has some juice in it for those who care to understand it and extend it intelligently.
The example comes from the blog of a company, Palantir which does (something like?) analytical / decision support software for both finance and intelligence-gathering services (quants and spooks – spooky quants?). The specific example is here and is described thusly:
In this study we explore a trading strategy that isolates pairs of instruments within a sector that are highly correlated. We enter a trade if the price paths of these instruments diverge, going long one instrument and short the other, with the assumption that their price paths will converge.
We construct our strategy in four parts: (1) isolate the target set of tradable instruments, (2) choose a rule for finding correlated pairs within that set, (3) pick criteria for entering trades, and (4) test the trading strategy.
…
For this study we want to isolate pairs of instruments whose correlation is above a certain threshold. At the beginning of each quarter, we restrict our target stocks to the top 5 in the sector by 252-day correlation to the S&P 500. Then our Strategy uses a metric that, for a given group of stocks, outputs a list of pairs with a correlation above our target threshold of .60 over the same time period.
…
Across every day and every pair in our trading list, we check the 21-day z-score (number of standard deviations from the mean) of the difference between the two series. We classify the stocks as diverging when the absolute value of the z-score is between 1.5 and 3.0. When this condition is met we short the stock that is rising and long the stock that is falling, sizing our position relative to the z-score and the number of pairs currently trading.
~(sectors) ; reframing the strategy
While I’ll implement a strategy broadly based on their description, I will use some poetic license to modify it in ways that seem to me sensible. First, I have good experience writing equity strategies which deal with sectors and have pretty uniformly found that sectors, as defined by S&P or whomever, are more trouble than they’re worth. Instead, looking at correlation matrices and determining on that basis who should be paired, is much more fruitful in my experience. Tr8der has some very nice posts illustrating (very prettily!) a similar point here and here. (Actually, his point is a bit broader and represents an area of possibly promising development of this strategy which I won’t pursue here.)
So, we’re going to chunk the idea of explicitly using sectors. Instead, we’ll simply construct, on a monthly basis (Palantir did this quarterly) a TrailDays correlation matrix from which we’ll extract the set of pairs which have a correlation greater than MinCorrelation. On a daily basis, we will calculate the ZScoreDays z-scores of each of these pairs and sort them by absolute value, tossing those whose values are less than MinZScore or exceed MaxZScore. Since I don’t like the idea of having an open-ended number of pairs/positions in my portfolio, I will constrain the portfolio to a maximum of N names and will Allocate $1M to the strategy. Finally, we will look at the effect of trading an EvenlyWeighted portfolio or a z-score weighted portfolio (as Palantir had done).
The initial ‘universe’ of equities across which we will calculate our correlations will be filtered from my database of daily equity data going back to 2005 where the closing price is over $1, the average daily dollar-volume is over $50M and the data is relatively clean (few if any gaps). This yields an initial universe of ~600 equities and ~180K pairs.
Assuming that we keep N above 20 or so and we keep our allocation constant at $1M, and assume execution at the close price, we should expect our results to be reasonably believable as it’s unlikely that the size we’re trading in would distort the market. Scaling any strategy presents its own set of challenges which we’re not going to go into for this example, but an obvious direction for scaling the strategy would be to simply run it over intraday data and scale into and out of positions as conditions permit.
Ok, so that’s what our strategy is going to look like. Now, how do we “make it so”?
structure of a stratpart
Within Stratbox, strategies are represented as composites made-up of ‘stratparts’ each of which has its own metadata descriptor containing parameters. Stratparts can be composed together within a strategy to enhance or modify the behavior of a strategy. Thus, one can have a stratpart which implements the above logic paired with another stratpart which will e.g., modify the portfolio to impose beta neutrality, damp volatility or some other form of reporting or risk management. Given that this example is already complexish, we’ll just stick to the one stratpart.
A stratpart has a few key elements. One is the metadata descriptor which allows the strategy container (stratbox) to interact with it for back-testing, optimization or forward-walking purposes, or to allow a ‘trader’ to interact with it dynamically during run-time (the so-called ‘gray box’). Beyond that, it has an essentially event-driven design. The method quote() is called when relevant (i.e., subscribed) market data arrives. The method execution() is called when executions for orders that originated with this strategy are received. And the method strategyEvent() is invoked on a variety of ‘interesting’ events that the strategy implementer might care about. None of these methods are required, so in its simplest form, a stratpart is a very simple piece of code to write.
services in a stratpart
Once one has complied with the minimal requirements for implementing a stratpart, one is rewarded with a variety of useful services: subscription-based market data feeds as well as a fast snapshot db and an in-memory historical db for quick correlation analyses cover market data needs. Position management is handled at both the strategy level and at the exchange level (if you’re trading across multiple exchanges). There is more, including performance analysis (e.g., sharpe, and its various analytic friends) and hierarchical cash allocation within and across strategies, but we won’t look at these in this example.
implementing metadata
So, the first thing we need to do is define our metadata. This is done through the use of a conventionally-named static method Descriptor().
public static Descriptor Descriptor() {
ArrayList<Param> params = new ArrayList<Param>();
String cd = "Unleveraged capital to apply";
params.add(new Param(InitialBalance, cd, false, 1000000.0));
String n = "# of instruments to hold in portfolio";
params.add(new Param(N, n, true, 20));
String tc = "Trailing days over which to calculate correlations";
params.add(new Param(TrailDays, tc, true, 252));
String mc = "the minimum correlation of pairs to consider";
params.add(new Param(MinCorrelation, mc, true, .75));
String zd = "trailing days over which to calc z-score";
params.add(new Param(ZScoreDays, zd, true, 21));
String mizs = "minimum z-score";
params.add(new Param(MinZScore, mizs, true, 1.5));
String mazs = "maximum z-score";
params.add(new Param(MaxZScore, mazs, true, 3.0));
String ew = "evenly-weight portfolio? (or weight by z-score)";
params.add(new Param(EvenlyWeight, ew, true, true));
Descriptor _desc = new Descriptor
("puppetmaster.strats.Pairs", _Desc, params);
return _desc;
}
public Pairs(Strategy strat, Descriptor d) { super(strat, d); }
Not the most interesting chunk of code, but pretty straightforward and its inclusion opens up a lot of functionality within the environment as each of these parameters can now be systematically or manually modified in-flight. The third parameter to the Param constructor, a boolean, indicates if we want to allow the system to optimize this parameter. Thus, the allocation/initialBalance parameter isn’t optimizable as it wouldn’t make any sense to do so. Even so, we can (ourselves or through an allocation algorithm) modify this value to force the strategy to lighten or extend its financial footprint. The others are fair game for optimization.
The constructor is also required – with that exact signature. This is pretty much it for the ‘boilerplate’ code required.
‘installing’ the stratpart
Now that we’ve met the minimum requirements for a stratpart, we can install it into stratbox. This requires adding one line to your personal stratparts.xml config file. Like so:
<?xml version='1.0'?> <stratParts> <descriptor class='puppetmaster.model.strategy.FwdWalker'/> <descriptor class='puppetmaster.model.strategy.StrategyPortfolio'/> <descriptor class='puppetmaster.strats.Pairs'/> <descriptor class='puppetmaster.strats.AMBO'/> <descriptor class='puppetmaster.strats.meta.AdjMeanReverter'/> <descriptor class='puppetmaster.strats.meta.AdjTrendFollower'/> <descriptor class='puppetmaster.strats.meta.Binary'/>
Once we’ve informed stratbox of the new stratpart we’ve written, we can view it within our library of stratparts within the strategy wizard in stratbox:

Our Pairs strategy in the Strategy Wizard's stratparts library
And all of the parameters we defined within our descriptor show up on the next page where we can parameterize or optimize the strategy:

Configuring the Pairs stratpart within the strategy wizard
This means that the stratpart can be combined with other stratparts to create new strategies as I’ve described and that it can partake in the rich set of functionality within the stratbox gui. Including actually trading. But first, we must make the strategy actually do something…
implementing event handling
To that end, we must implement the event handling methods I’d described previously. Since we’re not going to do any detailed execution handling in this strategy, we’ll only implement the quote() method and a subset of the events sent to the strategyEvent() method. In particular, we’ll listen to activation events (so we can initialize cleanly), changes to our descriptor and changes to our positions.
/** listen for select strategyEvents */
public void strategyEvent(StrategyEvent event) {
super.strategyEvent(event);
switch (event.type) {
case Activated: _init(); break;
case DescriptorChanged: _readDesc(); break;
case PositionChanged: _posnChange(event); break;
}
}
<pre>
For quotes, we’ll just listen to the Beginning of Day (BOD) event. If we wanted to trade intraday, we’d have to listen to tick events, but we’ll leave that for another day…
From a high-level, our code is pretty simple. We determine the universe of pairs we might trade, we select from among them, we determine what our new portfolio should look like and then we trade to transition from our current state to our desired state. That’s it.
/** mkt data goes here, but we only listen for the BOD
* ("Beginning of day") event */
public void quote(Quote q) {
if (q.type() != Quote.Type.BOD) return; // daily strat...
try {
List<_Pair> pairs = _getPairs();
List<_Pair> sel = _selectPairs(pairs);
List<Position> dp = (List<Position>)_desiredPfolio(pairs,sel);
_Log.debug("PORTFOLIO: "+dp);
_trade(dp);
} catch(Exception e) { _Log.error(e.getMessage(),e); }
}
the messy details
Digging into the details gets more complex, but not horrifically so, as stratbox already has basic analytics like correlation matrices, z-scores, volatility built into it. Likewise with basic portfolio analytics and transformations. Thus, our code to trade a (pairs-based) long-short portfolio with an arbitrary number of elements is surprisingly simple:
/** given a desired portfolio, trade to make it so */
void _trade(List<? extends Position> desiredFolio) {
PositionRecord[] currFolio = posns();
// Given our current portfolio (posns) and our desired state (dfolio),
// we generate the set of orders which will transform from the former
// to the latter
//
List<Order> orders = PortfolioComposer.TransformPortfolio
(currFolio, desiredFolio, _orderF(), _strat, _strat.account());
// place the orders for execution
for (Order order : orders) {
try {
order.setType(Order.Type.MOC); // we trade at the close only
_subscribe(order.contract());
_execP().placeOrder(order);
} catch (Exception e) { _Log.error(e.getMessage(), e); }
}
}
As promised, I provide the full source listing below for those interested, but won’t over burden the discussion with a complete description of every little piece of it.
a telling omission and some results
Early in my career, working as a software engineer in a wall st front office technology department, I was befriended by a manager who told me he had once been a trader. I asked him why he wasn’t a trader anymore. He quipped:
I was half of a talented trader. I knew, in my bones, when to get into a trade. Sadly, I was never really sure when to get out…
It seems that the author of the Palantir example suffers a similar characteristic to my old friend (or is just being understandably cagey) – she specifies an entry condition, but not an exit! Since she doesn’t impose any costs for trading, this has no big side effects, but in stratbox we do impose (pretty onerous -> realistic!) costs for trading.
In any case, since this is just a simple example I’ve maintained the ‘no exit’ policy and simply exit a trade once its pair goes out of the acceptable ranges for either correlation or z-score. This has a bad effect on the strategy as you end up churning a lot of trades at the high end of the z-score range. That is, if we set our upper boundary for z-score to be 3, then we might enter and exit a position multiple times as the z-score oscillates between, say, 2.9 and 3.1. A more careful implementation could minimize this effect.
Another difference imposed by our more realistic simulation as opposed to the original example is that I’m restricting the portfolio size explicitly and explicitly limiting myself to trading a given contract within one pair as opposed to allowing a contract to trade across multiple pairs (invoking higher costs but allowing positions to offset and thus providing a neat little optimization – assuming trading costs nothing!).
All the same, the strategy clearly has some positive characteristics and when parameterized to take advantage of its naive exit strategy (by making the z-score low-end quite low, e.g., 0.5) takes on the character of a hedged trend-following strategy. How so? Well, while we are getting churned around the high-end (entry) z-scores, our profitable exits only happen once the z-scores have diminished (the prices have converged) considerably. So we take more, smaller losses while our winners are held a bit longer.

some Pairs results in '10
Adding to my portfolio size improves things generally (and also increases the strategy’s scalability) while increasing the MinCorrelation parameter seems to hurt performance (not sure why, honestly – perhaps because if they’re very highly correlated and already diverging so strongly it may mean they’re actually diverging fundamentally). Returning to the topic of costs, each of these strategies incurred costs of over $5K for the period in question. And returning to the parameter which governs the weighting strategy employed, although I haven’t illustrated it here (as I haven’t looked at it very carefully), my initial finding is that it changed very little.
There’s a great many areas where this strategy could be improved – that’s the fun, after all! – but hopefully even in its primitive form this example brings to light both an interesting baseline portfolio pairs strategy, while answering the question of how one goes about concretely implementing it within stratbox.
The entire strategy weighs-in at ~400 LOC including comments, boilerplate and individualized imports. The source is here: Pairs.java.
Yet another fascinating post, I have been considering implementing pairs trading in my framework yet I seem to constrained by the design decision to hard link contracts to a given strategy (via a database). May I ask how this strategy ‘knows’ what its universe of stocks is?
Good question. This is all sort of hidden from view via the innocuous looking MemCube thingie. When the system starts up, it loads historical daily data for equities going back some configurable number of years. It sifts through all of it, trying to determine what instruments have a) sufficient data and b) sufficient volume so that a) back-testing makes sense and b) trading is possible. This winnows “the universe” down to a cube of some 1-2K instruments for some 5 years or so. The memcube also facilitates a variety of common analytic operations as can be seen in the code.
When not writing portfolio-oriented strategies, the contract(s) meant to be traded are passed-in as params. This has the felicitous (?) side-effect that one can now do data-mining exercises by “optimizing” the strategy across a potentially large set of contracts.
I’m afraid to ask how a strategy gets tied to a particular contract inside a database… but, undoubtedly due to the same wretched human impulse that causes otherwise ordinary folk to slow down and rubberneck at the sight of a highway crash, I must. :-/ How’d that happen? Any good side-effects? ;^>
It’s just a generic parent-child linkage between the strategy and its contacts. It’s more generic than some I’ve seen in terms of that’s it able to handle any number of configured contracts (or inputs), but it can’t deal with a changing set of inputs.
So the ‘memcube’ is some sort of in memory object that holds all a given set of contracts data within a certain date range?
Exactly.
Is the memcube per strategy or some kind of singleton?
There are a few default cubes that are constructed at initialization time for the container, like the daily one used here. These are singletons. One can also construct custom cubes.
Hello,
As far as I know we should look at cointegration and not correlation to pairs trade
Cheers
Raf