Brokers and Orders¶
Backtrader provides a comprehensive broker simulation system that models real trading conditions including commissions, slippage, margin, and various order types.
Broker Basics¶
The broker manages:
Cash: Available trading capital
Value: Total portfolio value (cash + positions)
Positions: Current holdings
Orders: Order management and execution
cerebro = bt.Cerebro()
# Set initial cash
cerebro.broker.setcash(100000)
# Get current values
cash = cerebro.broker.getcash()
value = cerebro.broker.getvalue()
Commission Models¶
Percentage Commission¶
# Simple percentage commission (0.1%)
cerebro.broker.setcommission(commission=0.001)
# Per-trade commission with percentage
cerebro.broker.setcommission(
commission=0.001, # 0.1% per trade
margin=None, # No margin requirement
mult=1.0 # Contract multiplier
)
Fixed Commission¶
# Fixed commission per trade
class FixedCommission(bt.CommInfoBase):
params = (
('commission', 5.0), # $5 per trade
('stocklike', True),
('commtype', bt.CommInfoBase.COMM_FIXED),
)
def _getcommission(self, size, price, pseudoexec):
return self.p.commission
cerebro.broker.addcommissioninfo(FixedCommission())
Futures Commission¶
# Futures with margin and multiplier
cerebro.broker.setcommission(
commission=2.0, # $2 per contract
margin=5000, # Margin per contract
mult=10, # Contract multiplier
commtype=bt.CommInfoBase.COMM_FIXED
)
Slippage¶
Slippage simulates the difference between expected and actual execution prices.
# Fixed slippage (price points)
cerebro.broker.set_slippage_fixed(fixed=0.01)
# Percentage slippage
cerebro.broker.set_slippage_perc(perc=0.001) # 0.1%
# Volume-based slippage
cerebro.broker.set_slippage_perc(
perc=0.001,
slip_open=True, # Apply to open orders
slip_limit=False, # Don't apply to limit orders
slip_match=True, # Apply to matched orders
slip_out=False # Don't slip out of high/low range
)
Order Types¶
Market Orders¶
Execute immediately at current market price.
# Buy at market
self.buy()
# Sell at market with size
self.sell(size=100)
Limit Orders¶
Execute at specified price or better.
# Buy limit order
self.buy(exectype=bt.Order.Limit, price=100.0)
# Sell limit order
self.sell(exectype=bt.Order.Limit, price=110.0, size=50)
Stop Orders¶
Trigger when price reaches stop level.
# Stop buy (for short covering)
self.buy(exectype=bt.Order.Stop, price=105.0)
# Stop sell (stop loss)
self.sell(exectype=bt.Order.Stop, price=95.0)
Stop-Limit Orders¶
Combination of stop and limit orders.
# Stop-limit order
self.buy(
exectype=bt.Order.StopLimit,
price=105.0, # Stop trigger price
plimit=106.0 # Limit price after trigger
)
Bracket Orders¶
Entry with take-profit and stop-loss.
# Bracket order (entry + stop loss + take profit)
orders = self.buy_bracket(
price=100.0, # Entry price (limit)
stopprice=95.0, # Stop loss price
limitprice=110.0, # Take profit price
size=100
)
# Returns tuple: (main_order, stop_order, limit_order)
Target Orders¶
Automatically calculate order size to reach target.
# Target percent of portfolio value
self.order_target_percent(target=0.5) # 50% of portfolio
# Target size
self.order_target_size(target=100) # Target 100 shares
# Target value
self.order_target_value(target=10000) # Target $10,000 position
Order Management¶
Order Status¶
def notify_order(self, order):
if order.status in [order.Submitted, order.Accepted]:
return # Order pending
if order.status == order.Completed:
if order.isbuy():
print(f'BUY executed at {order.executed.price:.2f}')
else:
print(f'SELL executed at {order.executed.price:.2f}')
elif order.status == order.Canceled:
print('Order canceled')
elif order.status == order.Margin:
print('Insufficient margin')
elif order.status == order.Rejected:
print('Order rejected')
Cancel Orders¶
# Cancel specific order
self.cancel(order)
# Cancel all pending orders
for order in self.broker.get_orders_open():
self.cancel(order)
Order Validity¶
from datetime import datetime, timedelta
# Good Till Date
self.buy(
exectype=bt.Order.Limit,
price=100.0,
valid=datetime.now() + timedelta(days=5)
)
# Good Till Canceled (default)
self.buy(exectype=bt.Order.Limit, price=100.0, valid=None)
# Day order
self.buy(exectype=bt.Order.Limit, price=100.0, valid=bt.Order.DAY)
Position Management¶
def next(self):
# Check current position
if self.position:
print(f'Position size: {self.position.size}')
print(f'Average price: {self.position.price}')
print(f'Current P&L: {self.position.size * (self.data.close[0] - self.position.price)}')
# Get position for specific data
pos = self.getposition(self.datas[0])
# Close all positions
if self.position:
self.close()
Margin and Leverage¶
# Set leverage for futures
cerebro.broker.setcommission(
commission=0.0,
margin=10000, # Margin per contract
mult=50, # Contract multiplier (e.g., E-mini S&P)
leverage=10.0 # Maximum leverage
)
# Check available margin
def next(self):
available_margin = self.broker.getcash()
required_margin = self.broker.get_margin_info(self.data)
Cheat-On-Open / Cheat-On-Close¶
Special execution modes for backtesting convenience:
# Cheat-On-Open: Order placed at bar close, executed at next bar's open
cerebro.broker.set_coo(True)
# Cheat-On-Close: Order placed and executed at current bar's close
# WARNING: Not realistic - used only for specific testing scenarios
cerebro.broker.set_coc(True)
警告
Cheat-On-Close is not realistic and should only be used for testing specific scenarios where you need to simulate executing at the closing price.
Broker Methods Reference¶
Method |
Description |
|---|---|
|
Set initial capital |
|
Get available cash |
|
Get total portfolio value |
|
Get position for a data feed |
|
Get list of pending orders |
|
Set percentage slippage |
|
Set fixed slippage (points) |
|
Set commission parameters |
|
Enable Cheat-On-Open |
|
Enable Cheat-On-Close |
|
Add or remove cash (negative value) |
Best Practices¶
Realistic Simulation
# Include all trading costs cerebro.broker.setcommission(commission=0.001) cerebro.broker.set_slippage_perc(perc=0.0005)
Order Tracking
def __init__(self): self.order = None def next(self): if self.order: # Check if order pending return # Trading logic here
Risk Management
def next(self): # Check if we have enough cash if self.broker.getcash() < 1000: return # Position sizing size = int(self.broker.getvalue() * 0.02 / self.data.close[0]) if size > 0: self.buy(size=size)
See Also¶
strategies - Strategy development
analyzers - Performance analysis
live_trading - Live trading setup