Home Artists Posts Import Register

Content

But at a cost…

Introduction

Okay, so in my last posts I was explaining that making video’s about strategies. that were actually not that great, was taking up a lot of time. Time that could better be invested in strategies that actually do work. and add to your trading account.
Saving me and you lots of time and frustration.

Well, here you have the first of these strategies that I found and that is not great at all. So I’ll make this post short but sweet. But also in a way you are informed on the possibilities of this strategies code and maybe the applicability of it in your own strategies. You can always learn from somebody elses ideas, even if they prove to be bad in practice (and their specific situation). It might as wel be a very lucrative idea in your own code after all…

The strategy

At the moment it’s hard for me to tell where exactly I got this code from. I had this strategy file quite a while and found it in a hoarding period where I did not know it was important to write down where I downloaded the code from. Searching on the name “Gumbo1.py” gave met the strategy repository of PeteDX. However I am not sure if he is the original author of this code because in the code there is this reference to https://github.com/raph92?tab=repositories. But it ends on a github repo without any code…

So again, if you are an algorithmic strategy creator, please always(!) add your name, contacts, something that leads it to you, to your strategy file. It’s the only way for me to give credits where credits are due!!

Now back to the strategy itself…

I’ll discuss the most noticable things here and will add the complete code to the end of this post for you.

ewo_low = DecimalParameter(-20.0, 1, default=0, space="buy", optimize=True)
t3_periods = IntParameter(5, 20, default=5, space="buy", optimize=True)
stoch_high = IntParameter(60, 100, default=80, space="sell", optimize=True)
stock_periods = IntParameter(70, 90, default=80, space="sell", optimize=True)

The code is ready to be optimized on both the buy and sell parameters. But also a warning from my side. I have noticed that optimizing buy, sell, roi and stoploss does not always end in ‘optimized’ parameters. On the contrary. Sometimes it’ll end up worse for the strategy. So be warned about this from me.

def informative_pairs(self) -> ListPairsWithTimeframes:
pairs = self.dp.current_whitelist()
informative_pairs = [(pair, '1h') for pair in pairs]
return informative_pairs

The strategy uses informative pairs on the 1hour timeframe. Here you’ll have to remember that I cannot test this strategy on higher timeframes than 1h. This can impact the endresults of this strategy…

def T3(dataframe, length=5):
"""
T3 Average by HPotter on Tradingview
https://www.tradingview.com/script/qzoC9H1I-T3-Average/
"""
df = dataframe.copy()
df['xe1'] = ta.EMA(df['close'], timeperiod=length)
df['xe2'] = ta.EMA(df['xe1'], timeperiod=length)
df['xe3'] = ta.EMA(df['xe2'], timeperiod=length)
df['xe4'] = ta.EMA(df['xe3'], timeperiod=length)
df['xe5'] = ta.EMA(df['xe4'], timeperiod=length)
df['xe6'] = ta.EMA(df['xe5'], timeperiod=length)
b = 0.7
c1 = -b * b * b
c2 = 3 * b * b + 3 * b * b * b
c3 = -6 * b * b - 3 * b - 3 * b * b * b
c4 = 1 + 3 * b + b * b * b + 3 * b * b
df['T3Average'] = c1 * df['xe6'] + c2 * df['xe5'] + c3 * df['xe4'] + c4 * df['xe3']
return df['T3Average']
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["low"] * 100
return emadif
def stoch_sma(dataframe: DataFrame, window=80):
""""""
stoch = qtpylib.stoch(dataframe, window)
return qtpylib.sma((stoch['slow_k'] + stoch['slow_d']) / 2, 10)

We’ll skip to the end of the code because here we discover that the author has created three specific indicators. Especially for this strategy. The T3 average, Elliot Wave Oscillator (EWO) and stochastic_sma. I will not go into these indicators, but if you are interested, you can search the internet for it.

Buy signal

def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
conditions = []
# ewo < 0
conditions.append(dataframe['EWO'] < self.ewo_low.value)
# middleband 1h >= t3 1h
conditions.append(dataframe['bb_middleband_1h'] >= dataframe['T3_1h'])
# t3 <= ema
conditions.append(dataframe[f'T3_{self.t3_periods.value}'] <= dataframe['EMA'])
if conditions:
dataframe.loc[reduce(lambda x, y: x & y, conditions), 'buy'] = 1
return dataframe

Now For the buy signal to be valid, the Elliot Wave Oscillator should be above the configured value in the Hyperopt settings (is 0), the Bollinger middle band on the 1 hour is higher or equal to the T3 on the 1hour. And finally, also the T3 value (which is default 5) should be lower than the EMA.

Sell signal

