Login Register FREE Account

Creating an Algo Trading Strategy in MATLAB

matlab algo tradingThis article describes how to develop an algorithmic trading strategy in MATLAB. The TA Developer toolbox is used to visualize stock data and perform a backtest over a portfolio of stocks. A band trading strategy is developed which enters long trades when prices fall below the lower band and short trades are entered when prices rise above the upper band. The strategy is backtested over 10 years of historical data. In particular, a portfolio backtest over the data of 11 technology stocks is performed. The execution results are displayed by the TA Developer toolbox in form of a statistics summary page, a detailed trade list, and an equity curve. The statistic summary shows statistics like the number of winning and losing trades as well as common trading metrics like the Sharpe and Sortino Ratios.

 

 

 

Requirements
  • MATLAB
  • Microsoft .NET 4.0 or higher
  • TA Developer Toolbox

It is assumed that you have read the following articles:

 

 

Band Trading

The trading strategy we are going to create will use a trading band. A band consists of two lines that form the upper and lower boundaries of the band. The upper and lower boundaries can be used to enter and exit trades. For example, if prices fall below the lower boundary a buy signal is generated. When prices return to the area within the band a sell signal is generated. There are a number of different ways how to construct these bands. In general, bands are used to detect short term breakouts and are used in mean reversion systems. Mean reversion means that it is assumed that the breakout is a short term anomaly and that prices will return to the mean in the long term.

Matlab Band Trading Strategy

 

 

Creating a new strategy m-file

A trading strategy consists of a MATLAB function with a single parameter called sys. The sys parameter contains trading system data like the open, high, low and close prices of a stock or a future.

 

Start by creating a MATLAB function with the name BandTrader.m

>> edit BandTrader.m

The function should look like this:

function BandTrader(sys)

end

This empty function will serve as starting point for our band trading strategy.

 

 

Creating the Bands

In order to create the bands, two variables are needed: a centerline and a distance from the centerline. Dr R Koch describes in his article "Creating Your Own Trading System" [1] a variation of techniques that can be used to create these variables. The following sections give a brief summary of these techniques. Please refer to the original article for a detailed discussion.

 

THE CENTER LINE

There are a number of ways to calculate the center line.

  • Average Price: Avg = (High + Low) / 2
  • Average of high, low, and close: AvgC = (High + Low + Close) / 3
  • A weighted mean of open, high, low, and close: AvgP = (w1*Open + w2 *High + w3*Low + w4*Close) / (w1+w2+w3+w4)

These averages can be very noisy so it is recommended to smooth the center line using a moving average. The TA Developer toolbox has the following moving averages built in:

  • Sma: Simple Moving Average
  • Ema: Exponential Moving Average
  • Wma: Weighted Moving Average

The following code example creates a center line using the average price. This average price is smoothed by an exponential moving average using a lookback period of 10 bars. In addition, the centerline is plotted to the price chart using the PlotDataSeries function.

% Construct the center line
Average = (sys.High + sys.Low)/2;
CenterLine = Ema(Average, 10);
PlotDataSeries(sys, CenterLine, 'blue', 'solid', 2, '');

For more information about the PlotDataSeries function type 'help PlotDataSeries' in the MATLAB command prompt.

 

DISTANCE

Similar to the center line there are a number of ways to determine the distance of the bands. A simple way to create the bands is the use a constant distance from the center line. More advanced approaches are based on price volatility so that the bands get wider during volatile times and more narrow during less volatile times. The following techniques can be used as an estimate for volatility:

  • Moving average of the Range (High-Low)
  • Moving average of the True Range (The True Range uses the previous Close if outside the High or Low price for the range calculation)
  • Standard deviation of the Close

In our example we use the built in Atr (Average True Range) indicator to determine the distance of the bands.

% Construct the bands
AverageTrueRange = Atr(sys.BarData, 10); % Moving average period of 10
BandWidth = 2;
UpperBand = CenterLine + BandWidth * AverageTrueRange;
LowerBand = CenterLine - BandWidth * AverageTrueRange;
PlotDataSeries(sys, UpperBand, 'magenta', 'solid', 2, '');
PlotDataSeries(sys, LowerBand, 'magenta', 'solid', 2, '');

