This commit is contained in:
Ubuntu 2025-06-12 01:58:50 +08:00
parent b89762d805
commit 8ba203f2b1
4 changed files with 178 additions and 153 deletions

View File

@ -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,

View File

@ -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"
}

View File

@ -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"

View File

@ -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