掘金社区

对于技术指标的选股和择时的探讨Pinned highlighted

掘金小Q 【 论坛管理员 】 发表在策略分享 2021-10-15 14:19:17

策略分享
1473
6
0

本文旨在提供一个使用技术指标进行择时,通过行业轮动选股的综合性的量化投资策略

通过它,我们可以推导其它择时指标及选股指标,将它们进一步排列组合,筛选出一个适合自己的量化投资策略。

全篇共有三个策略,基于CCI指标进行策略研究、开发。

第一个策略是使用CCI指标进行择时买卖。但发现其收益率惨淡。于是,选择在此基础上加入行业轮动选股策略,产生第二个策略。

第二个策略的收益率虽然高了,但夏普比率低。所以在第三个策略中对第二个策略进行参数寻优

以下为笔者做出的尝试,如对这一块有见解的宽友,欢迎留言探讨。

实战交易策略

CCI指标应用于沪深300指数

1.说明

顺势指标CCI由唐纳德拉姆伯特所创,是通过测量股价的波动是否已超出其正常范围,来预测股价变化趋势的技术分析指标。

2.计算

以日 CCI 计算为例,一般采用的计算方法如下:

已知到今天为止连续n天的每天的最高价Hi*、最低价Li、收盘价Ci*,

其中i=1,2,3...n ,今天为第n天

0_1634277322502_0297fb69-c613-4661-90ed-c24355827043-image.png

代码如下:

##CCIdef cci(data,alpha=0.015,n=20):    
tp_i=(data.close+data.low+data.high)/3    
matp_i=tp_i.rolling(n).mean()    
meanDev=tp_i.rolling(n).apply(lambda x:abs(x-x.mean()).mean(),raw=False) cci=(tp_i-matp_i)/(alpha*meanDev)    
return cci

3.回测设置

  • 标的:沪深 300 指数
  • 择时规则:当 CCI 上穿 100,买入 ;当 CCI 下穿-100,卖出
  • 回测时间:2020.7.1---2021.10.1

4.策略代码

# coding=utf-8
from __future__ import print_function, absolute_import
from gm.api import *
import pandas as pd 

##定义CCI计算
def cci(data,alpha=0.015,n=20):
    tp_i=(data.close+data.low+data.high)/3
    matp_i=tp_i.rolling(n).mean()
    meanDev=tp_i.rolling(n).apply(lambda x:abs(x-x.mean()).mean(),raw=False)
    cci=(tp_i-matp_i)/(alpha*meanDev)
    return cci.iloc[-1]

# 策略中必须有init方法
def init(context):
    #设置标的
    context.symbol='SHSE.000300'
    #订阅标的的分钟数据
    subscribe(symbols='SHSE.000300', frequency='1d', count=21, wait_group=True, wait_group_timeout='6s', unsubscribe_previous=True)
def on_bar(context,bars):
    #接取数据
    data = context.data(symbol='SHSE.000300', frequency='1d', count=21)
    #计算cci的值
    CCI = cci(data,alpha=0.015,n=20)
  
    # 当 CCI 上穿 100,买入,信号为 1 
    if CCI > 100 :
        # 获取当前的价格
        current_data = current(symbols='SHSE.000300')[0]['price']
        #获取当前的全部持仓
        Account_positions = context.account().positions()
        #如果没有持仓的话就买入
        if not Account_positions:
            #调仓到目标持仓,也可以使用按指定量委托
            order = order_target_percent(symbol='SHSE.000300', percent=0.8, position_side=PositionSide_Long, order_type=OrderType_Limit, price=current_data)
            print(context.now,'此时的CCI的值为',CCI)
            print('以价格为',order[0]['price'],'买入标的',order[0]['symbol'],'数量',order[0]['volume'])
    
    # 当 CCI 下穿-100,卖空,信号为-1 
    elif CCI < -100 :
        #获取当前的全部持仓
        Account_positions = context.account().positions()
        # 获取当前的价格
        current_data = current(symbols='SHSE.000300')[0]['price']
        #如果有持仓的话就卖出
        if Account_positions:
            #调仓到目标持仓,也可以使用按指定量委托
            order = order_target_percent(symbol='SHSE.000300', percent=0, position_side=PositionSide_Long, order_type=OrderType_Limit, price=current_data)
            print(context.now,'此时的CCI的值为',CCI)
            print('以价格为',order[0]['price'],'卖出标的',order[0]['symbol'],'数量',order[0]['volume'])

