Home Creators Posts Import Register Favorites Logout
haven't archived this post yet. have a subscription? use the importer!

Downloads

  • 57.macd_obos_futures.7z
  • 57.macd_obos_spot.7z
  • Macd_obos_patreon_notebook.7z

Missing 3 files, as this post has not yet been imported.

Content

Introduction

In this blog post, I will do something that I wanted to do for a long time. This time I will make an automatic algorithmic trading strategy out of the instructions that were given in a Youtube video. And in this case spesifically the video on the MACD OBOS (overbough / oversold) strategy. The link of that video will also be in the description:

Video setup

In this video I will explain again first how this strategy works and which remarks I have to this strategy. Then I will show how I coded the strategy into a Pandas dataframe in preparation to the actual code I used with my trading bot. With that code I can automatically backtest this strategies performance.

This automatic backtest will be on my setup so that I can compare these results with the performance of other strategies which I have tested in earlier video's. And with all these tests and comparisons I can make a well grounded assumption that this strategy might be profitable for real trading.

Trading strategy

Now, before I continue, the original video's, I base this code on, do not mention anything about a specific timeframe or pair to trade. Just that it can be generally used. So here I have some freedom to test it on my own setup to find out in these statements are really true.

So, lets start with the strategy that is proposed in the MACD OBOS video.

Long trading

In that Youtube video the rules for long trading the strategy are as follows:

  • Use the 200 EMA as a baseline. If price is above the EMA, then only long trades are allowed.
  • For a long enter, the MACD should cross above it's signal line. But this crossover should be below the horizontal 0-line.
  • The Stochastics oscillator is the second confirmation indicator. The long trade should only be made if the stochastics has been oversold below the 25% line in the near past. In the video there is only spoken of a return from oversold conditions if both lines have returned to levels above 25.
  • The final signal is given with the 12 EMA. If the price is also above that EMA line, then there is a final confirmation and the trade can be taken.

Short trading

For short trading, these signals are reversed.

  • If the price is below the 200 EMA baseline then only short trades are allowed.
  • The first signal occurs when the MACD crosses below its signal line. But it is only valid if this occurs above the 0-line.
  • Then the Stochastics oscillator should return from the overbought condition. And again I have to make some of my own assumptions here how my exact signal is used because in that video a return from overbought is defined as the two stochastics lines that return from above the 75% level.
  • And finally, the last confirmation here is when price is of gets below the 12 EMA.

Take profit and stop loss

The video explains that the stop loss point should be set below the last swing low. But the actual take profit points are unclear to me. Maybe I missed something in these videos. But, as far as I know, there is not much explained about this.

No exact risk to reward setting, no exit indicator or anything else. Except that with each example, the profit point is taken exactly at the next swing high.

And if you already have some exerience at trading, than you know that there is no way to predict the exact swing high point in trading. So maybe it is presented a little bit too optimistic here.

The benefit is that this will give me the liberty to determine myself how to determine the stop loss and take profit settings. And you have to be aware that is a deviation from the proposed strategy due to a lack of information.

Coding the strategy

Next I will explain a little bit how to create the trading signals in a pandas dataframe with the help of the given indicators.

Now I will not get into detail on how to create the actual code for the backtest engine of the trading bot, since this will make the video way too long.

However I will leave that code on my Patreon side as a downloadable file, together with the backtest results. So if you want to use it for your own setup, then become a Patron and experience the additional benefits there.

# In this code I will make use of Pandas_ta. If you have not installed this module, then download it with the following command:

!pip install pandas_ta

Next I will import the necessary modules so that I can use them with Pandas and Python

import pandas as pd
from pandas import DataFrame
import numpy as np
import pandas_ta as pta

After importing the libraries, its time to load the Bitcoin prices into a dataframe.

For that I will use a downloaded json file with the bitcoin prices.

df = pd.read_json("./BTC_USDT-1d.json")

Now its time to name the columns correctly and change the date into human readable form

