分享

MFI指标及其背离应用代码讲解

 wuxi1969 2023-04-04 发布于上海

今天给大家带来的内容是关于背离策略的代码实现方法。

在几乎所有的技术指标功效中,都有一种背离的功能,也就是预示市场走势即将见顶或者见底,其中MACD、RSI和CCI都有这种提示作用。

投资者可以用这些指标的背离功能来预测头部的风险和底部的买入机会,但在选择的时间参数上应适当延长。

先简单介绍一下指标背离的理论基础。

背离是指当股价或指数在下跌或上涨过程中不断创新低(高),而一些技术指标不跟随股价或指数创新低(高),形成背离。

下面以 MFI 指标举例先简单介绍一下 MFI 指标:

MFI 指标 也可以叫资金流量指标是相对强弱指标和人气指标(OBV)两者的结合。 MFI指标实际是将RSI加以修改后,演变而来。RSI以成交价为计算基础;MFI指标则结合价和量,将其列入综合考虑的范围。可以说,MFI指标是成交量的RSI指标。

MFI 指标可以用于测度交易量的动量和投资兴趣,而交易量的变化为股价未来的变化提供了线索,所以 MFI 指标可以帮助判断股票价格变化的趋势。

其对应的计算公式和应用法则如下:

MFI 其指标计算公式如下:

TYP:=(HIGH+LOW+CLOSE)/3;

V1:=SUM(IF(TYP>REF(TYP,1),TYP*VOL,0),N)/SUM(IF(TYP<REF(TYP,1),TYP*VOL,0),N)

MFI:100-(100/(1+V1))

具体是说:

1.先计算一定期限内(一般14天)每天的典型价格(即TYP ),它是当天最高价,最低价和收盘价三者的均值。也有给收盘价更大权值再算三者均值的算法。

简化下来就是这样一句话:典型价格(TP)= 当日最高价、最低价与收盘价的算术平均值

2.如果当天的典型价格大于昨天的典型价格则定义为流入,反之为流出,流入流出金额为典型价格乘以当天交易量。这样把14天每天结果计算出来,然后再把流出额和流入额分别加总,得到14天内的流入总额和流出总额,接着前者除以后者,大于1则14天内的资金为流入,反之为流出。V1就是代表这个比值。

简化下来就是:货币流量(MF)=典型价格(TP)×N日内成交量。如果当日MF>昨日MF,则将当日的MF值视为正货币流量(PMF),如果当日MF<昨日MF,则将当日的MF值视为负货币流量(NMF)

3.MFI就是在V1的基础上,为了更好地在坐标上显示出来,进行的数据处理。 MFI=100-[100/(1+PMF/NMF)],参数周期一般设为14天。

同时,在 V1 的计算过程中,因为有出现连续 14天 均为正货币流量的情形,负货币流量的流出总额可能为 0 ,会导致公式计算错误,因此出现此情形将会把对应的MFI 值设置为100

买入信号

1.MFI<20时,代表资金短期冷却讯号.但是,必须等待MFI指标再度向上突破20时,才能确认资金转向.

2.MFI在20左右的水平,出现一底比一底高,和股价“背离”的现象时,可视为中期反转上涨的讯号.

3.MFI指标连续二次向上交叉其平均线时,视为买进讯号.(平均线一般设定为6天).

卖出信号

1.MFI>80时,代表资金短期过热讯号.但是,必须等待MFI指标再度向下跌破80时,才能确认资金转向.

2.MFI在80左右的水平,出现一顶比一顶低,和股价“背离”的现象时,可视为中期反转下跌的讯号.

3.MFI指标连续两次向下交叉其平均线时,视为卖出讯号.(平均线一般设定为6天).

因为这里为教学背离的代码讲解使用,因此将会采用第2点应用规则,即用背离的买入卖出信号进行策略编写

接下来,我们再详细介绍背离买入卖出原理依据:

1、顶背离:当价格(收盘价)走势一浪高过一浪,行情一直在上涨(即不断创新高),而 MFI 指标却是一浪低过一浪。则表示价格虽然创出了新高,但是 MFI 动能却未创新高。这种情况叫做顶背离。

因此顶背离的现象:

表示价格上涨过程力量不足,外强中干,暗示未来很快会有一波下跌,是强烈的下跌信号,因此要执行标的卖出的操作

