代码有所删减,完整版及在线运行可访问Backtrader官方中文文档
backtrader是高度可配置的,希望大家发现其中的乐趣。
基本要求是: Python 2.7 Python 3.2 / 3.3 / 3.4 / 3.5 如有绘图需求,则要求 Matplotlib> = 1.4.1
!pip install backtrader -i https://mirrors.cloud.tencent.com/pypi/simple
Looking in indexes: https://mirrors.cloud.tencent.com/pypi/simple
Requirement already satisfied: backtrader in /opt/conda/lib/python3.6/site-packages (1.9.76.123)
[33mWARNING: You are using pip version 20.3.3; however, version 21.3.1 is available.
You should consider upgrading via the '/opt/conda/bin/python -m pip install --upgrade pip' command.[0m
交易数据(Data Feeds)、技术指标(Indicators)和策略(Strategies)都是折线(Line)。 折线(Line)是由一系列的点组成的。
通常交易数据(Data Feeds)包含以下几个组成部分: 开盘价( Open)、最高价(High)、最低价(Low)、收盘价(Close)、成交量(Volume)、持仓量(OpenInterest)等。 比如:所有的开盘价( Open)按时间组成一条折线(Line),那么一组交易数据(Data Feeds)就 应该包含了6条折线(Line)。
再加上时间(DateTime)一共有7条折线(Line)。时间,一般用作一组交易数据的主键。
当访问一条折线(Line)的数据时,会默认从下标为0的位置开始,最后一个数据通过下标-1来获取。这样的设计和Python的迭代器是一致的,所以折线(Line)是可以迭代遍历的。
例如:创建一个简单移动平均值的策略(均值策略): self.sma = SimpleMovingAverage(…)
访问此移动平均线的当前值的最简单方法:av = self.sma[0]
所以在回测过程中,无需知道已经处理了多少条/分钟/天/月,“0”一直指向当前值。
按照Python遍历数组的方式,用下标-1来访问最后一个值: previous_value = self.sma[-1] 同理:-2、-3下标也是可以照常使用。
先跑一个基本框架
import backtrader as bt
import time
# 创建Cerebro引擎
cerebro = bt.Cerebro()
#Cerebro引擎在后台创建broker(经纪人),系统默认资金量为10000
# 引擎运行前打印期出资金
print('组合期初资金: %.2f' % cerebro.broker.getvalue())
cerebro.run()
# 引擎运行后打期末资金
print('组合期末资金: %.2f' % cerebro.broker.getvalue())
组合期初资金: 10000.00
组合期末资金: 10000.00
在金融世界中,可以肯定的是,只有Losser才以1万开始。让我们更改现金并再次运行该示例。
#导入backtrader框架
import backtrader as bt
# 创建Cerebro引擎
cerebro = bt.Cerebro()
# Cerebro引擎在后台创建broker(经纪人),系统默认资金量为10000
# 设置投资金额100000.0
cerebro.broker.setcash(100000.0)
# 引擎运行前打印期出资金
print('组合期初资金: %.2f' % cerebro.broker.getvalue())
cerebro.run()
# 引擎运行后打期末资金
print('组合期末资金: %.2f' % cerebro.broker.getvalue())
组合期初资金: 100000.00
组合期末资金: 100000.00
拥有现金是一件快乐的事,这一切背后最终的目的就是: 不动一根手指,就能让自动化策略通过操作资产的交易数据来增加现金。 无数据,不快乐,让我们继续加入交易数据。
import datetime #
import os.path # 路径管理
import sys # 获取当前运行脚本的路径 (in argv[0])
#导入backtrader框架
import backtrader as bt
# 创建Cerebro引擎
cerebro = bt.Cerebro()
# Cerebro引擎在后台创建broker(经纪人),系统默认资金量为10000
# 获取当前运行脚本所在目录
modpath = os.path.dirname(os.path.abspath(sys.argv[0]))
# 拼接加载路径
datapath = os.path.join(modpath, '/home/mw/input/backtrader7822/orcl-1995-2014.txt')
# 创建交易数据集
data = bt.feeds.YahooFinanceCSVData(
dataname=datapath,
# 数据必须大于fromdate
fromdate=datetime.datetime(2000, 1, 1),
# 数据必须小于todate
todate=datetime.datetime(2000, 12, 31),
reverse=False)
# 加载交易数据
cerebro.adddata(data)
# 设置投资金额100000.0
cerebro.broker.setcash(100000.0)
# 引擎运行前打印期出资金
print('组合期初资金: %.2f' % cerebro.broker.getvalue())
cerebro.run()
# 引擎运行后打期末资金
print('组合期末资金: %.2f' % cerebro.broker.getvalue())
组合期初资金: 100000.00
组合期末资金: 100000.00
代码量略有增加,因为我们添加了:找出数据文件所在的路径通过datetime对象过滤我们想要操作的数据
注意: Yahoo的价格数据有点非主流,它是以时间倒序排列的。datetime.datetime()中的reversed=True参数是将顺序反转一次,这样就得到了我们想要的正序数据。
资金和交易数据都有了,探险即将开始。让我们给策略加点代码,打印出每天的”收盘价”。 DataSeries(交易数据的基类)对象,可以直接访问到 OHLC (开盘价、最高价、最低价、收盘价)等数据。这使我们打印数据很方便。
import datetime #
import os.path # 路径管理
import sys # 获取当前运行脚本的路径 (in argv[0])
#导入backtrader框架
import backtrader as bt
# 创建策略继承bt.Strategy
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
def next(self):
# 记录收盘价
self.log('Close, %.2f' % self.dataclose[0])
# 创建Cerebro引擎
cerebro = bt.Cerebro()
# Cerebro引擎在后台创建broker(经纪人),系统默认资金量为10000
# 为Cerebro引擎添加策略
cerebro.addstrategy(TestStrategy)
# 获取当前运行脚本所在目录
modpath = os.path.dirname(os.path.abspath(sys.argv[0]))
# 拼接加载路径
datapath = os.path.join(modpath, '/home/mw/input/backtrader7822/orcl-1995-2014.txt')
# 创建交易数据集
data = bt.feeds.YahooFinanceCSVData( dataname=datapath,
# 数据必须大于fromdate
fromdate=datetime.datetime(2000, 1, 1),
# 数据必须小于todate
todate=datetime.datetime(2000, 12, 31),
reverse=False)
# 加载交易数据
cerebro.adddata(data)
# 设置投资金额100000.0
cerebro.broker.setcash(100000.0)
# 引擎运行前打印期出资金
print('组合期初资金: %.2f' % cerebro.broker.getvalue())
cerebro.run()
# 引擎运行后打期末资金
print('组合期末资金: %.2f' % cerebro.broker.getvalue())
组合期初资金: 100000.00
2000-01-03, Close, 26.27
2000-01-04, Close, 23.95
2000-01-05, Close, 22.68
2000-01-06, Close, 21.35
2000-01-07, Close, 22.99
2000-01-10, Close, 25.74
2000-01-11, Close, 24.99
2000-01-12, Close, 23.49
2000-01-13, Close, 23.36
2000-01-14, Close, 23.75
2000-01-18, Close, 24.74
2000-01-19, Close, 25.41
2000-01-20, Close, 26.35
2000-01-21, Close, 26.55
2000-01-24, Close, 24.10
2000-01-25, Close, 25.10
2000-01-26, Close, 24.49
2000-01-27, Close, 23.04
2000-01-28, Close, 21.07
2000-01-31, Close, 22.22
2000-02-01, Close, 24.02
2000-02-02, Close, 24.16
2000-02-03, Close, 25.21
2000-02-04, Close, 25.71
2000-02-07, Close, 26.66
2000-02-08, Close, 26.49
2000-02-09, Close, 26.66
2000-02-10, Close, 27.71
2000-02-11, Close, 26.55
2000-02-14, Close, 27.66
2000-02-15, Close, 27.30
2000-02-16, Close, 27.24
2000-02-17, Close, 27.41
2000-02-18, Close, 26.05
2000-02-22, Close, 26.38
2000-02-23, Close, 28.05
2000-02-24, Close, 27.55
2000-02-25, Close, 31.41
2000-02-28, Close, 30.52
2000-02-29, Close, 33.02
2000-03-01, Close, 31.80
2000-03-02, Close, 30.47
2000-03-03, Close, 33.36
2000-03-06, Close, 33.69
2000-03-07, Close, 33.33
2000-03-08, Close, 36.97
2000-03-09, Close, 37.36
2000-03-10, Close, 36.30
2000-03-13, Close, 35.02
2000-03-14, Close, 34.25
2000-03-15, Close, 34.97
2000-03-16, Close, 36.44
2000-03-17, Close, 35.50
2000-03-20, Close, 34.75
2000-03-21, Close, 35.89
2000-03-22, Close, 37.39
2000-03-23, Close, 38.64
2000-03-24, Close, 38.69
2000-03-27, Close, 39.33
2000-03-28, Close, 38.50
2000-03-29, Close, 36.69
2000-03-30, Close, 34.88
2000-03-31, Close, 34.72
2000-04-03, Close, 34.19
2000-04-04, Close, 33.77
2000-04-05, Close, 34.80
2000-04-06, Close, 36.55
2000-04-07, Close, 38.75
2000-04-10, Close, 36.69
2000-04-11, Close, 34.41
2000-04-12, Close, 32.52
2000-04-13, Close, 31.99
2000-04-14, Close, 27.80
2000-04-17, Close, 33.27
2000-04-18, Close, 35.11
2000-04-19, Close, 33.16
2000-04-20, Close, 31.49
2000-04-24, Close, 32.22
2000-04-25, Close, 33.61
2000-04-26, Close, 32.11
2000-04-27, Close, 34.38
2000-04-28, Close, 35.55
2000-05-01, Close, 35.44
2000-05-02, Close, 34.61
2000-05-03, Close, 33.72
2000-05-04, Close, 33.02
2000-05-05, Close, 34.16
2000-05-08, Close, 32.16
2000-05-09, Close, 32.02
2000-05-10, Close, 30.08
2000-05-11, Close, 32.19
2000-05-12, Close, 32.99
2000-05-15, Close, 34.25
2000-05-16, Close, 35.22
2000-05-17, Close, 34.77
2000-05-18, Close, 32.49
2000-05-19, Close, 31.16
2000-05-22, Close, 30.16
2000-05-23, Close, 27.85
2000-05-24, Close, 28.57
2000-05-25, Close, 29.55
2000-05-26, Close, 29.80
2000-05-30, Close, 32.99
2000-05-31, Close, 31.97
2000-06-01, Close, 34.63
2000-06-02, Close, 35.66
2000-06-05, Close, 36.00
2000-06-06, Close, 34.27
2000-06-07, Close, 35.58
2000-06-08, Close, 36.64
2000-06-09, Close, 36.77
2000-06-12, Close, 35.83
2000-06-13, Close, 36.33
2000-06-14, Close, 35.13
2000-06-15, Close, 36.69
2000-06-16, Close, 36.41
2000-06-19, Close, 38.25
2000-06-20, Close, 38.27
2000-06-21, Close, 38.33
2000-06-22, Close, 36.25
2000-06-23, Close, 35.36
2000-06-26, Close, 36.77
2000-06-27, Close, 36.58
2000-06-28, Close, 36.89
2000-06-29, Close, 35.97
2000-06-30, Close, 37.39
2000-07-03, Close, 35.66
2000-07-05, Close, 32.16
2000-07-06, Close, 33.63
2000-07-07, Close, 33.75
2000-07-10, Close, 32.97
2000-07-11, Close, 32.16
2000-07-12, Close, 33.22
2000-07-13, Close, 33.69
2000-07-14, Close, 33.86
2000-07-17, Close, 33.86
2000-07-18, Close, 32.99
2000-07-19, Close, 32.80
2000-07-20, Close, 34.75
2000-07-21, Close, 33.55
2000-07-24, Close, 33.36
2000-07-25, Close, 33.80
2000-07-26, Close, 34.13
2000-07-27, Close, 33.38
2000-07-28, Close, 32.19
2000-07-31, Close, 33.44
2000-08-01, Close, 32.52
2000-08-02, Close, 32.52
2000-08-03, Close, 34.44
2000-08-04, Close, 36.27
2000-08-07, Close, 36.41
2000-08-08, Close, 36.91
2000-08-09, Close, 36.19
2000-08-10, Close, 35.61
2000-08-11, Close, 36.08
2000-08-14, Close, 36.64
2000-08-15, Close, 36.14
2000-08-16, Close, 36.11
2000-08-17, Close, 37.33
2000-08-18, Close, 36.16
2000-08-21, Close, 37.00
2000-08-22, Close, 37.16
2000-08-23, Close, 36.86
2000-08-24, Close, 37.66
2000-08-25, Close, 37.64
2000-08-28, Close, 38.58
2000-08-29, Close, 39.03
2000-08-30, Close, 39.25
2000-08-31, Close, 40.44
2000-09-01, Close, 41.19
2000-09-05, Close, 40.50
2000-09-06, Close, 39.69
2000-09-07, Close, 40.56
2000-09-08, Close, 38.50
2000-09-11, Close, 37.11
2000-09-12, Close, 35.30
2000-09-13, Close, 36.39
2000-09-14, Close, 37.78
2000-09-15, Close, 34.83
2000-09-18, Close, 34.01
2000-09-19, Close, 35.27
2000-09-20, Close, 35.55
2000-09-21, Close, 35.11
2000-09-22, Close, 35.91
2000-09-25, Close, 35.02
2000-09-26, Close, 35.33
2000-09-27, Close, 35.52
2000-09-28, Close, 36.24
2000-09-29, Close, 35.02
2000-10-02, Close, 35.02
2000-10-03, Close, 30.91
2000-10-04, Close, 30.30
2000-10-05, Close, 30.38
2000-10-06, Close, 30.08
2000-10-09, Close, 29.69
2000-10-10, Close, 28.74
2000-10-11, Close, 27.69
2000-10-12, Close, 28.02
2000-10-13, Close, 31.69
2000-10-16, Close, 30.74
2000-10-17, Close, 29.96
2000-10-18, Close, 29.85
2000-10-19, Close, 32.36
2000-10-20, Close, 31.35
2000-10-23, Close, 30.30
2000-10-24, Close, 31.85
2000-10-25, Close, 30.58
2000-10-26, Close, 30.30
2000-10-27, Close, 30.41
2000-10-30, Close, 28.13
2000-10-31, Close, 29.35
2000-11-01, Close, 27.91
2000-11-02, Close, 26.30
2000-11-03, Close, 26.96
2000-11-06, Close, 24.85
2000-11-07, Close, 23.63
2000-11-08, Close, 22.07
2000-11-09, Close, 24.18
2000-11-10, Close, 22.63
2000-11-13, Close, 22.01
2000-11-14, Close, 25.24
2000-11-15, Close, 25.68
2000-11-16, Close, 24.35
2000-11-17, Close, 25.63
2000-11-20, Close, 22.01
2000-11-21, Close, 21.24
2000-11-22, Close, 19.85
2000-11-24, Close, 21.46
2000-11-27, Close, 20.57
2000-11-28, Close, 20.15
2000-11-29, Close, 20.35
2000-11-30, Close, 23.57
2000-12-01, Close, 23.52
2000-12-04, Close, 25.07
2000-12-05, Close, 28.02
2000-12-06, Close, 26.85
2000-12-07, Close, 25.18
2000-12-08, Close, 26.74
2000-12-11, Close, 28.41
2000-12-12, Close, 27.35
2000-12-13, Close, 25.24
2000-12-14, Close, 24.46
2000-12-15, Close, 25.41
2000-12-18, Close, 28.46
2000-12-19, Close, 27.24
2000-12-20, Close, 25.35
2000-12-21, Close, 26.24
2000-12-22, Close, 28.35
2000-12-26, Close, 27.52
2000-12-27, Close, 27.30
2000-12-28, Close, 27.63
2000-12-29, Close, 25.85
组合期末资金: 100000.00
有人说股市有风险,看起来不像啊。 一些解释说明:
框架在调用init时,该策略已经具有一个数据列表datas,这是标准的Python列表,可以按插入顺序访问数据。 列表中的第一个数据 self.datas[0]是用于交易操作,并且策略中的所有元素都是由框架的系统时钟进行同步的。 由于只需访问收盘价数据,于是使用 self.dataclose = self.datas[0].close将第一条价格数据的收盘价赋值给新变量。 系统时钟当经过一个 K线柱的时候,策略的next()方法就会被调用一次。这一过程 将一直循环,直到其他指标信号出现为止。此时,便会输出最终结果。关于这些,后继内容会讲到。
如果K线收盘价出现三连跌,则买入。
import datetime #
import os.path # 路径管理
import sys # 获取当前运行脚本的路径 (in argv[0])
#导入backtrader框架
import backtrader as bt
# 创建策略继承bt.Strategy
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
def next(self):
# 记录收盘价
self.log('Close, %.2f' % self.dataclose[0])
# 今天的收盘价 < 昨天收盘价
if self.dataclose[0] < self.dataclose[-1]:
# 昨天收盘价 < 前天的收盘价
if self.dataclose[-1] < self.dataclose[-2]:
# 买入
self.log('买入, %.2f' % self.dataclose[0])
self.buy()
# 创建Cerebro引擎
cerebro = bt.Cerebro()
# Cerebro引擎在后台创建broker(经纪人),系统默认资金量为10000
# 为Cerebro引擎添加策略
cerebro.addstrategy(TestStrategy)
# 获取当前运行脚本所在目录
modpath = os.path.dirname(os.path.abspath(sys.argv[0]))
# 拼接加载路径
datapath = os.path.join(modpath, '/home/mw/input/backtrader7822/orcl-1995-2014.txt')
# 创建交易数据集
data = bt.feeds.YahooFinanceCSVData(
dataname=datapath,
# 数据必须大于fromdate
fromdate=datetime.datetime(2000, 1, 1),
# 数据必须小于todate
todate=datetime.datetime(2000, 12, 31),
reverse=False)
# 加载交易数据
cerebro.adddata(data)
# 设置投资金额100000.0
cerebro.broker.setcash(100000.0)
# 引擎运行前打印期出资金
print('组合期初资金: %.2f' % cerebro.broker.getvalue())
cerebro.run()
# 引擎运行后打期末资金
print('组合期末资金: %.2f' % cerebro.broker.getvalue())
组合期初资金: 100000.00
2000-01-03, Close, 26.27
2000-01-04, Close, 23.95
2000-01-05, Close, 22.68
2000-01-05, 买入, 22.68
2000-01-06, Close, 21.35
2000-01-06, 买入, 21.35
2000-01-07, Close, 22.99
2000-01-10, Close, 25.74
2000-01-11, Close, 24.99
2000-01-12, Close, 23.49
2000-01-12, 买入, 23.49
2000-01-13, Close, 23.36
2000-01-13, 买入, 23.36
2000-01-14, Close, 23.75
2000-01-18, Close, 24.74
2000-01-19, Close, 25.41
2000-01-20, Close, 26.35
2000-01-21, Close, 26.55
2000-01-24, Close, 24.10
2000-01-25, Close, 25.10
2000-01-26, Close, 24.49
2000-01-27, Close, 23.04
2000-01-27, 买入, 23.04
2000-01-28, Close, 21.07
2000-01-28, 买入, 21.07
2000-01-31, Close, 22.22
2000-02-01, Close, 24.02
2000-02-02, Close, 24.16
2000-02-03, Close, 25.21
2000-02-04, Close, 25.71
2000-02-07, Close, 26.66
2000-02-08, Close, 26.49
2000-02-09, Close, 26.66
2000-02-10, Close, 27.71
2000-02-11, Close, 26.55
2000-02-14, Close, 27.66
2000-02-15, Close, 27.30
2000-02-16, Close, 27.24
2000-02-16, 买入, 27.24
2000-02-17, Close, 27.41
2000-02-18, Close, 26.05
2000-02-22, Close, 26.38
2000-02-23, Close, 28.05
2000-02-24, Close, 27.55
2000-02-25, Close, 31.41
2000-02-28, Close, 30.52
2000-02-29, Close, 33.02
2000-03-01, Close, 31.80
2000-03-02, Close, 30.47
2000-03-02, 买入, 30.47
2000-03-03, Close, 33.36
2000-03-06, Close, 33.69
2000-03-07, Close, 33.33
2000-03-08, Close, 36.97
2000-03-09, Close, 37.36
2000-03-10, Close, 36.30
2000-03-13, Close, 35.02
2000-03-13, 买入, 35.02
2000-03-14, Close, 34.25
2000-03-14, 买入, 34.25
2000-03-15, Close, 34.97
2000-03-16, Close, 36.44
2000-03-17, Close, 35.50
2000-03-20, Close, 34.75
2000-03-20, 买入, 34.75
2000-03-21, Close, 35.89
2000-03-22, Close, 37.39
2000-03-23, Close, 38.64
2000-03-24, Close, 38.69
2000-03-27, Close, 39.33
2000-03-28, Close, 38.50
2000-03-29, Close, 36.69
2000-03-29, 买入, 36.69
2000-03-30, Close, 34.88
2000-03-30, 买入, 34.88
2000-03-31, Close, 34.72
2000-03-31, 买入, 34.72
2000-04-03, Close, 34.19
2000-04-03, 买入, 34.19
2000-04-04, Close, 33.77
2000-04-04, 买入, 33.77
2000-04-05, Close, 34.80
2000-04-06, Close, 36.55
2000-04-07, Close, 38.75
2000-04-10, Close, 36.69
2000-04-11, Close, 34.41
2000-04-11, 买入, 34.41
2000-04-12, Close, 32.52
2000-04-12, 买入, 32.52
2000-04-13, Close, 31.99
2000-04-13, 买入, 31.99
2000-04-14, Close, 27.80
2000-04-14, 买入, 27.80
2000-04-17, Close, 33.27
2000-04-18, Close, 35.11
2000-04-19, Close, 33.16
2000-04-20, Close, 31.49
2000-04-20, 买入, 31.49
2000-04-24, Close, 32.22
2000-04-25, Close, 33.61
2000-04-26, Close, 32.11
2000-04-27, Close, 34.38
2000-04-28, Close, 35.55
2000-05-01, Close, 35.44
2000-05-02, Close, 34.61
2000-05-02, 买入, 34.61
2000-05-03, Close, 33.72
2000-05-03, 买入, 33.72
2000-05-04, Close, 33.02
2000-05-04, 买入, 33.02
2000-05-05, Close, 34.16
2000-05-08, Close, 32.16
2000-05-09, Close, 32.02
2000-05-09, 买入, 32.02
2000-05-10, Close, 30.08
2000-05-10, 买入, 30.08
2000-05-11, Close, 32.19
2000-05-12, Close, 32.99
2000-05-15, Close, 34.25
2000-05-16, Close, 35.22
2000-05-17, Close, 34.77
2000-05-18, Close, 32.49
2000-05-18, 买入, 32.49
2000-05-19, Close, 31.16
2000-05-19, 买入, 31.16
2000-05-22, Close, 30.16
2000-05-22, 买入, 30.16
2000-05-23, Close, 27.85
2000-05-23, 买入, 27.85
2000-05-24, Close, 28.57
2000-05-25, Close, 29.55
2000-05-26, Close, 29.80
2000-05-30, Close, 32.99
2000-05-31, Close, 31.97
2000-06-01, Close, 34.63
2000-06-02, Close, 35.66
2000-06-05, Close, 36.00
2000-06-06, Close, 34.27
2000-06-07, Close, 35.58
2000-06-08, Close, 36.64
2000-06-09, Close, 36.77
2000-06-12, Close, 35.83
2000-06-13, Close, 36.33
2000-06-14, Close, 35.13
2000-06-15, Close, 36.69
2000-06-16, Close, 36.41
2000-06-19, Close, 38.25
2000-06-20, Close, 38.27
2000-06-21, Close, 38.33
2000-06-22, Close, 36.25
2000-06-23, Close, 35.36
2000-06-23, 买入, 35.36
2000-06-26, Close, 36.77
2000-06-27, Close, 36.58
2000-06-28, Close, 36.89
2000-06-29, Close, 35.97
2000-06-30, Close, 37.39
2000-07-03, Close, 35.66
2000-07-05, Close, 32.16
2000-07-05, 买入, 32.16
2000-07-06, Close, 33.63
2000-07-07, Close, 33.75
2000-07-10, Close, 32.97
2000-07-11, Close, 32.16
2000-07-11, 买入, 32.16
2000-07-12, Close, 33.22
2000-07-13, Close, 33.69
2000-07-14, Close, 33.86
2000-07-17, Close, 33.86
2000-07-18, Close, 32.99
2000-07-19, Close, 32.80
2000-07-19, 买入, 32.80
2000-07-20, Close, 34.75
2000-07-21, Close, 33.55
2000-07-24, Close, 33.36
2000-07-24, 买入, 33.36
2000-07-25, Close, 33.80
2000-07-26, Close, 34.13
2000-07-27, Close, 33.38
2000-07-28, Close, 32.19
2000-07-28, 买入, 32.19
2000-07-31, Close, 33.44
2000-08-01, Close, 32.52
2000-08-02, Close, 32.52
2000-08-03, Close, 34.44
2000-08-04, Close, 36.27
2000-08-07, Close, 36.41
2000-08-08, Close, 36.91
2000-08-09, Close, 36.19
2000-08-10, Close, 35.61
2000-08-10, 买入, 35.61
2000-08-11, Close, 36.08
2000-08-14, Close, 36.64
2000-08-15, Close, 36.14
2000-08-16, Close, 36.11
2000-08-16, 买入, 36.11
2000-08-17, Close, 37.33
2000-08-18, Close, 36.16
2000-08-21, Close, 37.00
2000-08-22, Close, 37.16
2000-08-23, Close, 36.86
2000-08-24, Close, 37.66
2000-08-25, Close, 37.64
2000-08-28, Close, 38.58
2000-08-29, Close, 39.03
2000-08-30, Close, 39.25
2000-08-31, Close, 40.44
2000-09-01, Close, 41.19
2000-09-05, Close, 40.50
2000-09-06, Close, 39.69
2000-09-06, 买入, 39.69
2000-09-07, Close, 40.56
2000-09-08, Close, 38.50
2000-09-11, Close, 37.11
2000-09-11, 买入, 37.11
2000-09-12, Close, 35.30
2000-09-12, 买入, 35.30
2000-09-13, Close, 36.39
2000-09-14, Close, 37.78
2000-09-15, Close, 34.83
2000-09-18, Close, 34.01
2000-09-18, 买入, 34.01
2000-09-19, Close, 35.27
2000-09-20, Close, 35.55
2000-09-21, Close, 35.11
2000-09-22, Close, 35.91
2000-09-25, Close, 35.02
2000-09-26, Close, 35.33
2000-09-27, Close, 35.52
2000-09-28, Close, 36.24
2000-09-29, Close, 35.02
2000-10-02, Close, 35.02
2000-10-03, Close, 30.91
2000-10-04, Close, 30.30
2000-10-04, 买入, 30.30
2000-10-05, Close, 30.38
2000-10-06, Close, 30.08
2000-10-09, Close, 29.69
2000-10-09, 买入, 29.69
2000-10-10, Close, 28.74
2000-10-10, 买入, 28.74
2000-10-11, Close, 27.69
2000-10-11, 买入, 27.69
2000-10-12, Close, 28.02
2000-10-13, Close, 31.69
2000-10-16, Close, 30.74
2000-10-17, Close, 29.96
2000-10-17, 买入, 29.96
2000-10-18, Close, 29.85
2000-10-18, 买入, 29.85
2000-10-19, Close, 32.36
2000-10-20, Close, 31.35
2000-10-23, Close, 30.30
2000-10-23, 买入, 30.30
2000-10-24, Close, 31.85
2000-10-25, Close, 30.58
2000-10-26, Close, 30.30
2000-10-26, 买入, 30.30
2000-10-27, Close, 30.41
2000-10-30, Close, 28.13
2000-10-31, Close, 29.35
2000-11-01, Close, 27.91
2000-11-02, Close, 26.30
2000-11-02, 买入, 26.30
2000-11-03, Close, 26.96
2000-11-06, Close, 24.85
2000-11-07, Close, 23.63
2000-11-07, 买入, 23.63
2000-11-08, Close, 22.07
2000-11-08, 买入, 22.07
2000-11-09, Close, 24.18
2000-11-10, Close, 22.63
2000-11-13, Close, 22.01
2000-11-13, 买入, 22.01
2000-11-14, Close, 25.24
2000-11-15, Close, 25.68
2000-11-16, Close, 24.35
2000-11-17, Close, 25.63
2000-11-20, Close, 22.01
2000-11-21, Close, 21.24
2000-11-21, 买入, 21.24
2000-11-22, Close, 19.85
2000-11-22, 买入, 19.85
2000-11-24, Close, 21.46
2000-11-27, Close, 20.57
2000-11-28, Close, 20.15
2000-11-28, 买入, 20.15
2000-11-29, Close, 20.35
2000-11-30, Close, 23.57
2000-12-01, Close, 23.52
2000-12-04, Close, 25.07
2000-12-05, Close, 28.02
2000-12-06, Close, 26.85
2000-12-07, Close, 25.18
2000-12-07, 买入, 25.18
2000-12-08, Close, 26.74
2000-12-11, Close, 28.41
2000-12-12, Close, 27.35
2000-12-13, Close, 25.24
2000-12-13, 买入, 25.24
2000-12-14, Close, 24.46
2000-12-14, 买入, 24.46
2000-12-15, Close, 25.41
2000-12-18, Close, 28.46
2000-12-19, Close, 27.24
2000-12-20, Close, 25.35
2000-12-20, 买入, 25.35
2000-12-21, Close, 26.24
2000-12-22, Close, 28.35
2000-12-26, Close, 27.52
2000-12-27, Close, 27.30
2000-12-27, 买入, 27.30
2000-12-28, Close, 27.63
2000-12-29, Close, 25.85
组合期末资金: 99740.45
若干个买入操作被执行,所以余额在减少。 细心的朋友可能会问,买了多少?买的什么?订单怎么被执行的?BackTrader框架替我们做了这些事: 如果没有指定的话, self.datas[0]即是标的物当前交易数据。 交易数量 =仓位数量,默认值等于1,后面例子我们会修改此参数。
订单被以”市价”成交了。 Broker(经纪人,之前提到过)使用了下一个交易日的开盘价,因为是broker在当前的交日易收盘后天提交的订单,下一个交易日开盘价是他接触到的第一个价格。 这里没有为订单设置佣金费,后边会加上。
在知道如何买入(做多)之后,需要知道如何卖出,并且还需要了解该策略是否在市场中。 Strategy类有一个变量position保存当前持有的资产数量(可以理解为金融术语中的头寸), buy()和sell()会返回被创建的订单(尚未执行的), 订单状态的更改将通过 notify方法通知给策略Strategy。
卖出逻辑也很简单: 5个K线柱后(第6个K线柱)不管涨跌都卖。 请注意,这里没有指定具体时间,而是指定的柱的数量。一个柱可能代表 1分钟、1小时、1天、1星期等等,这取决于你价格数据文件里一条数据代表的周期。 虽然我们知道每个柱代表一天,但策略并不知道。
因为买入的时候,用的是交易日,所以这里应翻译为:5个交易日(第6个交易日)不管涨跌都卖。
另外,当还有头寸的时候,就不再买入了。
注意: 没有将柱的下标传给 next()方法,怎么知道已经经过了5个柱了呢? 这里用了 Python的len()方法获取它Line数据的长度。 交易发生时记下它的长度,后边比较大小,看是否经过了5个柱。
import datetime #
import os.path # 路径管理
import sys # 获取当前运行脚本的路径 (in argv[0])
#导入backtrader框架
import backtrader as bt
# 创建策略继承bt.Strategy
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]:
# broker 提交/接受了,买/卖订单则什么都不做
return
# 检查一个订单是否完成
# 注意: 当资金不足时,broker会拒绝订单
if order.status in [order.Completed]:
if order.isbuy():
self.log('已买入, %.2f' % order.executed.price)
elif order.issell():
self.log('已卖出, %.2f' % order.executed.price)
# 记录当前交易数量
self.bar_executed = len(self)
elif order.status in [order.Canceled, order.Margin, order.Rejected]:
self.log('订单取消/保证金不足/拒绝')
# 其他状态记录为:无挂起订单
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('买入, %.2f' % self.dataclose[0])
# 跟踪订单避免重复
self.order = self.buy()
else:
# 如果已经持仓,且当前交易数据量在买入后5个单位后
if len(self) >= (self.bar_executed + 5):
# 全部卖出
self.log('卖出, %.2f' % self.dataclose[0])
# 跟踪订单避免重复
self.order = self.sell()
# 创建Cerebro引擎
cerebro = bt.Cerebro()
# Cerebro引擎在后台创建broker(经纪人),系统默认资金量为10000
# 为Cerebro引擎添加策略
cerebro.addstrategy(TestStrategy)
# 获取当前运行脚本所在目录
modpath = os.path.dirname(os.path.abspath(sys.argv[0]))
# 拼接加载路径
datapath = os.path.join(modpath, '/home/mw/input/backtrader7822/orcl-1995-2014.txt')
# 创建交易数据集
data = bt.feeds.YahooFinanceCSVData(
dataname=datapath,
# 数据必须大于fromdate
fromdate=datetime.datetime(2000, 1, 1),
# 数据必须小于todate
todate=datetime.datetime(2000, 12, 31),
reverse=False)
# 加载交易数据
cerebro.adddata(data)
# 设置投资金额100000.0
cerebro.broker.setcash(100000.0)
# 引擎运行前打印期出资金
print('组合期初资金: %.2f' % cerebro.broker.getvalue())
cerebro.run()
# 引擎运行后打期末资金
print('组合期末资金: %.2f' % cerebro.broker.getvalue())
组合期初资金: 100000.00
2000-01-03, Close, 26.27
2000-01-04, Close, 23.95
2000-01-05, Close, 22.68
2000-01-05, 买入, 22.68
2000-01-06, 已买入, 22.27
2000-01-06, Close, 21.35
2000-01-07, Close, 22.99
2000-01-10, Close, 25.74
2000-01-11, Close, 24.99
2000-01-12, Close, 23.49
2000-01-13, Close, 23.36
2000-01-13, 卖出, 23.36
2000-01-14, 已卖出, 24.24
2000-01-14, Close, 23.75
2000-01-18, Close, 24.74
2000-01-19, Close, 25.41
2000-01-20, Close, 26.35
2000-01-21, Close, 26.55
2000-01-24, Close, 24.10
2000-01-25, Close, 25.10
2000-01-26, Close, 24.49
2000-01-27, Close, 23.04
2000-01-27, 买入, 23.04
2000-01-28, 已买入, 22.90
2000-01-28, Close, 21.07
2000-01-31, Close, 22.22
2000-02-01, Close, 24.02
2000-02-02, Close, 24.16
2000-02-03, Close, 25.21
2000-02-04, Close, 25.71
2000-02-04, 卖出, 25.71
2000-02-07, 已卖出, 26.38
2000-02-07, Close, 26.66
2000-02-08, Close, 26.49
2000-02-09, Close, 26.66
2000-02-10, Close, 27.71
2000-02-11, Close, 26.55
2000-02-14, Close, 27.66
2000-02-15, Close, 27.30
2000-02-16, Close, 27.24
2000-02-16, 买入, 27.24
2000-02-17, 已买入, 27.46
2000-02-17, Close, 27.41
2000-02-18, Close, 26.05
2000-02-22, Close, 26.38
2000-02-23, Close, 28.05
2000-02-24, Close, 27.55
2000-02-25, Close, 31.41
2000-02-25, 卖出, 31.41
2000-02-28, 已卖出, 31.69
2000-02-28, Close, 30.52
2000-02-29, Close, 33.02
2000-03-01, Close, 31.80
2000-03-02, Close, 30.47
2000-03-02, 买入, 30.47
2000-03-03, 已买入, 31.63
2000-03-03, Close, 33.36
2000-03-06, Close, 33.69
2000-03-07, Close, 33.33
2000-03-08, Close, 36.97
2000-03-09, Close, 37.36
2000-03-10, Close, 36.30
2000-03-10, 卖出, 36.30
2000-03-13, 已卖出, 34.91
2000-03-13, Close, 35.02
2000-03-13, 买入, 35.02
2000-03-14, 已买入, 36.41
2000-03-14, Close, 34.25
2000-03-15, Close, 34.97
2000-03-16, Close, 36.44
2000-03-17, Close, 35.50
2000-03-20, Close, 34.75
2000-03-21, Close, 35.89
2000-03-21, 卖出, 35.89
2000-03-22, 已卖出, 36.02
2000-03-22, Close, 37.39
2000-03-23, Close, 38.64
2000-03-24, Close, 38.69
2000-03-27, Close, 39.33
2000-03-28, Close, 38.50
2000-03-29, Close, 36.69
2000-03-29, 买入, 36.69
2000-03-30, 已买入, 34.91
2000-03-30, Close, 34.88
2000-03-31, Close, 34.72
2000-04-03, Close, 34.19
2000-04-04, Close, 33.77
2000-04-05, Close, 34.80
2000-04-06, Close, 36.55
2000-04-06, 卖出, 36.55
2000-04-07, 已卖出, 37.22
2000-04-07, Close, 38.75
2000-04-10, Close, 36.69
2000-04-11, Close, 34.41
2000-04-11, 买入, 34.41
2000-04-12, 已买入, 34.66
2000-04-12, Close, 32.52
2000-04-13, Close, 31.99
2000-04-14, Close, 27.80
2000-04-17, Close, 33.27
2000-04-18, Close, 35.11
2000-04-19, Close, 33.16
2000-04-19, 卖出, 33.16
2000-04-20, 已卖出, 32.83
2000-04-20, Close, 31.49
2000-04-20, 买入, 31.49
2000-04-24, 已买入, 29.96
2000-04-24, Close, 32.22
2000-04-25, Close, 33.61
2000-04-26, Close, 32.11
2000-04-27, Close, 34.38
2000-04-28, Close, 35.55
2000-05-01, Close, 35.44
2000-05-01, 卖出, 35.44
2000-05-02, 已卖出, 35.11
2000-05-02, Close, 34.61
2000-05-02, 买入, 34.61
2000-05-03, 已买入, 34.19
2000-05-03, Close, 33.72
2000-05-04, Close, 33.02
2000-05-05, Close, 34.16
2000-05-08, Close, 32.16
2000-05-09, Close, 32.02
2000-05-10, Close, 30.08
2000-05-10, 卖出, 30.08
2000-05-11, 已卖出, 30.66
2000-05-11, Close, 32.19
2000-05-12, Close, 32.99
2000-05-15, Close, 34.25
2000-05-16, Close, 35.22
2000-05-17, Close, 34.77
2000-05-18, Close, 32.49
2000-05-18, 买入, 32.49
2000-05-19, 已买入, 32.02
2000-05-19, Close, 31.16
2000-05-22, Close, 30.16
2000-05-23, Close, 27.85
2000-05-24, Close, 28.57
2000-05-25, Close, 29.55
2000-05-26, Close, 29.80
2000-05-26, 卖出, 29.80
2000-05-30, 已卖出, 30.63
2000-05-30, Close, 32.99
2000-05-31, Close, 31.97
2000-06-01, Close, 34.63
2000-06-02, Close, 35.66
2000-06-05, Close, 36.00
2000-06-06, Close, 34.27
2000-06-07, Close, 35.58
2000-06-08, Close, 36.64
2000-06-09, Close, 36.77
2000-06-12, Close, 35.83
2000-06-13, Close, 36.33
2000-06-14, Close, 35.13
2000-06-15, Close, 36.69
2000-06-16, Close, 36.41
2000-06-19, Close, 38.25
2000-06-20, Close, 38.27
2000-06-21, Close, 38.33
2000-06-22, Close, 36.25
2000-06-23, Close, 35.36
2000-06-23, 买入, 35.36
2000-06-26, 已买入, 35.69
2000-06-26, Close, 36.77
2000-06-27, Close, 36.58
2000-06-28, Close, 36.89
2000-06-29, Close, 35.97
2000-06-30, Close, 37.39
2000-07-03, Close, 35.66
2000-07-03, 卖出, 35.66
2000-07-05, 已卖出, 34.16
2000-07-05, Close, 32.16
2000-07-05, 买入, 32.16
2000-07-06, 已买入, 31.91
2000-07-06, Close, 33.63
2000-07-07, Close, 33.75
2000-07-10, Close, 32.97
2000-07-11, Close, 32.16
2000-07-12, Close, 33.22
2000-07-13, Close, 33.69
2000-07-13, 卖出, 33.69
2000-07-14, 已卖出, 33.88
2000-07-14, Close, 33.86
2000-07-17, Close, 33.86
2000-07-18, Close, 32.99
2000-07-19, Close, 32.80
2000-07-19, 买入, 32.80
2000-07-20, 已买入, 33.27
2000-07-20, Close, 34.75
2000-07-21, Close, 33.55
2000-07-24, Close, 33.36
2000-07-25, Close, 33.80
2000-07-26, Close, 34.13
2000-07-27, Close, 33.38
2000-07-27, 卖出, 33.38
2000-07-28, 已卖出, 33.41
2000-07-28, Close, 32.19
2000-07-28, 买入, 32.19
2000-07-31, 已买入, 31.91
2000-07-31, Close, 33.44
2000-08-01, Close, 32.52
2000-08-02, Close, 32.52
2000-08-03, Close, 34.44
2000-08-04, Close, 36.27
2000-08-07, Close, 36.41
2000-08-07, 卖出, 36.41
2000-08-08, 已卖出, 36.02
2000-08-08, Close, 36.91
2000-08-09, Close, 36.19
2000-08-10, Close, 35.61
2000-08-10, 买入, 35.61
2000-08-11, 已买入, 35.55
2000-08-11, Close, 36.08
2000-08-14, Close, 36.64
2000-08-15, Close, 36.14
2000-08-16, Close, 36.11
2000-08-17, Close, 37.33
2000-08-18, Close, 36.16
2000-08-18, 卖出, 36.16
2000-08-21, 已卖出, 36.52
2000-08-21, Close, 37.00
2000-08-22, Close, 37.16
2000-08-23, Close, 36.86
2000-08-24, Close, 37.66
2000-08-25, Close, 37.64
2000-08-28, Close, 38.58
2000-08-29, Close, 39.03
2000-08-30, Close, 39.25
2000-08-31, Close, 40.44
2000-09-01, Close, 41.19
2000-09-05, Close, 40.50
2000-09-06, Close, 39.69
2000-09-06, 买入, 39.69
2000-09-07, 已买入, 40.08
2000-09-07, Close, 40.56
2000-09-08, Close, 38.50
2000-09-11, Close, 37.11
2000-09-12, Close, 35.30
2000-09-13, Close, 36.39
2000-09-14, Close, 37.78
2000-09-14, 卖出, 37.78
2000-09-15, 已卖出, 36.08
2000-09-15, Close, 34.83
2000-09-18, Close, 34.01
2000-09-18, 买入, 34.01
2000-09-19, 已买入, 34.44
2000-09-19, Close, 35.27
2000-09-20, Close, 35.55
2000-09-21, Close, 35.11
2000-09-22, Close, 35.91
2000-09-25, Close, 35.02
2000-09-26, Close, 35.33
2000-09-26, 卖出, 35.33
2000-09-27, 已卖出, 35.66
2000-09-27, Close, 35.52
2000-09-28, Close, 36.24
2000-09-29, Close, 35.02
2000-10-02, Close, 35.02
2000-10-03, Close, 30.91
2000-10-04, Close, 30.30
2000-10-04, 买入, 30.30
2000-10-05, 已买入, 30.38
2000-10-05, Close, 30.38
2000-10-06, Close, 30.08
2000-10-09, Close, 29.69
2000-10-10, Close, 28.74
2000-10-11, Close, 27.69
2000-10-12, Close, 28.02
2000-10-12, 卖出, 28.02
2000-10-13, 已卖出, 27.57
2000-10-13, Close, 31.69
2000-10-16, Close, 30.74
2000-10-17, Close, 29.96
2000-10-17, 买入, 29.96
2000-10-18, 已买入, 28.07
2000-10-18, Close, 29.85
2000-10-19, Close, 32.36
2000-10-20, Close, 31.35
2000-10-23, Close, 30.30
2000-10-24, Close, 31.85
2000-10-25, Close, 30.58
2000-10-25, 卖出, 30.58
2000-10-26, 已卖出, 30.91
2000-10-26, Close, 30.30
2000-10-26, 买入, 30.30
2000-10-27, 已买入, 30.69
2000-10-27, Close, 30.41
2000-10-30, Close, 28.13
2000-10-31, Close, 29.35
2000-11-01, Close, 27.91
2000-11-02, Close, 26.30
2000-11-03, Close, 26.96
2000-11-03, 卖出, 26.96
2000-11-06, 已卖出, 27.30
2000-11-06, Close, 24.85
2000-11-07, Close, 23.63
2000-11-07, 买入, 23.63
2000-11-08, 已买入, 24.35
2000-11-08, Close, 22.07
2000-11-09, Close, 24.18
2000-11-10, Close, 22.63
2000-11-13, Close, 22.01
2000-11-14, Close, 25.24
2000-11-15, Close, 25.68
2000-11-15, 卖出, 25.68
2000-11-16, 已卖出, 25.57
2000-11-16, Close, 24.35
2000-11-17, Close, 25.63
2000-11-20, Close, 22.01
2000-11-21, Close, 21.24
2000-11-21, 买入, 21.24
2000-11-22, 已买入, 21.01
2000-11-22, Close, 19.85
2000-11-24, Close, 21.46
2000-11-27, Close, 20.57
2000-11-28, Close, 20.15
2000-11-29, Close, 20.35
2000-11-30, Close, 23.57
2000-11-30, 卖出, 23.57
2000-12-01, 已卖出, 23.46
2000-12-01, Close, 23.52
2000-12-04, Close, 25.07
2000-12-05, Close, 28.02
2000-12-06, Close, 26.85
2000-12-07, Close, 25.18
2000-12-07, 买入, 25.18
2000-12-08, 已买入, 26.74
2000-12-08, Close, 26.74
2000-12-11, Close, 28.41
2000-12-12, Close, 27.35
2000-12-13, Close, 25.24
2000-12-14, Close, 24.46
2000-12-15, Close, 25.41
2000-12-15, 卖出, 25.41
2000-12-18, 已卖出, 26.68
2000-12-18, Close, 28.46
2000-12-19, Close, 27.24
2000-12-20, Close, 25.35
2000-12-20, 买入, 25.35
2000-12-21, 已买入, 24.74
2000-12-21, Close, 26.24
2000-12-22, Close, 28.35
2000-12-26, Close, 27.52
2000-12-27, Close, 27.30
2000-12-28, Close, 27.63
2000-12-29, Close, 25.85
2000-12-29, 卖出, 25.85
组合期末资金: 100017.52
竟然盈利了,是不是哪里出错了?
这比费用叫做佣金。让我们设定一个常见的费率0.1%,买卖都要收(经纪人就是这么贪)。 一行代码就能搞定 :
cerebro.broker.setcommission(commission=0.001) # 0.001即是0.1%
让我看看加不加手续费,结果到底有什么区别。
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
# 买入价格和手续费
self.buyprice = None
self.buycomm = None
# 订单状态通知,买入卖出都是下单
def notify_order(self, order):
if order.status in [order.Submitted, order.Accepted]:
# broker 提交/接受了,买/卖订单则什么都不做
return
# 检查一个订单是否完成
# 注意: 当资金不足时,broker会拒绝订单
if order.status in [order.Completed]:
if order.isbuy():
self.log(
'已买入, 价格: %.2f, 费用: %.2f, 佣金 %.2f' %
(order.executed.price,
order.executed.value,
order.executed.comm))
self.buyprice = order.executed.price
self.buycomm = order.executed.comm
elif order.issell():
self.log('已卖出, 价格: %.2f, 费用: %.2f, 佣金 %.2f' %
(order.executed.price,
order.executed.value,
order.executed.comm))
# 记录当前交易数量
self.bar_executed = len(self)
elif order.status in [order.Canceled, order.Margin, order.Rejected]:
self.log('订单取消/保证金不足/拒绝')
# 其他状态记录为:无挂起订单
self.order = None
# 交易状态通知,一买一卖算交易
def notify_trade(self, trade):
if not trade.isclosed:
return
self.log('交易利润, 毛利润 %.2f, 净利润 %.2f' %
(trade.pnl, trade.pnlcomm))
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('买入单, %.2f' % self.dataclose[0])
# 跟踪订单避免重复
self.order = self.buy()
else:
# 如果已经持仓,且当前交易数据量在买入后5个单位后
if len(self) >= (self.bar_executed + 5):
# 全部卖出
self.log('卖出单, %.2f' % self.dataclose[0])
# 跟踪订单避免重复
self.order = self.sell()
#创建Cerebro引擎
cerebro=bt.Cerebro()
#Cerebro引擎在后台创建broker(经纪人),系统默认资金量为10000
#为Cerebro引擎添加策略
cerebro.addstrategy(TestStrategy)
#获取当前运行脚本所在目录
modpath=os.path.dirname(os.path.abspath(sys.argv[0]))
#拼接加载路径
datapath=os.path.join(modpath,'/home/mw/input/backtrader7822/orcl-1995-2014.txt')
#创建交易数据集
data=bt.feeds.YahooFinanceCSVData(
dataname=datapath,
#数据必须大于fromdate
fromdate=datetime.datetime(2000,1,1),
#数据必须小于todate
todate=datetime.datetime(2000,12,31),
reverse=False)
#加载交易数据
cerebro.adddata(data)
#设置投资金额100000.0
cerebro.broker.setcash(100000.0)
#设置佣金为0.001,除以100去掉%号
cerebro.broker.setcommission(commission=0.001)
#引擎运行前打印期出资金
print('组合期初资金:%.2f'%cerebro.broker.getvalue())
cerebro.run()
#引擎运行后打期末资金
print('组合期末资金:%.2f'%cerebro.broker.getvalue())
组合期初资金:100000.00
2000-01-03, Close, 26.27
2000-01-04, Close, 23.95
2000-01-05, Close, 22.68
2000-01-05, 买入单, 22.68
2000-01-06, 已买入, 价格: 22.27, 费用: 22.27, 佣金 0.02
2000-01-06, Close, 21.35
2000-01-07, Close, 22.99
2000-01-10, Close, 25.74
2000-01-11, Close, 24.99
2000-01-12, Close, 23.49
2000-01-13, Close, 23.36
2000-01-13, 卖出单, 23.36
2000-01-14, 已卖出, 价格: 24.24, 费用: 22.27, 佣金 0.02
2000-01-14, 交易利润, 毛利润 1.97, 净利润 1.92
2000-01-14, Close, 23.75
2000-01-18, Close, 24.74
2000-01-19, Close, 25.41
2000-01-20, Close, 26.35
2000-01-21, Close, 26.55
2000-01-24, Close, 24.10
2000-01-25, Close, 25.10
2000-01-26, Close, 24.49
2000-01-27, Close, 23.04
2000-01-27, 买入单, 23.04
2000-01-28, 已买入, 价格: 22.90, 费用: 22.90, 佣金 0.02
2000-01-28, Close, 21.07
2000-01-31, Close, 22.22
2000-02-01, Close, 24.02
2000-02-02, Close, 24.16
2000-02-03, Close, 25.21
2000-02-04, Close, 25.71
2000-02-04, 卖出单, 25.71
2000-02-07, 已卖出, 价格: 26.38, 费用: 22.90, 佣金 0.03
2000-02-07, 交易利润, 毛利润 3.48, 净利润 3.43
2000-02-07, Close, 26.66
2000-02-08, Close, 26.49
2000-02-09, Close, 26.66
2000-02-10, Close, 27.71
2000-02-11, Close, 26.55
2000-02-14, Close, 27.66
2000-02-15, Close, 27.30
2000-02-16, Close, 27.24
2000-02-16, 买入单, 27.24
2000-02-17, 已买入, 价格: 27.46, 费用: 27.46, 佣金 0.03
2000-02-17, Close, 27.41
2000-02-18, Close, 26.05
2000-02-22, Close, 26.38
2000-02-23, Close, 28.05
2000-02-24, Close, 27.55
2000-02-25, Close, 31.41
2000-02-25, 卖出单, 31.41
2000-02-28, 已卖出, 价格: 31.69, 费用: 27.46, 佣金 0.03
2000-02-28, 交易利润, 毛利润 4.23, 净利润 4.17
2000-02-28, Close, 30.52
2000-02-29, Close, 33.02
2000-03-01, Close, 31.80
2000-03-02, Close, 30.47
2000-03-02, 买入单, 30.47
2000-03-03, 已买入, 价格: 31.63, 费用: 31.63, 佣金 0.03
2000-03-03, Close, 33.36
2000-03-06, Close, 33.69
2000-03-07, Close, 33.33
2000-03-08, Close, 36.97
2000-03-09, Close, 37.36
2000-03-10, Close, 36.30
2000-03-10, 卖出单, 36.30
2000-03-13, 已卖出, 价格: 34.91, 费用: 31.63, 佣金 0.03
2000-03-13, 交易利润, 毛利润 3.28, 净利润 3.21
2000-03-13, Close, 35.02
2000-03-13, 买入单, 35.02
2000-03-14, 已买入, 价格: 36.41, 费用: 36.41, 佣金 0.04
2000-03-14, Close, 34.25
2000-03-15, Close, 34.97
2000-03-16, Close, 36.44
2000-03-17, Close, 35.50
2000-03-20, Close, 34.75
2000-03-21, Close, 35.89
2000-03-21, 卖出单, 35.89
2000-03-22, 已卖出, 价格: 36.02, 费用: 36.41, 佣金 0.04
2000-03-22, 交易利润, 毛利润 -0.39, 净利润 -0.46
2000-03-22, Close, 37.39
2000-03-23, Close, 38.64
2000-03-24, Close, 38.69
2000-03-27, Close, 39.33
2000-03-28, Close, 38.50
2000-03-29, Close, 36.69
2000-03-29, 买入单, 36.69
2000-03-30, 已买入, 价格: 34.91, 费用: 34.91, 佣金 0.03
2000-03-30, Close, 34.88
2000-03-31, Close, 34.72
2000-04-03, Close, 34.19
2000-04-04, Close, 33.77
2000-04-05, Close, 34.80
2000-04-06, Close, 36.55
2000-04-06, 卖出单, 36.55
2000-04-07, 已卖出, 价格: 37.22, 费用: 34.91, 佣金 0.04
2000-04-07, 交易利润, 毛利润 2.31, 净利润 2.24
2000-04-07, Close, 38.75
2000-04-10, Close, 36.69
2000-04-11, Close, 34.41
2000-04-11, 买入单, 34.41
2000-04-12, 已买入, 价格: 34.66, 费用: 34.66, 佣金 0.03
2000-04-12, Close, 32.52
2000-04-13, Close, 31.99
2000-04-14, Close, 27.80
2000-04-17, Close, 33.27
2000-04-18, Close, 35.11
2000-04-19, Close, 33.16
2000-04-19, 卖出单, 33.16
2000-04-20, 已卖出, 价格: 32.83, 费用: 34.66, 佣金 0.03
2000-04-20, 交易利润, 毛利润 -1.83, 净利润 -1.90
2000-04-20, Close, 31.49
2000-04-20, 买入单, 31.49
2000-04-24, 已买入, 价格: 29.96, 费用: 29.96, 佣金 0.03
2000-04-24, Close, 32.22
2000-04-25, Close, 33.61
2000-04-26, Close, 32.11
2000-04-27, Close, 34.38
2000-04-28, Close, 35.55
2000-05-01, Close, 35.44
2000-05-01, 卖出单, 35.44
2000-05-02, 已卖出, 价格: 35.11, 费用: 29.96, 佣金 0.04
2000-05-02, 交易利润, 毛利润 5.15, 净利润 5.08
2000-05-02, Close, 34.61
2000-05-02, 买入单, 34.61
2000-05-03, 已买入, 价格: 34.19, 费用: 34.19, 佣金 0.03
2000-05-03, Close, 33.72
2000-05-04, Close, 33.02
2000-05-05, Close, 34.16
2000-05-08, Close, 32.16
2000-05-09, Close, 32.02
2000-05-10, Close, 30.08
2000-05-10, 卖出单, 30.08
2000-05-11, 已卖出, 价格: 30.66, 费用: 34.19, 佣金 0.03
2000-05-11, 交易利润, 毛利润 -3.53, 净利润 -3.59
2000-05-11, Close, 32.19
2000-05-12, Close, 32.99
2000-05-15, Close, 34.25
2000-05-16, Close, 35.22
2000-05-17, Close, 34.77
2000-05-18, Close, 32.49
2000-05-18, 买入单, 32.49
2000-05-19, 已买入, 价格: 32.02, 费用: 32.02, 佣金 0.03
2000-05-19, Close, 31.16
2000-05-22, Close, 30.16
2000-05-23, Close, 27.85
2000-05-24, Close, 28.57
2000-05-25, Close, 29.55
2000-05-26, Close, 29.80
2000-05-26, 卖出单, 29.80
2000-05-30, 已卖出, 价格: 30.63, 费用: 32.02, 佣金 0.03
2000-05-30, 交易利润, 毛利润 -1.39, 净利润 -1.45
2000-05-30, Close, 32.99
2000-05-31, Close, 31.97
2000-06-01, Close, 34.63
2000-06-02, Close, 35.66
2000-06-05, Close, 36.00
2000-06-06, Close, 34.27
2000-06-07, Close, 35.58
2000-06-08, Close, 36.64
2000-06-09, Close, 36.77
2000-06-12, Close, 35.83
2000-06-13, Close, 36.33
2000-06-14, Close, 35.13
2000-06-15, Close, 36.69
2000-06-16, Close, 36.41
2000-06-19, Close, 38.25
2000-06-20, Close, 38.27
2000-06-21, Close, 38.33
2000-06-22, Close, 36.25
2000-06-23, Close, 35.36
2000-06-23, 买入单, 35.36
2000-06-26, 已买入, 价格: 35.69, 费用: 35.69, 佣金 0.04
2000-06-26, Close, 36.77
2000-06-27, Close, 36.58
2000-06-28, Close, 36.89
2000-06-29, Close, 35.97
2000-06-30, Close, 37.39
2000-07-03, Close, 35.66
2000-07-03, 卖出单, 35.66
2000-07-05, 已卖出, 价格: 34.16, 费用: 35.69, 佣金 0.03
2000-07-05, 交易利润, 毛利润 -1.53, 净利润 -1.60
2000-07-05, Close, 32.16
2000-07-05, 买入单, 32.16
2000-07-06, 已买入, 价格: 31.91, 费用: 31.91, 佣金 0.03
2000-07-06, Close, 33.63
2000-07-07, Close, 33.75
2000-07-10, Close, 32.97
2000-07-11, Close, 32.16
2000-07-12, Close, 33.22
2000-07-13, Close, 33.69
2000-07-13, 卖出单, 33.69
2000-07-14, 已卖出, 价格: 33.88, 费用: 31.91, 佣金 0.03
2000-07-14, 交易利润, 毛利润 1.97, 净利润 1.90
2000-07-14, Close, 33.86
2000-07-17, Close, 33.86
2000-07-18, Close, 32.99
2000-07-19, Close, 32.80
2000-07-19, 买入单, 32.80
2000-07-20, 已买入, 价格: 33.27, 费用: 33.27, 佣金 0.03
2000-07-20, Close, 34.75
2000-07-21, Close, 33.55
2000-07-24, Close, 33.36
2000-07-25, Close, 33.80
2000-07-26, Close, 34.13
2000-07-27, Close, 33.38
2000-07-27, 卖出单, 33.38
2000-07-28, 已卖出, 价格: 33.41, 费用: 33.27, 佣金 0.03
2000-07-28, 交易利润, 毛利润 0.14, 净利润 0.07
2000-07-28, Close, 32.19
2000-07-28, 买入单, 32.19
2000-07-31, 已买入, 价格: 31.91, 费用: 31.91, 佣金 0.03
2000-07-31, Close, 33.44
2000-08-01, Close, 32.52
2000-08-02, Close, 32.52
2000-08-03, Close, 34.44
2000-08-04, Close, 36.27
2000-08-07, Close, 36.41
2000-08-07, 卖出单, 36.41
2000-08-08, 已卖出, 价格: 36.02, 费用: 31.91, 佣金 0.04
2000-08-08, 交易利润, 毛利润 4.11, 净利润 4.04
2000-08-08, Close, 36.91
2000-08-09, Close, 36.19
2000-08-10, Close, 35.61
2000-08-10, 买入单, 35.61
2000-08-11, 已买入, 价格: 35.55, 费用: 35.55, 佣金 0.04
2000-08-11, Close, 36.08
2000-08-14, Close, 36.64
2000-08-15, Close, 36.14
2000-08-16, Close, 36.11
2000-08-17, Close, 37.33
2000-08-18, Close, 36.16
2000-08-18, 卖出单, 36.16
2000-08-21, 已卖出, 价格: 36.52, 费用: 35.55, 佣金 0.04
2000-08-21, 交易利润, 毛利润 0.97, 净利润 0.90
2000-08-21, Close, 37.00
2000-08-22, Close, 37.16
2000-08-23, Close, 36.86
2000-08-24, Close, 37.66
2000-08-25, Close, 37.64
2000-08-28, Close, 38.58
2000-08-29, Close, 39.03
2000-08-30, Close, 39.25
2000-08-31, Close, 40.44
2000-09-01, Close, 41.19
2000-09-05, Close, 40.50
2000-09-06, Close, 39.69
2000-09-06, 买入单, 39.69
2000-09-07, 已买入, 价格: 40.08, 费用: 40.08, 佣金 0.04
2000-09-07, Close, 40.56
2000-09-08, Close, 38.50
2000-09-11, Close, 37.11
2000-09-12, Close, 35.30
2000-09-13, Close, 36.39
2000-09-14, Close, 37.78
2000-09-14, 卖出单, 37.78
2000-09-15, 已卖出, 价格: 36.08, 费用: 40.08, 佣金 0.04
2000-09-15, 交易利润, 毛利润 -4.00, 净利润 -4.08
2000-09-15, Close, 34.83
2000-09-18, Close, 34.01
2000-09-18, 买入单, 34.01
2000-09-19, 已买入, 价格: 34.44, 费用: 34.44, 佣金 0.03
2000-09-19, Close, 35.27
2000-09-20, Close, 35.55
2000-09-21, Close, 35.11
2000-09-22, Close, 35.91
2000-09-25, Close, 35.02
2000-09-26, Close, 35.33
2000-09-26, 卖出单, 35.33
2000-09-27, 已卖出, 价格: 35.66, 费用: 34.44, 佣金 0.04
2000-09-27, 交易利润, 毛利润 1.22, 净利润 1.15
2000-09-27, Close, 35.52
2000-09-28, Close, 36.24
2000-09-29, Close, 35.02
2000-10-02, Close, 35.02
2000-10-03, Close, 30.91
2000-10-04, Close, 30.30
2000-10-04, 买入单, 30.30
2000-10-05, 已买入, 价格: 30.38, 费用: 30.38, 佣金 0.03
2000-10-05, Close, 30.38
2000-10-06, Close, 30.08
2000-10-09, Close, 29.69
2000-10-10, Close, 28.74
2000-10-11, Close, 27.69
2000-10-12, Close, 28.02
2000-10-12, 卖出单, 28.02
2000-10-13, 已卖出, 价格: 27.57, 费用: 30.38, 佣金 0.03
2000-10-13, 交易利润, 毛利润 -2.81, 净利润 -2.87
2000-10-13, Close, 31.69
2000-10-16, Close, 30.74
2000-10-17, Close, 29.96
2000-10-17, 买入单, 29.96
2000-10-18, 已买入, 价格: 28.07, 费用: 28.07, 佣金 0.03
2000-10-18, Close, 29.85
2000-10-19, Close, 32.36
2000-10-20, Close, 31.35
2000-10-23, Close, 30.30
2000-10-24, Close, 31.85
2000-10-25, Close, 30.58
2000-10-25, 卖出单, 30.58
2000-10-26, 已卖出, 价格: 30.91, 费用: 28.07, 佣金 0.03
2000-10-26, 交易利润, 毛利润 2.84, 净利润 2.78
2000-10-26, Close, 30.30
2000-10-26, 买入单, 30.30
2000-10-27, 已买入, 价格: 30.69, 费用: 30.69, 佣金 0.03
2000-10-27, Close, 30.41
2000-10-30, Close, 28.13
2000-10-31, Close, 29.35
2000-11-01, Close, 27.91
2000-11-02, Close, 26.30
2000-11-03, Close, 26.96
2000-11-03, 卖出单, 26.96
2000-11-06, 已卖出, 价格: 27.30, 费用: 30.69, 佣金 0.03
2000-11-06, 交易利润, 毛利润 -3.39, 净利润 -3.45
2000-11-06, Close, 24.85
2000-11-07, Close, 23.63
2000-11-07, 买入单, 23.63
2000-11-08, 已买入, 价格: 24.35, 费用: 24.35, 佣金 0.02
2000-11-08, Close, 22.07
2000-11-09, Close, 24.18
2000-11-10, Close, 22.63
2000-11-13, Close, 22.01
2000-11-14, Close, 25.24
2000-11-15, Close, 25.68
2000-11-15, 卖出单, 25.68
2000-11-16, 已卖出, 价格: 25.57, 费用: 24.35, 佣金 0.03
2000-11-16, 交易利润, 毛利润 1.22, 净利润 1.17
2000-11-16, Close, 24.35
2000-11-17, Close, 25.63
2000-11-20, Close, 22.01
2000-11-21, Close, 21.24
2000-11-21, 买入单, 21.24
2000-11-22, 已买入, 价格: 21.01, 费用: 21.01, 佣金 0.02
2000-11-22, Close, 19.85
2000-11-24, Close, 21.46
2000-11-27, Close, 20.57
2000-11-28, Close, 20.15
2000-11-29, Close, 20.35
2000-11-30, Close, 23.57
2000-11-30, 卖出单, 23.57
2000-12-01, 已卖出, 价格: 23.46, 费用: 21.01, 佣金 0.02
2000-12-01, 交易利润, 毛利润 2.45, 净利润 2.41
2000-12-01, Close, 23.52
2000-12-04, Close, 25.07
2000-12-05, Close, 28.02
2000-12-06, Close, 26.85
2000-12-07, Close, 25.18
2000-12-07, 买入单, 25.18
2000-12-08, 已买入, 价格: 26.74, 费用: 26.74, 佣金 0.03
2000-12-08, Close, 26.74
2000-12-11, Close, 28.41
2000-12-12, Close, 27.35
2000-12-13, Close, 25.24
2000-12-14, Close, 24.46
2000-12-15, Close, 25.41
2000-12-15, 卖出单, 25.41
2000-12-18, 已卖出, 价格: 26.68, 费用: 26.74, 佣金 0.03
2000-12-18, 交易利润, 毛利润 -0.06, 净利润 -0.11
2000-12-18, Close, 28.46
2000-12-19, Close, 27.24
2000-12-20, Close, 25.35
2000-12-20, 买入单, 25.35
2000-12-21, 已买入, 价格: 24.74, 费用: 24.74, 佣金 0.02
2000-12-21, Close, 26.24
2000-12-22, Close, 28.35
2000-12-26, Close, 27.52
2000-12-27, Close, 27.30
2000-12-28, Close, 27.63
2000-12-29, Close, 25.85
2000-12-29, 卖出单, 25.85
组合期末资金:100016.06
在实战中,一般不将参数写死到策略中。Parameters(参数)就是用来处理这个的。 参数的定义像这样 :
# 参数的定义像这样:
params = (('myparam', 27), ('exitbars', 5),)
#这个 tuple 嵌套看着不方便,格式化一下:
params = (
('myparam', 27),
('exitbars', 5),
)
将策略添加到引擎的时候,可以指定刚才定义的参数:
# 指定参数
cerebro.addstrategy(TestStrategy, myparam=20, exitbars=7)
1
注意: 下面的 setsizing 方法已经被弃用。这里还保留是因为还有一些老示例在用。方法已改为下面这种:
cerebro.addsizer(bt.sizers.FixedSize, stake=10) 请参考 sizers 章节。
在策略类中使用买卖数量参数很容易,它们被保存在 “params” 参数里。例如,参数已经传入,在策略类里的 init 方法中这样调用就可以了:
#根据传入的参加设置买卖数量
self.sizer.setsizing(self.params.stake)
也可以直接将买卖数量传入buy和sell方法。 卖出的逻辑改为 :
已经持有,可以卖出了
if len(self) >= (self.bar_executed + self.params.exitbars)
代码修改为:
import datetime #
import os.path # 路径管理
import sys # 获取当前运行脚本的路径 (in argv[0])
#导入backtrader框架
import backtrader as bt
# 创建策略继承bt.Strategy
class TestStrategy(bt.Strategy):
params = (
# 持仓够5个单位就卖出
('exitbars', 5),
)
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
# 买入价格和手续费
self.buyprice = None
self.buycomm = None
# 订单状态通知,买入卖出都是下单
def notify_order(self, order):
if order.status in [order.Submitted, order.Accepted]:
# broker 提交/接受了,买/卖订单则什么都不做
return
# 检查一个订单是否完成
# 注意: 当资金不足时,broker会拒绝订单
if order.status in [order.Completed]:
if order.isbuy():
self.log(
'已买入, 价格: %.2f, 费用: %.2f, 佣金 %.2f' %
(order.executed.price,
order.executed.value,
order.executed.comm))
self.buyprice = order.executed.price
self.buycomm = order.executed.comm
elif order.issell():
self.log('已卖出, 价格: %.2f, 费用: %.2f, 佣金 %.2f' %
(order.executed.price,
order.executed.value,
order.executed.comm))
# 记录当前交易数量
self.bar_executed = len(self)
elif order.status in [order.Canceled, order.Margin, order.Rejected]:
self.log('订单取消/保证金不足/拒绝')
# 其他状态记录为:无挂起订单
self.order = None
# 交易状态通知,一买一卖算交易
def notify_trade(self, trade):
if not trade.isclosed:
return
self.log('交易利润, 毛利润 %.2f, 净利润 %.2f' %
(trade.pnl, trade.pnlcomm))
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('买入单, %.2f' % self.dataclose[0])
# 跟踪订单避免重复
self.order = self.buy()
else:
# 如果已经持仓,且当前交易数据量在买入后5个单位后
# 此处做了更新将5替换为参数
if len(self) >= (self.bar_executed + self.params.exitbars):
# 全部卖出
self.log('卖出单, %.2f' % self.dataclose[0])
# 跟踪订单避免重复
self.order = self.sell()
# 创建Cerebro引擎
cerebro = bt.Cerebro()
# Cerebro引擎在后台创建broker(经纪人),系统默认资金量为10000
# 为Cerebro引擎添加策略
cerebro.addstrategy(TestStrategy)
# 获取当前运行脚本所在目录
modpath = os.path.dirname(os.path.abspath(sys.argv[0]))
# 拼接加载路径
datapath = os.path.join(modpath, '/home/mw/input/backtrader7822/orcl-1995-2014.txt')
# 创建交易数据集
data = bt.feeds.YahooFinanceCSVData(
dataname=datapath,
# 数据必须大于fromdate
fromdate=datetime.datetime(2000, 1, 1),
# 数据必须小于todate
todate=datetime.datetime(2000, 12, 31),
reverse=False)
# 加载交易数据
cerebro.adddata(data)
# 设置投资金额1000000.0
cerebro.broker.setcash(1000000.0)
# 每笔交易使用固定交易量
cerebro.addsizer(bt.sizers.FixedSize, stake=10)
# 设置佣金为0.001,除以100去掉%号
cerebro.broker.setcommission(commission=0.001)
# 引擎运行前打印期出资金
print('组合期初资金: %.2f' % cerebro.broker.getvalue())
cerebro.run()
# 引擎运行后打期末资金
print('组合期末资金: %.2f' % cerebro.broker.getvalue())
组合期初资金: 1000000.00
2000-01-03, Close, 26.27
2000-01-04, Close, 23.95
2000-01-05, Close, 22.68
2000-01-05, 买入单, 22.68
2000-01-06, 已买入, 价格: 22.27, 费用: 222.70, 佣金 0.22
2000-01-06, Close, 21.35
2000-01-07, Close, 22.99
2000-01-10, Close, 25.74
2000-01-11, Close, 24.99
2000-01-12, Close, 23.49
2000-01-13, Close, 23.36
2000-01-13, 卖出单, 23.36
2000-01-14, 已卖出, 价格: 24.24, 费用: 222.70, 佣金 0.24
2000-01-14, 交易利润, 毛利润 19.70, 净利润 19.23
2000-01-14, Close, 23.75
2000-01-18, Close, 24.74
2000-01-19, Close, 25.41
2000-01-20, Close, 26.35
2000-01-21, Close, 26.55
2000-01-24, Close, 24.10
2000-01-25, Close, 25.10
2000-01-26, Close, 24.49
2000-01-27, Close, 23.04
2000-01-27, 买入单, 23.04
2000-01-28, 已买入, 价格: 22.90, 费用: 229.00, 佣金 0.23
2000-01-28, Close, 21.07
2000-01-31, Close, 22.22
2000-02-01, Close, 24.02
2000-02-02, Close, 24.16
2000-02-03, Close, 25.21
2000-02-04, Close, 25.71
2000-02-04, 卖出单, 25.71
2000-02-07, 已卖出, 价格: 26.38, 费用: 229.00, 佣金 0.26
2000-02-07, 交易利润, 毛利润 34.80, 净利润 34.31
2000-02-07, Close, 26.66
2000-02-08, Close, 26.49
2000-02-09, Close, 26.66
2000-02-10, Close, 27.71
2000-02-11, Close, 26.55
2000-02-14, Close, 27.66
2000-02-15, Close, 27.30
2000-02-16, Close, 27.24
2000-02-16, 买入单, 27.24
2000-02-17, 已买入, 价格: 27.46, 费用: 274.60, 佣金 0.27
2000-02-17, Close, 27.41
2000-02-18, Close, 26.05
2000-02-22, Close, 26.38
2000-02-23, Close, 28.05
2000-02-24, Close, 27.55
2000-02-25, Close, 31.41
2000-02-25, 卖出单, 31.41
2000-02-28, 已卖出, 价格: 31.69, 费用: 274.60, 佣金 0.32
2000-02-28, 交易利润, 毛利润 42.30, 净利润 41.71
2000-02-28, Close, 30.52
2000-02-29, Close, 33.02
2000-03-01, Close, 31.80
2000-03-02, Close, 30.47
2000-03-02, 买入单, 30.47
2000-03-03, 已买入, 价格: 31.63, 费用: 316.30, 佣金 0.32
2000-03-03, Close, 33.36
2000-03-06, Close, 33.69
2000-03-07, Close, 33.33
2000-03-08, Close, 36.97
2000-03-09, Close, 37.36
2000-03-10, Close, 36.30
2000-03-10, 卖出单, 36.30
2000-03-13, 已卖出, 价格: 34.91, 费用: 316.30, 佣金 0.35
2000-03-13, 交易利润, 毛利润 32.80, 净利润 32.13
2000-03-13, Close, 35.02
2000-03-13, 买入单, 35.02
2000-03-14, 已买入, 价格: 36.41, 费用: 364.10, 佣金 0.36
2000-03-14, Close, 34.25
2000-03-15, Close, 34.97
2000-03-16, Close, 36.44
2000-03-17, Close, 35.50
2000-03-20, Close, 34.75
2000-03-21, Close, 35.89
2000-03-21, 卖出单, 35.89
2000-03-22, 已卖出, 价格: 36.02, 费用: 364.10, 佣金 0.36
2000-03-22, 交易利润, 毛利润 -3.90, 净利润 -4.62
2000-03-22, Close, 37.39
2000-03-23, Close, 38.64
2000-03-24, Close, 38.69
2000-03-27, Close, 39.33
2000-03-28, Close, 38.50
2000-03-29, Close, 36.69
2000-03-29, 买入单, 36.69
2000-03-30, 已买入, 价格: 34.91, 费用: 349.10, 佣金 0.35
2000-03-30, Close, 34.88
2000-03-31, Close, 34.72
2000-04-03, Close, 34.19
2000-04-04, Close, 33.77
2000-04-05, Close, 34.80
2000-04-06, Close, 36.55
2000-04-06, 卖出单, 36.55
2000-04-07, 已卖出, 价格: 37.22, 费用: 349.10, 佣金 0.37
2000-04-07, 交易利润, 毛利润 23.10, 净利润 22.38
2000-04-07, Close, 38.75
2000-04-10, Close, 36.69
2000-04-11, Close, 34.41
2000-04-11, 买入单, 34.41
2000-04-12, 已买入, 价格: 34.66, 费用: 346.60, 佣金 0.35
2000-04-12, Close, 32.52
2000-04-13, Close, 31.99
2000-04-14, Close, 27.80
2000-04-17, Close, 33.27
2000-04-18, Close, 35.11
2000-04-19, Close, 33.16
2000-04-19, 卖出单, 33.16
2000-04-20, 已卖出, 价格: 32.83, 费用: 346.60, 佣金 0.33
2000-04-20, 交易利润, 毛利润 -18.30, 净利润 -18.97
2000-04-20, Close, 31.49
2000-04-20, 买入单, 31.49
2000-04-24, 已买入, 价格: 29.96, 费用: 299.60, 佣金 0.30
2000-04-24, Close, 32.22
2000-04-25, Close, 33.61
2000-04-26, Close, 32.11
2000-04-27, Close, 34.38
2000-04-28, Close, 35.55
2000-05-01, Close, 35.44
2000-05-01, 卖出单, 35.44
2000-05-02, 已卖出, 价格: 35.11, 费用: 299.60, 佣金 0.35
2000-05-02, 交易利润, 毛利润 51.50, 净利润 50.85
2000-05-02, Close, 34.61
2000-05-02, 买入单, 34.61
2000-05-03, 已买入, 价格: 34.19, 费用: 341.90, 佣金 0.34
2000-05-03, Close, 33.72
2000-05-04, Close, 33.02
2000-05-05, Close, 34.16
2000-05-08, Close, 32.16
2000-05-09, Close, 32.02
2000-05-10, Close, 30.08
2000-05-10, 卖出单, 30.08
2000-05-11, 已卖出, 价格: 30.66, 费用: 341.90, 佣金 0.31
2000-05-11, 交易利润, 毛利润 -35.30, 净利润 -35.95
2000-05-11, Close, 32.19
2000-05-12, Close, 32.99
2000-05-15, Close, 34.25
2000-05-16, Close, 35.22
2000-05-17, Close, 34.77
2000-05-18, Close, 32.49
2000-05-18, 买入单, 32.49
2000-05-19, 已买入, 价格: 32.02, 费用: 320.20, 佣金 0.32
2000-05-19, Close, 31.16
2000-05-22, Close, 30.16
2000-05-23, Close, 27.85
2000-05-24, Close, 28.57
2000-05-25, Close, 29.55
2000-05-26, Close, 29.80
2000-05-26, 卖出单, 29.80
2000-05-30, 已卖出, 价格: 30.63, 费用: 320.20, 佣金 0.31
2000-05-30, 交易利润, 毛利润 -13.90, 净利润 -14.53
2000-05-30, Close, 32.99
2000-05-31, Close, 31.97
2000-06-01, Close, 34.63
2000-06-02, Close, 35.66
2000-06-05, Close, 36.00
2000-06-06, Close, 34.27
2000-06-07, Close, 35.58
2000-06-08, Close, 36.64
2000-06-09, Close, 36.77
2000-06-12, Close, 35.83
2000-06-13, Close, 36.33
2000-06-14, Close, 35.13
2000-06-15, Close, 36.69
2000-06-16, Close, 36.41
2000-06-19, Close, 38.25
2000-06-20, Close, 38.27
2000-06-21, Close, 38.33
2000-06-22, Close, 36.25
2000-06-23, Close, 35.36
2000-06-23, 买入单, 35.36
2000-06-26, 已买入, 价格: 35.69, 费用: 356.90, 佣金 0.36
2000-06-26, Close, 36.77
2000-06-27, Close, 36.58
2000-06-28, Close, 36.89
2000-06-29, Close, 35.97
2000-06-30, Close, 37.39
2000-07-03, Close, 35.66
2000-07-03, 卖出单, 35.66
2000-07-05, 已卖出, 价格: 34.16, 费用: 356.90, 佣金 0.34
2000-07-05, 交易利润, 毛利润 -15.30, 净利润 -16.00
2000-07-05, Close, 32.16
2000-07-05, 买入单, 32.16
2000-07-06, 已买入, 价格: 31.91, 费用: 319.10, 佣金 0.32
2000-07-06, Close, 33.63
2000-07-07, Close, 33.75
2000-07-10, Close, 32.97
2000-07-11, Close, 32.16
2000-07-12, Close, 33.22
2000-07-13, Close, 33.69
2000-07-13, 卖出单, 33.69
2000-07-14, 已卖出, 价格: 33.88, 费用: 319.10, 佣金 0.34
2000-07-14, 交易利润, 毛利润 19.70, 净利润 19.04
2000-07-14, Close, 33.86
2000-07-17, Close, 33.86
2000-07-18, Close, 32.99
2000-07-19, Close, 32.80
2000-07-19, 买入单, 32.80
2000-07-20, 已买入, 价格: 33.27, 费用: 332.70, 佣金 0.33
2000-07-20, Close, 34.75
2000-07-21, Close, 33.55
2000-07-24, Close, 33.36
2000-07-25, Close, 33.80
2000-07-26, Close, 34.13
2000-07-27, Close, 33.38
2000-07-27, 卖出单, 33.38
2000-07-28, 已卖出, 价格: 33.41, 费用: 332.70, 佣金 0.33
2000-07-28, 交易利润, 毛利润 1.40, 净利润 0.73
2000-07-28, Close, 32.19
2000-07-28, 买入单, 32.19
2000-07-31, 已买入, 价格: 31.91, 费用: 319.10, 佣金 0.32
2000-07-31, Close, 33.44
2000-08-01, Close, 32.52
2000-08-02, Close, 32.52
2000-08-03, Close, 34.44
2000-08-04, Close, 36.27
2000-08-07, Close, 36.41
2000-08-07, 卖出单, 36.41
2000-08-08, 已卖出, 价格: 36.02, 费用: 319.10, 佣金 0.36
2000-08-08, 交易利润, 毛利润 41.10, 净利润 40.42
2000-08-08, Close, 36.91
2000-08-09, Close, 36.19
2000-08-10, Close, 35.61
2000-08-10, 买入单, 35.61
2000-08-11, 已买入, 价格: 35.55, 费用: 355.50, 佣金 0.36
2000-08-11, Close, 36.08
2000-08-14, Close, 36.64
2000-08-15, Close, 36.14
2000-08-16, Close, 36.11
2000-08-17, Close, 37.33
2000-08-18, Close, 36.16
2000-08-18, 卖出单, 36.16
2000-08-21, 已卖出, 价格: 36.52, 费用: 355.50, 佣金 0.37
2000-08-21, 交易利润, 毛利润 9.70, 净利润 8.98
2000-08-21, Close, 37.00
2000-08-22, Close, 37.16
2000-08-23, Close, 36.86
2000-08-24, Close, 37.66
2000-08-25, Close, 37.64
2000-08-28, Close, 38.58
2000-08-29, Close, 39.03
2000-08-30, Close, 39.25
2000-08-31, Close, 40.44
2000-09-01, Close, 41.19
2000-09-05, Close, 40.50
2000-09-06, Close, 39.69
2000-09-06, 买入单, 39.69
2000-09-07, 已买入, 价格: 40.08, 费用: 400.80, 佣金 0.40
2000-09-07, Close, 40.56
2000-09-08, Close, 38.50
2000-09-11, Close, 37.11
2000-09-12, Close, 35.30
2000-09-13, Close, 36.39
2000-09-14, Close, 37.78
2000-09-14, 卖出单, 37.78
2000-09-15, 已卖出, 价格: 36.08, 费用: 400.80, 佣金 0.36
2000-09-15, 交易利润, 毛利润 -40.00, 净利润 -40.76
2000-09-15, Close, 34.83
2000-09-18, Close, 34.01
2000-09-18, 买入单, 34.01
2000-09-19, 已买入, 价格: 34.44, 费用: 344.40, 佣金 0.34
2000-09-19, Close, 35.27
2000-09-20, Close, 35.55
2000-09-21, Close, 35.11
2000-09-22, Close, 35.91
2000-09-25, Close, 35.02
2000-09-26, Close, 35.33
2000-09-26, 卖出单, 35.33
2000-09-27, 已卖出, 价格: 35.66, 费用: 344.40, 佣金 0.36
2000-09-27, 交易利润, 毛利润 12.20, 净利润 11.50
2000-09-27, Close, 35.52
2000-09-28, Close, 36.24
2000-09-29, Close, 35.02
2000-10-02, Close, 35.02
2000-10-03, Close, 30.91
2000-10-04, Close, 30.30
2000-10-04, 买入单, 30.30
2000-10-05, 已买入, 价格: 30.38, 费用: 303.80, 佣金 0.30
2000-10-05, Close, 30.38
2000-10-06, Close, 30.08
2000-10-09, Close, 29.69
2000-10-10, Close, 28.74
2000-10-11, Close, 27.69
2000-10-12, Close, 28.02
2000-10-12, 卖出单, 28.02
2000-10-13, 已卖出, 价格: 27.57, 费用: 303.80, 佣金 0.28
2000-10-13, 交易利润, 毛利润 -28.10, 净利润 -28.68
2000-10-13, Close, 31.69
2000-10-16, Close, 30.74
2000-10-17, Close, 29.96
2000-10-17, 买入单, 29.96
2000-10-18, 已买入, 价格: 28.07, 费用: 280.70, 佣金 0.28
2000-10-18, Close, 29.85
2000-10-19, Close, 32.36
2000-10-20, Close, 31.35
2000-10-23, Close, 30.30
2000-10-24, Close, 31.85
2000-10-25, Close, 30.58
2000-10-25, 卖出单, 30.58
2000-10-26, 已卖出, 价格: 30.91, 费用: 280.70, 佣金 0.31
2000-10-26, 交易利润, 毛利润 28.40, 净利润 27.81
2000-10-26, Close, 30.30
2000-10-26, 买入单, 30.30
2000-10-27, 已买入, 价格: 30.69, 费用: 306.90, 佣金 0.31
2000-10-27, Close, 30.41
2000-10-30, Close, 28.13
2000-10-31, Close, 29.35
2000-11-01, Close, 27.91
2000-11-02, Close, 26.30
2000-11-03, Close, 26.96
2000-11-03, 卖出单, 26.96
2000-11-06, 已卖出, 价格: 27.30, 费用: 306.90, 佣金 0.27
2000-11-06, 交易利润, 毛利润 -33.90, 净利润 -34.48
2000-11-06, Close, 24.85
2000-11-07, Close, 23.63
2000-11-07, 买入单, 23.63
2000-11-08, 已买入, 价格: 24.35, 费用: 243.50, 佣金 0.24
2000-11-08, Close, 22.07
2000-11-09, Close, 24.18
2000-11-10, Close, 22.63
2000-11-13, Close, 22.01
2000-11-14, Close, 25.24
2000-11-15, Close, 25.68
2000-11-15, 卖出单, 25.68
2000-11-16, 已卖出, 价格: 25.57, 费用: 243.50, 佣金 0.26
2000-11-16, 交易利润, 毛利润 12.20, 净利润 11.70
2000-11-16, Close, 24.35
2000-11-17, Close, 25.63
2000-11-20, Close, 22.01
2000-11-21, Close, 21.24
2000-11-21, 买入单, 21.24
2000-11-22, 已买入, 价格: 21.01, 费用: 210.10, 佣金 0.21
2000-11-22, Close, 19.85
2000-11-24, Close, 21.46
2000-11-27, Close, 20.57
2000-11-28, Close, 20.15
2000-11-29, Close, 20.35
2000-11-30, Close, 23.57
2000-11-30, 卖出单, 23.57
2000-12-01, 已卖出, 价格: 23.46, 费用: 210.10, 佣金 0.23
2000-12-01, 交易利润, 毛利润 24.50, 净利润 24.06
2000-12-01, Close, 23.52
2000-12-04, Close, 25.07
2000-12-05, Close, 28.02
2000-12-06, Close, 26.85
2000-12-07, Close, 25.18
2000-12-07, 买入单, 25.18
2000-12-08, 已买入, 价格: 26.74, 费用: 267.40, 佣金 0.27
2000-12-08, Close, 26.74
2000-12-11, Close, 28.41
2000-12-12, Close, 27.35
2000-12-13, Close, 25.24
2000-12-14, Close, 24.46
2000-12-15, Close, 25.41
2000-12-15, 卖出单, 25.41
2000-12-18, 已卖出, 价格: 26.68, 费用: 267.40, 佣金 0.27
2000-12-18, 交易利润, 毛利润 -0.60, 净利润 -1.13
2000-12-18, Close, 28.46
2000-12-19, Close, 27.24
2000-12-20, Close, 25.35
2000-12-20, 买入单, 25.35
2000-12-21, 已买入, 价格: 24.74, 费用: 247.40, 佣金 0.25
2000-12-21, Close, 26.24
2000-12-22, Close, 28.35
2000-12-26, Close, 27.52
2000-12-27, Close, 27.30
2000-12-28, Close, 27.63
2000-12-29, Close, 25.85
2000-12-29, 卖出单, 25.85
组合期末资金: 1000160.58
为了显示改变已生效,输出中显示了买卖数量。 买卖数量改为了原来的 10倍,盈亏也变为了原来的10倍。
之前我提到过indicators(技术指标),下一步就该添加他们了,要做的肯定比“三连跌”这种复杂点。借用PyAlgoTrade这个框架的一个使用移动 平均线的例子:
大多数代码不用改变,在 init 方法中加入移动平均线的实例化:
sma = bt.indicators.MovingAverageSimple(datas[0], period=params.maperiod)
当然买入卖出的逻辑依赖平均价,具体代码如下。
注意: 起始金额为1000元,无手续费,这和 PyAlgoTrade 保持一致。
import datetime #
import os.path # 路径管理
import sys # 获取当前运行脚本的路径 (in argv[0])
#导入backtrader框架
import backtrader as bt
# 创建策略继承bt.Strategy
class TestStrategy(bt.Strategy):
params = (
# 均线参数设置15天,15日均线
('maperiod', 15),
)
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
# 买入价格和手续费
self.buyprice = None
self.buycomm = None
# 加入均线指标
self.sma = bt.indicators.SimpleMovingAverage(self.datas[0], period=self.params.maperiod)
# 订单状态通知,买入卖出都是下单
def notify_order(self, order):
if order.status in [order.Submitted, order.Accepted]:
# broker 提交/接受了,买/卖订单则什么都不做
return
# 检查一个订单是否完成
# 注意: 当资金不足时,broker会拒绝订单
if order.status in [order.Completed]:
if order.isbuy():
self.log(
'已买入, 价格: %.2f, 费用: %.2f, 佣金 %.2f' %
(order.executed.price,
order.executed.value,
order.executed.comm))
self.buyprice = order.executed.price
self.buycomm = order.executed.comm
elif order.issell():
self.log('已卖出, 价格: %.2f, 费用: %.2f, 佣金 %.2f' %
(order.executed.price,
order.executed.value,
order.executed.comm))
# 记录当前交易数量
self.bar_executed = len(self)
elif order.status in [order.Canceled, order.Margin, order.Rejected]:
self.log('订单取消/保证金不足/拒绝')
# 其他状态记录为:无挂起订单
self.order = None
# 交易状态通知,一买一卖算交易
def notify_trade(self, trade):
if not trade.isclosed:
return
self.log('交易利润, 毛利润 %.2f, 净利润 %.2f' %
(trade.pnl, trade.pnlcomm))
def next(self):
# 记录收盘价
self.log('Close, %.2f' % self.dataclose[0])
# 如果有订单正在挂起,不操作
if self.order:
return
# 如果没有持仓则买入
if not self.position:
# 今天的收盘价在均线价格之上
if self.dataclose[0] > self.sma[0]:
# 买入
self.log('买入单, %.2f' % self.dataclose[0])
# 跟踪订单避免重复
self.order = self.buy()
else:
# 如果已经持仓,收盘价在均线价格之下
if self.dataclose[0] < self.sma[0]:
# 全部卖出
self.log('卖出单, %.2f' % self.dataclose[0])
# 跟踪订单避免重复
self.order = self.sell()
# 创建Cerebro引擎
cerebro = bt.Cerebro()
# Cerebro引擎在后台创建broker(经纪人),系统默认资金量为10000
# 为Cerebro引擎添加策略
cerebro.addstrategy(TestStrategy)
# 获取当前运行脚本所在目录
modpath = os.path.dirname(os.path.abspath(sys.argv[0]))
# 拼接加载路径
datapath = os.path.join(modpath, '/home/mw/input/backtrader7822/orcl-1995-2014.txt')
# 创建交易数据集
data = bt.feeds.YahooFinanceCSVData(
dataname=datapath,
# 数据必须大于fromdate
fromdate=datetime.datetime(2000, 1, 1),
# 数据必须小于todate
todate=datetime.datetime(2000, 12, 31),
reverse=False)
# 加载交易数据
cerebro.adddata(data)
# 设置投资金额1000.0
cerebro.broker.setcash(1000.0)
# 每笔交易使用固定交易量
cerebro.addsizer(bt.sizers.FixedSize, stake=10)
# 设置佣金为0.0
cerebro.broker.setcommission(commission=0.0)
# 引擎运行前打印期出资金
print('组合期初资金: %.2f' % cerebro.broker.getvalue())
cerebro.run()
# 引擎运行后打期末资金
print('组合期末资金: %.2f' % cerebro.broker.getvalue())
组合期初资金: 1000.00
2000-01-24, Close, 24.10
2000-01-25, Close, 25.10
2000-01-25, 买入单, 25.10
2000-01-26, 已买入, 价格: 25.24, 费用: 252.40, 佣金 0.00
2000-01-26, Close, 24.49
2000-01-27, Close, 23.04
2000-01-27, 卖出单, 23.04
2000-01-28, 已卖出, 价格: 22.90, 费用: 252.40, 佣金 0.00
2000-01-28, 交易利润, 毛利润 -23.40, 净利润 -23.40
2000-01-28, Close, 21.07
2000-01-31, Close, 22.22
2000-02-01, Close, 24.02
2000-02-02, Close, 24.16
2000-02-02, 买入单, 24.16
2000-02-03, 已买入, 价格: 24.63, 费用: 246.30, 佣金 0.00
2000-02-03, Close, 25.21
2000-02-04, Close, 25.71
2000-02-07, Close, 26.66
2000-02-08, Close, 26.49
2000-02-09, Close, 26.66
2000-02-10, Close, 27.71
2000-02-11, Close, 26.55
2000-02-14, Close, 27.66
2000-02-15, Close, 27.30
2000-02-16, Close, 27.24
2000-02-17, Close, 27.41
2000-02-18, Close, 26.05
2000-02-18, 卖出单, 26.05
2000-02-22, 已卖出, 价格: 26.30, 费用: 246.30, 佣金 0.00
2000-02-22, 交易利润, 毛利润 16.70, 净利润 16.70
2000-02-22, Close, 26.38
2000-02-22, 买入单, 26.38
2000-02-23, 已买入, 价格: 26.77, 费用: 267.70, 佣金 0.00
2000-02-23, Close, 28.05
2000-02-24, Close, 27.55
2000-02-25, Close, 31.41
2000-02-28, Close, 30.52
2000-02-29, Close, 33.02
2000-03-01, Close, 31.80
2000-03-02, Close, 30.47
2000-03-03, Close, 33.36
2000-03-06, Close, 33.69
2000-03-07, Close, 33.33
2000-03-08, Close, 36.97
2000-03-09, Close, 37.36
2000-03-10, Close, 36.30
2000-03-13, Close, 35.02
2000-03-14, Close, 34.25
2000-03-15, Close, 34.97
2000-03-16, Close, 36.44
2000-03-17, Close, 35.50
2000-03-20, Close, 34.75
2000-03-21, Close, 35.89
2000-03-22, Close, 37.39
2000-03-23, Close, 38.64
2000-03-24, Close, 38.69
2000-03-27, Close, 39.33
2000-03-28, Close, 38.50
2000-03-29, Close, 36.69
2000-03-30, Close, 34.88
2000-03-30, 卖出单, 34.88
2000-03-31, 已卖出, 价格: 35.66, 费用: 267.70, 佣金 0.00
2000-03-31, 交易利润, 毛利润 88.90, 净利润 88.90
2000-03-31, Close, 34.72
2000-04-03, Close, 34.19
2000-04-04, Close, 33.77
2000-04-05, Close, 34.80
2000-04-06, Close, 36.55
2000-04-06, 买入单, 36.55
2000-04-07, 已买入, 价格: 37.22, 费用: 372.20, 佣金 0.00
2000-04-07, Close, 38.75
2000-04-10, Close, 36.69
2000-04-11, Close, 34.41
2000-04-11, 卖出单, 34.41
2000-04-12, 已卖出, 价格: 34.66, 费用: 372.20, 佣金 0.00
2000-04-12, 交易利润, 毛利润 -25.60, 净利润 -25.60
2000-04-12, Close, 32.52
2000-04-13, Close, 31.99
2000-04-14, Close, 27.80
2000-04-17, Close, 33.27
2000-04-18, Close, 35.11
2000-04-18, 买入单, 35.11
2000-04-19, 已买入, 价格: 34.97, 费用: 349.70, 佣金 0.00
2000-04-19, Close, 33.16
2000-04-19, 卖出单, 33.16
2000-04-20, 已卖出, 价格: 32.83, 费用: 349.70, 佣金 0.00
2000-04-20, 交易利润, 毛利润 -21.40, 净利润 -21.40
2000-04-20, Close, 31.49
2000-04-24, Close, 32.22
2000-04-25, Close, 33.61
2000-04-26, Close, 32.11
2000-04-27, Close, 34.38
2000-04-27, 买入单, 34.38
2000-04-28, 已买入, 价格: 34.91, 费用: 349.10, 佣金 0.00
2000-04-28, Close, 35.55
2000-05-01, Close, 35.44
2000-05-02, Close, 34.61
2000-05-03, Close, 33.72
2000-05-04, Close, 33.02
2000-05-04, 卖出单, 33.02
2000-05-05, 已卖出, 价格: 32.91, 费用: 349.10, 佣金 0.00
2000-05-05, 交易利润, 毛利润 -20.00, 净利润 -20.00
2000-05-05, Close, 34.16
2000-05-05, 买入单, 34.16
2000-05-08, 已买入, 价格: 33.49, 费用: 334.90, 佣金 0.00
2000-05-08, Close, 32.16
2000-05-08, 卖出单, 32.16
2000-05-09, 已卖出, 价格: 32.77, 费用: 334.90, 佣金 0.00
2000-05-09, 交易利润, 毛利润 -7.20, 净利润 -7.20
2000-05-09, Close, 32.02
2000-05-10, Close, 30.08
2000-05-11, Close, 32.19
2000-05-12, Close, 32.99
2000-05-15, Close, 34.25
2000-05-15, 买入单, 34.25
2000-05-16, 已买入, 价格: 34.52, 费用: 345.20, 佣金 0.00
2000-05-16, Close, 35.22
2000-05-17, Close, 34.77
2000-05-18, Close, 32.49
2000-05-18, 卖出单, 32.49
2000-05-19, 已卖出, 价格: 32.02, 费用: 345.20, 佣金 0.00
2000-05-19, 交易利润, 毛利润 -25.00, 净利润 -25.00
2000-05-19, Close, 31.16
2000-05-22, Close, 30.16
2000-05-23, Close, 27.85
2000-05-24, Close, 28.57
2000-05-25, Close, 29.55
2000-05-26, Close, 29.80
2000-05-30, Close, 32.99
2000-05-30, 买入单, 32.99
2000-05-31, 已买入, 价格: 32.58, 费用: 325.80, 佣金 0.00
2000-05-31, Close, 31.97
2000-06-01, Close, 34.63
2000-06-02, Close, 35.66
2000-06-05, Close, 36.00
2000-06-06, Close, 34.27
2000-06-07, Close, 35.58
2000-06-08, Close, 36.64
2000-06-09, Close, 36.77
2000-06-12, Close, 35.83
2000-06-13, Close, 36.33
2000-06-14, Close, 35.13
2000-06-15, Close, 36.69
2000-06-16, Close, 36.41
2000-06-19, Close, 38.25
2000-06-20, Close, 38.27
2000-06-21, Close, 38.33
2000-06-22, Close, 36.25
2000-06-22, 卖出单, 36.25
2000-06-23, 已卖出, 价格: 35.94, 费用: 325.80, 佣金 0.00
2000-06-23, 交易利润, 毛利润 33.60, 净利润 33.60
2000-06-23, Close, 35.36
2000-06-26, Close, 36.77
2000-06-26, 买入单, 36.77
2000-06-27, 已买入, 价格: 36.64, 费用: 366.40, 佣金 0.00
2000-06-27, Close, 36.58
2000-06-27, 卖出单, 36.58
2000-06-28, 已卖出, 价格: 36.50, 费用: 366.40, 佣金 0.00
2000-06-28, 交易利润, 毛利润 -1.40, 净利润 -1.40
2000-06-28, Close, 36.89
2000-06-28, 买入单, 36.89
2000-06-29, 已买入, 价格: 36.50, 费用: 365.00, 佣金 0.00
2000-06-29, Close, 35.97
2000-06-29, 卖出单, 35.97
2000-06-30, 已卖出, 价格: 35.75, 费用: 365.00, 佣金 0.00
2000-06-30, 交易利润, 毛利润 -7.50, 净利润 -7.50
2000-06-30, Close, 37.39
2000-06-30, 买入单, 37.39
2000-07-03, 已买入, 价格: 36.08, 费用: 360.80, 佣金 0.00
2000-07-03, Close, 35.66
2000-07-03, 卖出单, 35.66
2000-07-05, 已卖出, 价格: 34.16, 费用: 360.80, 佣金 0.00
2000-07-05, 交易利润, 毛利润 -19.20, 净利润 -19.20
2000-07-05, Close, 32.16
2000-07-06, Close, 33.63
2000-07-07, Close, 33.75
2000-07-10, Close, 32.97
2000-07-11, Close, 32.16
2000-07-12, Close, 33.22
2000-07-13, Close, 33.69
2000-07-14, Close, 33.86
2000-07-17, Close, 33.86
2000-07-18, Close, 32.99
2000-07-19, Close, 32.80
2000-07-20, Close, 34.75
2000-07-20, 买入单, 34.75
2000-07-21, 已买入, 价格: 34.44, 费用: 344.40, 佣金 0.00
2000-07-21, Close, 33.55
2000-07-21, 卖出单, 33.55
2000-07-24, 已卖出, 价格: 34.30, 费用: 344.40, 佣金 0.00
2000-07-24, 交易利润, 毛利润 -1.40, 净利润 -1.40
2000-07-24, Close, 33.36
2000-07-25, Close, 33.80
2000-07-25, 买入单, 33.80
2000-07-26, 已买入, 价格: 33.27, 费用: 332.70, 佣金 0.00
2000-07-26, Close, 34.13
2000-07-27, Close, 33.38
2000-07-27, 卖出单, 33.38
2000-07-28, 已卖出, 价格: 33.41, 费用: 332.70, 佣金 0.00
2000-07-28, 交易利润, 毛利润 1.40, 净利润 1.40
2000-07-28, Close, 32.19
2000-07-31, Close, 33.44
2000-07-31, 买入单, 33.44
2000-08-01, 已买入, 价格: 33.44, 费用: 334.40, 佣金 0.00
2000-08-01, Close, 32.52
2000-08-01, 卖出单, 32.52
2000-08-02, 已卖出, 价格: 32.47, 费用: 334.40, 佣金 0.00
2000-08-02, 交易利润, 毛利润 -9.70, 净利润 -9.70
2000-08-02, Close, 32.52
2000-08-03, Close, 34.44
2000-08-03, 买入单, 34.44
2000-08-04, 已买入, 价格: 34.83, 费用: 348.30, 佣金 0.00
2000-08-04, Close, 36.27
2000-08-07, Close, 36.41
2000-08-08, Close, 36.91
2000-08-09, Close, 36.19
2000-08-10, Close, 35.61
2000-08-11, Close, 36.08
2000-08-14, Close, 36.64
2000-08-15, Close, 36.14
2000-08-16, Close, 36.11
2000-08-17, Close, 37.33
2000-08-18, Close, 36.16
2000-08-21, Close, 37.00
2000-08-22, Close, 37.16
2000-08-23, Close, 36.86
2000-08-24, Close, 37.66
2000-08-25, Close, 37.64
2000-08-28, Close, 38.58
2000-08-29, Close, 39.03
2000-08-30, Close, 39.25
2000-08-31, Close, 40.44
2000-09-01, Close, 41.19
2000-09-05, Close, 40.50
2000-09-06, Close, 39.69
2000-09-07, Close, 40.56
2000-09-08, Close, 38.50
2000-09-08, 卖出单, 38.50
2000-09-11, 已卖出, 价格: 38.28, 费用: 348.30, 佣金 0.00
2000-09-11, 交易利润, 毛利润 34.50, 净利润 34.50
2000-09-11, Close, 37.11
2000-09-12, Close, 35.30
2000-09-13, Close, 36.39
2000-09-14, Close, 37.78
2000-09-15, Close, 34.83
2000-09-18, Close, 34.01
2000-09-19, Close, 35.27
2000-09-20, Close, 35.55
2000-09-21, Close, 35.11
2000-09-22, Close, 35.91
2000-09-25, Close, 35.02
2000-09-26, Close, 35.33
2000-09-27, Close, 35.52
2000-09-28, Close, 36.24
2000-09-28, 买入单, 36.24
2000-09-29, 已买入, 价格: 36.18, 费用: 361.80, 佣金 0.00
2000-09-29, Close, 35.02
2000-09-29, 卖出单, 35.02
2000-10-02, 已卖出, 价格: 35.47, 费用: 361.80, 佣金 0.00
2000-10-02, 交易利润, 毛利润 -7.10, 净利润 -7.10
2000-10-02, Close, 35.02
2000-10-03, Close, 30.91
2000-10-04, Close, 30.30
2000-10-05, Close, 30.38
2000-10-06, Close, 30.08
2000-10-09, Close, 29.69
2000-10-10, Close, 28.74
2000-10-11, Close, 27.69
2000-10-12, Close, 28.02
2000-10-13, Close, 31.69
2000-10-16, Close, 30.74
2000-10-17, Close, 29.96
2000-10-18, Close, 29.85
2000-10-19, Close, 32.36
2000-10-19, 买入单, 32.36
2000-10-20, 已买入, 价格: 32.13, 费用: 321.30, 佣金 0.00
2000-10-20, Close, 31.35
2000-10-23, Close, 30.30
2000-10-24, Close, 31.85
2000-10-25, Close, 30.58
2000-10-26, Close, 30.30
2000-10-27, Close, 30.41
2000-10-30, Close, 28.13
2000-10-30, 卖出单, 28.13
2000-10-31, 已卖出, 价格: 29.02, 费用: 321.30, 佣金 0.00
2000-10-31, 交易利润, 毛利润 -31.10, 净利润 -31.10
2000-10-31, Close, 29.35
2000-11-01, Close, 27.91
2000-11-02, Close, 26.30
2000-11-03, Close, 26.96
2000-11-06, Close, 24.85
2000-11-07, Close, 23.63
2000-11-08, Close, 22.07
2000-11-09, Close, 24.18
2000-11-10, Close, 22.63
2000-11-13, Close, 22.01
2000-11-14, Close, 25.24
2000-11-15, Close, 25.68
2000-11-16, Close, 24.35
2000-11-17, Close, 25.63
2000-11-17, 买入单, 25.63
2000-11-20, 已买入, 价格: 21.63, 费用: 216.30, 佣金 0.00
2000-11-20, Close, 22.01
2000-11-20, 卖出单, 22.01
2000-11-21, 已卖出, 价格: 22.07, 费用: 216.30, 佣金 0.00
2000-11-21, 交易利润, 毛利润 4.40, 净利润 4.40
2000-11-21, Close, 21.24
2000-11-22, Close, 19.85
2000-11-24, Close, 21.46
2000-11-27, Close, 20.57
2000-11-28, Close, 20.15
2000-11-29, Close, 20.35
2000-11-30, Close, 23.57
2000-11-30, 买入单, 23.57
2000-12-01, 已买入, 价格: 23.46, 费用: 234.60, 佣金 0.00
2000-12-01, Close, 23.52
2000-12-04, Close, 25.07
2000-12-05, Close, 28.02
2000-12-06, Close, 26.85
2000-12-07, Close, 25.18
2000-12-08, Close, 26.74
2000-12-11, Close, 28.41
2000-12-12, Close, 27.35
2000-12-13, Close, 25.24
2000-12-14, Close, 24.46
2000-12-14, 卖出单, 24.46
2000-12-15, 已卖出, 价格: 26.18, 费用: 234.60, 佣金 0.00
2000-12-15, 交易利润, 毛利润 27.20, 净利润 27.20
2000-12-15, Close, 25.41
2000-12-15, 买入单, 25.41
2000-12-18, 已买入, 价格: 26.68, 费用: 266.80, 佣金 0.00
2000-12-18, Close, 28.46
2000-12-19, Close, 27.24
2000-12-20, Close, 25.35
2000-12-20, 卖出单, 25.35
2000-12-21, 已卖出, 价格: 24.74, 费用: 266.80, 佣金 0.00
2000-12-21, 交易利润, 毛利润 -19.40, 净利润 -19.40
2000-12-21, Close, 26.24
2000-12-21, 买入单, 26.24
2000-12-22, 已买入, 价格: 27.02, 费用: 270.20, 佣金 0.00
2000-12-22, Close, 28.35
2000-12-26, Close, 27.52
2000-12-27, Close, 27.30
2000-12-28, Close, 27.63
2000-12-29, Close, 25.85
2000-12-29, 卖出单, 25.85
组合期末资金: 975.60
让我们仔细地看一下出现在下面日志中的第一条记录: 不再是新千年的第一个交易日2000-01-03了。 变成了2000-01-24,怎么回事呢? 是因为,框架根据新代码做出了改变: 在策略中我们加入了移动平均技术指标。 移动平均需要有个均线周期参数,程序根据这个参数回看计算前边的X条价格数据然后进行开仓判断,例子中周期是15。 2000-01-24就是第15天 backtrader 框架假定策略加入这个技术指标是有正当理由的,比如做开平仓的决策。 框架不会在数据没到位的时候就进行下一步。 在技术指标产生第一条数据之后,next方法第一个被调用。 在示例中只有一个技术指标,其实策略支持添加多个技术指标。
一个盈利系统被改变之后开始亏损了,还是在手续费率设置为0的情况下。看来简单添加一个技术指标并不是万能的。
注意: 同样的交易逻辑和数据,和 PyAlgoTrade输出的结果并不完全一致,当然只是轻微不一致。最可疑的原因是因为:小数点 处理 ”调整后价格”(分红、拆股后调整)时,PyAlgoTrade并不对小数点进行四舍五入。 在对价格进行调整后, backtrader的数据引擎将Yahoo价格数据的价格小数点缩减到2位。虽然输出看起来差不多,但积少成多结果就不同了。 将价格小数点缩减到 2位是合理的,一般交易所只允许价格保留小数点后面2位。
从 1.8.11.99 版本开始,backtrader的Yahoo数据引擎可以设置是否做小数点位数保留,还可以设置保留多少位。
文字日志虽然能看到细节,但人们还是喜欢看可视化的东西,所以有必要将结果绘制成图表。 绘图很容易使用,只需添加一行代码 :
cerebro.plot()
这行代码要放在 cerebro.run() 之后。 为方便使用,框架做了下面这些自动化的事情: 将添加第二条指数移动平均线,默认将使用数据进行绘制 (就像第1条)。 将添加第三条加权移动平均线,在单独区域绘制(也许看起来不合理)。 将添加一条 Stochastic(慢),使用默认参数。 将添加一条MACD,使用默认参数。 将添加一条 RSI指标,使用默认参数。 将添加一条 RSI指标的简单移动平均线,使用默认参数(将和RSI一起被绘制)。 将添加一条ATR指标,修改了默认参数以避免被绘制。
上面添加的这些指标,等于在策略类的 init 方法中添加了以下语句:
bt.indicators.ExponentialMovingAverage(self.datas[0], period=25)
bt.indicators.WeightedMovingAverage(self.datas[0], period=25).subplot = True
bt.indicators.StochasticSlow(self.datas[0])
bt.indicators.MACDHisto(self.datas[0])
rsi = bt.indicators.RSI(self.datas[0])
bt.indicators.SmoothedMovingAverage(rsi, period=10)
bt.indicators.ATR(self.datas[0]).plot = False
注意: 即使指标没有被显式地声明为成员变量(如 self.sma = MovingAverageSimple…), 它们还是会被自动注册到策略类中,并影响开始执行 next 的最小周期,而且会被绘制。 在例子中,只有 RSI的指标被赋予了一个rsi的变量,供后边为它创建移动平均线使用。
现在程序变成了这样:
import datetime #
import os.path # 路径管理
import sys # 获取当前运行脚本的路径 (in argv[0])
#导入backtrader框架
import backtrader as bt
# 创建策略继承bt.Strategy
class TestStrategy(bt.Strategy):
params = (
# 均线参数设置15天,15日均线
('maperiod', 15),
)
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
# 买入价格和手续费
self.buyprice = None
self.buycomm = None
# 加入均线指标
self.sma = bt.indicators.SimpleMovingAverage(self.datas[0], period=self.params.maperiod)
# 绘制图形时候用到的指标
bt.indicators.ExponentialMovingAverage(self.datas[0], period=25)
bt.indicators.WeightedMovingAverage(self.datas[0], period=25,subplot=True)
bt.indicators.StochasticSlow(self.datas[0])
bt.indicators.MACDHisto(self.datas[0])
rsi = bt.indicators.RSI(self.datas[0])
bt.indicators.SmoothedMovingAverage(rsi, period=10)
bt.indicators.ATR(self.datas[0], plot=False)
# 订单状态通知,买入卖出都是下单
def notify_order(self, order):
if order.status in [order.Submitted, order.Accepted]:
# broker 提交/接受了,买/卖订单则什么都不做
return
# 检查一个订单是否完成
# 注意: 当资金不足时,broker会拒绝订单
if order.status in [order.Completed]:
if order.isbuy():
self.log(
'已买入, 价格: %.2f, 费用: %.2f, 佣金 %.2f' %
(order.executed.price,
order.executed.value,
order.executed.comm))
self.buyprice = order.executed.price
self.buycomm = order.executed.comm
elif order.issell():
self.log('已卖出, 价格: %.2f, 费用: %.2f, 佣金 %.2f' %
(order.executed.price,
order.executed.value,
order.executed.comm))
# 记录当前交易数量
self.bar_executed = len(self)
elif order.status in [order.Canceled, order.Margin, order.Rejected]:
self.log('订单取消/保证金不足/拒绝')
# 其他状态记录为:无挂起订单
self.order = None
# 交易状态通知,一买一卖算交易
def notify_trade(self, trade):
if not trade.isclosed:
return
self.log('交易利润, 毛利润 %.2f, 净利润 %.2f' %
(trade.pnl, trade.pnlcomm))
def next(self):
# 记录收盘价
self.log('Close, %.2f' % self.dataclose[0])
# 如果有订单正在挂起,不操作
if self.order:
return
# 如果没有持仓则买入
if not self.position:
# 今天的收盘价在均线价格之上
if self.dataclose[0] > self.sma[0]:
# 买入
self.log('买入单, %.2f' % self.dataclose[0])
# 跟踪订单避免重复
self.order = self.buy()
else:
# 如果已经持仓,收盘价在均线价格之下
if self.dataclose[0] < self.sma[0]:
# 全部卖出
self.log('卖出单, %.2f' % self.dataclose[0])
# 跟踪订单避免重复
self.order = self.sell()
# 创建Cerebro引擎
cerebro = bt.Cerebro()
# Cerebro引擎在后台创建broker(经纪人),系统默认资金量为10000
# 为Cerebro引擎添加策略
cerebro.addstrategy(TestStrategy)
# 获取当前运行脚本所在目录
modpath = os.path.dirname(os.path.abspath(sys.argv[0]))
# 拼接加载路径
datapath = os.path.join(modpath, '/home/mw/input/backtrader7822/orcl-1995-2014.txt')
# 创建交易数据集
data = bt.feeds.YahooFinanceCSVData(
dataname=datapath,
# 数据必须大于fromdate
fromdate=datetime.datetime(2000, 1, 1),
# 数据必须小于todate
todate=datetime.datetime(2000, 12, 31),
reverse=False)
# 加载交易数据
cerebro.adddata(data)
# 设置投资金额1000.0
cerebro.broker.setcash(1000.0)
# 每笔交易使用固定交易量
cerebro.addsizer(bt.sizers.FixedSize, stake=10)
# 设置佣金为0.0
cerebro.broker.setcommission(commission=0.0)
# 引擎运行前打印期出资金
print('组合期初资金: %.2f' % cerebro.broker.getvalue())
cerebro.run()
# 引擎运行后打期末资金
print('组合期末资金: %.2f' % cerebro.broker.getvalue())
# 绘制图像
cerebro.plot()
组合期初资金: 1000.00
2000-02-18, Close, 26.05
2000-02-22, Close, 26.38
2000-02-22, 买入单, 26.38
2000-02-23, 已买入, 价格: 26.77, 费用: 267.70, 佣金 0.00
2000-02-23, Close, 28.05
2000-02-24, Close, 27.55
2000-02-25, Close, 31.41
2000-02-28, Close, 30.52
2000-02-29, Close, 33.02
2000-03-01, Close, 31.80
2000-03-02, Close, 30.47
2000-03-03, Close, 33.36
2000-03-06, Close, 33.69
2000-03-07, Close, 33.33
2000-03-08, Close, 36.97
2000-03-09, Close, 37.36
2000-03-10, Close, 36.30
2000-03-13, Close, 35.02
2000-03-14, Close, 34.25
2000-03-15, Close, 34.97
2000-03-16, Close, 36.44
2000-03-17, Close, 35.50
2000-03-20, Close, 34.75
2000-03-21, Close, 35.89
2000-03-22, Close, 37.39
2000-03-23, Close, 38.64
2000-03-24, Close, 38.69
2000-03-27, Close, 39.33
2000-03-28, Close, 38.50
2000-03-29, Close, 36.69
2000-03-30, Close, 34.88
2000-03-30, 卖出单, 34.88
2000-03-31, 已卖出, 价格: 35.66, 费用: 267.70, 佣金 0.00
2000-03-31, 交易利润, 毛利润 88.90, 净利润 88.90
2000-03-31, Close, 34.72
2000-04-03, Close, 34.19
2000-04-04, Close, 33.77
2000-04-05, Close, 34.80
2000-04-06, Close, 36.55
2000-04-06, 买入单, 36.55
2000-04-07, 已买入, 价格: 37.22, 费用: 372.20, 佣金 0.00
2000-04-07, Close, 38.75
2000-04-10, Close, 36.69
2000-04-11, Close, 34.41
2000-04-11, 卖出单, 34.41
2000-04-12, 已卖出, 价格: 34.66, 费用: 372.20, 佣金 0.00
2000-04-12, 交易利润, 毛利润 -25.60, 净利润 -25.60
2000-04-12, Close, 32.52
2000-04-13, Close, 31.99
2000-04-14, Close, 27.80
2000-04-17, Close, 33.27
2000-04-18, Close, 35.11
2000-04-18, 买入单, 35.11
2000-04-19, 已买入, 价格: 34.97, 费用: 349.70, 佣金 0.00
2000-04-19, Close, 33.16
2000-04-19, 卖出单, 33.16
2000-04-20, 已卖出, 价格: 32.83, 费用: 349.70, 佣金 0.00
2000-04-20, 交易利润, 毛利润 -21.40, 净利润 -21.40
2000-04-20, Close, 31.49
2000-04-24, Close, 32.22
2000-04-25, Close, 33.61
2000-04-26, Close, 32.11
2000-04-27, Close, 34.38
2000-04-27, 买入单, 34.38
2000-04-28, 已买入, 价格: 34.91, 费用: 349.10, 佣金 0.00
2000-04-28, Close, 35.55
2000-05-01, Close, 35.44
2000-05-02, Close, 34.61
2000-05-03, Close, 33.72
2000-05-04, Close, 33.02
2000-05-04, 卖出单, 33.02
2000-05-05, 已卖出, 价格: 32.91, 费用: 349.10, 佣金 0.00
2000-05-05, 交易利润, 毛利润 -20.00, 净利润 -20.00
2000-05-05, Close, 34.16
2000-05-05, 买入单, 34.16
2000-05-08, 已买入, 价格: 33.49, 费用: 334.90, 佣金 0.00
2000-05-08, Close, 32.16
2000-05-08, 卖出单, 32.16
2000-05-09, 已卖出, 价格: 32.77, 费用: 334.90, 佣金 0.00
2000-05-09, 交易利润, 毛利润 -7.20, 净利润 -7.20
2000-05-09, Close, 32.02
2000-05-10, Close, 30.08
2000-05-11, Close, 32.19
2000-05-12, Close, 32.99
2000-05-15, Close, 34.25
2000-05-15, 买入单, 34.25
2000-05-16, 已买入, 价格: 34.52, 费用: 345.20, 佣金 0.00
2000-05-16, Close, 35.22
2000-05-17, Close, 34.77
2000-05-18, Close, 32.49
2000-05-18, 卖出单, 32.49
2000-05-19, 已卖出, 价格: 32.02, 费用: 345.20, 佣金 0.00
2000-05-19, 交易利润, 毛利润 -25.00, 净利润 -25.00
2000-05-19, Close, 31.16
2000-05-22, Close, 30.16
2000-05-23, Close, 27.85
2000-05-24, Close, 28.57
2000-05-25, Close, 29.55
2000-05-26, Close, 29.80
2000-05-30, Close, 32.99
2000-05-30, 买入单, 32.99
2000-05-31, 已买入, 价格: 32.58, 费用: 325.80, 佣金 0.00
2000-05-31, Close, 31.97
2000-06-01, Close, 34.63
2000-06-02, Close, 35.66
2000-06-05, Close, 36.00
2000-06-06, Close, 34.27
2000-06-07, Close, 35.58
2000-06-08, Close, 36.64
2000-06-09, Close, 36.77
2000-06-12, Close, 35.83
2000-06-13, Close, 36.33
2000-06-14, Close, 35.13
2000-06-15, Close, 36.69
2000-06-16, Close, 36.41
2000-06-19, Close, 38.25
2000-06-20, Close, 38.27
2000-06-21, Close, 38.33
2000-06-22, Close, 36.25
2000-06-22, 卖出单, 36.25
2000-06-23, 已卖出, 价格: 35.94, 费用: 325.80, 佣金 0.00
2000-06-23, 交易利润, 毛利润 33.60, 净利润 33.60
2000-06-23, Close, 35.36
2000-06-26, Close, 36.77
2000-06-26, 买入单, 36.77
2000-06-27, 已买入, 价格: 36.64, 费用: 366.40, 佣金 0.00
2000-06-27, Close, 36.58
2000-06-27, 卖出单, 36.58
2000-06-28, 已卖出, 价格: 36.50, 费用: 366.40, 佣金 0.00
2000-06-28, 交易利润, 毛利润 -1.40, 净利润 -1.40
2000-06-28, Close, 36.89
2000-06-28, 买入单, 36.89
2000-06-29, 已买入, 价格: 36.50, 费用: 365.00, 佣金 0.00
2000-06-29, Close, 35.97
2000-06-29, 卖出单, 35.97
2000-06-30, 已卖出, 价格: 35.75, 费用: 365.00, 佣金 0.00
2000-06-30, 交易利润, 毛利润 -7.50, 净利润 -7.50
2000-06-30, Close, 37.39
2000-06-30, 买入单, 37.39
2000-07-03, 已买入, 价格: 36.08, 费用: 360.80, 佣金 0.00
2000-07-03, Close, 35.66
2000-07-03, 卖出单, 35.66
2000-07-05, 已卖出, 价格: 34.16, 费用: 360.80, 佣金 0.00
2000-07-05, 交易利润, 毛利润 -19.20, 净利润 -19.20
2000-07-05, Close, 32.16
2000-07-06, Close, 33.63
2000-07-07, Close, 33.75
2000-07-10, Close, 32.97
2000-07-11, Close, 32.16
2000-07-12, Close, 33.22
2000-07-13, Close, 33.69
2000-07-14, Close, 33.86
2000-07-17, Close, 33.86
2000-07-18, Close, 32.99
2000-07-19, Close, 32.80
2000-07-20, Close, 34.75
2000-07-20, 买入单, 34.75
2000-07-21, 已买入, 价格: 34.44, 费用: 344.40, 佣金 0.00
2000-07-21, Close, 33.55
2000-07-21, 卖出单, 33.55
2000-07-24, 已卖出, 价格: 34.30, 费用: 344.40, 佣金 0.00
2000-07-24, 交易利润, 毛利润 -1.40, 净利润 -1.40
2000-07-24, Close, 33.36
2000-07-25, Close, 33.80
2000-07-25, 买入单, 33.80
2000-07-26, 已买入, 价格: 33.27, 费用: 332.70, 佣金 0.00
2000-07-26, Close, 34.13
2000-07-27, Close, 33.38
2000-07-27, 卖出单, 33.38
2000-07-28, 已卖出, 价格: 33.41, 费用: 332.70, 佣金 0.00
2000-07-28, 交易利润, 毛利润 1.40, 净利润 1.40
2000-07-28, Close, 32.19
2000-07-31, Close, 33.44
2000-07-31, 买入单, 33.44
2000-08-01, 已买入, 价格: 33.44, 费用: 334.40, 佣金 0.00
2000-08-01, Close, 32.52
2000-08-01, 卖出单, 32.52
2000-08-02, 已卖出, 价格: 32.47, 费用: 334.40, 佣金 0.00
2000-08-02, 交易利润, 毛利润 -9.70, 净利润 -9.70
2000-08-02, Close, 32.52
2000-08-03, Close, 34.44
2000-08-03, 买入单, 34.44
2000-08-04, 已买入, 价格: 34.83, 费用: 348.30, 佣金 0.00
2000-08-04, Close, 36.27
2000-08-07, Close, 36.41
2000-08-08, Close, 36.91
2000-08-09, Close, 36.19
2000-08-10, Close, 35.61
2000-08-11, Close, 36.08
2000-08-14, Close, 36.64
2000-08-15, Close, 36.14
2000-08-16, Close, 36.11
2000-08-17, Close, 37.33
2000-08-18, Close, 36.16
2000-08-21, Close, 37.00
2000-08-22, Close, 37.16
2000-08-23, Close, 36.86
2000-08-24, Close, 37.66
2000-08-25, Close, 37.64
2000-08-28, Close, 38.58
2000-08-29, Close, 39.03
2000-08-30, Close, 39.25
2000-08-31, Close, 40.44
2000-09-01, Close, 41.19
2000-09-05, Close, 40.50
2000-09-06, Close, 39.69
2000-09-07, Close, 40.56
2000-09-08, Close, 38.50
2000-09-08, 卖出单, 38.50
2000-09-11, 已卖出, 价格: 38.28, 费用: 348.30, 佣金 0.00
2000-09-11, 交易利润, 毛利润 34.50, 净利润 34.50
2000-09-11, Close, 37.11
2000-09-12, Close, 35.30
2000-09-13, Close, 36.39
2000-09-14, Close, 37.78
2000-09-15, Close, 34.83
2000-09-18, Close, 34.01
2000-09-19, Close, 35.27
2000-09-20, Close, 35.55
2000-09-21, Close, 35.11
2000-09-22, Close, 35.91
2000-09-25, Close, 35.02
2000-09-26, Close, 35.33
2000-09-27, Close, 35.52
2000-09-28, Close, 36.24
2000-09-28, 买入单, 36.24
2000-09-29, 已买入, 价格: 36.18, 费用: 361.80, 佣金 0.00
2000-09-29, Close, 35.02
2000-09-29, 卖出单, 35.02
2000-10-02, 已卖出, 价格: 35.47, 费用: 361.80, 佣金 0.00
2000-10-02, 交易利润, 毛利润 -7.10, 净利润 -7.10
2000-10-02, Close, 35.02
2000-10-03, Close, 30.91
2000-10-04, Close, 30.30
2000-10-05, Close, 30.38
2000-10-06, Close, 30.08
2000-10-09, Close, 29.69
2000-10-10, Close, 28.74
2000-10-11, Close, 27.69
2000-10-12, Close, 28.02
2000-10-13, Close, 31.69
2000-10-16, Close, 30.74
2000-10-17, Close, 29.96
2000-10-18, Close, 29.85
2000-10-19, Close, 32.36
2000-10-19, 买入单, 32.36
2000-10-20, 已买入, 价格: 32.13, 费用: 321.30, 佣金 0.00
2000-10-20, Close, 31.35
2000-10-23, Close, 30.30
2000-10-24, Close, 31.85
2000-10-25, Close, 30.58
2000-10-26, Close, 30.30
2000-10-27, Close, 30.41
2000-10-30, Close, 28.13
2000-10-30, 卖出单, 28.13
2000-10-31, 已卖出, 价格: 29.02, 费用: 321.30, 佣金 0.00
2000-10-31, 交易利润, 毛利润 -31.10, 净利润 -31.10
2000-10-31, Close, 29.35
2000-11-01, Close, 27.91
2000-11-02, Close, 26.30
2000-11-03, Close, 26.96
2000-11-06, Close, 24.85
2000-11-07, Close, 23.63
2000-11-08, Close, 22.07
2000-11-09, Close, 24.18
2000-11-10, Close, 22.63
2000-11-13, Close, 22.01
2000-11-14, Close, 25.24
2000-11-15, Close, 25.68
2000-11-16, Close, 24.35
2000-11-17, Close, 25.63
2000-11-17, 买入单, 25.63
2000-11-20, 已买入, 价格: 21.63, 费用: 216.30, 佣金 0.00
2000-11-20, Close, 22.01
2000-11-20, 卖出单, 22.01
2000-11-21, 已卖出, 价格: 22.07, 费用: 216.30, 佣金 0.00
2000-11-21, 交易利润, 毛利润 4.40, 净利润 4.40
2000-11-21, Close, 21.24
2000-11-22, Close, 19.85
2000-11-24, Close, 21.46
2000-11-27, Close, 20.57
2000-11-28, Close, 20.15
2000-11-29, Close, 20.35
2000-11-30, Close, 23.57
2000-11-30, 买入单, 23.57
2000-12-01, 已买入, 价格: 23.46, 费用: 234.60, 佣金 0.00
2000-12-01, Close, 23.52
2000-12-04, Close, 25.07
2000-12-05, Close, 28.02
2000-12-06, Close, 26.85
2000-12-07, Close, 25.18
2000-12-08, Close, 26.74
2000-12-11, Close, 28.41
2000-12-12, Close, 27.35
2000-12-13, Close, 25.24
2000-12-14, Close, 24.46
2000-12-14, 卖出单, 24.46
2000-12-15, 已卖出, 价格: 26.18, 费用: 234.60, 佣金 0.00
2000-12-15, 交易利润, 毛利润 27.20, 净利润 27.20
2000-12-15, Close, 25.41
2000-12-15, 买入单, 25.41
2000-12-18, 已买入, 价格: 26.68, 费用: 266.80, 佣金 0.00
2000-12-18, Close, 28.46
2000-12-19, Close, 27.24
2000-12-20, Close, 25.35
2000-12-20, 卖出单, 25.35
2000-12-21, 已卖出, 价格: 24.74, 费用: 266.80, 佣金 0.00
2000-12-21, 交易利润, 毛利润 -19.40, 净利润 -19.40
2000-12-21, Close, 26.24
2000-12-21, 买入单, 26.24
2000-12-22, 已买入, 价格: 27.02, 费用: 270.20, 佣金 0.00
2000-12-22, Close, 28.35
2000-12-26, Close, 27.52
2000-12-27, Close, 27.30
2000-12-28, Close, 27.63
2000-12-29, Close, 25.85
2000-12-29, 卖出单, 25.85
组合期末资金: 982.30
[[
虽然策略逻辑没有变,但回测结果却变了。这是由于bar的数量发生了变化。
注意: 前面提到过,框架会等待所有指标数据到位之后,才会运行 next函数。 在上例中,MACD是最后一个数据到位的指标(它的3条线都完成了输出)。 所以第一笔下单已经不是 2000年1月份了,而是2000年2月份末。
许多交易书籍都会说每个市场、每只股票(或期货等等)都有不同的节奏,也就是说没有一个参数能适应所有。在之前的例子里,策略里使用的默认参数是15。这个参数可以被更换并进行测试,以评估什么值更适合于市场。
注意: 大量文献讨论了关于优化的优缺点。一般建议都会指向同一方向:不要过度优化。如果策略不理想,而在拟合上下功夫,则可能产生一个在回测数据上非常优秀的参数,但这个参数在将来表现可能并不好。
修改了代码,以测试移动平均线的最优周期参数。为保持清新,删除了所有买入、卖出的输出。
import datetime #
import os.path # 路径管理
import sys # 获取当前运行脚本的路径 (in argv[0])
#导入backtrader框架
import backtrader as bt
# 创建策略继承bt.Strategy
class TestStrategy(bt.Strategy):
params = (
# 均线参数设置15天,15日均线
('maperiod', 15),
('printlog', False),
)
def log(self, txt, dt=None, doprint=False):
# 记录策略的执行日志
if self.params.printlog or doprint:
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
# 买入价格和手续费
self.buyprice = None
self.buycomm = None
# 加入均线指标
self.sma = bt.indicators.SimpleMovingAverage(self.datas[0], period=self.params.maperiod)
# 订单状态通知,买入卖出都是下单
def notify_order(self, order):
if order.status in [order.Submitted, order.Accepted]:
# broker 提交/接受了,买/卖订单则什么都不做
return
# 检查一个订单是否完成
# 注意: 当资金不足时,broker会拒绝订单
if order.status in [order.Completed]:
if order.isbuy():
self.log(
'已买入, 价格: %.2f, 费用: %.2f, 佣金 %.2f' %
(order.executed.price,
order.executed.value,
order.executed.comm))
self.buyprice = order.executed.price
self.buycomm = order.executed.comm
elif order.issell():
self.log('已卖出, 价格: %.2f, 费用: %.2f, 佣金 %.2f' %
(order.executed.price,
order.executed.value,
order.executed.comm))
# 记录当前交易数量
self.bar_executed = len(self)
elif order.status in [order.Canceled, order.Margin, order.Rejected]:
self.log('订单取消/保证金不足/拒绝')
# 其他状态记录为:无挂起订单
self.order = None
# 交易状态通知,一买一卖算交易
def notify_trade(self, trade):
if not trade.isclosed:
return
self.log('交易利润, 毛利润 %.2f, 净利润 %.2f' %
(trade.pnl, trade.pnlcomm))
def next(self):
# 记录收盘价
self.log('Close, %.2f' % self.dataclose[0])
# 如果有订单正在挂起,不操作
if self.order:
return
# 如果没有持仓则买入
if not self.position:
# 今天的收盘价在均线价格之上
if self.dataclose[0] > self.sma[0]:
# 买入
self.log('买入单, %.2f' % self.dataclose[0])
# 跟踪订单避免重复
self.order = self.buy()
else:
# 如果已经持仓,收盘价在均线价格之下
if self.dataclose[0] < self.sma[0]:
# 全部卖出
self.log('卖出单, %.2f' % self.dataclose[0])
# 跟踪订单避免重复
self.order = self.sell()
# 测略结束时,多用于参数调优
def stop(self):
self.log('(均线周期 %2d)期末资金 %.2f' %
(self.params.maperiod, self.broker.getvalue()), doprint=True)
# 创建Cerebro引擎
cerebro = bt.Cerebro()
# Cerebro引擎在后台创建broker(经纪人),系统默认资金量为10000
# 为Cerebro引擎添加策略, 优化策略
# 使用参数来设定10到31天的均线,看看均线参数下那个收益最好
strats = cerebro.optstrategy( TestStrategy,
maperiod=range(10, 31))
# 获取当前运行脚本所在目录
modpath = os.path.dirname(os.path.abspath(sys.argv[0]))
# 拼接加载路径
datapath = os.path.join(modpath, '/home/mw/input/backtrader7822/orcl-1995-2014.txt')
# 创建交易数据集
data = bt.feeds.YahooFinanceCSVData(
dataname=datapath,
# 数据必须大于fromdate
fromdate=datetime.datetime(2000, 1, 1),
# 数据必须小于todate
todate=datetime.datetime(2000, 12, 31),
reverse=False)
# 加载交易数据
cerebro.adddata(data)
# 设置投资金额1000.0
cerebro.broker.setcash(1000.0)
# 每笔交易使用固定交易量
cerebro.addsizer(bt.sizers.FixedSize, stake=10)
# 设置佣金为0.0
cerebro.broker.setcommission(commission=0.0)
cerebro.run(maxcpus=1)
2000-12-29, (均线周期 10)期末资金 877.50
2000-12-29, (均线周期 11)期末资金 878.70
2000-12-29, (均线周期 12)期末资金 839.80
2000-12-29, (均线周期 13)期末资金 899.90
2000-12-29, (均线周期 14)期末资金 902.50
2000-12-29, (均线周期 15)期末资金 975.60
2000-12-29, (均线周期 16)期末资金 961.90
2000-12-29, (均线周期 17)期末资金 952.60
2000-12-29, (均线周期 18)期末资金 1011.00
2000-12-29, (均线周期 19)期末资金 1039.40
2000-12-29, (均线周期 20)期末资金 1073.20
2000-12-29, (均线周期 21)期末资金 1055.10
2000-12-29, (均线周期 22)期末资金 1057.60
2000-12-29, (均线周期 23)期末资金 1021.50
2000-12-29, (均线周期 24)期末资金 1018.80
2000-12-29, (均线周期 25)期末资金 1012.40
2000-12-29, (均线周期 26)期末资金 998.30
2000-12-29, (均线周期 27)期末资金 983.10
2000-12-29, (均线周期 28)期末资金 976.90
2000-12-29, (均线周期 29)期末资金 984.20
2000-12-29, (均线周期 30)期末资金 980.80
[[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[]]
注意:在上面的例子中,移除了多余的用来绘图的指标,数据开始回测的时间仅取决于我们添加的简单移动平均线。所以周期为 15的回测结果和之前的略有不同。
上面的教程,我们从一个头开始,一步步搭建了一个能运行的回测系统,并且具备绘制结果和优化参数功能。 除此之外,还能做一些提高胜率的事情: 自定义指标:创建自定义指标很容易,绘制它们同样简单; 下单数量:资金管理是交易成功的关键之一; 委托单类型(限价单、止损单、限价止损单)等等。
所有的代码示例,需要导入以下库才能使用:
import backtrader as bt
import backtrader.indicators as btind
import backtrader.feeds as btfeeds
注意: 访问子模块的另一种语法: 以 bt形式导入backtrader 然后: thefeed = bt.feeds.OneOfTheFeeds(…) theind = bt.indicators.SimpleMovingAverage(…)
该平台的基础工作都是由“策略”完成。 将交易数据传递给策略,用户无需关心怎么接收收据。 交易数据以数组的形式传递给 “策略”,并作为“策略”的成员变量,可以通过数组下标的方式快捷访问。 快速预览一下策略派生类的声明和框架的运行
class MyStrategy(bt.Strategy):
params = dict(period=20)
def __init__(self):
sma = btind.SimpleMovingAverage(self.datas[0], period=self.params.period)
cerebro = bt.Cerebro()
#...
data = btfeeds.MyFeed(...)
cerebro.adddata(data)
#...
cerebro.addstrategy(MyStrategy, period=30)
...
请注意以下几点:策略的构造方法init中并未接收到 args或* kwargs 任何参数(但是它们仍可以使用),成员变量self.datas,该成员变量为数组/列表/可迭代,至少要要有一条记录(否则将引发异常)。就是这样,交易数据已添加到框架中,并且将按照添加到系统中的顺序显示在策略中。
注意: 这种方式,同样适用于框架源码中的现有指标,或者用户开发的自定义指标。
可以使用其他自动成员变量直接访问self.datas数组项:
class MyStrategy(bt.Strategy):
params = dict(period=20)
def __init__(self):
sma = btind.SimpleMovingAverage(self.data, period=self.params.period)
...
上面的示例可以进一步简化为:
class MyStrategy(bt.Strategy):
params = dict(period=20)
def __init__(self):
sma = btind.SimpleMovingAverage(period=self.params.period)
...
self.data已从SimpleMovingAverage的调用中完全删除,SimpleMovingAverage默认首个参数就是self.data也即self.data0(self.data[0])。
不仅仅交易数据是数据源,可以传递给测策略, 指标和操作结果同样也是数据源。
在前面的示例中,SimpleMovingAverage接收self.datas[0]作为要进行操作的输入。下面看一个操作结果和额外指标的示例:
class MyStrategy(bt.Strategy):
params = dict(period1=20, period2=25, period3=10, period4)
def __init__(self):
sma1 = btind.SimpleMovingAverage(self.datas[0], period=self.p.period1)
# 第二移动平均线使用sma1作为参数,均线的均线
sma2 = btind.SimpleMovingAverage(sma1, period=self.p.period2)
# 通过算术运算创建的新数据
something = sma2 - sma1 + self.data.close
# 第三移动平均线使用something作为参数
sma3 = btind.SimpleMovingAverage(something, period=self.p.period3)
# 比较sma3和sma1...
greater = sma3 > sma1
# 并没有实际意义的均值
# 第四移动平均线使用greater做为参数
sma3 = btind.SimpleMovingAverage(greater, period=self.p.period4)
...
基本上,所有内容都会转换为一个对象,一旦对其进行操作,就可以用作数据源。
通常,平台中的所有其他类都支持参数的概念。 参数和默认值一起声明为类的属性(元组或类似字典的对象)。 扫描关键字 args( kwargs)以查找匹配的参数,如果找到则将它们从 kwargs中删除,并将值分配给相应的参数。 通过访问成员变量 self.params(简写为self.p),最终可以在类的实例中使用参数。 先前的简洁策略已经包含一个参数示例,这里我们再次聚焦参数的使用。
通过元组方式:
class MyStrategy(bt.Strategy):
params = (('period', 20),)
def __init__(self):
sma = btind.SimpleMovingAverage(self.data, period=self.p.period)
通过字典方式:
class MyStrategy(bt.Strategy):
params = dict(period=20)
def __init__(self):
sma = btind.SimpleMovingAverage(self.data, period=self.p.period)
b同样,平台中几乎所有对象都是使用了Lines(线群)对象。 从用户的角度来看,这意味着:
框架中对线的使用通常就是读取操作, 前面的小例子少加改造如下:
class MyStrategy(bt.Strategy):
params = dict(period=20)
def __init__(self):
self.movav = btind.SimpleMovingAverage(self.data, period=self.p.period)
def next(self):
if self.movav.lines.sma[0] > self.data.lines.close[0]:
print('移动平均线大于收盘价')
展现了拥有线的两个对象:
注意: 由此可见,线只是一个名字而已。可以按照声明时的顺序访问它,但这仅适用于指标开发的过程中。
也可以通过快捷方式访问线:
此外,也可以通过以下方式直接访问线的属性:
注意: 不支持使用后面的两种标记方式来给 lines赋值
如果开发一个指标,则该指标所拥有的Lines必须要声明。 就像params一样,但是只能作为元组类型的成员属性,不支持字典,因为Lines不按照插入顺序存储内容。 对于简单移动平均线,可以这样进行声明:
class SimpleMovingAverage(Indicator):
lines = ('sma',)
...
注意: 如果您将单个字符串传递给元组,则在元组中声明后的添加逗号。否则,字符串中的每个字母都将被解释为要添加到元组的项。 这可能是Python语法中少数不合理的几个地方之一。
如上例所示,该声明在指标中创建了一条sma线,以后可以在该策略进行访问(并可能传递给其他指标来创建更复杂的指标)。
对于开发而言,通过非命名的方式访问lines会很有用,这也是使用数字索引访问的方便之处:
如果定义了更多的线,可以将使用索引1、2或者更高的索引进行访问。当然,也确实存在快捷访问版本:
交易数据对象也可以通过数字快速访问对象内部的lines:
在交易数据内部,也可以省略的方式来访问lines,比如可以用比较自然的方式访问收盘价:
data = btfeeds.BacktraderCSVData(dataname='mydata.csv')
...
class MyStrategy(bt.Strategy):
...
def next(self):
if self.data.close[0] > 30.0:
...
这种方式肯定比 self.data.lines.close[0] > 30.0: 更加自然些。但是这不适用于以下指标:
Lines是一组点的集合,并且在执行过程中会动态增长,因此可以通过调用Python中的len函数随时测量长度。 这适用于:
交易数据预加载后,data属性也可使用buflen方法,返回交易数据的柱线数。
len和buflen之间的区别:
如果两个都返回相同的值,则要么没有数据被预加载,要么当前处理已消耗了所有预加载的数据(除非系统连接到实时交易数据,否则将意味着处理结束)。
框架提供了一种元语言来支持参数和线的声明。我们尽量使其与Python的继承规则兼容。
参数继承
Lines继承
如前所述,Lines是线群,线是一组点的集合,这些点在绘制在一起形成一条线(例如,沿着时间轴将所有收盘价连在一起就形成收盘价曲线)
要在常规代码中访问这些点,一般通过0索引的方式对当前点进行get/set操作。 策略只能读取数据, 指标既可以读取也可以写入数据。
回顾前面简单的示例,策略中的next方法:
def next(self):
if self.movav.lines.sma[0] > self.data.lines.close[0]: print('简单移动平均线大于收盘价')
通过索引0获得移动平均线的当前值和当前收盘价,并比较它们的大小。
注意: 实际上对于索引 0,可直接进行逻辑/算术运算操作,如下所示
更多相关说明请参阅文档后面的《操作章节》。
在指标开发的应用中,会出现赋值操作。 例如 SimpleMovingAverage的当前值可以通过如下方式进行读写:
def next(self):
self.line[0] = math.fsum(self.data.get(0, size=self.p.period)) / self.p.period
访问前一个点集合可以按照Python访问数组索引为-1的方式:
框架认为最后一项为(读写当前点的前一个点)索引值为-1。 因此,在策略中比较当前收盘价与前一个收盘价是通过 0 vs -1的方式。例如:
def next(self):
if self.data.close[0] > self.data.close[-1]:
print('今天收盘价更高')
同理,使用-1,-2,-3,…便访问-1之前项的价格。
backtrader不支持对线对象进行切片,这是遵循[0]和[-1]索引方案的设计决策。 使用常规的可索引 Python对象,可以执行以下操作:
# 从开始到结尾的切片
myslice = self.my_sma[0:]
但是请记住,选择0…实际上是当前开始传递的值,之后也没有任何值。
# 从开始到结尾的切片
myslice = self.my_sma[0:-1]
同样,…0是当前值,而-1是先前交付的值。 这就是为什么从 0->-1进行的切片反向操作就毫无意义的原因。如果可以反向操作,那么切片可能应该这样:
# 从当前点向前的切片
myslice = self.my_sma[:0]
# 从最后的值到当前值
myslice = self.my_sma[-1:0]
# 从最后的值到倒数第3个值
可以获得具有最新值的数组,语法:
# 显示默认值
myslice = self.my_sma.get(ago=0, size=1)
返回一个数组,该数组的大小为1,当前时刻为0,向后获取。
要从当前时间点获取10个值(即:最后10个值):
# ago的默认值为0
myslice = self.my_sma.get(size=10)
常规数组具有你所期望的顺序。最左边的值是最旧的值,最右边的值是最新的值(这是常规的python数组,而不是lines对象)。
# 跳过当前点获取最后10个值
myslice = self.my_sma.get(ago=-1, size=10)
[]运算符可用于在next逻辑阶段提取单个值。lines对象支持附加的符号,以便在__init__阶段通过延迟的线对象寻址取值。
假设一条逻辑是将先前的收盘价与简单移动平均线的实际值进行比较。无需在每次next迭代中进行手动操作,而是可以生成预定义的lines对象:
class MyStrategy(bt.Strategy):
params = dict(period=20)
def __init__(self):
self.movav = btind.SimpleMovingAverage(self.data, period=self.p.period)
self.cmpval = self.data.close(-1) > self.sma
def next(self):
if self.cmpval[0]:
print('上一个收盘价高于当前移动平均值')
这里使用"()"延迟符号:
运算符()可以与延迟的数值一起使用,以提供延迟的line对象。
如果使用中不提供延迟数值,则返回LinesCoupler对象。这是为了在操作具有不同时间范围的数据指标之间建立耦合。
不同时间范围的交易数据具有不同的长度,并且指标在操作这些数据时会复制数据的长度。例如:
尝试创建一个比较两个简单移动平均线的操作,每次操作在引用数据时都有可能被中断。因为系统不知道如何在250条的日交易数据和52条的周交易数据之间进行匹配。
读者可以通过找出一天和一周的对应关系进行比较,但是:
于是()表示法(空调用)可用于解决这个问题:
class MyStrategy(bt.Strategy):
params = dict(period=20)
def __init__(self):
# data0 是日交易数据
sma0 = btind.SMA(self.data0, period=15) # 15天sma
# data1 是周要以数据
sma1 = btind.SMA(self.data1, period=5) # 5周sma
self.buysig = sma0 > sma1()
def next(self):
if self.buysig[0]:
print('每日sma大于每周sma1')
在这里,较大的时间范围指标sma1通过sma1()与每日时间范围耦合。这将返回与更大数量的sma0兼容的对象,并复制sma1产生的值,从而有效地将52个周数据分散为250个日数据。
为了实现“简单易用”的目标,backtrader允许(在Python的语法范围内)使用操作符。为了进一步简单化,操作符的使用有两种情景。
我们之前已经看到了一个例子。在指标和策略类的对象初始化阶段(__ init__方法)中,操作符创建并保存可以持续使用的对象,供策略逻辑在评估阶段使用。
将SimpleMovingAverage的潜在实现方式进一步细分为多个步骤。
SimpleMovingAverage指标__init__内的代码可能如下:
def __ init__(self):
# N个周期值的总和,数据总和是一个Lines对象
# 在与运算符[]和索引0查询时
# 返回当前总和
datasum = btind.SumN(self.data, period=self.params.period)
# datasum(虽然是单行,但仍是一个Lines对象)
# 在这种情况下它可以除以int/float类型的数据。
# 但实际上它被除以后得到另外一个Lines对象。
# 该操作返回分配给av对象
# 当查询为[0],则返回当前时间点的平均值
av = datasum / self.params.period
# av是对新的Lines对象的命名
# 其他对象使用这个指标可以直接访问计算
self.line.sma = av
策略初始化期间显示了更加完整的用法:
class MyStrategy(bt.Strategy):
def __init__(self):
sma = btind.SimpleMovinAverage(self.data, period=20)
close_over_sma = self.data.close > sma
sma_dist_to_high = self.data.high - sma
sma_dist_small = sma_dist_to_high < 3.5
# 不幸的是,"and"不能在Python中被重载
# 在python中and不属于运算符,所以backtrader提供一个函数模拟这个功能
sell_sig = bt.And(close_over_sma, sma_dist_small)
完成上述操作后,sell_sig是一个Lines对象,当指示满足条件时,可以直接在策略中使用。
首先,策略的next方法,系统要处理每个柱时都要调用该方法,这就是操作符处于情景2地方。以前面的示例为基础:
class MyStrategy(bt.Strategy):
def __init__(self):
sma = btind.SimpleMovinAverage(self.data, period=20)
close_over_sma = self.data.close > sma
sma_dist_to_high = self.data.high - sma
sma_dist_small = sma_dist_to_high < 3.5
# 不幸的是,"and"不能在Python中被重载
# 在python中and不属于运算符,所以backtrader提供一个函数模拟这个功能
sell_sig = bt.And(close_over_sma, sma_dist_small)
def next(self):
# 尽管这看起来不像是“操作号”,但确实返回的是正在测试对象的True/False
if self.sma > 30.0:
print('sma大于30.0')
if self.sma > self.data.close:
print('sma高于收盘价')
if self.sell_sig: # if sell_sig == True: would also be valid
print('卖出标志为True')
else:
print('卖出标志为False')
if self.sma_dist_to_high > 5.0:
print('sma到high的距离大于5.0')
这不是一个非常有用的策略,只是一个例子。在情景2中,操作符返回期望值(如果测试值为True,则返回布尔值;如果是浮点数进行比较,则
返回浮点数),并且算术运算也返回期望值。
注意:
为了进一步简化,比较实际上没有使用操做符。
if self.sma > 30.0: …比较 self.sma[0] 和 30.0
if self.sma > self.data.close: … 比较 self.sma[0] 和 self.data.close[0]
Python不允许重载所有内容,因此提供了一些功能函数来应对这种情况。
注意:仅适用于情景1,以创建对象供后面使用。
操作符:
逻辑控制:
函数:
Sum实际上使用math.fsum作为底层操作,因为backtrader使用浮点数计算,如果用常规sum可能会影响精度。
这些实用的操作符/函数可迭代使用。可迭代的元素可以是常规的Python数字类型(int,float等),也可以是带有Lines的对象。 例如一个非常原
始的买入信号:
class MyStrategy(bt.Strategy):
def __init__(self):
sma1 = btind.SMA(self.data.close, period=15)
self.buysig = bt.And(sma1 > self.data.close, sma1 > self.data.high)
def next(self):
if self.buysig[0]:
pass # do something here
例如sma1高于最高价,则必高于收盘价,这里重点是说明bt.And的用法。
bt.If用法:
class MyStrategy(bt.Strategy):
def __init__(self):
# 在period=15的data.close上生成SMA
sma1 = btind.SMA(self.data.close, period=15)
# 如果sma的值大于close,则返回low,否则返回high
high_or_low = bt.If(sma1 > self.data.close, self.data.low, self.data.high)
sma2 = btind.SMA(high_or_low, period=15)
解释说明:
class MyStrategy(bt.Strategy):
def __init__(self):
sma1 = btind.SMA(self.data.close, period=15)
high_or_30 = bt.If(sma1 > self.data.close, 30.0, self.data.high)
sma2 = btind.SMA(high_or_30, period=15)
现在,sma2使用30.0或最高价进行计算,具体取决于sma1和close的比较结果。
注意:数值30在内部转换为伪迭代,始终返回30
Cerebro类是backtrader的引擎,是以下几个方面的核心:
1.以创建cerebro开始:
# **kwargs参数支持某些控制执行,请参阅后面文档(相同的参数也应用于run方法)
cerebro = bt.Cerebro(**kwargs)
2.添加交易数据
最常见的模式是 cerebro.adddata(data),其中data是已实例化的数据源。例:
data = bt.BacktraderCSVData(dataname='mypath.days', timeframe=bt.TimeFrame.Days)
cerebro.adddata(data)
重新采样或数据重播遵循相同的模式:
data = bt.BacktraderCSVData(dataname='mypath.min', timeframe=bt.TimeFrame.Minutes)
cerebro.resampledata(data, timeframe=bt.TimeFrame.Days)
# 或者
data = bt.BacktraderCSVData(dataname='mypath.min', timeframe=bt.TimeFrame.Minutes)
cerebro.replaydatadata(data, timeframe=bt.TimeFrame.Days)
系统可以接受任何数量的交易数据,包括将常规数据与重采样和/或重播的数据混合。当然,这些组合中的某些组合肯定会毫无意义,并且为了组合数据有意义增加了限制条件:根据时间条件限制。请参阅数据章节的多个时间范围、数据重采样和数据重播部分。
3.添加策略
与交易数据不同的是交易数据已经是类的实例,而cerebro是直接使用策略类和参数。因为:在策略优化方案中,该类将被实例化多次并传递不同的参数。
即使没有运行策略优化方案,该模式仍然适用:
cerebro.addstrategy(MyStrategy, myparam1=value1, myparam2=value2)
# 优化时,必须将参数作为可迭代对象添加。有关详细说明,请参见“优化”部分。
cerebro.optstrategy(MyStrategy, myparam1=range(10, 20))
它将使用myparam1值从10到19的值运行MyStrategy10次(记住Python中的范围是半开的,不会取到20)
4.其他要素
可以添加其他一些元素来增强回测体验,请参见相应的部分。方法是:
5.更换经纪人
Cerebro将在backtrader中使用默认经纪人,但是可以被重写:
broker = MyBroker()
cerebro.broker = broker # property using getbroker/setbroker methods
6.接收通知
如果交易数据和/或代理发送通知(被store provider创建的通知),则它们将通过Cerebro.notify_store方法接收。有三种处理通知的方法:
运行回测比较简单,但是它支持多个选项来决定如何运行(可以在实例化时指定运行模式):
result = cerebro.run(**kwargs)
请参阅下文中相关部分,以了解可用的参数选项。
cerebro(除非另有说明)自动实例化三个标准观察者
cerebro返回在回测期间创建的策略实例。由于策略中的所有元素都可访问,因此可以分析他们的操作:
result = cerebro.run(**kwargs)
返回结果的格式将根据是否使用优化选项而有所不同(策略可以通过optstrategy方式添加到cerebro中):
注意 : 为使得内核传递的消息更轻便,现在optimization默认返回分析器analyzers。
如果希望将整套策略作为返回值,请将参数optreturn设置为False。
如果安装了matplotlib,则可以绘制策略结果。 通常的模式是:
cerebro.plot()
请阅读下面的参考中“绘图”部分。
回测流程概述:
1.传递store中的所有通知;
2.令数交易数据以柱集合方式传递给next方法;
在版本1.9.0.99中已更改为:
交易数据根据datetime同步检索出来,然后提供给next方法。当没有新时间段的交易数据时,就使用旧的数据,有则使用新的(指标中的计算也是如此)。
如果过使用旧版本的默认行为,就在使用Cerebro时,将oldsync=True。
插入到系统中的第一个数据是主数据,其他交易数据为从属数据,系统将等待第一次响应时钟,并且:
该逻辑目的在于同步多个具有不同时间点的数据。
3.将有关订单,交易和现金/价值的消息队列,通过经纪通知给策略;
4.告知经纪人接受订单队列并使用新数据执行挂单操作;
5.调用该策略的next方法,以使该策略评估新数据(以及可能发布在经纪人中排队的订单)根据阶段的不同,在满足策略/指标的最短期限要求之前(可能在prenext或者nextstart方法中),这些策略内部还将对观察者,指标,分析器和其他活跃要素产生影响;
6.告诉任何记录器将数据写入目标。
重要的考虑因素:
注意:
在上面的步骤1中,当交易数据传递新的一组柱线时,旧柱线被关闭,这意味着这些被关闭的数据已经发生过。
因此,无法使用步骤1中的数据执行步骤4中策略发出的订单。
这意味着定单将以x + 1的概念执行,其中x是定单执行的柱线,x + 1是下一个定单,这是可能执行定单的最早时间。
class backtrader.Cerebro() 参数:
backtrader带有一组交易数据解析器(解析所有基于CSV格式的文件),可让你从不同来源加载数据。
从快速入门指南中可以清楚地看到,你已将交易数据添加到Cerebro中。 交易数据将用于下不同的策略:
Cerebro是backtrader的心核心,策略是用户的核心。
研究一下策略的生命周期:
注意:
一个策略可能在出生时被来自backtrader.errors模块中的StrategySkipError异常所中断,这样可以避免在回测期间继续执行该策略,详情请参阅《异常》部分章节。
def __init__():
self.sma = btind.SimpleMovingAverage(period = 15)
启动:start cerebro通知strategy开始运行,默认值是一个空方法。
预循环:prenext
初始化时,声明一个指标,指标限制了策略可使用的最小期限。例如:在init上方,创建了一个Period = 15的SimpleMovingAverage。
只要系统看到的柱线数量少于15条,就会调用prenext(默认实现是空操作)
循环:next
一旦系统看到交易数据中有超过15条柱线,并且SimpleMovingAverage具有足够大的缓冲区,该策略可以真正执行。
有一个nextstart方法,该方法仅被调用一次,以标记从prenext到next的切换。 nextstart的默认实现是简单地调用next方法。
重复:none
主要在进行参数优化时(使用不同的参数),系统会根据不同的参数重复实例化多次。
停止:stop
进行重置的时间到了,系统会告之该策略进行重置操作, 默认方法为空。
在大多数情况下,常规使用模式,如下:
class MyStrategy(bt.Strategy):
def __init__(self):
self.sma = btind.SimpleMovingAverage(period=15)
def next(self):
if self.sma > self.data.close:
# Do something
pass
elif self.sma < self.data.close:
# Do something else
pass
在此代码段中:
策略就像现实世界中的交易者一样,当有事件发生时会得到通知。实际上,回测过程中的每个周期next方法被调用一次。该策略将执行以下动作:
请参阅Cerebro对有关store通知的说明,即使store通知传递给了Cerebro,也一样会传递给策略(使用重写的notify_store方法或通过回调的方式)。
策略也希望交易者扑捉到市场中的机会,并在next方法中,通过以下操作来获利:
指标可以在backtrader的两个地方使用:
hilo_diff = self.data.high - self.data.low
# 变量hilo_diff拥有对lines对象的引用,该引用在调用next之前已预先计算,可以使用标准数组符号[]进行访问,它包含了每个条交易数据的最高价和最低价之差。
当混合简单的线(如self.data交易数据的线)和复杂的线(如指标)时,同样适用:
sma = bt.SimpleMovingAverage(self.data.close)
close_sma_diff = self.data.close - sma
# close_sma_diff 包含一个线对象
使用逻辑运算符:
close_over_sma = self.data.close > sma
# 此时生成的lines对象将包含一个布尔数组
# 操作示例(逻辑运算符)
close_over_sma = self.data.close> self.sma
# 使用等效数组(基于索引0的表示法)
close_over_sma = self.data.close [0]> self.sma [0]
在这种情况下,close_over_sma会产生一个布尔值,该值是比较两个浮点值的结果,这两个浮点值是由[0]运算符返回的值应用于self.data.close和self.sma的
逻辑简化易用是关键。可以在__init__期间进行相关的声明和计算,以便在next方法中将实际操作逻辑降至最低。
还有额外的好处:速度(因为一开始就预先计算好了)。
一个在__init__期间生成购买信号的完整示例:
class MyStrategy(bt.Strategy):
def __init__(self):
sma1 = btind.SimpleMovingAverage(self.data)
ema1 = btind.ExponentialMovingAverage()
close_over_sma = self.data.close > sma1
close_over_ema = self.data.close > ema1
sma_ema_diff = sma1 - ema1
buy_sig = bt.And(close_over_sma, close_over_ema, sma_ema_diff > 0)
def next(self):
if buy_sig:
self.buy()
注意
无法覆盖Python的and运算符,所以使用backtrader自定义的And,类似的函数还有Or和If。
显而易见,在__init__期间使用“声明式”方法可以将next方法中(实际策略触发的地方)的cpu使用降至最低。
注意
当逻辑变得非常复杂并涉及多个操作时,通常最好将其封装在一个指标类中。
在上面的示例中,与其他平台相比,backtrader简化了两件事:
首先,最重要的是:
close_over_sma = self.data.close> self.sma
LinePlotterIndicator(close_over_sma,name ='Close_over_SMA')
# name参数将名称指定给此指标
如果必须定制任何内容(一个或多个获胜策略除外),这一定是自定义指标。
backtrader下自定义指标非常容易。
必要流程如下:
指标为收到的每个柱线产生输出。 不必假设同一柱将被发送多少次,操作必须是幂等的。
其基本原理:
例如,“重播”每日会话,可以使用由5分钟柱组成的日内数据。
backtrader允许使用实时交易数据。
class DummyInd(bt.Indicator):
lines = ('dummyline',)
params = (('value', 5),)
def __init__(self):
self.lines.dummyline = bt.Max(0.0, self.params.value)
指标将始终输出相同的值:0.0或self.params.value(如果恰好大于0.0)。
相同的指标,但使用next方法:
class DummyInd(bt.Indicator):
lines = ('dummyline',)
params = (('value', 5),)
def next(self):
self.lines.dummyline[0] = max(0.0, self.params.value)
注意 在__init__版本中,使用bt.Max分配Line对象给self.lines.dummyline。
bt.Max返回一个line对象,该对象会针对传递到指标的每个柱线自动进行迭代。
如果使用max代替,则分配将是毫无意义的,因为指标得到一个固定值,而不是一个线对象。 在 next方法中,将直接使用浮点值完成工作,并且可以使用内置标准的max函数。
让我们回顾一下,self.lines.dummyline是长符号,可以将其缩短为:
class DummyInd(bt.Indicator):
lines = ('dummyline',)
params = (('value', 5),)
def next(self):
self.lines.dummyline[0] = max(0.0, self.params.value)
def once(self, start, end):
dummy_array = self.lines.dummyline.array
for i in xrange(start, end):
dummy_array[i] = max(0.0, self.params.value)
无论如何__init__版本是最好的:
如果可能,backtrader将对其进行计算,但可能需要手动操作。 这是简单移动平均线的潜在实现:
class SimpleMovingAverage1(Indicator):
lines = ('sma',)
params = (('period', 20),)
def next(self):
datasum = math.fsum(self.data.get(size=self.p.period))
self.lines.sma[0] = datasum / self.p.period
尽管听起来不错,但backtrader不知道最小周期是多少,即使该参数被命名为“period”(名称可能会引起误解,并且某些指标会收到几种用法不同的“period”)
在这种情况下,将已经为第一个数据柱调用了next方法,因为get方法无法返回所需的self.p.period,将会导致一系列的异常。在解决问题之前,必须考虑以下事项:
class SimpleMovingAverage1(Indicator):
lines = ('sma',)
params = (('period', 20),)
def __init__(self):
self.addminperiod(self.params.period)
def next(self):
datasum = math.fsum(self.data.get(size=self.p.period))
self.lines.sma[0] = datasum / self.p.period
addminperiod方法是让系统考虑该指标所需的额外周期条,以考虑可能存在的任何最小周期。 如果所有计算都是使用已经将其周期需求传达给系统的对象完成的,则有时绝对不需要。
快速实现带有直方图的MACD指标:
from backtrader.indicators import EMA
class MACD(Indicator):
lines = ('macd', 'signal', 'histo',)
params = (('period_me1', 12), ('period_me2', 26), ('period_signal', 9),)
def __init__(self):
me1 = EMA(self.data, period=self.p.period_me1)
me2 = EMA(self.data, period=self.p.period_me2)
self.l.macd = me1 - me2
self.l.signal = EMA(self.l.macd, period=self.p.period_signal)
self.l.histo = self.l.macd - self.l.signal
完成,无需考虑最小周期问题:
让我们开发一个简单的自定义指标,该指标的移动平均值可以用参数修改:
import backtrader as bt
import backtrader.indicators as btind
class OverUnderMovAv(bt.Indicator):
lines = ('overunder',)
params = dict(period=20, movav=btind.MovAv.Simple)
def __init__(self):
movav = self.p.movav(self.data, period=self.p.period)
self.l.overunder = bt.Cmp(movav, self.data)
完成,如果平均值高于数据,则指标的值为“1”,如果低于数据,则指标的值为“-1”。如果数据是常规数据,则与收盘价相比会产生1和-1。为了让指标可以优雅的绘图(更多内容可以参考绘图部分),我们可以添加以下几点:
import backtrader as bt
import backtrader.indicators as btind
class OverUnderMovAv(bt.Indicator):
lines = ('overunder',)
params = dict(period=20, movav=bt.ind.MovAv.Simple)
plotinfo = dict(
# 1s和-1s的上方和下方添加额外的边距
plotymargin=0.15,
# 在1.0和-1.0处绘制参考水平线
plothlines=[1.0, -1.0],
# 将y的范围设置为1.0和-1.0之间
plotyticks=[1.0, -1.0])
# 用破折号样式绘制“overunder”线,ls代表线的样式,并直接传递给matplotlib
plotlines = dict(overunder=dict(ls='--'))
def _plotlabel(self):
# 此方法返回将显示的标签列表,指标名称跟在绘图点后面
# period 必须要有
plabels = [self.p.period]
# 如果不是默认值,则仅放置移动平均线
plabels += [self.p.movav] * self.p.notdefault('movav')
return plabels
def __init__(self):
movav = self.p.movav(self.data, period=self.p.period)
self.l.overunder = bt.Cmp(movav, self.data)
无论是回测还是交易,能够分析交易系统的性能是非常重要的。因为不仅了解获得利润,而且还要了解风险。如果风险太大我们就要是否值得为这样的资产付出努力。参考资产(或无风险资产)。
这就是Analyzer对象家族的用武之地:提供对发生的事件甚至实际发生的事件的分析。
该接口是按照Lines对象的接口建模的,特征有点像next方法,但有一个主要区别:
通过cerebro实例将Analyzer对象(与策略,观察者和数据方式雷同)添加到系统中:
当在cerebro.run中运行时,系统中存在的每种策略都会发生以下情况:
某些分析器对象实际上可能会使用其他分析器来完成其工作。 例如: SharpeRatio使用TimeReturn的输出进行计算。
这些子分析器或从属分析器也将被插入到创建它们的策略中,但是它们对于用户是完全不可见的。
为了执行预期的工作,Analyzer对象提供了一些默认属性,这些属性会自动传递并在实例中进行设置,以便于使用:
可以使用其他一些别名,尽管它们可能有点过头了:
如果该行具有名称,则以下内容也可用:
对于第一个数据,没有初始X的情况下,最后的快捷方式可用的。例如:
Analyzer基类创建一个self.rets(类型为collections.OrderedDict)成员属性以返回分析结果。这是在create_analysis方法中完成的,如果创建自定义Analyzer,则子类可以覆盖该方法。
尽管Analyzer对象不是“线”对象,因此不会在线上进行迭代,但它们被设计为遵循相同的操作模式。
一旦完成正常的操作周期,Analyzers便可使用用于提取/输出信息的方法:
最后:
在backtrader平台中Analyzer对象的开发揭示了两种不同模式来产生成分析结果:
from __future__ import (absolute_import, division, print_function,
unicode_literals)
import datetime
import backtrader as bt
import backtrader.analyzers as btanalyzers
import backtrader.feeds as btfeeds
import backtrader.strategies as btstrats
cerebro = bt.Cerebro()
# data
dataname = '2005-2006-day-001.txt'
data = btfeeds.BacktraderCSVData(dataname=dataname)
cerebro.adddata(data)
# strategy
cerebro.addstrategy(btstrats.SMA_CrossOver)
# Analyzer
cerebro.addanalyzer(btanalyzers.SharpeRatio, _name='mysharpe')
thestrats = cerebro.run()
thestrat = thestrats[0]
print('夏普比率:', thestrat.analyzers.mysharpe.get_analysis())
夏普比率: OrderedDict([('sharperatio', 11.647332609673251)])
执行它(已将其存储在analyzer-test.py中):
$ ./analyzer-test.py
Sharpe Ratio: {'sharperatio': 11.647332609673256}
这里没有绘图,因为SharpeRatio在计算结束时是单个值。
注意
从(至少)2017年7月25日起,pyfolio API已更改,并且create_full_tear_sheet不再有Gross_lev作为命名参数。因此,集成示例不起作用。
以下引用来自pyfolio主页(http://quantopian.github.io/pyfolio/)
pyfolio是一个Python库,用于投资组合的财务绩效和风险分析,由Quantopian公司开发。可以很好的配合Zipline框架,进行回测。
现在,也可以与backtrader一起使用。需要支持库:
注意
在与版本0.5.1集成期间,需要更新依赖库的最新软件包。例如将seaborn从先前安装的0.7.0-dev升级到0.7.1,这主要是因为缺少方法
swarmplot造成的。
cerebro.addanalyzer(bt.analyzers.PyFolio)
strats = cerebro.run()
strat0 = strats[0]
pyfolio = strats.analyzers.getbyname('pyfolio')
returns, positions, transactions, gross_lev = pyfoliozer.get_pf_items()
一些与backtrader没有直接关系的使用说明
...
cerebro.addanalyzer(bt.analyzers.PyFolio, _name='pyfolio')
...
results = cerebro.run()
strat = results[0]
pyfoliozer = strat.analyzers.getbyname('pyfolio')
returns, positions, transactions, gross_lev = pyfoliozer.get_pf_items()
...
...
# pyfolio showtime
import pyfolio as pf
pf.create_full_tear_sheet(
returns,
positions=positions,
transactions=transactions,
gross_lev=gross_lev,
live_start_date='2005-05-01', # 指定日期
round_trips=True)
# 当前节点的图表将展现
查看PyFolio analyzer的内部使用的相关参考内容。
Ticket #08中提出了对投资组合工具pyfolio的集成。
鉴于zipline和pyfolio之间的紧密集成,该教程的初学者认为这很难,但是其实pyfolio可以用于其他示例。
一部分用例已经集成到backtrader中:
只需一个主PyFolio Analyzer和3个简易的子Analyzer,加上一个pyfolio方法,以及它的依赖库pandas。
首先更新所有依赖项:
在类Unix环境下,时间完全取决于C编译器。 在Windows下,甚至安装了特定的Microsoft编译器(在本例中为Python 2.7)可能会失败。 但是:一个知名站点收集了Windows的最新软件包。 如果需要,请访问它: http://www.lfd.uci.edu/~gohlke/pythonlibs/
如果测试不通过,集成将无法完成,这就是为什么通常样例都选择最新的。
该示例使用random.randint决定何时买入/卖出,这只是一个简单能跑起来的例子:
$ ./pyfoliotest.py --printout --no-pyfolio --plot
输出:
Len,Datetime,Open,High,Low,Close,Volume,OpenInterest
0001,2005-01-03T23:59:59,38.36,38.90,37.65,38.18,25482800.00,0.00
买入 1000 @%23.58
0002,2005-01-04T23:59:59,38.45,38.54,36.46,36.58,26625300.00,0.00
买入 1000 @%36.58
卖出 500 @%22.47
0003,2005-01-05T23:59:59,36.69,36.98,36.06,36.13,18469100.00,0.00
...
卖出 500 @%37.51
0502,2006-12-28T23:59:59,25.62,25.72,25.30,25.36,11908400.00,0.00
0503,2006-12-29T23:59:59,25.42,25.82,25.33,25.54,16297800.00,0.00
卖出 250 @%17.14
卖出 250 @%37.01
在2年的默认时间周期内,随机选择了3个数据和几个买入和卖出操作。
当在Jupyter Notebook内运行(包括内联绘图)时,pyfolio效果很好。
注意:runstrat作为参数与此处[]一起使用,则跳过传递的参数,使用默认参数运行。
%matplotlib inline
from __future__ import (absolute_import, division, print_function,
unicode_literals)
import argparse
import datetime
import random
import backtrader as bt
class St(bt.Strategy):
params = (
('printout', False),
('stake', 1000),
)
def __init__(self):
pass
def start(self):
if self.p.printout:
txtfields = list()
txtfields.append('Len')
txtfields.append('Datetime')
txtfields.append('Open')
txtfields.append('High')
txtfields.append('Low')
txtfields.append('Close')
txtfields.append('Volume')
txtfields.append('OpenInterest')
print(','.join(txtfields))
def next(self):
if self.p.printout:
# Print only 1st data ... is just a check that things are running
txtfields = list()
txtfields.append('%04d' % len(self))
txtfields.append(self.data.datetime.datetime(0).isoformat())
txtfields.append('%.2f' % self.data0.open[0])
txtfields.append('%.2f' % self.data0.high[0])
txtfields.append('%.2f' % self.data0.low[0])
txtfields.append('%.2f' % self.data0.close[0])
txtfields.append('%.2f' % self.data0.volume[0])
txtfields.append('%.2f' % self.data0.openinterest[0])
print(','.join(txtfields))
# Data 0
for data in self.datas:
toss = random.randint(1, 10)
curpos = self.getposition(data)
if curpos.size:
if toss > 5:
size = curpos.size // 2
self.sell(data=data, size=size)
if self.p.printout:
print('SELL {} @%{}'.format(size, data.close[0]))
elif toss < 5:
self.buy(data=data, size=self.p.stake)
if self.p.printout:
print('BUY {} @%{}'.format(self.p.stake, data.close[0]))
def runstrat(args=None):
args = parse_args(args)
cerebro = bt.Cerebro()
cerebro.broker.set_cash(args.cash)
dkwargs = dict()
if args.fromdate:
fromdate = datetime.datetime.strptime(args.fromdate, '%Y-%m-%d')
dkwargs['fromdate'] = fromdate
if args.todate:
todate = datetime.datetime.strptime(args.todate, '%Y-%m-%d')
dkwargs['todate'] = todate
data0 = bt.feeds.BacktraderCSVData(dataname=args.data0, **dkwargs)
cerebro.adddata(data0, name='Data0')
data1 = bt.feeds.BacktraderCSVData(dataname=args.data1, **dkwargs)
cerebro.adddata(data1, name='Data1')
data2 = bt.feeds.BacktraderCSVData(dataname=args.data2, **dkwargs)
cerebro.adddata(data2, name='Data2')
cerebro.addstrategy(St, printout=args.printout)
if not args.no_pyfolio:
cerebro.addanalyzer(bt.analyzers.PyFolio, _name='pyfolio')
results = cerebro.run()
if not args.no_pyfolio:
strat = results[0]
pyfoliozer = strat.analyzers.getbyname('pyfolio')
returns, positions, transactions, gross_lev = pyfoliozer.get_pf_items()
if args.printout:
print('-- RETURNS')
print(returns)
print('-- POSITIONS')
print(positions)
print('-- TRANSACTIONS')
print(transactions)
print('-- GROSS LEVERAGE')
print(gross_lev)
import pyfolio as pf
pf.create_full_tear_sheet(
returns,
positions=positions,
transactions=transactions,
live_start_date='2005-05-01')
if args.plot:
cerebro.plot(style=args.plot_style)
def parse_args(args=None):
parser = argparse.ArgumentParser(
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
description='Sample for pivot point and cross plotting')
parser.add_argument('--data0', required=False,
default='yhoo-1996-2015.txt',
help='Data to be read in')
parser.add_argument('--data1', required=False,
default='orcl-1995-2014.txt',
help='Data to be read in')
parser.add_argument('--data2', required=False,
default='nvda-1999-2014.txt',
help='Data to be read in')
parser.add_argument('--fromdate', required=False,
default='2005-01-01',
help='Starting date in YYYY-MM-DD format')
parser.add_argument('--todate', required=False,
default='2006-12-31',
help='Ending date in YYYY-MM-DD format')
parser.add_argument('--printout', required=False, action='store_true',
help=('Print data lines'))
parser.add_argument('--cash', required=False, action='store',
type=float, default=50000,
help=('Cash to start with'))
parser.add_argument('--plot', required=False, action='store_true',
help=('Plot the result'))
parser.add_argument('--plot-style', required=False, action='store',
default='bar', choices=['bar', 'candle', 'line'],
help=('Plot style'))
parser.add_argument('--no-pyfolio', required=False, action='store_true',
help=('Do not do pyfolio things'))
import sys
aargs = args if args is not None else sys.argv[1:]
return parser.parse_args(aargs)
runstrat([])
Start date | 2005-01-03 | |||
---|---|---|---|---|
End date | 2006-12-29 | |||
In-sample months | 3 | |||
Out-of-sample months | 20 | |||
All | In-sample | Out-of-sample | ||
Annual return | -1.7% | -10.0% | 0.0% | |
Cumulative returns | -3.3% | -3.4% | 0.0% | |
Annual volatility | 2.8% | 6.9% | 0.0% | |
Sharpe ratio | -0.60 | -1.49 | 0.54 | |
Calmar ratio | -0.39 | -2.31 | 0.48 | |
Stability | 0.00 | 0.00 | 0.37 | |
Max drawdown | -4.3% | -4.3% | -0.0% | |
Omega ratio | 0.44 | 0.41 | 1.10 | |
Sortino ratio | -0.61 | -1.54 | 0.73 | |
Skew | -18.98 | -7.52 | -0.93 | |
Kurtosis | 403.13 | 61.80 | 5.91 | |
Tail ratio | 1.17 | 0.60 | 0.93 | |
Daily value at risk | -0.4% | -0.9% | -0.0% | |
Gross leverage | 0.00 | 0.01 | 0.00 | |
Daily turnover | inf% | inf% | 0.0% |
/opt/conda/lib/python3.6/site-packages/numpy/core/fromnumeric.py:56: FutureWarning:
The current behaviour of 'Series.argmin' is deprecated, use 'idxmin'
instead.
The behavior of 'argmin' will be corrected to return the positional
minimum in the future. For now, use 'series.values.argmin' or
'np.argmin(np.array(values))' to get the position of the minimum
row.
return getattr(obj, method)(*args, **kwds)
Worst drawdown periods | Net drawdown in % | Peak date | Valley date | Recovery date | Duration |
---|---|---|---|---|---|
0 | 4.34 | 2005-01-03 | 2005-01-06 | NaT | NaN |
1 | 0.00 | 2005-01-03 | 2005-01-03 | 2005-01-03 | 1 |
2 | 0.00 | 2005-01-03 | 2005-01-03 | 2005-01-03 | 1 |
3 | 0.00 | 2005-01-03 | 2005-01-03 | 2005-01-03 | 1 |
4 | 0.00 | 2005-01-03 | 2005-01-03 | 2005-01-03 | 1 |
/opt/conda/lib/python3.6/site-packages/numpy/core/fromnumeric.py:56: FutureWarning:
The current behaviour of 'Series.argmin' is deprecated, use 'idxmin'
instead.
The behavior of 'argmin' will be corrected to return the positional
minimum in the future. For now, use 'series.values.argmin' or
'np.argmin(np.array(values))' to get the position of the minimum
row.
return getattr(obj, method)(*args, **kwds)
Stress Events | mean | min | max |
---|---|---|---|
Low Volatility Bull Market | -0.01% | -3.74% | 0.65% |
Top 10 long positions of all time | max |
---|---|
Data2 | 16.53% |
Top 10 short positions of all time | max |
---|
Top 10 positions of all time | max |
---|---|
Data2 | 16.53% |
/opt/conda/lib/python3.6/site-packages/numpy/lib/function_base.py:3405: RuntimeWarning: All-NaN slice encountered
r = func(a, **kwargs)
/opt/conda/lib/python3.6/site-packages/pyfolio/tears.py:779: UserWarning: Unable to generate turnover plot.
warnings.warn('Unable to generate turnover plot.', UserWarning)
backtrader内部执行策略,主要处理交易数据和指标。 交易数据被添加到 Cerebro实例中,并最终成为策略输入的一部分(被解析并做为实力的
属性使用),而指标则由策略本身声明和管理。到目前为止,所有backtrader样本图都绘制了3样东西:
观察者存在于子模块backtrader.observers中。 Cerebro有参数支持自动添加(或不添加)到策略中:
import backtrader as bt
...
cerebro = bt.Cerebro() #默认参数: stdstats=True
cerebro.addobserver(bt.observers.Broker)
cerebro.addobserver(bt.observers.Trades)
cerebro.addobserver(bt.observers.BuySell)
我们来看看带有这三个默认观察者的常规图表(即使没有发出任何订单,因此也没有交易发生,现金和投资组合价值也没有变化)
import backtrader as bt import backtrader.feeds as btfeeds
if name == 'main': cerebro = bt.Cerebro(stdstats=False) cerebro.addstrategy(bt.Strategy) data =
bt.feeds.BacktraderCSVData(dataname='../../datas/2006-day-001.txt') cerebro.adddata(data)
cerebro.run()
cerebro.plot()
![](http://stoi.jusiu.com/bt/observers-default.png)
现在,让我们在创建Cerebro实例时将stdstats的值更改为False(也可以在调用run时完成):
```python
cerebro = bt.Cerebro(stdstats=False)
图表现在有所不同。