df.columns=['date','open','high','low','close','volume']
df['date']=(pd.to_datetime(df['date'],unit='ms'))
df.head()

Now it's time to add the indicators to the dataframe. In this strategy the EMA200, EMA12, Stochastics oscillator and MACD are used. The MACD has a setting of 12, 26, 9 and the stochastics has a setting of 12, 6, 3.

In the original video the stochastics indicators has specific settings that matches the MACD indicator. So I'll add these corrections here as well. And ofcourse you can watch the original video on why these settings are used here.

[see the video at this point for the settings]https://youtu.be/CYym2Ghuw10?si=l5oNNfQQu2A4Cden&t=368)

Let's first add the MACD indicator to the dataframe

In this case I want to show the output of the pure pandas macd signal to the notebook here

pta.macd(close=df['close'], fast=None, slow=None, signal=None, offset=None).tail(5)

From this output I can dertermine now the actual columns outputs this command gives.

Each column from this output can now be added to my dataframe.

​df['macd'] = pta.macd(close=df['close'], fast=None, slow=None, signal=None, offset=None)['MACD_12_26_9']
df['macs'] = pta.macd(close=df['close'], fast=None, slow=None, signal=None, offset=None)['MACDs_12_26_9']
df['macdh'] = pta.macd(close=df['close'], fast=None, slow=None, signal=None, offset=None)['MACDh_12_26_9']
df

Great, Now I have the MACD in my dataframe. Let's also do this for the stochastics oscillator. But here I am also going to change some indicator values.

First the raw output of the Pandas_Ta indicator. And notive I've changed the base indicator values here.

pta.stoch(high=df['high'], low=df['low'], close=df['close'], k=12, d=6, smooth_k=3, offset=None)

Now I can add these stochastics indicator outputs to the actual dataframe.

​df['stochk']=pta.stoch(high=df['high'], low=df['low'], close=df['close'], k=12, d=6, smooth_k=3, offset=None)['STOCHk_12_6_3']
df['stochd']=pta.stoch(high=df['high'], low=df['low'], close=df['close'], k=12, d=6, smooth_k=3, offset=None)['STOCHd_12_6_3']
df.tail(15)

Now I only have to add the EMA200 and EMA12 indicator and I'm all set to go to create the signals for long and short trading.

df['ema200'] = pta.ema(df["close"], length=200)
df['ema12'] = pta.ema(df["close"], length=12)
df.tail(20)

The easiest signals to create are that of the EMA indicators.

I'll create additional columns based on the position of the price and these indicators first.

First I am going to create basic long/short position columns based on the EMA200 and then a signal column based on the shorter timerange EMA12.

The 200ema_long signal is True when close price is above the ema200. And vice versa for the short signal.

Two columns that indicate if there should be long or short trades.

df['ema200_long'] = df['close'] > df['ema200']
df['ema200_short'] = df['close'] < df['ema200']
df[['date','close','ema200','ema200_long','ema200_short']].tail(5)

The next two columns are indicators to determine if price is above or below the EMA12 indicator. I'll use the same principles here. So long if price is above 12ema and short if price is below 12ema.

Two columns that indicate if there should be long or short trades.

df['ema12_long'] = df['close'] > df['ema12']
df['ema12_short'] = df['close'] < df['ema12']
df[['date','close','ema12','ema12_long','ema12_short']].tail(50)

Now come the more difficult signals to create.

As I told earlier, the long MACD signal occurs when there is a bullish macd crossover above it's signal line. This has to happen below the 0 line. And vice versa for a short MACD signal.

Also the stochastics signal for longs is valid if the stochastics was first oversold under the 25% level but now in neutral terrain, above the 25% line. And again for shorts this is the reverse. Only go short if stochastics was overbought above 75% and now has returned below the 75% line.

Let me show you how I solved this...

MACD signals

To create a MACD signal for long and short trades, i'll have to create a function that adds a signal column to the dataframe and for each respective row.

Of the two indicators, the MACD has the simplest function.