2、底背离:反之当价格(收盘价)走势一浪低过一浪,行情一直在下跌(即不断创新低),而 MFI 指标却是一浪高过一浪,则表示价格虽创出新低,而 MFI 动能却未创新低,这种情况叫底背离。表示价格下跌过程动能不足,暗示未来会有一波反弹,是强烈的上涨信号

那么回到正题,我们是否可以利用量化的方式解决这个的问题场景呢?当然还是可以的。

按照课程教授的金字塔拆解方法,我们来梳理下背景信息。

第一步先有想法:当出现底背离的情况,进行买入的操作,出现顶背离的情况,进行卖出的操作。

即我们此次交易的主观描述是:当未持仓的状态,价格不断下跌,创新低的同时,MFI 指标没有出现新低,反而越来越高,进行买入。

那怎么确认收盘价创新低呢?因为回测的时候,未来的价格是无法预测的,因此,要做一个拐点的数据收集,即 前天收盘价大于昨天的收盘价,昨天的收盘价小于今天的收盘价的时候,将昨天的拐点数据收集起来~

此外,当持仓的状态,价格不断上涨,创新高的同时,MFI 指标没有出现新高,反而越来越低,进行卖出。

那怎么确认收盘价创新高呢?同理,这里也需要做拐点的数据收集。 前天收盘价小于昨天的收盘价,昨天的收盘价大于今天的收盘价的时候,将昨天的拐点数据收集起来~

接下来第二步,我们先从 MFI 指标底背离买入标的逻辑走!

因为底背离的情况需要两个底点去判断趋势情况,因此首先要确认什么是底点,这里采用了拐点的形式记录底点,比如前天的收盘价大于昨天的收盘价,昨天的收盘价小于今天的收盘价那我们就将昨天的点设置为底点,同时记录 ①收盘价,②当天的 MFI 指标值,③当前位于的 K 线长度。

为什么要记录这3个指标值呢?

①记录收盘价,用于与下个底点比较是否价格在下跌,从而判断趋势走向

②记录当天的 MFI 指标值,用于与下个底点比较 MFI 指标值是否往上升

③记录当前位于的 K 线长度,用于判断买卖点之间相隔多少个交易日以及买卖时间节点是否按顺序进行

!
导入失败


同理 MFI 顶背离卖出标的逻辑为:

因为顶背离的情况需要两个顶点去判断趋势情况,因此首先要确认什么是顶点,同理,这里采用了拐点的形式记录顶点,比如前天的收盘价小于昨天的收盘价,昨天的收盘价大于今天的收盘价,将昨天的点设置为底点同时记录 ①收盘价,②当天的 MFI 指标值,③当前位于的 K 线长度。

!
导入失败


接下来我们将指标关系用数字语言串联起来。还是按 ForTrader 的策略编写框架顺序来构建指标关系,即按照标的、择时、风控3个模块拆分指标关系。

而由于此次主要是在做验证性实验,指标部分没有进行量化选股,默认选取浦发600000.XSHG作为实验标的。

这里,择时需要满足的条件有:

底背离:

1)前底的收盘价 大于 后底的收盘价

2)前底的 MFI 值小于后底的 MFI 值

顶背离:

1)前顶的收盘价 小于 后顶的收盘价

2)前顶的 MFI 值大于后顶的 MFI 值

我们一个一个来,首先是底背离

前底的收盘价大于后底的收盘价,这个意味着收盘价逐渐下降,转换成数据语言就是:

(前底)收盘价 >(后底)收盘价

第二个条件,前底的 MFI 值小于后底的 MFI 值,这个意味着 MFI 的值逐渐增加,转换成数据语言就是:

(前底) MFI <(后底)MFI

对于顶背离:

前顶的收盘价小于后顶的收盘价,这个意味着收盘价逐渐上涨,转换成数据语言就是:

(前顶)收盘价 <(后顶)收盘价

第二个条件,前顶的 MFI 值大于后顶的 MFI 值,这个意味着 MFI 的值逐渐减少,转换成数据语言就是:

(前顶) MFI >(后顶)MFI

风控:这里会使用到跟踪止损的风控知识,这里风控因人而异,(助教比较喜欢用跟踪止损)。

至此,MFI 背离相关指标的数学关系就已经整理完成了。

!
导入失败


接下来,我们正式开始策略的编写。

