学习笔记_如何搭建一个完整的策略框架:以均线策略为例

参考资料:
https://bbs.pinggu.org/thread-4745852-1-1.html
【量化小讲堂-Python、Pandas系列15】完整策略框架:以均线策略为例
这个是邢不行老师的量化系列课程,有源码分享,很赞!强烈推荐!

https://www.jianshu.com/p/363aa2dd3441
Python计算量化策略评估指标
微信公众号:Python金融量化(id:tkfy920),专注于分享python在金融领域的应用。推荐推荐!

下图是 JoinQuant 聚宽量化平台上的回测界面,这个学习笔记的内容就是如何做出这样一个图!

工具:Spyder
数据:tushare
学习笔记_如何搭建一个完整的策略框架:以均线策略为例_第1张图片
学习笔记_如何搭建一个完整的策略框架:以均线策略为例_第2张图片

一、产生买卖信号的策略

1、导入原始数据

from __future__ import division #导入精确除法
import pandas as pd
import warnings     # warning通常用于提示用户一些错误或者过时的用法。
warnings.filterwarnings("ignore")
import numpy as np
import tushare as ts
import matplotlib.pyplot as plt


pro = ts.pro_api()
api = ts.pro_api('29acf1ff24d0c5539a992821efac9b2037d42610f4cb0724ea93e084')

stock_data = ts.pro_bar(pro_api=api, ts_code='002916.SZ', adj='qfq', start_date='20171213', end_date='20190128')[['ts_code','trade_date','open','close','pct_chg']]
stock_data['pct_chg'] = stock_data['pct_chg'].div(100)
stock_data.axes
stock_data.index = pd.DatetimeIndex(stock_data.index)
stock_data.sort_values(by='trade_date', inplace=True)

学习笔记_如何搭建一个完整的策略框架:以均线策略为例_第3张图片
2、写策略函数,得到买卖信号:
e.g. 均线策略

def simple_ma(stock_data, window_short=5, window_long=60):
    """
    :param stock_data: 股票数据集
    :param window_short: 较短的窗口期
    :param window_long: 较长的窗口期
    :return: 当天收盘时持有该股票的仓位数据
    
    最简单的均线策略逻辑:
    短期均线上穿长期均线,且第二天开盘没有涨停,则以第二天开盘价全仓买入;
    短期均线下穿长期均线,且第二天开盘没有跌停,则以第二天开盘价全仓卖出。
    """
    
    # 计算短期和长期的移动平均线
    stock_data['ma_short'] = pd.Series.rolling(stock_data['close'], window=window_short, min_periods=5).mean()
    stock_data['ma_long'] = pd.Series.rolling(stock_data['close'], window=window_long, min_periods=60).mean()
    
    # 出现买入信号而且第二天开盘没有涨停
    stock_data.ix[(stock_data['ma_short'].shift(1) > stock_data['ma_long'].shift(1)) & 
                  (stock_data['open'] < stock_data['close'].shift(1) * 1.097), 'position'] = 1
                  
    # 出现卖出信号而且第二天开盘没有跌停
    stock_data.ix[(stock_data['ma_short'].shift(1) < stock_data['ma_long'].shift(1)) & 
                  (stock_data['open'] > stock_data['close'].shift(1) * 0.903), 'position'] = 0

    stock_data['position'].fillna(method='ffill', inplace=True)
    stock_data['position'].fillna(0, inplace=True) 
    
    return stock_data
   
stock_data = simple_ma(stock_data, window_short=5, window_long=60)
      

学习笔记_如何搭建一个完整的策略框架:以均线策略为例_第4张图片

二、根据买卖信号生成资金曲线

1、根据每日仓位计算策略的日收益率

def account(df, slippage=1.0 / 1000, commision_rate=2.0 / 1000):
    """
    :param df: 股票账户数据集
    :param slippage: 买卖滑点 默认为1.0 / 1000 #滑点是指下单的点位和最后成交的点位有差距。
    :param commision_rate: 手续费 默认为2.0 / 1000
    :return: 返回账户资产的日收益率和日累计收益率的数据集
    """
    
    # 第一天没有交易信号产生
    df.ix[0, 'capital_rtn'] = 0    
    
    # 当加仓时,计算当天资金曲线涨幅 capital_rtn.capital_rtn = 今天开盘新买入的 position 在今天的涨幅(扣除手续费) + 昨天的position在今天涨幅
    df.ix[df['position'] > df['position'].shift(1), 'capital_rtn'] = (df['close'] / df['open'] - 1) * (1 - slippage - commision_rate) * (df['position'] - df['position'].shift(1)) 
    + df['pct_chg'] * df['position'].shift(1)
   
    # 当减仓时,计算当天资金曲线涨幅 capital_rtn.capital_rtn = 今天开盘卖出的 position 在今天的涨幅(扣除手续费) + 还剩的position在今天的涨幅
    df.ix[df['position'] < df['position'].shift(1), 'capital_rtn'] = (df['open'] / df['close'].shift(1) - 1) * (1 - slippage - commision_rate) * (df['position'].shift(1) - df['position']) 
    + df['pct_chg'] * df['position']
    
    # 当仓位不变时,当天的 capital_rtn 是当天的 pct_chg * position
    df.ix[df['position'] == df['position'].shift(1), 'capital_rtn'] = df['pct_chg'] * df['position']
    
    return df
    
