优化退出条件, 试图提升获利空间 https://x.com/i/grok?conversation=1940703718133125312
This commit is contained in:
parent
04ad9ee171
commit
e6a35a8506
64
freqtrade/templates/df.diff
Normal file
64
freqtrade/templates/df.diff
Normal 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
|
||||
35
freqtrade/templates/freqaiprimer.json.orig
Normal file
35
freqtrade/templates/freqaiprimer.json.orig
Normal 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"
|
||||
}
|
||||
@ -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
45
tools/exchanges.py
Normal 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
4
tools/exchanges.sh
Executable file
@ -0,0 +1,4 @@
|
||||
cd ../
|
||||
source .venv/bin/activate
|
||||
cd -
|
||||
python exchanges.py
|
||||
Loading…
x
Reference in New Issue
Block a user