A popular variation of the technique is the Bollinger Band. The Bollinger Bands use a 20 period simple moving average (SMA) as the centerline and the distance is a multiple of the standard deviation. The TA Developer toobox has the following Bollinger Band technical indicator built in: BBandLower, BBandMiddle, BBandUpper. To see how these indicators work, you can type e.g. 'help BBandMiddle' into the MATLAB command prompt or drag&drop the indicator from the indicator window into the price chart area.

 

 

Entry and Exit Rules

After constructing the bands we can use the upper and lower boundaries to create trading signals. We want to create a long entry signal when the close price falls below the lower band and we want to create a short entry signal when the close price rises above the upper band.

% Entry and Exit Signals
LongSignal = sys.Close < LowerBand;
ShortSignal = sys.Close > UpperBand;
AddLongSignal(sys, LongSignal);
AddShortSignal(sys, ShortSignal);

The AddLongSignal and AddShortSignal functions use the information of the LongSignal and ShortSignal time series to create trade entries and exits. For example, LongSignal contains zeros (representing 'false') if the close price is greater or equal than the LowerBand. LongSignal contains ones (representing 'true') when the close price is smaller than the lower band. If the signal is one (or 'true') the TA Developer toolbox creates an trade entry and if the signal is zero (or 'false') the toolbox creates a trade exit. Trades are executed at the open price of the next bar to avoid a look ahead bias. E.g. if the close price falls below the lower band on the 1st of March, the trade is entered on the 2nd of March using the open price.

In our example the same signal time series is used for trade entries and exits i.e. enter when the close falls below the lower band and exit when the close price rises above the lower band. It is also possible to specify separate entry and exit signals. For example you could enter a long trade when the price falls below the lower band and hold the position until the price rises above the center line (instead of the lower band used before).

% Example for separate entry and exit signals
LongEntry = sys.Close < LowerBand;
LongExit = sys.Close > CenterLine;
AddLongSignal(sys, LongEntry, LongExit);

 

 

Using Strategy Trading Parameters

When a strategy backtest is performed on the MATLAB command prompt, trading parameters can be passed to the strategy. This way parameter optimizations can be performed easily. The trading parameters are stored MATLAB variable of type 'containers.Map'. This map contains key value pairs with the parameter name as a key and a value of type double. In our strategy example above, we have hardcoded the period of the center line and average true range moving averages to 10 and the band width to 2. In order to optimize these parameters later (see section parameter optimization) we need to read the parameter values before the strategy is executed. First it is checked if the parameter is set in the map using the 'isKey' function. If the parameter is not set, we use a default value of 10 for the moving average and 2 for the band width.

% custom trading parameters
if sys.TradingParameters.isKey('movingAverage'),
movingAverage = sys.TradingParameters('movingAverage');
else
movingAverage = 10; % default value if unset
end;
if sys.TradingParameters.isKey('bandWidth'),
bandWidth = sys.TradingParameters('bandWidth');
else
bandWidth = 2; % default value if unset
end;

 

 

Putting It All Together

In the previous sections we have explored the steps needed to create a band trading strategy. The following function combines all steps into a trading strategy.

function BandTrader(sys)
% custom trading parameters
if sys.TradingParameters.isKey('movingAverage'),
movingAverage = sys.TradingParameters('movingAverage');
else
movingAverage = 10; % default value if unset
end;
if sys.TradingParameters.isKey('bandWidth'),
bandWidth = sys.TradingParameters('bandWidth');
else
bandWidth = 2; % default value if unset
end;

% Construct the center line
Average = (sys.High + sys.Low)/2;
CenterLine = Ema(Average, movingAverage);
PlotDataSeries(sys, CenterLine, 'blue', 'solid', 2, '');

% Construct the bands
AverageTrueRange = Atr(sys.BarData, movingAverage);
UpperBand = CenterLine + bandWidth * AverageTrueRange;
LowerBand = CenterLine - bandWidth * AverageTrueRange;
PlotDataSeries(sys, UpperBand, 'magenta', 'solid', 2, '');
PlotDataSeries(sys, LowerBand, 'magenta', 'solid', 2, '');

% Entry and Exit Signals
LongSignal = sys.Close < LowerBand;
ShortSignal = sys.Close > UpperBand;
AddLongSignal(sys, LongSignal);
AddShortSignal(sys, ShortSignal);
end

 

 

Backtesting the Strategy

Type 'tadeveloper' into the MATLAB command prompt to open up the TA Developer graphical user interface. Click File>Open from the menu and browse to the location where you saved the BandTrader.m strategy and open the file.

