Algorithmic trading is a field that’s generally quite daunting to beginners, forcing them to juggle learning advanced programming techniques and market mechanics. Throughout the process there’s usually not a lot of guidance, and even less coding examples. Our goal is to demystify this process and take you from beginner to quant with a handson lesson. We’ll program our own technical indicator and build a trading strategy on top of it. Along the way you’ll get a taste of the key tenants of quantitative trading methodology to be able to repeat the process all on your own.
We’ll be using the free and opensource Quantiacs Toolbox which supports both Python and MATLAB. The best part is having access to 15+ years of free historical market data for backtesting  we’ll definitely be taking advantage of that.
HeikinAshi
We’re going to build the famous HeikinAshi indicator. Here are the basics: HeikinAshi translates to “average bar” in Japanese, and provides traders with a way to isolate trends. The technique revolves around using two days of price data and combining them into one “averaged” candle. You can read up more on HeikinAshi
here.
Figure 1 – Original Candlestick Apple stock daily chart (chart from TradingView)
Figure 2 – HeikinAshi Candlesticks on Apple stock daily chart (chart from TradingView)
Often times HeikinAshi is a purely visual aid, and as you can see from the charts it smooths out the bars to emphasize trends with streaks of increasing/decreasing bars. To build our system, we’ll have to dive into the math behind the indicator. For any indicator this is usually well documented and can be found with a quick Google search. Here’s what we get for HeikinAshi:
HeikinAshi Candle Calculations
HA_Close = (Open + High + Low + Close) / 4
HA_Open = (previous HA_Open + previous HA_Close) / 2
HA_Low = minimum of Low, HA_Open, and HA_Close
HA_High = maximum of High, HA_Open, and HA_Close
One thing you may notice immediately is that the HeikinAshi Open price is a result of the previous HeikinAshi values. So when you’re first starting to calculate HeikinAshi, how do you obtain “previous” values? Well the standard solution is to do this on the first run:
HeikinAshi Calculations on First Run
HA_Close = (Open + High + Low + Close) / 4
HA_Open = (Open + Close) / 2
HA_Low = Low
HA_High = High
Let's Start Coding
Here’s what the process will look like:
1. Grab a default trading system template
2. Create a function to handle all the indicator math
3. Account for initial conditions
4. Create functions to handle trading logic and execution of trading positions
A good way to start is to copy the general framework from one of the
sample trading systems. Let’s clear out all the contents and just keep the empty skeleton. Starting with settings, we can keep the default markets, but we can set lookback to 11 since that’s all that’s needed for HeikinAshi.
Custom Function
From here we need to start defining our custom indicator. Since the actual math behind HeikinAshi is pretty straightforward to implement in both MATLAB and Python, we can write a separate function to carry out that math. Although we don’t yet have a clear picture of how we’ll incorporate the function, we do know that HeikinAshi requires the following to be calculated: open, high, low, close, previous HA_Open, and previous HA_Close. Naturally these become the input parameters for our function. With these input parameters, both HA_Open and HA_Close (from the math above) become very easy to calculate. Since HA_Low is the lowest value of all elements, and HA_High is the highest, we can compute both of these from one set of elements (High, Low, HA_Open, and HA_Close). Taking the minimum or maximum of that set of elements gives us the lowest and highest values respectively. Finally, since we have just one row of data for each variable, our function can return or output a matrix where each variable, such as HA_Close, is its own row.
MATLAB
function out = HEIKIN(O, H, L, C, oldO, oldC)
HA_Close = (O+H+L+C)/4;
HA_Open = (oldO + oldC)/2;
elements = [H; L; HA_Open; HA_Close];
HA_High = max(elements,[],1);
HA_Low = min(elements,[],1);
out = [HA_Close; HA_Open; HA_High; HA_Low];
end
Python
def HEIKIN(O, H, L, C, oldO, oldC):
HA_Close = (O + H + L + C)/4
HA_Open = (oldO + oldC)/2
elements = numpy.array([H, L, HA_Open, HA_Close])
HA_High = elements.max(0)
HA_Low = elements.min(0)
out = numpy.array([HA_Close, HA_Open, HA_High, HA_Low])
return out
Great, we’ve built a tidy function to do HeikinAshi calculations for us. Now let’s do a quick stop and make sure we fully understand what this function is doing. It takes input parameters that are all just a vector of 1 x Number of Markets (we’ll make sure that’s the case in initial conditions later), and averages them and finds maximums and minimums. For all of these operations we are working elementwise across the columns. For example, the max, is the max of each column in the elements matrix. Elements is just a stack of H, L etc. In each column, and each column represents a market, it looks for the highest or lowest values.

