Backtrader(1)
1、简介
创建一个Cerebro引擎:
1、加入一个Strategy
2、加载数据
3、执行:cerebro.run()
4、对执行结果可视化
就是这么简单!
2、安装
pip install backtrader
特别注意的是,和backtrader适配的matplotlib版本不能太高,测试可用的版本是3.2.2。如果高于此版本,可以通过如下命令降级:
pip uninstall matplotlib
pip install matplotlib==3.2.2
3、一个例子
Line
Line在Backtrader系统中,是最重要的对象。Line的本意就是一连串可以连接在一起的点,所有可以在坐标上形成一条线的数据,就称为Line。在量化投资的领域,这个Line通常指的是证券的open(开盘价)、high(最高价)、low(最低价)、close(收盘价)、volume(成交量)。例如如下我们下载的某股票的数据:
Excle中画个图:
可以看出,每一列均可形成一个Line,这个Line就是需要进行处理的数据,对于证券数据,我们通常对close(收盘价)进行处理。
除了数据本身可以形成Line之外,对数据进行处理后形成的数据也可以作为Line,比如计算close列的移动平均线,也可以形成一个Line。
Index为0的含义
Backtrader系统中,对Line的数据是逐行处理的,如下:
系统处理顺序从上至下,当处理到26.5的时候,对应的Index为0.如果要访问之前的数据,就是-1,-2.之后的数据,就是1,2.
特别值得注意的是-1在python中用于访问一个列表的最后一个数据,而在backtrader中,-1指的是最后已经处理过得数据,在当前处理数据之前 ,值会随着系统的处理而不断变化。
如果系统在创建Strategy的时候,系统初始化了一个移动平均线:
self.sma = SimpleMovingAverage(.....)
访问移动平均线当前值的简单易用的方法是:
av = self.sma[0]
这种方法的好处就是无需知道已经处理了多少行(在Backtrader中,统一将行称之为Bar),也无需知道还有多少Bar需要处理,因为0唯一确定了系统正在处理的数据(Bar)。
创建第一个程序
from __future__ import (absolute_import, division, print_function,
unicode_literals)
import backtrader as bt
if __name__ == '__main__':
cerebro = bt.Cerebro()
print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue())
cerebro.run()
print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())
执行下,看看啥结果:
Starting Portfolio Value: 10000.00
Final Portfolio Value: 10000.00
这个程序做了啥?
1、将Backtrader引入到咱们程序中,命名为bt。
2、创建了一个机器人大脑(Cerebro),同时隐含创建了一个borker(券商)。
3、让机器人大脑开始运行。
4、显示了机器人在券商那里存有多少钱。
等等,为啥只有10000元,失败啊,太少了,没事,咱可以多存点钱到券商。
cerebro = bt.Cerebro()
cerebro.broker.setcash(100000.0)#加到100000元,咱们也富裕了。
再执行看看,果然富裕了:
Starting Portfolio Value: 100000.00
Final Portfolio Value: 100000.00
给空白的大脑加载数据
import backtrader as bt
import pandas as pd
from datetime import datetime
if __name__ == '__main__':
cerebro = bt.Cerebro()
# 获取数据
stock_hfq_df = pd.read_excel("./data/000858.SZ.xlsx", index_col='trade_date', parse_dates=True)
start_date = datetime(2023, 1, 1) # 回测开始时间
end_date = datetime(2023, 12, 31) # 回测结束时间
data = bt.feeds.PandasData(dataname=stock_hfq_df, fromdate=start_date, todate=end_date) # 加载数据
cerebro.adddata(data) # 将数据传入回测系统
cerebro.broker.setcash(100000.0)
print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue())
cerebro.run()
print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())
再执行看看:
Starting Portfolio Value: 100000.00
Final Portfolio Value: 100000.00
大脑开始发育了,起码做到了:
1、能接受股票的数据了。(这里提供的是下载五粮液(SZ.000858)的历史数据作为示例)
2、也能对datetime进行处理,根据需要看具体特定时期的数据了。
给大脑第一个策略
钱有了(在broker券商),股票数据也有了,似乎马上就可以进行有风险的投资了。要投资,就得有一个有投资策略。而投资策略,针对的通常是数据的收盘价(close),也就是根据收盘价决定如何投资。
毕竟股市太凶险,机器人新加的策略是先看看股票的收盘价。
import backtrader as bt
import pandas as pd
from datetime import datetime
# Create a Stratey
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):
# 引用到输入数据的close价格
self.dataclose = self.datas[0].close
def next(self):
# 目前的策略就是简单显示下收盘价。
self.log('Close, %.2f' % self.dataclose[0])
if __name__ == '__main__':
cerebro = bt.Cerebro()
# 增加一个策略
cerebro.addstrategy(TestStrategy)
# 获取数据
stock_hfq_df = pd.read_excel("./data/000858.SZ.xlsx", index_col='trade_date', parse_dates=True)
start_date = datetime(2023, 1, 1) # 回测开始时间
end_date = datetime(2023, 12, 31) # 回测结束时间
data = bt.feeds.PandasData(dataname=stock_hfq_df, fromdate=start_date, todate=end_date) # 加载数据
cerebro.adddata(data) # 将数据传入回测系统
cerebro.broker.setcash(100000.0)
print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue())
cerebro.run()
print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())
执行后结果:
2023-01-03, Close, 178.20
2023-01-04, Close, 178.62
2023-01-05, Close, 189.00
2023-01-06, Close, 190.46
......省略n行
2023-12-29, Close, 140.31
Final Portfolio Value: 100000.00
看起来还好,没赔钱,股票市场似乎没那么凶险。
关于这个策略(Strategy),需要重点关注:
Strategy初始化的时候,将大脑加载的数据更新到dataclose属性中(注意,这是一个列表,保存股票回测开始时间到结束时间的所有close数据)。 self.datas[0]指向的是大脑通过cerebro.adddata函数加载的第一个数据,本例中指加载浦发银行的股票数据。
self.dataclose = self.datas[0].close指向的是close (收盘价)line
strategy 的next方法针对self.dataclose(也就是收盘价Line)的每一行(也就是Bar)进行处理。在本例中,只是打印了下close的值。next方法是Strategy最重要的的方法,具体策略的实现都在这个函数中,后续还会详细介绍。
这个策略还是没能挣钱,没买卖咋挣钱?大胆点,咱们开始买!
加入买的逻辑到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):
# Simply log the closing price of the series from the reference
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()
执行结果如下:
2023-01-03, Close, 178.20
2023-01-04, Close, 178.62
......省略n行
2023-12-22, Close, 133.42
2023-12-25, Close, 134.55
2023-12-26, Close, 134.32
2023-12-27, Close, 133.83
2023-12-27, BUY CREATE, 133.83
2023-12-28, Close, 140.10
2023-12-29, Close, 140.31
Final Portfolio Value: 97556.94
额,亏了2000多
虽然咱们亏了钱,订单(order)好像创建了,但是不知道是否执行了以及何时以什么价格执行了。后面我们会展示如何监听订单执行的状态。
也许你要问了,咱们买了多少股票(称之为资产asset)?买了啥股票?订单是咋执行的?后续会回答这些问题,在当前示例中:
1、self.datas[0] 就是我们购买了的股票。本例中没有输入其他数据,如果输入了其他数据,购买的股票就不一定是啥了,这个要看具体的策略执行情况。
2、买了多少股本(stake)的股票?这个通过机器人大脑的position sizer属性来记录,缺省值为1,就是缺省咱们每一次操作只买卖1股。
3、当前order执行的时候,采用的价格是第二天的开盘价。
4、当前order执行的时候,没有收佣金。佣金如何设置后续还会说明。
不仅要买,还要卖
一次完整的交易,不仅要买,还要卖。啥时候卖?咱们简单点,就是处理了5个bar数据之后。值得注意的是,这里使用了bar这个概念,没有包含任何时间的概念,也就是一个bar,可以是1分钟,1个小时,也可以是一天,一个月,这些基于你输入的数据,如果你输入的股票每小时(分时)数据,那么一个bar就是一分钟,如果提供是周K数据,一个bar就是一周。本例中,我们获取的数据基于每天,那么一个bar就是一天。
特别之一,这里代码使用了len这个函数,在python中,len通常返回的一个列表中数据的多少,而在backtrader中,重写了len函数,返回的是已经处理过数据行(也就是Bar)。
注意,如果我们不在市场内,就不能卖。什么叫不在市场内,就是你不拥有任何股票头寸,也就是没有买入资产。在策略中,通过 position 属性来记录。
本次代码中,我们将要增加如下:
1、访问postion获取是否在市场内
2、会创建买和卖的订单(order)
3、订单状态的改变会通过notify方法通知到strategy。
执行结果:
Starting Portfolio Value: 100000.00
2023-01-03, Close, 178.20
2023-01-04, Close, 178.62
2023-01-05, Close, 189.00
2023-01-06, Close, 190.46
2023-01-09, Close, 195.88
2023-01-10, Close, 195.24
......省略n行
2023-12-25, SELL EXECUTED, 133.32
2023-12-25, Close, 134.55
2023-12-26, Close, 134.32
2023-12-27, Close, 133.83
2023-12-27, BUY CREATE, 133.83
2023-12-28, BUY EXECUTED, 133.83
2023-12-28, Close, 140.10
2023-12-29, Close, 140.31
Final Portfolio Value: 100002.60
券商说,俺的钱呢?
券商说,你这里又买又卖的,我的佣金在哪儿?好的,咱们加上佣金,就0.1%吧,一行代码的事情:
# 0.1% ... 除以100去掉%号。
cerebro.broker.setcommission(commission=0.0002)
import backtrader as bt
import pandas as pd
from datetime import datetime
# Create a Stratey
class TestStrategy(bt.Strategy):
def log(self, txt, dt=None):
''' Logging function fot this strategy'''
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] dataseries
self.dataclose = self.datas[0].close
# To keep track of pending orders
self.order = None
def notify_order(self, order):
if order.status in [order.Submitted, order.Accepted]:
# Buy/Sell order submitted/accepted to/by broker - Nothing to do
return
# Check if an order has been completed
# Attention: broker could reject order if not enough cash
if order.status in [order.Completed]:
if order.isbuy():
self.log('BUY EXECUTED, %.2f' % order.executed.price)
elif order.issell():
self.log('SELL EXECUTED, %.2f' % order.executed.price)
self.bar_executed = len(self)
elif order.status in [order.Canceled, order.Margin, order.Rejected]:
self.log('Order Canceled/Margin/Rejected')
# Write down: no pending order
self.order = None
def next(self):
# Simply log the closing price of the series from the reference
self.log('Close, %.2f' % self.dataclose[0])
# Check if an order is pending ... if yes, we cannot send a 2nd one
if self.order:
return
# 检查是否在市场
if not self.position:
# 不在,那么连续3天价格下跌就买点
if self.dataclose[0] < self.dataclose[-1]:
# 当前价格比上一次低
if self.dataclose[-1] < self.dataclose[-2]:
# 上一次的价格比上上次低
# 买入!!!
self.log('BUY CREATE, %.2f' % self.dataclose[0])
# Keep track of the created order to avoid a 2nd order
self.order = self.buy()
else:
# 已经在市场,5天后就卖掉。
if len(self) >= (
self.bar_executed + 5): # 这里注意,Len(self)返回的是当前执行的bar数量,每次next会加1.而Self.bar_executed记录的最后一次交易执行时的bar位置。
# 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 order
self.order = self.sell()
if __name__ == '__main__':
cerebro = bt.Cerebro()
# 增加一个策略
cerebro.addstrategy(TestStrategy)
# 获取数据
stock_hfq_df = pd.read_excel("./data/000858.SZ.xlsx", index_col='trade_date', parse_dates=True)
start_date = datetime(2023, 1, 1) # 回测开始时间
end_date = datetime(2023, 12, 31) # 回测结束时间
data = bt.feeds.PandasData(dataname=stock_hfq_df, fromdate=start_date, todate=end_date) # 加载数据
cerebro.adddata(data) # 将数据传入回测系统
cerebro.broker.setcash(100000.0)
# 设置佣金0.02% ... 除以100去掉%号。
cerebro.broker.setcommission(commission=0.0002)
print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue())
cerebro.run()
print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())
结果如下:
2023-01-03, Close, 178.20
2023-01-04, Close, 178.62
2023-01-05, Close, 189.00
2023-01-06, Close, 190.46
2023-01-09, Close, 195.88
2023-01-10, Close, 195.24
2023-01-11, Close, 194.10
2023-01-11, BUY CREATE, 194.10
2023-01-12, BUY EXECUTED, 195.00
......省略n行
2023-12-25, OPERATION PROFIT, GROSS -0.35, NET -0.40
2023-12-25, Close, 134.55
2023-12-26, Close, 134.32
2023-12-27, Close, 133.83
2023-12-27, BUY CREATE, 133.83
2023-12-28, BUY EXECUTED, 133.83
2023-12-28, Close, 140.10
2023-12-29, Close, 140.31
Final Portfolio Value: 100000.86
同时,我们注意到,每次订单执行,都有一个操作盈利记录(Gross:毛利;Net:净利)
如何给策略Strategy传递参数
策略这个东西,是高度可配置的,大脑想调整下策略,可以通过配置参数来设定,免得大量硬编码数据,后续难以修改。比如策略之前是过5天就卖,现在想改为过7天再卖,通过修改参数就可以做到。参数如何传递呢?也很简单:
params = (('myparam', 27), ('exitbars', 5),)
就是典型的python元组数据。
我们在创建策略的时候,大脑通过如下方法传递给策略实例
# 加一个策略
cerebro.addstrategy(TestStrategy, myparam=20, exitbars=7)
那么卖出的 逻辑可以修改为:
if len(self) >= (self.bar_executed + self.params.exitbars):
那么卖出的 逻辑可以修改为:
if len(self) >= (self.bar_executed + self.params.exitbars):
之前我们每次只买1股,太少了,咱们中国一手最少100股,那咋整?可以通过如下代码设置:
cerebro.addsizer(bt.sizers.FixedSize, stake=100)
看如下,每次买了100股
2023-12-28, BUY EXECUTED, Price: 133.83, Cost: 13383.00, Comm 2.68
加一个指标(indicator)
家投资的时候,经常听说这指标那指标的,没听说?那均线总听说过吧?咱们加个均线指标来指导我们的策略,毕竟连续两天下跌这个指标太土了,咱们要使用高大上的移动均线指标:
1、如果当前价格大于均线,就买买买。
2、如果低于均线,就卖卖卖。怎么听起来像追涨杀跌啊,看起来咱们的机器人像个韭菜。
3、为了简单起见,同时只能存在一次买卖。
修改也不大,在策略(Strategy)的初始化的时候,引用一个简单的移动平均指标,如下代码:
self.sma = bt.indicators.MovingAverageSimple(self.datas[0], period=self.params.maperiod)
Strategy代码如下。为了减少数据,对比查看sma的数据,说明一个重要的概念,暂时将回测时间修改为2023-2-1。
import backtrader as bt
import pandas as pd
from datetime import datetime
# Create a Stratey
class TestStrategy(bt.Strategy):
params = (
('maperiod', 20),
)
def log(self, txt, dt=None):
''' Logging function fot this strategy'''
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] dataseries
self.dataclose = self.datas[0].close
# To keep track of pending orders and buy price/commission
self.order = None
self.buyprice = None
self.buycomm = None
# Add a MovingAverageSimple indicator
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]:
# Buy/Sell order submitted/accepted to/by broker - Nothing to do
return
# Check if an order has been completed
# Attention: broker could reject order if not enough cash
if 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.price
self.buycomm = order.executed.comm
else: # Sell
self.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 = None
def notify_trade(self, trade): # 交易执行后,在这里处理
if not trade.isclosed:
return
self.log('OPERATION PROFIT, GROSS %.2f, NET %.2f' %
(trade.pnl, trade.pnlcomm)) # 记录下盈利数据。
def next(self):
# Simply log the closing price of the series from the reference
self.log('Close, %.2f' % self.dataclose[0])
# Check if an order is pending ... if yes, we cannot send a 2nd one
if self.order:
return
# Check if we are in the market
if not self.position:
# 大于均线就买
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 order
self.order = self.buy()
else:
if self.dataclose[0] < self.sma[0]:
# 小于均线卖卖卖!
self.log('SELL CREATE, %.2f' % self.dataclose[0])
# Keep track of the created order to avoid a 2nd order
self.order = self.sell()
if __name__ == '__main__':
cerebro = bt.Cerebro()
# 增加一个策略
cerebro.addstrategy(TestStrategy)
# 获取数据
stock_hfq_df = pd.read_excel("./data/000858.SZ.xlsx", index_col='trade_date', parse_dates=True)
start_date = datetime(2023, 2, 1) # 回测开始时间
end_date = datetime(2023, 12, 31) # 回测结束时间
data = bt.feeds.PandasData(dataname=stock_hfq_df, fromdate=start_date, todate=end_date) # 加载数据
cerebro.adddata(data) # 将数据传入回测系统
cerebro.broker.setcash(100000.0)
cerebro.addsizer(bt.sizers.FixedSize, stake=100)
# 设置佣金0.02% ... 除以100去掉%号。
cerebro.broker.setcommission(commission=0.0002)
print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue())
cerebro.run()
print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())
执行下,看看:
......省略n行
2023-12-25, Close, 134.55
2023-12-26, Close, 134.32
2023-12-27, Close, 133.83
2023-12-28, Close, 140.10
2023-12-28, BUY CREATE, 140.10
2023-12-29, BUY EXECUTED, Price: 140.00, Cost: 14000.00, Comm 2.80
2023-12-29, Close, 140.31
Final Portfolio Value: 95785.65
哎,亏了,还不如追涨杀跌呢!
眼尖的你可能看到了,咱们回测时间是2023-1-1,为啥系统从2023-2-6开始测试?
这是因为sma(移动平均)的参数为20,也就是要20bar(本例中一个bar就是一天)的数据才能计算,1到19天没数据。
从第20天开始(20个工作日之后就是2月6日)才有数据,而strategy的next在所有line(本例中,有两个line,一个是open价格,一个是sma)都有有效的时候,才会进行策略的处理。这个例子中,只有一个指标(indicator),实际上可以加随便多少个,来实现复杂的策略,后续再详细讨论。
另外,注意一点,backtrader中对数据的处理是向下取两位小数。
可视化-画图
前面执行程序,每一步都输出文字描述,这个看起来实在不友好,于是Backtrader提供了画图功能。
画图使用的是matplotlib库,前面描述过,必须安装特定版本,不然就有问题。
实现画图,也很简单,加一行代码:
cerebro.plot()
放到cerebro.run()之后就可以了,这里不贴代码了,执行试试看:
一张图可以看出所有:
1、收盘价和移动平均线
2、盈利图
3、买卖点
4、买卖盈利还是亏损
5、成交量
你以为只能这么点,为了展示咱们强大的画图能力,再加点指标来自动画图显示:
1、指数移动平均线
2、权重移动平均线
3、随机指标
4、MACD
5、RSI
6、RSI的简单移动平均
7、ATR。这个不画图,展示如何控制Line是否参与画图
实现的时候,就是在Strategy初始化的时候,加上如下代码:
# 新加指标用于画图
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
整个Strategy代码如下:
class TestStrategy(bt.Strategy):
params = (
('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):
# Keep a reference to the "close" line in the data[0] dataseries
self.dataclose = self.datas[0].close
# To keep track of pending orders and buy price/commission
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]:
# Buy/Sell order submitted/accepted to/by broker - Nothing to do
return
# Check if an order has been completed
# Attention: broker could reject order if not enough cash
if 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.price
self.buycomm = order.executed.comm
else: # Sell
self.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 order
self.order = None
def notify_trade(self, trade):
if not trade.isclosed:
return
self.log('OPERATION PROFIT, GROSS %.2f, NET %.2f' %
(trade.pnl, trade.pnlcomm))
def next(self):
# Simply log the closing price of the series from the reference
self.log('Close, %.2f' % self.dataclose[0])
# Check if an order is pending ... if yes, we cannot send a 2nd one
if self.order:
return
# Check if we are in the market
if 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 order
self.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 order
self.order = self.sell()
看看效果:
看起来还不错。不过实话说,整个图还是挺丑的,后面优化下。
4、总结
咱们一步一步从最原始的啥也不能做的机器人大脑,慢慢进化成能根据输入的股票数据,采用不同的策略来挣钱的成熟大脑,还能把执行过程可视化,实在是一个合格的投资者了。
后面咱们还可以:
1、自定义的指标
2、更好地管理资金;
3、详细的投资收益信息
...