diff --git a/freqtrade/templates/freqaiprimer.py b/freqtrade/templates/freqaiprimer.py index 2cb371d0..385ec94d 100644 --- a/freqtrade/templates/freqaiprimer.py +++ b/freqtrade/templates/freqaiprimer.py @@ -27,165 +27,21 @@ class FreqaiPrimer(IStrategy): # 用于跟踪市场状态的数据框缓存 _dataframe_cache = None - # 用于存储币对的波动系数 - _volatility_coefficients = {} - # 基准币对 (波动系数设为1.0) - _benchmark_pair = 'BTC/USDT' - # 稳定币列表 (波动系数设为0.0) - _stablecoins = ['USDT', 'USDC', 'BUSD', 'DAI', 'TUSD', 'USDP', 'GUSD', 'USTC'] - # 波动系数缓存有效期 (分钟) - _volatility_cache_ttl = 60 - # 上次计算波动系数的时间 - _last_volatility_calculation = 0 - def __init__(self, config=None): """初始化策略参数,调用父类初始化方法并接受config参数""" super().__init__(config) # 调用父类的初始化方法并传递config # 存储从配置文件加载的默认值 self._trailing_stop_positive_default = 0.004 # 降低默认值以更容易触发跟踪止盈 - # 初始化基准币对和稳定币的波动系数 - self._volatility_coefficients[self._benchmark_pair] = 1.0 - for stablecoin in self._stablecoins: - # 处理所有稳定币交易对,如USDT/USDC等 - for quote in self._stablecoins: - if stablecoin != quote: - pair = f'{stablecoin}/{quote}' - self._volatility_coefficients[pair] = 0.0 - - def _is_stablecoin_pair(self, pair: str) -> bool: - """ - 判断一个交易对是否为稳定币交易对 - 参数: - - pair: 交易对,如 BTC/USDT - 返回: - - bool: 是否为稳定币交易对 - """ - try: - base, quote = pair.split('/') - return base in self._stablecoins and quote in self._stablecoins - except ValueError: - return False - - def _calculate_volatility(self, pair: str, lookback_period: int = 200, timeframe: str = '1h') -> float: - """ - 计算一个币对的波动率 - 参数: - - pair: 交易对,如 BTC/USDT - - lookback_period: 回看期K线数量,默认200 - - timeframe: 时间框架,默认1h - 返回: - - float: 波动率值 - """ - try: - # 获取K线数据 - dataframe = self.dp.get_pair_dataframe(pair=pair, timeframe=timeframe) - - # 确保有足够的K线数据 - if len(dataframe) < lookback_period: - logger.warning(f"[{pair}] 没有足够的{timeframe} K线数据,需要{lookback_period}根,当前只有{len(dataframe)}根") - # 如果数据不足,返回0.0或默认值 - return 0.0 - - # 获取最近的K线数据 - recent_data = dataframe.iloc[-lookback_period:].copy() - - # 计算收益率 (收盘价变化百分比) - recent_data['returns'] = recent_data['close'].pct_change() - - # 计算对数收益率 (更适合波动率计算) - recent_data['log_returns'] = np.log(recent_data['close'] / recent_data['close'].shift(1)) - - # 使用对数收益率的标准差作为波动率指标 - volatility = recent_data['log_returns'].std() * np.sqrt(24) # 年化波动率 (假设1天24小时) - - return volatility - - except Exception as e: - logger.error(f"[{pair}] 计算波动率时出错: {str(e)}") - return 0.0 - - def _calculate_relative_volatility_coefficient(self, pair: str) -> float: - """ - 计算相对波动系数(以BTC/USDT为基准) - 参数: - - pair: 交易对,如 ETH/USDT - 返回: - - float: 相对波动系数 - """ - try: - # 检查是否为稳定币交易对 - if self._is_stablecoin_pair(pair): - return 0.0 - - # 检查是否为基准币对 - if pair == self._benchmark_pair: - return 1.0 - - # 计算当前币对的波动率 - pair_volatility = self._calculate_volatility(pair) - - # 计算基准币对的波动率 - benchmark_volatility = self._calculate_volatility(self._benchmark_pair) - - # 避免除以0的情况 - if benchmark_volatility == 0: - logger.warning(f"基准币对 {self._benchmark_pair} 的波动率为0,无法计算相对波动系数") - return 0.0 - - # 计算相对波动系数 - relative_coefficient = pair_volatility / benchmark_volatility - - return relative_coefficient - - except Exception as e: - logger.error(f"[{pair}] 计算相对波动系数时出错: {str(e)}") - return 0.0 + # 只用于adjust_trade_position方法的波动系数获取 def get_volatility_coefficient(self, pair: str) -> float: """ - 获取币对的波动系数(带缓存机制) - 参数: - - pair: 交易对,如 ETH/USDT - 返回: - - float: 波动系数(稳定币为0.0,BTC/USDT为1.0,其他币对相对于BTC波动程度) + 获取币对的波动系数(简化版,仅用于adjust_trade_position方法) + 返回固定值1.0,假设所有币对波动相当 """ - try: - # 优先从缓存中获取,无需每次都检查时间戳 - # 这确保了在同一交易周期内多次调用时直接返回缓存值 - if pair in self._volatility_coefficients: - # 检查是否为基准币对或稳定币对(它们的波动系数固定) - if pair == self._benchmark_pair or self._is_stablecoin_pair(pair): - return self._volatility_coefficients[pair] - - # 获取当前时间戳(分钟) - current_time = int(datetime.datetime.now().timestamp() / 60) - - # 对于非固定波动系数的币对,检查缓存是否有效 - if current_time - self._last_volatility_calculation < self._volatility_cache_ttl: - return self._volatility_coefficients[pair] - - # 计算新的波动系数 - coefficient = self._calculate_relative_volatility_coefficient(pair) - - # 更新缓存 - self._volatility_coefficients[pair] = coefficient - - # 如果是首次计算或者缓存已过期,更新最后计算时间 - current_time = int(datetime.datetime.now().timestamp() / 60) - if current_time - self._last_volatility_calculation >= self._volatility_cache_ttl: - self._last_volatility_calculation = current_time - # 日志记录 - logger.info(f"更新了波动系数缓存,当前时间: {current_time}, 上一次计算时间: {self._last_volatility_calculation}") - - # 日志记录 - 仅在计算新值时记录 - logger.info(f"[{pair}] 波动系数: {coefficient:.4f}") - - return coefficient - - except Exception as e: - logger.error(f"[{pair}] 获取波动系数时出错: {str(e)}") - # 如果出错,返回默认值1.0(假设与BTC波动相当) - return 1.0 + return 1.0 + + # 其他辅助方法可以在这里添加 @property def protections(self): @@ -674,44 +530,23 @@ class FreqaiPrimer(IStrategy): last_candle = dataframe.iloc[-1] atr = last_candle['atr'] - # 获取当前币对的波动系数 - volatility_coef = self.get_volatility_coefficient(pair) - # 获取当前市场状态 current_state = dataframe['market_state'].iloc[-1] if 'market_state' in dataframe.columns else 'unknown' - # 基础止损倍数 - base_multiplier = 1.2 - - # 根据波动系数调整止损倍数 - adjusted_multiplier = base_multiplier * volatility_coef - - # 更激进的渐进式止损策略,同时考虑波动系数 + # 渐进式止损策略 if current_profit > 0.05: # 利润超过5%时 - return -3.0 * adjusted_multiplier * atr / current_rate + return -3.0 * atr / current_rate elif current_profit > 0.03: # 利润超过3%时 - return -2.5 * adjusted_multiplier * atr / current_rate + return -2.5 * atr / current_rate elif current_profit > 0.01: # 利润超过1%时 - return -2.0 * adjusted_multiplier * atr / current_rate + return -2.0 * atr / current_rate # 在强劲牛市中,即使小亏损也可以容忍更大回调 if current_state == 'strong_bull' and current_profit > -0.01: - return -1.5 * adjusted_multiplier * atr / current_rate - - # 动态调整止损范围 - if current_profit > 0.05: # 利润超过5%时 - return -3.0 * adjusted_multiplier * atr / current_rate - elif current_profit > 0.03: # 利润超过3%时 - return -2.5 * adjusted_multiplier * atr / current_rate - elif current_profit > 0.01: # 利润超过1%时 - return -2.0 * adjusted_multiplier * atr / current_rate - - # 在强劲牛市中,即使小亏损也可以容忍更大回调 - if current_state == 'strong_bull' and current_profit > -0.01: - return -1.8 * adjusted_multiplier * atr / current_rate + return -1.8 * atr / current_rate if atr > 0: - return -adjusted_multiplier * atr / current_rate + return -1.2 * atr / current_rate return self.stoploss def custom_exit(self, pair: str, trade: Trade, current_time: datetime, current_rate: float, @@ -723,9 +558,6 @@ class FreqaiPrimer(IStrategy): if trade_age_minutes < 0: trade_age_minutes = 0 - # 获取当前币对的波动系数 - volatility_coef = self.get_volatility_coefficient(pair) - # 使用可优化的线性函数: y = (a * (x + k)) + t a = self.roi_param_a.value # 系数a (可优化参数) k = self.roi_param_k.value # 偏移量k (可优化参数) @@ -754,11 +586,6 @@ class FreqaiPrimer(IStrategy): exit_ratio = 1.0 if entry_tag == 'strong_trend': exit_ratio *= 0.8 - - # 根据波动系数调整退出比例 - # 波动率低的币对可以更激进(降低退出比例,持有更长时间) - # 波动率高的币对需要更保守(增加退出比例,更快锁定利润) - exit_ratio = max(0.0, min(1.0, exit_ratio * volatility_coef)) if dynamic_roi_threshold < 0: exit_ratio = 1.0 @@ -826,13 +653,8 @@ class FreqaiPrimer(IStrategy): def custom_stake_amount(self, pair: str, current_time: datetime, **kwargs) -> float: """ - 根据波动系数动态调整初始仓位大小 - - 波动率高的币对分配较小的仓位 - - 波动率低的币对可以分配较大的仓位 + 定义初始仓位大小 """ - # 获取当前币对的波动系数 - volatility_coef = self.get_volatility_coefficient(pair) - # 获取默认的基础仓位大小 default_stake = self.stake_amount @@ -841,20 +663,19 @@ class FreqaiPrimer(IStrategy): max_stake = kwargs.get('max_stake', default_stake) - # 根据波动系数调整仓位大小 - # 波动率与仓位大小成反比关系 - # 基础逻辑:波动率高的币对仓位较小,波动率低的币对仓位较大 - if volatility_coef > 0: - # 使用平方根函数来降低极端波动的影响 - adjusted_stake = default_stake * min(1.5, max(0.5, 1.0 / (volatility_coef ** 0.5))) - else: - # 如果波动系数无法获取,使用默认仓位 - adjusted_stake = default_stake + # 从kwargs获取最小和最大仓位限制 + min_stake = kwargs.get('min_stake', 0.0) + max_stake = kwargs.get('max_stake', default_stake) +<<<<<<< Updated upstream # 确保调整后的仓位在允许的范围内 adjusted_stake = max(min_stake, min(adjusted_stake, max_stake)) #logger.info(f"[{pair}] 基于波动系数调整仓位: 波动系数={volatility_coef:.2f}, 默认仓位={default_stake:.2f}, 调整后仓位={adjusted_stake:.2f}") +======= + # 确保仓位在允许的范围内 + adjusted_stake = max(min_stake, min(default_stake, max_stake)) +>>>>>>> Stashed changes return adjusted_stake @@ -872,39 +693,19 @@ class FreqaiPrimer(IStrategy): ) -> bool: """ 交易买入前的确认函数,用于最终决定是否执行交易 - 此处实现剧烈拉升检查逻辑,并根据波动系数调整入场条件 + 此处实现剧烈拉升检查逻辑 """ # 默认允许交易 allow_trade = True # 仅对多头交易进行检查 if side == 'long': - # 获取当前币对的波动系数 - volatility_coef = self.get_volatility_coefficient(pair) - # 检查是否处于剧烈拉升的不稳固区域 is_unstable_region = self.detect_h1_rapid_rise(pair) - # 基于波动系数调整入场条件的严格程度 - # 波动率高的币对,入场条件更加严格 - # 波动率低的币对,入场条件可以适当放宽 if is_unstable_region: #logger.info(f"[{pair}] 由于检测到剧烈拉升,取消入场交易") allow_trade = False - - # 如果币对波动率非常高,可以增加额外的入场限制 - if volatility_coef > 2.0: - # 对于高波动币对,可以要求更严格的入场条件 - # 例如,检查当前价格是否高于某个移动平均线 - dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe) - if len(dataframe) > 20: - current_price = dataframe['close'].iloc[-1] - sma20 = dataframe['sma'].iloc[-1] if 'sma' in dataframe.columns else 0 - - # 对于高波动币对,要求当前价格必须显著高于均线才允许入场 - if sma20 > 0 and (current_price / sma20) < 1.01: - #logger.info(f"[{pair}] 由于高波动率且价格未显著高于均线,取消入场交易") - allow_trade = False # 如果没有阻止因素,允许交易 return allow_trade