自学量化投资之旅-学习第一个策略EMV指标

虽然说是学习的第一个指标,但是其实在上个月我是学过量化的,当时第一个学习的是均线策略,所以这里指的是我在网上开始分享式学习的第一个策略。

这里为什么选择EMV作为第一个策略进行学习呢,主要还是觉得相比均线这种只考虑价格的指标来说,EMV是兼顾量的指标可能更能真实反映市场行情。

为更好地编写代码和理解指标这里复习一下EMV的公式:首先EMV是由EM计算出来的,短均线和长均线组成。EM = ((当日最高价+当日最低价)/2 - (昨日最高价+昨日最低价)/2)*(当日最高-当日最低)/成交量。

接下来一步一步来:

1、定义获取本地股票数据的函数

def get_stock_data(inport_code, start_date=None, end_date=None,
                   usecols=('trade_date','open','high','low','close','pre_close','pct_chg','vol','amount'),
                   recover_type='后复权'):
    '''
    读取股票行情数据(只支持price)
    :param inport_code: 股票代码(纯数字字符串)
    :param start_date: 开始时间
    :param end_date: 截止时间
    :param usecols: 获取的列
    :param recover_type: 复权类型
    :return: 
    '''
    # 判断所属市场
    if inport_code.startswith('6'):  # 判断市场标识
        market_code = inport_code + '.SH'
    else:
        market_code = inport_code + '.SZ'

    # 读取文件
    file_root = os.path.join(r'G:\PycharmProjects\DeltaTrader\data\data_tushare\{}.csv'.format(market_code))
    data = pd.read_csv(file_root, parse_dates=['trade_date'], index_col='trade_date', usecols=usecols)

    # 对起始时间参数进行处理
    if start_date is not None: 
        start_date = datetime.datetime.strptime(start_date, '%Y-%m-%d')
    else:
        start_date = data.index[0]
    # 对结束时间参数进行处理
    if end_date is not None: 
        end_date = datetime.datetime.strptime(end_date, '%Y-%m-%d')
    else:
        end_date = data.index[-1]
    # 获取指定日期的数据
    data = data.loc[start_date:end_date,]

    # 计算复权价
    data = cal_recover_price(data, recover_type)
    return data
if __name__ == '__main__':
    # pass
    df = get_stock_data('000001','2010-01-01')
    print(df)  

2、接下来计算指标并计算买卖信号(由于成交量会受到除权的影响,所以我采用成交额替代成交量,不合适的话可以指出来哪里有问题。)

3、根据买卖信号计算每日收益率

def account(data, slippage=1/1000, commision_rate=1/1000):
    '''
    根据每日仓位计算总资产的 日收益率
    :param df:
    :param slippage: 滑点 默认千分一
    :param commision_rate: 手续费 默认千分一
    :return: df['capital_rtn']
    '''
    # 生成副本
    data = data.copy()
    data['capital_rtn'] = np.nan
    data.iloc[0, -1] = 0

    # 当加仓时,计算当天的计算曲线 capital_rtn = 昨天的position在今天的涨幅 + 今天开盘新买入position涨幅(扣除手续费)
    data['capital_rtn'] = np.where(
        data['position'] > data['position'].shift(1),
        data['position'].shift(1) * data['pct_chg'] * 0.01 + \
        (data['position'] - data['position'].shift(1)) * \
        (data['close'] / data['open'] - 1) * (1 - slippage - commision_rate),
        data['capital_rtn']
    )
    # 当减仓时,计算当天的计算曲线 capital_rtn = 今天的position的涨幅 + 今天开盘卖出position在今天的涨幅(扣除手续费)
    data['capital_rtn'] = np.where(
        data['position'] < data['position'].shift(1),
        data['position'] * data['pct_chg'] * 0.01 + \
        (data['position'].shift(1) - data['position']) * \
        (data['open'] / data['close'].shift(1) - 1) * \
        (1 - slippage - commision_rate),
        data['capital_rtn']
    )
    # 当仓位不变时,当天capital_rtn = 当天的ptc_change*position
    data['capital_rtn'] = np.where(
    data['position'] == data['position'].shift(1),
    data['position'] * data['pct_chg'] * 0.01, \
    data['capital_rtn'])

    return data

4、根据资产日收益率计算年化收益率,为了规避停牌造成的影响这里要用市场的交易日历为计算,另外顺便计算最大回撤。

def market_calendar():

    # 获取交易日历
    calendars = pd.read_csv(r'G:\PycharmProjects\DeltaTrader\data\finance\2021-08-07_tsc.csv',
                            index_col='cal_date', parse_dates=['cal_date'])
    return calendars

