分享

回测trix

 imelee 2017-03-13

前言

最近在看趋势择时,逛了下UQER,发现不少均线、MACD等指标的,但是木有看到关于TRIX的,就半参考着矿友accretion的策略Simple MACD做了个TRIX策略。

TRIX简介

TRIX(Triple Exponentially Smoothed Moving Average)中文名称:三重指数平滑移动平均,长线操作时采用本指标的讯号,可以过滤掉一些短期波动的干扰,避免交易次数过于频繁,造成部分无利润的买卖,及手续费的损失。
本指标是一项超长周期的指标,长时间按照本指标讯号交易,获利百分比大于损失百分比,利润相当可观。

计算公式

1.计算N日的指数移动平均线EMA
2.对上述EMA再进行两次N日指数移动平均后得到TR
3.TRIX = (TR - TR(昨日))/昨日TR * 100
4.MATRIX = TRIX的M日简单平均移动

TRIX的运用

1.当TRIX线一旦从下向上突破TRMA线,形成“金叉”时,预示着股价开始进入强势拉升阶段,投资者应及时买进股票。
2.当TRIX线向上突破TRMA线后,TRIX线和TRMA线同时向上运动时,预示着股价强势依旧,投资者应坚决持股待涨。
3.当TRIX线在高位有走平或掉头向下时,可能预示着股价强势特征即将结束,投资者应密切注意股价的走势,一旦K线图上的股价出现大跌迹象,投资者应及时卖出股票。
4.当TRIX线在高位向下突破TRMA线,形成“死叉”时,预示着股价强势上涨行情已经结束,投资者应坚决卖出余下股票,及时离场观望。
5.当TRIX线向下突破TRMA线后,TRIX线和TRMA线同时向下运动时,预示着股价弱势特征依旧,投资者应坚决持币观望。
6.当TRIX线在TRMA下方向下运动很长一段时间后,并且股价已经有较大的跌幅时,如果TRIX线在底部有走平或向上勾头迹象时,一旦股价在大的成交量的推动下向上攀升时,投资者可以及时少量地中线建仓。
7.当TRIX线再次向上突破TRMA线时,预示着股价将重拾升势,投资者可及时买入,持股待涨。

其中1、4为最重要的买入卖出信号,也是本策略的基石。
策略如下:

1
import pandas as pd
2
import numpy as np
3
4
start = '2011-08-01'                       # 回测起始时间
5
end = '2016-04-17'                         # 回测结束时间
6
benchmark = 'HS300'                        # 策略参考标准
7
universe = StockScreener(Factor.LCAP.nsmall(30))           # 因子选股,选取市值最小的30只股票作为备选
8
9
capital_base = 1000000                      # 起始资金
10
freq = 'd'                                 # 策略类型,'d'表示日间策略使用日线回测,'m'表示日内策略使用分钟线回测
11
refresh_rate = 5                           # 调仓频率,表示执行handle_data的时间间隔,若freq = 'd'时间间隔的单位为交易日,若freq = 'm'时间间隔为分钟
12
13
14
        
15
def initialize(account):                   # 初始化虚拟账户状态
16
    pass
17
18
def handle_data(account):                  # 每个交易日的买入卖出指令
19
   
20
    N = 15   # 计算TR时的N
21
    M = 45   # 计算MATRIX时的M
22
    length_of_data = 3*N+M +10  # 取closeprice的天数,为了足够计算MATRIX、TRIX
23
    
24
    all_close_prices = account.get_attribute_history('closePrice', length_of_data) # 获取历史closePrice数据
25
    
26
    buy_list = [] # 备选买入清单
27
    sell_list = [] # 卖出清单
28
    for stk in account.universe:
29
        prices = all_close_prices[stk]  
30
        if prices is None:
31
            continue
32
        try:
33
            TRIX = talib.TRIX(prices,timeperiod=N) # 计算TRIX
34
            MATRIX = talib.MA(TRIX,M,0)         # 机选MATRIX
35
        except:
36
            continue
37
            
38
        # 买入卖出判断
39
        if (TRIX[-1]-MATRIX[-1]) > 0 and (TRIX[-5]-MATRIX[-5]) < 0:    # 认为TRIX线向上突破TRMA线 金叉
40
            buy_list.append(stk)
41
        elif (TRIX[-1]-MATRIX[-1]) < 0 and (TRIX[-5]-MATRIX[-5]) > 0:  # 认为TRIX线在高位向下突破TRMA线 死叉
42
            sell_list.append(stk)
43
    
44
    hold = []
45
    buy = [] # 最终买入清单
46
    
47
    # 买入卖出
48
    for stk in account.valid_secpos:
49
        # sell_list卖出
50
        if stk in sell_list:
51
            order_to(stk, 0) 
52
        # 其余继续持股
53
        else:
