站点图标 AI技术聚合

基于MT策略的实战分析

基于MT策略的实战分析

一、概述

1.1研究背景

    量化投资是指通过数量化方式及计算机程序化发出买卖指令,以获取稳定收益为目的的交易方式。在海外的发展已有30多年的历史,其投资业绩稳定,市场规模和份额不断扩大、得到了越来越多投资者认可。从全球市场的参与主体来看,按照管理资产的规模,全球排名前四以及前六位中的五家资管机构,都是依靠计算机技术来开展投资决策,由量化及程序化交易所管理的资金规模在不断扩大。事实上,互联网的发展,使得新概念在世界范围的传播速度非常快,作为一个概念,量化投资并不算新,国内投资者早有耳闻。但是,真正的量化基金在国内还比较罕见。本文主要介绍动量交易策略。
    Jegadeesh与Titman(1993)在对资产股票组合的中期收益进行研究时发现,与DeBond和Thaler(1985)的价格长期回归趋势、Jegadeesh(1990)与Lehmann(1990)的以周为间隔的短期价格回归趋势的实证结果不同,以3到12个月为间隔所构造的股票组合的中期收益呈现出延续性,即中期价格具有向某一方向连续变动的动量效应。
    Rouvenhorst(1998)在其他十二个国家发现了类似的中期价格动量效应,表明这种效应并非来自于数据采样偏差。事实上,动量交易策略,也有称相对强度交易策略,在实践中早在这些研究之前就已有了广泛的应用,如美国的价值线排名的利用等。

1.2研究意义

    动量投资策略的主要论据是反应不足和保守心理,研究认为动量交易策略能够获利,存在着许多解释:一种解释是,“收益动量”,即当股票收益的增长超过预期,或者当投资者一致预测股票未来收益的增长时,股票的收益会趋于升高。因此,动量交易策略所获得的利润是由于股票基本价值的变动带来的。另一种解释是,基于价格动量和收益动量的策略因为利用了市场对不同信息的反应不足而获利。收益动量策略是利用了对公司短期前景的反应不足一一最终体现在短期收益中;价格动量策略利用了对公司价值有关信息反应迟缓和在短期收益中未被近期收益和历史收益增长充分反应的公司长期前景。

二、策略介绍

2.1数据集

2.2MT策略

    MT策略即动量交易策略(Momentum Trading Strategy)。在经典力学里,动量指的是物体的质量和速度的乘积。牛顿当初在创造动量这个概念时,意图捕捉的是“运动的量”这个概念。所以我们可以理解为物体必须有运动才会有动量。背后的直觉是:当物体有运动,就会有速度;速度加上物体本身的质量,就决定了动量。速度和质量一方面描述了物体的运动状态,另一方面也刻画出其保持已有运动状态的趋势,即惯性大小。

2.2.1动量效应产生的原因

    对于证券市场上动量效应的产生原因,传统金融学与行为金融学有截然不同的看法。传统金融学在保留期望效用与理性人假设的前提下,试图引入更多的风险因子,以便产生新的风险溢酬来捕捉动量效应产生的超额收益。一般而言,这些尝试并没有获得广泛的认可,同时也存在是否过度拟合的疑虑。与传统金融理论相悖,行为金融学则认为投资人在复杂的市场环境中无法完美地预期与判断,故有“非理性”的行为与随之而来的定价偏差;同时,套利者受限于市场机制与风险承担能力,也不一定能及时纠正偏差的价格。不过,真正的原因何在,说法却不一而足。较为人所熟知的有“反应不足”、“正反馈模式”和“过度反应”等见解。  
    针对第一种说法“反应不足”举例来说,当上市公司出现利好信息时,其证券价格会随之上涨,但由于投资者没有及时地接收、消化这一信息,价格对此信息的反应无法一步到位。如百度公司发布其年中财报,财报显示百度公司运营很好。投资者A可能是在财报发布当下即购买百度股票,投资者B在五天后才看到这个财报,其又观望了一天,然后再去购买百度股票。对于百度的股价来说,这个利好信息本身应导致百度股价上涨;市场上A类的投资者,会使百度股价在财报发布的当下做出反应;但由于市场上还有很多信息反应不足的B类投资者,百度股价上涨会持续一段时间,因而产生百度股价的动量效应。  
    另一种说法“正反馈模式”,借由羊群效应来说明动量产生的原因。大多数投资人有从众心理,认知或判断倾向于公众舆论或行为,证券市场即有“赢者恒赢,输者恒输”的现象。  
    第三种说法“过度反应”,是指投资人对私有信息的预测性,自身的投资判断能力等高估而产生的过度反应。更甚者,短期的趋势变化“不出所料”这个心理、行为现象会被进一步强化。  
    上述三种行为金融的理论除了解释的角度有所差异外,其预测的现象也不尽然相同。“反应不足”预测动量效应只存在于短期,会随着时间的延长而逐渐消失。“正反馈模式”与“过度反应”则认为动量为短期偏离基本价值的泡沫,当泡沫随着时间拉长而有朝一日破裂时,股价便会反转。  