编写策略第一个步骤,我们需要先设置标的池 context.symbol_list。这是一个固定配置,没有这一句代码我们的程序将无法运行成功。

def choose_stock(context):
'''标的'''
context.symbol_list = ['600000.XSHG']

我们进行背离指标计算的数据来源,来源于标的函数里面的 context.symbol_list = ['600000.XSHG'] 代码,由 context.symbol_list 指定获取对应的数据,如标的的开盘价、收盘价、最高价、最低价、成交量等数据。

首先看看我们指标模块。

def indicators(context):
    '''指标'''
    # 储存 MFI
    context.list_MFI = []
    # 用于存储 MFI 高处拐点的列表
    context.top_divergence = []
    # 用于存储 MFI 低处拐点的列表
    context.bottom_divergence = []
    # 累积运行代码时候,K线的长度
    context.number = 0
    # 最大资金使用范围
    context.money = 0.9
    # 设置最高价为0
    context.h_price = 0
    # 设置止损比例
    context.stop_rate = 0.05
    # 记录买卖点的 K 线位置
    context.point = 0
context.list_MFI = []

因为 MFI 的值是需要计算获取的,因此我们先创建一个列表,储存每一天计算出来的 MFI 值,计算过程会在择时函数里面进行讲解。

# 用于存储 MFI 高处拐点的列表
context.top_divergence = []
# 用于存储 MFI 低处拐点的列表
context.bottom_divergence = []

这段代码用于储存满足 拐点 条件的值,用于后面的择时函数进行提取比较。这部分数据的存储和提取流程放在后面,待会将会仔细讲解。

接下来,看最后的几行代码。

# 累积运行代码时候,K线的长度
context.number = 0
# 最大资金使用范围
context.money = 0.9
# 设置最高价为0
context.h_price = 0
# 设置止损比例
context.stop_rate = 0.05
# 记录买卖点的 K 线位置
context.point = 0
# 累积运行代码时候,K线的长度
context.number = 0

这个代码用于记录回测的时候,当天属于第几个交易日。

# 最大资金使用范围
context.money = 0.9

这里默认是九成仓位进行交易。

# 设置最高价为0
context.h_price = 0
# 设置止损比例
context.stop_rate = 0.05

这里是定义最高价为0,后续用于风控的跟踪止损,当低于最高价 5% 的时候,卖出标的止损。

接下来是标的代码的讲解。这里我们设置标的为【浦发银行】即可,具体代码如下:

def choose_stock(context):
'''标的'''
context.symbol_list = ['600000.XSHG']

因为标的没有进行量化选股,我随机挑选了600000.XSHG 浦发银行作为选股标的。

然后是择时模块代码的编写,这部分主要控制标的买入和卖出的时机。来看看刚才定义的主观描述:底背离买入,顶背离卖出。

先看看择时代码怎么书写吧。

def timing(context):
    '''择时'''
    # 计算全仓买入的股数
    size = context.broker.getvalue() * context.money / context.data.close[0] // 100*100

    # 当前K线长度,用于后面进行两点之间的相减,比较 前点 到 后点 经历了多少个交易日
    context.number += 1

    # 判断是否已经经过14个交易日,并开始计算 MFI
    if context.number >= 14:
        # 计算每日的 MFI 指标
        # 正货币流量(PMF)
        context.PMF = 0
        # 负货币流量(NMF)
        context.NMF = 0
        # MFI 周期一般设为14天
        for i in range(14):
            # 典型价格(TP)= 当日最高价、最低价与收盘价的算术平均值
            context.TP_new = (context.data.close[-i] + context.data.low[-i] + context.data.high[-i]) / 3
            context.TP_old = (context.data.close[-i-1] + context.data.low[-i-1] + context.data.high[-i-1]) / 3
            # 如果当日 TP > 昨日 TP ,为正货币流量(PMF),计算正货币流量的流入总额
            if context.TP_new > context.TP_old:
                context.PMF += context.TP_new * context.data.volume[-i]
            # 如果当日 TP < 昨日 TP,为负货币流量(NMF),计算负货币流量的流出总额
            elif context.TP_new < context.TP_old:
                context.NMF += context.TP_old * context.data.volume[-i]
        # MFI 指标计算公式,并储存
        # 当出现连续 14天 均为正货币流量的情形,负货币流量的流出总额可能为 0 ,会导致公式计算错误,因此出现此情形需要使用 DivByZero() 进行除法计算
        if context.NMF != 0:
            context.MFI = 100-(100/(1+context.PMF/context.NMF))
        else:
            context.MFI = 100
        # 储存 MFI
        context.list_MFI.append(context.MFI)