54
            hold.append(stk)
55
            
56
    buy = hold
57
    for stk in buy_list:
58
        # 若buy_list中股票有未买入的,加入
59
        if stk not in hold:
60
            buy.append(stk)
61
            
62
    if len(buy) > 0:
63
        # 等仓位买入
64
        amout = account.referencePortfolioValue/len(buy) # 每只股票买入数量
65
        for stk in buy:
66
            num = int(amout/account.referencePrice[stk] / 100.0) * 100
67
            order_to(stk, num)   
68
             
69
    return
70
查看全部
  • 年化收益率82.8%
  • 基准年化收益率8.4%
  • 阿尔法76.2%
  • 贝塔0.63
  • 夏普比率2.88
  • 收益波动率27.5%
  • 信息比率2.21
  • 最大回撤34.3%
  • 换手率18.55
累计收益率策略基准2012-012012-072013-012013-072014-012014-072015-012015-072016-01-250.00%0.00%250.00%500.00%750.00%1000.00%1250.00%

关于N、M的选取

N、M的选取将较大的影响策略效果,我也不是很懂如何去选,在TRIX的百度百科,最后有实践采用的是(24,72)的组合。
依葫芦画瓢,我以M=3N 试了下(9,27)、(24,72)、(15,45)的组合,其中(15,45)表现最优
不妨再试试其他的?下面再看看(12,9)和(12,72)

1
import pandas as pd
2
import numpy as np
3
4
start = '2011-08-01'                       # 回测起始时间
5
end = '2016-04-17'                         # 回测结束时间
6
benchmark = 'HS300'                        # 策略参考标准
7
universe = StockScreener(Factor.LCAP.nsmall(30))           # 因子选股,选取市值最小的30只股票作为备选
8
9
capital_base = 1000000                      # 起始资金
10
freq = 'd'                                 # 策略类型,'d'表示日间策略使用日线回测,'m'表示日内策略使用分钟线回测
11
refresh_rate = 5                           # 调仓频率,表示执行handle_data的时间间隔,若freq = 'd'时间间隔的单位为交易日,若freq = 'm'时间间隔为分钟
12
13
14
        
15
def initialize(account):                   # 初始化虚拟账户状态
16
    pass
17
18
def handle_data(account):                  # 每个交易日的买入卖出指令
19
   
20
    N = 12   # 计算TR时的N
21
    M = 9   # 计算MATRIX时的M
22
    length_of_data = 3*N+M +10  # 取closeprice的天数,为了足够计算MATRIX、TRIX
23
    
24
    all_close_prices = account.get_attribute_history('closePrice', length_of_data) # 获取历史closePrice数据
25
    
26
    buy_list = [] # 备选买入清单
27
    sell_list = [] # 卖出清单
28
    for stk in account.universe:
29
        prices = all_close_prices[stk]  
30
        if prices is None:
31
            continue
32
        try:
33
            TRIX = talib.TRIX(prices,timeperiod=N) # 计算TRIX
34
            MATRIX = talib.MA(TRIX,M,0)         # 机选MATRIX
35
        except:
36
            continue
37
            
38
        # 买入卖出判断
39
        if (TRIX[-1]-MATRIX[-1]) > 0 and (TRIX[-5]-MATRIX[-5]) < 0:    # 认为TRIX线向上突破TRMA线 金叉
40
            buy_list.append(stk)
41
        elif (TRIX[-1]-MATRIX[-1]) < 0 and (TRIX[-5]-MATRIX[-5]) > 0:  # 认为TRIX线在高位向下突破TRMA线 死叉
42
            sell_list.append(stk)
43
    
44
    hold = []
45
    buy = [] # 最终买入清单
46
    
47
    # 买入卖出
48
    for stk in account.valid_secpos:
49
        # sell_list卖出
50
        if stk in sell_list:
51
            order_to(stk, 0) 
52
        # 其余继续持股
53
        else:
54
            hold.append(stk)
55
            
56
    buy = hold
57
    for stk in buy_list:
58
        # 若buy_list中股票有未买入的,加入
59
        if stk not in hold:
60
            buy.append(stk)
61
            
62
    if len(buy) > 0:
63
        # 等仓位买入
64
        amout = account.referencePortfolioValue/len(buy) # 每只股票买入数量
65
        for stk in buy:
66
            num = int(amout/account.referencePrice[stk] / 100.0) * 100
67
            order_to(stk, num)   
68
             
69
    return
查看全部
  • 年化收益率66.0%
  • 基准年化收益率5.6%
  • 阿尔法60.9%
  • 贝塔0.74
  • 夏普比率1.98
  • 收益波动率31.4%
  • 信息比率1.84
  • 最大回撤44.5%
  • 换手率39.06
