Backtrader - 新增策略

以下内容皆参考 Backtrader 官网

昨天使用了 backtrader 将 shioaji 的历史资料载入,今天我们就做一个很简单的策略来测试实际回测看看

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('收盘价: %.2f' % dataclose[0])

我们使用 pandas.DataFrame 将 shioaji 的资料载入到 backtrader 的 datas 里,所以 self.datas[0] 就是我们载入的资料,载入的资料有 close, high, low, open, volume,这 5 条线,如果把 datetime 也算进来的话,那就是 6 条线。
init 里,我们把 close 这一条线特别设了一个变数叫 self.dataclose,然後在 next 这个函数里,我们将 dataclose[0] 的数值输出来。
回测就是从最旧的资料,一笔一笔往最新的资料跑,所以这个 dataclose[0] 就是目前程序跑到的那一笔,如果要看前一笔,就是 dataclose[-1], 以此类推,可以是 -2, -3 前两笔,前三笔。
以上这段程序就是让它一天一天跑,然後把收盘价输出来

from datetime import datetime
import pandas as pd
import shioaji as sj
import backtrader as bt

api = sj.Shiaji()
api.login(person_id="身分证字号", passwd="密码")

stock2330 = api.Contracts.Stocks["2330"]
kbar2330 = api.kbars(stock2330, start='2020-01-01', end='2020-12-31')

dts = list(map(lambda x:dateime.utcformattimestamp(x/10**9), kbr2330.ts))

df = pd.DataFrame(
    {
        "open": pd.Series(kbar2330.Open),
        "high": pd.Series(kbar2330.High),
        "low": pd.Series(kbar2330.Low),
        "close": pd.Series(kbar2330.close),
        "volume": pd.Series(kbar2330.Volume),
    }
)

df.index = pd.Index(dts)

以上为取得资料的程序码,如果是用 jupyter notebook 的话,可以放在一个独立的程序区块,这样就不用一直去抓资料下来了,执行起来会快一些

cerebro = bt.Cerebro()

# 载入要执行的策略
cerebro.addstrategy(TestStrategy)

# 因为台积的股价有点高,所以我们初始资金设 100 万
cerebro.broker.setcash(10**6)

# 券商的手续费
cerebro.broker.setcommission(commission=0.1425/100)

data = bt.feeds.PandasData(dataname=df, timeframe=bt.TimeFrame.Minutes)

cerebro.resampledata(data, timeframe=bt.TimeFrame.Days)

# 如果跑到最後,还有股票没有卖出的话,会以当天收盘价的价格来加总
print("初始资金: %.2f" % cerebro.broker.getvalue())

# 执行策略
cerebro.run()

print("结束资金:%.2f" % cerebro.broker.getvalue())

以上程序,执行之後可以看到输出每天的收盘价,接下来我们来卖加一个买入的策略

class TestStrategy(bt.Strategy):
    def log(self, txt, dt=None):
        #...略
    
    def __init__(self):
        #...略

    def next(self):
        self.log('收盘价: %.2f' % dataclose[0])
        
        # 当天收盘价比昨天低 -> 跌
        if self.dataclose[0] < self.dataclose[-1]:
            
            # 昨天的收盘价比前天低 -> 连跌 2 天
            if self.dataclose[-1] < self.dataclose[-2]:
                self.log("买入: %.2f" % self.dataclose[0])
                # 一张股票是1000股
                self.buy(size=1000)

这个策略就是,如果股票连跌 2 天,就买入 1 张 (1000 股),执行完的个策略後,再去跑回测,就可以看到我们买进股票,再来我们要增加卖出的策略

class TestStrategy(bt.Strategy):
    def log(self, txt, dt=None):
        # ...略
    
    def __init__(self):
        # ...略

        # 建立一个变数储存我们己经买的股票
        self.order = None

    def notify_order(self, order):
        if order.status in [order.Submitted, order.Accepted]:
            # 买卖单传送到券商,券商接收,无需处理
            return

        if order.status in [order.Completed]:
            # 买卖单交易完成
            if order.isbuy():
                # 买单
                self.log("已买入:%.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("收盘价:%.2f" % self.dataclose[0])

        # 还有未处理的订单,直接跳过
        if self.order:
            return

        # 没有持股 -> 进行买入策略
        if not self.position:
            if self.dataclose[-1] < self.dataclose[0]:
                if self.dataclose[-2] < self.dataclose[-1]:
                    self.log("买入:%.2f" % self.dataclose[0])
                    self.order = self.buy(size=1000)
        # 有持股 -> 进行卖出策略
        else:
            if len(self) >= (self.bar_executed + 5):
                self.log("卖出:%.2f" % sef.dataclose[0])
                self.order = self.sell(size=1000)

这样就是一个简单的买入,卖出策略回测,执行之後,就可以看到以这样子的策略,我们的在 2020 年可以赚到多少,当然因为这个是历史资料,所以只是一个评估,并不代表未来用这个策略去执行也可以获得同样的收益喔


<<:  创建App-联络客服

>>:  Day 0x18 - 使用 Laravel 串接之结尾及自我检讨

Day04 - Python基本语法 Part 1

今天开始将进行Python基本语法练习,因大部分语法跟很多程序语言相似,故这个部分将主要以笔记方式注...

SSL 凭证制作与汇入

凭证请求档制作 本文是在 Windows 环境下操作 下载工程师必备神器 Cmder 最省事,该程序...

Day19 - 中场休息时间 - 怎麽样用Canvas精准的写出一个『字』 - 成为Canvas Ninja ~ 理解2D渲染的精髓

呃,首先呢~ 敝人小弟在下我今天仔细的思考了一下,决定这次还是再来一篇『中场休息』科普文,等到明天再...

成员 9 人:在办公室内,建立时空虫洞

根据相对论,如果一对挛生兄弟, 哥哥搭乘宇宙飞船,以接近光速,飞离地球,在宇宙间航行; 当回到地球的...

那些被忽略但很好用的 Web API / CreateDocumentFragment

除了功能完善,有时候效能也该一并考虑。 今天要介绍的是 CreateDocumentFragmen...