商务网站建设包含了,新化网站建设,asp.net网站开发与应用,盘石 网站建设本文档参考backtrader官方文档#xff0c;是官方文档的完整中文翻译#xff0c;可作为backtrader中文教程、backtrader中文参考手册、backtrader中文开发手册、backtrader入门资料使用。 快速入门章节目录 快速入门使用平台从0到100#xff1a;一步一步的演示基本设置设置现… 本文档参考backtrader官方文档是官方文档的完整中文翻译可作为backtrader中文教程、backtrader中文参考手册、backtrader中文开发手册、backtrader入门资料使用。 快速入门章节目录 快速入门使用平台从0到100一步一步的演示基本设置设置现金添加一个数据源我们的第一个策略给策略添加执行逻辑不仅要买...还要卖券商说:打钱!自定义策略参数添加指标可视化检查绘图让我们优化结论 快速入门
使用平台
让我们通过一系列的示例从几乎空白到完全成熟的策略来运行但在此之前让我们大致解释一下使用backtrader时的两个基本概念。 Lines线 数据源、指标和策略都有线。 一条线是一系列点的连续性这些点连接在一起形成这条线。当谈论市场时数据源通常每天有以下一组点 Open, High, Low, Close, Volume, OpenInterest 开盘价、最高价、最低价、收盘价、成交量、持仓量 时间序列中的开盘价是一条线。因此数据源通常有6条线。 如果我们还考虑DateTime这是单个点的实际参考我们可以计算出7条线。 索引0的用法 在访问线中的值时使用索引0访问当前值 last输出值使用*-1访问。这符合Python可迭代对象的惯例线可以迭代因此是可迭代的其中索引-1*用于访问可迭代/数组的last项。 在我们的情况下访问的是最后一个输出值。 因此索引0紧随*-1*之后用于访问线中的当前时刻。
有了这个想法如果我们想在初始化期间创建一个简单移动平均线的策略 self.sma SimpleMovingAverage(.....)访问此移动平均线的当前值的最简单和最简单的方法是 av self.sma[0]无需知道已处理了多少个bar/分钟/天/月因为0是当前时刻的唯一标识。
按照Python的传统使用*-1*访问last输出值 previous_value self.sma[-1]当然可以使用-2、-3等访问早期的输出值。
从0到100一步一步的演示
基本设置
让我们开始吧。
from __future__ import (absolute_import, division, print_function,unicode_literals)
# 导入Backtrader
import backtrader as btif __name__ __main__:# 创建一个Cerebro引擎实例cerebro bt.Cerebro()# 打印初始资金print(Starting Portfolio Value: %.2f % cerebro.broker.getvalue())# 执行回测cerebro.run()# 打印最终资金print(Final Portfolio Value: %.2f % cerebro.broker.getvalue())上面示例代码实现虽然什么也没做但是流程走完了
导入Backtrader实例化Cerebro引擎打印初始资金执行回测打印最终资金
代码的执行结果(小白注意不用拷贝执行这是运行后得到的内容
Starting Portfolio Value: 10000.00
Final Portfolio Value: 10000.00在这个例子中 导入了backtrader 实例化了Cerebro引擎 生成的cerebro实例被告知run循环数据 并打印出了结果
虽然看起来不起眼但让我们明确指出一些东西
Cerebro引擎在后台创建了一个broker实例该实例已经有一些现金可以开始交易
这种幕后经纪人实例化是平台中的一个常见特征以简化用户的生活。如果用户没有设置经纪人则会放置一个默认经纪人。
而10K的测试货币量是回测常用的一个货币价值。
设置现金
在金融世界中只有输家才从10k开始。让我们改变现金并再次运行示例。
from __future__ import (absolute_import, division, print_function,unicode_literals)import backtrader as btif __name__ __main__:cerebro bt.Cerebro()cerebro.broker.setcash(100000.0)print(Starting Portfolio Value: %.2f % cerebro.broker.getvalue())cerebro.run()print(Final Portfolio Value: %.2f % cerebro.broker.getvalue())执行后的输出为: Starting Portfolio Value: 1000000.00Final Portfolio Value: 1000000.00本节任务完成让我们提升任务的难度。
添加一个数据源
有现金很有趣但所有这一切的目的是让策略在我们提供的数据源资产上自动操作而不需要动手。
因此…没有数据源-没有乐趣。让我们将其添加到不断增长的示例中。
from __future__ import (absolute_import, division, print_function,unicode_literals)import datetime # 用于日期时间对象
import os.path # 用于管理路径
import sys # 用于查找脚本名称在argv [0]中# 导入backtrader平台
import backtrader as btif __name__ __main__:# 创建cerebro实体cerebro bt.Cerebro()# 数据在样本的子文件夹中。需要找到脚本所在的位置# 因为它可以从任何地方调用modpath os.path.dirname(os.path.abspath(sys.argv[0]))datapath os.path.join(modpath, ../datas/orcl-1995-2014.txt)# 创建数据源data bt.feeds.YahooFinanceCSVData(datanamedatapath,# 不传递此日期之前的值fromdatedatetime.datetime(2000, 1, 1),# 不传递此日期之后的值todatedatetime.datetime(2000, 12, 31),reverseFalse)# 将数据源添加到Cerebrocerebro.adddata(data)# 设置我们所需的现金起始值cerebro.broker.setcash(100000.0)# 打印出起始条件print(起始投资组合价值%.2f % cerebro.broker.getvalue())# 运行所有cerebro.run()# 打印出最终结果print(最终投资组合价值%.2f % cerebro.broker.getvalue())执行后的输出为:
起始投资组合价值100000.00
最终投资组合价值100000.00模板代码的数量略有增加因为我们添加了 找出我们的示例脚本所在的位置以便能够定位示例数据源文件 有datetime对象以过滤我们将要操作的数据源中的数据
除此之外Data Feed被创建并添加到cerebro中。
输出没有改变如果它改变了那将是一个奇迹。 注意Yahoo Online以日期降序发送CSV数据这不是标准约定。reversedTrue参数考虑到CSV数据在文件中已经被反转并具有标准预期的日期升序。 我们的第一个策略
现金在broker中Data Feed也在那里。我们马上就可以跑生意了。
让我们将策略放入方程式中并打印每天bar的Close价格。
DataSeriesData Feeds中的基础类对象具有访问众所周知的OHLCOpen High Low Close每日值的别名。这应该简化我们的打印逻辑的创建。
# 导入所需模块
from __future__ import (absolute_import, division, print_function, unicode_literals)
import datetime # 日期时间模块
import os.path # 路径模块
import sys # 系统模块# 导入backtrader平台
import backtrader as bt# 创建策略
class TestStrategy(bt.Strategy):def log(self, txt, dtNone): 日志函数 dt dt or self.datas[0].datetime.date(0)print(%s, %s % (dt.isoformat(), txt))def __init__(self):# 保留对数据序列中close线的引用self.dataclose self.datas[0].closedef next(self):# 记录数据序列的收盘价self.log(收盘价, %.2f % self.dataclose[0])if __name__ __main__:# 创建cerebro实体cerebro bt.Cerebro()# 添加策略cerebro.addstrategy(TestStrategy)# 数据在样本的子文件夹中。需要找到脚本所在的位置因为它可以从任何地方调用modpath os.path.dirname(os.path.abspath(sys.argv[0]))datapath os.path.join(modpath, ../datas/orcl-1995-2014.txt)# 创建数据源data bt.feeds.YahooFinanceCSVData(datanamedatapath,# 不传递此日期之前的值fromdatedatetime.datetime(2000, 1, 1),# 不传递此日期之前的值todatedatetime.datetime(2000, 12, 31),reverseFalse)# 将数据源添加到Cerebrocerebro.adddata(data)# 设置初始资金cerebro.broker.setcash(100000.0)# 打印初始条件print(初始资产价值: %.2f % cerebro.broker.getvalue())# 运行策略cerebro.run()# 打印最终结果print(最终资产价值: %.2f % cerebro.broker.getvalue())
执行后的输出为::
初始资产价值: 100000.00
2000-01-03, 收盘价, 26.27
2000-01-04, 收盘价, 23.95.........
2000-12-28, 收盘价, 27.63
2000-12-29, 收盘价, 25.85
最终资产价值: 100000.00有人说股票市场是冒险的生意但似乎并非如此。
让我们解释一些神奇的事 在调用__init__时策略已经有了一个存在于平台中的数据列表 这是一个标准的Python的list可以按照它们插入的顺序访问数据。 列表中的第一个数据self.datas[0]是用于交易操作的默认数据并且为了保持所有策略元素同步它是系统时钟 self.dataclose self.datas[0].close保留对close line的引用。只需要一个间接级别即可访问关闭值。 策略next方法将在系统时钟self.datas[0]的每个柱上调用。这是真实的直到其他事情进入比如indicators它需要一些柱才能开始产生输出。稍后再说。
给策略添加执行逻辑
让我们尝试一些疯狂的想法通过查看一些图表
如果价格连续下跌3个会话… 买买买!!!
from __future__ import (absolute_import, division, print_function,unicode_literals)import datetime # 导入日期时间库
import os.path # 导入路径管理库
import sys # 导入系统库# 导入backtrader平台
import backtrader as bt# 创建策略
class TestStrategy(bt.Strategy):def log(self, txt, dtNone): 日志记录函数dt dt or self.datas[0].datetime.date(0)print(%s, %s % (dt.isoformat(), txt))def __init__(self):# 保留对数据序列中close线的引用self.dataclose self.datas[0].closedef 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(BUY CREATE, %.2f % self.dataclose[0])self.buy()if __name__ __main__:# 创建cerebro实体cerebro bt.Cerebro()# 添加策略cerebro.addstrategy(TestStrategy)# 数据在样本的子文件夹中。需要找到脚本所在的位置# 因为它可以从任何地方调用modpath os.path.dirname(os.path.abspath(sys.argv[0]))datapath os.path.join(modpath, ../datas/orcl-1995-2014.txt)# 创建数据源data bt.feeds.YahooFinanceCSVData(datanamedatapath,# 不要传递此日期之前的值fromdatedatetime.datetime(2000, 1, 1),# 不要传递此日期之前的值todatedatetime.datetime(2000, 12, 31),# 不要传递此日期之后的值reverseFalse)# 将数据源添加到Cerebrocerebro.adddata(data)# 设置我们的期望现金起始值cerebro.broker.setcash(100000.0)# 打印出起始条件print(Starting Portfolio Value: %.2f % cerebro.broker.getvalue())# 运行策略cerebro.run()# 打印出最终结果print(Final Portfolio Value: %.2f % cerebro.broker.getvalue())执行后的输出为
Starting Portfolio Value: 100000.00
2000-01-03, Close, 26.27
2000-01-04, Close, 23.95
2000-01-05, Close, 22.68
2000-01-05, BUY CREATE, 22.68
2000-01-06, Close, 21.35
2000-01-06, BUY CREATE, 21.35
...
...
...
2000-12-20, BUY CREATE, 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, BUY CREATE, 27.30
2000-12-28, Close, 27.63
2000-12-29, Close, 25.85
Final Portfolio Value: 99740.45发出了几个买入订单我们的组合价值减少了。显然缺少了一些重要的事情。 订单已创建但不知道是否执行何时执行以及以什么价格执行。 下一个示例将在此基础上构建通过监听订单状态的通知来解决这个问题。
好奇的读者可能会问买了多少股票买了哪些资产以及如何执行订单。在可能的情况下在这种情况下平台填补了这些空白
self.datas[0]主数据即系统时钟是操作买卖的目标资产如果没有指定其他资产的话即默认操作datas[0])买卖股份数量由position sizer在幕后提供使用固定股份即默认值1。它将在稍后的示例中进行修改订单是市价执行的。经纪人在前面的示例中显示使用下一个条的开盘价执行此操作因为那是当前正在检查的条下一个tick。订单迄今为止没有任何佣金稍后会详细介绍
不仅要买…还要卖
在了解如何进入市场多头之后需要一个退出概念即给策略添加从市场退出的逻辑卖股。
幸运的是Strategy对象提供了对默认数据源的position属性的访问方法buy和sell返回已创建尚未执行的订单订单状态的更改将通过notify方法通知策略
退出概念将是一个简单的概念 在5个bar第6个bar之后退出无论好坏 请注意1个bar的bar可以表示1分钟1小时1天1周或其他任何长度的时间单位。 尽管我们知道数据源是每日的但策略不会对此做出任何假设。
此外为了简化
仅在尚未进入市场时才允许购买订单 注意next方法没有传递bar索引因此似乎不清楚如何理解何时可能已经过去了5个bar但是这已经以pythonic的方式建模在对象上调用len它将告诉您其lines的长度。 只需在操作中记录保存在变量中已经迭代过的bar的长度然后查看当前长度是否相距5个bar。 from __future__ import (absolute_import, division, print_function,unicode_literals)import datetime # 导入datetime模块
import os.path # 导入os.path模块
import sys # 导入sys模块# 导入backtrader平台
import backtrader as bt# 创建策略
class TestStrategy(bt.Strategy):def log(self, txt, dtNone): 日志记录函数dt dt or self.datas[0].datetime.date(0)print(%s, %s % (dt.isoformat(), txt))def __init__(self):# 保留对数据序列中close线的引用self.dataclose self.datas[0].close# 跟踪待处理订单self.order Nonedef notify_order(self, order):if order.status in [order.Submitted, order.Accepted]:# 买入/卖出订单已提交/已接受 - 无需操作return# 检查订单是否已完成# 注意如果现金不足经纪人可能会拒绝订单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 Nonedef 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:# 已经在市场中...我们可能会卖出if len(self) (self.bar_executed 5):# 卖出self.log(卖出创建, %.2f % self.dataclose[0])# 记录已创建的订单以避免第二个订单self.order self.sell()if __name__ __main__:# 创建cerebro实体cerebro bt.Cerebro()# 添加策略cerebro.addstrategy(TestStrategy)# 数据在样本的子文件夹中。需要找到脚本所在的位置# 因为它可以从任何地方调用modpath os.path.dirname(os.path.abspath(sys.argv[0]))datapath os.path.join(modpath, ../datas/orcl-1995-2014.txt)# 创建数据源data bt.feeds.YahooFinanceCSVData(datanamedatapath,# 不要传递此日期之前的值fromdatedatetime.datetime(2000, 1, 1),# 不传递此日期之后的值todatedatetime.datetime(2000, 12, 31),reverseFalse)# 将数据源添加到Cerebrocerebro.adddata(data)# 设置我们的期望现金起始值cerebro.broker.setcash(100000.0)# 打印出起始条件print(Starting Portfolio Value: %.2f % cerebro.broker.getvalue())# 运行整个策略cerebro.run()# 打印出最终结果print(Final Portfolio Value: %.2f % cerebro.broker.getvalue())
执行后的输出为(打印日志是源文档未翻译但代码中打印内容已翻译
Starting Portfolio Value: 100000.00
2000-01-03, Close, 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-12-20, Close, 25.35
2000-12-20, 买入创建, 25.35
2000-12-21, 买入已执行, 24.74
2000-12-21, Close, 26.24
...
2000-12-28, Close, 27.63
2000-12-29, Close, 25.85
2000-12-29, 卖出创建, 25.85
Final Portfolio Value: 100017.52系统赚了钱…一定有什么问题
券商说:打钱!
这笔钱叫做佣金。
让我们为每次操作买入和卖出添加合理的*0.1%*佣金率是的经纪人很贪婪……
只需要一行代码
# 0.1% ... divide by 100 to remove the %
cerebro.broker.setcommission(commission0.001)熟悉平台后我们想在买卖周期之后查看有无佣金的利润或损失。
from __future__ import (absolute_import, division, print_function,unicode_literals)import datetime # For datetime objects
import os.path # To manage paths
import sys # To find out the script name (in argv[0])# Import the backtrader platform
import backtrader as bt# Create a Stratey
class TestStrategy(bt.Strategy):def log(self, txt, dtNone): Logging function fot this strategydt dt or self.datas[0].datetime.date(0)print(%s, %s % (dt.isoformat(), txt))def __init__(self):# Keep a reference to the close line in the data[0] dataseriesself.dataclose self.datas[0].close# To keep track of pending orders and buy price/commissionself.order Noneself.buyprice Noneself.buycomm Nonedef notify_order(self, order):if order.status in [order.Submitted, order.Accepted]:# Buy/Sell order submitted/accepted to/by broker - Nothing to doreturn# Check if an order has been completed# Attention: broker could reject order if not enough cashif order.status in [order.Completed]:if order.isbuy():self.log(BUY EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f %(order.executed.price,order.executed.value,order.executed.comm))self.buyprice order.executed.priceself.buycomm order.executed.commelse: # Sellself.log(SELL EXECUTED, Price: %.2f, Cost: %.2f, Comm %.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(Order Canceled/Margin/Rejected)self.order Nonedef notify_trade(self, trade):if not trade.isclosed:returnself.log(OPERATION PROFIT, GROSS %.2f, NET %.2f %(trade.pnl, trade.pnlcomm))def next(self):# Simply log the closing price of the series from the referenceself.log(Close, %.2f % self.dataclose[0])# Check if an order is pending ... if yes, we cannot send a 2nd oneif self.order:return# Check if we are in the marketif not self.position:# Not yet ... we MIGHT BUY if ...if self.dataclose[0] self.dataclose[-1]:# current close less than previous closeif self.dataclose[-1] self.dataclose[-2]:# previous close less than the previous close# BUY, BUY, BUY!!! (with default parameters)self.log(BUY CREATE, %.2f % self.dataclose[0])# Keep track of the created order to avoid a 2nd orderself.order self.buy()else:# Already in the market ... we might sellif len(self) (self.bar_executed 5):# SELL, SELL, SELL!!! (with all possible default parameters)self.log(SELL CREATE, %.2f % self.dataclose[0])# Keep track of the created order to avoid a 2nd orderself.order self.sell()if __name__ __main__:# Create a cerebro entitycerebro bt.Cerebro()# Add a strategycerebro.addstrategy(TestStrategy)# Datas are in a subfolder of the samples. Need to find where the script is# because it could have been called from anywheremodpath os.path.dirname(os.path.abspath(sys.argv[0]))datapath os.path.join(modpath, ../datas/orcl-1995-2014.txt)# Create a Data Feeddata bt.feeds.YahooFinanceCSVData(datanamedatapath,# Do not pass values before this datefromdatedatetime.datetime(2000, 1, 1),# Do not pass values before this datetodatedatetime.datetime(2000, 12, 31),# Do not pass values after this datereverseFalse)# Add the Data Feed to Cerebrocerebro.adddata(data)# Set our desired cash startcerebro.broker.setcash(100000.0)# Set the commission - 0.1% ... divide by 100 to remove the %cerebro.broker.setcommission(commission0.001)# Print out the starting conditionsprint(Starting Portfolio Value: %.2f % cerebro.broker.getvalue())# Run over everythingcerebro.run()# Print out the final resultprint(Final Portfolio Value: %.2f % cerebro.broker.getvalue())执行后的输出为
Starting Portfolio Value: 100000.00
2000-01-03, Close, 26.27
2000-01-04, Close, 23.95
2000-01-05, Close, 22.68
2000-01-05, BUY CREATE, 22.68
2000-01-06, BUY EXECUTED, Price: 22.27, Cost: 22.27, Comm 0.02
...
...
...
2000-12-21, BUY EXECUTED, Price: 24.74, Cost: 24.74, Comm 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, SELL CREATE, 25.85
Final Portfolio Value: 100016.06上帝保佑系统仍然赚了钱。
在继续之前让我们通过过滤OPERATION PROFIT行来注意一些事情
2000-01-14, OPERATION PROFIT, GROSS 1.97, NET 1.92
2000-02-07, OPERATION PROFIT, GROSS 3.48, NET 3.43
2000-02-28, OPERATION PROFIT, GROSS 4.23, NET 4.17
2000-03-13, OPERATION PROFIT, GROSS 3.28, NET 3.21
2000-03-22, OPERATION PROFIT, GROSS -0.39, NET -0.46
2000-04-07, OPERATION PROFIT, GROSS 2.31, NET 2.24
2000-04-20, OPERATION PROFIT, GROSS -1.83, NET -1.90
2000-05-02, OPERATION PROFIT, GROSS 5.15, NET 5.08
2000-05-11, OPERATION PROFIT, GROSS -3.53, NET -3.59
2000-05-30, OPERATION PROFIT, GROSS -1.39, NET -1.45
2000-07-05, OPERATION PROFIT, GROSS -1.53, NET -1.60
2000-07-14, OPERATION PROFIT, GROSS 1.97, NET 1.90
2000-07-28, OPERATION PROFIT, GROSS 0.14, NET 0.07
2000-08-08, OPERATION PROFIT, GROSS 4.11, NET 4.04
2000-08-21, OPERATION PROFIT, GROSS 0.97, NET 0.90
2000-09-15, OPERATION PROFIT, GROSS -4.00, NET -4.08
2000-09-27, OPERATION PROFIT, GROSS 1.22, NET 1.15
2000-10-13, OPERATION PROFIT, GROSS -2.81, NET -2.87
2000-10-26, OPERATION PROFIT, GROSS 2.84, NET 2.78
2000-11-06, OPERATION PROFIT, GROSS -3.39, NET -3.45
2000-11-16, OPERATION PROFIT, GROSS 1.22, NET 1.17
2000-12-01, OPERATION PROFIT, GROSS 2.45, NET 2.41
2000-12-18, OPERATION PROFIT, GROSS -0.06, NET -0.11将净利润相加最终数字为
15.83备注原文数字实际执行代码结果和文档有差异此处示意但系统在最后说了以下内容
2000-12-29, SELL CREATE, 25.85
Final Portfolio Value: 100016.06显然15.83不是16.06。没有任何错误。15.83的净利润已经到手了。
不幸的是或者幸运的是为了更好地理解平台在数据源最后一天有一个未平仓头寸。即使已经发送了SELL操作……它还没有被执行。
经纪人计算的最终组合价值考虑了2000-12-29的收盘价格。实际执行价格将在下一个交易日即2001-1-3设置。将数据源扩展到考虑这一天后输出为
2000-12-29, SELL CREATE, 25.85
2001-01-02, SELL EXECUTED, Price: 26.30, Cost: 24.74, Comm 0.03
2001-01-02, OPERATION PROFIT, GROSS 1.56, NET 1.51
2001-01-02, Close, 23.46
2001-01-02, BUY CREATE, 23.46
Final Portfolio Value: 100016.48现在将前面的净利润添加到已完成操作的净利润中
15.83 1.59 17.42备注原文数字实际执行代码结果和文档有差异此处示意忽略print语句中的舍入误差这是初始100000货币单位以上的额外组合。
自定义策略参数
在策略中硬编码一些值并没有什么实际意义而且很难轻松更改它们。参数很有用。
定义参数很容易看起来像这样
params ((myparam, 27), (exitbars, 5),)
这是一个标准的Python元组里面有一些元组以下可能更吸引人
params ((myparam, 27),(exitbars, 5),
)无论哪种格式化策略都允许在将策略添加到Cerebro引擎时进行参数化
# Add a strategy
cerebro.addstrategy(TestStrategy, myparam20, exitbars7)注意下面的setsizing方法已弃用。这个内容是为了让任何人查看旧的源代码示例而保留的。源代码已经更新为使用 # 已经弃用
cerebro.addsizer(bt.sizers.FixedSize, stake10)可以阅读sizers相关章节查看更多。
在策略中使用参数很容易因为它们存储在params属性中。如果我们例如想要设置固定的股份我们可以在__init__期间将股份参数传递给position sizer
# Set the sizer stake from the params
self.sizer.setsizing(self.params.stake)我们也可以使用stake参数和self.params.stake作为值来调用buy和sell。
退出逻辑被修改
# Already in the market ... we might sell
if len(self) (self.bar_executed self.params.exitbars):考虑到所有这些示例的演变如下
from __future__ import (absolute_import, division, print_function,unicode_literals)import datetime # For datetime objects
import os.path # To manage paths
import sys # To find out the script name (in argv[0])# Import the backtrader platform
import backtrader as bt# Create a Stratey
class TestStrategy(bt.Strategy):params ((exitbars, 5),)def log(self, txt, dtNone): Logging function fot this strategydt dt or self.datas[0].datetime.date(0)print(%s, %s % (dt.isoformat(), txt))def __init__(self):# Keep a reference to the close line in the data[0] dataseriesself.dataclose self.datas[0].close# To keep track of pending orders and buy price/commissionself.order Noneself.buyprice Noneself.buycomm Nonedef notify_order(self, order):if order.status in [order.Submitted, order.Accepted]:# Buy/Sell order submitted/accepted to/by broker - Nothing to doreturn# Check if an order has been completed# Attention: broker could reject order if not enough cashif order.status in [order.Completed]:if order.isbuy():self.log(BUY EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f %(order.executed.price,order.executed.value,order.executed.comm))self.buyprice order.executed.priceself.buycomm order.executed.commelse: # Sellself.log(SELL EXECUTED, Price: %.2f, Cost: %.2f, Comm %.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(Order Canceled/Margin/Rejected)self.order Nonedef notify_trade(self, trade):if not trade.isclosed:returnself.log(OPERATION PROFIT, GROSS %.2f, NET %.2f %(trade.pnl, trade.pnlcomm))def next(self):# Simply log the closing price of the series from the referenceself.log(Close, %.2f % self.dataclose[0])# Check if an order is pending ... if yes, we cannot send a 2nd oneif self.order:return# Check if we are in the marketif not self.position:# Not yet ... we MIGHT BUY if ...if self.dataclose[0] self.dataclose[-1]:# current close less than previous closeif self.dataclose[-1] self.dataclose[-2]:# previous close less than the previous close# BUY, BUY, BUY!!! (with default parameters)self.log(BUY CREATE, %.2f % self.dataclose[0])# Keep track of the created order to avoid a 2nd orderself.order self.buy()else:# Already in the market ... we might sellif len(self) (self.bar_executed self.params.exitbars):# SELL, SELL, SELL!!! (with all possible default parameters)self.log(SELL CREATE, %.2f % self.dataclose[0])# Keep track of the created order to avoid a 2nd orderself.order self.sell()if __name__ __main__:# Create a cerebro entitycerebro bt.Cerebro()# Add a strategycerebro.addstrategy(TestStrategy)# Datas are in a subfolder of the samples. Need to find where the script is# because it could have been called from anywheremodpath os.path.dirname(os.path.abspath(sys.argv[0]))datapath os.path.join(modpath, ../datas/orcl-1995-2014.txt)# Create a Data Feeddata bt.feeds.YahooFinanceCSVData(datanamedatapath,# Do not pass values before this datefromdatedatetime.datetime(2000, 1, 1),# Do not pass values before this datetodatedatetime.datetime(2000, 12, 31),# Do not pass values after this datereverseFalse)# Add the Data Feed to Cerebrocerebro.adddata(data)# Set our desired cash startcerebro.broker.setcash(100000.0)# Add a FixedSize sizer according to the stakecerebro.addsizer(bt.sizers.FixedSize, stake10)# Set the commission - 0.1% ... divide by 100 to remove the %cerebro.broker.setcommission(commission0.001)# Print out the starting conditionsprint(Starting Portfolio Value: %.2f % cerebro.broker.getvalue())# Run over everythingcerebro.run()# Print out the final resultprint(Final Portfolio Value: %.2f % cerebro.broker.getvalue())执行后的输出为
Starting Portfolio Value: 100000.00
2000-01-03, Close, 26.27
2000-01-04, Close, 23.95
2000-01-05, Close, 22.68
2000-01-05, BUY CREATE, 22.68
2000-01-06, BUY EXECUTED, Price: 22.27, Cost: 222.70, Comm 0.22
...
...
...
2000-12-28, Close, 27.63
2000-12-29, Close, 25.85
2000-12-29, SELL CREATE, 25.85
Final Portfolio Value: 100160.58为了看到差异打印输出也已扩展以显示执行大小。
将股份乘以10后显然发生了以下情况利润和损失乘以10。而不是16.58剩余现金现在是165.80。
添加指标
指标可以简单理解为用于衡量市场趋势和价格变化的工具。指标可以是基于价格、成交量或其他市场数据的计算结果。
听说过指标后下一步任何人都会添加一个指标到策略中。毫无疑问它们一定比简单的*3个较低的收盘价*策略好得多。
受PyAlgoTrade中的一个示例启发使用简单移动平均线的策略。
如果收盘价大于平均值则以市价买入如果在市场上如果收盘价小于平均值则卖出市场上只允许有1个活动操作
大部分现有的代码可以保持不变。让我们在__init__中添加平均值并保留对它的引用: self.sma bt.indicators.MovingAverageSimple(self.datas[0], periodself.params.maperiod)当然进入和退出市场的逻辑将依赖于平均值。在代码中查找逻辑。 注意:: 起始现金将为1000货币单位以与PyAlgoTrade示例保持一致并且不会应用佣金 from __future__ import (absolute_import, division, print_function,unicode_literals)import datetime # For datetime objects
import os.path # To manage paths
import sys # To find out the script name (in argv[0])# Import the backtrader platform
import backtrader as bt# Create a Stratey
class TestStrategy(bt.Strategy):params ((maperiod, 15),)def log(self, txt, dtNone): Logging function fot this strategydt dt or self.datas[0].datetime.date(0)print(%s, %s % (dt.isoformat(), txt))def __init__(self):# Keep a reference to the close line in the data[0] dataseriesself.dataclose self.datas[0].close# To keep track of pending orders and buy price/commissionself.order Noneself.buyprice Noneself.buycomm None# Add a MovingAverageSimple indicatorself.sma bt.indicators.SimpleMovingAverage(self.datas[0], periodself.params.maperiod)def notify_order(self, order):if order.status in [order.Submitted, order.Accepted]:# Buy/Sell order submitted/accepted to/by broker - Nothing to doreturn# Check if an order has been completed# Attention: broker could reject order if not enough cashif order.status in [order.Completed]:if order.isbuy():self.log(BUY EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f %(order.executed.price,order.executed.value,order.executed.comm))self.buyprice order.executed.priceself.buycomm order.executed.commelse: # Sellself.log(SELL EXECUTED, Price: %.2f, Cost: %.2f, Comm %.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(Order Canceled/Margin/Rejected)self.order Nonedef notify_trade(self, trade):if not trade.isclosed:returnself.log(OPERATION PROFIT, GROSS %.2f, NET %.2f %(trade.pnl, trade.pnlcomm))def next(self):# Simply log the closing price of the series from the referenceself.log(Close, %.2f % self.dataclose[0])# Check if an order is pending ... if yes, we cannot send a 2nd oneif self.order:return# Check if we are in the marketif not self.position:# Not yet ... we MIGHT BUY if ...if self.dataclose[0] self.sma[0]:# BUY, BUY, BUY!!! (with all possible default parameters)self.log(BUY CREATE, %.2f % self.dataclose[0])# Keep track of the created order to avoid a 2nd orderself.order self.buy()else:if self.dataclose[0] self.sma[0]:# SELL, SELL, SELL!!! (with all possible default parameters)self.log(SELL CREATE, %.2f % self.dataclose[0])# Keep track of the created order to avoid a 2nd orderself.order self.sell()if __name__ __main__:# Create a cerebro entitycerebro bt.Cerebro()# Add a strategycerebro.addstrategy(TestStrategy)# Datas are in a subfolder of the samples. Need to find where the script is# because it could have been called from anywheremodpath os.path.dirname(os.path.abspath(sys.argv[0]))datapath os.path.join(modpath, ../datas/orcl-1995-2014.txt)# Create a Data Feeddata bt.feeds.YahooFinanceCSVData(datanamedatapath,# Do not pass values before this datefromdatedatetime.datetime(2000, 1, 1),# Do not pass values before this datetodatedatetime.datetime(2000, 12, 31),# Do not pass values after this datereverseFalse)# Add the Data Feed to Cerebrocerebro.adddata(data)# Set our desired cash startcerebro.broker.setcash(1000.0)# Add a FixedSize sizer according to the stakecerebro.addsizer(bt.sizers.FixedSize, stake10)# Set the commissioncerebro.broker.setcommission(commission0.0)# Print out the starting conditionsprint(Starting Portfolio Value: %.2f % cerebro.broker.getvalue())# Run over everythingcerebro.run()# Print out the final resultprint(Final Portfolio Value: %.2f % cerebro.broker.getvalue())现在在跳转到下一节之前请仔细查看日志中显示的第一个日期 它不再是2K年的第一个交易日2000-01-03。 它是2000-01-24 …
缺失的天数并不不是真的缺失。而是平台已适应新的情况 已将指标SimpleMovingAverage添加到策略中。 此指标需要X个条形图才能产生输出例如15 2000-01-24是第15个条形图出现的日期
backtrader平台假定策略已经有了指标有一个很好的理由在决策过程中使用它。如果指标尚未准备好并产生值则尝试做出决策是没有意义的。 当所有指标已经达到产生值所需的最小周期时next将首先被调用 在示例中只有一个指标但是策略可以有任意数量的指标。
执行后的输出为
Starting Portfolio Value: 1000.00
2000-01-24, Close, 24.10
2000-01-25, Close, 25.10
2000-01-25, BUY CREATE, 25.10
2000-01-26, BUY EXECUTED, Price: 25.24, Cost: 252.40, Comm 0.00
...
...
...
2000-12-21, OPERATION PROFIT, GROSS -19.40, NET -19.40
2000-12-21, Close, 26.24
2000-12-21, BUY CREATE, 26.24
2000-12-22, BUY EXECUTED, Price: 27.02, Cost: 270.20, Comm 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, SELL CREATE, 25.85
Final Portfolio Value: 975.60一个获胜的系统变成了一个失败的系统…而且没有佣金。很可能仅仅添加一个指标说明指标并不是万能药。 注意:在PyAlgoTrade中使用相同的逻辑和数据会产生略有不同的结果略微偏离。查看整个打印输出会发现一些操作不完全相同。罪魁祸首再次是通常的嫌疑人四舍五入。 PyAlgoTrade在将分裂的调整收盘价应用于数据源值时不会将其舍入到小数点后2位。
由backtrader提供的Yahoo数据源将在应用调整后将值向下舍入到2位小数。在打印值时一切似乎都是相同的但很明显有时第5位小数起作用。
向下舍入到2位小数似乎更为现实因为Marke Exchange仅允许每个资产有一定数量的小数位数通常为股票的2个小数位数 注意:Yahoo数据源从版本1.8.11.99开始允许指定是否进行舍入以及舍入到多少位小数) 可视化检查绘图
打印或记录系统在每个bar的实际执行情况是可以的但人类是视觉动物因此提供一个图表视图输出机制无疑是正确的。 注意:要绘制图形需要安装matplotlib 再次默认情况下绘图可帮助平台用户。绘图是一个仅有的1行操作
cerebro.plot()在调用cerebro.run()后位置肯定是在那里。
为了显示自动绘图功能和一些简单的自定义将执行以下操作
添加第二个移动平均线指数。默认情况下将其与数据一起绘制就像第一个一样。添加第三个移动平均线加权。自定义绘制在自己的图中即使不明智添加随机慢。默认情况下不更改。添加MACD。默认情况下不更改。添加RSI。默认情况下不更改。对RSI应用移动平均线简单。默认情况下不更改将与RSI一起绘制添加AverageTrueRange。更改默认值以避免绘制。
Strategy的init方法中添加的全部内容:
# Indicators for the plotting show
bt.indicators.ExponentialMovingAverage(self.datas[0], period25)
bt.indicators.WeightedMovingAverage(self.datas[0], period25).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, period10)
bt.indicators.ATR(self.datas[0]).plot False注意:即使indicators没有显式添加到策略的成员变量中例如self.sma MovingAverageSimple…它们也将自动注册到策略中并影响next的最小周期并成为绘图的一部分。 在示例中只有RSI被添加到临时变量rsi中其唯一目的是在其上创建一个MovingAverageSmoothed。
现在的例子
from __future__ import (absolute_import, division, print_function,unicode_literals)import datetime # For datetime objects
import os.path # To manage paths
import sys # To find out the script name (in argv[0])# Import the backtrader platform
import backtrader as bt# Create a Stratey
class TestStrategy(bt.Strategy):params ((maperiod, 15),)def log(self, txt, dtNone): Logging function fot this strategydt dt or self.datas[0].datetime.date(0)print(%s, %s % (dt.isoformat(), txt))def __init__(self):# Keep a reference to the close line in the data[0] dataseriesself.dataclose self.datas[0].close# To keep track of pending orders and buy price/commissionself.order Noneself.buyprice Noneself.buycomm None# Add a MovingAverageSimple indicatorself.sma bt.indicators.SimpleMovingAverage(self.datas[0], periodself.params.maperiod)# Indicators for the plotting showbt.indicators.ExponentialMovingAverage(self.datas[0], period25)bt.indicators.WeightedMovingAverage(self.datas[0], period25,subplotTrue)bt.indicators.StochasticSlow(self.datas[0])bt.indicators.MACDHisto(self.datas[0])rsi bt.indicators.RSI(self.datas[0])bt.indicators.SmoothedMovingAverage(rsi, period10)bt.indicators.ATR(self.datas[0], plotFalse)def notify_order(self, order):if order.status in [order.Submitted, order.Accepted]:# Buy/Sell order submitted/accepted to/by broker - Nothing to doreturn# Check if an order has been completed# Attention: broker could reject order if not enough cashif order.status in [order.Completed]:if order.isbuy():self.log(BUY EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f %(order.executed.price,order.executed.value,order.executed.comm))self.buyprice order.executed.priceself.buycomm order.executed.commelse: # Sellself.log(SELL EXECUTED, Price: %.2f, Cost: %.2f, Comm %.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(Order Canceled/Margin/Rejected)# Write down: no pending orderself.order Nonedef notify_trade(self, trade):if not trade.isclosed:returnself.log(OPERATION PROFIT, GROSS %.2f, NET %.2f %(trade.pnl, trade.pnlcomm))def next(self):# Simply log the closing price of the series from the referenceself.log(Close, %.2f % self.dataclose[0])# Check if an order is pending ... if yes, we cannot send a 2nd oneif self.order:return# Check if we are in the marketif not self.position:# Not yet ... we MIGHT BUY if ...if self.dataclose[0] self.sma[0]:# BUY, BUY, BUY!!! (with all possible default parameters)self.log(BUY CREATE, %.2f % self.dataclose[0])# Keep track of the created order to avoid a 2nd orderself.order self.buy()else:if self.dataclose[0] self.sma[0]:# SELL, SELL, SELL!!! (with all possible default parameters)self.log(SELL CREATE, %.2f % self.dataclose[0])# Keep track of the created order to avoid a 2nd orderself.order self.sell()if __name__ __main__:# Create a cerebro entitycerebro bt.Cerebro()# Add a strategycerebro.addstrategy(TestStrategy)# Datas are in a subfolder of the samples. Need to find where the script is# because it could have been called from anywheremodpath os.path.dirname(os.path.abspath(sys.argv[0]))datapath os.path.join(modpath, ../datas/orcl-1995-2014.txt)# Create a Data Feeddata bt.feeds.YahooFinanceCSVData(datanamedatapath,# Do not pass values before this datefromdatedatetime.datetime(2000, 1, 1),# Do not pass values before this datetodatedatetime.datetime(2000, 12, 31),# Do not pass values after this datereverseFalse)# Add the Data Feed to Cerebrocerebro.adddata(data)# Set our desired cash startcerebro.broker.setcash(1000.0)# Add a FixedSize sizer according to the stakecerebro.addsizer(bt.sizers.FixedSize, stake10)# Set the commissioncerebro.broker.setcommission(commission0.0)# Print out the starting conditionsprint(Starting Portfolio Value: %.2f % cerebro.broker.getvalue())# Run over everythingcerebro.run()# Print out the final resultprint(Final Portfolio Value: %.2f % cerebro.broker.getvalue())# Plot the resultcerebro.plot()执行后的输出为
Starting Portfolio Value: 1000.00
2000-02-18, Close, 26.05
2000-02-22, Close, 26.38
2000-02-22, BUY CREATE, 26.38
2000-02-23, BUY EXECUTED, Price: 26.77, Cost: 267.70, Comm 0.00
2000-02-23, Close, 28.05
...
...
...
2000-12-22, BUY EXECUTED, Price: 27.02, Cost: 270.20, Comm 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, SELL CREATE, 25.85
Final Portfolio Value: 982.30最终结果已更改即使逻辑没有。这是真的但逻辑没有应用于相同数量的bar。 注意:如前所述平台将在所有指标准备好生成值时首先调用next。在这个绘图示例中在图表中非常清晰MACD是最后一个完全准备好的指标所有3条线都产生输出。第一个BUY订单不再在2000年1月期间计划而是接近2000年2月底。 图表
让我们优化
许多交易书籍都说每个市场和每个交易的股票或商品或…都有不同的节奏。没有一种适合所有人的东西。
在绘图示例之前当策略开始使用指标时期间的默认值为15个bar。这是一个策略参数可以在优化中使用该参数的值并查看哪个更适合市场。 注意:有很多关于优化和相关利弊的文献。但是建议总是指向同一个方向不要过度优化。如果交易想法不合理则优化可能会产生仅对回测数据集有效的正面结果。 示例已修改为优化简单移动平均线的期间。为了清晰起见已删除与买入/卖出订单相关的任何输出
现在的例子
from __future__ import (absolute_import, division, print_function,unicode_literals)import datetime # For datetime objects
import os.path # To manage paths
import sys # To find out the script name (in argv[0])# Import the backtrader platform
import backtrader as bt# Create a Stratey
class TestStrategy(bt.Strategy):params ((maperiod, 15),(printlog, False),)def log(self, txt, dtNone, doprintFalse): Logging function fot this strategyif self.params.printlog or doprint:dt dt or self.datas[0].datetime.date(0)print(%s, %s % (dt.isoformat(), txt))def __init__(self):# Keep a reference to the close line in the data[0] dataseriesself.dataclose self.datas[0].close# To keep track of pending orders and buy price/commissionself.order Noneself.buyprice Noneself.buycomm None# Add a MovingAverageSimple indicatorself.sma bt.indicators.SimpleMovingAverage(self.datas[0], periodself.params.maperiod)def notify_order(self, order):if order.status in [order.Submitted, order.Accepted]:# Buy/Sell order submitted/accepted to/by broker - Nothing to doreturn# Check if an order has been completed# Attention: broker could reject order if not enough cashif order.status in [order.Completed]:if order.isbuy():self.log(BUY EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f %(order.executed.price,order.executed.value,order.executed.comm))self.buyprice order.executed.priceself.buycomm order.executed.commelse: # Sellself.log(SELL EXECUTED, Price: %.2f, Cost: %.2f, Comm %.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(Order Canceled/Margin/Rejected)# Write down: no pending orderself.order Nonedef notify_trade(self, trade):if not trade.isclosed:returnself.log(OPERATION PROFIT, GROSS %.2f, NET %.2f %(trade.pnl, trade.pnlcomm))def next(self):# Simply log the closing price of the series from the referenceself.log(Close, %.2f % self.dataclose[0])# Check if an order is pending ... if yes, we cannot send a 2nd oneif self.order:return# Check if we are in the marketif not self.position:# Not yet ... we MIGHT BUY if ...if self.dataclose[0] self.sma[0]:# BUY, BUY, BUY!!! (with all possible default parameters)self.log(BUY CREATE, %.2f % self.dataclose[0])# Keep track of the created order to avoid a 2nd orderself.order self.buy()else:if self.dataclose[0] self.sma[0]:# SELL, SELL, SELL!!! (with all possible default parameters)self.log(SELL CREATE, %.2f % self.dataclose[0])# Keep track of the created order to avoid a 2nd orderself.order self.sell()def stop(self):self.log((MA Period %2d) Ending Value %.2f %(self.params.maperiod, self.broker.getvalue()), doprintTrue)if __name__ __main__:# Create a cerebro entitycerebro bt.Cerebro()# Add a strategystrats cerebro.optstrategy(TestStrategy,maperiodrange(10, 31))# Datas are in a subfolder of the samples. Need to find where the script is# because it could have been called from anywheremodpath os.path.dirname(os.path.abspath(sys.argv[0]))datapath os.path.join(modpath, ../datas/orcl-1995-2014.txt)# Create a Data Feeddata bt.feeds.YahooFinanceCSVData(datanamedatapath,# Do not pass values before this datefromdatedatetime.datetime(2000, 1, 1),# Do not pass values before this datetodatedatetime.datetime(2000, 12, 31),# Do not pass values after this datereverseFalse)# Add the Data Feed to Cerebrocerebro.adddata(data)# Set our desired cash startcerebro.broker.setcash(1000.0)# Add a FixedSize sizer according to the stakecerebro.addsizer(bt.sizers.FixedSize, stake10)# Set the commissioncerebro.broker.setcommission(commission0.0)# Run over everythingcerebro.run(maxcpus1)不是调用addstrategy将策略类添加到Cerebro中而是调用optstrategy。而不是传递一个值而是传递一系列值。
添加了Strategy钩子之一stop方法当数据用尽并且回测结束时将调用该方法。它用于在经纪人的投资组合中打印最终净值之前在Cerebro中完成
系统将为范围内的每个值执行策略。将输出以下内容
2000-12-29, (MA Period 10) Ending Value 877.50
2000-12-29, (MA Period 11) Ending Value 878.70
2000-12-29, (MA Period 12) Ending Value 839.80
2000-12-29, (MA Period 13) Ending Value 899.90
2000-12-29, (MA Period 14) Ending Value 902.50
2000-12-29, (MA Period 15) Ending Value 975.60
2000-12-29, (MA Period 16) Ending Value 961.90
2000-12-29, (MA Period 17) Ending Value 952.60
2000-12-29, (MA Period 18) Ending Value 1011.00
2000-12-29, (MA Period 19) Ending Value 1039.40
2000-12-29, (MA Period 20) Ending Value 1073.20
2000-12-29, (MA Period 21) Ending Value 1055.10
2000-12-29, (MA Period 22) Ending Value 1057.60
2000-12-29, (MA Period 23) Ending Value 1021.50
2000-12-29, (MA Period 24) Ending Value 1018.80
2000-12-29, (MA Period 25) Ending Value 1012.40
2000-12-29, (MA Period 26) Ending Value 998.30
2000-12-29, (MA Period 27) Ending Value 983.10
2000-12-29, (MA Period 28) Ending Value 976.90
2000-12-29, (MA Period 29) Ending Value 984.20
2000-12-29, (MA Period 30) Ending Value 980.80结果
对于小于18的期间策略无佣金会亏钱。对于18到26之间的期间包括两者策略会赚钱。26以上再次失去了钱。
而该策略和给定数据集的获胜期为
20个bar赢得1000 $的78.00个单位7.8 注意::绘图示例中的额外指标已被删除并且操作的开始仅受到正在优化的简单移动平均线的影响。因此期间15的结果略有不同。 结论
示例逐步的展示了如何从一个简单的脚本到一个完整的交易系统甚至可以绘制结果并进行优化。
可以做很多事情来提高获胜的机会
自定义指标
创建指标很容易甚至绘制它们也很容易
仓位管理
对于许多人来说资金管理是成功的关键 订单类型限价止损止损限价 其他一些
为了确保可以充分利用上述所有项目文档提供了对它们和其他主题的深入了解。
查看目录并继续阅读…并开发。
祝你好运