优化退出条件, 试图提升获利空间 https://x.com/i/grok?conversation=1940703718133125312

This commit is contained in:
zhangkun9038@dingtalk.com 2025-07-03 12:33:15 +00:00
parent 04ad9ee171
commit e6a35a8506
5 changed files with 201 additions and 6 deletions

View File

@ -0,0 +1,64 @@
--- original_strategy.py
+++ modified_strategy.py
@@ -297,6 +297,9 @@ class FreqaiPrimer(IStrategy):
dataframe["bb_lowerband"] = lowerband
dataframe["rsi"] = ta.RSI(dataframe, timeperiod=14)
dataframe["volume_mean_20"] = dataframe["volume"].rolling(20).mean()
dataframe["volume_std_20"] = dataframe["volume"].rolling(20).std()
dataframe["volume_z_score"] = (dataframe["volume"] - dataframe["volume_mean_20"]) / dataframe["volume_std_20"]
+ # 计算15分钟ATR和3分钟EMA10、EMA20
+ dataframe_15m = self.dp.get_pair_dataframe(pair=pair, timeframe="15m")
+ cuyuan
+ dataframe_15m['atr15m'] = ta.ATR(dataframe_15m, timeperiod=14)
+ dataframe['atr15m'] = dataframe_15m['atr15m'].reindex(dataframe.index, method="ffill")
+ dataframe['ema10'] = ta.EMA(dataframe, timeperiod=10)
+ dataframe['ema20'] = ta.EMA(dataframe, timeperiod=20)
@@ -390,8 +390,11 @@ class FreqaiPrimer(IStrategy):
conditions = []
if "&-price_value_divergence" in dataframe.columns:
cond1 = (dataframe["&-price_value_divergence"] > self.sell_threshold * 1.025)
- cond2 = (dataframe["rsi"] > 75)
+ rsi_threshold = 70 if trend_score > 50 else 65 # 动态RSI阈值
+ cond2 = (dataframe["rsi"] > rsi_threshold)
+ cond3 = (dataframe['ema10'] < dataframe['ema20']) # 短期均线交叉
sell_condition = cond1 | cond2
conditions.append(sell_condition)
@@ -442,6 +442,13 @@ class FreqaiPrimer(IStrategy):
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}")
+ # 分批出场:短期涨幅触发
+ short_term_return = (current_rate - dataframe['close'].shift(3).iloc[-1]) / dataframe['close'].shift(3).iloc[-1]
+ if short_term_return >= 0.03: # 3%涨幅
+ reduce_amount = -0.5 * trade.stake_amount # 卖出50%仓位
+ logger.info(f"{pair} 短期涨幅 {short_term_return*100:.2f}%卖出50%仓位 {abs(reduce_amount):.2f}")
+ return (reduce_amount, f"Short-term gain {short_term_return*100:.2f}%")
+
# 加仓逻辑
max_entry_adjustments = self.MAX_ENTRY_POSITION_ADJUSTMENT.value
@@ -484,12 +484,11 @@ class FreqaiPrimer(IStrategy):
# 追踪止损逻辑
trailing_stop_start = self.TRAILING_STOP_START.value
trailing_stop_distance = self.TRAILING_STOP_DISTANCE.value
- # 使用 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
+ # 使用15分钟ATR动态调整止损距离
+ atr15m = dataframe['atr15m'].iloc[-1]
+ trailing_stop_distance = min(max(2 * atr15m / current_rate, 0.01), 0.05) # 2倍ATR限制在0.01-0.05
+ if trend_score > 50: # 强趋势时放宽止损
+ trailing_stop_distance *= 1.2
+ trailing_stop_price = max_rate * (1 - trailing_stop_distance)
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}%,启动追踪止损")
return None

View File

@ -0,0 +1,35 @@
{
"strategy_name": "FreqaiPrimer",
"params": {
"roi": {},
"stoploss": {
"stoploss": -0.05
},
"max_open_trades": {
"max_open_trades": 5
},
"buy": {
"ADD_POSITION_THRESHOLD": -0.021,
"BUY_THRESHOLD_MAX": -0.001,
"BUY_THRESHOLD_MIN": -0.035,
"COOLDOWN_PERIOD_MINUTES": 9,
"MAX_ENTRY_POSITION_ADJUSTMENT": 3
},
"sell": {
"EXIT_POSITION_RATIO": 0.472,
"SELL_THRESHOLD_MAX": 0.065,
"SELL_THRESHOLD_MIN": 0.002,
"TRAILING_STOP_DISTANCE": 0.015,
"TRAILING_STOP_START": 0.016
},
"protection": {},
"trailing": {
"trailing_stop": true,
"trailing_stop_positive": 0.106,
"trailing_stop_positive_offset": 0.196,
"trailing_only_offset_is_reached": false
}
},
"ft_stratparam_v": 1,
"export_time": "2025-07-01 14:51:29.420394+00:00"
}

View File

