https://x.com/i/grok?conversation=1932621153291616573 hyperopt 20250510 20250515 性能杠杠的
This commit is contained in:
parent
b89762d805
commit
8ba203f2b1
@ -2,9 +2,9 @@
|
||||
"$schema": "https://schema.freqtrade.io/schema.json",
|
||||
"trading_mode": "spot",
|
||||
"margin_mode": "isolated",
|
||||
"max_open_trades": 4,
|
||||
"max_open_trades": 5,
|
||||
"stake_currency": "USDT",
|
||||
"stake_amount": 150,
|
||||
"stake_amount": 100,
|
||||
"tradable_balance_ratio": 1,
|
||||
"fiat_display_currency": "USD",
|
||||
"dry_run": true,
|
||||
@ -12,6 +12,9 @@
|
||||
"dry_run_wallet": 1000,
|
||||
"cancel_open_orders_on_exit": true,
|
||||
"stoploss": -0.05,
|
||||
"max_entry_position_adjustment": 3,
|
||||
"position_adjustment_enable": true,
|
||||
"amount_reserve_percent": 0.05,
|
||||
"unfilledtimeout": {
|
||||
"entry": 5,
|
||||
"exit": 15
|
||||
@ -23,7 +26,7 @@
|
||||
"name": "okx",
|
||||
"key": "314e75b0-1113-47e8-ad01-1fca7e3c0496",
|
||||
"secret": "9C8B170390F46EA6FB87592AD46F5A34",
|
||||
"password": "nekFoylf:Om0",
|
||||
"password": "nekFoylf:Om0",
|
||||
"enable_ws": false,
|
||||
"ccxt_config": {
|
||||
"enableRateLimit": true,
|
||||
|
||||
@ -1,36 +1,40 @@
|
||||
{
|
||||
"strategy_name": "FreqaiPrimer",
|
||||
"params": {
|
||||
"stoploss": {
|
||||
"stoploss": -0.05
|
||||
},
|
||||
"max_open_trades": {
|
||||
"max_open_trades": 4
|
||||
"max_open_trades": 5
|
||||
},
|
||||
"buy": {
|
||||
"ADD_POSITION_THRESHOLD": -0.015,
|
||||
"BUY_THRESHOLD_MAX": -0.005,
|
||||
"BUY_THRESHOLD_MIN": -0.05
|
||||
"BUY_THRESHOLD_MIN": -0.061,
|
||||
"COOLDOWN_PERIOD_MINUTES": 10,
|
||||
"MAX_ENTRY_POSITION_ADJUSTMENT": 2
|
||||
},
|
||||
"sell": {
|
||||
"SELL_THRESHOLD_MAX": 0.05,
|
||||
"SELL_THRESHOLD_MIN": 0.005,
|
||||
"TRAILING_STOP_DISTANCE": 0.01,
|
||||
"TRAILING_STOP_START": 0.03
|
||||
"EXIT_POSITION_RATIO": 0.547,
|
||||
"SELL_THRESHOLD_MAX": 0.043,
|
||||
"SELL_THRESHOLD_MIN": 0.012,
|
||||
"TRAILING_STOP_DISTANCE": 0.005,
|
||||
"TRAILING_STOP_START": 0.013
|
||||
},
|
||||
"protection": {},
|
||||
"roi": {
|
||||
"0": 0.028,
|
||||
"12": 0.016,
|
||||
"48": 0.009,
|
||||
"120": 0
|
||||
},
|
||||
"stoploss": {
|
||||
"stoploss": -0.279
|
||||
"0": 0.186,
|
||||
"7": 0.049,
|
||||
"15": 0.008,
|
||||
"79": 0
|
||||
},
|
||||
"trailing": {
|
||||
"trailing_stop": true,
|
||||
"trailing_stop_positive": 0.237,
|
||||
"trailing_stop_positive_offset": 0.318,
|
||||
"trailing_stop_positive": 0.094,
|
||||
"trailing_stop_positive_offset": 0.181,
|
||||
"trailing_only_offset_is_reached": false
|
||||
}
|
||||
},
|
||||
"ft_stratparam_v": 1,
|
||||
"export_time": "2025-06-06 14:26:19.180290+00:00"
|
||||
"export_time": "2025-06-11 17:45:35.898655+00:00"
|
||||
}
|
||||
@ -15,26 +15,27 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
class FreqaiPrimer(IStrategy):
|
||||
"""
|
||||
基于 FreqAI 的动态阈值交易策略
|
||||
基于 FreqAI 的动态阈值交易策略,集成动态加仓和减仓逻辑,兼容最新 Freqtrade 版本
|
||||
"""
|
||||
|
||||
# --- 🧪 Hyperopt Parameters ---
|
||||
|
||||
# ROI 参数(用于收益目标)
|
||||
ROI_T0 = DecimalParameter(0.01, 0.05, default=0.02, space='roi', optimize=True)
|
||||
ROI_T1 = DecimalParameter(0.005, 0.02, default=0.01, space='roi', optimize=True)
|
||||
ROI_T2 = DecimalParameter(0.0, 0.01, default=0.0, space='roi', optimize=True)
|
||||
|
||||
# Trailing Stop 参数(追踪止损)
|
||||
TRAILING_STOP_START = DecimalParameter(0.01, 0.05, default=0.03, space='sell', optimize=True)
|
||||
TRAILING_STOP_DISTANCE = DecimalParameter(0.005, 0.02, default=0.01, space='sell', optimize=True)
|
||||
|
||||
# 买卖阈值边界(你要优化的目标)
|
||||
BUY_THRESHOLD_MIN = DecimalParameter(-0.1, -0.01, default=-0.05, space='buy', optimize=True)
|
||||
BUY_THRESHOLD_MAX = DecimalParameter(-0.02, -0.001, default=-0.005, space='buy', optimize=True)
|
||||
SELL_THRESHOLD_MIN = DecimalParameter(0.001, 0.02, default=0.005, space='sell', optimize=True)
|
||||
SELL_THRESHOLD_MAX = DecimalParameter(0.02, 0.1, default=0.05, space='sell', optimize=True)
|
||||
|
||||
# 新增:加仓和减仓参数
|
||||
ADD_POSITION_THRESHOLD = DecimalParameter(-0.05, -0.01, default=-0.02, space='buy', optimize=True)
|
||||
EXIT_POSITION_RATIO = DecimalParameter(0.2, 0.7, default=0.5, space='sell', optimize=True)
|
||||
COOLDOWN_PERIOD_MINUTES = IntParameter(1, 10, default=5, space='buy', optimize=True)
|
||||
MAX_ENTRY_POSITION_ADJUSTMENT = IntParameter(1, 3, default=2, space='buy', optimize=True)
|
||||
|
||||
# --- 🛠️ 固定配置参数 ---
|
||||
minimal_roi = {
|
||||
@ -46,6 +47,7 @@ class FreqaiPrimer(IStrategy):
|
||||
stoploss = -0.015
|
||||
timeframe = "3m"
|
||||
use_custom_stoploss = True
|
||||
position_adjustment_enable = True # 启用动态仓位调整
|
||||
|
||||
plot_config = {
|
||||
"main_plot": {
|
||||
@ -95,11 +97,9 @@ class FreqaiPrimer(IStrategy):
|
||||
|
||||
def __init__(self, config: dict, *args, **kwargs):
|
||||
super().__init__(config, *args, **kwargs)
|
||||
logger.setLevel(logging.DEBUG) # 保持 DEBUG 级别以查看更多日志
|
||||
logger.setLevel(logging.DEBUG)
|
||||
logger.debug("✅ 策略已初始化,日志级别设置为 DEBUG")
|
||||
self.trailing_stop_enabled = False
|
||||
self.trailing_stop_start = 0.03
|
||||
self.trailing_stop_distance = 0.01
|
||||
self.pair_stats = {}
|
||||
self.stats_logged = False
|
||||
self.fit_live_predictions_candles = self.freqai_info.get("fit_live_predictions_candles", 100)
|
||||
@ -188,7 +188,7 @@ class FreqaiPrimer(IStrategy):
|
||||
for col in ["ema200", "bb_upperband", "bb_middleband", "bb_lowerband", "rsi", "volume_z_score", "&-price_value_divergence", "price_value_divergence"]:
|
||||
dataframe[col] = dataframe[col].replace([np.inf, -np.inf], 0).ffill().fillna(0)
|
||||
|
||||
# 添加调试日志:打印关键指标
|
||||
# 添加调试日志
|
||||
logger.debug(f"[{pair}] 最新数据 - close:{dataframe['close'].iloc[-1]:.6f}, "
|
||||
f"rsi:{dataframe['rsi'].iloc[-1]:.2f}, "
|
||||
f"&-price_value_divergence:{dataframe['&-price_value_divergence'].iloc[-1]:.6f}, "
|
||||
@ -199,9 +199,6 @@ class FreqaiPrimer(IStrategy):
|
||||
labels_mean = None
|
||||
labels_std = None
|
||||
|
||||
logger.debug(f"freqai_info identifier:{self.freqai_info['identifier']}")
|
||||
logger.debug(f"user_data_dir:{self.config['user_data_dir']}")
|
||||
|
||||
try:
|
||||
model_base_dir = os.path.join(self.config["user_data_dir"], "models", self.freqai_info["identifier"])
|
||||
pair_base = pair.split('/')[0] if '/' in pair else pair
|
||||
@ -214,7 +211,7 @@ class FreqaiPrimer(IStrategy):
|
||||
pair_base_lower = pair_base.lower()
|
||||
timestamp = latest_sub_dir.split('_')[-1]
|
||||
metadata_file = os.path.join(latest_sub_dir, f"cb_{pair_base_lower}_{timestamp}_metadata.json")
|
||||
|
||||
|
||||
if os.path.exists(metadata_file):
|
||||
with open(metadata_file, "r") as f:
|
||||
metadata = json.load(f)
|
||||
@ -237,55 +234,35 @@ class FreqaiPrimer(IStrategy):
|
||||
labels_std = 0.01
|
||||
logger.warning(f"[{pair}] labels_std 计算异常,使用默认值 0.01")
|
||||
|
||||
# 根据 labels_std 调整 k 值
|
||||
if labels_std > 0.015:
|
||||
k_buy = 1.2
|
||||
k_sell = 1.5
|
||||
elif labels_std < 0.010:
|
||||
k_buy = 0.8
|
||||
k_sell = 1.0
|
||||
else:
|
||||
k_buy = 1.0
|
||||
k_sell = 1.2
|
||||
|
||||
if labels_mean > 0.015:
|
||||
k_sell += 0.5
|
||||
|
||||
self.buy_threshold = labels_mean - k_buy * labels_std
|
||||
self.sell_threshold = labels_mean + k_sell * labels_std
|
||||
else:
|
||||
self.buy_threshold = -0.02
|
||||
self.sell_threshold = 0.03
|
||||
self.pair_stats[pair] = {"labels_mean": labels_mean, "labels_std": labels_std}
|
||||
|
||||
if labels_std > 0.015:
|
||||
k_buy = 1.2
|
||||
k_sell = 1.5
|
||||
elif labels_std < 0.010:
|
||||
k_buy = 0.8
|
||||
k_sell = 1.0
|
||||
# 根据市场趋势动态调整买卖阈值
|
||||
market_trend = self.get_market_trend()
|
||||
k_buy = 1.0
|
||||
k_sell = 1.2
|
||||
if market_trend == 'bull':
|
||||
k_buy = 0.8 # 放宽买入阈值
|
||||
k_sell = 1.0 # 收紧卖出阈值
|
||||
elif market_trend == 'bear':
|
||||
k_buy = 1.2 # 收紧买入阈值
|
||||
k_sell = 1.5 # 放宽卖出阈值
|
||||
else:
|
||||
k_buy = 1.0
|
||||
k_sell = 1.2
|
||||
|
||||
if labels_mean > 0.015:
|
||||
k_sell += 0.5
|
||||
logger.info(f"[{pair}] labels_mean 较高({labels_mean:.4f}),增加 k_sell 到 {k_sell:.2f}")
|
||||
|
||||
self.buy_threshold = labels_mean - k_buy * labels_std
|
||||
self.sell_threshold = labels_mean + k_sell * labels_std
|
||||
|
||||
# 使用 Hyperopt 参数动态限制阈值
|
||||
# 使用 Hyperopt 参数限制阈值
|
||||
self.buy_threshold = max(self.buy_threshold, self.BUY_THRESHOLD_MIN.value)
|
||||
self.buy_threshold = min(self.buy_threshold, self.BUY_THRESHOLD_MAX.value)
|
||||
self.sell_threshold = min(self.sell_threshold, self.SELL_THRESHOLD_MAX.value)
|
||||
self.sell_threshold = max(self.sell_threshold, self.SELL_THRESHOLD_MIN.value)
|
||||
|
||||
logger.info(f"[{pair}] labels_mean:{labels_mean:.4f}, labels_std:{labels_std:.4f}")
|
||||
logger.info(f"[{pair}] 市场趋势:{market_trend}, labels_mean:{labels_mean:.4f}, labels_std:{labels_std:.4f}")
|
||||
logger.info(f"[{pair}] k_buy:{k_buy:.2f}, k_sell:{k_sell:.2f}")
|
||||
logger.info(f"[{pair}] 动态买入阈值:{self.buy_threshold:.4f}")
|
||||
logger.info(f"[{pair}] 动态卖出阈值:{self.sell_threshold:.4f}")
|
||||
logger.info(f"[{pair}] 当前买入阈值:{self.buy_threshold:.6f},卖出阈值:{self.sell_threshold:.6f}")
|
||||
logger.info(f"[{pair}] 动态买入阈值:{self.buy_threshold:.4f}, 卖出阈值:{self.sell_threshold:.4f}")
|
||||
|
||||
if not self.stats_logged:
|
||||
logger.info("===== 所有币对的 labels_mean 和 labels_std 汇总 =====")
|
||||
@ -298,9 +275,9 @@ class FreqaiPrimer(IStrategy):
|
||||
|
||||
def generate_roi_table(self, params: Dict) -> Dict[int, float]:
|
||||
roi_table = {
|
||||
"0": params.get("roi_t0", 0.02),
|
||||
"30": params.get("roi_t1", 0.01),
|
||||
"60": params.get("roi_t2", 0.0),
|
||||
"0": params.get("roi_t0", self.ROI_T0.value),
|
||||
"30": params.get("roi_t1", self.ROI_T1.value),
|
||||
"60": params.get("roi_t2", self.ROI_T2.value),
|
||||
}
|
||||
return roi_table
|
||||
|
||||
@ -317,12 +294,19 @@ class FreqaiPrimer(IStrategy):
|
||||
DecimalParameter(0.005, 0.02, name="trailing_stop_distance")
|
||||
]
|
||||
|
||||
def leverage_space(self):
|
||||
return [
|
||||
DecimalParameter(-0.05, -0.01, name="add_position_threshold", default=-0.02),
|
||||
DecimalParameter(0.2, 0.7, name="exit_position_ratio", default=0.5),
|
||||
IntParameter(1, 10, name="cooldown_period_minutes", default=5),
|
||||
IntParameter(1, 3, name="max_entry_position_adjustment", default=2)
|
||||
]
|
||||
|
||||
def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
||||
pair = metadata.get('pair', 'Unknown')
|
||||
conditions = []
|
||||
|
||||
if "&-price_value_divergence" in dataframe.columns:
|
||||
# 逐个检查买入条件
|
||||
cond1 = (dataframe["&-price_value_divergence"] < self.buy_threshold)
|
||||
cond2 = (dataframe["volume_z_score"] > 1.5)
|
||||
cond3 = (dataframe["rsi"] < 40)
|
||||
@ -330,7 +314,6 @@ class FreqaiPrimer(IStrategy):
|
||||
buy_condition = cond1 & cond2 & cond3 & cond4
|
||||
conditions.append(buy_condition)
|
||||
|
||||
# 添加调试日志:打印条件是否满足
|
||||
logger.debug(f"[{pair}] 买入条件检查 - "
|
||||
f"&-price_value_divergence < {self.buy_threshold:.6f}: {cond1.iloc[-1]}, "
|
||||
f"volume_z_score > 1.5: {cond2.iloc[-1]}, "
|
||||
@ -343,12 +326,7 @@ class FreqaiPrimer(IStrategy):
|
||||
combined_condition = reduce(lambda x, y: x & y, conditions)
|
||||
if combined_condition.any():
|
||||
dataframe.loc[combined_condition, 'enter_long'] = 1
|
||||
|
||||
if 'exit_long' in dataframe.columns and (dataframe["exit_long"] == 1).any():
|
||||
logger.warning(f"[{pair}] 同时检测到买入和卖出信号,忽略买入信号")
|
||||
dataframe['enter_long'] = 0
|
||||
else:
|
||||
logger.debug(f"[{pair}] 入场信号触发,条件满足")
|
||||
logger.debug(f"[{pair}] 入场信号触发,条件满足")
|
||||
else:
|
||||
logger.debug(f"[{pair}] 买入条件均不满足,未触发入场信号")
|
||||
else:
|
||||
@ -361,13 +339,11 @@ class FreqaiPrimer(IStrategy):
|
||||
conditions = []
|
||||
|
||||
if "&-price_value_divergence" in dataframe.columns:
|
||||
# 逐个检查卖出条件
|
||||
cond1 = (dataframe["&-price_value_divergence"] > self.sell_threshold)
|
||||
cond2 = (dataframe["rsi"] > 75)
|
||||
sell_condition = cond1 | cond2
|
||||
conditions.append(sell_condition)
|
||||
|
||||
# 添加调试日志:打印条件是否满足
|
||||
logger.debug(f"[{pair}] 卖出条件检查 - "
|
||||
f"&-price_value_divergence > {self.sell_threshold:.6f}: {cond1.iloc[-1]}, "
|
||||
f"rsi > 75: {cond2.iloc[-1]}")
|
||||
@ -376,140 +352,182 @@ class FreqaiPrimer(IStrategy):
|
||||
|
||||
if len(conditions) > 0:
|
||||
dataframe.loc[reduce(lambda x, y: x & y, conditions), 'exit_long'] = 1
|
||||
logger.debug(f"[{pair}] 出场信号触发,条件满足") # 改为 DEBUG 级别
|
||||
logger.debug(f"[{pair}] 出场信号触发,条件满足")
|
||||
else:
|
||||
logger.debug(f"[{pair}] 无有效卖出条件")
|
||||
|
||||
return dataframe
|
||||
|
||||
def buy_space(self):
|
||||
return [
|
||||
DecimalParameter(-0.1, -0.01, name="buy_threshold_min"),
|
||||
DecimalParameter(-0.02, -0.001, name="buy_threshold_max"),
|
||||
DecimalParameter(-0.05, -0.01, name="add_position_threshold", default=-0.02),
|
||||
IntParameter(1, 10, name="cooldown_period_minutes", default=5),
|
||||
IntParameter(1, 3, name="max_entry_position_adjustment", default=2)
|
||||
]
|
||||
|
||||
def sell_space(self):
|
||||
return [
|
||||
DecimalParameter(0.001, 0.02, name="sell_threshold_min"),
|
||||
DecimalParameter(0.02, 0.1, name="sell_threshold_max"),
|
||||
DecimalParameter(0.2, 0.7, name="exit_position_ratio", default=0.5)
|
||||
]
|
||||
|
||||
def adjust_trade_position(self, trade: Trade, current_time: datetime,
|
||||
current_rate: float, current_profit: float,
|
||||
min_roi: Dict[float, float], max_profit: float):
|
||||
min_stake: float | None, max_stake: float,
|
||||
current_entry_rate: float, current_exit_rate: float,
|
||||
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: 最小下单金额(可能为 None)
|
||||
max_stake: 最大可用下单金额
|
||||
current_entry_rate: 当前入场价格
|
||||
current_exit_rate: 当前退出价格
|
||||
current_entry_profit: 当前入场利润百分比
|
||||
current_exit_profit: 当前退出利润百分比
|
||||
返回值:
|
||||
- 正数(float):加仓金额(报价货币,例如 USDT)
|
||||
- 负数(float):减仓金额(例如 -30.0 表示卖出 30 USDT)
|
||||
- None:不调整仓位
|
||||
- tuple[float | None, str | None]:(调整金额, 调整原因)
|
||||
"""
|
||||
pair = trade.pair
|
||||
hold_time = (current_time - trade.open_date_utc).total_seconds() / 60
|
||||
|
||||
if hold_time < 15:
|
||||
logger.info(f"[{trade.pair}] 持仓时间 {hold_time:.1f} 分钟,未达到最小持仓时间 15 分钟,暂不退出")
|
||||
return None
|
||||
|
||||
market_trend = self.get_market_trend()
|
||||
profit_ratio = (current_rate - trade.open_rate) / trade.open_rate
|
||||
|
||||
# 获取市场趋势并分别处理
|
||||
market_trend = self.get_market_trend()
|
||||
# --- 加仓逻辑 ---
|
||||
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 in ['bear', 'sideways']: # 熊市或震荡市更倾向加仓
|
||||
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},不加仓")
|
||||
return None
|
||||
|
||||
# 👇 分别处理 bull、bear、sideways 三种情况
|
||||
# --- 减仓逻辑 ---
|
||||
exit_position_ratio = self.EXIT_POSITION_RATIO.value
|
||||
if profit_ratio >= 0.03: # 利润达到 3%
|
||||
if market_trend == 'bull':
|
||||
reduce_amount = -exit_position_ratio * 0.6 * trade.stake_amount # 牛市减仓较少
|
||||
logger.info(f"[{pair}] 牛市,利润 {profit_ratio*100:.2f}%,减仓 {abs(reduce_amount):.2f}")
|
||||
return (reduce_amount, f"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}")
|
||||
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 == 'bull':
|
||||
# 牛市:放宽 trailing stop,允许更多利润积累
|
||||
self.trailing_stop_distance = 0.015
|
||||
self.trailing_stop_start = 0.04
|
||||
logger.debug(f"[{trade.pair}] 当前为牛市,设置 trailing_stop_distance={self.trailing_stop_distance}, start={self.trailing_stop_start}")
|
||||
|
||||
trailing_stop_distance *= 1.5 # 牛市放宽追踪止损
|
||||
trailing_stop_start *= 1.2
|
||||
elif market_trend == 'bear':
|
||||
# 熊市:收紧 trailing stop,快速止盈
|
||||
self.trailing_stop_distance = 0.005
|
||||
self.trailing_stop_start = 0.01
|
||||
logger.debug(f"[{trade.pair}] 当前为熊市,设置 trailing_stop_distance={self.trailing_stop_distance}, start={self.trailing_stop_start}")
|
||||
trailing_stop_distance *= 0.7 # 熊市收紧追踪止损
|
||||
trailing_stop_start *= 0.8
|
||||
|
||||
elif market_trend == 'sideways':
|
||||
# 震荡市:保持默认值
|
||||
self.trailing_stop_distance = 0.01
|
||||
self.trailing_stop_start = 0.03
|
||||
logger.debug(f"[{trade.pair}] 当前为震荡市,使用默认 trailing_stop 参数")
|
||||
|
||||
if profit_ratio >= self.trailing_stop_start and not self.trailing_stop_enabled:
|
||||
if profit_ratio >= trailing_stop_start and not self.trailing_stop_enabled:
|
||||
self.trailing_stop_enabled = True
|
||||
trade.adjust_max_rate(current_rate)
|
||||
logger.info(f"[{trade.pair}] 价格上涨超过 {self.trailing_stop_start*100:.1f}%,启动 Trailing Stop")
|
||||
trade.adjust_min_max_rates(current_rate, current_rate) # 初始化最高和最低价格
|
||||
logger.info(f"[{pair}] 价格上涨超过 {trailing_stop_start*100:.1f}%,启动 Trailing Stop")
|
||||
return None
|
||||
|
||||
if self.trailing_stop_enabled:
|
||||
max_rate = trade.max_rate
|
||||
trailing_stop_price = max_rate * (1 - self.trailing_stop_distance)
|
||||
trailing_stop_price = max_rate * (1 - trailing_stop_distance)
|
||||
if current_rate < trailing_stop_price:
|
||||
logger.info(f"[{trade.pair}] 价格回落至 Trailing Stop 点 {trailing_stop_price:.6f},触发卖出")
|
||||
return -1
|
||||
trade.adjust_max_rate(current_rate)
|
||||
logger.info(f"[{pair}] 价格回落至 Trailing Stop 点 {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"[{trade.pair}] 持仓时间超过30分钟,强制平仓")
|
||||
logger.info(f"[{pair}] 持仓时间超过30分钟,强制清仓")
|
||||
return (-trade.stake_amount, "Max hold time exceeded")
|
||||
|
||||
return -1
|
||||
return None
|
||||
|
||||
def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float,
|
||||
time_in_force: str, current_time: datetime, **kwargs) -> bool:
|
||||
# """
|
||||
# 使用内存变量代替数据库查询,防止 Hyperopt 报错
|
||||
# """
|
||||
#
|
||||
cooldown_period_minutes = 5 # 可以改为 DecimalParameter 进行 Hyperopt
|
||||
market_trend = self.get_market_trend()
|
||||
cooldown_period_minutes = self.COOLDOWN_PERIOD_MINUTES.value if market_trend == 'bull' else self.COOLDOWN_PERIOD_MINUTES.value // 2
|
||||
|
||||
if pair in self.last_entry_time:
|
||||
last_time = self.last_entry_time[pair]
|
||||
if (current_time - last_time).total_seconds() < cooldown_period_minutes * 60:
|
||||
logger.info(f"[{pair}] 冷却期内,跳过本次入场")
|
||||
logger.info(f"[{pair}] 冷却期内({cooldown_period_minutes} 分钟),跳过本次入场")
|
||||
return False
|
||||
|
||||
# 更新最后入场时间
|
||||
self.last_entry_time[pair] = current_time
|
||||
self.trailing_stop_enabled = False
|
||||
|
||||
# 如果我猜的没错的话, 用了这个方法上述被注释的内容, 反到降低了本策略盈利, 需要更多测试,所以暂时禁用并不删除
|
||||
logger.info(f"[{pair}] 确认入场,价格:{rate:.6f}")
|
||||
return True
|
||||
|
||||
def confirm_trade_exit(self, pair: str, trade: Trade, order_type: str, amount: float,
|
||||
rate: float, time_in_force: str, exit_reason: str,
|
||||
current_time: datetime, **kwargs) -> bool:
|
||||
# 调整卖出价格:比当前报价高 0.25%
|
||||
rate: float, time_in_force: str, exit_reason: str,
|
||||
current_time: datetime, **kwargs) -> bool:
|
||||
adjusted_rate = rate * (1 + 0.0025)
|
||||
logger.info(f"[{pair}] 退出交易,原因:{exit_reason}, 原始利润:{trade.calc_profit_ratio(rate):.2%},"
|
||||
f"调整后卖出价:{adjusted_rate:.6f}")
|
||||
logger.info(f"[{pair}] 退出交易,原因:{exit_reason}, 原始利润:{trade.calc_profit_ratio(rate):.2%},"
|
||||
f"调整后卖出价:{adjusted_rate:.6f}")
|
||||
return True
|
||||
### 1️⃣ 自定义买入价格:`custom_entry_price()`
|
||||
|
||||
def custom_entry_price(self, pair: str, trade: Trade | None, current_time: datetime, proposed_rate: float,
|
||||
entry_tag: str | None, side: str, **kwargs) -> float:
|
||||
# 比当前市场价低 0.5% 下单
|
||||
entry_tag: str | None, side: str, **kwargs) -> float:
|
||||
adjusted_rate = proposed_rate * (1 - 0.005)
|
||||
logger.debug(f"[{pair}] 自定义买入价:{adjusted_rate:.6f}(原价:{proposed_rate:.6f})")
|
||||
return adjusted_rate
|
||||
|
||||
### 2️⃣ 自定义卖出价格:`custom_exit_price()`
|
||||
|
||||
def custom_exit_price(self, pair: str, trade: Trade,
|
||||
current_time: datetime, proposed_rate: float,
|
||||
current_profit: float, exit_tag: str | None, **kwargs) -> float:
|
||||
# 比当前市场价高 0.25% 下单
|
||||
current_time: datetime, proposed_rate: float,
|
||||
current_profit: float, exit_tag: str | None, **kwargs) -> float:
|
||||
adjusted_rate = proposed_rate * (1 + 0.0025)
|
||||
logger.debug(f"[{pair}] 自定义卖出价:{adjusted_rate:.6f}(原价:{proposed_rate:.6f})")
|
||||
return adjusted_rate
|
||||
|
||||
def get_market_trend(self, dataframe: DataFrame = None) -> str:
|
||||
"""
|
||||
使用 BTC/USDT 的 EMA200 判断大盘趋势
|
||||
返回值: 'bull'(牛市)、'bear'(熊市)、'sideways'(震荡)
|
||||
"""
|
||||
|
||||
try:
|
||||
# 获取 BTC/USDT 的数据
|
||||
btc_df = self.dp.get_pair_dataframe("BTC/USDT", self.timeframe)
|
||||
if len(btc_df) < 200:
|
||||
logger.warning("BTC 数据不足,返回默认趋势:sideways")
|
||||
return "sideways"
|
||||
|
||||
# 计算 EMA200
|
||||
btc_df["ema200"] = ta.EMA(btc_df, timeperiod=200)
|
||||
|
||||
# 当前价格与 EMA200 的关系
|
||||
price_above_ema = (btc_df["close"].iloc[-1] > btc_df["ema200"].iloc[-1])
|
||||
|
||||
# 近期趋势强度(过去 50 根 K 线的平均涨跌幅)
|
||||
btc_df["adx"] = ta.ADX(btc_df, timeperiod=14)
|
||||
trend_strength = btc_df["close"].pct_change(50).iloc[-1]
|
||||
|
||||
# 判断市场状态
|
||||
if price_above_ema and trend_strength > 0.03:
|
||||
price_above_ema = btc_df["close"].iloc[-1] > btc_df["ema200"].iloc[-1]
|
||||
adx_value = btc_df["adx"].iloc[-1]
|
||||
|
||||
if price_above_ema and trend_strength > 0.03 and adx_value > 25:
|
||||
return "bull"
|
||||
elif not price_above_ema and trend_strength < -0.03:
|
||||
elif not price_above_ema and trend_strength < -0.03 and adx_value > 25:
|
||||
return "bear"
|
||||
else:
|
||||
return "sideways"
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"获取市场趋势失败:{e}", exc_info=True)
|
||||
return "sideways"
|
||||
|
||||
|
||||
@ -43,7 +43,7 @@ echo "docker-compose run --rm freqtrade hyperopt \
|
||||
--timerange ${START_DATE}-${END_DATE} \
|
||||
-e 200 \
|
||||
--hyperopt-loss ShortTradeDurHyperOptLoss \
|
||||
--spaces roi stoploss trailing \
|
||||
--spaces buy sell roi trailing \
|
||||
--fee 0.0016"
|
||||
docker-compose run --rm freqtrade hyperopt \
|
||||
--logfile /freqtrade/user_data/logs/freqtrade.log \
|
||||
@ -55,7 +55,7 @@ docker-compose run --rm freqtrade hyperopt \
|
||||
--timerange ${START_DATE}-${END_DATE} \
|
||||
-e 200 \
|
||||
--hyperopt-loss SharpeHyperOptLoss \
|
||||
--spaces roi stoploss trailing \
|
||||
--spaces buy sell roi trailing \
|
||||
--fee 0.0016
|
||||
|
||||
#>output.log 2>&1
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user