# 判断是否有 3 个点可用于进行拐点判断
        if len(context.list_MFI) >= 3:
         # 记录周期里面的股价最高值拐点及其对应的 MFI 的值
            if context.data.close[-2] < context.data.close[-1] and context.data.close[0] < context.data.close[-1]:
                context.top_divergence.append([context.data.close[-1],context.list_MFI[-2],context.number])
            # 记录周期里面的股价最低值拐点及其对应的 MFI 的值
            if context.data.close[-2] > context.data.close[-1] and context.data.close[0] > context.data.close[-1]:
                context.bottom_divergence.append([context.data.close[-1],context.list_MFI[-2],context.number])
            # MFI 底背离买入,这里采用了邻峰对比,同时 context.bottom_divergence[-2][2] > context.point 是为了避免卖出平仓后,以之前的判断重复,再次进行买入
            # 判断是否持仓
            if context.position.size == 0:
                # 因为需要有2个点进行比较,所以需要两个数据
                if len(context.bottom_divergence) >= 2:
                    # MFI在20左右的水平,这里用了10~30的区间,出现一底比一底高,和股价下跌“背离”的现象时,可视为中期反转上涨的讯号
                    if abs(context.bottom_divergence[-1][1] - 20) <= 10:
                        if context.bottom_divergence[-2][0] > context.bottom_divergence[-1][0] and context.bottom_divergence[-2][1] < context.bottom_divergence[-1][1] and context.bottom_divergence[-2][2] > context.point:
                            context.buy(data=context.data,size=size)
                            # 用于查找是否找对两个波
                            context.log('这里是{}前底峰'.format(context.bottom_divergence[-2]))
                            context.log('这里是{}后底峰'.format(context.bottom_divergence[-1]))
                            # 记录此时第二个底点买入的K线位置
                            context.point = context.number
            # MFI 顶背离卖出,需要有2个高点,且因为先买后卖,顶背离的卖点需要在买的K线之后
            elif len(context.top_divergence) >= 2 and context.top_divergence[-2][2] > context.point:
                # MFI在80左右的水平,这里用了70~90的区间,出现一顶比一顶低,和股价“背离”的现象时,可视为中期反转下跌的讯号
                if abs(context.top_divergence[-1][1] - 80) <= 10:
                    if context.top_divergence[-2][0] < context.top_divergence[-1][0] and context.top_divergence[-2][1] > context.top_divergence[-1][1]:
                        context.close(data=context.data,size=size)
                        # 重置最高价
                        context.h_price = 0
                        # 记录此时清仓的K线值
                        context.point = context.number
                        context.log('这里是{}前高峰'.format(context.top_divergence[-2]))
                        context.log('这里是{}后高峰'.format(context.top_divergence[-1]))
                        context.log('这里执行清仓')
# 计算全仓买入的股数
size = context.broker.getvalue() * context.money / context.data.close[0] // 100*100

这个代码是用来计算九成仓位所能够购买的标的股数。

context.number += 1

这个代码用来记录这是第几天运行代码,可以获取总共进行了多少个交易日,这里是每回测一次,就是过了一天的交易日,所以进行 +1。

# 判断是否已经经过14个交易日,以此确定 MFI 指标列表可以取到 [-2] 的值
if context.number >= 14:

因为 MFI 指标需要用到过去 14 天交易日里面的最高价、最低价、收盘价的数据进行计算,所以一开始的14天需要进行数据累积计算

当把数据取出来之后,就开始计算了,下面是每个交易日中,进行 MFI 指标计算的公式公式已经在 MFI 指标介绍过程,进行过解释

