Trading the Envelopes — A Python Study.

Coding & Back-testing the Envelopes as a Trading Strategy.

Sofien Kaabar
Mar 3 · 8 min read

The concept that something is overbought or oversold is very known in Technical Analysis and yet it does not seem to always deliver something positive even though there are many good and stable strategies that deliver above average returns as we have seen in previous articles. This article will present the Envelopes Indicator and see how to create a trading system that uses it to generate trades.

If you are interested by market sentiment and how to model the sentiment of institutional traders, feel free to have a look at the below article:

The Concept of Moving Averages

Moving averages help us confirm and ride the trend. They are the most known technical indicator and this is because of their simplicity and their proven track record of adding value to the analyses. We can use them to find support and resistance levels, stops and targets, and to understand the underlying trend. This versatility makes them an indispensable tool in our trading arsenal.

EURUSD hourly values with the 200-period simple moving average.

As the name suggests, this is your plain simple mean that is used everywhere in statistics and basically any other part in our lives. It is simply the total values of the observations divided by the number of observations. Mathematically speaking, it can be written down as:

We can see that the moving average is providing decent dynamic support and resistance levels from where we can place our orders in case the market goes down there.

The code for the moving average can be written down as the following:

def ma(Data, lookback, what, where):

for i in range(len(Data)):
Data[i, where] = (Data[i - lookback + 1:i + 1, what].mean())

except IndexError:
return Data

To use it, we need to have an OHLC data array with an extra empty column. This can be done by using the following code:

# Defining the function that adds a certain 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# Adding 1 extra column
my_data = adder(my_data, 1)
# Calling the moving average function
my_data = ma(my_data, 200, 3, 4)

The above states that the moving average function will be called on the array named my_data for a lookback period of 200, on the column indexed at 3 (closing prices in an OHLC array). The moving average values will then be put in the column indexed at 4 which is the one we have added using the adder function.

USDCHF hourly values with the 200-period simple moving average.

Creating the Envelopes

The Envelopes are a set of moving averages modified to envelop prices in a way similar to the Bollinger Bands. They use a simpler formula however. We first start by calculating a simple moving average and then calculate a percentage deviation from it to give an envelope around the market price. For example, a 20-period moving average with a deviation of 1% will give us two lines, one above the moving average by 1% of its current value and the other below it by 1%.

Envelopes are usually used to find oversold/overbought levels from where contrarian trades can be initiated. And while it is obvious, I must insist that my aim is to back-test all indicators and a wide range of strategies hence, we might not always witness a profitable strategy in the articles which is a good thing because it serves as a demystification and detox from the mainstream courses that advocate easy gains on such simplistic techniques. Now, on with the formula of the Envelopes:

EURUSD with the 10-period Envelopes using a 0.25% deviation.
def envelopes(Data, lookback, deviation, what, where):    # Calculating the moving average
Data = ma(Data, lookback, what, where)
# Upper Envelope
Data[:, where + 1] = Data[:, where] + (Data[:, where] * deviation)
# Lower Envelope
Data[:, where + 2] = Data[:, where] - (Data[:, where] * deviation)

return Data
USDCHF with the 10-period Envelopes using a 0.25% deviation.

They do seem like the well-known Bollinger Bands. The above were 10-period Envelopes with a deviation of 0.25%. The code to generate the above data can be written down as:

# Calling the function on the closing price (index = 3) 
my_data = envelopes(my_data, 10, 0.0025, 3, 4)

If you are also interested by more technical indicators and using Python to create strategies, then my latest book may interest you:

Back-testing the Envelopes

As with any proper research method, the aim is to back-test the indicator and to be able to see for ourselves whether it is worth having as an add-on to our pre-existing trading framework. Note that the below only back-tests one time frame on only 10 currency pairs for the last 10 years. It is possible that this is not the optimal time frame for the strategy, but we are just trying to find a one-shoe-size-almost-fits-all strategy.

The conditions are as expected:

  • Go long (Buy) whenever the market reaches or breaches the lower envelope with the previous value being above the previous envelope.
  • Go short (Sell) whenever the market reaches or surpasses the upper envelope with the previous value being below the previous envelope.

We can write the signal function that generates trades based on the above conditions as follows:

def signal(Data, what, upper_band, lower_band, buy, sell):

for i in range(len(Data)):

if Data[i, what] < Data[i, lower_band] and Data[i - 1, what] > Data[i - 1, lower_band]:
Data[i, buy] = 1

