第 4 章 · 03 Trade 对象 本节摘要:回调函数(上一节)里反复出现的 参数,就是 对象——每一笔未平仓持仓在代码里的抽象。它由引擎创建并持久化到 SQLite,在回调中传给你只读使用(不能直接改属性)。本节讲透 Trade 的核心字段:基础信息( / / )、价格与仓位( / / )、盈亏( 是回调参数, / 是字段)、止损( / )、合约特有( / / ),以及几个最常用的类方法( / / )。读完本节,你能在任意回调里安全地读 trade 的状态,做出基于持仓信息的决策。 内容来源:原项目文档 、源码 ,汉化并套用体系化模板。 学习目标 阅读完本节,你应当能够: 说清 在引擎与策略之间的传递方向(只读)。 列举基础信息字段: / / / / / 。
本节摘要:回调函数(上一节)里反复出现的
trade参数,就是Trade对象——每一笔未平仓持仓在代码里的抽象。它由引擎创建并持久化到 SQLite,在回调中传给你只读使用(不能直接改属性)。本节讲透 Trade 的核心字段:基础信息(pair/is_short/open_date_utc)、价格与仓位(open_rate/amount/stake_amount)、盈亏(current_profit是回调参数,realized_profit/close_profit是字段)、止损(stop_loss/stop_loss_pct)、合约特有(leverage/liquidation_price/funding_fees),以及几个最常用的类方法(get_trades_proxy/get_open_trade_count/get_total_closed_profit)。读完本节,你能在任意回调里安全地读 trade 的状态,做出基于持仓信息的决策。
内容来源:原项目文档
docs/trade-object.md、源码freqtrade/persistence/trade_model.py,汉化并套用体系化模板。
阅读完本节,你应当能够:
Trade 在引擎与策略之间的传递方向(只读)。pair/is_open/is_short/open_date_utc/enter_tag/exit_reason。open_rate/amount/stake_amount/max_stake_amount。current_profit(回调参数)、realized_profit、close_profit。stop_loss/stop_loss_pct/initial_stop_loss/stoploss_or_liquidation。leverage/liquidation_price/funding_fees/interest_rate。Trade.get_trades_proxy() 查历史,避开 backtesting 不支持的类方法。引擎开仓时,创建一个 Trade 对象并存入 SQLite;每次迭代从库里取出所有 is_open=True 的 trade,逐个判断要不要平仓、要不要补仓;平仓后 is_open=False 但记录保留。
💡 只读原则:文档明确「The object passed to the strategy cannot be modified directly」——回调里不要写
trade.open_rate = xxx,这不会生效或导致状态不一致。要改 trade,通过回调的返回值(custom_stoploss返回止损比例、adjust_trade_position返回加减仓量等),让引擎代为执行。
| 字段 | 类型 | 说明 |
|---|---|---|
pair |
str | 交易对,如 "ETH/USDT" |
is_open |
bool | 是否仍持仓 |
is_short |
bool | 是否做空 |
trade_direction |
str | "long"/"short"(文字) |
entry_side/exit_side |
str | 入/出场方向("buy"/"sell") |
exchange |
str | 交易所名 |
strategy |
str | 所属策略名 |
open_date_utc |
datetime | 开仓时间(UTC,优先用它而非 open_date) |
close_date_utc |
datetime | 平仓时间(仅 is_open=False) |
enter_tag |
str | 入场标签(来自 DataFrame 的 enter_tag 列) |
exit_reason |
str | 出场理由("stop_loss"/"roi"/"exit_signal"/custom_exit 返回的字符串) |
enter_tag 与 exit_reason 是策略可观测性的关键——你可以给不同入场条件打不同 tag,事后统计哪类信号表现好。
| 字段 | 类型 | 说明 |
|---|---|---|
open_rate |
float | 实际开仓均价(DCA 加仓后会重算) |
open_rate_requested |
float | 请求的开仓价(可能与成交价不同) |
close_rate |
float | 平仓价(仅 is_open=False) |
amount |
float | 持有的 base 币数量(初始订单成交前为 0) |
amount_requested |
float | 请求的数量 |
stake_amount |
float | 当前投入的 quote 币金额 |
max_stake_amount |
float | 历史最大 stake(DCA 后增加) |
open_trade_value |
float | 含费用的开仓总值 |
leverage |
float | 杠杆倍数(现货为 1.0) |
注意 open_rate 是「均价」——DCA 加仓后,引擎会按新的总量与总投入重新算均价。这也是为什么 current_profit 相对 open_rate 计算时,DCA 后会「摊低」浮亏。
盈亏在 freqtrade 里分三个层次,容易混淆:
| 名称 | 何时存在 | 含义 |
|---|---|---|
current_profit |
仅 open 时,回调参数(不是字段) | 当前未实现浮盈比例(0.01=1%) |
realized_profit |
open 与 closed 都有,字段 | 部分平仓已锁定的绝对盈亏(stake 币计) |
close_profit |
仅 closed,字段 | 最终平仓的相对盈亏比例 |
close_profit_abs |
仅 closed,字段 | 最终平仓的绝对盈亏(stake 币计) |
💡
realized_profit何时非零:只有用了adjust_trade_position做部分平仓(返回负数),才会有部分仓位被锁定盈亏。没做部分平仓,realized_profit一直是 0,直到完全平仓。
回调里要算「总盈亏」,需要把 current_profit(浮盈)与已实现盈亏合并。最稳妥的做法是用 trade.realized_profit + current_profit * trade.stake_amount(近似,严格公式见文档)。
| 字段 | 说明 |
|---|---|
stop_loss |
当前止损价(绝对值,会随 custom_stoploss/trailing 更新) |
stop_loss_pct |
当前止损比例(相对开仓价) |
initial_stop_loss |
初始止损价(开仓时的,不更新) |
initial_stop_loss_pct |
初始止损比例 |
stoploss_or_liquidation |
「止损价」与「强平价」中更激进的那个——实际的强制出场价 |
stoploss_last_update_utc |
止损最后更新时间 |
initial_stop_loss_* 让你能比较「当前止损 vs 初始止损」——常用于判断「是否已经把止损上移到保本位」。
| 字段 | 说明 |
|---|---|
liquidation_price |
强平价(杠杆持仓才会被强平) |
funding_fees |
资金费累计(永续合约) |
interest_rate |
利率(保证金交易) |
合约里 stoploss_or_liquidation 取「止损」与「强平」更紧的那个——如果你的止损比强平价还远,实际会在强平时被爆,这是合约交易要小心的。
max_rate / min_rate:持仓期间到达过的最高/最低价(用于追踪止损判断,不是 100% 精确,回测里按 K 线极值)。nr_of_successful_entries / nr_of_successful_exits:已成交的入/出场订单数(DCA 判断「加过仓没」的关键)。has_open_position:是否有实际持仓(初始订单未成交前为 False)。has_open_orders / open_orders:是否有挂单 / 挂单列表。orders:订单列表(含已成交与已取消),每个 Order 又有自己的字段(见下一节)。Trade 还有类方法(用 Trade.xxx() 调),用于跨笔查询:
from freqtrade.persistence import Trade # 当前 open trades 数 n = Trade.get_open_trade_count() # 查最近 2 天的已平仓 trade from datetime import timedelta history = Trade.get_trades_proxy(is_open=False, open_date=current_time - timedelta(days=2)) # 累计已实现盈亏 total = Trade.get_total_closed_profit() # 当前所有 open trades 占用的总 stake total_stake = Trade.total_open_trades_stakes()
get_trades_proxy 参数pair:按交易对过滤。is_open:True 仅 open,False 仅 closed,不传=全部。open_date / close_date:按开/平仓时间过滤。⚠️ backtesting 限制:大多数类方法在 backtesting 里只在回调中可用,在
populate_*方法里不支持(会得到错误结果)。get_overall_performance、get_trading_volume在 backtesting 里完全不支持,需要先判断self.config['runmode']。
完整例子——「持仓期间最大回撤超过 8% 就出场」:
from datetime import datetime from freqtrade.persistence import Trade def custom_exit(self, pair, trade, current_time, current_rate, current_profit, **kwargs): # 最大回撤 = (max_rate - current_rate) / max_rate if trade.max_rate > 0: drawdown = (trade.max_rate - current_rate) / trade.max_rate if drawdown > 0.08: return "max_drawdown_exit" # 持仓超过 1 天且微亏,止损走人 if (current_time - trade.open_date_utc).days >= 1 and current_profit < -0.02: return "timeout" return None
注意:
trade.max_rate、trade.open_date_utc 这些字段做判断。exit_reason,事后可统计。pair/is_short/trade_direction/open_date_utc/enter_tag/exit_reason,优先用 _utc 后缀的时间字段。open_rate(DCA 后是均价)/amount/stake_amount/max_stake_amount。current_profit(浮盈,回调参数)/realized_profit(部分平仓锁定)/close_profit(最终);总盈亏需合并。stop_loss(会更新)/initial_stop_loss(原始)/stoploss_or_liquidation(止损与强平更紧者)。leverage/liquidation_price/funding_fees/interest_rate。Trade.get_trades_proxy() 查历史、get_open_trade_count 查数量、get_total_closed_profit 查累计盈亏;backtesting 中只在回调里可用,populate_* 里不可用。下一节,我们用 Trade 字段实打实地做「追踪止损」——
trailing_stop配置与custom_stoploss实战。