☰
TensorTrade 订单成交记录 Trade 对象全解析:从订单撮合到资产转移的数据模型与序列化
2026/10/8 1:56:20 网站建设 项目流程
  • 人工智能
  • 金融科技
  • 机器学习

【免费下载链接】tensortrade

An open source reinforcement learning framework for training, evaluating, and deploying robust trading agents.

项目地址:https://gitcode.com/gh_mirrors/te/tensortrade
点击查看免费下载

TensorTrade 是一个面向训练、评估与部署强化学习交易智能体的开源框架,其 oms/orders 子系统把“下单 → 撮合 → 成交 → 记账”拆分为职责清晰的独立对象。本文聚焦其中最容易被人忽视、却贯穿整个交易闭环的Trade(成交)对象:它由谁创建、携带哪些字段、如何在订单与钱包之间流转,以及如何被 Broker 记录与序列化。读完本文,你将掌握Trade、TradeSide、TradeType三个核心类的完整语义,能够读懂 TensorTrade 的模拟成交日志,并在此基础上实现自定义成交数据导出与分析。

1. 模块定位:Trade 在 OMS 交易闭环中的角色

Trade定义在 tensortrade/oms/orders/trade.py,其文档字符串仅一句话:"A trade object for use within trading environments."(用于交易环境内部的成交对象)。要理解它的分量,需要把它放进订单管理系统(OMS)的完整调用链中看。

在 TensorTrade 中,一笔交易的生命周期大致为:

  1. 智能体通过 Order 创建订单(含side、trade_type、exchange_pair、quantity、price等);
  2. Order.execute()将订单状态置为OPEN,并把执行请求交给Exchange.execute_order()(见 exchange.py);
  3. 交易所调用执行服务(默认为 simulated.py 中的execute_order),撮合成功后构造并返回一个Trade对象;
  4. Exchange.execute_order()拿到非空Trade后调用order.fill(trade),由订单记录该笔成交;
  5. 成交事件通过监听器传播到 Broker 的on_fill,Broker 将Trade归档进trades字典,用于后续回放与分析。

因此,Trade是“订单意图”与“账户资产变动”之间的桥梁:它既是订单撮合成功的结果证据,也是钱包资金转移(Wallet.transfer)的产物快照。脱离这一闭环,Trade就只是一堆字段;结合闭环,它才是回测可复现的关键数据单元。

2. 三个核心类的语义

trade.py 定义了三个顶层类:两个枚举TradeType、TradeSide,以及主类Trade。

2.1 TradeType:市价单与限价单

class TradeType(Enum): LIMIT: str = "limit" MARKET: str = "market" def __str__(self): return str(self.value)
  • TradeType.LIMIT("limit"):限价单,仅在价格满足条件时成交;
  • TradeType.MARKET("market"):市价单,按当前市价立即成交;
  • __str__返回字符串值(如"market"),保证序列化时输出的是可读文本而非TradeType.MARKET这种枚举表示。

该枚举与 OrderStatus(PENDING/OPEN/CANCELLED/PARTIALLY_FILLED/FILLED)一样是订单域的核心类型。在 simulated.py 中,order.type直接被透传进Trade(trade_type=order.type),因此一笔成交的类型与产生它的订单类型始终一致。

2.2 TradeSide:买与卖的方向语义

class TradeSide(Enum): BUY: str = "buy" SELL: str = "sell" def instrument(self, pair: "TradingPair") -> "Instrument": return pair.base if self == TradeSide.BUY else pair.quote def __str__(self): return str(self.value)

TradeSide在普通枚举之外还提供了一个关键方法instrument(pair):

  • TradeSide.BUY返回交易对中的base(基础)工具,例如 BTC/USD 中的 BTC;
  • TradeSide.SELL返回交易对中的quote(计价)工具,例如 BTC/USD 中的 USD。

这一方法在订单与钱包联动中反复出现。例如 order.py 中创建订单时会调用self.side.instrument(self.exchange_pair.pair)来定位“用哪个钱包锁定量”;is_complete 判断订单是否完成时同样使用它。理解TradeSide不是简单的方向标签,而是资产归属的判定逻辑,是正确阅读 TensorTrade 账户变动的前提。

2.3 Trade 主类:构造参数与字段

class Trade(TimedIdentifiable): def __init__(self, order_id: str, step: int, exchange_pair: 'ExchangePair', side: TradeSide, trade_type: TradeType, quantity: 'Quantity', price: float, commission: 'Quantity'):

Trade继承自 TimedIdentifiable,因此自动获得:

  • id:uuid.uuid4()生成的全局唯一标识(首次访问时惰性生成);
  • created_at:创建时刻,取自所关联时钟self._clock.now()。

构造参数的完整语义如下表:

参数类型含义
order_idstr产生该笔成交的订单 ID,用于把成交回溯到订单
stepint该笔成交发生在交易回合(episode)中的哪个时间步
exchange_pairExchangePair成交所涉及的交易所与交易对,如 BTC/USDT、ETH/BTC、AAPL/USD、CAD/USD 等
sideTradeSide方向:BUY表示用计价工具买入基础工具,SELL反之
trade_typeTradeType订单类型:LIMIT或MARKET
quantityQuantity成交数量(扣除佣金后的净数量)
pricefloat每单位计价工具以基础工具计的价格(如 10000 表示基础工具为 USD 时的 10000.00 美元)
commissionQuantity该笔成交支付的佣金,同样以基础工具计

字段在__init__中被直接赋值:order_id、step、exchange_pair、side、type、quantity,而price与commission走属性赋值(见下文)。

3. 派生属性:从组合数据中快速取读

Trade提供了若干只读派生属性,避免调用方直接解构exchange_pair:

@property def base_instrument(self) -> 'Instrument': return self.exchange_pair.pair.base @property def quote_instrument(self) -> 'Instrument': return self.exchange_pair.pair.quote @property def size(self) -> float: return self.quantity.size
  • base_instrument/quote_instrument:直接取交易对两侧的工具对象;
  • size:成交数量对应的数值大小,来自quantity.size。

方向与类型的快速判定属性同样齐全:

@property def is_buy(self) -> bool: return self.side == TradeSide.BUY @property def is_sell(self) -> bool: return self.side == TradeSide.SELL @property def is_limit_order(self) -> bool: return self.type == TradeType.LIMIT @property def is_market_order(self) -> bool: return self.type == TradeType.MARKET

这四个布尔属性与 Order 上的同名属性一一对应,让调用方可以用统一的is_buy/is_sell/is_limit_order/is_market_order接口同时处理订单与成交,例如在执行服务中根据order.is_buy分发到买入或卖出逻辑(见 simulated.py)。

price与commission还被实现为带校验语义的属性对(getter/setter),为将来添加取值约束预留了空间:

@property def price(self) -> float: return self._price @price.setter def price(self, price: float): self._price = price @property def commission(self) -> 'Quantity': return self._commission @commission.setter def commission(self, commission: 'Quantity'): self._commission = commission

4. 成交从何而来:SimulatedExchange 的执行服务

Trade对象并非智能体直接构造,而是由交易所的执行服务在撮合成功后创建。默认的模拟执行服务实现在 tensortrade/oms/services/execution/simulated.py,其中execute_buy_order与execute_sell_order是两个对称的实现。

4.1 买入成交的构造过程

def execute_buy_order(order, base_wallet, quote_wallet, current_price, options, clock): if order.type == TradeType.LIMIT and order.price < current_price: return None filled = order.remaining.contain(order.exchange_pair) if order.type == TradeType.MARKET: scale = order.price / max(current_price, order.price) filled = scale * filled commission = options.commission * filled # 佣金若低于工具精度下限,则提升到最小精度值(并发出告警) minimum_commission = Decimal(10) ** -filled.instrument.precision if options.commission > 0 and commission < minimum_commission: logging.warning("Commission is > 0 but less than instrument precision. ...") commission.size = minimum_commission quantity = filled - commission transfer = Wallet.transfer( source=base_wallet, target=quote_wallet, quantity=quantity, commission=commission, exchange_pair=order.exchange_pair, reason="BUY") trade = Trade( order_id=order.id, step=clock.step, exchange_pair=order.exchange_pair, side=TradeSide.BUY, trade_type=order.type, quantity=transfer.quantity, price=transfer.price, commission=transfer.commission) return trade

关键点:

  1. 限价保护:LIMIT且order.price < current_price时直接返回None,不产生成交;
  2. 市价比例成交:MARKET单按order.price / max(current_price, order.price)缩放成交量,模拟“以设定价格挂单、被市价部分吞噬”的效果;
  3. 佣金计算:commission = options.commission * filled,其中options.commission是 ExchangeOptions 的默认值0.003(0.3%);若佣金为正但低于工具精度下限,会被抬升到最小精度并输出logging.warning;
  4. 净数量:quantity = filled - commission,即实际入账数量是扣除佣金后的净值;
  5. 钱包转移:通过Wallet.transfer(...)完成base_wallet → quote_wallet(买入方向)的真实资金划转,reason="BUY"会记录进账本;
  6. 构造 Trade:step取自clock.step(成交发生的时间步),price与commission直接取自转移结果transfer。

卖出方向execute_sell_order完全对称:方向为TradeSide.SELL,转移方向为quote_wallet → base_wallet,reason="SELL"。之后 Exchange.execute_order 拿到非空Trade即调用order.fill(trade),把成交写回订单。

5. 成交如何被消费:order.fill 与 Broker.on_fill

5.1 Order.fill:把成交计入订单剩余量

def fill(self, trade: 'Trade') -> None: self.status = OrderStatus.PARTIALLY_FILLED filled = trade.quantity + trade.commission self.remaining -= filled self.trades += [trade] for listener in self.listeners or []: listener.on_fill(self, trade)