累计收益率策略基准2012-012012-072013-012013-072014-012014-072015-012015-072016-010.00%1000.00%-250.00%250.00%500.00%750.00%
1
import pandas as pd
2
import numpy as np
3
4
start = '2011-08-01'                       # 回测起始时间
5
end = '2016-04-17'                         # 回测结束时间
6
benchmark = 'HS300'                        # 策略参考标准
7
universe = StockScreener(Factor.LCAP.nsmall(30))           # 因子选股,选取市值最小的30只股票作为备选
8
9
capital_base = 1000000                      # 起始资金
10
freq = 'd'                                 # 策略类型,'d'表示日间策略使用日线回测,'m'表示日内策略使用分钟线回测
11
refresh_rate = 5                           # 调仓频率,表示执行handle_data的时间间隔,若freq = 'd'时间间隔的单位为交易日,若freq = 'm'时间间隔为分钟
12
13
14
        
15
def initialize(account):                   # 初始化虚拟账户状态
16
    pass
17
18
def handle_data(account):                  # 每个交易日的买入卖出指令
19
   
20
    N = 12   # 计算TR时的N
21
    M = 72   # 计算MATRIX时的M
22
    length_of_data = 3*N+M +10  # 取closeprice的天数,为了足够计算MATRIX、TRIX
23
    
24
    all_close_prices = account.get_attribute_history('closePrice', length_of_data) # 获取历史closePrice数据
25
    
26
    buy_list = [] # 备选买入清单
27
    sell_list = [] # 卖出清单
28
    for stk in account.universe:
29
        prices = all_close_prices[stk]  
30
        if prices is None:
31
            continue
32
        try:
33
            TRIX = talib.TRIX(prices,timeperiod=N) # 计算TRIX
34
            MATRIX = talib.MA(TRIX,M,0)         # 机选MATRIX
35
        except:
36
            continue
37
            
38
        # 买入卖出判断
39
        if (TRIX[-1]-MATRIX[-1]) > 0 and (TRIX[-5]-MATRIX[-5]) < 0:    # 认为TRIX线向上突破TRMA线 金叉
40
            buy_list.append(stk)
41
        elif (TRIX[-1]-MATRIX[-1]) < 0 and (TRIX[-5]-MATRIX[-5]) > 0:  # 认为TRIX线在高位向下突破TRMA线 死叉
42
            sell_list.append(stk)
43
    
44
    hold = []
45
    buy = [] # 最终买入清单
46
    
47
    # 买入卖出
48
    for stk in account.valid_secpos:
49
        # sell_list卖出
50
        if stk in sell_list:
51
            order_to(stk, 0) 
52
        # 其余继续持股
53
        else:
54
            hold.append(stk)
55
            
56
    buy = hold
57
    for stk in buy_list:
58
        # 若buy_list中股票有未买入的,加入
59
        if stk not in hold:
60
            buy.append(stk)
61
            
62
    if len(buy) > 0:
63
        # 等仓位买入
64
        amout = account.referencePortfolioValue/len(buy) # 每只股票买入数量
65
        for stk in buy:
66
            num = int(amout/account.referencePrice[stk] / 100.0) * 100
67
            order_to(stk, num)   
68
             
69
    return
查看全部
  • 年化收益率75.4%
  • 基准年化收益率7.2%
  • 阿尔法69.6%
  • 贝塔0.63
  • 夏普比率2.60
  • 收益波动率27.7%
  • 信息比率2.06
  • 最大回撤37.6%
  • 换手率16.94
累计收益率策略基准2012-072013-012013-072014-012014-072015-012015-072016-010.00%1000.00%-250.00%250.00%500.00%750.00%

小结

对比来看,似乎M越大,初期的效果越好。但是总体表现并无太大提高。
我这里是抛砖引玉、关于N,M的选择、策略性能的提高,希望社区牛人们能指点指点。

