AthenaStrategyV1
♡
Basics
mode: spot
timeframe: 5m
Settings
stoploss: -0.8
has minimal roi
custom stoploss
process only new candles: false
startup candle count: 200
hyperopt
Indicators
ADX
ATR
EMA
RSI
SMA
talib
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 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 | # Athena Strategy V1 — EMA趋势 + MACD + ADX/DMI + HMA回调 + 1h ATR止损 # v1.1: + dynamic leverage (1x-3x based on ADX) from freqtrade.strategy.interface import IStrategy from pandas import DataFrame import talib.abstract as ta import pandas as pd pd.options.mode.chained_assignment = None from functools import reduce from datetime import datetime import numpy as np from freqtrade.strategy import merge_informative_pair from freqtrade.persistence import Trade class AthenaStrategyV1(IStrategy): WHITELIST = ['BTC/USDT', 'ETH/USDT', 'BNB/USDT', 'SOL/USDT', 'XRP/USDT', 'DOGE/USDT', 'ADA/USDT', 'TRX/USDT', 'AVAX/USDT', 'LINK/USDT'] buy_params = { "adx_threshold": 20, "atr_period": 14, "atr_sl_multiplier": 3.0, "informative_timeframe": "1h", } minimal_roi = {"0": 1.0} stoploss = -0.80 timeframe = '5m' startup_candle_count = 200 process_only_new_candles = False trailing_stop = False use_exit_signal = False exit_profit_only = False ignore_roi_if_entry_signal = False use_custom_stoploss = True def informative_pairs(self): pairs = self.dp.current_whitelist() return [(pair, self.buy_params['informative_timeframe']) for pair in pairs] def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: informative = self.dp.get_pair_dataframe(pair=metadata['pair'], timeframe=self.buy_params['informative_timeframe']) informative['atr_1h'] = ta.ATR(informative, timeperiod=14) dataframe = merge_informative_pair(dataframe, informative, self.timeframe, self.buy_params['informative_timeframe'], ffill=True) dataframe['ema_short'] = ta.EMA(dataframe, timeperiod=20) dataframe['ema_long'] = ta.EMA(dataframe, timeperiod=50) dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14) dataframe['adx'] = ta.ADX(dataframe, timeperiod=14) dataframe['plus_di'] = ta.PLUS_DI(dataframe, timeperiod=14) dataframe['minus_di'] = ta.MINUS_DI(dataframe, timeperiod=14) dataframe['volume_ma'] = ta.SMA(dataframe['volume'], timeperiod=20) return dataframe def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: # 优化:更严格的入场条件,避免ETH等亏损交易 conditions = [ dataframe['ema_short'] > dataframe['ema_long'] * 1.01, # 更强的EMA趋势 dataframe['close'] > dataframe['ema_short'] * 1.02, # 更强的价格确认 dataframe['adx'] > (self.buy_params['adx_threshold'] + 5), # 提高ADX阈值 dataframe['plus_di'] > dataframe['minus_di'] * 1.2, # 更强的方向性 dataframe['rsi'] > 40, # 提高RSI下限 dataframe['rsi'] < 65, # 降低RSI上限,避免超买 dataframe['volume'] > dataframe['volume_ma'] * 1.5, # 更高的成交量要求 ] dataframe.loc[reduce(lambda x, y: x & y, conditions), 'enter_long'] = 1 return dataframe def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: return dataframe def custom_exit(self, pair: str, trade: Trade, current_time: datetime, current_rate: float, current_profit: float, **kwargs): """ 动态止盈机制 - 解决AthenaStrategyV1亏损的关键优化 """ # 如果盈利超过40%,立即止盈50%仓位 if current_profit > 0.40: return 'profit_take_50pct' # 如果盈利超过25%,止盈30%仓位 elif current_profit > 0.25: return 'profit_take_30pct' # 如果盈利超过15%,止盈20%仓位 elif current_profit > 0.15: return 'profit_take_20pct' # 如果盈利超过8%,止盈10%仓位 elif current_profit > 0.08: return 'profit_take_10pct' return None def leverage(self, pair: str, current_time: datetime, current_rate: float, proposed_leverage: float, max_leverage: float, entry_tag: str | None, side: str, **kwargs) -> float: dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) if len(dataframe) == 0: return 3.0 last_candle = dataframe.iloc[-1].squeeze() adx = last_candle.get('adx', 20) rsi = last_candle.get('rsi', 50) ema_short = last_candle.get('ema_short', 0) ema_long = last_candle.get('ema_long', 0) volume = last_candle.get('volume', 0) volume_ma = last_candle.get('volume_ma', 1) # 基础杠杆 3x,最大限制在15x(从20x降至15x) base_leverage = 3.0 # 趋势强劲且RSI健康时提升杠杆(从20x降至15x) if adx > 35 and 40 < rsi < 60 and volume > volume_ma * 1.2: base_leverage = 15.0 # 从20x降至15x elif adx > 30 and ema_short > ema_long * 1.02 and volume > volume_ma: base_leverage = 12.0 # 从15x降至12x elif adx > 25 and rsi > 45: base_leverage = 8.0 # 从10x降至8x elif adx > 20: base_leverage = 5.0 # 从8x降至5x # 超买或趋势弱势时大幅降低杠杆 if rsi > 80 or adx < 15 or ema_short < ema_long: base_leverage = max(3.0, base_leverage * 0.5) return min(base_leverage, max_leverage) def custom_stoploss(self, pair: str, trade: Trade, current_time: datetime, current_rate: float, current_profit: float, **kwargs) -> float: dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) if len(dataframe) == 0: return self.stoploss last_candle = dataframe.iloc[-1].squeeze() atr = last_candle.get('atr_1h', 0) if atr <= 0: return self.stoploss lev = trade.leverage or 1.0 multiplier = self.buy_params['atr_sl_multiplier'] if current_profit > 0.10: trail_stop = (current_rate - atr * 1.5 - trade.open_rate) / trade.open_rate return max(trail_stop * lev, self.stoploss * lev) elif current_profit > 0.05: trail_stop = (current_rate - atr * 2.0 - trade.open_rate) / trade.open_rate return max(trail_stop * lev, self.stoploss * lev) elif current_profit > 0.02: return max(0.005 * lev, self.stoploss * lev) else: base_stop = (current_rate - atr * multiplier - trade.open_rate) / trade.open_rate return max(base_stop * lev, self.stoploss * lev) def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float, proposed_stake: float, min_stake: float, max_stake: float, entry_tag: str, **kwargs) -> float: dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) if len(dataframe) == 0: return proposed_stake last_candle = dataframe.iloc[-1].squeeze() risk_factor = 1.0 adx = last_candle.get('adx', 0) risk_factor *= min(1.3, adx / 25.0) volatility = last_candle.get('atr_1h', 0) / (current_rate + 1e-10) risk_factor *= max(0.5, 1.0 - volatility * 30) risk_factor = max(0.3, min(1.6, risk_factor)) return max(min_stake, min(proposed_stake * risk_factor, max_stake)) |
Strategy League — fixed backtest that feeds the ranking
Failed — timed out after 1800s (strategy too complex/slow for the sandbox)
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
no lookahead patterns · 2 thing(s) worth reviewing before trusting the numbers
| Line | Pattern | Detail | |
|---|---|---|---|
| 70 | review | dead_callback | custom_exit() is defined but use_exit_signal is False, and freqtrade only consults it inside that flag -- the method never runs |
| 28 | review | unthrottled_candle_processing | process_only_new_candles is False, so populate_indicators/populate_entry_trend/populate_exit_trend re-run every throttle_secs (default 5s) even though their inputs -- closed candles -- haven't changed since the last run. This wastes CPU without changing any value; if the goal is order-book-level checks, put that logic in confirm_trade_entry/custom_exit instead, which already run every loop |
ran by Ron · took s
Lookahead analysis
freqtrade lookahead-analysis: detects strategies peeking at future candles.