BackTrader SMA 金叉策略
MA(Moving Average)指移动平均线。根据不同加权类型内部又细分为多种。本文中研究的是简单移动平均线(Simple Moving Average)。
本文只用于数学、编程研究,不提供交易指导。
代码
下面代码中 TestStrategy 策略类是通用的。
import backtrader as bt
from newstock.data.mongo.mongo_data_manager import MongoDataManager
from newstock.date.stock_date import StockDate
from newstock.market.Exchange import SZSEExchange
from newstock.market.symbol import Symbol
import pandas as pd
class TestStrategy(bt.Strategy):
params = (
("short", 5),
("long", 10),
("printlog", False),
)
def log(self, txt, dt=None, doprint=False):
"""Logging function for this strategy"""
if 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] dataseries
self.dataclose = self.datas[0].close
# To keep track of pending orders
self.order = None
self.buyprice = None
self.buycomm = None
sma_s = bt.ind.SMA(period=self.p.short) # type: ignore
sma_l = bt.ind.SMA(period=self.p.long) # type: ignore
self.crossover = bt.ind.CrossOver(sma_s, sma_l) # crossover signal
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
# 交易完成
# 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 # 买入手续费
elif order.issell():
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.crossover > 0:
# current close less than 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 order
self.order = self.buy()
else:
# Already in the market ... we might sell
if self.crossover < 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()
def stop(self):
self.log("Ending Value %.2f" % (self.broker.getvalue()), doprint=True)
if __name__ == "__main__":
cerebro = bt.Cerebro()
print("Starting Portfolio Value: %.2f" % cerebro.broker.getvalue())
mongoManager = MongoDataManager()
df = mongoManager.getStockPeriodFromDB(
Symbol(SZSEExchange, "000001"),
StockDate.today().previousDays(300),
StockDate.today(),
)
df["date"] = pd.to_datetime(df["trade_date"], format="%Y%m%d")
data = bt.feeds.PandasData(dataname=df, datetime="date") # type: ignore
df.dropna()
# 0.1% ... divide by 100 to remove the %
cerebro.broker.setcommission(commission=0.001)
# Python 3.10 修复 module 'collections' has no attribute 'Iterable' 开始
import collections
collections.Iterable = collections.abc.Iterable
# Python 3.10 修复 module 'collections' has no attribute 'Iterable' 完成
# 策略参数优化
# cerebro.optstrategy(TestStrategy, maperiod=range(10, 31))
# 策略运行
cerebro.addstrategy(TestStrategy)
cerebro.adddata(data)
# Add a FixedSize sizer according to the stake
cerebro.addsizer(bt.sizers.FixedSize, stake=10)
cerebro.run()
cerebro.plot(style="bar", volume=False)
print("Final Portfolio Value: %.2f" % cerebro.broker.getvalue())
效果
以 sz000001 最近 300 交易日数据为例。
快线5 慢线10
可以看到,最终没有赔钱,但也没有挣钱。
从共交易了 7 次,3 次是成功的,4 次是亏损的,盈亏幅度都挺大。
快线5 慢线15
交易次数变少,对于盈利没有影响。
快线10 慢线30
把均线放慢试一下。
均线放慢后,收益下降(下降了一点点)。看到交易频率进一步下降,但全是失败的。
感觉 SMA 更加适合于作为一个短线指标。