fill做了三件事:

  • 把订单状态置为PARTIALLY_FILLED(部分成交;若这是最后一笔则随后由complete()置为FILLED);
  • 用trade.quantity + trade.commission还原毛成交额,并从order.remaining中扣除,从而实现“一次订单可被多笔 Trade 分批填满”;
  • 把Trade追加进order.trades列表,并广播on_fill事件。

5.2 Broker.on_fill:按订单归档成交

def on_fill(self, order: "Order", trade: "Trade") -> None: if trade.order_id in self.executed and trade not in self.trades: self.trades[trade.order_id] = self.trades.get(trade.order_id, []) self.trades[trade.order_id] += [trade] if order.is_complete: next_order = order.complete() ...

Broker(broker.py)维护一个trades: OrderedDict[str, Trade],以trade.order_id为键、以成交列表为值,把同一订单产生的多笔成交聚合在一起。这正是回测结束后重放“每笔订单实际发生了什么”的数据源。注意Broker.trades在reset()时会被清空(见 broker.py),因此它记录的是自上次重置以来的成交,适用于单个回合内的分析。

6. 序列化:to_dict 与 to_json

Trade提供了两种序列化方式,便于日志记录、可视化和外部系统对接:

def to_dict(self): return {'id': self.id, 'order_id': self.order_id, 'step': self.step, 'exchange_pair': self.exchange_pair, 'base_symbol': self.exchange_pair.pair.base.symbol, 'quote_symbol': self.exchange_pair.pair.quote.symbol, 'side': self.side, 'type': self.type, 'size': self.size, 'quantity': self.quantity, 'price': self.price, 'commission': self.commission, "created_at": self.created_at}

to_dict保留对象引用(如exchange_pair、side、quantity),适合进程内传递;而to_json则把所有值转为字符串/浮点等可 JSON 序列化形式:

def to_json(self): return {'id': str(self.id), 'order_id': str(self.order_id), 'step': int(self.step), 'exchange_pair': str(self.exchange_pair), 'base_symbol': str(self.exchange_pair.pair.base.symbol), 'quote_symbol': str(self.exchange_pair.pair.quote.symbol), 'side': str(self.side), 'type': str(self.type), 'size': float(self.size), 'quantity': str(self.quantity), 'price': float(self.price), 'commission': str(self.commission), "created_at": str(self.created_at)}

两者均额外展开base_symbol/quote_symbol,把交易对符号直接平铺为独立键,方便下游按标的物过滤成交。__str__与__repr__基于to_dict()生成形如<Trade: id=..., order_id=..., step=..., ...>的可读表示,调试时直接print(trade)即可看到全貌。

7. 测试与验证:订单域如何验证 Trade 语义

订单域的单测覆盖了TradeSide/TradeType与订单的结合行为,可当作Trade语义的活文档:

  • test_order.py 验证Order的is_buy/is_sell/is_market_order/is_limit_order、order.trades == []等属性,这些属性与Trade的对应属性同源同义;
  • test_broker.py 验证Broker.trades初始为空、成交按order_id归档等行为。

如需在自己项目中复现一笔成交,可参照 test_order.py 的模式:构造Exchange与ExchangePair(exchange, USD / BTC),再用Portfolio持有Wallet(exchange, 10000 * USD),随后通过模拟执行服务生成Trade并断言其字段。

8. 实践要点小结

  1. Trade 是执行服务的产物:不要直接Trade(...)手动构造,应通过交易所的execute_order服务获得,以保证step、price、commission与真实撮合过程一致;
  2. 方向决定资产归属:用TradeSide.instrument(pair)或trade.is_buy / trade.is_sell判断成交涉及的基础工具与计价工具,不要硬编码交易对顺序;
  3. 佣金单独记账:commission是独立字段,quantity已是净数量(filled - commission),计算账户净值时要区分毛额与净额;
  4. 订单与成交一对多:一个订单可产生多笔Trade(分批成交),用order.trades与Broker.trades[order_id]聚合;
  5. 序列化出口:需要跨进程或写入文件时使用to_json(),进程内调试使用to_dict()/print(trade)。

理解Trade之后,建议继续阅读 broker.py(订单撮合与成交归档)、simulated.py(模拟执行与佣金处理)与 wallet.py(资金转移与锁定),即可完整串起 TensorTrade 从“下单”到“记账”的整个链路。

  • 人工智能
  • 金融科技
  • 机器学习

【免费下载链接】tensortrade

An open source reinforcement learning framework for training, evaluating, and deploying robust trading agents.

项目地址:https://gitcode.com/gh_mirrors/te/tensortrade
点击查看免费下载

相关推荐

上一篇:YimMenu完整指南:GTA5防崩溃菜单的5分钟快速安装与安全使用教程
下一篇:GitHub Desktop 3分钟中文汉化终极指南:一键实现界面本地化

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询