@ -352,20 +352,67 @@ class FreqaiPrimer(IStrategy):
conditions = []
if "&-price_value_divergence" in dataframe.columns:
cond1 = (dataframe["&-price_value_divergence"] > self.sell_threshold * 1.025)
cond2 = (dataframe["rsi"] > 75)
sell_condition = cond1 | cond2
# 计算额外指标StochRSI、ADX 和短期价格变化
stochrsi = ta.STOCHRSI(dataframe, timeperiod=14, fastk_period=3, fastd_period=3)
dataframe["stochrsi_k"] = stochrsi["fastk"]
dataframe["adx"] = ta.ADX(dataframe, timeperiod=14)
# 计算短期价格涨幅(最近 5 根 K 线,约 15 分钟)
dataframe["short_term_return"] = dataframe["close"].pct_change(5) * 100 # 百分比回报
# 获取市场趋势得分
trend_score = self.get_market_trend(dataframe=dataframe, metadata={'pair': pair})
# 条件 1高阈值 &-price_value_divergence
cond1 = (
(dataframe["&-price_value_divergence"] > self.sell_threshold * 1.06) & # 提高到 1.06
(dataframe["adx"] > 20) # 趋势强度过滤
)
# 条件 2超买信号
cond2 = (
(dataframe["rsi"] > 65) &
(dataframe["stochrsi_k"] > 70) & # StochRSI 超买
(dataframe["adx"] > 25) # 趋势强度
)
# 条件 3快速拉升退出
# 检测最近 5 根 K 线(约 15 分钟)涨幅超过 3%,且有最低 2% 利润,结合 StochRSI 超买
min_profit = 0.02 # 最低利润 2%
rapid_rise_threshold = self.linear_map(trend_score, 0, 100, 4.0, 2.5) # 熊市 4%,牛市 2.5%
cond3 = (
(dataframe["short_term_return"] > rapid_rise_threshold) & # 短期快速拉升
(dataframe["close"] / dataframe["close"].shift(5) - 1 > min_profit) & # 确保最低利润
(dataframe["stochrsi_k"] > 80) & # 超买确认
(dataframe["adx"] > 20) # 趋势强度
)
# 综合卖出条件:根据 trend_score 调整逻辑
if trend_score > 85:
sell_condition = (cond1 & cond2) | (cond1 & cond3) | (cond2 & cond3) # 中等趋势,至少两个条件满足
logger.debug(f"[{pair}] 趋势得分 {trend_score:.2f} > 55需满足至少两个条件")
else:
sell_condition = cond1 | cond2 | cond3 # 弱势趋势,任一条件满足
logger.debug(f"[{pair}] 趋势得分 {trend_score:.2f} <= 55任一条件满足")
conditions.append(sell_condition)
# 调试日志
divergence_value = dataframe["&-price_value_divergence"].iloc[-1] if not dataframe["&-price_value_divergence"].isna().all() else np.nan
rsi_value = dataframe["rsi"].iloc[-1] if not dataframe["rsi"].isna().all() else np.nan
stochrsi_value = dataframe["stochrsi_k"].iloc[-1] if not dataframe["stochrsi_k"].isna().all() else np.nan
adx_value = dataframe["adx"].iloc[-1] if not dataframe["adx"].isna().all() else np.nan
short_term_return = dataframe["short_term_return"].iloc[-1] if not dataframe["short_term_return"].isna().all() else np.nan
logger.debug(f"[{pair}] 卖出条件检查 - "
f"&-price_value_divergence > {self.sell_threshold:.6f}: {cond1.iloc[-1]}, "
f"rsi > 75: {cond2.iloc[-1]}")
f"&-price_value_divergence={divergence_value:.6f} > {self.sell_threshold * 1.06:.6f}: {cond1.iloc[-1]}, "
f"rsi={rsi_value:.2f} > 75 & stochrsi_k={stochrsi_value:.2f} > 80: {cond2.iloc[-1]}, "
f"short_term_return={short_term_return:.2f}% > {rapid_rise_threshold:.2f}% & profit > {min_profit*100:.2f}%: {cond3.iloc[-1]}, "
f"adx={adx_value:.2f}, trend_score={trend_score:.2f}")
else:
logger.warning(f"[{pair}] ⚠️ &-price_value_divergence 列缺失,跳过该条件")
if len(conditions) > 0:
dataframe.loc[reduce(lambda x, y: x & y, conditions), 'exit_long'] = 1
logger.debug(f"[{pair}] 出场信号触发,条件满足")
logger.info(f"[{pair}] 出场信号触发,条件满足,趋势得分:{trend_score:.2f}")
else:
logger.debug(f"[{pair}] 无有效卖出条件")

45
tools/exchanges.py Normal file
View File

@ -0,0 +1,45 @@
import pandas as pd
# 加载回测结果
df = pd.read_csv('../result/backtest_trades.csv')
# 转换时间为 datetime 类型
df['open_date'] = pd.to_datetime(df['open_date'])
df['close_date'] = pd.to_datetime(df['close_date'])
# 按币种分组
grouped = df.groupby('pair')
# 存储最终结果
results = []
for pair, group in grouped:
# 按照开仓时间排序
group = group.sort_values(by='open_date')
for _, trade in group.iterrows():
entry_time = trade['open_date']
exit_time = trade['close_date']
entry_price = trade['open_rate']
exit_price = trade['close_rate']
profit_abs = trade['profit_abs']
profit_ratio = trade['profit_ratio']
results.append({
'币种': pair,
'入场时间': entry_time,
'入场价格': entry_price,
'出场时间': exit_time,
'出场价格': exit_price,
'盈利金额': profit_abs,
'盈利比例': f"{profit_ratio * 100:.2f}%"
})
# 输出为 DataFrame 并保存到 CSV
trade_log = pd.DataFrame(results)
trade_log.to_csv('../result/trade_log_detailed.csv', index=False)
print("✅ 已生成详细交易日志:../result/trade_log_detailed.csv")
print(trade_log)

4
tools/exchanges.sh Executable file
View File

@ -0,0 +1,4 @@
cd ../
source .venv/bin/activate
cd -
python exchanges.py