在上一篇文章中,通过收盘价与SMA设计的突破策略回测结果表现十分糟糕,在本文将引入突破比例,和优化器对策略进行优化,改进策略,使其变的可以持续盈利。
指标
使用SMA和收盘价
参数说明
X: 周期
S: 止盈比
L:止损比
C: 回调比
B:上涨幅度
N: k线数量
买入信号
连续N根k线下跌
收盘价>smaX
当日收盘价>昨日收盘价
当日涨幅>B
订单
交易价格:昨日收盘价*(1 - C)
止损价格: 交易价格*(1 - L)
止盈价格:交易价格*(1 + S)
参数
周期:X =100
止损:S= 0.05
止损比:L=0.01
回调比:C= 0.02
上涨幅度:B=0.04
K线数量:N=5
回测 | Value |
---|---|
初始资金 | 10000 |
期货品种 | ETH |
时间级别 | 1H |
回测时间 | 2017.7.15 - 2022.7.24 |
倍数 | 1 |
手续费 | 1% |
总盈利 | -813.7949095640433 |
优化后的参数
周期:X =100
止损:S= 0.05
止损比:L=0.01
回调比:C= 0.02
上涨幅度:B=0.04
K线数量:N=5
回测 | Value |
---|---|
初始资金 | 10000 |
期货品种 | ETH |
时间级别 | 1H |
回测时间 | 2017.7.15 - 2022.7.24 |
倍数 | 1 |
手续费 | 1% |
总盈利 | -813.7949095640433 |
加载数据
def load_csv_data(data_path, size=None, start=None, end=None):
return bt.feeds.GenericCSVData(
dataname=data_path,
nullvalue=0.0,
fromdate=start,
todate=end,
dtformat="%Y-%m-%d %H:%M:%S",
timeframe=bt.TimeFrame.Minutes,
datetime=0,
high=1,
low=2,
open=3,
close=4,
volume=5,
openinterest=-1
)
def create_cerebro(cash=10000.0, commission=0.01, stake=1, strategy=None):
"""
:param data: 数据
:param cash: 初始资金
:param commission: 佣金率
:param stake: 交易单位大小
:param strategy: 交易策略
:return:
"""
cerebro = bt.Cerebro()
# 设置启动资金
cerebro.broker.setcash(cash)
# 设置交易单位大小
cerebro.addsizer(bt.sizers.FixedSize, stake=stake)
# 设置佣金率为千分之一
cerebro.broker.setcommission(commission)
# 显示回测过程中的买入和卖出信号
cerebro.addobserver(bt.observers.Value)
# 显示了回测过程中的买入和卖出信号
cerebro.addobserver(bt.observers.BuySell)
return cerebro
突破策略
import datetime
import backtrader as bt
from utils import load_csv_data
class BreakthroughStrategy(bt.Strategy):
"""
"""
params = dict(
break_through=0.04, # 上涨幅度
callback=0.02, # 价格回调比例
period=100, # sma周期
down_day=5, # 连续下跌天数
stop_loss=0.05, # 止损比例
take_profit=0.1, # 止盈比例
validity_day=3, # 订单有效期
expired_day=1000, # 订单失效期
)
def notify_order(self, order):
if order.status == order.Completed:
self.holdstart = len(self)
if not order.alive() and order.ref in self.orefs:
self.orefs.remove(order.ref)
def __init__(self):
self.holdstart = None
self.dataclose = self.datas[0].close # 收盘价
self.sma = bt.ind.SMA(period=self.p.period, plot=True) # SMA
self.orefs = list() # order列表,用于存储尚未执行完成的订单
def next(self):
# 有尚未执行的订单
if self.orefs:
return
# 尚未进场
# 获取近几日收盘价用于判断是否连续下跌
last_closes = list()
for i in range(1, self.p.down_day + 1):
last_closes.append(self.dataclose[-i])
if not self.position:
# 判断十分突破
is_break = False
if self.dataclose[0] > self.dataclose[-1] \
and (self.data.high[0] - self.dataclose[0]) / self.dataclose[0] > self.p.break_through:
is_break = True
# 连续N日下跌 在 sma上方
if last_closes == sorted(last_closes, reverse=False) and is_break and self.dataclose[0] > self.sma[0]:
p1 = self.dataclose[0] * (1.0 - self.p.callback)
p2 = p1 - self.p.stop_loss * p1
p3 = p1 + self.p.take_profit * p1
# 计算订单有效期
validity_day = datetime.timedelta(self.p.validity_day)
expired_day = valid3 = datetime.timedelta(self.p.expired_day)
size = min(self.broker.getcash() / self.data.high[0], self.data.volume)
os = self.buy_bracket(size=size,
price=p1, valid=validity_day,
stopprice=p2, stopargs=dict(valid=expired_day),
limitprice=p3, limitargs=dict(valid=valid3), )
# 保存激活的的订单
self.orefs = [o.ref for o in os]
def set_params(self, params):
"""
设置参数
:param params:
:return:
"""
self.params = params
优化器
class BreakThroughStrategyOptimizer:
# 策略参数
params = dict(
break_through=0.04, # 上涨幅度
callback=0.02, # 价格回调比例
period=100, # 周期
down_day=5, # 连续下跌天数
stop_loss=0.05, # 止损比例
take_profit=0.1, # 止盈比例
)
# 调优参数范围
optimize_params = dict(
break_through=[0.01, 0.4], # 上涨幅度
callback=[0.01, 0.4], # 价格回调比例
period=[20, 200], #
down_day=[2, 20], # 连续下跌天数
stop_loss=[0.01, 0.4], # 止损比例
take_profit=[0.01, 0.4], # 止盈比例
)
def __init__(self):
# 数据
self.data = None
# 持有现金
self.cash = 10000.0
# 资产
self.value = 10000.0
self.cerebro = None
# 迭代次数
self.event_num = 500
def log(self, txt):
"""
日志记录
:param txt:
:return:
"""
print(txt)
def set_data(self, feed_data):
"""
设置数据
:param feed_data:
:return:
"""
self.data = feed_data
def get_data(self):
"""
获取数据
:return:
"""
return self.data
def get_optimize_total_assets(self):
"""
获取最优策略回测后的总资产
:return:
"""
self.params_optimize(
break_through=self.params['break_through'],
callback=self.params['callback'],
down_day=self.params['down_day'],
period=self.params['period'],
stop_loss=self.params['stop_loss'],
take_profit=self.params['take_profit'],
is_show_plot=False
)
return self.cerebro.broker.getvalue()
def params_optimize(self, break_through, callback, down_day, period, stop_loss=0.05, take_profit=0.01):
"""
参数优化
:param down_day: 连续下跌天数
:param period: 周期
:param break_through: 突破比例
:param callback: 价格回撤
:param stop_loss: 盈亏比 止损
:param take_profit: 盈亏比 止盈
"""
kines = self.get_data()
self.cerebro = create_cerebro(self.cash)
self.cerebro.adddata(kines)
self.cerebro.addstrategy(BreakthroughStrategy,
break_through=break_through,
callback=callback,
period=int(period),
down_day=int(down_day),
stop_loss=stop_loss,
take_profit=take_profit)
self.cerebro.run()
rate = self.cerebro.broker.getvalue() / self.cash
if rate > 10:
self.log(f"rate: {rate} ")
self.log(f"break_through:{break_through}")
self.log(f"callback: {callback}")
self.log(f"period: {period}")
self.log(f"down_day: {down_day}")
self.log(f"stop_loss: {stop_loss}")
self.log(f"take_profit: {take_profit}")
self.log(f"asset: {(self.cerebro.broker.getvalue())}")
self.log("")
return self.cerebro.broker.getvalue()
def run(self):
opt = optunity.maximize(
f=self.params_optimize,
num_evals=self.event_num,
solver_name='particle swarm',
break_through=[0.01, 0.4],
callback=[0.01, 0.4],
down_day=[2, 20],
period=[20, 200],
stop_loss=[0.02, 0.2],
take_profit=[0.02, 0.4]
)
optimal_pars, details, _ = opt # optimal_pars 最优参数组合
self.params = optimal_pars
print(self.params)
def get_trader_info(self):
return 'Starting Portfolio Value: %.2f \n' % self.cash \
+ 'Final Portfolio Value: %.2f \n' % self.cerebro.broker.getvalue() \
+ f'Final Portfolio Percentage: {100.0 * self.cerebro.broker.getvalue() / self.cash} %\n' \
+ f'PARAMS {self.params}\n'
def get_optimal_parameters(self):
"""
获取最优参数
:return:
"""
return self.params
运行策略
if __name__ == '__main__':
path = "D:\\work\\git\\Tools\\static\\data\\ETHUSDT_1h.csv"
data = load_csv_data(path)
cerebro = create_cerebro()
cerebro.adddata(data)
cerebro.addstrategy(BreakthroughStrategy)
cerebro.run()
print(".2f" % cerebro.broker.getvalue() - 10000)
cerebro.plot()
运行优化器
if __name__ == '__main__':
path = "D:\\work\\git\\Tools\\static\\data\\ETHUSDT_1h.csv"
k_data = load_csv_data(path, datetime.datetime(2021, 1, 1), datetime.datetime(2021, 12, 1))
optimizer = BreakThroughStrategyOptimizer()
optimizer.set_data(k_data)
optimizer.run()
目前从回测结果来看,该策略在破产的的边缘试探,表现结果十分糟糕。下一篇文章会对该策略进行优化,使策略可持续盈利,加油!!!!