# 计算每日的 MFI 指标
# 正货币流量(PMF)
context.PMF = 0
# 负货币流量(NMF)
context.NMF = 0
# MFI 周期一般设为14天
for i in range(14):
    # 典型价格(TP)= 当日最高价、最低价与收盘价的算术平均值
    context.TP_new = (context.data.close[-i] + context.data.low[-i] + context.data.high[-i]) / 3
    context.TP_old = (context.data.close[-i-1] + context.data.low[-i-1] + context.data.high[-i-1]) / 3
    # 如果当日 TP > 昨日 TP ,为正货币流量(PMF),计算正货币流量的流入总额
    if context.TP_new > context.TP_old:
        context.PMF += context.TP_new * context.data.volume[-i]
    # 如果当日 TP < 昨日 TP,为负货币流量(NMF),计算负货币流量的流出总额
    elif context.TP_new < context.TP_old:
        context.NMF += context.TP_old * context.data.volume[-i]
    # MFI 指标计算公式,并储存
# 当出现连续 14天 均为正货币流量的情形,负货币流量的流出总额可能为 0 ,会导致公式计算错误,因此出现此情形需要设置为 100
if context.NMF != 0:
    context.MFI = 100-(100/(1+context.PMF/context.NMF))
else:
    context.MFI = 100

# 储存 MFI
context.list_MFI.append(context.MFI)
# 正货币流量(PMF)
context.PMF = 0
# 负货币流量(NMF)
context.NMF = 0

先设置两个变量进行储存货币流入总额和货币流出总额,货币流入总额为正货币流量,货币流出总额为负货币流量,用于后续计算 V1

# MFI 周期一般设为14天
for i in range(14):
# 典型价格(TP)= 当日最高价、最低价与收盘价的算术平均值
context.TP_new = (context.data.close[-i] + context.data.low[-i] + context.data.high[-i]) / 3
context.TP_old = (context.data.close[-i-1] + context.data.low[-i-1] + context.data.high[-i-1]) / 3
# 如果当日 TP > 昨日 TP ,为正货币流量(PMF),计算正货币流量的流入总额
if context.TP_new > context.TP_old:
context.PMF += context.TP_new * context.data.volume[-i]
# 如果当日 TP < 昨日 TP,为负货币流量(NMF),计算负货币流量的流出总额
elif context.TP_new < context.TP_old:
context.NMF += context.TP_old * context.data.volume[-i]
# MFI 指标计算公式,并储存
# 当出现连续 14天 均为正货币流量的情形,负货币流量的流出总额可能为 0 ,会导致公式计算错误,因此出现此情形需要设置为 100
if context.NMF != 0:
context.MFI = 100-(100/(1+context.PMF/context.NMF))
else:
context.MFI = 100

在这个for循环里面,循环14次的原因是,计算 MFI 指标的参数默认是14天,每次循环都会计算出当天的最高价、最低价与收盘价的算数平均值 和 前一天的最高价、最低价与收盘价的算数平均值,两者进行比较,如果当日的典型价格( TYP )值 > 昨日的典型价格( TYP )值,视为正货币流量(PMF)计算正货币流量的流入总额,同理,如果当日 TP < 昨日 TP为负货币流量(NMF)计算负货币流量的流出总额。

总共循环14次,把最近14个交易日的货币流量进行计算后,最后开始计算 MFI 的值,因为有可能出现连续 14 天均为正货币流量的情形,负货币流量的流出总额可能为0,会导致分母为0,因此公式计算错误,因此出现此情形需要设置 MFI 的值为 100。

当完成 MFI 指标数据计算之后,先把每一天的 MFI 值储存到 context.list_MFI 列表里面,用于查看每天的 MFI 的值变化

最后就要按照顶背离和底背离的原理开始进行把高(低)点的数据进行记录。

实现这个功能的代码如下:

# MFI 顶背离数据记录
if len(context.list_MFI) >= 3:
# 记录周期里面的股价最高值拐点及其对应的 MFI 的值
    if context.data.close[-2] < context.data.close[-1] and context.data.close[0] < context.data.close[-1]:
        context.top_divergence.append([context.data.close[-1],context.list_MFI[-2],context.number])
    # 记录周期里面的股价最低值拐点及其对应的 MFI 的值
    if context.data.close[-2] > context.data.close[-1] and context.data.close[0] > context.data.close[-1]:
        context.bottom_divergence.append([context.data.close[-1],context.list_MFI[-2],context.number])

首先要判断拐点,至少需要有3个 MFI 指标值进行判断,所以这里需要至少在列表里面记录了3个 MFI 值才能够进行拐点判断

然后,当昨天的收盘价符合拐点的特征时,将其对应的三个数据记录到对应的高处拐点列表或低处拐点列表中。