def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
conditions = []
# stoch > 80
conditions.append(
dataframe[f'stoch_{self.stock_periods.value}'] > self.stoch_high.value
)
# t3 >= middleband_40
conditions.append(
dataframe[f'T3_{self.t3_periods.value}'] >= dataframe['bb_middleband_40']
)
if conditions:
dataframe.loc[reduce(lambda x, y: x | y, conditions), 'sell'] = 1
return dataframe

The sell signal depends on some other indicators like the stochastics oscillator and the T3 above the bb_middle band.

Unfortunately there is no clear explanation behind this code. Or a general thought, so lets just test this code to see how it responds to the historic market and see if I have to invest more time into this idea…

Initial backtest results

As I was spoiling the performance of this strategy right in the beginning of this post, the initial backtest results do not look promising at all.

The informative pairs at the 1h timeframe prevent me from testing this idea on higher timeframes than 1 hour. Which is a shame because a lot of the initial low timeframe strategies have better performances on higher timeframes. But here I cannot do this. So I was forced to only test 1h and lower timeframes.

New information

If you have followed my posts regularly, and also watched the stats closely above, then you might have seen three new columns. I have added the following columns to gain more information of the strategies performance on the specific timeframe here. These new columns are:

  • Trades: or amount of trades that this strategy has executed during the backtest. So to give you information on the amount of trades to expect on the specific timeframe. Sometimes excessive trades or way to low amount of trades also says something about the effectivity of the algorithm;
  • Winstreak (Maximum stream and Average): I created a calculation to determine the longest streak of winning trades and the average streak of winning trades. This gives information on the way the algorithm behaves in addition to the %winrate.
  • Lose streak (Maximum and Average): The same for losing trade streaks. These maximum and average numbers are a huge psychological factor for the owner of the bot.

If the winstreak max. and avg. is higher than the lose streak (in combination with a high %winrate), the bot owner can then have some faith that after a certain amount of losing trades the probability of a win get’s higher. Also after the backtested maximum losing trade this chance get’s higher. No guarantees given though…

Btw. this win/lose streak does not count against the final strategy score.

Getting back to the other ratio’s and results. They are all pretty poor.

If you look at the profit chart of this strategy, you can see why. The algo loses money right from the beginning and ends up with no trade balance. So therefore no chance of any trades anymore. And that was on the best scoring timeframe…

Can this be improved..?

The only thing that I can think of is to do an optimization run to see if the parameters can be improved. My experience with optimizing buy, sell, roi and stoploss at the same time is that this almost every time ends up with suboptimal parameter settings which makes a strategy perform even worse.

So this time I will only try to optimize the buy and sell settings and see if this improves the results somewhat.

So after after optimizing 500 epochs over 4 hours I got some better parameters for the backtest. The results of the strategy improved dramatically (however I have seen some bad strategies turned into REAL winners in the past). And by changing the EWO, Stoch and T3 periods I got the following results back.

A slight 51% win rate and an even distribution over winning and losing streak averages with a profit factor of 1.07 over 60% makes this strategy now a carefull winner. The profit chart below also suddenly looks familiar to other successful strategies. Although it is not a major winner here.

Some pairs really do not respond well to this strategy or the settings of the indicators and cause the biggest drawdown during bearish conditions. Maybe a bull/bear market differentiator can make this algo perform even better since it is acting on a rather small timeframe here.

Strategy league and conclusion

Nonetheless, after all improvements and tests this strategy does still perform as one of the worst algo’s so far. Actually the only positive thing that I have gotten from this algorithm was the specially created code for the T3, Stoch_sma and Elliot wave oscillator that the author of the code left behind in the Python file.

I will add that to a special ‘library’ of code for these custom made indicators and will also use these examples to create some of my own…

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["low"] * 100

return emadif

So again, This is the first of hopefully not so many strategies that I will test but only add to this blog and not make a video off.

I still hope you got something out of this post though. And I will see you in the next one!

Files

All the backtest output files, strategy files, optimization files and more are all available as an addendum post on my Patreon site. The exact location is here:

https://www.patreon.com/posts/85237720


The complete code (as of july 2023) is as follows:

