第 4 章 · 02 回调函数全解


文档摘要

第 4 章 · 02 回调函数全解 本节摘要:IStrategy 的三个 方法是「机械规则」——基于 DataFrame 列算信号,粒度是「K 线」。但实战中很多决策需要「持仓感知」(知道当前浮盈多少)、「时间感知」(持仓多久了)、「事件感知」(订单刚成交)。freqtrade 用回调函数(callbacks)满足这类需求——引擎在交易流程的关键节点上调用你重写的方法。本节把最常用的几个回调一次讲透: 、 、 、 、 、 、 ,以及 DCA 加仓用的 。读完本节,你能写出「持仓 30 分钟后止损上移」「浮盈 5% 后启动追踪止盈」「只在订单簿合理时入场」这类非机械逻辑。 内容来源:原项目文档 ,汉化并套用体系化模板。

第 4 章 · 02 回调函数全解

本节摘要:IStrategy 的三个 populate_* 方法是「机械规则」——基于 DataFrame 列算信号,粒度是「K 线」。但实战中很多决策需要「持仓感知」(知道当前浮盈多少)、「时间感知」(持仓多久了)、「事件感知」(订单刚成交)。freqtrade 用**回调函数(callbacks)**满足这类需求——引擎在交易流程的关键节点上调用你重写的方法。本节把最常用的几个回调一次讲透:custom_stoplosscustom_exitcustom_entry_pricecustom_exit_priceconfirm_trade_entry/exitbot_loop_startconfirm_trade_entry,以及 DCA 加仓用的 adjust_trade_position。读完本节,你能写出「持仓 30 分钟后止损上移」「浮盈 5% 后启动追踪止盈」「只在订单簿合理时入场」这类非机械逻辑。

内容来源:原项目文档 docs/strategy-callbacks.md,汉化并套用体系化模板。

⚠️ 风险提示:回调用错地方比不用更糟——比如 confirm_trade_entry 里基于未来数据决策、或 custom_stoploss 返回过松的值,都可能让单笔亏损失控。回调在实盘前必须经过回测 + Dry-Run。

学习目标

阅读完本节,你应当能够:

  1. 列举 8 个常用回调及其触发时机。
  2. 实现 custom_stoploss动态止损
  3. 实现 custom_entry_price / custom_exit_price自定义定价
  4. 实现 confirm_trade_entry / confirm_trade_exit最后一关确认。
  5. 实现 custom_exit基于持仓状态的出场。
  6. 实现 bot_loop_start每轮起始的预处理。
  7. 理解回调在 live 与 backtesting 的调用频率差异。

一、回调地图:谁在什么时候被调用

回忆第 2 章第 01 节的 process() 八步,回调就嵌在这些步骤里:

每个回调都是 IStrategy 上的可选方法——不重写就用默认行为,重写就接管那一步。

二、custom_stoploss:动态止损

签名:

def custom_stoploss(self, pair: str, trade: Trade, current_time: datetime, current_rate: float, current_profit: float, **kwargs) -> float: return None # None 表示用默认 stoploss
  • 触发:每次迭代对每个 open trade。
  • 返回:相对当前价的「止损比例」(负数,如 -0.05 = 5%);None 表示保持默认。
  • 典型用法:浮盈越大,止损越紧(锁利润)。
def custom_stoploss(self, pair, trade, current_time, current_rate, current_profit, **kwargs): # 浮盈 5% 后,把止损上移到 +2%(保证不亏) if current_profit > 0.05: return -0.02 # 注意:相对当前价,不是相对开仓价 # 默认 -10% return None

💡 返回值的语义:返回的是「相对当前价的止损距离」,不是「止损价」。-0.02 意思是「比当前价低 2% 的位置止损」。这与类属性 stoploss = -0.10(相对开仓价)的语义不同,容易混淆。第 04 节详讲。

三、custom_exit:持仓感知出场

签名:

def custom_exit(self, pair: str, trade: Trade, current_time: datetime, current_rate: float, current_profit: float, **kwargs) -> bool | str: return False # False/None 表示不出场
  • 触发:每次迭代对每个 open trade(与 custom_stoploss 同时机)。
  • 返回:True 或字符串(出场理由,会写入 exit_reason);False/None 不出场。
  • 典型用法:基于 trade 的字段(持仓时长、入场价、enter_tag)做复杂判断。
def custom_exit(self, pair, trade, current_time, current_rate, current_profit, **kwargs): # 持仓超过 2 小时且未盈利,走人 if (current_time - trade.open_date_utc).total_seconds() > 2 * 3600 and current_profit < 0: return "timeout_no_profit" return None

populate_exit_trend 的区别:populate_exit_trend 基于 DataFrame 列(K 线指标),custom_exit 基于 trade 状态(持仓信息)。两者「或」关系。

四、custom_entry_price / custom_exit_price:自定义定价

签名:

def custom_entry_price(self, pair: str, current_time: datetime, proposed_rate: float, entry_tag: str | None, side: str, **kwargs) -> float: return proposed_rate # 默认用 proposed_rate
  • 触发:开仓前(custom_entry_price)、平仓前(custom_exit_price)。
  • 输入:proposed_rate 是引擎基于 entry_pricing/exit_pricing 算出的「建议价」。
  • 返回:你想要的具体价格。

