Hyperopt-Tuned Moving Average Ratio Entries and Exits in Freqtrade
Summary
This Freqtrade strategy creates long entry and exit signals from ratios among three simple moving averages. It computes separate buy-side and sell-side averages, then checks whether adjacent-average ratios fall between configurable minimum and maximum thresholds. The strategy is set to a four-hour timeframe and includes a return-on-investment schedule and a fixed stop-loss. Its comments direct users to optimize parameters with Hyperopt and a Sharpe-ratio-based loss function.
The source shows the rule structure and parameter ranges, but reports no backtest period, market universe, benchmark, or realized performance, so it does not establish profitability. There are also inconsistencies that deserve review before use: some defaults lie outside their declared parameter ranges, and the sell-side minimum threshold exceeds its maximum, making those paired conditions impossible to satisfy as written. The document is implementation material rather than an explanation of the economic rationale for moving-average ratios; results would depend on corrected parameters, data, fees, and robust out-of-sample evaluation.
Key ideas
- The strategy uses ratios between short, medium, and longer simple moving averages to trigger long entries and exits.
- Buy and sell rules use separate parameters that are intended for Hyperopt optimization.
- The strategy specifies a four-hour timeframe, a return-on-investment schedule, and a fixed stop-loss.
- Several default parameter values conflict with their stated ranges, and the sell thresholds appear to prevent exit signals.
- No backtest evidence is included, so the code alone cannot establish profitability.
Tags
Full text
# mabStra.py
```py
# Author: @Mablue (Masoud Azizi)
# github: https://github.com/mablue/
# IMPORTANT: DO NOT USE IT WITHOUT HYPEROPT:
# freqtrade hyperopt --hyperopt-loss SharpeHyperOptLoss --spaces all --strategy mabStra --config config.json -e 100
# --- Do not remove these libs ---
from freqtrade.strategy import IntParameter, DecimalParameter, IStrategy
from pandas import DataFrame
# --------------------------------
# Add your lib to import here
import talib.abstract as ta
class mabStra(IStrategy):
INTERFACE_VERSION: int = 3
# #################### RESULTS PASTE PLACE ####################
# ROI table:
minimal_roi = {
"0": 0.598,
"644": 0.166,
"3269": 0.115,
"7289": 0
}
# Stoploss:
stoploss = -0.128
# Buy hypers
timeframe = '4h'
# #################### END OF RESULT PLACE ####################
# buy params
buy_mojo_ma_timeframe = IntParameter(2, 100, default=7, space='buy')
buy_fast_ma_timeframe = IntParameter(2, 100, default=14, space='buy')
buy_slow_ma_timeframe = IntParameter(2, 100, default=28, space='buy')
buy_div_max = DecimalParameter(
0, 2, decimals=4, default=2.25446, space='buy')
buy_div_min = DecimalParameter(
0, 2, decimals=4, default=0.29497, space='buy')
# sell params
sell_mojo_ma_timeframe = IntParameter(2, 100, default=7, space='sell')
sell_fast_ma_timeframe = IntParameter(2, 100, default=14, space='sell')
sell_slow_ma_timeframe = IntParameter(2, 100, default=28, space='sell')
sell_div_max = DecimalParameter(
0, 2, decimals=4, default=1.54593, space='sell')
sell_div_min = DecimalParameter(
0, 2, decimals=4, default=2.81436, space='sell')
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
# SMA - ex Moving Average
dataframe['buy-mojoMA'] = ta.SMA(dataframe,
timeperiod=self.buy_mojo_ma_timeframe.value)
dataframe['buy-fastMA'] = ta.SMA(dataframe,
timeperiod=self.buy_fast_ma_timeframe.value)
dataframe['buy-slowMA'] = ta.SMA(dataframe,
timeperiod=self.buy_slow_ma_timeframe.value)
dataframe['sell-mojoMA'] = ta.SMA(dataframe,
timeperiod=self.sell_mojo_ma_timeframe.value)
dataframe['sell-fastMA'] = ta.SMA(dataframe,
timeperiod=self.sell_fast_ma_timeframe.value)
dataframe['sell-slowMA'] = ta.SMA(dataframe,
timeperiod=self.sell_slow_ma_timeframe.value)
return dataframe
def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
dataframe.loc[
(
(dataframe['buy-mojoMA'].div(dataframe['buy-fastMA'])
> self.buy_div_min.value) &
(dataframe['buy-mojoMA'].div(dataframe['buy-fastMA'])
< self.buy_div_max.value) &
(dataframe['buy-fastMA'].div(dataframe['buy-slowMA'])
> self.buy_div_min.value) &
(dataframe['buy-fastMA'].div(dataframe['buy-slowMA'])
< self.buy_div_max.value)
),
'enter_long'] = 1
return dataframe
def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
dataframe.loc[
(
(dataframe['sell-fastMA'].div(dataframe['sell-mojoMA'])
> self.sell_div_min.value) &
(dataframe['sell-fastMA'].div(dataframe['sell-mojoMA'])
< self.sell_div_max.value) &
(dataframe['sell-slowMA'].div(dataframe['sell-fastMA'])
> self.sell_div_min.value) &
(dataframe['sell-slowMA'].div(dataframe['sell-fastMA'])
< self.sell_div_max.value)
),
'exit_long'] = 1
return dataframe
```Shown in full with attribution under the source's licence. Licence: GPL-3.0
This summary was written by Stratmill's research agent from the original; it is not a copy of the source.