Markets 


Lumber 
S&P 500 
… 
HA_Close 
21219 
18000.63 
… 
HA_Open 
20996.25 
18130 
… 
HA_High 
21538 
18130 
… 
HA_Low 
20988 
17805 
… 
Initial Conditions
The next step is to figure out how our function ties into the whole system, and plan for initial conditions. A unique aspect of HeikinAshi is that we’ll have to initialize it across the Lookback period once. A great way to test whether the trading system is being run for the first time, is to look for any custom defined fields in the settings struct. Now we have to prepare our HeikinAshi values so that we can easily pass them into our function. Having the first HA_Close and HA_Open defined separately based on the first day of available data allows us to easily pass in these values into our function as oldC and oldO. Our initial and latest HA_Close and HA_Open values will be saved as custom fields in the settings struct.
Our trading logic also relies on knowing the previous HeikinAshi candles. In order for the trading logic to work on our initial run, we have to add a check to stop the HA_Close and HA_Open in from being set to the latest values within those initial conditions before reaching the trading logic. Lastly, we initialize an empty array
p for the first run because we’ll be saving each set of market positions to know when to exit.
MATLAB
% Check if initial run
if ~exist('settings.HA_close','var')
% Initial p vector, only need to define on first run
settings.lastP = zeros(1,numel(settings.markets));
% Initial Heikin Values
settings.HA_close = (OPEN(1,:) + HIGH(1,:) + LOW(1,:) + CLOSE(1,:))/4;
settings.HA_open = (OPEN(1,:) + CLOSE(1,:))/2;
% Run across lookback period, starting with 2nd row
for i=2:size(CLOSE,1)
HAmatrix = HEIKIN(OPEN(i,:),HIGH(i,:),LOW(i,:),CLOSE(i,:),settings.HA_open,settings.HA_close);
% To keep from running on latest value to use in trade logic
if i < size(CLOSE,1)
settings.HA_close = HAmatrix(1,:);
settings.HA_open = HAmatrix(2,:);
end
end
Python
def myTradingSystem(DATE, OPEN, HIGH, LOW, CLOSE, settings):
# Check if initial run
if ~hasattr(settings, 'HA_Close'):
nMarkets = CLOSE.shape[1]
nRows = CLOSE.shape[0]
# Initial p vector, only need to define on first run
settings['lastP'] = numpy.zeros(nMarkets)
# Initial Heikin Values
settings['HA_Close'] = (OPEN[0,]+HIGH[0,]+LOW[0,]+CLOSE[0,])/4
settings['HA_Open'] = (OPEN[0,]+CLOSE[0,])/2
# Run across lookback period, starting with 2nd row
for i in range(1,nRows):
HAmatrix = HEIKIN(OPEN[i,:],HIGH[i,:],LOW[i,:],CLOSE[i,:],settings['HA_Open'],settings['HA_Close'])
# To keep from running on the latest value to use in trade logic
if i < nRows1:
settings['HA_Close'] = HAmatrix[0,:]
settings['HA_Open'] = HAmatrix[1,:]
So most of our trading system is now completely defined. We check to see if this is the trading system’s first run, if so we run HeikinAshi across the lookback period. If not, we just iterate across the latest date with the HeikinAshi function. We now have a custom market indicator, the HeikinAshi, at our disposal. It’s the perfect template for putting in our trading logic.
Trading Strategy
For this trading system we have the following entry conditions:
Go Long (buy) if all of these are met:
Latest HeikinAshi candle is bearish
Previous HeikinAshi candle was also bearish
Latest HeikinAshi candle body is longer than the previous candle
Latest HeikinAshi candle has no upper wick
Go Short (sell) if all of these are met:
Latest HeikinAshi candle is bullish
Previous HeikinAshi candle was also bullish
Latest HeikinAshi candle body is longer than the previous candle
Latest HeikinAshi candle has no lower wick
Exit Conditions:
Same as an opposing entry signal except no latest HeikinAshi candle body can be smaller
Let’s stop and assess this trading strategy and what our likelihood of profit is. HeikinAshi is a delayed indicator, where each HeikinAshi candle is behind the market’s candles. This makes it great for minimizing noise and showing trends. However, this trading strategy is focused on reversing whatever HeikinAshi indicates. What’s more, it plans to do this based on just 2 candles – assuming that is enough confirmation for a trend. The underlying assumptions are that markets are very trendy, trends can be confirmed with just 2 daily candles, and that HeikinAshi’s lag means that trends it identifies will reverse fast. Immediately we can see this system is unlikely to be very profitable. However, this won’t discourage us from building it and seeing what the actual results are.
As for the code, we can create an array of Booleans for every entry/exit position. The reasoning behind making an array of them is to have each column represent one of the markets. Then the Boolean in a certain column represents whether a buying/selling condition has been met for that market. The input parameters for our function will just be the HeikinAshi matrix generated by our indicator function, and the previous HA values saved in the settings struct. Since the buy logic is almost the exact opposite of the sell logic, we really only need one set of Booleans. Similarly, since the exit logic is quite close to the entry logic, we can just reuse those Booleans there as well.
MATLAB
function out = TRADES(HA, oldO, oldC)
% Trading Logic  Naive Reversal from earnForex
%  Entry 
% Buying
% the latest completed HA candle is bearish, HA_close < HA_open
long1 = HA(1,:) < HA(2,:);
% body is longer than previous candle's body abs
long2 = abs(HA(1,:)  HA(2,:)) > abs(oldC  oldO);
% previous candle also bearish
long3 = oldC < oldO;
% latest candle has no upper wick HA_open == HA_high
long4 = HA(2,:) == HA(3,:);
long = long1 & long2 & long3 & long4;
% Selling
% latest candle is bullish
% body is longer than previous candle's body
% previous candle also bullish
% latest candle has no lower wick HA_open == HA_low
short4 = HA(2,:) == HA(4,:);
short = ~long1 & long2 & ~long3 & short4;
end
Python
def trades(HA, oldO, oldC):
# Heikin Ashi Reversal Strategy
#  Entry 
# Buying
# latest HA candle is bearish, HA_Close < HA_Open
long1 = HA[0,:] < HA[1,:]
# current candle body is longer than previous candle body
long2 = numpy.abs(HA[0,:]  HA[1,:]) > numpy.abs(oldC  oldO)
# previous candle was bearish
long3 = oldC < oldO
# latest candle has no upper wick HA_Open == HA_High
long4 = HA[1,:] == HA[2,:]
long = long1 & long2 & long3 & long4
# Selling
# latest candle bullish, previous candle bullish with smaller body
# latest candle has no lower wick HA_Open == HA_Low
short4 = HA[1,:] == HA[3,:]
short = ~long1 & long2 & ~long3 & short4
#  Exit 
# Exiting Long Positions  same conditions as short except for candle body
long_exit = ~long1 & ~long3 & short4
# Exiting Short Positions  same conditions as long except for candle body
short_exit = long1 & long3 & long4
out = numpy.array([long, short, long_exit, short_exit])
return out
Executing Positions
Now that we have our arrays of Booleans, we can execute our positions. In order to be able to close out long vs short positions, we’ll first separate our position array. From there we can simply execute long and short positions just by doing p(long) = 1 or p(short) = 1. This is why it’s advantageous to have separate long and short Boolean arrays. If you’re not quite sure of what
p is, I recommend taking a look at the
rundown of the Quantiacs Toolbox.
MATLAB
function out = EXECUTE_P(L, S, L_e, S_e, oldP)
% Splitting buy and sell from P
Pbought = oldP > 0;
Psold = oldP < 0;
% Close Long Positions
closebuy = Pbought & L_e;
oldP(closebuy) = 0;
% Close Short Positions
closesell = Psold & S_e;
oldP(closesell) = 0;
% Enter New Long Positions
oldP(L) = 1;
% Enter New Short Positions
oldP(S) = 1;
out = oldP;
end
Python
def executeP(L, S, L_e, S_e, oldP):
# Split buy and sell from p
Pbought = oldP > 0
Psold = oldP < 0
# Close Long Positions
closeBuy = Pbought & L_e
oldP[closeBuy] = 0
# Close Sort Positions
closeSell = Psold & S_e
oldP[closeSell] = 0
# Enter New Long Positions
oldP[L] = 1
# Enter New Short Positions
oldP[S] = 1
return oldP
Backtesting
You can find the entire HeikinAshi trading system code on the Quantiacs
GitHub. We can now run our trading system on the Quantiacs backtester to see how well it performs.
Python
python path/to/trading_system.py
Alternatively, you can also just hit run if you’re using the Anaconda IDE for Python.
MATLAB
runts(‘Trading System Name’)
Figure 3  HeikinAshi trading strategy performance against 44 futures
As expected, the results aren’t great because of the underlying assumptions in this particular strategy: that markets are very trendy, trends can be confirmed with just 2 daily candles, and that HeikinAshi’s lag means that trends it identifies will reverse fast.
CURVE FITTING & OPTIMIZATION
A common pitfall of quant strategy development is overfitting. A curve fit strategy is one that’s been optimized so well, it perfectly fits the past performance of the markets. The end result is that it will completely fail with future price action and market events. Overfitting will produce fantastic backtesting results from unrealistic and unprofitable trading strategies. Going back to our unsuccessful HeikinAshi strategy, we can demonstrate the dangers of overfitting. Since our trading system doesn’t have any modifiable input parameters, we can optimize the time period and the equities/futures it trades on. Essentially, we can arbitrarily fit HeikinAshi to a certain time period and certain markets to extract desirable performance numbers.
Here’s what that looks like:
Figure 4  Curvefit one year returns from HeikinAshi on the symbols BAC, UNH, TWX
Wow a Sharpe Ratio of 4! All of a sudden our trading strategy goes from a complete failure on backtests to an amazing winner. Of course there are endless caveats. This “optimized” strategy would never work in the real world. The moment the start date of the backtest is moved out by a few years, all the perceived market edge evaporates. Arbitrarily hunting for good backtesting results is a dangerous practice and won’t produce truly profitable strategies. However, the backtest above can be approached as a learning lesson. For example, what sort of prevailing market conditions allowed the HeikinAshi to have a temporary edge? Were the markets trending together? Was there a consensus among market experts? This sort of investigation is a valid way to rationalize and approach trading strategy development.
One thing to point out is that optimization itself is perfectly fine, in fact there are entire books written on this topic alone. In general, optimization should be approached as a way to filter out some additional noise from an already profitable strategy.
A few pointers to help avoid overfitting a trading strategy:
· Use in sample and out of sample for backtesting your strategy. This merely means backtest over 2/3 of available historical data, do some optimization, then test your optimized strategy on the remaining 1/3 of your historical data.
· Avoid putting in knowledge of future market events. This means not stopping your strategy from trading during the 2008 recession, or using Apple as the stock for a longonly strategy since we already know it’s historically a great choice for buying and holding.
· Use walk forward analysis. This is a bit more complex but involves testing your strategy over a small period of time, optimizing it, then testing it on another equal period of time that’s out of sample. The intent is to mimic your own behavior in trading the strategy. There’s even a walk forward analysis toolbox for MATLAB.
This should provide a good framework for you to apply any sort of trading logic to HeikinAshi. A Google search will provide a multitude of alternative trading systems using HeikinAshi that will probably perform a bit better. Since we were able to create the code behind one of the more complex technical indicators, you should now feel comfortable enough to implement almost any technical indicator just by finding the math behind it and converting it into its own function.
If you have any feedback, follow ups, or inquiries feel free to contact me: denis@quantiacs.com