Here I will only make a long signal if the MACD is above it's signal line but both lines still are under the 0 level. If this crossover happens above, the function will do nothing and will stay neutral.

The reverse is for bearish signals. MACD should cross below the MACD signal line, but only above the 0 level. In other cases the signal stays neutral.

In pseudocode it should look like this:

  • (MACD > Signal) < 0 = Bullish
  • (MACD < Signal) > 0 = Bearish

And in all other cases the signal is neutral.

So not lets adds this to the dataframe:

def create_macd_signal(df):
Create empty placeholder for signal list
signal = []
​
for i in range(len(df)):
# Check for a bullish MACD crossover condition
if (df['macd'][i] > df['macs'][i]) and (df['macd'][i] < 0) and (df['macs'][i] < 0):
signal.append('long_signal')
# Check for a bearish MACD crossover condition
elif (df['macd'][i] < df['macs'][i]) and (df['macd'][i] > 0) and (df['macs'][i] > 0):
signal.append('short_signal')
else:
signal.append('neutral')
​
return signal

After the function is complete, i'll have to execute it with the dataframe as input. Then the addional macd_condition column will be filled.

df['macd_condition'] = create_macd_signal(df)

# Let's see if this signal happens or changes during the last 50 candle entries of the dataframe

df[['date','close','macd','macs','macdh','macd_condition']].tail(50)

Stochastics signals

As for the stochastics signals, Again, to remind you. The video only states that a return from oversold or overbought conditions happen if both lines return to the normal zone between 25 and 75 percent. Here I will take the liberty to only use the 'D' line for my signal since this line is slower thant the 'K' line and is less prone to zigzagging accross the 25 and 75 level.

Although with the advised settings of the video, the stochastics is already smoothed a lot.

I'll also make this function so, that other buy or sell signals of the other indicators should occur within 5 candles of this overbought or oversold condition. Otherwise it is a false signal and there will be no trade. And of course you can play with this time length to see which one gets the best signals...

The function I am using to determine this signal is based on the following rules, or pseudocode:

  • Check if the stochd value in the current row is between 75 and 25 (if 25 <= tochd <= 75)
  • If the value in one of the dataframe 5 rows (which are candles) before the current row was below 25, then add the value 'oversold' in the stochastics_condition
  • However, If the value in one of the 5 rows before the current row was above 75, then add the value 'overbought' in the stochastics_condition

This will determine if the stochd was in oversold or overbought territory and will be part of the total long or short trade signals.

def stochastics_condition(dataframe, previous_rows=5):
# Create new colum to save values to first
dataframe['stochastics_condition'] = ''
# Set overbought / oversold value tresholds here
overbought_value = 75
oversold_value = 25
​
for i in range(len(dataframe)):
# Get the current stochd value
current_stochd = dataframe['stochd'][i]
# Set the neutral signal so that it will be the 'default' signal after previous_rows setting
neutral = True  # Assume neutral until proven otherwise
# Determine the current signal according to the current stochd value
if current_stochd < oversold_value:
dataframe.at[i, 'stochastics_condition'] = 'oversold'
neutral = False
elif current_stochd > overbought_value:
dataframe.at[i, 'stochastics_condition'] = 'overbought'
neutral = False
else:
# Check cell value of the previous stochd rows
previous_stochd = dataframe['stochd'].iloc[max(0, i - previous_rows):i]
# If any of the previous rows values is above or below the trigger value, then
# change value of stochastics_condition cell
if any(previous_stochd < oversold_value):
dataframe.at[i, 'stochastics_condition'] = 'oversold_trigger'
neutral = False
elif any(previous_stochd > overbought_value):
dataframe.at[i, 'stochastics_condition'] = 'overbought_trigger'
neutral = False
if neutral:
dataframe.at[i, 'stochastics_condition'] = 'neutral'

Apply the function to the dataframe

stochastics_condition(df)

Now let me show the dataframe with only the overbought stochastic conditions