典型用法——「只在订单簿最佳买盘的基础上再低 0.5% 挂买」:

def custom_entry_price(self, pair, current_time, proposed_rate, entry_tag, side, **kwargs): orderbook = self.dp.orderbook(pair, 1) if not orderbook: return proposed_rate best_bid = orderbook["bids"][0][0] return min(best_bid * 0.995, proposed_rate) # 更低一点

⚠️ 回测限制:回测中没有真实订单簿,self.dp.orderbook 返回空。涉及订单簿的回调只在 live/dry_run 有意义,回测会回退到 proposed_rate

五、confirm_trade_entry / confirm_trade_exit:最后一关

签名:

def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float, time_in_force: str, current_time: datetime, entry_tag: str | None, side: str, **kwargs) -> bool: return True # True 确认下单,False 取消
  • 触发:入场/出场订单即将提交前
  • 返回:True 放行,False 取消。
  • 典型用法:在最后关头做一次「冷静检查」——例如订单簿突然变差、或同一时间点已经有太多挂单。
def confirm_trade_entry(self, pair, order_type, amount, rate, time_in_force, current_time, entry_tag, side, **kwargs): # 5 分钟内同对进场超过 3 次,怀疑异常,取消 recent = Trade.get_trades_proxy(pair=pair, is_open=True, open_date=current_time - timedelta(minutes=5)) if len(recent) >= 3: return False return True

💡 confirm 与 custom_exit 的分工:custom_exit 决定「要不要出」;confirm_trade_exit 决定「已经决定出了,最后确认下」。前者是发起,后者是审批。

六、bot_loop_start:每轮预处理

def bot_loop_start(self, current_time: datetime, **kwargs) -> None: # 每次主循环开始时跑一次(对所有对) pass
  • 触发:每轮迭代开始,在 populate_* 之前。
  • 典型用法:更新策略内部状态、刷新某个数据缓存、做跨对统计。
def bot_loop_start(self, current_time, **kwargs): # 计算今日已平仓的总盈利,用于动态调整仓位 today = current_time.date() closed = Trade.get_trades_proxy(is_open=False, close_date=datetime(today.year, today.month, today.day)) self.today_profit = sum(t.close_profit_abs for t in closed)

七、adjust_trade_position:DCA 与分批出场

def adjust_trade_position(self, trade: Trade, current_time: datetime, current_rate: float, current_profit: float, min_stake: float | None, max_stake: float | None, **kwargs) -> float | None | list: return None
  • 触发:每轮迭代对每个 open trade(需在配置开启 position_adjustment_enable: true)。
  • 返回:
    • 正数 → 加仓(再买这么多 stake)。
    • 负数 → 减仓(部分平仓,partial exit)。
    • None → 不动。
    • 列表 → 一次性返回多个调整。

经典 DCA——「浮亏 5% 时补一仓」:

def adjust_trade_position(self, trade, current_time, current_rate, current_profit, min_stake, max_stake, **kwargs): if current_profit < -0.05 and trade.nr_of_successful_entries == 1: return stake_amount_for_dca # 加一仓 return None

第 05 节会详讲 DCA、partial exit 与杠杆相关。

八、回调在 live 与 backtesting 的频率差异

⚠️ 关键警告:这是回测与实盘最容易失配的地方。

模式 回调调用频率
Live 大多数回调每次迭代(~5 秒) 调用一次
Backtesting 大多数回调每根 K 线调用一次(除非用 --timeframe-detail)

含义:

  • 在 live 里,custom_stoploss 每 5 秒跑一次,能在 5m K 线内多次反应。
  • 在 backtesting 里,custom_stoploss 每根 K 线只跑一次,反应粒度更粗。
  • 结果:某些「依赖细粒度反应」的策略,回测结果会与实盘有差异

💡 对策:对价格敏感的回调(止损、定价),回测时用 --timeframe-detail 1m 把回调调用频率提到 1m 粒度,缩小回测-实盘差距(第 6 章详讲)。

本节要点回顾

  1. 回调地图:bot_loop_start(开始)、order_filled(成交)、adjust_order_price(改价)、custom_stoploss/custom_exit(持仓管理)、custom_entry/exit_price(定价)、confirm_trade_entry/exit(最后确认)、adjust_trade_position(加减仓)。
  2. custom_stoploss:返回相对当前价的止损距离(负数),None 用默认;典型做「浮盈越大止损越紧」。
  3. custom_exit:基于 trade 状态返回是否出场 + 理由;与 populate_exit_trend「或」关系。
  4. custom_entry/exit_price:接管定价,可用 self.dp.orderbook 拿盘口(回测无效)。
  5. confirm_trade_entry/exit:下单前最后关,True 放行 False 取消;用于冷静检查。
  6. adjust_trade_position:DCA 加仓(正数)/ 部分平仓(负数),需开 position_adjustment_enable
  7. live vs backtest 频率:live 每迭代(~5s),backtest 每根 K 线;价格敏感策略用 --timeframe-detail 缩小差距。

下一节,我们认识回调里反复出现的 Trade 对象——它的字段、方法,以及在回调里如何安全地读它。


发布者: 作者: 灏天文库 转发
评论区 (0)
U