2.2.2价格动量的计算公式

(1)作差法求动量值
    作差法,即今天的价格减去一段时间间隔(m期)以前的价格,用公式表示 t 时期的二期动量Momentum*为:  

    其中,P_t为股票 t 时期的价格,m 表示时间间隔,P_{t-m}为股票在 t-m 期的价格。
(2)作除法求动量值
    动量的另一种计算方式是做除法,即 t 期的价格减去其 m 期以前的价格 P_{t-m},再除以P_{t-m}。做除法求出的动量值衡量的是价格变化的比率,用公式表示 t 时期的 ROC(Rate of Change)为:  

    其中,ROC_t 表示股票 t 时期的 m 期动量值(价格变化率),P_t 表示股票 t 时期的价格,m 表示时间间隔,P_{t-m}表示股票在 t-m 期的价格。
(3)动量交易策略的一般思路
    运用动量指标制定交易策略,常用的交易策略可以总结为下面的四个步骤。
  • 获取股票价格(一般为收盘价)数据;

  • 确定时间跨度和动量表达式,计算股票的动量值;

  • 根据动量指标制定交易策略;在动量指标运用上,最直觉的交易策略是动量大于0,说明股票可能还具备上涨的能量,释放出买入的信号;当股票的动量值小于0,说明股票可能有下跌的能量,释放出卖出信号。简而言之,若动量大于0,则买入股票;若动量小于0,则卖出股票。

  • 交易策略的回测与评价。

三、策略之Python实现

    本文以万科股票数据为例,进行动量交易策略的实战分析,下面是具体的代码实现过程。

3.1导入数据

# 导入相关包
import pandas as pd
import matplotlib.pyplot as plt
# 读取数据
Vanke=pd.read_csv('D://文档/桌面文件夹/Python Quant Book/part 5/028/Vanke.csv')
Vanke.index=Vanke.iloc[:,1]
Vanke.index=pd.to_datetime(Vanke.index, format='%Y-%m-%d')
Vanke=Vanke.iloc[:,2:]
# Vanke.head(5)
# 提取收盘价
Close=Vanke.Close
Close.describe()
count    344.000000
mean       9.846337
std        2.268338
min        6.570000
25%        7.987500
50%        9.250000
75%       11.967500
max       14.910000
Name: Close, dtype: float64
# 求滞后5期的收盘价变量
lag5Close=Close.shift(5)

3.2 利用作差法计算万科股票收盘价的 5 期动量值

# 求5日动量
momentum5=Close-lag5Close
momentum5.tail()
Date
2015-04-22    0.98
2015-04-23    0.46
2015-04-24   -0.25
2015-04-27    0.49
2015-04-28   -0.29
Name: Close, dtype: float64
# 绘制收盘价和5日动量曲线图
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.figure(figsize=(16,12))

plt.subplot(211)
plt.plot(Close,'b*')
plt.xlabel('date')
plt.ylabel('Close')
plt.title('万科股价5日动量图')

plt.subplot(212)
plt.plot(momentum5,'r-*')
plt.xlabel('date')
plt.ylabel('Momentum5')
Text(0, 0.5, 'Momentum5')



