请 [注册] 或 [登录]  | 返回主站

量化交易吧 /  数理科学 帖子:3364680 新帖:1

EMV指标的趋势效应与择时交易

醒掌天下权发表于:5 月 9 日 19:19回复(1)

计算EMV与画图的公用函数

def get_emv(security, start_date=None, end_date=None, n=10):
    trade_date = get_trade_days(start_date=start_date, end_date=end_date)
    HS_data=get_price(security,start_date=start_date,end_date=end_date,frequency='daily',fields=None,skip_paused=False,fq='pre')
    HS_data['Low_1'] = HS_data['low'].shift(1)
    HS_data['High_1'] = HS_data['high'].shift(1)
    Volume = HS_data['volume'].rolling(14).mean()
    Volume = Volume / HS_data['volume']
    Mid = 100* (HS_data['high']   HS_data['low'] - HS_data['High_1'] - HS_data['Low_1']) / (HS_data['high']   HS_data['low'])
    Temp = Mid * Volume * (HS_data['high'] - HS_data['low'])
    EMV = HS_data['high'] - HS_data['low']
    EMV = EMV.rolling(14).mean()
    EMV = (Temp / EMV).rolling(14).mean()
    MAEMV = EMV.rolling(9).mean().dropna()
    EMV = EMV.dropna()[8:]
    return EMV, MAEMV

def show_ema(EMA):
    plt.figure(figsize=(16, 3))
    plt.plot(EMV)
    plt.plot(MAEMV)
    plt.show()

EMA指标研究

简易波动指标(EMV),它是根据成交量和人气的变化,构成一个完整的股价系统循环。指示投资者在人气聚集且成交热络的时候买进股票,并且在成交量逐渐展现无力,而狂热的投资者尚未察觉能量即将用尽时,卖出股票。

原理:

用相对成交量除以相对振幅,作为衡量股价中间价波动百分比的基数,来得到股价中间价的相对波动范围。

公式(争议):

1、从百度、财经博客以及财经解析中获取的公式如下:

  • A=(今日最高 今日最低)/2
  • B=(前日最高 前日最低)/2
  • C= 今日最高-今日最低
  • EM=(A-B)*C/今日成交额
  • EMV= N日内EM的累和
  • MAEMV= EMV的M日的简单移动平均

2、同花顺计算公式如下:

  • VOLUME:=MA(VOL,N)/VOL;
  • MID:=100*(HIGH LOW-REF(HIGH LOW,1))/(HIGH LOW);
  • TEMP:= MIDVOLUME(HIGH-LOW);
  • EMV:MA(TEMP/MA(HIGH-LOW,N),N);
  • MAEMV:MA(EMV,M);

经过计算和对比,发现百度方式取得的数据倍数差距悬殊,同花顺计算方式获得的数据差距比较小,因此采用同花顺计算方式。(如有其他最优方法,希望可以交流或是批评指出)

使用方法:

  • 当EMV由下往上穿越0轴时,买进。
  • 当EMV由上往下穿越0轴时,卖出。
  • 当EMV由下往上穿越MAEMV时,买进。
  • 当EMV由上往下穿越MAEMV时,卖出。

    目的:

寻找最优参数N与M

导入需要模块

"""导入常用模块"""
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import datetime
import talib
from jqdata import *
from jqlib.technical_analysis import *
from environment import * # 导入大树工作室开发的回测模块

沪深300价量展示EMV

上图图例容易分辨出进出场位置,以零轴为中心的,但是我们后期可以加入金叉与死叉来辅助交易,它有特殊之处,其代表的是量价直接的关系,EMV线上升能显现出成交量与人气的增长,下降能显现出成交量与人气的枯竭。MAEMV则使得EMV更加平滑,具有转折的参考意义。改指标从其内在就能明白,不适合短线,而且急涨急跌行情也不适合,因为EMV的创造者认为,上升量是循序渐进的,而不是爆量增长。

EMV指标择时效应研究

深入了解该指标的时候,总让人有奇思妙想,单条指标线会如何呢,双条指标线会如何呢,多条呢。虽然自己在软件上去多次调整了EMV与MAEMV,看着效果一般,而且金死叉的搭配也不错,但是还需要数据来证明,所以大家就按照[魏稀饭]给大家喂稀饭的方式去了解该指标的部分情况吧。

本次研究的脉络为:

  • 单EMV指标回测-零轴判断
  • 双EMV指标回测(EMV MAEMV)-零轴判断,交叉判断

单EMV指标择时研究

单EMV指标,简单为一,不含糊,稀饭里面不加柴米油盐酱醋茶,至始至终皆为:量(水),价(米),比较清淡,吃起来总会感觉缺少了些什么,但是最基础的也是单一的稀饭(EMV)。咱们就来试试看这碗稀饭的水米搭配为多少最佳。

单EMV指标回测-零轴判断

  • 回测标的:沪深300指数。
  • 回测时间:2010年01月01日到2018年12月31日。
  • 初始资金:100000元。
  • 不考虑对冲成本,尽量投入全部资金。
  • 代码展示:
"""初始化以下内容"""
context = Context() # 账户对象
order = Order(context) # 下单对象
trade = Trade(context, order) # 回测对旬
context.start_date = '2010-01-01'
context.end_date = '2018-12-31'
context.universe = ['000300.XSHG']
context.base = '000300.XSHG'

"""策略主体"""
def handle(context, order):
    stock = context.universe[0]
    Ma_count = 50
    n = 14
    m = 9
    HS_da = get_price(security=stock, 
                      end_date= context.current_dt,
                      frequency= 'daily', 
                      fields= None, 
                      skip_paused= False, 
                      fq='pre',
                      count= Ma_count)
    EM_list = []
    for i in range(Ma_count):
        if i > (n-1):
            high_today = HS_da['high'][i]
            low_today = HS_da['low'][i]
            volume_today = HS_da['volume'][i]
            high_yesterday = HS_da['high'][i-1]
            low_yesterday = HS_da['low'][i-1]
            tmpVolume = talib.SMA(np.array(HS_da['volume'][(i-n 1):(i 1)]), n)[-1]/volume_today
            Mid = 100*(high_today   low_today - high_yesterday - low_yesterday)/(high_today   low_today)
            temp = Mid * tmpVolume * (high_today-low_today)
            high = talib.SMA(np.array(HS_da['high'][(i-n 1):(i 1)]), n)[-1]
            low = talib.SMA(np.array(HS_da['low'][(i-n 1):(i 1)]), n)[-1]
            EM = (temp/(high-low))     
        else:
            EM = 0
        EM_list.append(EM)

    HS_da['EM'] = EM_list
    HS_da['EMV'] = talib.SMA(np.array(HS_da['EM']), n)
    HS_da['MAEMV'] = talib.SMA(np.array(HS_da['EMV']), m)
    today_Emv = HS_da['EMV'][-1]
    yesterday_Emv = HS_da['EMV'][-2]
    today_MaEMV = HS_da['MAEMV'][-1]
    yesterday_Maemv = HS_da['MAEMV'][-2]

    if today_Emv > 0 and yesterday_Emv < 0:
        order.buy(stock, HS_da['close'][-1], context.cash // HS_da['close'][-1])
    elif today_Emv < 0 and yesterday_Emv > 0:
        if stock not in context.position.keys():
            return
        order.sell(stock, HS_da['close'][-1], context.position[stock]['count'])

"""执行策略"""
trade.trade(handle)


单回测数据来看,数据不是很符合心理要求,稀饭的味道明显有点烧焦味,我们需要调整看看,多方面的比例来找出最优的的参数。
已经吃完一汤勺的,给自己几拳,吐一吐,准备吃下一碗。

  • 回测标的:沪深300指数。
  • 回测时间:2010年01月01日到2018年12月31日。
  • 初始资金:100000元。
  • 不考虑对冲成本,尽量投入全部资金。
  • EMV参数:[5, 10, 15, 25, 30, 35, 40, 45, 50]
  • 代码展示:
trade_list = []
EMV_list = [5, 10, 15, 25, 30, 35, 40, 45, 50]
for _EMV in EMV_list:
    # 策略结构
    context = Context()
    order = Order(context)
    trade = Trade(context, order)
    context.start_date = '2010-01-01'
    context.end_date = '2018-12-31'
    context.universe = ['000300.XSHG']
    context.base = '000300.XSHG'


    def handle(context, order):
        stock = context.universe[0]
        Ma_count = 60
        n = _EMV
        m = 9
        HS_da = get_price(security=stock, 
                      end_date= context.current_dt,
                      frequency= 'daily', 
                      fields= None, 
                      skip_paused= False, 
                      fq='pre',
                      count= Ma_count)

        EM_list = []
        for i in range(Ma_count):
            if i > (n-1):
                high_today = HS_da['high'][i]
                low_today = HS_da['low'][i]
                volume_today = HS_da['volume'][i]
                high_yesterday = HS_da['high'][i-1]
                low_yesterday = HS_da['low'][i-1]
                tmpVolume = talib.SMA(np.array(HS_da['volume'][(i-n 1):(i 1)]), n)[-1]/volume_today
                Mid = 100*(high_today   low_today - high_yesterday - low_yesterday)/(high_today   low_today)
                temp = Mid * tmpVolume * (high_today-low_today)
                high = talib.SMA(np.array(HS_da['high'][(i-n 1):(i 1)]), n)[-1]
                low = talib.SMA(np.array(HS_da['low'][(i-n 1):(i 1)]), n)[-1]
                EM = (temp/(high-low))     
            else:
                EM = 0
            EM_list.append(EM)

        HS_da['EM'] = EM_list
        HS_da['EMV'] = talib.SMA(np.array(HS_da['EM']), n)
        HS_da['MAEMV'] = talib.SMA(np.array(HS_da['EMV']), m)
        today_Emv = HS_da['EMV'][-1]
        yesterday_Emv = HS_da['EMV'][-2]
        today_MaEMV = HS_da['MAEMV'][-1]
        yesterday_Maemv = HS_da['MAEMV'][-2]

        if today_Emv > 0 and yesterday_Emv < 0:
            if stock in context.position.keys():
                return
            order.buy(stock, HS_da['close'][-1], context.cash // HS_da['close'][-1])
        elif today_Emv < 0 and yesterday_Emv > 0:
            if stock not in context.position.keys():
                return
            order.sell(stock, HS_da['close'][-1], context.position[stock]['count'])
    trade.trade(handle, False)
    trade_list.append(trade)
# 展示
Trade.show_ratio_compare('EMV', EMV_list, trade_list, 3, 3)



由上图数据表对比分析,符合沪深300指数的EMV值为45,果然第一次一口闷的喂稀饭方式是不正确的。从这个比例看,于熊市时候,策略收益图下跌的斜率明显低于其他EMV数值的策略,于牛市时候,策略收益图上涨的斜率会高于其他EMV数值的策略,而且对比其他指标,最大回撤、胜率、策略收益、年化收益排名同列于第一,说明EMV值45是有效的最优参数,这个道理告诉我们:稀饭,一口一口吃,心急吃不了热豆腐,这块好的热豆腐温度45°。

双EMV指标择时研究

好稀饭的水与米比例,我们基本是以及确定了,但是上小菜也是必不可少的,现在我们上MAEMV这道菜,看是否符合市场味道。然后我们,下菜的方式有零轴判断、交叉判断、综合判断。

双EMV指标回测(EMV MAEMV)-零轴判断

  • 回测标的:沪深300指数。
  • 回测时间:2010年01月01日到2018年12月31日。
  • 初始资金:100000元。
  • 不考虑对冲成本,尽量投入全部资金。
  • EMV, MAEMV参数:[(5, 4),(10, 6),(15, 8), (25, 10), (30, 12), (35, 14), (40, 16), (45, 18), (50, 20)]
  • 代码展示:
trade_list = []
EMV_list = [(5, 4),(10, 6),(15, 8), (25, 10), (30, 12), (35, 14), (40, 16), (45, 18), (50, 20)]
for _EMV in EMV_list:
    # 策略结构
    context = Context()
    order = Order(context)
    trade = Trade(context, order)
    context.start_date = '2010-01-01'
    context.end_date = '2018-12-31'
    context.universe = ['000300.XSHG']
    context.base = '000300.XSHG'


    def handle(context, order):
        stock = context.universe[0]
        Ma_count = 60
        n = _EMV[0]
        m = _EMV[1]
        HS_da = get_price(security=stock, 
                      end_date= context.current_dt,
                      frequency= 'daily', 
                      fields= None, 
                      skip_paused= False, 
                      fq='pre',
                      count= Ma_count)

        EM_list = []
        for i in range(Ma_count):
            if i > (n-1):
                high_today = HS_da['high'][i]
                low_today = HS_da['low'][i]
                volume_today = HS_da['volume'][i]
                high_yesterday = HS_da['high'][i-1]
                low_yesterday = HS_da['low'][i-1]
                tmpVolume = talib.SMA(np.array(HS_da['volume'][(i-n 1):(i 1)]), n)[-1]/volume_today
                Mid = 100*(high_today   low_today - high_yesterday - low_yesterday)/(high_today   low_today)
                temp = Mid * tmpVolume * (high_today-low_today)
                high = talib.SMA(np.array(HS_da['high'][(i-n 1):(i 1)]), n)[-1]
                low = talib.SMA(np.array(HS_da['low'][(i-n 1):(i 1)]), n)[-1]
                EM = (temp/(high-low))     
            else:
                EM = 0
            EM_list.append(EM)

        HS_da['EM'] = EM_list
        HS_da['EMV'] = talib.SMA(np.array(HS_da['EM']), n)
        HS_da['MAEMV'] = talib.SMA(np.array(HS_da['EMV']), m)
        today_Emv = HS_da['EMV'][-1]
        yesterday_Emv = HS_da['EMV'][-2]
        today_MaEMV = HS_da['MAEMV'][-1]
        yesterday_Maemv = HS_da['MAEMV'][-2]

        if today_Emv > 0 and yesterday_Emv < 0 and today_MaEMV < today_Emv:
            if stock in context.position.keys():
                return
            order.buy(stock, HS_da['close'][-1], context.cash // HS_da['close'][-1])
        elif today_Emv < 0 and yesterday_Emv > 0:
            if stock not in context.position.keys():
                return
            order.sell(stock, HS_da['close'][-1], context.position[stock]['count'])
    trade.trade(handle, False)
    trade_list.append(trade)
# 展示
Trade.show_ratio_compare('EMV', EMV_list, trade_list, 3, 3)


首先解释一下该进场点,必须是EMV站于MAEMV,并且突破0轴。从口味来看,貌似还适中,但是仔细品尝,感觉还是有点杂味,现在的图例给我们最好的配比是(40,15),但是回撤已经超过了心理的承受能力了,就算你能吃,不过汤匙你拿得稳吗?这个问题很尖锐啊。数据中,也发现问题,n,m数值越大,标的信号几乎没有,因为数据几乎被磨平了,适当的比值被突显出关键。我们就全面对比数据试试看。

  • 回测标的:沪深300指数。
  • 回测时间:2010年01月01日到2018年12月31日。
  • 初始资金:100000元。
  • 不考虑对冲成本,尽量投入全部资金。
  • EMV参数:[5, 10, 15, 25, 30, 35, 40, 45, 50]
  • MAEMV参数:[4, 6, 8, 10, 12, 14, 16, 18, 20]



观察了这数据,[魏稀饭]觉得自己连碗可能也端得不是很稳了,因为回撤问题依旧。不过,我们依旧可以从中知道点知识,在EMV取值在40,MAEMV取值在15-20范围内,是最优的佐料比值。数据中有比较稳健的比值,比如(10,10)配比,回撤几乎是最小,碗拿得稳,汤匙也不抖了,但是收益率将近银行理财。

双EMV指标回测(EMV MAEMV)-交叉判断

  • 回测标的:沪深300指数。
  • 回测时间:2010年01月01日到2018年12月31日。
  • 初始资金:100000元。
  • 不考虑对冲成本,尽量投入全部资金。
  • EMV, MAEMV参数:[(5, 4),(10, 6),(15, 8), (25, 10), (30, 12), (35, 14), (40, 16), (45, 18), (50, 20)]
  • 代码展示:
trade_list = []
EMV_list = [(5, 4),(10, 6),(15, 8), (25, 10), (30, 12), (35, 14), (40, 16), (45, 18), (50, 20)]
for _EMV in EMV_list:
    # 策略结构
    context = Context()
    order = Order(context)
    trade = Trade(context, order)
    context.start_date = '2010-01-01'
    context.end_date = '2018-12-31'
    context.universe = ['000300.XSHG']
    context.base = '000300.XSHG'


    def handle(context, order):
        stock = context.universe[0]
        Ma_count = 60
        n = _EMV[0]
        m = _EMV[1]
        HS_da = get_price(security=stock, 
                      end_date= context.current_dt,
                      frequency= 'daily', 
                      fields= None, 
                      skip_paused= False, 
                      fq='pre',
                      count= Ma_count)

        EM_list = []
        for i in range(Ma_count):
            if i > (n-1):
                high_today = HS_da['high'][i]
                low_today = HS_da['low'][i]
                volume_today = HS_da['volume'][i]
                high_yesterday = HS_da['high'][i-1]
                low_yesterday = HS_da['low'][i-1]
                tmpVolume = talib.SMA(np.array(HS_da['volume'][(i-n 1):(i 1)]), n)[-1]/volume_today
                Mid = 100*(high_today   low_today - high_yesterday - low_yesterday)/(high_today   low_today)
                temp = Mid * tmpVolume * (high_today-low_today)
                high = talib.SMA(np.array(HS_da['high'][(i-n 1):(i 1)]), n)[-1]
                low = talib.SMA(np.array(HS_da['low'][(i-n 1):(i 1)]), n)[-1]
                EM = (temp/(high-low))     
            else:
                EM = 0
            EM_list.append(EM)

        HS_da['EM'] = EM_list
        HS_da['EMV'] = talib.SMA(np.array(HS_da['EM']), n)
        HS_da['MAEMV'] = talib.SMA(np.array(HS_da['EMV']), m)
        today_Emv = HS_da['EMV'][-1]
        yesterday_Emv = HS_da['EMV'][-2]
        today_MaEMV = HS_da['MAEMV'][-1]
        yesterday_Maemv = HS_da['MAEMV'][-2]

        if today_Emv > today_MaEMV and yesterday_Emv < yesterday_Maemv:
            if stock in context.position.keys():
                return
            order.buy(stock, HS_da['close'][-1], context.cash // HS_da['close'][-1])
        elif today_Emv < today_MaEMV and yesterday_Emv > yesterday_Maemv:
            if stock not in context.position.keys():
                return
            order.sell(stock, HS_da['close'][-1], context.position[stock]['count'])
    trade.trade(handle, False)
    trade_list.append(trade)
# 展示
Trade.show_ratio_compare('EMV', EMV_list, trade_list, 3, 3)


交叉判断顾名思义,加的佐料是金枪鱼,当EMV与MAEMV出现金叉,立马咬。数据的展示,依旧在给我们说:来啊,快活呀!回撤辣么大,想吃金枪鱼,就得陪鲨鱼。再仔细分析数据,最优参数是(40,16),有点老味道的气息,但是这次是有陷阱,收益仅有40.2%,悲伤更大了,可以摔碗筷了,舍不得碗筷就去喝摔碗酒吧。

  • 回测标的:沪深300指数。
  • 回测时间:2010年01月01日到2018年12月31日。
  • 初始资金:100000元。
  • 不考虑对冲成本,尽量投入全部资金。
  • EMV参数:[5, 10, 15, 25, 30, 35, 40, 45, 50]
  • MAEMV参数:[4, 6, 8, 10, 12, 14, 16, 18, 20]



以[魏稀饭]性格,分析这组数据,需要谨慎思考,因为数据的回撤与收益是反比的关系,单一EMV回测数据,本身在风控范围内是不够全面,但是双EMV却加重的情况。可以给我们一个警示,简单的菜的虽然好,但是部分的菜在不合时宜的时候吃,容易拉肚子。

双EMV指标回测(EMV MAEMV)-零轴 交叉

  • 回测标的:沪深300指数。
  • 回测时间:2010年01月01日到2018年12月31日。
  • 初始资金:100000元。
  • 不考虑对冲成本,尽量投入全部资金。
  • EMV, MAEMV参数:[(5, 4),(10, 6),(15, 8), (25, 10), (30, 12), (35, 14), (40, 16), (45, 18), (50, 20)]
  • 代码展示:
    ```python
    trade_list = []
    EMV_list = [(5, 4),(10, 6),(15, 8), (25, 10), (30, 12), (35, 14), (40, 16), (45, 18), (50, 20)]
    for _EMV in EMV_list:

    策略结构

    context = Context()
    order = Order(context)
    trade = Trade(context, order)
    context.start_date = '2010-01-01'
    context.end_date = '2018-12-31'
    context.universe = ['000300.XSHG']
    context.base = '000300.XSHG'
def handle(context, order):
    stock = context.universe[0]
    Ma_count = 60
    n = _EMV[0]
    m = _EMV[1]
    HS_da = get_price(security=stock, 
                  end_date= context.current_dt,
                  frequency= 'daily', 
                  fields= None, 
                  skip_paused= False, 
                  fq='pre',
                  count= Ma_count)

    EM_list = []
    for i in range(Ma_count):
        if i > (n-1):
            high_today = HS_da['high'][i]
            low_today = HS_da['low'][i]
            volume_today = HS_da['volume'][i]
            high_yesterday = HS_da['high'][i-1]
            low_yesterday = HS_da['low'][i-1]
            tmpVolume = talib.SMA(np.array(HS_da['volume'][(i-n 1):(i 1)]), n)[-1]/volume_today
            Mid = 100*(high_today   low_today - high_yesterday - low_yesterday)/(high_today   low_today)
            temp = Mid * tmpVolume * (high_today-low_today)
            high = talib.SMA(np.array(HS_da['high'][(i-n 1):(i 1)]), n)[-1]
            low = talib.SMA(np.array(HS_da['low'][(i-n 1):(i 1)]), n)[-1]
            EM = (temp/(high-low))     
        else:
            EM = 0
        EM_list.append(EM)

    HS_da['EM'] = EM_list
    HS_da['EMV'] = talib.SMA(np.array(HS_da['EM']), n)
    HS_da['MAEMV'] = talib.SMA(np.array(HS_da['EMV']), m)
    today_Emv = HS_da['EMV'][-1]
    yesterday_Emv = HS_da['EMV'][-2]
    today_MaEMV = HS_da['MAEMV'][-1]
    yesterday_Maemv = HS_da['MAEMV'][-2]

    if today_Emv > 0 and yesterday_Emv < 0 and today_MaEMV < today_Emv:
        if today_Emv > today_MaEMV and yesterday_Emv < yesterday_Maemv:
            if stock in context.position.keys():
                return
            order.buy(stock, HS_da['close'][-1], context.cash // HS_da['close'][-1])
    elif today_Emv < today_MaEMV and yesterday_Emv > yesterday_Maemv:
        if stock not in context.position.keys():
            return
        order.sell(stock, HS_da['close'][-1], context.position[stock]['count'])
trade.trade(handle, False)
trade_list.append(trade)

展示

Trade.show_ratio_compare('EMV', EMV_list, trade_list, 3, 3)
```

零轴 金叉等于是双拼,就好比吃稀饭,点油条又加馒头,可能会吃太饱。由上面数据,发现回撤开始缩小,这是好兆头,虽然大部分收益不乐观,还是有独角兽的,如参数(40,16),收益与回撤的改善是正比的。既然有好效果,我们就继续回测多数据看看。

  • 回测标的:沪深300指数。
  • 回测时间:2010年01月01日到2018年12月31日。
  • 初始资金:100000元。
  • 不考虑对冲成本,尽量投入全部资金。
  • EMV参数:[5, 10, 15, 25, 30, 35, 40, 45, 50]
  • MAEMV参数:[4, 6, 8, 10, 12, 14, 16, 18, 20]



数据列表,给了信心。EMV参数基本为40,MAEMV参数取值区间:12-20,从整个分析研究中,40基本比较突出,而且双拼配效果也优秀,因为回撤控制了,收益率也提升,更为突出的是盈亏比。看来着喂稀饭的方式还是算可以的,没白喂。

结论

EMV指标从回测数据观察可以得知,于短线操作是不利的,因为入场与出场资金的瞬间放大容易造成误差,参数设置过于敏感也同样受上述情况影响。EMV指标之所以加入MAEMV是为使得整个趋势更加明显,确定出一定的走势,过滤掉大涨大跌情况,贴合市场真正的情绪。数据的最优参数和最优方案都有给出:双拼 (40,[12-20]).但是实际的运用上可能存在误差,因为就算是厨师,也无法百分百确定盐的比例是最标准的。

如果有什么想法或是不对的地方,欢迎指出,一起交流,共同进步。

全部回复

0/140

量化课程

    移动端课程