经纪商与订单¶
Backtrader 提供了完整的经纪商模拟系统,可以模拟真实交易环境, 包括手续费、滑点、保证金和各种订单类型。
经纪商基础¶
经纪商负责管理:
现金:可用交易资金
市值:总投资组合价值(现金 + 持仓)
持仓:当前持有的头寸
订单:订单管理和执行
cerebro = bt.Cerebro()
# 设置初始资金
cerebro.broker.setcash(100000)
# 获取当前值
cash = cerebro.broker.getcash()
value = cerebro.broker.getvalue()
手续费模型¶
百分比手续费¶
# 简单百分比手续费 (0.1%)
cerebro.broker.setcommission(commission=0.001)
# 带百分比的每笔交易手续费
cerebro.broker.setcommission(
commission=0.001, # 每笔交易 0.1%
margin=None, # 无保证金要求
mult=1.0 # 合约乘数
)
固定手续费¶
# 每笔交易固定手续费
class FixedCommission(bt.CommInfoBase):
params = (
('commission', 5.0), # 每笔 5 元
('stocklike', True),
('commtype', bt.CommInfoBase.COMM_FIXED),
)
def _getcommission(self, size, price, pseudoexec):
return self.p.commission
cerebro.broker.addcommissioninfo(FixedCommission())
期货手续费¶
# 期货带保证金和乘数
cerebro.broker.setcommission(
commission=2.0, # 每手 2 元
margin=5000, # 每手保证金
mult=10, # 合约乘数
commtype=bt.CommInfoBase.COMM_FIXED
)
滑点¶
滑点模拟预期价格和实际成交价格之间的差异。
# 固定滑点(价格点数)
cerebro.broker.set_slippage_fixed(fixed=0.01)
# 百分比滑点
cerebro.broker.set_slippage_perc(perc=0.001) # 0.1%
# 基于成交量的滑点
cerebro.broker.set_slippage_perc(
perc=0.001,
slip_open=True, # 应用于开盘订单
slip_limit=False, # 不应用于限价订单
slip_match=True, # 应用于匹配订单
slip_out=False # 不滑出最高/最低价范围
)
订单类型¶
市价订单¶
以当前市场价格立即成交。
# 市价买入
self.buy()
# 市价卖出指定数量
self.sell(size=100)
限价订单¶
以指定价格或更优价格成交。
# 限价买入
self.buy(exectype=bt.Order.Limit, price=100.0)
# 限价卖出
self.sell(exectype=bt.Order.Limit, price=110.0, size=50)
止损订单¶
当价格达到触发价时执行。
# 止损买入(用于空头回补)
self.buy(exectype=bt.Order.Stop, price=105.0)
# 止损卖出
self.sell(exectype=bt.Order.Stop, price=95.0)
止损限价订单¶
止损和限价订单的组合。
# 止损限价订单
self.buy(
exectype=bt.Order.StopLimit,
price=105.0, # 止损触发价
plimit=106.0 # 触发后的限价
)
括号订单¶
入场订单 + 止盈 + 止损。
# 括号订单(入场 + 止损 + 止盈)
orders = self.buy_bracket(
price=100.0, # 入场价格(限价)
stopprice=95.0, # 止损价格
limitprice=110.0, # 止盈价格
size=100
)
# 返回元组:(主订单, 止损订单, 限价订单)
目标订单¶
自动计算订单数量以达到目标。
# 目标百分比
self.order_target_percent(target=0.5) # 投资组合的 50%
# 目标数量
self.order_target_size(target=100) # 目标 100 股
# 目标价值
self.order_target_value(target=10000) # 目标 10000 元仓位
订单管理¶
订单状态¶
def notify_order(self, order):
if order.status in [order.Submitted, order.Accepted]:
return # 订单待处理
if order.status == order.Completed:
if order.isbuy():
print(f'买入成交于 {order.executed.price:.2f}')
else:
print(f'卖出成交于 {order.executed.price:.2f}')
elif order.status == order.Canceled:
print('订单已取消')
elif order.status == order.Margin:
print('保证金不足')
elif order.status == order.Rejected:
print('订单被拒绝')
取消订单¶
# 取消特定订单
self.cancel(order)
# 取消所有待处理订单
for order in self.broker.get_orders_open():
self.cancel(order)
订单有效期¶
from datetime import datetime, timedelta
# 指定日期前有效
self.buy(
exectype=bt.Order.Limit,
price=100.0,
valid=datetime.now() + timedelta(days=5)
)
# 撤销前有效(默认)
self.buy(exectype=bt.Order.Limit, price=100.0, valid=None)
# 当日有效
self.buy(exectype=bt.Order.Limit, price=100.0, valid=bt.Order.DAY)
持仓管理¶
def next(self):
# 检查当前持仓
if self.position:
print(f'持仓数量: {self.position.size}')
print(f'持仓均价: {self.position.price}')
print(f'当前盈亏: {self.position.size * (self.data.close[0] - self.position.price)}')
# 获取特定数据的持仓
pos = self.getposition(self.datas[0])
# 平仓所有头寸
if self.position:
self.close()
保证金与杠杆¶
# 设置期货杠杆
cerebro.broker.setcommission(
commission=0.0,
margin=10000, # 每手保证金
mult=50, # 合约乘数
leverage=10.0 # 最大杠杆
)
# 检查可用保证金
def next(self):
available_margin = self.broker.getcash()
Cheat-On-Open / Cheat-On-Close¶
回测便利的特殊执行模式:
# Cheat-On-Open: 订单在K线收盘时下达,在下一根K线开盘时执行
cerebro.broker.set_coo(True)
# Cheat-On-Close: 订单在当前K线收盘价下达并执行
# 警告:不真实 - 仅用于特定测试场景
cerebro.broker.set_coc(True)
警告
Cheat-On-Close 不真实,只应用于需要模拟以收盘价执行的特定测试场景。
Broker 方法参考¶
方法 |
描述 |
|---|---|
|
设置初始资金 |
|
获取可用现金 |
|
获取投资组合总价值 |
|
获取某个数据源的持仓 |
|
获取未成交订单列表 |
|
设置百分比滑点 |
|
设置固定滑点(点数) |
|
设置佣金参数 |
|
启用 Cheat-On-Open |
|
启用 Cheat-On-Close |
|
添加或减少资金(负值) |
最佳实践¶
真实模拟
# 包含所有交易成本 cerebro.broker.setcommission(commission=0.001) cerebro.broker.set_slippage_perc(perc=0.0005)
订单跟踪
def __init__(self): self.order = None def next(self): if self.order: # 检查是否有待处理订单 return # 交易逻辑
风险管理
def next(self): # 检查是否有足够资金 if self.broker.getcash() < 1000: return # 仓位管理 size = int(self.broker.getvalue() * 0.02 / self.data.close[0]) if size > 0: self.buy(size=size)