D:\Anaconda3\lib\site-packages\matplotlib\backends\backend_agg.py:211: RuntimeWarning: Glyph 8722 missing from current font.
  font.set_text(s, 0.0, flags=flags)
D:\Anaconda3\lib\site-packages\matplotlib\backends\backend_agg.py:180: RuntimeWarning: Glyph 8722 missing from current font.
  font.set_text(s, 0, flags=flags)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-S4MxBV8b-1653482057758)(output_16_2.png)]

# 作除法,求万科股票的5日动量值
Momen5=Close/lag5Close-1
Momen5=Momen5.dropna();
Momen5[0:5]
Date
2014-01-08   -0.075965
2014-01-09   -0.066333
2014-01-10   -0.058673
2014-01-13   -0.032086
2014-01-14   -0.026918
Name: Close, dtype: float64

3.3编写动量函数 momentum()

    动量值一般采用作差法求得,为了计算动量值方便,此处编写一个简单的动量函数momentum(),该函数有价格 price 和期数 period 两个参数,函数定义如下:
#momentum function
def momentum(price,periond):
    lagPrice=price.shift(periond)
    momen=price-lagPrice
    momen= momen.dropna()
    return(momen)
momentum5 = momentum(Close,5)
momentum5.head()
Date
2014-01-08   -0.61
2014-01-09   -0.53
2014-01-10   -0.46
2014-01-13   -0.24
2014-01-14   -0.20
Name: Close, dtype: float64
momentum(Close,5).tail(n=5)
Date
2015-04-22    0.98
2015-04-23    0.46
2015-04-24   -0.25
2015-04-27    0.49
2015-04-28   -0.29
Name: Close, dtype: float64

3.4 万科股票2015年走势和35日动量线

# 计算35日动量值
momen35=momentum(Close,35)
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.dates import DateFormatter, WeekdayLocator, \
                                DayLocator, MONDAY, date2num
# from matplotlib.finance import candlestick_ohlc
from mpl_finance import candlestick_ohlc

plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
D:\Anaconda3\lib\site-packages\mpl_finance.py:22: DeprecationWarning: 

  =================================================================

   WARNING: `mpl_finance` is deprecated:

    Please use `mplfinance` instead (no hyphen, no underscore).

    To install: `pip install --upgrade mplfinance` 

   For more information, see: https://pypi.org/project/mplfinance/

  =================================================================

  category=DeprecationWarning)
# 定义 candleLinePlots 函数
def candleLinePlots(candleData, candleTitle='a', **kwargs):
    Date = [date2num(date) for date in candleData.index]
    candleData.loc[:,'Date'] = Date
    listData = []
    
    for i in range(len(candleData)):
        a = [candleData.Date[i],\
            candleData.Open[i],candleData.High[i],\
            candleData.Low[i],candleData.Close[i]]
        listData.append(a)
    # 如 果 不 定 长 参 数 无 取 值 , 只 画 蜡 烛 图
    ax = plt.subplot()
    
    # 如 果 不 定 长 参 数 有 值 , 则 分 成 两 个 子 图
    flag=0
    if kwargs:
        if kwargs['splitFigures']:
            ax = plt.subplot(211)
            ax2= plt.subplot(212)
            flag=1;
        # 如 果 无 参 数 splitFigures , 则 只 画 一 个 图 形 框
        # 如 果 有 参 数 splitFigures , 则 画 出 两 个 图 形 框
        
        for key in kwargs:
            if key=='title':
                ax2.set_title(kwargs[key])
            if key=='ylabel':
                ax2.set_ylabel(kwargs[key])
            if key=='grid':
                ax2.grid(kwargs[key])
            if key=='Data':
                plt.sca(ax)
                if flag:
                    plt.sca(ax2)
                    
                #一维数据
                if kwargs[key].ndim==1:
                    plt.plot(kwargs[key],\
                             color='k',\
                             label=kwargs[key].name)
                    plt.legend(loc='best')
                #二维数据有两个columns
                elif all([kwargs[key].ndim==2,\
                          len(kwargs[key].columns)==2]):
                    plt.plot(kwargs[key].iloc[:,0], color='k', 
                             label=kwargs[key].iloc[:,0].name)
                    plt.plot(kwargs[key].iloc[:,1],\
                             linestyle='dashed',\
                             label=kwargs[key].iloc[:,1].name)
                    plt.legend(loc='best')
    
    mondays = WeekdayLocator(MONDAY)
    weekFormatter = DateFormatter('%y %b %d')
    ax.xaxis.set_major_locator(mondays)
    ax.xaxis.set_minor_locator(DayLocator())
    ax.xaxis.set_major_formatter(weekFormatter)
    plt.sca(ax)
    
    candlestick_ohlc(ax,listData, width=0.7,\
                     colorup='r',colordown='g')
    ax.set_title(candleTitle)
    plt.setp(ax.get_xticklabels(),\
             rotation=20,\
             horizontalalignment='center')
    ax.autoscale_view()
    
    return(plt.show())