stock_data = account(stock_data, slippage=1.0 / 1000, commision_rate=2.0 / 1000)

学习笔记_如何搭建一个完整的策略框架:以均线策略为例_第5张图片
2、获取数据函数(为下面作图和计算做准备):

def get_stock_data(stock_data, index_code='000300.SH', start_date='20171213', end_date='20190128'):
    """
    :param stock_data: 股票账户数据集
    :param index_code: 指数代码,e.g. 沪深300指数:'000300.SH'
    :param start_date: 回测开始日期,e.g. '20171213'
    :param end_date: 回测结束日期, e.g. '20190128'
    :return: 函数返回股票和基准的数据集
    """
    # 选取在日期范围内的股票数据序列并按日期排序:
    stock_data_subset = stock_data[['ts_code', 'trade_date', 'close', 'pct_chg', 'capital_rtn']]
        
    # 获取基准指数数据序列并按日期排序:   
    benchmark = pro.index_daily(ts_code=index_code, start_date=start_date, end_date=end_date)
    benchmark['pct_chg'] = benchmark['pct_chg'].div(100)
    benchmark = benchmark[['trade_date', 'pct_chg']]
    benchmark.sort_values(by='trade_date', inplace=True)
    benchmark['trade_date'] = pd.DatetimeIndex(benchmark['trade_date'])
    benchmark.set_index('trade_date', inplace=True) 

    return stock_data_subset, benchmark
	
data = get_stock_data(stock_data, index_code='000300.SH', start_date='20171213', end_date='20190128')
stock_data_subset = data[0]
benchmark = data[1]
code_list = list(stock_data_subset['ts_code'].unique())

3、计算策略和基准在回测期间的累计收益率并画图:

def cumulative_return(stock_data_subset, benchmark):
    """
    :param stock_data_subset: 股票数据集
    :param benchmark: 基准的数据集
    :return: 输出策略和基准的累计收益率曲线
    """
    df = stock_data_subset['capital_rtn']
    df1 = benchmark['pct_chg']

    stock_data_subset['strategy_rtn_line'] = (df + 1).cumprod() - 1
    benchmark['benchmark_rtn_line'] = (df1 + 1).cumprod() - 1

    SR = stock_data_subset[['strategy_rtn_line']]
    BR = benchmark[['benchmark_rtn_line']]
    
    df_concat = pd.concat([SR,BR], axis=1, join='outer')
    df_concat.fillna(method = 'ffill', inplace=True) 

    df_concat.plot(kind='line', figsize=(12,5), title='return_line')

    return plt.show()

学习笔记_如何搭建一个完整的策略框架:以均线策略为例_第6张图片

三、根据资金曲线计算策略评价指标

1、计算策略累计收益率、年化收益率函数、基准累计收益率

def all_return(stock_data_subset, benchmark):
    """
    :param stock_data_subset:策略日收益率数据
    :param benchmark:基准日收益率数据
    :return: 输出在回测期间的策略累计收益率、年化收益率,基准累计收益率
    """   
    # 计算策略累计收益率:
    strategy_ret = stock_data_subset[['capital_rtn']].apply(lambda x: (x + 1.0).prod() - 1.0)
    SR = pd.DataFrame(float(strategy_ret), columns=['策略收益'], index=code_list) 

	# 计算策略年化收益率:
    annual_ret = pow(1+float(strategy_ret), 250/len(stock_data_subset))-1     
    AR = pd.DataFrame(float(annual_ret), columns=['策略年化收益'], index=code_list)

	# 计算基准累计收益率
    benchmark_ret = benchmark[['pct_chg']].apply(lambda x: (x + 1.0).prod() - 1.0)
    BR = pd.DataFrame(float(benchmark_ret), columns=['基准收益'], index=code_list) 
    
    return SR, AR, BR

all_return = all_return(stock_data_subset, benchmark)
strategy_ret = all_return[0]
annual_ret = all_return[1]
benchmark_ret = all_return[2]

2、计算 Alpha 和 Beta

# 计算贝塔的函数:
def beta(stock_data_subset, benchmark):
    """
    :param stock_data_subset:策略日收益率数据
    :param benchmark:基准日收益率数据
    :return: 输出在回测期间的策略的贝塔值
    """   
    # 计算协方差、方差:
    cov = stock_data_subset['capital_rtn'].cov(benchmark['pct_chg'])
    var = benchmark['pct_chg'].var()

	# 计算贝塔:
    beta = cov / var
    beta = pd.DataFrame(beta, columns=['贝塔系数'], index=code_list)
    
    return beta

beta = beta(stock_data_subset, benchmark)
    