还记得是哪三个数据吗?

①context.data.close[-1] 昨天的收盘价的值

②context.context.list_MFI[-2] 昨天的 MFI 的值

③context.number 当天处于回测区间内第几个交易日

接下来要开始实际的买入了,我们看下下面的代码。

# 判断是否持仓
if context.position.size == 0:
    # 因为需要有2个点进行比较,所以需要两个数据
    if len(context.bottom_divergence) >= 2:
        # MFI在20左右的水平,这里用了10~30的区间,出现一底比一底高,和股价下跌“背离”的现象时,可视为中期反转上涨的讯号
        if abs(context.bottom_divergence[-1][1] - 20) <= 10:
            if context.bottom_divergence[-2][0] > context.bottom_divergence[-1][0] and context.bottom_divergence[-2][1] < context.bottom_divergence[-1][1] and context.bottom_divergence[-2][2] > context.point:
                context.buy(data=context.data,size=size)
                # 用于查找是否找对两个波
                context.log('这里是{}前底峰'.format(context.bottom_divergence[-2]))
                context.log('这里是{}后底峰'.format(context.bottom_divergence[-1]))
                # 记录此时第二个底点买入的K线位置
                context.point = context.number

根据我们前面的表述,我们首先需要在未持仓的情况下,判断是否出现底背离的情况,如果出现就可以进行标的的买入。

因此在这里我们除了要判断当前是否持仓之外,还要判断 context.bottom_divergence 这个存储低处拐点的列表是否拥有2个数据,毕竟只有2个低点才能进行底背离的判断。

接下来,context.bottom_divergence[-1] 可以取到后底的三个数据,context.bottom_divergence[-2] 即取前底的三个数据。

if abs(context.bottom_divergence[-1][1] - 20) <= 10:

我们在第3个 if 进行 MFI 值大小的判断。因为 MFI 指标的底背离在 MFI 指标值为 20 左右的水平有效

因此在这里我是用了10~30的区间,MFI 指标出现一底比一底高,和股价下跌“背离”的现象时,便将其视为中期反转上涨的讯号,进行标的的买入。

在明确这个条件的写法之前,我们先讲解一下各个值是怎么呈现的。还记得底背离的择时条件是怎样的么?

这里得用 if 判断语句进行底背离两个底点的买入判断,即当 前底的收盘价比后底的收盘价 还要高,即 P(前)大于 P(低),同时前底 MFI 的值小于后底 MFI 的值,即 M(前)小于 M(后)。出现价格创新低,但指标一底比一底低的底背离现象,进行 标的买入 操作。

前面说过context.bottom_divergence[-1] 可以取到后底的三个数据,context.bottom_divergence[-2] 可以取前底的三个数据。

因此,前底的收盘价格是 context.bottom_divergence[-2][0]

后底的收盘价格是 context.bottom_divergence[-1][0]

前底的 MFI 值 context.bottom_divergence[-1][1]

后底的 MFI 值 context.bottom_divergence[-2][1]

同时 context.bottom_divergence[-2][2] > context.point 是为了避免之后卖出标的后,会以之前低处拐点列表储存的数据去进行底背离判断,造成卖出标的后又立即买入标的情况出现。

# 记录此时第二个底点买入的K线位置
context.point = context.number

这个地方是用于后续判断顶背离卖出标的,避免后续出现过的顶背离时间在 买入标的的时间前面,导致用了错误的卖出判断条件

然后我们再看看顶背离卖出的代码。

# MFI 顶背离卖出,需要有2个高点,且因为先买后卖,顶背离的卖点需要在买的K线之后
elif len(context.top_divergence) >= 2 and context.top_divergence[-2][2] > context.point:
    # MFI在80左右的水平,这里用了70~90的区间,出现一顶比一顶低,和股价“背离”的现象时,可视为中期反转下跌的讯号
    if abs(context.top_divergence[-1][1] - 80) <= 10:
        if context.top_divergence[-2][0] < context.top_divergence[-1][0] and context.top_divergence[-2][1] > context.top_divergence[-1][1]:
            context.close(data=context.data,size=size)
            # 重置最高价
            context.h_price = 0
            # 记录此时清仓的K线值
            context.point = context.number
            context.log('这里是{}前高峰'.format(context.top_divergence[-2]))
            context.log('这里是{}后高峰'.format(context.top_divergence[-1]))
            context.log('这里执行清仓')

