线性映射加仓阈值

This commit is contained in:
zhangkun9038@dingtalk.com 2025-06-23 02:22:42 +08:00
parent 0b00430b9a
commit eb1c40cbf5
2 changed files with 117 additions and 66 deletions

View File

@ -8,33 +8,33 @@
"max_open_trades": 5
},
"buy": {
"ADD_POSITION_THRESHOLD": -0.042,
"BUY_THRESHOLD_MAX": -0.008,
"BUY_THRESHOLD_MIN": -0.045,
"COOLDOWN_PERIOD_MINUTES": 6,
"MAX_ENTRY_POSITION_ADJUSTMENT": 2
"ADD_POSITION_THRESHOLD": -0.01,
"BUY_THRESHOLD_MAX": -0.011,
"BUY_THRESHOLD_MIN": -0.079,
"COOLDOWN_PERIOD_MINUTES": 8,
"MAX_ENTRY_POSITION_ADJUSTMENT": 1
},
"sell": {
"EXIT_POSITION_RATIO": 0.596,
"SELL_THRESHOLD_MAX": 0.099,
"SELL_THRESHOLD_MIN": 0.01,
"TRAILING_STOP_DISTANCE": 0.02,
"TRAILING_STOP_START": 0.048
"EXIT_POSITION_RATIO": 0.341,
"SELL_THRESHOLD_MAX": 0.082,
"SELL_THRESHOLD_MIN": 0.012,
"TRAILING_STOP_DISTANCE": 0.016,
"TRAILING_STOP_START": 0.026
},
"protection": {},
"roi": {
"0": 0.081,
"17": 0.058,
"23": 0.01,
"30": 0
"0": 0.057,
"15": 0.025,
"22": 0.013,
"39": 0
},
"trailing": {
"trailing_stop": true,
"trailing_stop_positive": 0.054,
"trailing_stop_positive_offset": 0.101,
"trailing_only_offset_is_reached": true
"trailing_stop_positive": 0.139,
"trailing_stop_positive_offset": 0.23700000000000002,
"trailing_only_offset_is_reached": false
}
},
"ft_stratparam_v": 1,
"export_time": "2025-06-21 14:12:39.140743+00:00"
"export_time": "2025-06-22 18:22:25.969981+00:00"
}

View File