13 条评论
添加评论
  • ValueError Traceback (most recent call last)

    in ()
    98 freq=freq, security_base=security_base, security_cost=security_cost,
    99 max_history_window=max_history_window, preload_data=_QUARTZ_PRELOAD_DATA,
    --> 100 display=True, return_quartz_data=True)
    101 _QUARTZ_CACHE['start'] = start
    102 _QUARTZ_CACHE['end'] = end

    /home/ipython/anaconda/lib/python2.7/site-packages/mercuryq-quartz.egg/quartz/backtest.pyc in backtest(start, end, benchmark, universe, capital_base, initialize, handle_data, commission, slippage, refresh_rate, freq, security_base, security_cost, max_history_window, args, *kwargs)

    /home/ipython/anaconda/lib/python2.7/site-packages/mercuryq-quartz.egg/quartz/backtest.pyc in backtest_daily(account, data, backtest_calendar, sim_params, **kwargs)

    /home/ipython/anaconda/lib/python2.7/site-packages/mercuryq-quartz.egg/quartz/simulation/account.pyc in handle_data(self)

    in handle_data(account)
    22 length_of_data = 3*N+M +10 # 取closeprice的天数,为了足够计算MATRIX、TRIX
    23
    ---> 24 all_close_prices = account.get_attribute_history('closePrice', length_of_data) # 获取历史closePrice数据
    25
    26 buy_list = [] # 备选买入清单

    /home/ipython/anaconda/lib/python2.7/site-packages/mercuryq-quartz.egg/quartz/simulation/account.pyc in get_attribute_history(self, attribute, time_range)

    /home/ipython/anaconda/lib/python2.7/site-packages/mercuryq-quartz.egg/quartz/simulation/account.pyc in _get_daily_attribute_history(self, attribute, time_range)

    ValueError: History overflow. Your current max daily history window is 30. Please use a shorter parameter, or change max_history_window according to document!

  • 求知者 15天前

    @fyiqi 错误

    ValueError Traceback (most recent call last)

    in ()
    98 freq=freq, security_base=security_base, security_cost=security_cost,
    99 max_history_window=max_history_window, preload_data=_QUARTZ_PRELOAD_DATA,
    --> 100 display=True, return_quartz_data=True)
    101 _QUARTZ_CACHE['start'] = start
    102 _QUARTZ_CACHE['end'] = end

    /home/ipython/anaconda/lib/python2.7/site-packages/mercuryq-quartz.egg/quartz/backtest.pyc in backtest(start, end, benchmark, universe, capital_base, initialize, handle_data, commission, slippage, refresh_rate, freq, security_base, security_cost, max_history_window, args, *kwargs)

    /home/ipython/anaconda/lib/python2.7/site-packages/mercuryq-quartz.egg/quartz/backtest.pyc in backtest_daily(account, data, backtest_calendar, sim_params, **kwargs)

    /home/ipython/anaconda/lib/python2.7/site-packages/mercuryq-quartz.egg/quartz/simulation/account.pyc in handle_data(self)

    in handle_data(account)
    22 length_of_data = 3*N+M +10 # 取closeprice的天数,为了足够计算MATRIX、TRIX
    23
    ---> 24 all_close_prices = account.get_attribute_history('closePrice', length_of_data) # 获取历史closePrice数据
    25
    26 buy_list = [] # 备选买入清单

    /home/ipython/anaconda/lib/python2.7/site-packages/mercuryq-quartz.egg/quartz/simulation/account.pyc in get_attribute_history(self, attribute, time_range)

    /home/ipython/anaconda/lib/python2.7/site-packages/mercuryq-quartz.egg/quartz/simulation/account.pyc in _get_daily_attribute_history(self, attribute, time_range)

    ValueError: History overflow. Your current max daily history window is 30. Please use a shorter parameter, or change max_history_window according to document!

  • 124127 2个月前

    对应单只股票,实测效果并不好,参数选择上,有过度拟合嫌疑。对应不同的股票,通过调整N、M的参数的确可以产生好的效果,但是没有一个通用的参数可满足多个股票。
    从效果上来说,和MACD应该差不多,都是基于ema来做的,同样有很强的延时效应。
    实际使用上,感觉还不如MACD。

  • fyiqi 10个月前

    @xiaobingge 个人觉得1天的趋势可能不明显,而且本来换手率这边就确定的5,我就直接和5天前比了,-3,-4什么的都可以试试,你可以都跑一下看看效果。

  • xiaobingge 10个月前

    if (TRIX[-1]-MATRIX[-1]) > 0 and (TRIX[-5]-MATRIX[-5]) < 0

    请教一下,为什么不是取 -1 和 -2 相比,而是取 -1 和 -5 相比,这里有什么考虑吗?

  • @fyiqi 非常感谢!

  • fyiqi 1年前

    @shinefuture 不好意思之前没看到回复。。。代码如下

    # 去除ST股
    STlist = DataAPI.SecSTGet(secID=account.universe, beginDate='20160422',endDate='20160422',field['secID']).tolist()
    account.universe = [s for s in account.universe if s not in STlist]
    
    # HS300中选市值最小30只的话,我不知道有啥函数可以直接获取,我的方法是:获取当天HS300所有LCAP因子数据,然后对得到的dataframe对LCAP排序,取最小的30只就行。代码如下:
    universe = set_universe('HS300')
    data = DataAPI.MktStockFactorsOneDayGet(tradeDate='20160422',secID=universe,field['SecID','LCAP'],pandas="1")
    data = data.sort(columns='LCAP')
    SecID_of_30_smallest =data['secID'][:30].tolist()
    
  • 你好,看回测结果中有ST股,请问该如何去掉?请教下如果要从HS300中选市值最小的30只该怎么选,谢谢!

  • @陈亚龙 可以了,謝謝您

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

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多