Before executing the strategy, we need to set a few parameters first. In the bottom right corner is a window called Properties. This window contains important execution parameters. We will set the starting capital for the simulation to 100000. The position type is set to 'Percent' and the position amount is set to 10 which means that 10% of the available capital is used per trade. Raw Profit Mode is set to false. If Raw Profit Mode was set to true, the amount of available capital would be ignored. With Raw Profit Mode set to false, trades will NOT be entered in our simulation if not enough capital is available to enter the trade.

 

Matlab Trading Parameters

 

 

Make sure the watchlist root node (called TechnologyStocks from our previous example in the 'Getting Started Guide') is selected in the Symbols window. You should see the backtest panel. Press the green Play button the start the simulation.

 

Matlab Trading Strategy Backtest Panel

 

 

Strategy Performance Evaluation

When the strategy has executed successfully, the 'Statistics' tab becomes available. It shows various trading metrics like the Annualized Return, Sharpe Ratio, Sortino Ratio, Ulcer Index, Number of Trades and many more. These metrics are divided into 'All' (for all simulated trades), 'Long' (long trades only) and 'Short' (short trades only). In addition, the metrics for the Buy&Hold strategy are shown to allow an easy comparison.

Matlab backtest trading metrics

 

 

Running the Strategy from the Command Line

It is also possible to run the strategy simulation from the MATLAB command prompt. This is for example useful if you want to automate the simulation or want to perform a parameter optimization. In order to execute the strategy, you need to set the trading parameters first. By selecting 'Generate Code' in the File menu all the necessary code is generated. After clicking 'Generate Code' copy the generated source code into a file called 'runBandTrader.m'. The file contents should look similar to this:

function systemStatistics=runBandTrader(tradingParameters)
%RUNBANDTRADER
% Runs the BandTrader strategy. Auto-generated by the TA Developer toolbox.

% Optional user defined trading parameters
if ~exist('tradingParameters', 'var'),
% create parameter map if not passed as a function parameter
tradingParameters = containers.Map('KeyType', 'char', 'ValueType', 'double');
% add your own trading parameters here
% e.g. tradingParameters('trueRangePeriod') = 5;
end

% System properties used for the strategy execution
properties=SystemProperties;
% Advanced
properties.DecimalPlaces=2;
properties.RoundPosition=0;
properties.TickRoundingOff=0;
properties.WorstProfit=0;
% Backtesting
properties.BeginDate=[];
properties.EndDate=[];
% Commission and Slippage
properties.CommissionAmount=0;
properties.CommissionType='PerTrade';
properties.SlippageAmount=0;
properties.SlippageType='Percent';
% Interest Rates
properties.CashInterest=0;
properties.MarginInterest=0;
% Money Management
properties.MarginMultiplier=1;
properties.PositionAmount=10;
properties.PositionType='Percent';
properties.RawProfitMode=0;
properties.StartingCapital=100000;

% Execute the strategy
strategyPath='C:\TradingScripts\BandTrader.m';
watchlist='TechnologyStocks';
symbol=''; % leave empty to execute over all symbols in watchlist
systemStatistics=tadeveloper('simulate', strategyPath, watchlist, symbol, properties, tradingParameters);

end

In order to execute the strategy just run the generated 'runBandTrader' function. All metrics are available in the systemStatistics object. Use the name as displayed in the 'Performance Evaluation' section above the access the values.

>> systemStatistics=runBandTrader;
>> systemStatistics.All.Metrics('Sharpe Ratio')
ans =
0.7979

>> systemStatistics.All.Metrics('Annualized Return (%)')
ans =
1.9110

 

 

Parameter Optimization

Earlier we created trading parameters called 'movingAverage' and 'bandWidth' in the trading strategy. The movingAverage parameter is used as the lookback period to smoothen the moving averages and the band width parameter is used as a multiplier in calculation of the distance between the center line and the bands. So for we used default values of 10 for the moving average and 2 for the band width. The default values have been chosen more or less arbitrarily. In order to see if increasing or decreasing these values improves the trading strategy, you could simply change these values and re-run the strategy. The following script automates this task by setting the parameters 'movingAverage' and 'bandWidth' to a range of different combinations to see which parameter settings work best. In this example the Sharpe Ratio metric is used to evaluate how a strategy performed. This can be changed to any metric you are interest in.

function sharpeMatrix=optimizeBandTrader()