if __name__ == '__main__':
    run(strategy_id='463d22be-27fa-11ec-80a7-7085c223669d',
        filename='main.py',
        mode=MODE_BACKTEST,
        token='xxxxxxxxxxxxx',
        backtest_start_time='2020-07-01 08:00:00',
        backtest_end_time='2021-10-01 16:00:00',
        backtest_adjust=ADJUST_PREV,
        backtest_initial_cash=10000000,
        backtest_commission_ratio=0.0001,
        backtest_slippage_ratio=0.0001)

5.回测结果:

该策略累计收益率仅仅为1.69%,并不理想。但这是因为该策略中仅抓住一个标的进行交易,而且交易较频繁,所以产生的手续费特别高。基于此,即使该策略的收益率特别低,也并不能说明这个技术指标已经失效。下面我们加入选股策略,解救这一惨淡的收益率。

0_1634277886965_080c1aea-1a8a-456a-b2b3-f7600e12aadb-image.png

CCI实战交易策略二

加入行业轮动选股策略

1.说明

在交易策略一中采用了CCI指标进行择时买卖,但效果并不好,而且现实中只交易一个标的并不现实,所以基于上面的策略,考虑加入行业轮动选股策略,行业轮动策略大家可以参考掘金官网给出的实例。

2.加入行业轮动策略回测设置

  • 标的:300工业.300材料.300可选.300消费.300医药.300金融 这六个行业指数的成分股

  • 轮动选股频率:每月第一个交易日

  • 选股规则:计算上述六个行业指数过去20个交易日的收益率并选取了收益率最高的指数的成分股

  • 择时买入:在每天的09:40:00,使用CCI指标择时

  • 调仓规则:如果CCI值大于100且该成分股不在持仓中,则按照比率调仓买入,如果CCI值小于-100且该成分股在持仓中,则平仓

  • 回测时间:2020.7.1---2021.10.1

3.策略代码

# coding=utf-8
from __future__ import print_function, absolute_import, unicode_literals
from gm.enum import OrderType_Limit
import numpy as np
import pandas as pd 
from gm.api import *

'''
本策略每隔1个月定时触发计算SHSE.000910.SHSE.000909.SHSE.000911.SHSE.000912.SHSE.000913.SHSE.000914
(300工业.300材料.300可选.300消费.300医药.300金融)这几个行业指数过去20个交易日的收益率并选取了收益率最高的指数
每一天都去计算选出来的指数成分股的CCI值,随后进行调仓,
如果CCI值大于100且该成分股不在持仓中,则按照比率调仓买入,如果CCI值小于-100且该成分股在持仓中,则平仓
回测数据为:SHSE.000910.SHSE.000909.SHSE.000911.SHSE.000912.SHSE.000913.SHSE.000914和他们的成份股
回测时间为:2020-07-01 08:00:00到2021-10-01 16:00:00
'''

def init(context):
    # 每月第一个交易日的09:40 定时执行algo任务(仿真和实盘时不支持该频率)
    schedule(schedule_func=algo, date_rule='1m', time_rule='09:40:00')
    # 每日的09:40 定时执行algo_day任务
    schedule(schedule_func=algo_day, date_rule='1d', time_rule='09:40:00')
    # 用于筛选的行业指数
    context.index = ['SHSE.000910', 'SHSE.000909', 'SHSE.000911', 'SHSE.000912', 'SHSE.000913', 'SHSE.000914']
    # 用于统计数据的天数
    context.date = 20
    # 最大下单资金比例
    context.ratio = 0.8
    #获取到的最好的行业指数、
    context.sector = None


##定义CCI计算
def cci(data,alpha=0.015,n=20):
    tp_i=(data.close+data.low+data.high)/3
    matp_i=tp_i.rolling(n).mean()
    meanDev=tp_i.rolling(n).apply(lambda x:abs(x-x.mean()).mean(),raw=False)
    cci=(tp_i-matp_i)/(alpha*meanDev)
    return cci.iloc[-1]


