简介
通过向cerebro传入自定义Sizer,修改buy/sell的执行逻辑,控制下单量等。
在自定义的Sizer类中,可以访问策略对象self.strategy,以及策略的经纪行对象self.strategy.broker
(self.broker
),通过broker可以访问仓位(self.broker.getposition(data)
)和市值(self.broker.getvalue()
)等信息.
在Sizer类中的 def _getsizing(self, comminfo, cash, data, isbuy):
方法中,可通过其参数访问更多信息,包括佣金信息,现金,行情数据。
其中isbuy参数确定订单是买单还是卖单。该方法需要返回 int
类型表示下单量,如果返回 0。则不执行订单操作。
将自定义的Sizer注入Cerebro
有两种方法注入sizer到cerebro
1、所有策略都会使用该Sizer
cerebro.addsizer(LongOnly) # 全部策略使用自定义sizer
2、按策略id设置Sizer,给不同的策略设置不同的Sizer
idx = cerebro.addstrategy(SmaCross)
cerebro.addsizer_byidx(idx, maxRiskSizer)
示例
'''自定义sizer'''
import math
import backtrader as bt
from feed import feed
from logger import lg
class maxRiskSizer(bt.Sizer):
params = (
('risk', 0.03),
)
def __init__(self):
if self.p.risk > 1 or self.p.risk < 0:
raise ValueError(
'The risk parameter is a percentage which must be enter as a float: e.g. 0.5'
)
def _getsizing(self, comminfo, cash, data, isbuy):
if isbuy:
size = math.floor(
(cash * self.p.risk) / data[0]
)
else:
size = math.floor(
(cash * self.p.risk) / data[0]
) * -1
return size
class LongOnly(bt.Sizer):
'''自定义订单管理类
1、默认股数 100
2、不能做空
'''
params = (
('stake', 100),
)
def _getsizing(self, comminfo, cash, data, isbuy):
if isbuy:
return self.p.stake
# 处理卖单情况,先获取仓位对象
position = self.broker.getposition(data)
# 返回卖单的下单量,防止形成做空
return min(self.p.stake, position.size)
class SmaCross(bt.Strategy):
params = (
('period', 5),
)
def __init__(self):
self.move_average = bt.ind.MovingAverageSimple(
self.data.close,
period=self.p.period
)
self.crossover = bt.ind.CrossOver(
self.data.close,
self.move_average
)
def log(self, txt, dt=None):
dt = dt or self.data.datetime.date(0)
if isinstance(dt, float):
dt = bt.num2date(dt)
lg.info(
'%s, %s' % (dt.isoformat(), txt)
)
def notify_order(self, order):
if order.status in [order.Submitted, order.Accepted]:
return
if order.status in [order.Completed]:
if order.isbuy():
self.log(f'买单执行, price {order.executed.price}、size {order.executed.size}')
elif order.issell():
self.log(f'卖单执行, price {order.executed.price}、size {order.executed.size}')
else:
self.log(f'订单状态 {order.getstatusname(order.status)}')
def notify_trade(self, trade):
if trade.isclosed:
self.log(
f'毛收益 {trade.pnl}, 扣佣后收益 {trade.pnlcomm}, 佣金 {trade.commission}'
)
def next(self):
if self.crossover > 0:
self.buy()
if self.crossover < 0:
self.close()
if __name__ == '__main__':
cerebro = bt.Cerebro()
cerebro.adddata(feed)
idx = cerebro.addstrategy(SmaCross)
cerebro.broker.setcash(10000.0)
cerebro.broker.setcommission(0.001)
cerebro.broker.set_slippage_fixed(0.05)
# cerebro.addsizer(LongOnly) # 全部策略使用自定义sizer
cerebro.addsizer_byidx(idx, maxRiskSizer)
lg.warning(f'初始市值 {cerebro.broker.get_value()}')
cerebro.run(stdstats=False)
lg.warning(f'最终市值 {cerebro.broker.get_value()}')
cerebro.plot()