*This post was originally published by Sofien Kaabar at Medium [AI]*

## Developing & Back-testing a New Indicator Using Range and Volatility.

Contrarian indicators are numerous and there are unlimited ways to construct such indicators. The issue is profitability, consistency, and signal frequency. The first one is obviously related to an indicator that provides quality signals around market inflection points. It can never be accurate but if on average the good signals are slightly more than the bad ones, then with proper risk management, a good trading system can be created. The second issue is consistency which is related to profitability through time. Some strategies underperform during certain periods and outperforme during others. We are looking for a strategy that is relatively stable across time. Finally, the number of signals is important. It should have enough signals for us to evaluate it but not too much signals that make us pay a lot of transaction costs.

If you are interested in structured indicators, you can also read this article I have recently published on Medium:

# The Pendulum Indicator

The idea of the Pendulum Indicator is simple. It is a volatility-adjusted price range with a moving average to smoothe it out. The steps needed to create the Indicator can be summed be as follows:

**Select a lookback period to be used for the differencing and a lookback period to be used for the moving average. In our example, we will choose 13 and 5.****Calculate the difference between the current closing price and the closing price 13 periods ago.****Calculate the standard deviation of the latest 13 periods of the closing price.****Divide the result from the second step by the third step.****Calculate a 5-period moving average on the result from the last step.**

The way to code this Indicator is as follows:

`def pendulum_indicator(Data, lookback, lookback_ma, what, where):`

** # Price Range**

for i in range(len(Data)):

Data[i, where] = Data[i, what] – Data[i – lookback, what]

** # Volatility**

Data = volatility(Data, lookback, what, where + 1)

** # Pendulum Ratio**

Data[:, where + 2] = Data[:, where] / Data[:, where + 1]

** # Pendulum Indicator**

Data = ma(Data, lookback_ma, where + 2, where + 3)

** # Removing Excess Columns
** Data = deleter(Data, where, 3)

return Data

For the above function to work, we need an OHLC array containing a few spare columns and the deleter/jump functions. Here is how to do that:

**# To add a number of columns**
def adder(Data, times):

for i in range(1, times + 1):

z = np.zeros((len(Data), 1), dtype = float)

Data = np.append(Data, z, axis = 1)

return Data# To delete a number of columns starting from an indexdef deleter(Data, index, times):

for i in range(1, times + 1):

Data = np.delete(Data, index, axis = 1)

return Data# To delete a number of rowsdef jump(Data, jump):

Data = Data[jump:, ]

return Data

**# Moving average function**
def ma(Data, lookback, what, where):

for i in range(len(Data)):

try:

Data[i, where] = (Data[i – lookback + 1:i + 1, what].mean())

except IndexError:

pass

return Data

**# Volatility (Standard Deviation) function**
def volatility(Data, lookback, what, where):

for i in range(len(Data)):

try:

Data[i, where] = (Data[i – lookback + 1:i + 1, what].std())

except IndexError:

pass

return Data

# Back-testing the Strategy

As we do with contrarian Indicators, we will initiate long and short orders based on perceived extremes on the Pendulum Indicator. The conditions can therefore be:

**Go long (Buy) whenever the Pendulum Indicator reaches -3 with the previous two readings above -3. Hold this position until getting a new signal or getting stopped out by the risk management system.****Go short (Sell) whenever the Pendulum Indicator reaches 3 with the previous two readings below 3. Hold this position until getting a new signal or getting stopped out by the risk management system.**

`def signal(Data, what, buy, sell):`

for i in range(len(Data)):

if Data[i, what] < lower_barrier and Data[i – 1, what] > lower_barrier and Data[i – 2, what] > lower_barrier :

Data[i, buy] = 1

if Data[i, what] > upper_barrier and Data[i – 1, what] < upper_barrier and Data[i – 2, what] < upper_barrier :

Data[i, sell] = -1

The results on the AUDCAD pair show that it relatively adds value using a 0.25 ATR risk management with a spread of 0.5 pips per trade.

But of course, as all indicators out there, it can go both ways. The below is the equity curve on the EURUSD. We can see that it underperfoms.

The good thing is that plenty of optimization can be done on the lookback period, the moving average lookback period, and the oversold/overbought levels.

It seems that the best way to use the Pendulum Indicator in discretionary trading more than systematic trading with the default parameters.

# A Word on Risk Management

When I say I use ATR-based risk management system (Average True Range), it means that the algorithm will do the following steps with regards to the position it takes.

**A long (Buy) position:**

- The algorithm initiates a buy order after a signal has been generated following a certain strategy.
- Then, the algorithm will monitor the ticks and whenever the high equals a certain constant multiplied by ATR value at the time of the trade inception, an exit (at profit) order is initiated. Simultaneously, if a low equals a certain constant multiplied by ATR value at the time of the trade inception is seen, an exit (at loss) is initiated. The exit encountered first is naturally the taken event.

**A short (Sell) position:**

- The algorithm initiates a short sell order after a signal has been generated following a certain strategy.
- Then, the algorithm will monitor the ticks and whenever the low equals a certain constant multiplied by ATR value at the time of the trade inception, an exit (at profit) order is initiated. Simultaneously, if a high equals a certain constant multiplied by ATR value at the time of the trade inception is seen, an exit (at loss) is initiated. The exit encountered first is naturally the taken event.

The plot above shows the Average True Range I generally use. It is based on an exponential moving average as opposed to the original smoothed moving average.

Take a look at the latest value on the ATR. It is around 0.0014 (14 pips). If we initiate a buy order following a simple 2.00 risk-reward ratio (risking half of what we expect to gain), we can place an order this way:

**Buy at current market price.****Take profit at current market price + (2 x 14 pips).****Stop the position at current market price — (1 x 14 pips).**

# Conclusion

Why was this article written? It is certainly not a spoon-feeding method or the way to a profitable strategy. If you follow my articles, you will notice that I place more emphasize on **how to do it** instead of **here it is **and that I also provide functions not full replicable code. In the financial industry, you should combine the pieces yourself from other exogenous information and data, only then, will you master the art of research and trading.

I always advise you to do the proper back-tests and understand any risks relating to trading. For example, the above results are not very indicative as the spread we have used is very competitive and may be considered hard to constantly obtain in the retail trading world (but not impossible). However, with institutional bid/ask spreads, it may be possible to lower the costs such as that a systematic medium-frequency strategy starts being very profitable.

*This post was originally published by Sofien Kaabar at Medium [AI]*