live模式下动态获取可用usdt并确认stake_amount
This commit is contained in:
parent
b688f82c4c
commit
467ae7639d
@ -8,7 +8,6 @@ import pandas_ta as ta
|
||||
from freqtrade.persistence import Trade
|
||||
import numpy as np
|
||||
import datetime
|
||||
import pandas as pd
|
||||
import math
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@ -28,11 +27,165 @@ 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
|
||||
|
||||
def get_volatility_coefficient(self, pair: str) -> float:
|
||||
"""
|
||||
获取币对的波动系数(带缓存机制)
|
||||
参数:
|
||||
- pair: 交易对,如 ETH/USDT
|
||||
返回:
|
||||
- float: 波动系数(稳定币为0.0,BTC/USDT为1.0,其他币对相对于BTC波动程度)
|
||||
"""
|
||||
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
|
||||
|
||||
@property
|
||||
def protections(self):
|
||||
@ -83,30 +236,32 @@ class FreqaiPrimer(IStrategy):
|
||||
|
||||
# 自定义指标参数 - 使用Hyperopt可优化参数
|
||||
bb_length = IntParameter(10, 30, default=20, optimize=True, load=True, space='buy')
|
||||
bb_std = DecimalParameter(1.5, 3.0, decimals=1, default=2.0, optimize=False, load=True, space='buy')
|
||||
rsi_length = IntParameter(7, 21, default=14, optimize=False, load=True, space='buy')
|
||||
bb_std = DecimalParameter(1.5, 3.0, decimals=1, default=2.0, optimize=True, load=True, space='buy')
|
||||
rsi_length = IntParameter(7, 21, default=14, optimize=True, load=True, space='buy')
|
||||
rsi_oversold = IntParameter(30, 50, default=42, optimize=True, load=True, space='buy')
|
||||
|
||||
|
||||
|
||||
# 入场条件阈值参数
|
||||
bb_lower_deviation = DecimalParameter(1.01, 1.05, decimals=2, default=1.03, optimize=True, load=True, space='buy')
|
||||
rsi_bull_threshold = IntParameter(45, 55, default=50, optimize=False, load=True, space='buy')
|
||||
stochrsi_bull_threshold = IntParameter(30, 40, default=35, optimize=False, load=True, space='buy')
|
||||
rsi_bull_threshold = IntParameter(45, 55, default=50, optimize=True, load=True, space='buy')
|
||||
stochrsi_bull_threshold = IntParameter(30, 40, default=35, optimize=True, load=True, space='buy')
|
||||
stochrsi_neutral_threshold = IntParameter(20, 30, default=25, optimize=True, load=True, space='buy')
|
||||
volume_multiplier = DecimalParameter(1.2, 2.0, decimals=1, default=1.5, optimize=False, load=True, space='buy')
|
||||
bb_width_threshold = DecimalParameter(0.01, 0.03, decimals=3, default=0.02, optimize=False, load=True, space='buy')
|
||||
volume_multiplier = DecimalParameter(1.2, 2.0, decimals=1, default=1.5, optimize=True, load=True, space='buy')
|
||||
bb_width_threshold = DecimalParameter(0.01, 0.03, decimals=3, default=0.02, optimize=True, load=True, space='buy')
|
||||
min_condition_count = IntParameter(2, 4, default=3, optimize=True, load=True, space='buy')
|
||||
|
||||
# 剧烈拉升检测参数 - 使用Hyperopt可优化参数
|
||||
h1_max_candles = IntParameter(100, 300, default=200, optimize=False, load=True, space='buy')
|
||||
h1_max_candles = IntParameter(100, 300, default=200, optimize=True, load=True, space='buy')
|
||||
h1_rapid_rise_threshold = DecimalParameter(0.05, 0.15, decimals=3, default=0.11, optimize=True, load=True, space='buy')
|
||||
h1_max_consecutive_candles = IntParameter(1, 4, default=2, optimize=False, load=True, space='buy')
|
||||
h1_max_consecutive_candles = IntParameter(1, 4, default=2, optimize=True, load=True, space='buy')
|
||||
# 定义可优化参数
|
||||
max_entry_adjustments = IntParameter(2, 5, default=3, optimize=False, load=True, space='buy') # 最大加仓次数
|
||||
add_position_callback = DecimalParameter(0.03, 0.06, decimals=3, default=0.053, optimize=True, load=True, space='buy') # 加仓回调百分比
|
||||
stake_divisor = DecimalParameter(2, 4, decimals=3, default=2.867, optimize=True, load=True, space='buy') # 加仓金额分母
|
||||
step_coefficient = DecimalParameter(0.5, 1.5, decimals=2, default=0.92, optimize=True, load=True, space='buy') # 加仓金额分母
|
||||
# -m 4 -c 0.045 -g 4.5 -p 1.22 -d 9.3 -s 75
|
||||
max_entry_adjustments = IntParameter(2, 5, default=3, optimize=True, load=True, space='buy') # 最大加仓次数
|
||||
add_position_callback = DecimalParameter(0.02, 0.06, decimals=3, default=0.045, optimize=True, load=True, space='buy') # 加仓回调百分比
|
||||
add_position_growth = DecimalParameter(1.5, 3.0, decimals=2, default=4.5, optimize=False, load=True, space='buy') # 加仓金额增长因子,保留2位小数用于hyperopt优化
|
||||
add_position_multiplier = DecimalParameter(0.2, 10.5, decimals=2, default=1.22, optimize=False, load=True, space='buy') # 加仓间隔系数,保留2位小数用于hyperopt优化
|
||||
stake_divisor = DecimalParameter(2.0, 12.0, decimals=2, default=9.3, optimize=False, load=True, space='buy') # 加仓金额分母(小数类型,保留2位小数)
|
||||
|
||||
# 线性ROI参数 - 用于线性函数: y = (a * (x + k)) + t
|
||||
roi_param_a = DecimalParameter(-0.0002, -0.00005, decimals=5, default=-0.0001, optimize=True, load=True, space='sell') # 系数a
|
||||
@ -116,14 +271,7 @@ class FreqaiPrimer(IStrategy):
|
||||
exit_bb_upper_deviation = DecimalParameter(0.98, 1.02, decimals=2, default=1.0, optimize=True, load=True, space='sell')
|
||||
exit_volume_multiplier = DecimalParameter(1.5, 3.0, decimals=1, default=2.0, optimize=True, load=True, space='sell')
|
||||
|
||||
rsi_overbought = IntParameter(57, 59, default=58, optimize=True, load=True, space='sell')
|
||||
|
||||
# 新增的可优化参数 - 用于should_exit逻辑
|
||||
rsi_overbought_level = IntParameter(60, 80, default=70, optimize=True, load=True, space='sell')
|
||||
macd_cross_threshold = DecimalParameter(0.0, 0.005, decimals=4, default=0.001, optimize=True, load=True, space='sell')
|
||||
volume_spike_multiplier = DecimalParameter(2.0, 4.0, decimals=1, default=3.0, optimize=True, load=True, space='sell')
|
||||
volume_drop_multiplier = DecimalParameter(0.1, 0.5, decimals=2, default=0.3, optimize=True, load=True, space='sell')
|
||||
cci_overbought_level = IntParameter(150, 250, default=200, optimize=True, load=True, space='sell')
|
||||
rsi_overbought = IntParameter(50, 70, default=58, optimize=True, load=True, space='sell')
|
||||
|
||||
|
||||
def informative_pairs(self):
|
||||
@ -139,19 +287,6 @@ class FreqaiPrimer(IStrategy):
|
||||
if missing_columns:
|
||||
logger.warning(f"[{metadata['pair']}] 数据框中缺少以下列: {missing_columns}")
|
||||
|
||||
def custom_stake_amount(self, pair: str, current_time: pd.Timestamp,
|
||||
current_rate: float,
|
||||
proposed_stake: float,
|
||||
min_stake: float,
|
||||
max_stake: float,
|
||||
**kwargs) -> float:
|
||||
# 获取初始资金(回测中固定为dry_run_wallet的值)
|
||||
initial_balance = self.config.get('dry_run_wallet', 10000)
|
||||
# 始终以初始资金的3.75%计算
|
||||
desired_stake = initial_balance * 0.0375
|
||||
desired_stake = math.floor(desired_stake) # 取整,去掉小数点后的数字
|
||||
return max(min(desired_stake, max_stake), min_stake)
|
||||
|
||||
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
||||
# 计算 3m 周期的指标
|
||||
bb_length_value = self.bb_length.value
|
||||
@ -317,9 +452,9 @@ class FreqaiPrimer(IStrategy):
|
||||
dataframe['prev_market_state'] = dataframe['prev_market_state'].fillna('neutral')
|
||||
|
||||
# 记录当前的市场状态
|
||||
# if len(dataframe) > 0:
|
||||
# current_score = dataframe['market_score'].iloc[-1]
|
||||
# current_state = dataframe['market_state'].iloc[-1]
|
||||
if len(dataframe) > 0:
|
||||
current_score = dataframe['market_score'].iloc[-1]
|
||||
current_state = dataframe['market_state'].iloc[-1]
|
||||
#logger.info(f"[{metadata['pair']}] 熊牛得分: {current_score:.1f}, 市场状态: {current_state}")
|
||||
#logger.info(f"[{metadata['pair']}] 各时间框架趋势: 3m={'牛' if dataframe['trend_3m'].iloc[-1] == 1 else '熊'}, \
|
||||
# 15m={'牛' if dataframe['trend_15m'].iloc[-1] == 1 else '熊'}, \
|
||||
@ -337,28 +472,7 @@ class FreqaiPrimer(IStrategy):
|
||||
return dataframe
|
||||
|
||||
def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
||||
# 计算额外需要的指标
|
||||
# 前一根K线的指标值
|
||||
dataframe['rsi_1h_prev'] = dataframe['rsi_1h'].shift(1)
|
||||
dataframe['macd_1h_prev'] = dataframe['macd_1h'].shift(1)
|
||||
dataframe['macd_signal_1h_prev'] = dataframe['macd_signal_1h'].shift(1)
|
||||
|
||||
# 下一根K线的成交量
|
||||
dataframe['volume_next'] = dataframe['volume'].shift(-1)
|
||||
|
||||
# 计算EMA指标
|
||||
dataframe['ema_5_1h'] = ta.ema(dataframe['close'], length=5)
|
||||
dataframe['ema_10_1h'] = ta.ema(dataframe['close'], length=10)
|
||||
|
||||
# 计算CCI指标
|
||||
dataframe['cci_1h'] = ta.cci(dataframe['high'], dataframe['low'], dataframe['close'], length=14)
|
||||
dataframe['cci_1h_prev'] = dataframe['cci_1h'].shift(1)
|
||||
|
||||
# 计算抛物线SAR指标 - 修复ValueError错误,提取正确的列
|
||||
sar_result = ta.psar(dataframe['high'], dataframe['low'], acceleration=0.02, maximum=0.2)
|
||||
# pandas_ta的psar函数返回DataFrame,我们需要选择正确的列
|
||||
dataframe['sar_1h'] = sar_result.iloc[:, 0] # 选择第一列作为SAR值
|
||||
# 原有出场条件
|
||||
# 出场信号基于趋势和量价关系
|
||||
# 条件1: 价格突破布林带上轨(使用可优化的偏差参数)
|
||||
breakout_condition = dataframe['close'] >= dataframe['bb_upper_1h'] * self.exit_bb_upper_deviation.value
|
||||
|
||||
@ -369,45 +483,22 @@ class FreqaiPrimer(IStrategy):
|
||||
macd_downward = dataframe['macd_1h'] < dataframe['macd_signal_1h']
|
||||
|
||||
# 条件4: RSI 进入超买区域(使用可优化的超买阈值)
|
||||
rsi_overbought_old = dataframe['rsi_1h'] > self.rsi_overbought.value
|
||||
|
||||
# 新增的出场条件 - should_exit逻辑
|
||||
# 条件1: MACD死叉 + RSI超买回落
|
||||
macd_dead_cross = (dataframe['macd_1h_prev'] > dataframe['macd_signal_1h_prev']) & \
|
||||
(dataframe['macd_1h'] <= dataframe['macd_signal_1h']) & \
|
||||
(abs(dataframe['macd_1h'] - dataframe['macd_signal_1h']) >= self.macd_cross_threshold.value)
|
||||
rsi_overbought_fallback = (dataframe['rsi_1h_prev'] > self.rsi_overbought_level.value) & \
|
||||
(dataframe['rsi_1h'] < self.rsi_overbought_level.value)
|
||||
exit_condition_1 = macd_dead_cross & rsi_overbought_fallback
|
||||
|
||||
# 条件2: 成交量异常放大后萎缩 + EMA死叉
|
||||
volume_spike_new = dataframe['volume'] > dataframe['volume_ma'] * self.volume_spike_multiplier.value
|
||||
volume_drop = dataframe['volume_next'] < dataframe['volume'] * self.volume_drop_multiplier.value
|
||||
ema_dead_cross = dataframe['ema_5_1h'] < dataframe['ema_10_1h']
|
||||
exit_condition_2 = volume_spike_new & volume_drop & ema_dead_cross
|
||||
|
||||
# 条件3: CCI超买回落 + 抛物线SAR反转
|
||||
cci_overbought_fallback = (dataframe['cci_1h_prev'] > self.cci_overbought_level.value) & \
|
||||
(dataframe['cci_1h'] < self.cci_overbought_level.value)
|
||||
sar_reversal = dataframe['close'] < dataframe['sar_1h']
|
||||
exit_condition_3 = cci_overbought_fallback & sar_reversal
|
||||
rsi_overbought = dataframe['rsi_1h'] > self.rsi_overbought.value
|
||||
|
||||
# 合并所有条件
|
||||
final_condition = (breakout_condition | volume_spike | macd_downward | rsi_overbought_old) | \
|
||||
(exit_condition_1 | exit_condition_2 | exit_condition_3)
|
||||
final_condition = breakout_condition | volume_spike | macd_downward | rsi_overbought
|
||||
|
||||
# 设置出场信号
|
||||
dataframe.loc[final_condition, 'exit_long'] = 1
|
||||
|
||||
# 增强调试信息
|
||||
#logger.info(f"[{metadata['pair']}] 出场条件检查:")
|
||||
#logger.info(f" - 原有条件: 价格突破布林带上轨: {breakout_condition.sum()} 次, 成交量显著放大: {volume_spike.sum()} 次, MACD下降趋势: {macd_downward.sum()} 次, RSI超买: {rsi_overbought_old.sum()} 次")
|
||||
#logger.info(f" - 新增条件1 (MACD死叉+RSI回落): {exit_condition_1.sum()} 次")
|
||||
#logger.info(f" - 新增条件2 (放量缩量+EMA死叉): {exit_condition_2.sum()} 次")
|
||||
#logger.info(f" - 新增条件3 (CCI回落+SAR反转): {exit_condition_3.sum()} 次")
|
||||
#logger.info(f" - 价格突破布林带上轨: {breakout_condition.sum()} 次")
|
||||
#logger.info(f" - 成交量显著放大: {volume_spike.sum()} 次")
|
||||
#logger.info(f" - MACD 下降趋势: {macd_downward.sum()} 次")
|
||||
#logger.info(f" - RSI 超买: {rsi_overbought.sum()} 次")
|
||||
#logger.info(f" - 最终条件: {final_condition.sum()} 次")
|
||||
#logger.info(f" - 使用参数: exit_bb_upper_deviation={self.exit_bb_upper_deviation.value}, exit_volume_multiplier={self.exit_volume_multiplier.value}, rsi_overbought={self.rsi_overbought.value}")
|
||||
#logger.info(f" - 新增参数: rsi_overbought_level={self.rsi_overbought_level.value}, macd_cross_threshold={self.macd_cross_threshold.value}, volume_spike_multiplier={self.volume_spike_multiplier.value}, volume_drop_multiplier={self.volume_drop_multiplier.value}, cci_overbought_level={self.cci_overbought_level.value}")
|
||||
|
||||
# 日志记录
|
||||
if dataframe['exit_long'].sum() > 0:
|
||||
@ -586,32 +677,35 @@ class FreqaiPrimer(IStrategy):
|
||||
# 获取当前市场状态
|
||||
current_state = dataframe['market_state'].iloc[-1] if 'market_state' in dataframe.columns else 'unknown'
|
||||
|
||||
# 基础止损倍数
|
||||
base_multiplier = 1.2
|
||||
|
||||
# 更激进的渐进式止损策略
|
||||
if current_profit > 0.05: # 利润超过5%时
|
||||
return -3.0 * atr / current_rate # 更大幅扩大止损范围,让利润奔跑
|
||||
return -3.0 * base_multiplier * atr / current_rate
|
||||
elif current_profit > 0.03: # 利润超过3%时
|
||||
return -2.5 * atr / current_rate # 更中等扩大止损范围
|
||||
return -2.5 * base_multiplier * atr / current_rate
|
||||
elif current_profit > 0.01: # 利润超过1%时
|
||||
return -2.0 * atr / current_rate # 更轻微扩大止损范围
|
||||
return -2.0 * base_multiplier * atr / current_rate
|
||||
|
||||
# 在强劲牛市中,即使小亏损也可以容忍更大回调
|
||||
if current_state == 'strong_bull' and current_profit > -0.01:
|
||||
return -1.5 * atr / current_rate
|
||||
return -1.5 * base_multiplier * atr / current_rate
|
||||
|
||||
# 动态调整止损范围
|
||||
if current_profit > 0.05: # 利润超过5%时
|
||||
return -3.0 * atr / current_rate # 更大幅扩大止损范围,让利润奔跑
|
||||
return -3.0 * base_multiplier * atr / current_rate
|
||||
elif current_profit > 0.03: # 利润超过3%时
|
||||
return -2.5 * atr / current_rate # 更中等扩大止损范围
|
||||
return -2.5 * base_multiplier * atr / current_rate
|
||||
elif current_profit > 0.01: # 利润超过1%时
|
||||
return -2.0 * atr / current_rate # 更轻微扩大止损范围
|
||||
return -2.0 * base_multiplier * atr / current_rate
|
||||
|
||||
# 在强劲牛市中,即使小亏损也可以容忍更大回调
|
||||
if current_state == 'strong_bull' and current_profit > -0.01:
|
||||
return -1.8 * atr / current_rate
|
||||
return -1.8 * base_multiplier * atr / current_rate
|
||||
|
||||
if atr > 0:
|
||||
return -1.2 * atr / current_rate # 基础1.2倍ATR止损
|
||||
return -base_multiplier * atr / current_rate
|
||||
return self.stoploss
|
||||
|
||||
def custom_exit(self, pair: str, trade: Trade, current_time: datetime, current_rate: float,
|
||||
@ -623,6 +717,8 @@ class FreqaiPrimer(IStrategy):
|
||||
if trade_age_minutes < 0:
|
||||
trade_age_minutes = 0
|
||||
|
||||
|
||||
|
||||
# 使用可优化的线性函数: y = (a * (x + k)) + t
|
||||
a = self.roi_param_a.value # 系数a (可优化参数)
|
||||
k = self.roi_param_k.value # 偏移量k (可优化参数)
|
||||
@ -652,6 +748,8 @@ class FreqaiPrimer(IStrategy):
|
||||
if entry_tag == 'strong_trend':
|
||||
exit_ratio *= 0.8
|
||||
|
||||
|
||||
|
||||
if dynamic_roi_threshold < 0:
|
||||
exit_ratio = 1.0
|
||||
|
||||
@ -685,19 +783,25 @@ class FreqaiPrimer(IStrategy):
|
||||
return 0.0
|
||||
price_diff_pct = (current_rate - initial_price) / initial_price
|
||||
|
||||
# 计算加仓次数(从1开始计数)
|
||||
adjustment_count = entry_count - 1 # 已加仓次数
|
||||
|
||||
# 检查价格回调是否达到加仓间隔
|
||||
dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
|
||||
current_state = dataframe['market_state'].iloc[-1] if 'market_state' in dataframe.columns else 'neutral'
|
||||
|
||||
if price_diff_pct <= -self.add_position_callback.value and current_state not in ['bear', 'weak_bear']:
|
||||
# 获取当前币对的波动系数,用于动态调整回调百分比
|
||||
volatility_coef = self.get_volatility_coefficient(pair)
|
||||
|
||||
# 计算当前所需的加仓间隔百分比 = 基础间隔 * (系数 ^ 已加仓次数)
|
||||
current_callback = self.add_position_callback.value * (self.add_position_multiplier.value ** adjustment_count) * volatility_coef
|
||||
|
||||
if price_diff_pct <= -current_callback:
|
||||
# 计算初始入场金额
|
||||
initial_stake = trade.orders[0].cost # 第一笔订单的成本
|
||||
|
||||
# 计算加仓次数(从1开始计数)
|
||||
adjustment_count = entry_count - 1 # 已加仓次数
|
||||
|
||||
# 计算加仓金额: (initial_stake / stake_divisor) ^ (adjustment_count + 1)
|
||||
additional_stake = (self.step_coefficient.value * initial_stake / self.stake_divisor.value) ** (adjustment_count + 1)
|
||||
additional_stake = (initial_stake / self.stake_divisor.value) * (self.add_position_growth.value ** (adjustment_count + 1))
|
||||
|
||||
# 确保加仓金额在允许的范围内
|
||||
additional_stake = max(min_stake, min(additional_stake, max_stake - trade.stake_amount))
|
||||
@ -709,3 +813,42 @@ class FreqaiPrimer(IStrategy):
|
||||
|
||||
# 不符合加仓条件,返回0
|
||||
return 0.0
|
||||
|
||||
def custom_stake_amount(self, pair: str, current_time: datetime, **kwargs) -> float:
|
||||
"""
|
||||
根据波动系数动态调整初始仓位大小
|
||||
- 波动率高的币对分配较小的仓位
|
||||
- 波动率低的币对可以分配较大的仓位
|
||||
"""
|
||||
# 返回默认仓位大小
|
||||
return self.stake_amount
|
||||
|
||||
def confirm_trade_entry(
|
||||
self,
|
||||
pair: str,
|
||||
order_type: str,
|
||||
amount: float,
|
||||
rate: float,
|
||||
time_in_force: str,
|
||||
current_time: datetime,
|
||||
entry_tag: str | None,
|
||||
side: str,
|
||||
**kwargs,
|
||||
) -> bool:
|
||||
"""
|
||||
交易买入前的确认函数,用于最终决定是否执行交易
|
||||
此处实现剧烈拉升检查逻辑
|
||||
"""
|
||||
# 默认允许交易
|
||||
allow_trade = True
|
||||
|
||||
# 仅对多头交易进行检查
|
||||
if side == 'long':
|
||||
# 检查是否处于剧烈拉升的不稳固区域
|
||||
is_unstable_region = self.detect_h1_rapid_rise(pair)
|
||||
if is_unstable_region:
|
||||
#logger.info(f"[{pair}] 由于检测到剧烈拉升,取消入场交易")
|
||||
allow_trade = False
|
||||
|
||||
# 如果没有阻止因素,允许交易
|
||||
return allow_trade
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user