天天看點

Python量化交易學習筆記(18)——放量突破布林線中軌買入政策

本文将探索新的政策回測程式,主要是為了嘗試不同的技術名額在backtrader平台上的應用,為後續複雜政策的實作做準備。

本文将實作的政策是,當股票放量突破布林線中軌時進行買入,當股票收盤價低于短期均線時賣出。

買入條件中,放量突破布林線中軌具體指的是,當日股票開盤價在布林線中軌下方,收盤價在布林線中軌上方,當日成交量為10日以來的最高量。賣出條件中,短期均線選取為5日線。回測初始資金100000元,單筆操作機關1000股,傭金千分之一,回測時間自2018年1月1日至2020年3月20日。

政策核心代碼還是位于政策類的init方法中:

def __init__(self):
        self.inds = dict()
        for i, d in enumerate(self.datas):
            self.inds[d] = dict()
            # 布林線中軌
            boll_mid = bt.ind.BBands(d.close).mid
            # 買入條件
            self.inds[d]['buy_con'] = bt.And( \
                # 突破中軌
                d.open < boll_mid, d.close > boll_mid, \
                # 放量
                d.volume == bt.ind.Highest(d.volume, period = self.p.p_period_volume, plot = False))
            # 賣出條件
            self.inds[d]['sell_con'] = d.close < bt.ind.SMA(d.close, period = self.p.p_sell_ma)
           

這裡需要注意的是,技術名額在backtrader裡是lines對象,而非數值,是以在使用與或操作時,不能使用python自帶的and和or操作符,而隻能調用backtrader的And和Or函數。對技術名額做比較時可以使用大于号、小于号等符号,這是因為backtrader對這些符号進行了重寫。

回測000001後的最終資産為101107.35元:

Python量化交易學習筆記(18)——放量突破布林線中軌買入政策

可以看到,該政策并非每筆交易都會盈利,但是盈利額度較大,虧損額度較小。由于我們選取的交易機關是1000股,而000001的股價隻是10元左右,相當于我們隻動用了不到20%的資金,是以總盈利額顯得較小,如果提高交易手數,盈利總額也會随之提升。

同時回測000001、000002後的最終資産為100082.86元:

Python量化交易學習筆記(18)——放量突破布林線中軌買入政策

同時回測000001、000002、000004後的最終資産為100247.27元:

Python量化交易學習筆記(18)——放量突破布林線中軌買入政策

回測603999後的最終資産為98871.44元:

Python量化交易學習筆記(18)——放量突破布林線中軌買入政策

可以看到該政策也有總體虧損的情況,直覺上判斷,該政策可能比較适合大盤股,對小盤股而言,放量的條件比較容易達到,政策缺乏穩定性。

我們進一步嘗試将放量突破的标準選為20日以來的最高值時,回測000001的最終資産為100613.27元,可以看到3筆盈利交易,1筆虧損交易。

Python量化交易學習筆記(18)——放量突破布林線中軌買入政策

當我們把回測起始時間改為2008年1月1日後,回測0000001的最終資産為103137.30元,可以看到也存在大量的虧損交易,但是虧損值都較小。

Python量化交易學習筆記(18)——放量突破布林線中軌買入政策

友情提示:本系列學習筆記隻做資料分析,記錄個人學習過程,不作為交易依據,盈虧自負。

放量突破布林線中軌買入政策代碼:

from __future__ import (absolute_import, division, print_function, unicode_literals)
import datetime  # 用于datetime對象操作
import os.path  # 用于管理路徑
import sys  # 用于在argvTo[0]中找到腳本名稱
import backtrader as bt # 引入backtrader架構
import pandas as pd

stk_num = 1  # 回測股票數目
# 建立政策
class BollStrategy(bt.Strategy):
    # 可配置政策參數
    params = dict(
        p_period_volume = 10,   # 前n日最大交易量
        p_sell_ma = 5,          # 跌破該均線賣出
        p_oneplot = False,      # 是否列印到同一張圖
        pstake = 1000,          # 單筆交易股票數
    )
    def __init__(self):
        self.inds = dict()
        for i, d in enumerate(self.datas):
            self.inds[d] = dict()
            # 布林線中軌
            boll_mid = bt.ind.BBands(d.close).mid
            # 買入條件
            self.inds[d]['buy_con'] = bt.And( \
                # 突破中軌
                d.open < boll_mid, d.close > boll_mid, \
                # 放量
                d.volume == bt.ind.Highest(d.volume, period = self.p.p_period_volume, plot = False))
            # 賣出條件
            self.inds[d]['sell_con'] = d.close < bt.ind.SMA(d.close, period = self.p.p_sell_ma)
            # 跳過第一隻股票data,第一隻股票data作為主圖資料
            if i > 0:
                if self.p.p_oneplot:
                    d.plotinfo.plotmaster = self.datas[0]
    def next(self):
        for i, d in enumerate(self.datas):
            dt, dn = self.datetime.date(), d._name           # 擷取時間及股票代碼
            pos = self.getposition(d).size
            if not pos:                                      # 不在場内,則可以買入
                if self.inds[d]['buy_con']:                # 如果金叉
                    self.buy(data = d, size = self.p.pstake) # 買買買
            elif self.inds[d]['sell_con']:                  # 在場内,且死叉
                self.close(data = d)                         # 賣賣賣

    def notify_trade(self, trade):
        dt = self.data.datetime.date()
        if trade.isclosed:
            print('{} {} Closed: PnL Gross {}, Net {}'.format(
                dt, trade.data._name, round(trade.pnl, 2), round(trade.pnlcomm, 2)
            ))
cerebro = bt.Cerebro()  # 建立cerebro
# 讀入股票代碼
stk_code_file = '../TQDat/data/tq_stock_code.csv'
stk_pools = pd.read_csv(stk_code_file, encoding = 'gbk')
if stk_num > stk_pools.shape[0]:
    print('股票數目不能大于%d' % stk_pools.shape[0])
    exit()
for i in range(stk_num):
    stk_code = stk_pools['code'][stk_pools.index[i]]
    stk_code = '%06d' % stk_code
    # 讀入資料
    datapath = '../TQDat/day/stk/' + stk_code + '.csv'
    # 建立價格資料
    data = bt.feeds.GenericCSVData(
            dataname = datapath,
            fromdate = datetime.datetime(2018, 1, 1),
            todate = datetime.datetime(2020, 3, 31),
            nullvalue = 0.0,
            dtformat = ('%Y-%m-%d'),
            datetime = 0,
            open = 1,
            high = 2,
            low = 3,
            close = 4,
            volume = 5,
            openinterest = -1
            )
    # 在Cerebro中添加股票資料
    cerebro.adddata(data, name = stk_code)
# 設定啟動資金
cerebro.broker.setcash(100000.0)
# 設定傭金為千分之一
cerebro.broker.setcommission(commission=0.001)
cerebro.addstrategy(BollStrategy, p_oneplot = False)  # 添加政策
cerebro.run()  # 周遊所有資料
# 列印最後結果
print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())
cerebro.plot()  # 繪圖
           

為了便于互相交流學習,建立了微信群,感興趣的讀者請加微信。

Python量化交易學習筆記(18)——放量突破布林線中軌買入政策