tradingParameters = containers.Map('KeyType', 'char', 'ValueType', 'double');
sharpeMatrix = NaN(21,21);
maxSharpeRatio = 0;
maxMovingAverage = 0;
maxBandWidth = 0;
x=1;
y=1;
% optimize parameters
for movingAverage=3:23,
x=1;
for bandWidth=0.5:0.1:2.5,
tradingParameters('movingAverage') = movingAverage;
tradingParameters('bandWidth') = bandWidth;
systemStatistics=runBandTrader(tradingParameters);
sharpeRatio = systemStatistics.All.Metrics('Sharpe Ratio');
sharpeMatrix(y,x) = sharpeRatio;
disp(['Sharpe: ' num2str(sharpeRatio) ' Moving Average: ' num2str(movingAverage) ' Band Width: ' num2str(bandWidth)]);
if sharpeRatio > maxSharpeRatio,
maxSharpeRatio = sharpeRatio;
maxMovingAverage = movingAverage;
maxBandWidth = bandWidth;
end
x=x+1;
end
y=y+1;
end

% display results
disp(['Highest Sharpe: ' num2str(maxSharpeRatio) ' Moving Average: ' num2str(maxMovingAverage) ' Band Width: ' num2str(maxBandWidth)]);

% create 3-D colored surface/contour plot
[xgrid,ygrid] = meshgrid(0.5:0.1:2.5, 3:23);
surfc(xgrid, ygrid, sharpeMatrix);
title('Sharpe Ratio Surface/Contour Plot');
xlabel('Band Width');
ylabel('Moving Average');

end

By running the script all parameter combinations are executed and at the end the parameters which lead to the highest Sharpe Ratio are shown. In our example a moving average of 7 and a band width of 1.9 lead to the highest Sharpe Ratio. In addition a surface plot is created which shows the distribution of Sharpe Ratios in relation to the trading parameters.

>> optimizeBandTrader;
...
Highest Sharpe: 1.1293 Moving Average: 7 Band Width: 1.9

Matlab trading parameter optimization

 

 

Commission and Slippage

The simulation we performed so far did not consider trading costs or slippage. Trading costs are for example transaction fees you need to pay your broker and slippage is the difference in price between your desired trade price and the price you actually pay in the market. For example at market open you might not be able to enter a trade at the exact (historical) opening price.

Broker commissions can be simulated by the TA Developer toolbox by setting the commission fields. Possible commission types are:

  • Percent: Commission amount is interpreted as percentage e.g. 1 means 1 percent of the trade value
  • PerShare: Commission amount is interpreted as dollar per share e.g. 1 means 1 dollar per share
  • PerTrade: Commission amount is interpreted as dollar per trade e.g. 1 means 1 dollar per trade

The slippage type can have the following settings:

  • Percent: Slippage amount is interpreted as percentage e.g. 0.1 means the trade entry price is adversily adjusted by 0.1% of the trade value
  • Tick: Commission amount is interpreted as number of ticks e.g. 1 means to adjust the entry price by $0.01 (assuming the tick size for the product is $0.01)

Commission and Slippage can be set in the properties window and can be used to simulate costs incurred during trading. It is recommended to set these parameters to sensible values to make the backtest more realistic.

 

 

Backtesting Pittfalls

This article showed how to create and backtest an algorithmic trading strategy. To keep the data acquisition simple data of 11 highly successful technology stocks was used. These stocks were selected with the benefit of hindsight. 10 years ago it would be very hard to predict the success of these stocks. To create a more realistic backtest you need to add all the components of a stock index (e.g. S&P 500) to the backtest. In the TA Developer toolbox this simply means adding more stocks to the watchlist.

In order to avoid survivorship bias you should even include data of stocks that have been delisted during the simulation period. Alternatively you can focus your backtest on more recent data to reduce the impact of delisted stocks. For a detailed discussion of survivorship bias and other backtesting pitfalls please refer to Dr E P Chan's book 'Quantitative Trading: How to Build Your Own Algorithmic Trading Business' [2].

 

 

Trade Execution

For information about sending trades directly from MATLAB to Interactive Brokers please see the IB-Matlab toolbox.

 

 

References

 

[1] Koch, R. (2004) Creating Your Own Trading System, Technical Analysis of Stocks and Commodities

[2] Chan, E. P. (2008) Quantitative Trading: How to Build Your Own Algorithmic Trading Business, Wiley