卖出操作 #
了解如何进入市场(多头,self.buy
)后,还要一个“退出概念”,以及了解策略是否在市场中。
本节将演示入场后如何退出市场。
退出逻辑很简单,入场持有 5 根 bar 后(在第 6 根 bar 上)退出,无论是盈利还是亏损。另外,为了简化逻辑,仅在未入场时允许买入订单,即如果有持仓或者进行中的订单都不可买入。
逻辑逻辑 #
要完成这个逻辑,要能确认订单成交时间、成交所在位置、当前是否有进行中的订单以及是否有持仓。
订单状态 #
订单确认状态通过 Strategy
的订单状态变化 notify_order
方法监听。
def notify_order(self, order):
print(order.status)
订单状态有 Accepted
(已接受)、Submitted
(已提交)、Completed
(已成交)、Margin
(保证金不足)、Rejected
(已拒绝)。
订单类中除了状态,还包括订单的其他信息,如执行价格 - order.executed.price
,已执行价值 - order.executed.value
,订单手续费 - order.executed.comm
。
更多订单的信息,可查看 订单- Orders。
成交位置 #
策略对象提供了对默认数据源位置的访问,通过 len(self)
即可获得。
len(self)
next 方法没有提供 “bar索引”,因此很难理解何时经过了 5 根bar,但调用对象的 len 方法,它会告诉你线的长度。
我们只需记下订单完成时的长度,并查看当前长度是否距离其5根bar。
def notify_order(self, order):
if order.status in [order.Completed]:
self.bar_executed = len(self)
def next(self):
# 其他代码
if len(self) >= (self.bar_executed + 5):
# 卖出退场
此处无 “时间” 或 “时间框架” 含义,仅仅是 bar 的数量。bar可以代表1分钟、1小时、1天、1周或任何其他时间周期。尽管我们知道数据源是每日的,但策略不对其做任何假设。
进行中的订单 #
是否有进行中的订单如何判断?
我们调用买入和卖出方法会返回创建的(尚未执行的)订单。我们可记录创建后的订单,待最终确认后(即非提交中 Submitted 和订单被接受 Accepted)将其清空。而我们只要在每次交易前,检查是否有进行中的订单即可。
def notify_order(self, order):
if order.status in [order.Submitted, order.Accepted]:
return
self.order = None
def next(self):
# 是否有进行中的订单
if self.order:
return
# 如果满足买入
self.order = self.buy()
# 如果满足退出
self.order = self.sell()
是否有持仓 #
是否有持仓的判断比较简单,通过 self.position
即可判断。
if not self.position:
# 入场逻辑
else:
# 出场逻辑
完整示例 #
from __future__ import (absolute_import, division, print_function,
unicode_literals)
import datetime # For datetime objects
import os.path # To manage paths
import sys # To find out the script name (in argv[0])
import backtrader as bt
class TestStrategy(bt.Strategy):
def log(self, txt, dt=None):
dt = dt or self.datas[0].datetime.date(0)
print('%s, %s' % (dt.isoformat(), txt))
def __init__(self):
self.dataclose = self.datas[0].close
self.order = None
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('BUY EXECUTED, %.2f' % order.executed.price)
elif order.issell():
self.log('SELL EXECUTED, %.2f' % order.executed.price)
self.bar_executed = len(self)
elif order.status in [order.Canceled, order.Margin, order.Rejected]:
self.log('Order Canceled/Margin/Rejected')
self.order = None
def next(self):
self.log('Close, %.2f' % self.dataclose[0])
if self.order:
return
if not self.position:
if self.dataclose[0] < self.dataclose[-1]:
if self.dataclose[-1] < self.dataclose[-2]:
self.log('BUY CREATE, %.2f' % self.dataclose[0])
self.order = self.buy()
else:
if len(self) >= (self.bar_executed + 5):
self.log('SELL CREATE, %.2f' % self.dataclose[0])
self.order = self.sell()
if __name__ == '__main__':
cerebro = bt.Cerebro()
cerebro.addstrategy(TestStrategy)
modpath = os.path.dirname(os.path.abspath(sys.argv[0]))
datapath = os.path.join(modpath, '../../datas/orcl-1995-2014.txt')
data = bt.feeds.YahooFinanceCSVData(
dataname=datapath,
fromdate=datetime.datetime(2000, 1, 1),
todate=datetime.datetime(2000, 12, 31),
reverse=False)
cerebro.adddata(data)
cerebro.broker.setcash(100000.0)
print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue())
cerebro.run()
print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())
输出:
Starting Portfolio Value: 100000.00
2000-01-03, Close, 27.85
2000-01-04, Close, 25.39
2000-01-05, Close, 24.05
2000-01-05, BUY CREATE, 24.05
2000-01-06, BUY EXECUTED, 23.61
2000-01-06, Close, 22.63
2000-01-07, Close, 24.37
2000-01-10, Close, 27.29
2000-01-11, Close, 26.49
2000-01-12, Close, 24.90
2000-01-13, Close, 24.77
2000-01-13, SELL CREATE, 24.77
2000-01-14, SELL EXECUTED, 25.70
...
...
...
2000-12-15, SELL CREATE, 26.93
2000-12-18, SELL EXECUTED, 28.29
2000-12-18, Close, 30.18
2000-12-19, Close, 28.88
2000-12-20, Close, 26.88
2000-12-20, BUY CREATE, 26.88
2000-12-21, BUY EXECUTED, 26.23
2000-12-21, Close, 27.82
2000-12-22, Close, 30.06
2000-12-26, Close, 29.17
2000-12-27, Close, 28.94
2000-12-28, Close, 29.29
2000-12-29, Close, 27.41
Final Portfolio Value: 100018.53
现在,系统确实赚到了钱,恭喜我们又进了一步。