import candle
Vanke15=Vanke['2015']
plt.figure(figsize=(16,12))
candle.candleLinePlots(Vanke['2015'],\
               candleTitle='万科股票2015年日K线图',\
               splitFigures=True,Data=momen35['2015'],\
               title='35日动量',ylabel='35日动量')

D:\Anaconda3\lib\site-packages\pandas\core\indexing.py:844: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  self.obj[key] = _infer_fill_value(value)
D:\Anaconda3\lib\site-packages\pandas\core\indexing.py:965: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  self.obj[item] = s

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-K3qh1S8y-1653482057760)(output_26_1.png)]

1

3.5 运用动量指标交易万科股票

    结合动量指标的交易思想,用Python编写代码来捕捉市场中的可能买卖点。从动量的计算公式可以看出,动量值的大小与时间跨度。有很大的关系,在时间跨度。的设定上,见仁见智,没有统一标准。在本次的动量指标计算中,将时间跨度设定为35日,根据35日动量的取值情况来捕捉买卖点。

    当35日动量为正值时,市场可能还存在上升的能量,我们推断第2期可以进行买入操作;

    当35日动量为负值时,我们预期未来价格可能要下跌,第2期可以进行卖出操作。

    还请注意,在投资实战中,投资者会综合多种指标和形态来确定买入点和卖出点。为了着重体现动量指标的交易思想,这里假定买卖操作的确定只依据动量这一个指标。
  • 首先提取出万科股票的收盘价数据,计算35日动量值。
# 提取万科股票收盘价格数据,计算35日动量值
Close=Vanke.Close
momen35=momentum(Close,35)
momen35.head()
Date
2014-02-20   -0.71
2014-02-21   -0.83
2014-02-24   -1.15
2014-02-25   -0.91
2014-02-26   -0.75
Name: Close, dtype: float64
  • 结合35日动量值的取值情况来判断买卖点,35日动量释放的买卖点信号用signal表示。
#当35日动量值为负值时,signal取值为一1,表示卖出;
#当35日动量值为非负值时,signal取值为1,表示买入;
signal=[]
for i in momen35:
    if i>0:
        signal.append(1)
    else:
        signal.append(-1)
        
signal = pd.Series(signal,index=momen35.index)
signal=pd.Series(signal,index=momen35.index)
signal.head()
Date
2014-02-20   -1
2014-02-21   -1
2014-02-24   -1
2014-02-25   -1
2014-02-26   -1
dtype: int64
  • 根据买卖点制定买入和卖出交易,并计算收益率。
tradeSig = signal.shift(1)
ret = Close/Close.shift(1) - 1

Mom35Ret = ret*(signal.shift(1))
Mom35Ret=ret*(signal.shift(1))
Mom35Ret[0:5]
real_Mom35Ret=Mom35Ret[Mom35Ret!=0]
real_ret=ret[ret!=0]
  • 35日动量指标交易策略评价。
    35日动量指标交易策略确捕捉到买卖点信号以后,我们来计算一下该35日动量指标买卖点预测的准确率。当出现买入信号时,signal取值为1,当我们预计的价格上涨这件事发生了(也就是预测准确,收益率大于0),则signal与ret的乘积大于0。当出现卖出信号时,signal取值为一1,且预测准确时,收益率小于0 9 signal(取值为一1)与re七(负值)的乘积也是大于0。当35日动量值交易的收益率大于0时,则说明买卖点预测正确。