"""
https://github.com/raph92?tab=repositories
"""
import logging
# --- Do not remove these libs ---
import sys
from functools import reduce
from pathlib import Path
import freqtrade.vendor.qtpylib.indicators as qtpylib
import talib.abstract as ta
from freqtrade.constants import ListPairsWithTimeframes
from freqtrade.strategy import (
IntParameter,
DecimalParameter,
merge_informative_pair,
)
from freqtrade.strategy.interface import IStrategy
from pandas import DataFrame
sys.path.append(str(Path(__file__).parent))
logger = logging.getLogger(__name__)
class Gumbo1(IStrategy):
# region Parameters
ewo_low = DecimalParameter(-20.0, 1, default=0, space="buy", optimize=True)
t3_periods = IntParameter(5, 20, default=5, space="buy", optimize=True)
stoch_high = IntParameter(60, 100, default=80, space="sell", optimize=True)
stock_periods = IntParameter(70, 90, default=80, space="sell", optimize=True)
# endregion
# region Params
minimal_roi = {"0": 0.10, "20": 0.05, "64": 0.03, "168": 0}
stoploss = -0.25
# endregion
timeframe = '5m'
use_custom_stoploss = False
inf_timeframe = '1h'
# Recommended
use_sell_signal = True
sell_profit_only = False
ignore_roi_if_buy_signal = True
startup_candle_count = 200
def informative_pairs(self) -> ListPairsWithTimeframes:
pairs = self.dp.current_whitelist()
informative_pairs = [(pair, '1h') for pair in pairs]
return informative_pairs
def populate_informative_indicators(self, dataframe: DataFrame, metadata):
informative = self.dp.get_pair_dataframe(
pair=metadata['pair'], timeframe=self.inf_timeframe
)
# t3 from custom_indicators
informative['T3'] = T3(informative)
# bollinger bands
bbands = ta.BBANDS(informative, timeperiod=20)
informative['bb_lowerband'] = bbands['lowerband']
informative['bb_middleband'] = bbands['middleband']
informative['bb_upperband'] = bbands['upperband']
dataframe = merge_informative_pair(
dataframe, informative, self.timeframe, self.inf_timeframe
)
return dataframe
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
# ewo
dataframe['EWO'] = EWO(dataframe)
# ema
dataframe['EMA'] = ta.EMA(dataframe)
# t3
for i in self.t3_periods.range:
dataframe[f'T3_{i}'] = T3(dataframe, i)
# bollinger bands 40
bbands = ta.BBANDS(dataframe, timeperiod=40)
dataframe['bb_lowerband_40'] = bbands['lowerband']
dataframe['bb_middleband_40'] = bbands['middleband']
dataframe['bb_upperband_40'] = bbands['upperband']
# stochastic
# stochastic windows
for i in self.stock_periods.range:
dataframe[f'stoch_{i}'] = stoch_sma(dataframe, window=i)
dataframe = self.populate_informative_indicators(dataframe, metadata)
return dataframe
def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
conditions = []
# ewo < 0
conditions.append(dataframe['EWO'] < self.ewo_low.value)
# middleband 1h >= t3 1h
conditions.append(dataframe['bb_middleband_1h'] >= dataframe['T3_1h'])
# t3 <= ema
conditions.append(dataframe[f'T3_{self.t3_periods.value}'] <= dataframe['EMA'])
if conditions:
dataframe.loc[reduce(lambda x, y: x & y, conditions), 'buy'] = 1
return dataframe
def populate_sell_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
conditions = []
# stoch > 80
conditions.append(
dataframe[f'stoch_{self.stock_periods.value}'] > self.stoch_high.value
)
# t3 >= middleband_40
conditions.append(
dataframe[f'T3_{self.t3_periods.value}'] >= dataframe['bb_middleband_40']
)
if conditions:
dataframe.loc[reduce(lambda x, y: x | y, conditions), 'sell'] = 1
return dataframe
def T3(dataframe, length=5):
"""
T3 Average by HPotter on Tradingview
https://www.tradingview.com/script/qzoC9H1I-T3-Average/
"""
df = dataframe.copy()
df['xe1'] = ta.EMA(df['close'], timeperiod=length)
df['xe2'] = ta.EMA(df['xe1'], timeperiod=length)
df['xe3'] = ta.EMA(df['xe2'], timeperiod=length)
df['xe4'] = ta.EMA(df['xe3'], timeperiod=length)
df['xe5'] = ta.EMA(df['xe4'], timeperiod=length)
df['xe6'] = ta.EMA(df['xe5'], timeperiod=length)
b = 0.7
c1 = -b * b * b
c2 = 3 * b * b + 3 * b * b * b
c3 = -6 * b * b - 3 * b - 3 * b * b * b
c4 = 1 + 3 * b + b * b * b + 3 * b * b
df['T3Average'] = c1 * df['xe6'] + c2 * df['xe5'] + c3 * df['xe4'] + c4 * df['xe3']
return df['T3Average']
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["low"] * 100
return emadif
def stoch_sma(dataframe: DataFrame, window=80):
""""""
stoch = qtpylib.stoch(dataframe, window)
return qtpylib.sma((stoch['slow_k'] + stoch['slow_d']) / 2, 10)

Comments

No comments found for this post.