def algo(context):
    #每月初先平仓
    order_close_all()
    print('平当前所有可平持仓')
    # 获取当天的日期
    today = context.now
    # 获取上一个交易日
    last_day = get_previous_trading_date(exchange='SHSE', date=today)
    return_index = []

    # 获取并计算行业指数收益率
    for i in context.index:
        return_index_his = history_n(symbol=i, frequency='1d', count=context.date, fields='close,bob',
                                     fill_missing='Last', adjust=ADJUST_PREV, end_time=last_day, df=True)
        return_index_his = return_index_his['close'].values
        return_index.append(return_index_his[-1] / return_index_his[0] - 1)
    # 获取指定数内收益率表现最好的行业
    context.sector = context.index[np.argmax(return_index)]
    print('最佳行业指数是: ', context.sector)

def algo_day(context):
     # 获取当天的日期
    today = context.now
    # 获取上一个交易日
    last_day = get_previous_trading_date(exchange='SHSE', date=today)
    # 获取最佳行业指数成份股
    symbols = get_history_constituents(index=context.sector, start_date=last_day, end_date=last_day)[0]['constituents'].keys()

    # 获取当天有交易的股票
    not_suspended_info = get_history_instruments(symbols=symbols, start_date=today, end_date=today)
    not_suspended_symbols = [item['symbol'] for item in not_suspended_info if not item['is_suspended']]

    #用于记录需要买入调仓的标的代码以及其cci值
    buy=pd.DataFrame()
    buy_cci=[]
    buy_symbol=[]
    #用于记录需要卖出调仓的标的代码以及其cci值
    sell=pd.DataFrame()
    sell_cci=[]
    sell_symbol=[]
    # 获取最佳行业指数成份股的最近20个交易日的收盘价并计算CCI值,选取CCI值大于100的股票进行买入  小于-100的股票进行卖出
    for item in not_suspended_symbols:
        history_n_data = history_n(symbol=item, frequency='1d', count=20, end_time=today, fields='symbol, close,low,high,open', adjust=ADJUST_PREV, df=True)
        if cci(history_n_data) > 100 :
            buy_cci.append(cci(history_n_data))
            buy_symbol.append(item)
        elif cci(history_n_data) < -100 :
            sell_cci.append(cci(history_n_data))
            sell_symbol.append(item)
    buy['buy_cci']=buy_cci
    buy.index = buy_symbol
    print('需要进行买入的股票以及其cci值',buy)

    sell['sell_cci']=sell_cci
    sell.index = sell_symbol
    print('需要进行卖出的股票以及其cci值',sell)

    # 获取当前所有仓位
    positions = context.account().positions()
    # 平掉持仓中CCI值小于-100的股票
    for position in positions:
        symbol = position['symbol']
        if symbol in sell.index:
            #查询当前该股票的价格
            price= current(symbols=symbol)[0]['price']
            order_target_percent(symbol=symbol, percent=0, order_type=OrderType_Limit,
                                 position_side=PositionSide_Long,price=price)
            print('以当前价格平不在标的池的', symbol)

    # 判断需要买入调仓的标的数量大于0
    if len(buy.index)>0:
        # 计算权重
        percent = 1.0 / len(buy.index) * context.ratio
        # 对标的池进行操作,如标的股票不在持仓中则买入 
        for symbol in buy.index:
            # 查询指定持仓
            pos = context.account().position(symbol=symbol,side = PositionSide_Long)
            #如果不在持仓中则买入
            if not pos:
                price= current(symbols=symbol)[0]['price']
                order_target_percent(symbol=symbol, percent=percent, order_type=OrderType_Limit,
                                    position_side=PositionSide_Long,price=price)
                print(symbol, '以当前价格下单调整至仓位', percent)

if __name__ == '__main__':
    run(strategy_id='68f3523b-28d7-11ec-b978-7085c223669d',
        filename='main.py',
        mode=MODE_BACKTEST,
        token='xxxxxxxxxxxxx',
        backtest_start_time='2020-07-01 08:00:00',
        backtest_end_time='2021-10-01 16:00:00',
        backtest_adjust=ADJUST_PREV,
        backtest_initial_cash=10000000,
        backtest_commission_ratio=0.0001,
        backtest_slippage_ratio=0.0001)

4.回测结果

在加入行业轮动选股之后,策略的累计收益率达到102.08%,要较高的优于沪深300的基准收益率,但伴随着高收益率的同时,其最大回撤高达12.34%,夏普比率为2,胜率为54.62%,结果还是不够理想,下面将考虑对该策略进一步参数寻优,提高回测的夏普比率。

0_1634277712909_f89bdd00-54d2-4b2b-9e8e-a6b6823bb034-image.png

CCI实战交易策略三

对上述策略进行参数寻优