@ -374,77 +374,114 @@ class FreqaiPrimer(IStrategy):
current_entry_profit: float, current_exit_profit: float,
**kwargs) -> float | None | tuple[float | None, str | None]:
"""
动态调整仓位支持加仓和分批减仓
动态调整仓位支持加仓减仓追踪止损和最大持仓时间限制
参数
- trade: 当前交易对象
- current_time: 当前时间
- current_rate: 当前价格
- current_profit: 当前总盈利
- min_stake: 最小下注金额
- max_stake: 最大下注金额
- current_entry_rate: 当前入场价格
- current_exit_rate: 当前退出价格
- current_entry_profit: 当前入场盈利
- current_exit_profit: 当前退出盈利
返回
- 调整金额正数为加仓负数为减仓 None
"""
pair = trade.pair
dataframe = self.dp.get_pair_dataframe(pair, self.timeframe)
trend_score = self.get_market_trend(dataframe=dataframe, metadata={'pair': pair})
hold_time = (current_time - trade.open_date_utc).total_seconds() / 60
market_trend_score = self.get_market_trend()
profit_ratio = (current_rate - trade.open_rate) / trade.open_rate
# --- 加仓逻辑 ---
max_entry_adjustments = self.MAX_ENTRY_POSITION_ADJUSTMENT.value
if trade.nr_of_successful_entries <= max_entry_adjustments + 1: # +1 包括初始入场
add_position_threshold = self.ADD_POSITION_THRESHOLD.value
if profit_ratio <= add_position_threshold and hold_time > 5:
if market_trend_score <= 60: # 熊市或中性更倾向加仓
add_amount = 0.5 * trade.stake_amount # 加仓 50% 的初始仓位
if min_stake is not None and add_amount < min_stake:
logger.warning(f"[{pair}] 加仓金额 {add_amount:.2f} 低于最小下单金额 {min_stake},取消加仓")
return (None, "Add amount below min_stake")
if add_amount > max_stake:
logger.warning(f"[{pair}] 加仓金额 {add_amount:.2f} 超出最大可用金额 {max_stake},取消加仓")
return (None, "Add amount exceeds max_stake")
logger.info(f"[{pair}] 价格下跌 {profit_ratio*100:.2f}%,触发加仓 {add_amount:.2f}")
return (add_amount, f"Price dropped {profit_ratio*100:.2f}%")
else:
logger.debug(f"[{pair}] 价格下跌但市场趋势得分 {market_trend_score},不加仓")
return None
initial_stake_amount = trade.stake_amount / 3
logger.debug(f"{pair} 首次入场金额: {initial_stake_amount:.2f}, 当前持仓金额: {trade.stake_amount:.2f}, "
f"加仓次数: {trade.nr_of_successful_entries - 1}")
# --- 减仓逻辑 ---
# 动态最大持仓时间
# max_hold_time = self.MAX_HOLD_TIME_MINUTES.value
# # 线性映射持仓时间因子
# hold_factor = self.HOLD_TIME_REDUCTION.value + (self.HOLD_TIME_EXTENSION.value - self.HOLD_TIME_REDUCTION.value) * (trend_score / 100)
# max_hold_time *= hold_factor
# if profit_ratio > 0.02:
# max_hold_time *= 1.2
# elif profit_ratio < -0.02:
# max_hold_time *= 0.8
# max_hold_time = int(max_hold_time)
# 加仓逻辑
max_entry_adjustments = self.MAX_ENTRY_POSITION_ADJUSTMENT.value
if trade.nr_of_successful_entries <= max_entry_adjustments + 1:
add_position_threshold = self.ADD_POSITION_THRESHOLD.value
# 线性映射加仓阈值,趋势值越高,加仓越严格
add_threshold = 80 - 30 * (trend_score / 100) # 趋势值 100 -> 50, 0 -> 80
if profit_ratio <= add_position_threshold and hold_time > 5 and trend_score <= add_threshold:
logger.debug(f"{pair} 初始下注金额: {initial_stake_amount:.2f}, trend_score: {trend_score:.2f}, add_threshold: {add_threshold} ")
# 计算加仓金额
add_count = trade.nr_of_successful_entries - 1
multipliers = [2, 4, 8]
if add_count < len(multipliers):
multiplier = multipliers[add_count]
add_amount = initial_stake_amount * multiplier
logger.debug(f"{pair}{add_count + 1} 次加仓,倍数={multiplier}, "
f"金额 = {initial_stake_amount:.2f} * {multiplier} = {add_amount:.2f}")
logger.debug(f"{pair} 加仓计算: 第 {add_count + 1} 次加仓,倍数={multiplier}, "
f"金额 = {initial_stake_amount:.2f} * {multiplier} = {add_amount:.2f}")
if min_stake is not None and add_amount < min_stake:
logger.warning(f"{pair} 加仓金额 {add_amount:.2f} 低于最小下注金额 {min_stake:.2f},取消加仓")
return (None, f"Add amount {add_amount:.2f} below min_stake {min_stake:.2f}")
if add_amount > max_stake:
logger.warning(f"{pair} 加仓金额 {add_amount:.2f} 超出最大可用金额 {max_stake:.2f},调整为 {max_stake:.2f}")
add_amount = max_stake
logger.info(f"{pair} 价格下跌 {profit_ratio*100:.2f}%,触发第 {add_count + 1} 次加仓 {add_amount:.2f}")
return (add_amount, f"Price dropped {profit_ratio*100:.2f}%, add {add_amount:.2f}")
# 减仓逻辑
exit_position_ratio = self.EXIT_POSITION_RATIO.value
if profit_ratio >= 0.03: # 利润达到 3%
if market_trend_score > 70: # 强牛市
reduce_amount = -exit_position_ratio * 0.6 * trade.stake_amount # 减仓较少
logger.info(f"[{pair}] 强牛市(得分 {market_trend_score}),利润 {profit_ratio*100:.2f}%,减仓 {abs(reduce_amount):.2f}")
return (reduce_amount, f"Strong bull market, profit {profit_ratio*100:.2f}%")
else:
reduce_amount = -exit_position_ratio * trade.stake_amount # 其他市场减仓较多
logger.info(f"[{pair}] 利润 {profit_ratio*100:.2f}%,减仓 {abs(reduce_amount):.2f}")
return (reduce_amount, f"Profit {profit_ratio*100:.2f}%")
elif profit_ratio >= 0.05: # 利润达到 5%
reduce_amount = -exit_position_ratio * 1.4 * trade.stake_amount # 卖出更多
logger.info(f"[{pair}] 利润 {profit_ratio*100:.2f}%,减仓 {abs(reduce_amount):.2f}")
if profit_ratio >= 0.03:
# 趋势值越高,减仓比例越低
reduce_factor = 0.6 + 0.4 * (1 - trend_score / 100) # 牛市(100) -> 0.6, 熊市(0) -> 1.0
reduce_amount = -exit_position_ratio * reduce_factor * trade.stake_amount
logger.info(f"{pair} 趋势值 {trend_score:.2f},利润 {profit_ratio*100:.2f}%,减仓 {abs(reduce_amount):.2f}")
return (reduce_amount, f"Profit {profit_ratio*100:.2f}%")
elif profit_ratio >= 0.05:
reduce_factor = 1.4 - 0.4 * (trend_score / 100) # 牛市(100) -> 1.0, 熊市(0) -> 1.4
reduce_amount = -exit_position_ratio * reduce_factor * trade.stake_amount
logger.info(f"{pair} 趋势值 {trend_score:.2f},利润 {profit_ratio*100:.2f}%,减仓 {abs(reduce_amount):.2f}")
return (reduce_amount, f"Profit {profit_ratio*100:.2f}%")
# --- 追踪止损逻辑 ---
# 追踪止损逻辑
trailing_stop_start = self.TRAILING_STOP_START.value
trailing_stop_distance = self.TRAILING_STOP_DISTANCE.value
if market_trend_score > 70: # 强牛市
trailing_stop_distance *= 1.5
trailing_stop_start *= 1.2
elif market_trend_score < 30: # 强熊市
trailing_stop_distance *= 0.7
trailing_stop_start *= 0.8
# 使用 Sigmoid 映射调整追踪止损参数
sigmoid = 1 / (1 + np.exp(-0.1 * (trend_score - 50)))
trailing_factor = 0.8 + (1.2 - 0.8) * sigmoid # 牛市(100) -> 1.2, 熊市(0) -> 0.8
distance_factor = 0.7 + (1.5 - 0.7) * sigmoid # 牛市(100) -> 1.5, 熊市(0) -> 0.7
trailing_stop_start *= trailing_factor
trailing_stop_distance *= distance_factor
if profit_ratio >= trailing_stop_start and not self.trailing_stop_enabled:
self.trailing_stop_enabled = True
trade.adjust_min_max_rates(current_rate, current_rate)
logger.info(f"[{pair}] 价格上涨超过 {trailing_stop_start*100:.1f}%,启动 Trailing Stop")
logger.info(f"{pair} 价格上涨超过 {trailing_stop_start*100:.1f}%,启动追踪止损")
return None
if self.trailing_stop_enabled:
max_rate = trade.max_rate
max_rate = trade.max_rate or current_rate
trailing_stop_price = max_rate * (1 - trailing_stop_distance)
if current_rate < trailing_stop_price:
logger.info(f"[{pair}] 价格回落至 Trailing Stop 点 {trailing_stop_price:.6f},触发全部卖出")
logger.info(f"{pair} 价格回落至 {trailing_stop_price:.6f},触发全部卖出")
return (-trade.stake_amount, f"Trailing stop at {trailing_stop_price:.6f}")
trade.adjust_min_max_rates(current_rate, trade.min_rate)
return None
# --- 最大持仓时间限制 ---
if hold_time > 30:
logger.info(f"[{pair}] 持仓时间超过30分钟强制清仓")
return (-trade.stake_amount, "Max hold time exceeded")
# 最大持仓时间限制
# if hold_time > max_hold_time:
# logger.info(f"{pair} 趋势值 {trend_score:.2f},持仓时间超过 {max_hold_time} 分钟,强制清仓")
# return (-trade.stake_amount, f"Max hold time ({max_hold_time} min) exceeded")
return None
@ -499,7 +536,8 @@ class FreqaiPrimer(IStrategy):
logger.debug(f"[{pair}] 自定义卖出价:{adjusted_rate:.6f}(原价:{proposed_rate:.6f}")
return adjusted_rate
def get_market_trend(self, dataframe: DataFrame = None) -> int:
def get_market_trend(self, dataframe: DataFrame = None, metadata: dict = None) -> int:
"""
计算市场趋势得分0 表示强熊100 表示强牛50 表示中性
使用对数函数映射非线性增强两端趋势
@ -512,6 +550,19 @@ class FreqaiPrimer(IStrategy):
int: 0-100 的趋势得分
"""
try:
# 获取 BTC/USDT 数据
if dataframe is None:
btc_df = self.dp.get_pair_dataframe("BTC/USDT", self.timeframe)
else:
btc_df = dataframe
if len(btc_df) < 200:
logger.warning("BTC 数据不足返回默认趋势得分50")
return 50
# 示例:使用 metadata 记录币种信息
pair = metadata.get('pair', 'Unknown') if metadata else 'Unknown'
logger.debug(f"[{pair}] 正在计算市场趋势得分")
# 获取 BTC/USDT 数据
btc_df = dataframe if dataframe is not None else self.dp.get_pair_dataframe("BTC/USDT", self.timeframe)
if len(btc_df) < 200: