海龟策略的信号来源于唐奇安通道突破。简单的说就是若突破上轨则做多,突破下轨则做空。我们可以对信号进行改良,如换成布林带通道,金肯特纳通道等等,并且增加过滤条件和离场条件。
但是呢?新的问题又来了:若用了新的指标,需要通过不断调试来得到“最优”参数,这样会耗费大量的时间。
那么,有没有办法在尽量少得时间内,尽可能得到全局最优解或者次优解呢?
答案就是遗传算法啦!
遗传算法原理
具体原理详见:
遗传算法原理简介
一文读懂遗传算法工作原理(附Python实现)
遗传算法要做的事情并不复杂:
- 随机生成一大推策略参数(称之为族群,族群内的个体对应某一组策略参数)
- 族群内个体间进行3类活动:个体间两两交叉互换;个体某个参数发生变异;个体繁殖(即直接复制参数)
- 形成子代
- 通过目标函数(如最大化夏普比率,最大化总盈亏等)对母代族群和子代族群进行评分
- 通过特点筛选标准(如NSGA-Ⅱ)从母代和子代中筛选个体,形成第二代族群。(类似进化论中的“自然选择”)
- 新的族群在特定的评分标准和筛选标准中不断迭代(即重复1-5步骤),得到最优解/次优解。
遗传算法代码示例
1.随机生成待优化的策略参数
def parameter_generate():
'''
根据设置的起始值,终止值和步进,随机生成待优化的策略参数
'''
parameter_list = []
p1 = random.randrange(4,50,2) #入场窗口
p2 = random.randrange(4,50,2) #出场窗口
p3 = random.randrange(4,50,2) #基于ATR窗口止损窗
p4 = random.randrange(18,40,2) #基于ATR的动态调仓
parameter_list.append(p1)
parameter_list.append(p2)
parameter_list.append(p3)
parameter_list.append(p4)
return parameter_list
- 设置目标优化函数(收益回撤比和夏普比率)
def object_func(strategy_avg):
"""
本函数为优化目标函数,根据随机生成的策略参数,运行回测后自动返回2个结果指标:收益回撤比和夏普比率
"""
# 创建回测引擎对象
engine = BacktestingEngine()
# 设置回测使用的数据
engine.setBacktestingMode(engine.BAR_MODE) # 设置引擎的回测模式为K线
engine.setDatabase("VnTrader_Daily_Db", 'XBTHOUR') # 设置使用的历史数据库
engine.setStartDate('20170401') # 设置回测用的数据起始日期
engine.setEndDate('20181230') # 设置回测用的数据起始日期
# 配置回测引擎参数
engine.setSlippage(0.5)
engine.setRate(0.2/100)
engine.setSize(10)
engine.setPriceTick(0.5)
engine.setCapital(1000000)
setting = {'entryWindow': strategy_avg[0], #布林带窗口
'exitWindow': strategy_avg[1], #布林带通道阈值
'atrWindow': strategy_avg[2], #CCI窗口
'artWindowUnit': strategy_avg[3],} #ATR窗口
#加载策略
engine.initStrategy(TurtleTradingStrategy, setting)
# 运行回测,返回指定的结果指标
engine.runBacktesting() # 运行回测
#逐日回测
engine.calculateDailyResult()
backresult = engine.calculateDailyStatistics()[1]
returnDrawdownRatio = round(backresult['returnDrawdownRatio'],2) #收益回撤比
sharpeRatio= round(backresult['sharpeRatio'],2) #夏普比率
return returnDrawdownRatio , sharpeRatio
3.运行基于Deap库的遗传算法(具体步骤看代码中文注释)
#设置优化方向:最大化收益回撤比,最大化夏普比率
creator.create("FitnessMulti", base.Fitness, weights=(1.0, 1.0)) # 1.0 求最大值;-1.0 求最小值
creator.create("Individual", list, fitness=creator.FitnessMulti)
def optimize():
""""""
toolbox = base.Toolbox() #Toolbox是deap库内置的工具箱,里面包含遗传算法中所用到的各种函数
# 初始化
toolbox.register("individual", tools.initIterate, creator.Individual,parameter_generate) # 注册个体:随机生成的策略参数parameter_generate()
toolbox.register("population", tools.initRepeat, list, toolbox.individual) #注册种群:个体形成种群
toolbox.register("mate", tools.cxTwoPoint) #注册交叉:两点交叉
toolbox.register("mutate", tools.mutUniformInt,low = 4,up = 40,indpb=0.6) #注册变异:随机生成一定区间内的整数
toolbox.register("evaluate", object_func) #注册评估:优化目标函数object_func()
toolbox.register("select", tools.selNSGA2) #注册选择:NSGA-II(带精英策略的非支配排序的遗传算法)
#遗传算法参数设置
MU = 40 #设置每一代选择的个体数
LAMBDA = 160 #设置每一代产生的子女数
pop = toolbox.population(400) #设置族群里面的个体数量
CXPB, MUTPB, NGEN = 0.5, 0.35, 40 #分别为种群内部个体的交叉概率、变异概率、产生种群代数
hof = tools.ParetoFront() #解的集合:帕累托前沿(非占优最优集)
#解的集合的描述统计信息
#集合内平均值,标准差,最小值,最大值可以体现集合的收敛程度
#收敛程度低可以增加算法的迭代次数
stats = tools.Statistics(lambda ind: ind.fitness.values)
np.set_printoptions(suppress=True) #对numpy默认输出的科学计数法转换
stats.register("mean", np.mean, axis=0) #统计目标优化函数结果的平均值
stats.register("std", np.std, axis=0) #统计目标优化函数结果的标准差
stats.register("min", np.min, axis=0) #统计目标优化函数结果的最小值
stats.register("max", np.max, axis=0) #统计目标优化函数结果的最大值
#运行算法
algorithms.eaMuPlusLambda(pop, toolbox, MU, LAMBDA, CXPB, MUTPB, NGEN, stats,
halloffame=hof) #esMuPlusLambda是一种基于(μ+λ)选择策略的多目标优化分段遗传算法
return pop
遗传算法效果
夏普比率1.9,总收益率1958%,最大百分比回撤37%,收益回撤比达53。
其解集收敛程度如下:
在得到一个好的曲线后,还要检查一下这些参数是否符合市场逻辑,尽量去避免过拟合的情况。下面举个反例:
这里使用金肯特纳通道+基于固定百分比移动止损策略。不管从曲线的形态和收敛程度来看都是挺正常的
但是在这种优化后参数中我们观察到trailingPercent=18%,这意味着价格从最高点回落18个点才平仓离场。在正常情况,这会带来非常糟糕的盈亏比。
总结
遗传算法本质上是一种加快策略研究的技术,相对于暴力穷举,它可以大大节省电脑运算时间。我们可以使用它,但不能过度依赖它,因为有可能输出的仅仅是一些很巧合的参数。所以,针对这些参数,需要做更加细致的回测。
毕竟,在策略研究中,细心与耐心也是非常重要的。