1.CCI指标参数寻优设置:

alpha = 0.010,0.015,0.020

n = 10,15,20

2.策略代码:

# coding=utf-8
# 行业轮动选股+CCI指标择时+参数寻优
from __future__ import print_function, absolute_import, unicode_literals
from gm.enum import OrderType_Limit
import numpy as np
import pandas as pd 
from gm.api import *
import multiprocessing
'''
本策略每隔1个月定时触发计算SHSE.000910.SHSE.000909.SHSE.000911.SHSE.000912.SHSE.000913.SHSE.000914
(300工业.300材料.300可选.300消费.300医药.300金融)这几个行业指数过去20个交易日的收益率并选取了收益率最高的指数
每一天都去计算选出来的指数成分股的CCI值,随后进行调仓,
如果CCI值大于100且该成分股不在持仓中,则按照比率调仓买入,如果CCI值小于-100且该成分股在持仓中,则平仓
回测数据为:SHSE.000910.SHSE.000909.SHSE.000911.SHSE.000912.SHSE.000913.SHSE.000914和他们的成份股
回测时间为:2020-07-01 08:00:00到2021-10-01 16:00:00
'''
def init(context):
    # 每月第一个交易日的09:40 定时执行algo任务(仿真和实盘时不支持该频率)
    schedule(schedule_func=algo, date_rule='1m', time_rule='09:40:00')
    # 每日的09:40 定时执行algo_day任务
    schedule(schedule_func=algo_day, date_rule='1d', time_rule='09:40:00')
    # 用于筛选的行业指数
    context.index = ['SHSE.000910', 'SHSE.000909', 'SHSE.000911', 'SHSE.000912', 'SHSE.000913', 'SHSE.000914']
    # 用于统计数据的天数
    context.date = 20
    # 最大下单资金比例
    context.ratio = 0.8
    #获取到的最好的行业指数、
    context.sector = None

##定义CCI计算
def cci(data,alpha=0.015,n=20):
    tp_i=(data.close+data.low+data.high)/3
    matp_i=tp_i.rolling(n).mean()
    meanDev=tp_i.rolling(n).apply(lambda x:abs(x-x.mean()).mean(),raw=False)
    cci=(tp_i-matp_i)/(alpha*meanDev)
    return cci.iloc[-1]

def algo(context):
    #每月初先平仓
    order_close_all()
    print('平当前所有可平持仓')
    # 获取当天的日期
    today = context.now
    # 获取上一个交易日
    last_day = get_previous_trading_date(exchange='SHSE', date=today)
    return_index = []

    # 获取并计算行业指数收益率
    for i in context.index:
        return_index_his = history_n(symbol=i, frequency='1d', count=context.date, fields='close,bob',
                                     fill_missing='Last', adjust=ADJUST_PREV, end_time=last_day, df=True)
        return_index_his = return_index_his['close'].values
        return_index.append(return_index_his[-1] / return_index_his[0] - 1)
    # 获取指定数内收益率表现最好的行业
    context.sector = context.index[np.argmax(return_index)]
    print('最佳行业指数是: ', context.sector)