if Data[i, what] > Data[i, upper_band] and Data[i - 1, what] < Data[i - 1, upper_band]:
Data[i, sell] = -1

We want to input 1’s in the column we call “buy” and -1 in the column we call “sell”. This later allows you to create a function that calculates the profit and loss by looping around these two columns and taking differences in the market price to find the profit and loss of a close-to-close strategy. Then you can use a risk management function that uses stops and profit orders.

The below is an example of a signal chart with the green arrows as the buy triggers and red arrows as the sell short triggers.

Signal chart on the EURUSD with the 10-period Envelopes using a 0.25% deviation.

The results are shown in the below plot:

Equity curves following the strategy.

The risk management used is 0.20 risk-reward ratio based on the Average True Range with 0.5 pips spread for every transaction.

We can see that there is an outperformer here called the EURCAD. It seems to be reacting nicely to an Envelopes strategy but a lot more research has to be done to be able to know for sure.

Equity curve with the Envelopes strategy applied on the EURCAD following the above conditions.

If you are interested in seeing more technical indicators and back-test, feel free to check out the below article:


If you regularly follow my articles, you will find that many of the indicators I develop or optimize have a high hit ratio and on average are profitable. This is mostly due to the risk management method I use. But what about market randomness and the fact that many underperformers blaming Technical Analysis for their failure?

First of all, I constantly publish my trading logs on Twitter before initiation and after initiation to show the results. This ensures transparency. I also publish a track record on Twitter every 1–3 months. However, I never guarantee a return nor superior skill whatsoever. As for the indicators that I develop, I constantly use them in my personal trading. Hence, I have no motive to publish biased research. My goal is to share back what I have learnt from the online community.

Remember to always do your back-tests. Even though I supply the indicator’s function (as opposed to just brag about it and say it is the holy grail and its function is a secret), you should always believe that other people are wrong. My indicators and style of trading works for me but maybe not for everybody. I rely on this rule:

The market price cannot be predicted or is very hard to be predicted more than 50% of the time. But market reactions can be predicted.

What the above quote means is that we can form a small zone around an area and say with some degree of confidence that the market price will show a reaction around that area. But we cannot really say that it will go down 4% from there, then test it again, and breakout on the third attempt to go to $103.85. The error term becomes exponentially higher because we are predicting over predictions.

While we are discussing this topic, I should point out a few things about my back-tests and articles:

  • The spread I use is based on institutional quotes of a small pip fraction. Generally, retail traders are given a whopping spread of 1–2 pips per trade. This is huge and unfair to them. I use 0.2–0.5 spread. However, most of the strategies that use the hourly time frame still work with 1 pip spread. For the ones that use M15 or M5 time frames, they cannot be profitable with a spread of 1 pip.
  • The holding period calculation I use is close-to-close in case there is no risk management process.
  • Although I discourage trading based on just one indicator, the numbers do not lie. What I am presenting is what could have happened when taking into account a low spread.
  • Some of the back-tests I provide are losers and they are published either to demystify a trading myth or to present interesting functions to be coded by readers.
  • Finally, I am a firm believer of not spoon-feeding the learners. I have learnt by doing and not by copying. You should get the idea, the function, the intuition, the conditions of the strategy, and then elaborate (an even better) one yourself so that you back-test it and improve it before deciding to take it live or to eliminate it.

To sum up, are the strategies I provide realistic? Yes, but with optimizing the environment (robust algorithm, low costs, honest broker, risk management). Are the strategies provided only for the sole use of trading? No, it is to stimulate brainstorming and getting more trading ideas as we are all sick of hearing about an oversold RSI as a reason to go short or a resistance being surpassed as a reason to go long.

The Startup

Get smarter at building your thing. Join The Startup’s +787K followers.

Sign up for Top 10 Stories

By The Startup

Get smarter at building your thing. Subscribe to receive The Startup's top 10 most read stories — delivered straight into your inbox, once a week. Take a look.

By signing up, you will create a Medium account if you don’t already have one. Review our Privacy Policy for more information about our privacy practices.

Check your inbox
Medium sent you an email at to complete your subscription.

Sofien Kaabar

Written by

Institutional FOREX Strategist & Trader. Author of “The Book of Back-tests”

The Startup

Get smarter at building your thing. Follow to join The Startup’s +8 million monthly readers & +787K followers.

Sofien Kaabar

Written by

Institutional FOREX Strategist & Trader. Author of “The Book of Back-tests”

The Startup

Get smarter at building your thing. Follow to join The Startup’s +8 million monthly readers & +787K followers.

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store