Simulating stock trading

Note: this is experimental functionality at this stage

Given a signal for a set of companies, one may wonder, whether there historically has been a connection between that signal and stock market returns. To investigate that question, you can simulate a simple trading strategy based on that signal. Note that this calculation does not take into account execution costs, and is thus only looking at the connection between the signal and share price movements, not trying to show the realized returns from actual trading.

First, a company group must be created with the companies that should be part of the portfolio. This can be done either by creating a tag for the companies (recommended), or by listing each company in the DSL. See the Retrieving companies section for information on how to do this.

Then, two functions, long_short and allocate, are available on the company group object to specify the trading strategy:

class CompanyGroup
long_short(score, n_long, n_short, market_neutral, scale)

Create a long-short strategy for the companies in this group based on the given signal. The score signal is evaluated for all of the companies every day, and the companies ranked from highest to lowest value. A long position is taken in the n_long highest scoring companies and a short position in the n_short lowest scoring companies. If n_long is not the same number as n_short, and market_neutral is True, an additional long or short position is taken in all the companies in the group to make the overall position sum to zero. The return is then calculated for this allocation for the following day.

Parameters
  • score – the signal to use for ranking the companies in this group.

  • n_long – the number of long positions to take.

  • n_short – the number of short positions to take. If not provided, defaults to being equal to n_long.

  • market_neutral – whether to subtract out the mean of the allocations. The default value is True.

  • scale – whether to scale the allocations so that no more than a unit position is taken in positive or negative direction. The default value is True.

Returns

The accumulated returns of the trading strategy over the requested time period.

allocate(score, market_neutral, scale)

Allocate share positions to the companies in this group based on the given signal. The score signal is evaluated for all of the companies every day, and a position is taken in proportion to the score.

Parameters
  • score – the signal to use for ranking the companies in this group.

  • market_neutral – whether to subtract out the mean of the allocations. The default value is True.

  • scale – whether to scale the allocations so that no more than a unit position is taken in positive or negative direction. The default value is True.

Returns

The accumulated returns of the trading strategy over the requested time period.

Examples

First, create a tag called “Fashion” consisting of some companies in the fashion industry (or any other industry you would like to try).

This expression calculates the close price over the past ninety days divided by the close price from the same period a year ago. Let us store this as the signal ‘close_price_momentum’:

close_price().moving_average(90)/close_price().moving_average(90).delay(days=365)

Let us now set up a trading strategy which makes allocations to these companies stock in proportion to their score on this metric:

tag("Fashion").allocate(close_price_momentum)

This signal can now be plotted in the Signal Explorer, and shows the accumulated return on investing according to that strategy.

Note that the above strategy by default is “market neutral”, thus a long-short strategy. To simulate a long-only strategy on the same signal, where allocations are made in proportion to the close_price_momentum:

tag("Fashion").allocate(close_price_momentum, market_neutral=False)

The above strategies make allocations proportionally to the score. To rather simulate a strategy where $0.333 is allocated to each of the top three ranking companies and -$0.333 is allocated to each of the bottom three ranking companies:

tag("Fashion").long_short(close_price_momentum, 3)

Or, to allocate $0.333 to each of the top three ranking companies, and a small negative allocation to all the rest to achieve market neutral total position:

tag("Fashion").long_short(close_price_momentum, 3, 0)
Signal Explorer plot

The accumulated returns from simulating a basic trading strategy based on close price YoY increase for a set of 25 companies in the fashion industry. In this particular example, taking $1 in long positions and $1 in short positions each day according to this strategy would have generated returns of $1.2 in the period from April 2016 to August 2019 (not accounting for execution costs).