先說說常見的均線指標:ma5和ma10,學名是五日價格移動平均線和十日價格移動平均線,簡稱5日線和10日線。大部分人對這兩個均線很熟,但是它們的實際作用,眾說紛紜,褒貶各異。具體怎麼樣,我利用backtrader進行驗證。
backtrader是python開發的一個回測框架,簡單易用,是國外一個牛人開發的,文件也是英文的,本次做一個簡單的量化策略回測。
先來看看這個簡單的量化策略:
ma5上穿ma10買入,ma10下穿ma5賣出。
做出這個策略的買入賣出理由很簡單:5日線上穿10日線,股價處於強勢;10日線下穿5日線,股價處於弱勢。明白這個後,接下來就是編寫程式碼了。
第一步,安裝backtrader。
pip install backtrader
第二步,準備資料。
我已經利用之前的開原始碼提前下載了資料,以股票程式碼為名的csv檔案。
每個csv檔案包含日期,開盤價,收盤價,最高價,最低價,成交量。
第三步,編寫程式碼。
首先編寫自己的策略類,引入backtrader,繼承裡面的策略類。這個地方我給自己的策略命名為MyStrategy,並且定義兩個變數分別代表需要的均線,比如5日均線和10日均線。
import backtrader as bt
class MyStrategy(bt.Strategy):
# 均線引數,5日和10日
params = (
('ma_1', 5),
('ma_2', 10),
)
def stop(self):
print("結束了")
backtrader自帶了指標計算的公式,因此我們只需要呼叫即可,根據策略,我們應該要計算5日均線和10日均線,繼續在策略類裡面編寫,程式碼實現起來很簡單。backtrader真的很優秀。
def __init__(self):
self.dataclose = self.datas[0].close
self.order = None
self.buyprice = None
self.buycomm = None
# 五日移動平均線
self.sma5 = bt.indicators.SimpleMovingAverage(period=self.params.ma_1)
# 十日移動平均線
self.sma10 = bt.indicators.SimpleMovingAverage(period=self.params.ma_2)
接下來實現買入賣出。按照策略,翻譯成通俗語言是:一,如果沒有倉位,當天ma5大於ma10,並且前一天ma5小於ma10買入;二,如果當前持倉,那麼當ma10大於ma5時就賣出。
backtrader給我們提供了對應的介面方法(notify_order、notify_trade、next),我們只需要編寫成這個策略的程式碼即可。明白這個後,程式碼應該這樣編寫:
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, 花費資金: %.2f, 手續費: %.2f' %
(order.executed.price,
order.executed.value,
order.executed.comm))
self.buyprice = order.executed.price
self.buycomm = order.executed.comm
else: # 賣出
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('收盤, %.2f' % self.dataclose[0])
# 檢查是否正在準備下單,如果是則直接返回
if self.order:
return
# 檢查是否持倉
if not self.position:
# 沒有倉位,執行買入條件判斷:
if self.sma5[0] > self.sma10[0] and self.sma5[-1] < self.sma10[-1]:
self.log('買入, %.2f' % self.dataclose[0])
self.log('當前ma5:%.2f, 當前ma10: %.2f, 前一日ma5:%.2f, 前一日ma10: %.2f' %
(self.sma5[0], self.sma10[0], self.sma5[-1], self.sma10[-1]))
# 買入
self.order = self.buy()
else:
# 執行條件賣出
if self.sma5[0] < self.sma10[0]:
self.log('賣出, %.2f' % self.dataclose[0])
# 賣出
self.order = self.sell()
上面的程式碼裡面都有註釋,閱讀起來應該沒有難度。順便說一下,如果你們也編寫程式碼,最好也多寫點註釋,方便自己後面閱讀。有的人可能不信邪,半個月或者半年後,這個程式碼也許就沒人看得懂了[淚奔]。相信黃sir,保持這個習慣,你會收穫更多的尊重和成就。
策略類已經編寫完了,下一步是把csv的檔案資料載入進去執行。backtrader提供了很多載入的介面,非常方便,這裡我用的是載入pandas資料介面。當然也可以直接傳檔案路徑進去,因為我的csv檔案裡面有很多其他的資料,因此讀取後進行修改只加載基礎的資料即可。
import pandas as pd
df = pd.read_csv("000001.csv")
new_df = df[["日期", "開盤", "收盤", "最高", "最低", "成交量"]]
new_df.columns = ["trade_date", "open", "close", "high", "low", "volume"]
new_df["trade_date"] = pd.to_datetime(new_df["trade_date"])
new_df.set_index("trade_date", inplace=True)
new_df['openinterest'] = 0
cerebro = bt.Cerebro()
# 載入我們的策略
cerebro.addstrategy(MyStrategy)
# 從2010-01-01到2021-12-17的資料進行回測
data = bt.feeds.PandasData(
dataname=new_df,
fromdate=datetime.datetime(2010, 1, 1),
todate=datetime.datetime(2021, 12, 17)
)
cerebro.adddata(data)
接下來設定初始金額和手續費。
# 初始資金
init_amount = 300000.0
cerebro.broker.setcash(init_amount)
# 設定每筆交易交易的股票數量
cerebro.addsizer(bt.sizers.FixedSize, stake=100)
# 手續費
cerebro.broker.setcommission(commission=0.0)
最後,執行策略。
# 執行策略
cerebro.run()
# 結果
result_amount = cerebro.broker.getvalue()
print('初始資金: %.2f' % init_amount)
print('期末資金: %.2f' % result_amount)
print('盈利: %.2f' % (float(result_amount) - init_amount))
一切順利,結果如下:
可以看到,對於000001,0手續費的情況下11年時間虧損了1176元。
當然,一個股票說明不了什麼,換成茅臺(600519)試試。
總算看到好的結果了,盈利了。如果改動一下指標,比如5日均線換成3日均線,10日均線換成12日均線,結果如何呢?
可以看到,虧損減少了,盈利增加了。說明啥?常用的指標賺錢越來越難!通俗點講:大家都知道的,就是很難賺錢。和股票總數朝市場最小阻力方向執行異曲同工。
當然可以對全市場的股票進行回測,我們把相關程式碼封裝成一個函式,每次把股票程式碼傳進去,然後把結果存起來,感興趣的朋友可以先自己試試,如果你們想看結果,可以評論留言,想看的人多,下期我再發出來。
另外,黃sir拉了一個群,如果對量化投資感興趣可以私信我。備註,黃sir很帥,[做鬼臉]
好了,本期的測試就完成了,如果希望看到更多關於量化方面的內容,記得加一個關注,以免迷路。