def annual_return(date_line, capital_line):
    '''
    计算年化收益率
    :param date_line: 时间序列
    :param capital_line: 账户价值序列
    :return:
    '''
    # 将数据合并为DateFrame,并按日期排列
    data = pd.DataFrame({'capital':capital_line}, index=date_line)
    # 计算年化收益率
    calendars = market_calendar()
    periods = data.index # 获取个股交易日
    days = calendars.loc[periods[0]:periods[-1]] # 计算股市开市天数
    annual = np.divide(data.iloc[-1]['capital'], data.iloc[0]['capital']) ** (243/len(days.index))-1

    return annual

def max_drawdown(date_line, capital_line):
    '''
    计算最大回撤
    :param date_line: 时间序列
    :param capital_line: 账户价值序列
    :return: 返回最大回撤的值
    '''

    # 将数据合并为DateFrame,并按日期排列
    data = pd.DataFrame({'date': date_line, 'capital': capital_line})
    # 计算最大净值
    data['max_here'] = data['capital'].expanding().max()
    # 计算当日回撤
    data['dd_here'] = data['capital']/data['max_here']
    # 找的最大化回撤的 结束时间 和 值
    end_date, remains = tuple(data.sort_values('capital',ascending=False).iloc[0][['date', 'dd_here']])
    # 找到最大回撤开始时间
    start_date = data.sort_values('capital').iloc[0]['date']
    # print(f'最大回撤为:{(1-remains)*100}%\n开始时间:{start_date}\n结束时间:{end_date}')

    return 1-remains

5、最后写一段代码,对所有股票进行回撤

if __name__ == '__main__':
    # 遍历所有股票文件获取文件名,得到股票代码
    for root, dirs, files in os.walk(r'G:\PycharmProjects\DeltaTrader\data\data_tushare'):
        # 结果容器
        result = []
        for code in files:
            try:
            # 获取对应股票数据
                df = dt.get_stock_data(code.split('.', maxsplit=1)[0])
            # 剔除空数据
            except IndexError as e:
                print(e, code)
                continue
            # 剔除数据长度不满一年半
            if len(df.index) < 366:
                continue
            print(code)
            # 策略数据容器
            re = pd.DataFrame(
                columns=['code', 'start', 'param', 'strategy_rtn', 'stock_rtn', 'strategy_ma', 'stock_ma', 'excessive_rtn'])
            # 计算股票累计收益
            df['stock_capital'] = (df['pct_chg'] * 0.01 +1).cumprod()
            # 调整param
            i = 0
            for p in range(5,27,2):
                for q in range(5,27,2):
                    if p >= q:
                        continue
                    # 计算交易信号
                    df_stg = EMV2(df, p, q)
                    # 计算策略单次收益
                    df_stg = account(df_stg)
                    # 计算策略资金曲线
                    df_stg['capital'] = (df_stg['capital_rtn']+1).cumprod()

                    # 获取时间序列 和 股票and策略 资金曲线
                    date_line = df_stg.index
                    capital_line = df_stg['capital']
                    stock_total = df_stg['stock_capital']

                    # 策略的年化收益
                    strategy_rtn = annual_return(date_line, capital_line)
                    # 股票的年化收益
                    stock_rtn = annual_return(date_line, stock_total)
                    # 股票最大回撤
                    stock_md = max_drawdown(date_line, stock_total)
                    # 策略最大回撤
                    strategy_md = max_drawdown(date_line, capital_line)
                    # ========根据资金曲线计算相关评价指标========
                    re.loc[i, 'code'] = df_stg.iloc[0]['ts_code']
                    re.loc[i, 'start'] = df_stg.index[0]
                    re.loc[i, 'param'] = str(p)+'_'+str(q)
                    re.loc[i, 'strategy_rtn'] = strategy_rtn
                    re.loc[i, 'stock_rtn'] = stock_rtn
                    re.loc[i, 'strategy_md'] = strategy_md
                    re.loc[i, 'stock_md'] = stock_md
                    re.loc[i, 'excessive_rtn'] = re.loc[i, 'strategy_rtn'] - re.loc[i, 'stock_rtn']
                    i +=1
                    print(f'循环{i}遍')

            # 获取最优参数和数据
            re.sort_values('excessive_rtn', ascending=False, inplace=True)
            result.append(re.iloc[0])
    # 合并表格
    re = pd.concat(result, ignore_index=True, axis=0)
    re.to_csv(r'G:\PycharmProjects\DeltaTrader\data\example\EMV.csv',header=False, index=False, mode='a')

搞定!由于数据量特别大,这里就不贴结果了,感兴趣的可以去看下这个帖子:https://blog.csdn.net/xingbuxing_py/article/details/78545509?spm=1001.2014.3001.5501

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值