YoyoActionStrategy
♡
Basics
mode: spot
timeframe: 4h
interface version: 3
Settings
has minimal roi
Indicators
ATR
EMA
RSI
talib
Concepts
mean_reversion
15 related strategies (⧉ identical code, ≈ similar name)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 | import numpy as np # noqa import pandas as pd # noqa from pandas import DataFrame from freqtrade.strategy.interface import IStrategy # -------------------------------- # Add your lib to import here import talib.abstract as ta import freqtrade.vendor.qtpylib.indicators as qtpylib class YoyoActionStrategy(IStrategy): INTERFACE_VERSION = 3 # Minimal ROI designed for the strategy. # This attribute will be overridden if the config file contains "minimal_roi". minimal_roi = {'0': 10} timeframe = '4h' # Optional order type mapping order_types = {'entry': 'limit', 'exit': 'limit', 'stoploss': 'limit', 'stoploss_on_exchange': False} # emaFast = 6 # emaSlow = 18 emaFast = 24 emaSlow = 112 rsiPeriod = 14 overBought = 80 overSold = 30 #stoploss = -0.20 # Fast Trail atrFast = 6 atrFM = 0.5 # fast ATR multiplier # Slow Trail atrSlow = 18 # Slow ATR perod atrSM = 2 # Slow ATR multiplier # Trailing stoploss trailing_stop = False def informative_pairs(self): return [] def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: dataframe['ohlc4'] = (dataframe['open'] + dataframe['high'] + dataframe['low'] + dataframe['close']) / 4 dataframe['ema_fast'] = ta.EMA(dataframe, timeperiod=self.emaFast) dataframe['ema_slow'] = ta.EMA(dataframe, timeperiod=self.emaSlow) dataframe['rsi'] = ta.RSI(dataframe, timeperiod=self.rsiPeriod) dataframe['macd'] = dataframe['ema_fast'] - dataframe['ema_slow'] dataframe['bullish'] = dataframe['macd'] > 0 dataframe['bearish'] = dataframe['macd'] < 0 dataframe['sl1'] = self.atrFM * ta.ATR(dataframe.high, dataframe.low, dataframe.close, timeperiod=self.atrFast) # Stop Loss dataframe['sl2'] = self.atrSM * ta.ATR(dataframe.high, dataframe.low, dataframe.close, timeperiod=self.atrSlow) dataframe.dropna(inplace=True) dataframe['red'] = False dataframe['brown'] = False dataframe['yellow'] = False dataframe['blue'] = False dataframe['green'] = False dataframe['long'] = False dataframe['preBuy'] = False dataframe['short'] = False dataframe['preSell'] = False dataframe['trail2'] = 0.0 for index in range(len(dataframe)): # Green = bullish and mainSource>fast dataframe.green.iloc[index] = dataframe.bullish.iloc[index] and dataframe.ohlc4.iloc[index] > dataframe.ema_fast.iloc[index] # Blue = bearish and mainSource>fast and mainSource>slow dataframe.blue.iloc[index] = dataframe.bearish.iloc[index] and dataframe.ohlc4.iloc[index] > dataframe.ema_fast.iloc[index] # Yellow = bullish and mainSource<fast and mainSource>slow dataframe.yellow.iloc[index] = dataframe.bullish.iloc[index] and dataframe.ohlc4.iloc[index] < dataframe.ema_slow.iloc[index] # Brown = bullish and mainSource<fast and mainSource<slow dataframe.brown.iloc[index] = dataframe.bullish.iloc[index] and dataframe.ohlc4.iloc[index] < dataframe.ema_fast.iloc[index] and (dataframe.ohlc4.iloc[index] < dataframe.ema_slow.iloc[index]) # Red = bearish and mainSource<fast dataframe.red.iloc[index] = dataframe.bearish.iloc[index] and dataframe.ohlc4.iloc[index] < dataframe.ema_fast.iloc[index] # iff(SC>nz(Trail2[1],0) and SC[1]>nz(Trail2[1],0) if dataframe.close.iloc[index] > dataframe.trail2.iloc[index - 1] and dataframe.close.iloc[index - 1] > dataframe.trail2.iloc[index - 1]: dataframe.trail2.iloc[index] = max(dataframe.trail2.iloc[index - 1], dataframe.close.iloc[index] - dataframe.sl2.iloc[index]) # iff(SC<nz(Trail2[1],0) and SC[1]<nz(Trail2[1],0) elif dataframe.close.iloc[index] < dataframe.trail2.iloc[index - 1] and dataframe.close.iloc[index - 1] < dataframe.trail2.iloc[index - 1]: dataframe.trail2.iloc[index] = min(dataframe.trail2.iloc[index - 1], dataframe.close.iloc[index - 1] + dataframe.sl2.iloc[index - 1]) # iff(SC>nz(Trail2[1],0), elif dataframe.close.iloc[index] > dataframe.trail2.iloc[index - 1]: dataframe.trail2.iloc[index] = dataframe.close.iloc[index] - dataframe.sl2.iloc[index] else: dataframe.trail2.iloc[index] = dataframe.close.iloc[index] + dataframe.sl2.iloc[index] # it can use rolling dataframe.long.iloc[index] = dataframe.bullish.iloc[index] and dataframe.bullish.iloc[index - 1] dataframe.preBuy.iloc[index] = dataframe.bullish.iloc[index] and dataframe.bullish.iloc[index - 1] # dataframe.preSell.iloc[index] = dataframe.yellow.iloc[index] and ta. dataframe.short.iloc[index] = dataframe.bearish.iloc[index] and dataframe.bearish.iloc[index - 1] # greenLine = SC>Trail2 dataframe['greenLine'] = False dataframe.loc[dataframe['close'] > dataframe['trail2'], 'greenLine'] = True dataframe['greenLine_last'] = dataframe.greenLine.shift(1) dataframe['short_last'] = dataframe.short.shift(1) dataframe['green_last'] = dataframe.green.shift(1) dataframe['red_last'] = dataframe.red.shift(1) dataframe['hold_state'] = False dataframe.dropna(inplace=True) dataframe # greenLine = SC>Trail2 dataframe['greenLine'] = False dataframe.loc[dataframe['close'] > dataframe['trail2'], 'greenLine'] = True dataframe['greenLine_last'] = dataframe.greenLine.shift(1) dataframe['short_last'] = dataframe.short.shift(1) dataframe['green_last'] = dataframe.green.shift(1) dataframe['red_last'] = dataframe.red.shift(1) dataframe['hold_state'] = False dataframe.dropna(inplace=True) # Green buy # Over ATR and blue dataframe.loc[(dataframe['green_last'] == False) & (dataframe['green'] == True) | (dataframe['greenLine'] == True) & (dataframe['blue'] == True), 'signal_buy'] = True # Red Sell # | ((dataframe['greenLine_last'] == True) & (dataframe['greenLine'] == False)) # Stop lost dataframe.loc[(dataframe['red_last'] == False) & (dataframe['red'] == True), 'signal_sell'] = True return dataframe def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: dataframe.loc[dataframe['signal_buy'] == True, 'enter_long'] = 1 return dataframe def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: dataframe.loc[dataframe['signal_sell'] == True, 'exit_long'] = 1 return dataframe |
Strategy League — fixed backtest that feeds the ranking
The fixed-params backtest (33 pairs · 20210101-20260101) — the only run that feeds the Strategy League ranking.
Backtests — over a market period
Backtest this strategy over a chosen crypto-cycle period. These don't affect the League ranking, and need that period's candle data downloaded.
Log in or sign up to run backtests.
| Period | Range | Total % | Win % | Max DD | Trades | |
|---|---|---|---|---|---|---|
| 2020 · DeFi Summer & Pre-Halving Rally | 20200101-20210101 | not run | ||||
| 2021 · Institutional Bull Market | 20210101-20220101 | not run | ||||
| 2022 · Post-Bull Crash & Macro Tightening | 20220101-20230101 | not run | ||||
| 2023–2024 · Recovery & ETF Anticipation | 20230101-20250101 | not run | ||||
| 2025–2026 · Current Cycle | 20250101-20260101 | not run | ||||
Walk forward
Out-of-sample backtest on recent data · 33 pairs · 20260101-20260701.
Backtest trust check
Static source analysis — instant, does not run the strategy. Flags future-data leaks, backtest-realism problems, and indicators worth a second look.
Lookahead analysis
freqtrade lookahead-analysis: detects strategies peeking at future candles.