def algo_day(context):
     # 获取当天的日期
    today = context.now
    # 获取上一个交易日
    last_day = get_previous_trading_date(exchange='SHSE', date=today)
    # 获取最佳行业指数成份股
    symbols = get_history_constituents(index=context.sector, start_date=last_day, end_date=last_day)[0]['constituents'].keys()

    # 获取当天有交易的股票
    not_suspended_info = get_history_instruments(symbols=symbols, start_date=today, end_date=today)
    not_suspended_symbols = [item['symbol'] for item in not_suspended_info if not item['is_suspended']]

    #用于记录需要买入调仓的标的代码以及其cci值
    buy=pd.DataFrame()
    buy_cci=[]
    buy_symbol=[]
    #用于记录需要卖出调仓的标的代码以及其cci值
    sell=pd.DataFrame()
    sell_cci=[]
    sell_symbol=[]
    # 获取最佳行业指数成份股的最近20个交易日的收盘价并计算CCI值,选取CCI值大于100的股票进行买入  小于-100的股票进行卖出
    for symbol in not_suspended_symbols:
        history_n_data = history_n(symbol=symbol, frequency='1d', count=20, end_time=today, fields='symbol, close,low,high,open', adjust=ADJUST_PREV, df=True)
        cci_data=cci(history_n_data,alpha=context.alpha,n=context.n) 
        if cci_data> 100 :
            buy_cci.append(cci_data)
            buy_symbol.append(symbol)
        elif cci_data < -100 :
            sell_cci.append(cci_data)
            sell_symbol.append(symbol)
    buy['buy_cci']=buy_cci
    buy.index = buy_symbol
    print('需要进行买入的股票以及其cci值',buy)

    sell['sell_cci']=sell_cci
    sell.index = sell_symbol
    print('需要进行卖出的股票以及其cci值',sell)


    # 获取当前所有仓位
    positions = context.account().positions()
    # 平掉持仓中CCI值小于-100的股票
    for position in positions:
        symbol = position['symbol']
        if symbol in sell.index:
            #查询当前该股票的价格
            price= current(symbols=symbol)[0]['price']
            order_target_percent(symbol=symbol, percent=0, order_type=OrderType_Limit,
                                 position_side=PositionSide_Long,price=price)
            print('以当前价格平不在标的池的', symbol)


    # 判断需要买入调仓的标的数量大于0
    if len(buy.index)>0:
        # 计算权重
        percent = 1.0 / len(buy.index) * context.ratio
        # 对标的池进行操作,如标的股票不在持仓中则买入 
        for symbol in buy.index:
            # 查询指定持仓
            pos = context.account().position(symbol=symbol,side = PositionSide_Long)
            #如果不在持仓中则买入
            if not pos:
                price= current(symbols=symbol)[0]['price']
                order_target_percent(symbol=symbol, percent=percent, order_type=OrderType_Limit,
                                    position_side=PositionSide_Long,price=price)
                print(symbol, '以当前价格下单调整至仓位', percent)

# 获取每次回测的报告数据
def on_backtest_finished(context, indicator):
    data = [indicator['pnl_ratio'], indicator['pnl_ratio_annual'], indicator['sharp_ratio'], indicator['max_drawdown'],
            context.alpha, context.n]
    # 将超参加入context.result
    context.result.append(data)

def run_strategy(alpha, n):
    from gm.model.storage import context
    # 用context传入参数
    context.alpha = alpha
    context.n = n
    # context.result用以存储超参
    context.result = []
    
    run(strategy_id='68f3523b-28d7-11ec-b978-7085c223669d',
        filename='main.py',
        mode=MODE_BACKTEST,
        token='xxxxxxxxxxx',
        backtest_start_time='2020-09-01 08:00:00',
        backtest_end_time='2021-10-01 16:00:00',
        backtest_adjust=ADJUST_PREV,
        backtest_initial_cash=10000000,
        backtest_commission_ratio=0.0001,
        backtest_slippage_ratio=0.0001)
    return context.result

if __name__ == '__main__':
    paras_list = []
    # 循环输入参数数值回测
    for alpha in [0.010,0.015,0.020]:
        for n in range(10, 25, 5):
            paras_list.append([alpha, n])

    a_list = []
    pool = multiprocessing.Pool(processes=12, maxtasksperchild=1)  # create 12 processes
    for i in range(len(paras_list)):
        a_list.append(pool.apply_async(func=run_strategy, args=(paras_list[i][0], paras_list[i][1])))
    pool.close()
    pool.join()
    info = []
    for pro in a_list:
        print('pro', pro.get()[0])
        info.append(pro.get()[0])
    print(info)
    info = pd.DataFrame(np.array(info), columns=['pnl_ratio', 'pnl_ratio_annual', 'sharp_ratio', 'max_drawdown', 'alpha', 'n'])
    info.to_csv('info.csv', index=False)

3.参数寻优结果:

下图中,回测结果最好的收益率为252.45%,最大回撤为11.01%,夏普比率为2.68。
0_1634278686801_7f6a4482-ba1f-4713-88b3-586ba50c9161-image.png

通过查询可知最优的参数为alpha=0.01, n=10。回测结果如下:

0_1634278655078_daca1930-8460-4520-99d5-31f1e37f6f29-image.png

附上常见技术指标的计算以及代码

**
**

1.技术指标

指标分类参考了研报:

https://robo.datayes.com/v2/details/report/4592676