plt.figure(figsize=(16,12))

plt.subplot(2,1,1)
plt.plot(real_ret,'b')
plt.ylabel('return')
plt.title('万科收益率时序图')

plt.subplot(2,1,2)
plt.plot(Mom35Ret,'r')
plt.ylabel('Mom35Ret')
plt.title('万科动量交易收益率时序图')
Text(0.5, 1.0, '万科动量交易收益率时序图')

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sGONJIrg-1653482057761)(output_36_1.png)]

# 计算指标交易获胜率
win = Mom35Ret[Mom35Ret>0]
winrate = len(win)/len(Mom35Ret)
loss = -Mom35Ret[Mom35Ret<0]

下面提取出35日东了指标预测正确时的收益率与预测失败是的收益率,并进行分析比较

plt.figure(figsize=(10,8))

plt.subplot(2,1,1)
win.hist()
plt.title("盈利直方图")

plt.subplot(2,1,2)
loss.hist()
plt.title("损失直方图")

plt.show()

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kE3w5So4-1653482057762)(output_39_0.png)]

  • 计算两种收益率的平均值与分位数值
performance=pd.DataFrame({"win":win.describe(),"loss":loss.describe()})
performance
winloss
count148.000000136.000000
mean0.0186460.018514
std0.0182610.015860
min0.0007690.000805
25%0.0054720.008214
50%0.0134440.015996
75%0.0247860.022360
max0.0996840.099852

四、结果分析

从两种收益率的分位数分布情况可以看出,尽管万科股票35日动量指标交易策略预测准确率高于0.5,但是动量指标预测错误时损失的收益率整体比预测准确时盈利的收益率大。

五、心得体会

    本学期通过对《量化投资以Python为工具》这门课程的学习,不但加强了对于Python基础知识的理解和认识 还掌握了复杂的算法,针对具体的数据能够进行合理有效的分析,针对不同的数据类型,先进行科学的数据预处理操作,之后针对处理过的数据,选取适当的算法进行分析,最后经过科学的检验和流程得出最有效的结果。  
    这篇文章主要是针对股票数据进行分析,本文用到的是动量交易策略,即预先对股票收益和交易量设定过滤准则,当股票收益或股票收益和交易量同时满足过滤准则就买入或卖出股票的投资策略。通过本次实验,极大的提升了我的实际操作能力,通过解决代码撰写过程中的一个一个问题,加强了我对Python这门语言的运用能力。第一个问题是对Python引用模块这一部分知识的学习,之前引用模块最主要的就是直接用import进行调用,调用过程中如果出现报错,一般是因为设备上没有这个python模块,需要自行安装(国内安装可以采用清华镜像进行下载,下载快速又稳定:-i https://pypi.tuna.tsinghua.edu.cn/simple )。另一个是调用自己编写的模块,我采用的是把编写好的python文件保存到jupyter notebook默认文件夹进行调用。第二个问题是画图时遇到的,自己画图时总是忘记设置画布的大小,导致生成的图片观察起来不是很舒适,这里建议大家可以在画图之前先设置好画布大小,这样生成的图片观察起来会比较舒适。这两个是在写代码过程中注意到的两个小细节,不足之处,还望不吝赐教。
    最后,感谢老师在日常学习中的敦敦教诲,以及同学们在撰写论文中对我的帮助。

参考文献

[1]:https://baike.baidu.com/item/%E5%8A%A8%E9%87%8F%E4%BA%A4%E6%98%93%E7%AD%96%E7%95%A5
[2]:https://blog.csdn.net/qq_43382509/article/details/102830532
[3]:https://blog.csdn.net/qq_35883464/article/details/87793951

文章出处登录后可见!

已经登录?立即刷新
退出移动版