同样的,要出现顶背离状态,也要求至少要有两个高点,因此需要判断 context.top_divergence 这个列表的长度是否大于 2。同时要确保用于顶背离判断的前高点在此前低点记录的位置后面。

另外,当前顶的收盘价比后顶的收盘价还要低,即 P(前)小于 P(低),同时前顶 MFI 的值比后顶 MFI 的值还要高时,即 M(前)大于 M(后)。出现价格创新高,但指标一顶比一顶低的顶背离现象时,进行标的卖出操作 。

至于风控模块,我比较喜欢用跟踪止损的代码,入门课第八关有讲跟踪止损的知识点,此处不作详细解释,只需要了解,跟踪止损是激进型的止损策略,特点是让盈利跟随价格奔跑,止损价格跟踪价格的正向波动自动调整,当收盘价格比跟踪到的价格少了5% 的时候,就会触发卖出,及时止盈。

def control_risk(context):
    '''风控'''
    if context.position.size != 0:
            # 获取最高价
            context.h_price = max(context.h_price, context.position.price, context.data.close[0])

            # 计算止损价
            stop_price = (1 - context.stop_rate) * context.h_price

            # 如果当前股价小于止损价
            if context.data.close[0] < stop_price:
                # 执行平仓
                context.close(data=context.data)
                # 重置最高价
                context.h_price = 0
                # 记录此时清仓的K线值
                context.point = context.number

代码写好了,接下来我们正式开始回测,回测条件如下:

!
导入失败


回测结果是这样的。

!
导入失败


可以看到这个策略的胜率有 100%,超额收益是 12.67%-(-15.66%)得出 28.33%,由此可以得出一个结论:当我们选择 MFI 背离和跟踪止损的策略时,或许会给我们带来一收益。但最大回撤和夏普比率数据较差,这点需要多加关注。

以上,我们就完成了一次完整的策略回测。

不难看出,当我们的脑海里有了想法之后,光靠空想和纸上谈兵是得不出个结论的,从别处听来的结论想必我们也不太敢轻易就尝试。所谓纸上得来终觉浅,绝知此事要躬行,做量化交易一定要勤动脑和勤动手,才能在每次灵感迸发的时刻抓住它们,找到属于自己的交易之路。

此外,从刚才 MFI 背离策略的结果来看,他触发条件比较困难,在整一个回测周期里面只有两次的符合开仓条件,这个地方是拥有一定的代码局限

比如因为此处的峰点之间的交易日间隔较小,导致趋势容易误判,可以在代码里面加上两个峰点至少间隔多少个交易日去判断。

这部分我已经用 context.number 帮同学把每个峰点位于的交易日存储起来,同学需要做的是进行至少间隔多少个交易日的判断! 此处是一千个人心中有一千个哈姆雷特,是一个开放性的命题,需要排找到属于你自己的策略,从而实践!

因此该背离仍有可优化的地方,本次因为属于验证性实验方法,只写个已经能用的模板,如果同学你有你自己的灵感,可以基于这个基础进行修改。

比如,助教是先确立价格的拐点,然后再把对应的指标值提取并储存。

同学也可以先确立指标的拐点,然后再把对应的收盘价提取并储存。

此外,从刚才这个 MFI背离策略的结果来看,它的胜率是100%,那就说明他是百战百胜的策略了吗?

其实我们也不能就此确认,毕竟这个策略的触发是如此艰难。

任何分析的方法都有其局限性和失败的情况,这种时候,我们还有一个杀手锏是止损!不要小看这个杀手锏,只有学会止损,及时止损,承认错误,才能减少损失,防止被套牢!

因此,风控这部分也有可优化的地方,可以不用跟踪止损,可以用同学自已灵感迸发出的风控方法。

当然,你也可以在基于这个背离策略的基础上,设计更加严格的背离判断,或许条件触发的可能性低了,但收益或胜率或许会有提升。

并且,你在学习方法后,可以不断寻求策略中的较优解,同时,也会让自己的投资想法变得更加成熟。

世间所有美好的事,都值得你花点时间慢慢来,让我们一起学习量化交易,回归理性操作,降低交易风险,提升获利概率!

ForTrader分享链接:https://www./app/#/home?share_code=LBd7QoKPr40m6e49

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多