df[['date','close','stochd','stochastics_condition']][df['stochastics_condition']=='overbought_trigger'].tail(10)

​

Now let me show the dataframe with only the oversold stochastic conditions

df[['date','close','stochd','stochastics_condition']][df['stochastics_condition']=='oversold_trigger'].tail(10)
​

Putting it all together

Now that I've created all the individual signals in the dataframe, it is time to put this all together for the definitive buy or sell signal for trading.

This it pretty straight forward since all conditions are there but only have to be assembled to form the final signal.

Again, to remind you the long signal occurs when:

  • Price is above 200 EMA
  • MACD is above MACD signal, but happens below the 0 line
  • Stochastics returns out of oversold condition
  • And price gets above the 12 EMA

I will add another column, named 'go_long' here to create the signal I am going to use for the bot. If that cell has a True value, then there is a trade signal.

For long signals, the code is as follows:

df['go_long'] = (df['ema200_long'] == True) & (df['macd_condition'] == 'long_signal') & (df['stochastics_condition'] == 'oversold_trigger') & (df['ema12_long'] == True)
df[['date','close','stochd','ema200_long','macd_condition', 'stochastics_condition','ema12_long', 'go_long']][df['go_long']== True].tail(50)

The long signals above on the daily timeframe are shown on the chart below:

For short signals, the rules are as follows:

  • Price is below 200 EMA
  • MACD crosses below MACD signal, but happens above the 0 line for strong signals
  • Stochastics returns out of overbought condition
  • And as the last configmatin indicator, price gets below the 12 EMA

For this I add a column, named 'go_short' to create the shorting signal I am going to use for the bot.

For short signals, its practically the same code but conditions are reversed

df['go_short'] = (df['ema200_short'] == True) & (df['macd_condition'] == 'short_signal') & (df['stochastics_condition'] == 'overbought_trigger') & (df['ema12_short'] == True)
df[['date','close','stochd','ema200_short','macd_condition', 'stochastics_condition','ema12_short', 'go_short']][df['go_short']== True].tail(50)

And in the charts the points to go short are shown here (only for 2022):

Strategy file

At this moment I have a basis on which I can create an actual working strategy file and I have made this as a downloadable file in the backtest results zip files

After I have the working code ready, i will do backtests on this strategy on multiple timeframes and with multiple cryptocurrency pairs. Of which I will show you the results next.

Hopefully all my efforts have not been for nothing and the original Youtube creator of this strategy really has made one of the best strategies to use.

Backtest results

With these backtests I took the liberty to make use of the default bot's stoploss and takeprofit or ROI settings. This is my personal take on this strategy and a slight deviation from the original Youtube one, since actual risk, reward ratio's or values are not really explained there.

Also determining the last low swing with the backtest engine I use is not easy to code. I could use the last lowest value of the last 10 candles for example, but that is a moving target. So that is why I deciced to keep things simple and used the percentage based stop loss.

But You are always welcome to add your own personal preferences for stoploss and takeprofit points ofcourse.

If I try to use a 1 to 3 Risk:Reward ratio here, then my stoploss point to exit the trade at 10% loss and takeprofit if an ROI is reached of 30%.

Futures backtest result

The first backtest I did was with futures trading.

And as you can see I got some very mixed results here.

The 4 hour, 1 hour and 15 minute timeframe had positive results, but on the other timeframes this strategy did not perform so well.

If I only stick to the most noticable results here, then the strategy, as it is now, has a very poor win rate. On all timeframes it is under 30%. And these winrates are more to expected on trend trading strategies. The amount of trades is also low on the lower timeframes and I expected more trades there. On the best scoring timeframe, the number of back to back losing trades on average is pretty high, with a maximum of 42 losses back to back. Manually trading this system might be a frustrating excersise at some point.


This is also shown in the Exit reasons stats of the 4h timeframe. Apparently after each trade signal the price moves the opposite site, taking it to it's stoploss point and exit the trade. And I consider a stop loss percentage of 10% pretty wide.