# 计算 Alpha的函数:
def alpha(stock_data_subset, benchmark):
    """
    :param stock_data_subset:策略日收益率数据
    :param benchmark:基准日收益率数据
    :return: 输出在回测期间的策略的 Alpha
    """
    # 计算贝塔值:
    cov = stock_data_subset['capital_rtn'].cov(benchmark['pct_chg'])
    var = benchmark['pct_chg'].var()
    beta = cov / var

	# 计算策略的年化收益率:
    strategy_ret = stock_data_subset[['capital_rtn']].apply(lambda x: (x + 1.0).prod() - 1.0)
    annual_stock = pow(1+float(strategy_ret), 250/len(stock_data_subset))-1

	# 计算基准的年化收益率:
    index_ret = benchmark[['pct_chg']].apply(lambda x: (x + 1.0).prod() - 1.0)
    annual_index = pow(1+float(index_ret), 250 / len(benchmark)) - 1 

    # 无风险利率:取10年期国债的到期年化收益率,中国债券信息网2月15日更新为:3.08%
    rf = 0.0308

    # 计算 Alpha值:
    alpha = annual_stock - (rf + beta * (annual_index - rf)) 
    alpha = pd.DataFrame(alpha, columns=['Alpha系数'], index=code_list)
    
    return alpha  
  
alpha = alpha(stock_data_subset, benchmark) 

3、计算夏普比率和信息比率

# 计算夏普比率函数:
def sharpe_ratio(stock_data_subset):
    """
    :param stock_data_subset:策略日收益率数据
    :return: 输出夏普比率
    """    
    # 无风险利率:取10年期国债的到期年化收益率,中国债券信息网2月15日更新为:3.08%
    rf = 0.0308 
    
    # 计算策略和无风险利率之间的日超额收益率:
    ex_return = stock_data_subset['capital_rtn'] - rf/250 
    
    # 计算夏普比率(年化):
    sharpe = np.sqrt(len(ex_return)) * ex_return.mean()/ex_return.std() 
    sharpe = pd.DataFrame(sharpe, columns=['Sharpe'], index=code_list)
    
    return sharpe

sharpe = sharpe_ratio(stock_data_subset)

def info_ratio(stock_data_subset, benchmark):
    """
    :param stock_data_subset:策略日收益率数据
    :param benchmark:基准日收益率数据
    :return: 输出在回测期间的策略的信息比率
    """
    # 计算策略和无风险利率之间的日超额收益率:
    ex_return = stock_data_subset['capital_rtn'] - benchmark['pct_chg']
    
    # 计算信息比率(年化):
    info = np.sqrt(len(ex_return)) * ex_return.mean()/ex_return.std() 
    info = pd.DataFrame(info, columns=['Info'], index=code_list)

    return info
 
info =  info_ratio(stock_data_subset, benchmark) 
	

4、计算胜率

# 计算策略在回测期间的胜率函数:
def win_rate(stock_data_subset):
    """
    :param stock_data_subset:策略日收益率数据
    :return: 输出策略在回测期间的胜率
    """
    df = stock_data_subset[['capital_rtn']]
    # 计算策略胜率
    win_rate = len(df[df['capital_rtn'] > 0]) / len(df[df['capital_rtn'] != 0])
    win_rate = pd.DataFrame(float(win_rate), columns=['胜率'], index=code_list)

    return win_rate
    
win_rate =  win_rate(stock_data_subset)

5、计算最大回撤

# 计算最大回撤函数:
def max_drawdown(stock_data_subset):
    """
    :param stock_data_subset:策略日收益率数据
    :return: 输出最大回撤及开始日期和结束日期
    """
    # 取出数据集中股票的qfq收盘价
    df = stock_data_subset[['trade_date', 'close']]

	# 计算当日之前的账户最大价值
    df['max2here'] = df['close'].cummax()
	# 计算当日的回撤  
    df['dd2here'] = (df['max2here'] - df['close'])/df['max2here'] 

    # 计算最大回撤和结束时间
    temp = df.sort_values(by='dd2here').iloc[-1][['trade_date', 'dd2here']]
    max_dd = temp['dd2here']
    max_dd = "%.2f%%" % (max_dd * 100)
    end_date = temp['trade_date']

	# 计算开始时间
    df = df[df['trade_date'] <= end_date]
    start_date = df.sort_values(by='close').iloc[-1]['trade_date']

    md = pd.DataFrame(max_dd, columns=['最大回撤'], index=code_list)
    
    print ('最大回撤为:%s, 开始日期:%s, 结束日期:%s' % (max_dd, start_date, end_date))
    
    return md   

md = max_drawdown(stock_data_subset)
    

6、所有指标合并成一个表格

indicators = pd.concat([strategy_ret, annual_ret, benchmark_ret, beta, alpha, sharpe, info, win_rate, md],
                       axis=1, join='outer')
# 结果保留三位小数:
indicators = indicators.round(3)

在这里插入图片描述
7、返回图+表

cumulative_return(stock_data_subset, benchmark)
print (indicators)

学习笔记_如何搭建一个完整的策略框架:以均线策略为例_第7张图片
如果有什么错误或建议,欢迎大家留言指教哈!

你可能感兴趣的:(学习笔记_如何搭建一个完整的策略框架:以均线策略为例)