技术指标大类 指标 指标构建方法 默认参数 使用原则
动量类 ROC ROC = (今收盘 - 前N周期收盘) / 前N周期收盘 * 100 N=10 当指标高于参考线时,发出1信号;指标低于参考线时,发出-1信号;其他时间维持原仓位。其中MTM指标选定均线为参考线,ROC指标则选择0为参考线。
MTM MTM = 当期收盘价 - N期前收盘价MAMTM = MTM的M周期移动平均 N=10,M=20
均线类 SMA 价格的N1日平均值 N1=10 以0作为参考线,SMA大于0,买入信号;SMA小于0,卖出信号
DMA DMA = N1平均值(SMA)–N2平均值(LMA)AMA = DMA的N1平均值 N1=10, N2=50 DMA&AMA均大于0且增加时,买入信号;DMA&AMA均小于0且减少时,卖出信号
MACD DIF = 短期EMA - 长期EMADEA = DIF的移动平均值MACD = 2 * (DIF - DEA) short=12,long=26, 当指标高于0时,发出1信号;指标低于0时,发出-1信号;其他时间维持原仓位。
TRIX TR=收盘价的N日指数移动平均TRIX=(TR-昨日TR)/昨日TR*100MATRIX=TRIX的M日简单移动平均 N=12,M=9, 以均线作为参考线,TRIX大于均线,买入信号;TRIX小于均线,卖出信号
BBI BBI = (N1周期平均价+N2周期平均价+N3周期平均价+N4周期平均价)/4 N1=3, N2=6, N3=12, N4=24, 以BBI作为参考线,收盘价大于BBI,买入信号;收盘价小于BBI,卖出信号
BOLL 中线MB = N周期移动平均收盘价偏移值 = P * N周期收盘价的标准差上轨UP(阻力线)=中线+偏移值下轨DOWN(支撑线)=中线-偏移值其中,默认参数N取20,即过去20日;P取2,表示波带半径取标准差的2倍 N=20,P=2, 对于布林带,突破上轨时发出1信号,跌破下轨时发出-1信号;其他时间维持原仓位。
BIAS BIAS(N) =(收盘价 - N周期移动平均价)/ N周期移动平均价 * 100其中,默认参数N常取5,10,20。 N=10 以20和-20为参考线;BIAS跌破-20,卖出信号,BIAS突破20,买入信号
CCI TP = (最高价 + 最低价 +收盘价) / 3MA = 最近N周期TP的累计和 / NMD = (最近N周期(MA - TP)累计和) / NCCI(N) = (TP - MA) / 0.015MD N=14 和100/-100对比,向上突破-100/100,买入信号;向下跌破-100/100,卖出信号
相对强弱类 KDJ RSV = (收盘价-最近N日最低价) / (最近N日最高价 - 最近N日最低价) × 100K线:K值 = (M1-1)/M1 * 前一日K值 + 1/M1 * 当日RSVD线:D值 = (M2-1)/M2 * 前一日D值 + 1/M2 * 当日K值J线:3 × 当日K值 - 2 × 当日D值 N=9;M1=3,M2=3。 线K向上突破线D,发出1信号;线K向下跌破线D,发出-1信号;其他时间维持原仓位。
RSI RSI = N日内收盘价上涨幅度累计 / N日内收盘价上涨及下跌幅度累计 * 100 N=6 以20和80为参考线,跌破20表明明显弱势,发出-1信号;突破80表明明显强势,发出1信号;其他时间维持原仓位。

2.代码

#动量类
##1、ROC
def roc(data,n=10):
    '''
    计算roc指标,n默认为10,并返回信号
    '''
    roc=100*(data.close.iloc[-1]-data.close.iloc[-n])/data.close.iloc[-n]
    #选择0为参考线,当指标高于参考线时,发出1信号;指标低于参考线时,发出-1信号;其他时间维持原仓位。
    if roc>0:
        return 1
    elif roc<0:
        return -1
    else: 
        return 0
##2、MTM
def htm(data,n=10,m=20):
    #当期收盘价 - N期前收盘价 的序列
    htm=data.close-data.close.shift(n)
    #计算MTM的M周期移动平均
    mahtm= htm.rolling(m).mean()
    #选择均线为参考线,当指标高于参考线时,发出1信号;指标低于参考线时,发出-1信号;其他时间维持原仓位。
    if htm.iloc[-1]>mahtm.iloc[-1]:
        return 1
    elif htm.iloc[-1] <mahtm.iloc[-1] :
        return -1
    else:
        return 0    
        
#均线类
##1、SMA
def sma(data,n1=10):
    sma=data.close.rolling(n1).mean()
    if sma.iloc[-1] > 0:return 1
    elif sma.iloc[-1] < 0: return -1
    else : return 0