If I look at the profit chart, it looks very jagged. I think that the signals this strategy gives are not always correct. This might be caused by the signal settings of the stochastics where a signal is only valid after it has returned from overbought or oversold levels and these levels might be too high to generate more and more profitable signals. Also only using the macd when it is on the opposite side of the 0 level might prevent taking more profitable trades here too. The characteristics of a bear and bull period are not the same. Where bull markets are prone to progress more slowly and bear markets result in steep, quick declines, this can have effect if you only use one specific setting for trading in both directions.

But let me ponder not too much at these results, maybe long trading only has much beter results.

Spot backtest result

If you want to use this strategy with spot trading, then you have endure ort of like the same experience. If you do not consider the profit percentage but only the ratios, then you see there sis not much improvement here. Still low winrates and high drawdowns. And the profit factor is even slightly worse than that of futures trading. The only, and main reason why this end score is higher, is that of the profit percentage is higher.


As already said, the stoploss setting I chose is the main reason why this strategy performs so poorly and maybe the initial 10% stop loss is set to tight. Also maybe, also the take profit percentage is set too wide too. So in a way I should risk more capital per trade in order to gain slightly less profits.


The only way to find out is to optimize the settings here. And coincidentally the bot has an hyperparameter optimization engine that can help find the best settings.

So let me kickstart the computer, calculate these optimal settings and retest the best performing spot timeframe again to find out..

Optimization results

Now because the SPOT backtest on the 5 timeframe performed the best, I optimized this specific timeframe. And because I thought that the biggest problem lies within my choice to deviate from the proposed but vague stop loss and take profit values. So I did an optimization on only the ROI and stop loss. Optimizing a strategy also involves the risk of overfitting. So the results I might get results that are not truly realistic. But it's nice to see if a strategy's performance can be improved or not. Because sometimes hyperparameter optimization does not optimize at all.


So after letting my PC calculate for almost eight hours I got some new ROI and stop loss settings from the optimization engine. In the hope that this could improve the performance significantly.


And the hyperoptimization indeed calculated that a better performance could be reached if I risked more by setting the stop loss to over 30% and lower the reward to a little bit over 20% take profit.

But unfortunately, the backtest results show very dissapointing results. It's even making the strategy perform as one of the worst versions. It has a much higher win rate but all other performance indicators perform way worse. Something you might not expect after an optimization run.


And that is surpiring and logical at the same time. The ROI is performing quite well with these settings because it exits with a win or a draw. But here the stop loss is set so wide that with these few times it gets hit, it drains the complete account. And the occurence of the 11 losing trades streak from the previous table, could mean that with these 11 losing trades your account get's blown in one streak.


Conclusion & Strategy League

So what is my conclusion on all of this?

Well I do initially think that the proposed strategy in the youtube video has potential and that the maker indeed had very good results when he tested this strategy.

However I try to test these hypotheses on a larger scale with more assets, larger date ranges and multiple timeranges. To see if this is a good strategy for algorithmic trading, also hopefully completely automatic with a trading bot.

You can argue that I did not 100% correctly followed the proposed strategy to the letter, and I am not denying this fact. But I did expect to have better results. For example the trade signalling quantity is pretty low, even on lower timeframes. Which might indicate that the initial settings on which the signals are based also have to be further optimized...

Figuring out where the exact problems lie can take up much time and I'd rather search and improve another algorithm that performs good right away, than trying to improve a mediocre one.

But I hope you have learned from this video nonetheless and I have shown you enough information that gave some nice ideas to make or improve one of your own algo's.

Strategy League

The initial 5 minute spot version of this strategy scored slightly below the middle of my Strategy League. Let's hope that in the next video I make or test one that performs way more better than this one...

Closure

Again I hope you have got some new ideas and new information from this blogpost and you liked it.

If you did, than show me by giving me a comment. You can also always subscribe to my Youtube channel and clicking the bell icon to be informed of future videos.

Thank you for reading and see you in the next one...

Goodbye!

Comments

No comments found for this post.