To the MOON with this SMA Offset algorithm (Patreon)
Downloads
Missing 1 full-res photo, 1 file, as this post has not yet been imported.
Content
Introduction
Hi there, and welcome to this blogpost where I want to present you a strategy I found on Github and that has earned a place very high up in the rankings. So let's not waste anymore time and get right to it.
Where I found this strategy
As with many precoded algorithms for the backtesting engine build in the Freqtrade bot, this strategy was found in a github repository by stash86 and the file is called the SmaOffsetProtectOptV1. But I will call this strategy the Sma Offset algorithm.
The algorithm
Let me glance over the code so that we can understand what it's intentions are.
The code for the algorithm has a function that creates the Elliot Wave Oscillator. That indicator will be used later in the buy and sell settings.
def EWO(dataframe, ema_length=5, ema2_length=35):
df = dataframe.copy()
ema1 = ta.EMA(df, timeperiod=ema_length)
ema2 = ta.EMA(df, timeperiod=ema2_length)
emadif = (ema1 - ema2) / df['close'] * 100
return emadifThen there is a section with dictionaries that holds the buy and sell settings. These will also be used later in the algorithm.
# Buy hyperspace params:
buy_params = {
"base_nb_candles_buy": 13,
"ewo_high": 5.835,
"ewo_low": -19.909,
"low_offset": 0.978,
"rsi_buy": 55,
}
# Sell hyperspace params:
sell_params = {
"base_nb_candles_sell": 18,
"high_offset": 1.012,
}The algorithm has two predefined exit defined. One that sells at 1000 percent profit, effectively disabling the ROI sell signal. And a stop loss setting that sells at a loss of 15 percent after entry.
# and disable roi:
# ROI table:
minimal_roi = {
"0": 100.0
}
# Stoploss:
stoploss = -0.15Then there are some protections defined which will be used in live trading. Effectively determining that pairs with low profit will be locked, or excluded, from trading for the next 60 candles when they do not have a required profit of -5 percent and at least 1 trade over the last 60 candles.
{
"method": "LowProfitPairs",
"lookback_period_candles": 60,
"trade_limit": 1,
"stop_duration": 60,
"required_profit": -0.05
},And a cooldown period that does not buy the pair again after selling within the next 2 candles.
{
"method": "CooldownPeriod",
"stop_duration_candles": 2
}In this next part there are some Hyperparameter optimization spaces determined for parameters that will be used for determining the buy and sell signals.
# SMAOffset
base_nb_candles_buy = IntParameter(
5, 80, default=buy_params['base_nb_candles_buy'], space='buy', optimize=True)
base_nb_candles_sell = IntParameter(
5, 80, default=sell_params['base_nb_candles_sell'], space='sell', optimize=True)
low_offset = DecimalParameter(
0.9, 0.99, default=buy_params['low_offset'], space='buy', optimize=True)
high_offset = DecimalParameter(
0.99, 1.1, default=sell_params['high_offset'], space='sell', optimize=True)
# Protection
fast_ewo = 50
slow_ewo = 200
ewo_low = DecimalParameter(-20.0, -8.0,
default=buy_params['ewo_low'], space='buy', optimize=True)
ewo_high = DecimalParameter(
2.0, 12.0, default=buy_params['ewo_high'], space='buy', optimize=True)
rsi_buy = IntParameter(30, 70, default=buy_params['rsi_buy'], space='buy', optimize=True)No trailing stop.
trailing_stop = FalseAnd the timeframe for this algorithm. It has been set at 5 minute here, with an informative timeframe of 1 hour. So in this case I will not test timeframes higher than 1 hour for this algo.
timeframe = '5m'
informative_timeframe = '1h'In this section we can see that the algo makes use of the pairslist as informative pairs and the 1-hour timeframe for creatig informative indicators.
def informative_pairs(self):
pairs = self.dp.current_whitelist()
informative_pairs = [(pair, self.informative_timeframe) for pair in pairs]
return informative_pairs
def get_informative_indicators(self, metadata: dict):
dataframe = self.dp.get_pair_dataframe(
pair=metadata['pair'], timeframe=self.informative_timeframe)
return dataframeAs said earlier, this strategie will use the EMA, Elliot Wave Oscillator and RSI to determine buy and sell moments.
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
if self.config['runmode'].value == 'hyperopt':
# Calculate all ma_buy values
for val in self.base_nb_candles_buy.range:
dataframe[f'ma_{val}'] = ta.EMA(dataframe, timeperiod=val)
else:
dataframe[f'ma_{self.base_nb_candles_buy.value}'] = ta.EMA(dataframe, timeperiod=self.base_nb_candles_buy.value)
dataframe[f'ma_{self.base_nb_candles_sell.value}'] = ta.EMA(dataframe, timeperiod=self.base_nb_candles_sell.value)
# Elliot
dataframe['EWO'] = EWO(dataframe, self.fast_ewo, self.slow_ewo)
# RSI
dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14)
return dataframeAnd the Entry signals will be made by the bot if:
- The strategy buys when the current price is lower than a specific EMA value adjusted by a low offset factor (low_offset), potentially indicating a buying opportunity.
- In the second line it also considers the EWO value to be above a high threshold (ewo_high). A high Elliot value can be indicative of strong positive momentum or a bullish trend.
- And in the last line, the RSI value is checked to be below a certain threshold (rsi_buy). This is typically used to identify oversold conditions, suggesting that the asset may be undervalued and could be due for a price increase.conditions.append(
(
(dataframe['close'] < (dataframe[f'ma_{self.base_nb_candles_buy.value}'] * self.low_offset.value)) &
(dataframe['EWO'] > self.ewo_high.value) &
(dataframe['rsi'] < self.rsi_buy.value) &
(dataframe['volume'] > 0)
)
)
However in the second set of conditions, the EWO value is below a specified low threshold. This could indicate strong negative momentum or a bearish trend. The strategy might interpret this as a potential reversal point where the price could start to increase. Since the conditions are evaluated in parallel, there's no hierarchical preference given to one over the other. The strategy treats both conditions as equally valid for generating entry signals.
conditions.append(
(
(dataframe['close'] < (dataframe[f'ma_{self.base_nb_candles_buy.value}'] * self.low_offset.value)) &
(dataframe['EWO'] < self.ewo_low.value) &
(dataframe['volume'] > 0)
)
)Selling will be done when A sell signal is generated when the close price is higher than a specific EMA value adjusted by a high offset factor (high_offset).
def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
conditions = []
conditions.append(
(
(dataframe['close'] > (dataframe[f'ma_{self.base_nb_candles_sell.value}'] * self.high_offset.value)) &
(dataframe['volume'] > 0)
)
)So as you can see, this code looks to be a momentum-based trading strategy that uses moving averages, Elliot Wave Oscillator, and RSI to make buy and sell decisions.
And, it includes dynamic parameters for optimization, protection mechanisms, and specific conditions for entry and exit.
As i see it now, this strategy is designed for short-term trends as indicated by its 5-minute operational timeframe, making it suitable for day trading or short-term speculation.
Now it is time to test this code and find out how its hypothetical performance would be in previous market conditions.
Strategy backtest results
Interestingly, the configured informative pairs and timeframe were never used in the actual algorithm. That's why I could also test higher timeframes on this algorithm.
Not that it mattered much though because the 30 minute timeframe seems to have the best cards here. But other low timeframes like the 15 and 5 minute also perform very good here.
The 30 minute timeframe wins because of its high profits, Sharpe and Sortino combination. But the other timeframes have their own, very specific, positive scores. Like higher win percentage, lower drawdown, Calmar ratio and Pairs percentage.
It's a close call here, but the 30 minute timeframe scores just a little higher. Choosing this strategies timeframe for you depends on how it performs on your preferred pairs and possible backtest results here.
Taking a look at it's weekly performance, you can see that it is almost a straight profit line here. You can hardly spot a large drawdown. Even after the top of the last bear cycle, this algorithm kept adding profits to your balance. Which I cannot remember seeing in the performance of earlier algorithms. They always had a period of large drawdowns at some point.
I can check this performance in the following chart here. And you can see that other algos have at some point a noticable drawdown, whereas this one just has an upward line without any noticable large periods of declines. Like the Clucmay algo for example.
Which brings us to the comparison of the performance indicators of this algo with the other top 10 algorithms. You can see that overall this algo does not visibly outperform the other top 10 algorithms. But what it does is being consistently in the top 3 or 4. Gathering points that add up to a total high score, where other algorithms sometimes fail to score well.
Trading is being consistent and this seems to be a consistent strategy. Whether it is in trading, with it's almost straight line, or scoring on performance indicators like the plots given here.
Strategy League
This algorithm ended up at the second spot in my personal league of tested trading strategies. Now this SMA Offset strategy might nog score the most on all performance indicators shown here, but you saw earlier that on average it scored consistenty high on all parameters, which made it get this overall added up score.
Ending
Please be aware that these results are tested on historical data and that there is no guarantee that it will keep performing in the future. Also always do your own research and forward test anything before using actual money.
As usual, I will add all the output to this post so that you can check and verify my results. I also included the algorithm for use with the Freqtrade bot.
I am curious to hear from you how this algo performs on your setup. Does it perform great there too? Or are there some things to take into consideration before usage...
This is it for me now, so thanks for reading this post and see you the next time.
Goodbye!!
Files
Previews only