##2、DMA
def dma(data,n1=10,n2=50):
    dma=data.close.rolling(n1).mean()-data.close.rolling(n2).mean()
    ama=dma.rolling(n1).mean()
    #DMA&AMA均大于0且增加时,买入信号;
    if dma.iloc[-1]>0 and ama.iloc[-1]>0 and dma.iloc[-1]>dma.iloc[-2] and ama.iloc[-1]>ama.iloc[-2]:
        return 1
    elif dma.iloc[-1]<0 and ama.iloc[-1]<0 and dma.iloc[-1]<dma.iloc[-2] and ama.iloc[-1]<ama.iloc[-2]:
        return -1
    else:return 0
##3、MACD
def macd(data,short=12,long=26,M=9):
    ema_short=data.close.ewm(span=short).mean()
    ema_long=data.close.ewm(span=long).mean()
    diff=ema_short-ema_long
    dea=diff.ewm(span=M).mean()
    macd=2*(diff-dea)
    return macd
    #当指标高于0时,发出1信号;指标低于0时,发出-1信号;其他时间维持原仓位。 
    if macd.iloc[-1]>0:return 1
    elif macd.iloc[-1]<0 : return -1
    else :return 0
##4、trix
def trix(data,n=12,m=9):
    tr=data.close.ewm(span=n).mean()
    trix=100*(tr-tr.shift(1))/tr.shift(1)
    matrix=trix.rolling(m).mean()
    if trix.iloc[-1]>matrix.iloc[-1]:return 1
    elif trix.iloc[-1]<matrix.iloc[-1]:return -1
    else :return 0    
##5、BBI
def bbi(data,n1=3,n2=6,n3=12,n4=24):
    bbi=(data.close.rolling(n1).mean()+data.close.rolling(n2).mean()+data.close.rolling(n3).mean()+data.close.rolling(n4).mean())/4
    if data.close.iloc[-1]>bbi.iloc[-1]:return 1
    elif data.close.iloc[-1]<bbi.iloc[-1]:return -1
    else: return 0
        
## 6、BOLL
def boll(data,N=20,P=2):
    boll = data.close.rolling(N).mean()
    UB = boll + P * data.close.rolling(N).std()
    LB = boll - P * data.close.rolling(N).std()
    if data.close.iloc[-1]>UB.iloc[-1]:return 1
    elif data.close.iloc[-1]<LB.iloc[-1]:return -1
    else: return 0   

## 7、 CCI
def cci(data,alpha=0.015,n=20):
    tp_i=(data.close+data.low+data.high)/3
    matp_i=tp_i.rolling(n).mean()
    meanDev=tp_i.rolling(n).apply(lambda x:abs(x-x.mean()).mean(),raw=False)
    cci=(tp_i-matp_i)/(alpha*meanDev)
    return cci
##8、BIAS
def bias(data,n=10):
    #N期BIAS=(当日收盘价-N期平均收盘价)/N期平均收盘价*100%
    bias_n=(data.close - data.close.rolling(6, min_periods=1).mean())/ data.close.rolling(6, min_periods=1).mean()*100
    return bias_n   
    
#相对强弱类    
##1、KDJ
def KDJ(data,N=9,M1=3,M2=3):
        llv = data.low.rolling(N).min() # 假设你的low是一个pandas.series的对象
        hhv = data.high.rolling(N).max() 
        rsv = (data.close - llv)/(hhv -llv)*100
        k = rsv.ewm(M1-1).mean()
        d = k.ewm(M2-1).mean()
        j = 3*k - 2*d
        return k,d,j
##2、RSI
def RSI(data,N1=6,N2=12,N3=24):
    lc = data.close.shift(1)
    #计算前收盘价
    max_diff = (data.close - lc)
    abs_diff = max_diff.copy()

    max_diff[max_diff <0 ] = 0#实现MAX(CLOSE-LC,0)
    abs_diff = abs_diff.abs()#实现ABS(CLOSE-LC)
    RSI1,RSI2,RSI3 = (max_diff.ewm(N-1).mean()/abs_diff.ewm(N-1).mean()*100 for N in [N1,N2,N3])
    return RSI1,RSI2,RSI3
评论: 6

Looks like your connection to 掘金量化社区 - 量化交易者的交流社区 was lost, please wait while we try to reconnect.