股票学习网

怎么买股票_怎么买股票入门_新手怎么买股票 - - 股票学习网!

持币观望打一生肖(持币观望英文)

2023-05-16 12:06分类:BOLL 阅读:

要说近期涨幅最大的茅台酒是什么酒?那我回答是生肖茅台酒。一天涨幅最高2000+,这谁顶得住?相比去年的唯唯诺诺,生肖茅台今年要重拳出击了!

 

我们知道茅台生肖酒是在2014年开始推出,到现在已经有着八款生肖酒(牛年还没发售)。生肖酒的发行年份和价格、数量都是不一样的,也因此当前的价值也不同。

 

在2014年最新发布的甲午马年生肖酒,首发价为849元,但是市场的销售价已经高达了18000元的程度,可以看到它的价格确实有着非常多的上涨。

 

当然,相对而言涨得最厉害的还是乙未羊年生肖酒,首发价为849,市场价已经上升到了25000元,翻了将近30倍。但是能够从其他白酒的发行局面来看,并非所有的酒都有着较好的市场表现力,从丙申猴年开始上涨的速度就有所放缓。

 

目前,在价格起伏不定的情况下,不少人开始持币观望,各种鱼龙混杂的信息也在干扰着不少人的购买心态,何时出手?何时再出手?价格走势成了他们最关心的问题。

 

其实在管家看来,茅台最火的两款爆品除了飞天就是生肖,虽然早期的猴,鸡,狗,猪被市场炒高,影响了后续生肖茅台的价格背书,但从长远来看,生肖茅台仍然是仅次于飞天的收藏爆品。

 

一、生肖传统文化

 

作为国酒推出的艺术典藏生肖纪念酒,融合我国传统的酒文化和生肖文化,通过文化的积累和时间的沉淀,能够记载收藏者在这个时代的记忆和故事!

 

同时,茅台生肖酒做工优良,瓶体制作工艺高超,每种颜色富有美好的寓意,每瓶生肖酒的瓶体都有当代的美术、书法大师的大作,集收藏与观赏于一身。

 

二、限量生产

 

每年只出一款,定量生产,不可再生。试想在2025年一套十二生肖纪念酒全部面世后,如果有一套完整的十二生肖酒那价值应该是极高的。我国人凡事都讲究个第1,这一套纪念酒未来是什么价值我们现在是不可估量。

 

三、投资价值

 

有不少人问管家,这茅台生肖酒这么能涨,如果我要买,是买散瓶还是原箱比较好?不为别的,就用来增值的。

 

管家个人建议各位茅粉如果在条件允许的情况下,以整箱为好,因为整箱比散瓶更有优势,增值更大。

 

至少买多少合适,这个要根据每个人的实际情况,一般的个人收藏者不建议大量囤积,一到两箱即可。

 

生肖茅台是长期收藏才有意义,如果你想短期变现,没什么意义,只是买亏买赚的一个心理安慰罢了!

 

不久前,华为Mate30系列发布。凭借着徕卡电影四摄、全新环幕屏设计和强劲的综合实力一直吸引了不少消费者的青睐。但其实,还有不少用户却跟我一样在“持币观望”:因为我们还在等待着搭载着全球首款旗舰5GSoC的华为第二代5G手机——Mate30系列5G版。

很多人可能要问了,华为第二代5G手机与第一代5G手机有什么区别,它的优势在哪里?今天,我们拿到了Mate30系列5G版,针对你的所有疑惑一一解答。话不多说,下面我们通过一系列对比实测上干货:

5G版与4G版外观上有区别吗?

在外观部分,华为Mate30系列5G版与4G版本差距不大,不过Mate30 5G版特别独享两个素皮版——丹霞橙和青山黛。笔者手里就拿着亮黑玻璃材质的华为Mate30 Pro 5G和青山黛配色素皮材质的华为Mate30 5G。亮黑色的华为Mate30 Pro 5G整体外观与4G版本几乎一致,这里我们重点看一下素皮版。

 

华为Mate30 5G 青山黛

 

素皮版的整体机身背部材质由玻璃换成了植物纤维,整体观感上商务风要更重一些,同时也更加的低调内敛。手感上,素皮版达到了近乎真正皮革的质感,摸在手上非常舒服。而且因为材质的缘故,机身温度摸上去始终保持在一个很舒服的范畴,加上不沾指纹易清洁的特性,素皮版真的非常加分。

总体而言,玻璃版的华为Mate30系列5G版在设计上更加吸睛,但是素皮版的手感却要好了不少。到底怎么选就是见仁见智的事情了。如果你跟我一样陷入纠结,不妨去线下的门店亲自试试手感,瞧瞧外观。

 

 

华为Mate30 5G 素皮版

 

此外,Mate30系列5G版的外观设计极具辨识度。影像系统嵌入圆形镜片与光环之中,完美延续了Mate20系列“超跑之眼”设计,机身左侧的闪光灯以及熟悉的徕卡认证标志也都得到保留。

 

 

华为Mate30 5G 青山黛:可以看到连近乎真皮的纹理都做了出来

 

机身正面的设计也是同样,华为Mate30 Pro 5G的88度超曲面环幕屏让每一次亮屏都成为一种享受;而华为Mate30 5G四边等宽,而且比iPhone11 Pro更窄一些的边框则是它最大的视觉亮点。

 

 

华为Mate30 5G底部边框非常窄

 

在其它方面。华为Mate30 5G系列与4G版本基本没有差别,包括电源键、扬声器等的位置,机身的三围重量等等。这里就不做赘述,我们直接进入大家最关心的问题——何谓华为第二代5G手机。

灵魂提问:什么是第二代5G手机?

前面提到,不少人可能并不清楚什么才是第二代5G手机,这里先给大家解惑:

在麒麟990 5G发布之前,市面上几乎所有的5G终端几乎都是采用了SoC+外挂5G基带的设计,麒麟980+巴龙5000、骁龙855+X50都是如此。两个互相独立的芯片协同工作,基本上可以视为第一代5G手机的一大特点。

 

 

华为Mate20 X (5G)的麒麟980+巴龙5000

 

简单来说,因为第一代5G手机要塞入体积庞大的5G基带,手机必须进一步压缩机身空间。要么机身变大变厚,要么就要缩小电池容量让出空间。这还不算,独立的5G基带甚至还需要额外的ROM和IC等器件来配套,这也使得整体的功耗很大

相比之下,麒麟990 5G首次将5GModem集成到SoC芯片中,这样的方案毫无疑问要比外挂基带要强得多——不仅能够减少手机内部的空间占用,同时功耗、发热在理论上都可以大幅降低。

把5G基带集成进去之后,性能不会受到影响吗?

 

根据此前曝光的消息,华为麒麟990 5G是目前业内最小的5G手机芯片方案,面积更小,功耗更低。凭借着全新的7nm+EUV工艺,麒麟990 5G在保持几乎和麒麟980相似的面积下集成了103亿晶体管,因此带来的性能和能效提升是显而易见的。

另外,华为Mate30 Pro 5G搭载的麒麟990 5G芯片支持5G双模全网通和5G+4G双卡双待,能提供极为出众的5G连接。在通信频段和网络支持上,华为Mate30系列5G版不仅没缩水,还成为了目前覆盖最广的手机之一。

最重要的是,华为Mate30 Pro 5G支持SA/NSA5G双模组网,能够充分应对不同网络、不同5G组网方式下的通信需求,为用户带来全方位的畅快5G体验。

网络速度上,麒麟990 5G在Sub-6GHz频段下可实现领先的2.3Gbps峰值下载速率,上行峰值速率达1.25Gbps。

 

 

使用华为第二代5G手机上网是一种怎样的体验?

 

目前不少人纠结买不买5G手机,其实是在担心5G的信号覆盖问题。以北京为例,5G的覆盖率已经非常可观,下图是中国联通在北京的5G覆盖率,从满地图大片的绿色5G覆盖来看,北京的5G信号覆盖率已经很高。

 

 

中国联通北京5G覆盖情况

 

我们可以看到,现在5G信号的铺设进程比我们预计得要快上不少。按照这个节奏,明年估计主流城市的大部分地区铺满5G信号也不是不可能。

因此,我们这次测试5G不再选择那几个标志性的测速点,而是在中关村在线附近测试了一下华为Mate30 Pro 5G的性能。以期得到相对更为真实的5G体验。

同时,我们选择了一台外挂基带的其它5G手机和华为Mate30系列5G版进行对比,来看一下第二代5G手机,究竟升级了些什么。首先,选择一个位置站定,在同一个位置,打开两款手机进行5G信号测试,下图是两台手机的5G测试结果。

 

 

华为Mate30 Pro 5G对比其他5G手机

 

从上图来看,即使是最简单的网速对比,搭载麒麟990 5G的华为Mate30 Pro 5G的上传和下载速率比起其它5G手机来说要快上不少(注:目前5G没有正式商用,而5G网速测速结果会随当前5G网络环境有所波动)。

 

5G环境下华为Mate30 Pro 5G下载软件

另外,在5G网络环境下,华为Mate30 Pro 5G软件下载也堪称是飞速,一个1.5GB大小的游戏,在5G的高速网络下飞速下载完成,让人来不及反应。

 

 

《腾讯手游加速器》记录5G环境下游戏时延情况

 

在游戏中,我们也能体验到5G网络带来的畅快,通过《腾讯手游加速器》的记录,我们可以看到,《王者荣耀》的时延曲线,以及平均47ms的时延。这对于这样的时延下玩游戏,能够感受到技能施放无延迟的快乐,游戏体验也能大幅提升。

像那些“脑子反应过来了手反应过来了但是网络没反应过来”的神奇操作在5G环境下都不会发生,有了够快的5G网络,队友的问号少了,分段上去了,游戏体验瞬间飙升。

 

 

《王者荣耀》内游戏时延情况

 

华为Mate30 Pro 5G高效的5G网络除了在下载和游戏环节能够带来很好的体验外,还能实现视频的高速缓存。

 

 

5G环境下视频体验

 

可以看到,我们点开一个视频,视频缓冲过程非常快的就完成了,拖动进度条之后也能实现高速的视频缓冲,做到了“进度条拖到哪里视频播到哪里”的操作,妈妈再也不用担心我看视频网卡,砸手机了!

另外,还有不少人担心的5G发热问题,我们也进行了实测。这几天北京气温较低,笔者于冷风中在北邮校园里测试5G测试了一段时间后,考虑到手机可能发烫,于是准备暖暖手,但是,显然,华为Mate30 Pro 5G不给我这个机会。“发烫是不可能发烫的,这辈子都不可能发烫”。在冷风中哆嗦的笔者看着手里的华为Mate30 Pro 5G总觉得它在嘲讽我。

其实,5G的意义远不止网速更快这一点,5G技术可以说是划时代的进步。相信大家都意识到了,现在的媒体从曾经的文字到后来的图文再到现如今的视频已经在逐渐转变。在将来,视频和直播等将会成为主流,那更快的5G将会成为这一切实现的基础。

除了视频和直播环节外,未来的AR和VR的发展也是离不开5G的,华为本次第二代5G手机的推出也展现了华为现在的研发实力和对5G技术的自信。

不好意思,话扯远了,回到我们华为Mate30系列5G版上,在本次5G测试中我们看到了华为第二代5G手机的出色实力,但除了速度之外,这款第二代5G手机还有什么亮点?

听说5G版连性能都强了那么一点?是真的

在前面我们提到了华为Mate30系列与华为Mate30系列5G版在芯片上的不同,除了麒麟990 5G带来更出色的5G体验外,其它方面也有不同。

 

 

麒麟990 5G与麒麟990对比

 

我们可以看到麒麟990 5G是7nm+EUV工艺,麒麟990是新一代7nm工艺。5G版采用最新的紫外线光刻技术,芯片表面的晶体管密度更大,功耗更低。在大小核心方面,麒麟990 5G版使用了2个超大核,主频2.86GHz,与4G版的大核完全相同。而5G版的两个大核主频是2.36GHz,4G版则是2.09GHz。5G版四个小核的主频是1.96GHz,4G版的小核主频是1.86GHz。考虑到中小核心在日常使用中的频率更高,因此5G版的性能可能还要稍强一些。

另外华为Mate30系列5G版还比4G版本多了一个NPU大核心,这里带来的运算提升相对CPU和GPU就要明显的多了。在需要AI运算能力展现实力的时候,相信麒麟990 5G会带来更好的体验。

听说5G手机都很烫,耗电还特别快?华为Mate30系列5G版真不是

前面在测试5G的环节曾提到华为Mate30系列5G版的散热能力,当然,那是以笔者的主观感受为主,为了确保测试更加客观准确,这里我们使用专业的仪器进行测试。

测试选择使用5G网络进行半小时的“吃鸡”:在经历了在5G网络下进行了半小时游戏“摧残”之后,我们来看下现在华为Mate30Pro 5G的散热情况。

 

 

半小时游戏游戏测试后机身温度

 

从上图我们可以看到,半个小时的游戏体验之后,华为Mate30 Pro 5G的正面平均温度为36.0℃,背面平均温度为31.7℃。就这个温度,在非5G手机中的表现也可以评上优秀级别,今后还有谁说5G手机发热严重,大家可以直接给他看这个图。打脸这种事情不太文明,所以我们都是摆事实说话。

5G手机耗电快?我猜你没用过华为Mate30系列5G版

很多人不买5G手机,其实都是在担心一个问题——续航!对于这个问题,华为Mate30系列5G版也是早有准备。比如华为Mate30 Pro 5G配备了4500mAh电池,超大电池容量,让华为Mate30Pro 5G的续航能够更持久。

前面提到华为Mate30 Pro 5G在冷风中进行了5G测试,测试期间在开启了5G的情况下,进行了一个小时的游戏+安装包下载+视频播放,电量也只下降了15%,从测试结果来看,大家担心的5G手机高功耗其实是多余的。

除了5G部分的续航外,我们还通过3小时续航测试,来看一下华为Mate30 Pro 5G的续航能力。其中,三个小时的续航测试包括30分钟5G网络下视频播放,30分钟5G网络下游戏,30分钟照片和视频拍摄,30分钟社交软件,30分钟音乐播放和30分钟电子书组成。

 

 

通过3小时的续航测试来看,华为Mate30 Pro 5G延续了华为Mate30系列的优秀基因,在续航这块表现依旧优秀。3小时的续航测试下,华为Mate30 Pro 5G还剩下了69%的电,担心5G手机续航的用户现在可以安心了。

从测试结果来看,对于担心5G耗电快的用户可以放心,华为Mate30 Pro 5G从电池容量到5G SoC优化双管齐下,为用户解决了5G手机的续航问题。

拍照什么的,DxOMark榜一还不够说明实力吗?

既然是华为Mate系列,那还是绕不开它的拍照。作为一款刚发布就强势屠榜的手机,华为Mate30Pro 5G自然也丝毫没有让我们失望。

 

 

华为Mate30 5G后置徕卡三摄

 

先来回顾一下两款手机在拍照方面的基本参数:Mate30 Pro 5G采用了徕卡电影四摄,其中一颗等效焦距约为18mm的4000万电影主摄,一颗等效26mm左右的超感光4000万主摄搭配一个3倍光学变焦的800万主摄和一颗3D深感摄像头。四颗摄像头协同工作,带来覆盖全场景的拍照体验。

下面我们来看一下它的样张:

 

 

 

从样张看,超高的解析度和色彩表现让人印象深刻,同时前实后虚的纵深感表现的很强烈。无论是日常还是夜拍,华为Mate30系列5G版的成片都非常出色。

除了拍照能力出众,华为Mate30 Pro 5G的视频拍摄能力也是令人称道,其中,最亮眼的当然还是华为Mate30 Pro 5G的慢动作视频拍摄了。华为Mate30 Pro 5G支持最高7680帧超高速摄影,每秒定格7680个瞬间。我们来看一下下图的拍摄:

 

 

慢动作拍摄

 

在华为Mate30 Pro 5G的摄像头下,高速旋转的指尖陀螺的旋转也能被清晰拍下,每个齿轮都清晰可见,一个高速变化的世界就在我们眼前展现了。

 

 

延时拍摄

 

此外,华为Mate30 Pro 5G还支持4K超广角暗态延时摄影,将时间的流淌用镜头记录下来,充满了一种时间流逝的美感。我们可以看到,华为Mate30 Pro 5G的镜头下,时间的快慢已经不重要了,岁月的节奏已经被徕卡电影四摄掌控,使用华为Mate30 Pro 5G,有一种时间就在指尖的感觉。

在影像部分,华为Mate30 Pro 5G的实力是毋庸置疑。DxOMark的榜单第一的位置,目前看来华为Mate30 Pro还是坐得很稳的。

系统为5G做的准备

出了硬件方面的全面提升外,华为还为Mate30系列5G版的系统进行了升级,比如5G开关的加入。

 

 

5G网速快固然是一件好事,但是一个不留神把一个月的流量用几分钟刷干可就不那么令人愉快了。同时,基于信号覆盖、功耗、网络环境等的影响,我们难免也会有需要关闭5G网络的时候。而华为Mate30系列5G版就在下滑菜单中默认加入了5G开关的快捷键。虽然这并不是一个什么大功能,却实实在在解决了5G初期的一个痛点。

EMUI10还有其他像隔空操控、深色模式等亮点功能,篇幅所限这里就不做过多说明,感兴趣的朋友,可以去看一下我们之前对华为Mate30系列所做的评测。

现在适合买5G手机么?

正如我们之前所说的一样,5G网络的铺设速度可能已经超过了很多人的预料——从北京的覆盖情况来看,这一次运营商所下的决心是空前的。在这里我们不妨断言,一年之内,大部分一线城市的5G网络将会普及到基本可用的程度,而下放到二三线城市的时间可能也用不了两年。

所以除非你的换机周期只有一年或者更短,否则现在选择一部5G手机很可能会让你比其他人更快一步体验到5G网络的畅爽。

当然,除了此前5G网络的不完善之外,5G终端的“半成品感”也是让用户们兴趣缺缺的原因之一:外挂基带耗电极高,除了5G之外缺乏亮点,又大又重的机身酷似“原型机”……如此种种不一而足。

但相信华为Mate30系列5G版能打破不少人对5G手机的顾虑:强大的5G体验,优秀的综合性能,不错的功耗控制和完整的生态服务。此前曾有人戏言“等到什么时候5G手机身上没有5G标志的时候,就是合适入手5G手机的日子。”其实也就是暗示当5G不再只是一个噱头,而是一部手机正常的功能之时,可能已经是可以购买的节点了。话虽然武断了点,仔细想想似乎还挺有几分道理。

但显然,华为Mate30系列5G版就是那种“不需要5G标志”的5G手机。作为华为第二代5G手机,华为Mate30系列5G版不仅极有可能成为接下来的首款5G爆品,也是华为在5G领域的一款具有里程碑意义的产品。甚至我相信,在5G终端发展史上,它也一定将会留下一个非常醒目的印记。

 

1.移动平均(英语:Moving Average,MA),又称“移动平均线”简称均线,是技术分析中一种分析时间序列数据的工具。最常见的是利用股价、回报或交易量等变量计算出移动平均。移动平均可抚平短期波动,反映出长期趋势或周期。

概念


  • MA:移动平均,分为简单移动平均 SMA, 和加权移动平均 WMA;

移动平均(英语:Moving Average,MA),又称“移动平均线”简称均线,是技术分析中一种分析时间序列数据的工具。最常见的是利用股价、回报或交易量等变量计算出移动平均。移动平均可抚平短期波动,反映出长期趋势或周期。原本的意思是移动平均,由于我们将其制作成线形,所以一般称之为移动平均线,简称均线。它是将某一段时间的收盘价之和除以该周期。比如日线MA5指5天内的收盘价除以5。 移动平均线常用线有5天、10天、30天、60天、120天和240天的指标。其中,5天和10天的短期移动平均线。是短线操作的参照指标,称做日均线指标;30天和60天的是中期均线指标,称做季均线指标;120天、240天的是长期均线指标,称做年均线指标。对移动平均线的考查一般从几个方面进行。 移动平均线按时间周期长短分为:短期移动平均线,中期移动平均线,长期移动平均线;按计算方法分为:算术移动平均线,加权移动平均线,指数平滑移动平均线(EMA)。

  • MACD:由MA演化而来;

指数平滑异同移动平均线(Moving Average Convergence / Divergence,MACD)是股票交易中一种常见的技术分析工具,由Gerald Appel于1970年代提出,用于研判股票价格变化的强度、方向、能量,以及趋势周期,以便把握股票买进和卖出的时机。MACD指标由一组曲线与图形组成,通过收盘时股价或指数的快变及慢变的指数移动平均值(EMA)之间的差计算出来。“快”指更短时段的EMA,而“慢”则指较长时段的EMA,最常用的是12及26日EMA。 MACD称为指数平滑异同平均线,是从双指数移动平均线发展而来的,由快的指数移动平均线(EMA)减去慢的指数移动平均线,MACD的意义和双移动平均线基本相同,但阅读起来更方便。当MACD从负数转向正数,是买的信号。当MACD从正数转向负数,是卖的信号。当MACD以大角度变化,表示快的移动平均线和慢的移动平均线的差距非常迅速的拉开,代表了一个市场大趋势的转变。

  • 特点

1)追踪趋势。 追踪趋势。注意价格的趋势,并追随这个趋势,不轻易放弃。如果从股价的图表中能够找出上升或下降趋势线,那么,MA的曲线将保持与趋势线方向一致,能消除中间股价在这个过程中出现的起伏。原始数据的股价图表不具备这个保持追踪趋势的特性。 2)稳定性。 通常愈长期的移动平均线,愈能表现安定的特性,即移动平均线不轻易往上往下,必须股价涨势真正明朗了,移动平均线才会往上延伸,而且经常股价开始回落之初,移动平均线却是向上的,等到股价下滑显著时,才见移动平均线走下坡,这是移动平均线最大的特色。愈短期的移动平均线,安定性愈差,愈长期移动平均线,安定性愈强,但也因此使得移动平均线有延迟反应的特性。 3)滞后性。 在股价原有趋势发生反转时,由于MA的追踪趋势的特性,MA的行动往往过于迟缓,调头速度落后于大趋势。这是MA的一个极大的弱点。等MA发出反转信号时,股价调头的深度已经很大了。 4)助涨助跌性。 助涨助跌性。当股价突破了MA时,无论是向上突破还是向下突破,股价有继续向突破方面再走一程的愿望,这就是MA的助涨助跌性。 5)依靠性。 6)支撑线和压力线的特性

  • 特征分析

1.多头稳定上升 当多头市场进入稳定上升时期,10MA、21MA、68MA向右上方推升,且三线多头排列(排列顺序自上而下分别为10MA、21MA、68MA)、略呈平行状。 2.技术回档 当10MA由上升趋势向右下方拐头而下,而21MA仍然向上方推升时,揭示此波段为多头市场中的技术回档,涨势并未结束。 3.由空转多 股市由空头市场进入多头市场时,10MA首先由上而下穿越K线图(注意是K线图),处于k线图的下方(即股价站在10MA之上),过几天21MA、68MA相继顺次,由上往下穿越K线图(既股价顺次站在21MA、68MA之上)。 4.股价盘整 股价盘整时10MA与21MA交错在一起,若时间拉长68MA也会粘合在一起。 5.盘高与盘低 股价处于盘局时若10MA往右上方先行突破上升,则后市必然盘高;若10MA往右下方下降时,则后市必然越盘越低。 6.空头进入尾声 空头市场中,若68MA能随10MA于21MA之后,由上而下贯穿K线图(既股价站在68MA之上),则后市会有一波强劲的反弹,甚至空头市场至此已接近尾声。 7.由多转空 若21MA随10MA向右下方拐头而下,68MA也开始向右下方反转时,表示多头市场既将结束,空头市场既将来临。 8.跌破10MA 当市场由多头市场转入空头市场时,10MA首先由下往上穿越K线图,到达K线图的上方(股价跌破10MA),过几天30MA、68MA相继顺次由下往上穿越K线图,到达K线图的上方。 9.移动平均线依次排列 空头市场移动平均线均在K线图之上,且排列顺序从上而下依次是68MA、21MA、10MA。 10.反弹开始 空头市场中,若移动10MA首先从上而下穿越K线图时(K线图在上方,10MA在下方)既股价站在10MA之上,是股价在空头市场反弹的先兆。 11.反弹趋势增强 空头市场中,若21MA也继10MA之后,由上而下穿越K线图,且10MA位于21MA之上(既股价站在21MA之上,10MA、21MA多头排列),则反弹趋势将转强。 12.深幅回档 若21MA随10MA向右下方拐头而下,68MA仍然向右上方推升时,揭示此波段为多头市场中的深幅回档。应以持币观望或放空的策略对应。

  • 格兰维尔法则

1、移动平均线从下降逐渐走平且略向上方抬头,而股价从移动平均线下方向上方突破,为买进信号。 2、股价位于移动平均线之上运行,回档时未跌破移动平均线后又再度上升时为买进时机。 3、股价位于移动平均线之上运行,回档时跌破移动平均线,但短期移动平均线继续呈上升趋势,此时为买进时机。 4、股价位于移动平均线以下运行,突然暴跌,距离移动平均线太远,极有可能向移动平均线靠近(物极必反,下跌反弹),此时为买进时机。 5、股价位于移动平均线之上运行,连续数日大涨,离移动平均线愈来愈远,说明内购买股票者获利丰厚,随时都会产生获利回吐的卖压,应暂时卖出持股。 6、移动平均线从上升逐渐走平,而股价从移动平均线上方向下跌破移动平均线时说明卖压渐重,应卖出所持股票。 7、股价位于移动平均线下方运行,反弹时未突破移动平均线,且移动平均线跌势减缓,趋于水平后又出现下跌趋势,此时为卖出时机。 8、股价反弹后在移动平均线上方徘徊,而移动平均线却继续下跌,宜卖出所持股票。

算法

import pandas as pd DataAPI.settings.cache_enabled = True

def cal_ema(data, shortNumber, longNumber): """计算N日EMA指数平均值,注意处理停盘情况 EMA[i] = EMA[i-1] * (long - 1)/(long + 1) + closePrice * 2 / (long + 1) """ ema_short = [data['closePrice'][0]] * len(data) ema_long = [data['closePrice'][0]] * len(data) for i in range(1, len(data)): if data['turnoverVol'][i] == 0: ema_short[i] = ema_short[i-1] ema_long[i] = ema_long[i-1] else: ema_short[i] = ema_short[i-1] * (shortNumber-1)/(shortNumber+1) + data['closePrice'][i] * 2/(shortNumber+1) ema_long[i] = ema_long[i-1] * (longNumber-1)/(longNumber+1) + data['closePrice'][i] * 2/(longNumber+1) data['EMA_' + str(shortNumber)] = ema_short data['EMA_' + str(longNumber)] = ema_long return data def cal_diff(ticker, listDate, shortNumber=12, longNumber=26): """计算DIFF偏移值 DIFF = EMA(short) - EMA(long) """ data = DataAPI.MktEqudAdjGet(ticker=ticker, beginDate=listDate.replace('-',''), field=["secShortName", "tradeDate", "closePrice", "turnoverVol"]) ema_data = cal_ema(data, shortNumber, longNumber) ema_data['DIFF'] = ema_data["EMA_" + str(shortNumber)] - ema_data["EMA_" + str(longNumber)] return ema_data def cal_dea(data, n=9): """计算DEA差离平均值 DEA[i] = DEA[i-1] * (n-1) / (n+1) + DIFF[i] * 2 / (n+1) """ dea = [data['DIFF'][0]] * len(data) for i in range(1, len(data)): if data['turnoverVol'][i] == 0: dea[i] = dea[i-1] else: dea[i] = dea[i-1] * (n-1)/(n+1) + data['DIFF'][i] * 2/(n+1) data['DEA'] = dea return data def cal_macd(ticker, listDate=None): """计算MACD指数平滑平均值 MACD = 2 * (DIFF - DEA) """ if not listDate: listDate = DataAPI.SecIDGet(assetClass='E', ticker=ticker, field=['listDate']).listDate[0] diff_data = cal_diff(ticker, listDate) macd_data = cal_dea(diff_data) macd_data['MACD'] = 2 * (macd_data['DIFF'] - macd_data['DEA']) return macd_data

测试

df = cal_macd('000002') df.tail()

 

df_open = df[df.turnoverVol != 0] df_open.tail()

作图

from lib import report df_open_tail = df_open.tail(100) # report library: https://uqer.datayes.com/community/share/56a5b221228e5b2047d916d9 ax = df_open_tail.plot(x=['tradeDate'], y=['closePrice'], figsize=(20, 6)) report.fig_style(ax, ['closePrice']) ax = df_open_tail.plot(x=['tradeDate'], y=['MACD'], kind='bar', figsize=(20, 6), color='r') df_open_tail['tmp'] = [min(i, 0) for i in df_open_tail.MACD] df_open_tail.plot(y=['tmp'], kind='bar', color='g', ax=ax) report.fig_style(ax, ['MACD'])

 

2.ADX,全称Average Directional Index,是一种用于衡量资产行情是否存在趋势的技术指标

ADX指标

  • ADX,全称Average Directional Index,是一种用于衡量资产行情是否存在趋势的技术指标
  • ADX的计算方法十分复杂,涉及到+DM, -DM, TR, +DI, -DI等很多中间变量:
    • 首先计算+DM和-DM,即所谓上升动向与下降动向,公式为
    •  
    • +DMt=t日最高价 - (t-1)日最高价−DMt=(t-1)日最低价 - t日最低价
    • 其次我们来计算TR,即所谓的真实波幅:TRt=max(|t日最高−t日最低|,|t日最高−(t−1)日收盘|,|t日最低−(t−1)日收盘|)
    • 然后利用+DM,-DM,TR来计算DI:±DMt=∑N−1k=0±DMt−k/∑N−1k=0TRt−k×100,这里的N是参数,一般取14
    • 然后我们就可以计算ADX和ADXR了:
    •  
    • ADXt=MA((+DIt)−(−DIt)(+DIt)+(−DIt)×100,N)
  • 回测结果,首先使用动态Universe消除survival bias,ADX可以在几乎为1的beta下带来一定的alpha,但是绝对水平较低,需要和其他因子配合使用

import numpy as np import pandas as pd start = '20120101' end = '20160901' benchmark = 'HS300' universe = DynamicUniverse('HS300') capital_base = 1000000 freq = 'd' refresh_rate = 5 adx_threshold = 40 # 判断是否存在趋势的阈值 rtn_threshold = 0.05 # 判断累计收益的变化阈值 # 定义并注册Signal模块 def initialize(account): account.signal_generator = SignalGenerator(Signal('ADX')) def handle_data(account): history = account.get_attribute_history('closePrice', 14) # 由于计算ADX用的周期是14 returns = {sec: history[sec][-1] / history[sec][0] - 1 for sec in account.universe} avgret = sum(returns.values()) / len(returns) up_down = {sec: returns[sec] > 0 for sec in account.universe} up_percent = 1. * sum(up_down.values()) / len(up_down) buylist = [] cash = account.cash for sec in account.universe: if account.signal_result['ADX'].get(sec, 0) > adx_threshold and returns[sec] > rtn_threshold and sec not in account.security_position: # 存在上涨趋势 buylist.append(sec) elif account.signal_result['ADX'].get(sec, 0) > adx_threshold and returns[sec] < -rtn_threshold and sec in account.security_position: # 存在下跌趋势 order_to(sec, 0) # 全部卖出 cash += account.security_position[sec] * account.reference_price[sec] # 估计买入金额 for sec in buylist: order(sec, cash / len(buylist) / account.reference_price[sec])

 

 

3.CMO(Chande Momentum Oscillator)动量震荡指标是由Tushar S. Chande提出的类似于RSI的指标

指标介绍

  • **CMO(Chande Momentum Oscillator)**动量震荡指标是由Tushar S. Chande提出的类似于RSI的指标
  • CMOn是一个n天滚动指标,在这n天中的第i天计算每天的 收盘价 - 前收盘价 ,如果为正则赋给upi(dni为0),为负则将绝对值赋给dni(upi为0)
  • 其计算公式为:

 

CMOn=∑ni=1upi−∑ni=1dni∑ni=1upi+∑ni=1dni∗100

 

策略思路

  • 计算上证50成分股当中所有股票过去n天的CMO
  • CMO大于0时买入,小于0时卖出
  • 根据一定的调仓原则进行调仓,细节见代码

可进一步挖掘的点

  • 考虑CMO的形态,如上/下穿0线作为买卖信号
  • 扩大股票池范围,观察CMO与股票池的关系,比如区分大小盘股观察CMO的有效性
  • 股票权重的分配方式
  • 其他调仓原则

import numpy as np start = '2010-01-01' end = '2015-06-20' benchmark = 'SH50' universe = set_universe('SH50') capital_base = 1000000 window = 35 # 参数,CMO指标计算周期 def initialize(account): pass def handle_data(account): clp = account.get_attribute_history('closePrice', window) prc = account.get_attribute_history('preClosePrice', window) p = account.referencePrice # 计算CMO CMO = {} for s in account.universe: diff = clp[s] - prc[s] u = sum(n for n in diff if n > 0) d = sum(-n for n in diff if n < 0) if u + d == 0: continue CMO[s] = (u - d) / (u + d) * 100 # 根据CMO卖出目前持有股票 v = account.cash for s,a in account.valid_secpos.items(): if CMO.get(s, 0) < 0 and s in account.universe: order_to(s, 0) v += a * p[s] # 根据CMO确定买入列表 buylist = [] for s in account.universe: if CMO.get(s, 0) > 0 and not np.isnan(p[s]) and s not in account.valid_secpos: buylist.append(s) # 根据买入列表和可用现金买入股票 if v > account.referencePortfolioValue * 0.33: # 为了避免调仓过于频繁,仅当可用现金超过账户市值1/3时买入 for s in buylist: order(s, v / len(buylist) / p[s])

 

#####上面的策略实现了策略思路所表述的意思,使用了非常简单的调仓原则,其表现还不错

#####但是,其中的关键参数 window 为什么设置为 35 呢?

#####这当然不是拍脑袋拍出来的,而是通过参数调试出来的:

start = '2010-01-01' end = '2015-06-20' benchmark = 'SH50' universe = set_universe('SH50') capital_base = 1000000. sim_params = quartz.sim_condition.env.SimulationParameters(start, end, benchmark, universe, capital_base) idxmap_all, data_all = quartz.sim_condition.data_generator.get_daily_data(sim_params)

import numpy as np def initialize(account): pass def handle_data(account): clp = account.get_attribute_history('closePrice', window) prc = account.get_attribute_history('preClosePrice', window) p = account.referencePrice CMO = {} for s in account.universe: diff = clp[s] - prc[s] u = sum(n for n in diff if n > 0) d = sum(-n for n in diff if n < 0) if u + d == 0: continue CMO[s] = (u - d) / (u + d) * 100 buylist = [] for s in account.universe: if CMO.get(s, 0) > 0 and not np.isnan(p[s]) and s not in account.valid_secpos: buylist.append(s) v = account.cash for s,a in account.valid_secpos.items(): if CMO.get(s, 0) < 0 and s in account.universe: order_to(s, 0) v += a * p[s] if v > account.referencePortfolioValue * 0.33: for s in buylist: order(s, v / len(buylist) / p[s]) print 'window annualized_return sharpe max_drawdown' for window in range(10, 51, 5): strategy = quartz.sim_condition.strategy.TradingStrategy(initialize, handle_data) bt_test, acct = quartz.quick_backtest(sim_params, strategy, idxmap_all, data_all) perf = quartz.perf_parse(bt_test, acct) print ' {0:2d} {1:>7.4f} {2:>7.4f} {3:>7.4f}'.format(window, perf['annualized_return'], perf['sharpe'], perf['max_drawdown'])

 

从上面的调试结果中可以看到,当 window = 35 时,夏普和最大回撤相对而言最好,因此有了最上面的那个策略

然而调试完了window,这个策略就没有优化空间了吗?不,我们还可以根据这个策略的表现来分析一下这个策略的缺陷在哪里,并加以改进

因为该策略的调仓原则是买入所有产生的信号,并没有对持仓进行限制,这会造成两个方面的影响:

  1. 仓位中的股票可能会有很多只,这样资金会比较分散,削弱信号的效果
  2. 如果买入信号比较少,而卖出信号比较多的话,现金的利用率会比较低

那么到底是否存在上述问题呢?我们可以通过最大持仓数量和现金走势来加以判断

x = map(len, bt['security_position'].tolist()) max(x)

42

bt.cash.plot()

 

从上面的两个cell中可以看出这两个问题还是比较明显的。为了解决这两个问题,我们对策略进行优化:一是限制最大持仓位10只股票,二是每次卖出的现金都平均分配给目前仓位中的股票和即将买入的股票

import numpy as np from heapq import nlargest start = '2010-01-01' end = '2015-06-20' benchmark = 'SH50' universe = set_universe('SH50') capital_base = 1000000 max_n = 10 # 参数,最大持仓数量 window = 15 # 参数,CMO指标计算周期 def initialize(account): pass def handle_data(account): clp = account.get_attribute_history('closePrice', window) prc = account.get_attribute_history('preClosePrice', window) p = account.referencePrice # 计算CMO CMO = {} for s in account.universe: diff = clp[s] - prc[s] u = sum(n for n in diff if n > 0) d = sum(-n for n in diff if n < 0) if u + d == 0: continue CMO[s] = (u - d) / (u + d) * 100 # 根据CMO卖出目前持有股票 n = len(account.valid_secpos) sellist = [] for s,a in account.valid_secpos.items(): if CMO.get(s, 0) < 0 and s in account.universe: order_to(s, 0) n -= 1 sellist.append(s) if n >= max_n: # 如果超过最大持仓,则不买入 return # 根据CMO确定买入列表 buylist = [] for s in account.universe: if CMO.get(s, 0) > 0 and not np.isnan(p[s]) and s not in account.valid_secpos: buylist.append(s) # 根据最大持仓数量确定买入列表数量,按CMO排序选较大的部分 if len(buylist) + n > max_n: buylist = nlargest(max_n - n, buylist, key=CMO.get) # 将资金重新分配到新买入的与已持有的股票中 buylist += [s for s in account.valid_secpos if s not in sellist] amount = {} for s in buylist: amount[s] = account.referencePortfolioValue / len(buylist) / p[s] - account.valid_secpos.get(s, 0) # 根据应调数量买卖股票,先卖出后买入 for s in sorted(amount, key=amount.get): order(s, amount[s])

import numpy as np from heapq import nlargest max_n = 10 # 参数,最大持仓数量 def initialize(account): pass def handle_data(account): clp = account.get_attribute_history('closePrice', window) prc = account.get_attribute_history('preClosePrice', window) p = account.referencePrice # 计算CMO CMO = {} for s in account.universe: diff = clp[s] - prc[s] u = sum(n for n in diff if n > 0) d = sum(-n for n in diff if n < 0) if u + d == 0: continue CMO[s] = (u - d) / (u + d) * 100 # 根据CMO卖出目前持有股票 n = len(account.valid_secpos) sellist = [] for s,a in account.valid_secpos.items(): if CMO.get(s, 0) < 0 and s in account.universe: order_to(s, 0) n -= 1 sellist.append(s) if n >= max_n: # 如果超过最大持仓,则不买入 return # 根据CMO确定买入列表 buylist = [] for s in account.universe: if CMO.get(s, 0) > 0 and not np.isnan(p[s]) and s not in account.valid_secpos: buylist.append(s) # 根据最大持仓数量确定买入列表数量,按CMO排序选较大的部分 if len(buylist) + n > max_n: buylist = nlargest(max_n - n, buylist, key=CMO.get) # 将资金重新分配到新买入的与已持有的股票中 buylist += [s for s in account.valid_secpos if s not in sellist] amount = {} for s in buylist: amount[s] = account.referencePortfolioValue / len(buylist) / p[s] - account.valid_secpos.get(s, 0) # 根据应调数量买卖股票,先卖出后买入 for s in sorted(amount, key=amount.get): order(s, amount[s]) print 'window annualized_return sharpe max_drawdown' for window in range(10, 51, 5): strategy = quartz.sim_condition.strategy.TradingStrategy(initialize, handle_data) bt_test, acct = quartz.quick_backtest(sim_params, strategy, idxmap_all, data_all) perf = quartz.perf_parse(bt_test, acct) print ' {0:2d} {1:>7.4f} {2:>7.4f} {3:>7.4f}'.format(window, perf['annualized_return'], perf['sharpe'], perf['max_drawdown'])

 

从上面的图表可以看出其表现相比最初的策略有了不少改善。其中 window = 15 是最适合目前调仓原则的参数。

但是这些优化的初衷是为了解决股票数量和资金利用率的问题,我们仍然通过最大持仓数量和现金走势来判断

x = map(len, bt['security_position'].tolist()) max(x)

10

bt.cash.plot()

 

以上是对CMO这个技术指标进行的一些简单的回测,并且针对策略本身的特点进行了一定的优化。在最前面列出了一些可挖掘的点,如果想进行更深入的研究还是有很多东西可以做的。

 

4.简易波动指标(EMV),是为数不多的考虑价量关系的技术指标。它刻画了股价在下跌的过程当中,由于买气不断的萎靡退缩,致使成交量逐渐的减少,EMV 数值也因而尾随下降,直到股价下跌至某一个合理支撑区,捡便宜货的买单促使成交量再度活跃,EMV 数值于是作相对反应向上攀升,当EMV 数值由负值向上趋近于零时,表示部分信心坚定的资金,成功的扭转了股价的跌势,行情不断反转上扬,并且形成另一次的买进讯号。

简易波动指标(EMV),是为数不多的考虑价量关系的技术指标。它刻画了股价在下跌的过程当中,由于买气不断的萎靡退缩,致使成交量逐渐的减少,EMV 数值也因而尾随下降,直到股价下跌至某一个合理支撑区,捡便宜货的买单促使成交量再度活跃,EMV 数值于是作相对反应向上攀升,当EMV 数值由负值向上趋近于零时,表示部分信心坚定的资金,成功的扭转了股价的跌势,行情不断反转上扬,并且形成另一次的买进讯号。


计算方法:

第一步

MID=TH+TL2 −YH+YL2

这里TH 为当天最高价,TL 为当天最低价,YH 为前日最高价,YL 为前日最低价。MID > 0意味着今天的平均价高于昨天的平均价。

 

第二步

BRO=VOLH−L

其中VOL代表交易量,H、L代表同一天的最高价与最低价

 

第三步

EM=MIDBRO

第四步

 

EMV = EM的N日简单移动平均

第五步

MAEMV = EMV的M日简单移动平均

def emv(stk_list,current_date,N=14): cal = Calendar('China.SSE') period = '-' + str(N+1) + 'B' begin_date = cal.advanceDate(current_date,period,BizDayConvention.Unadjusted) end_date = cal.advanceDate(current_date,'-1B',BizDayConvention.Unadjusted) eq_emv = {} eq_mid = {} eq_bro = {} eq_Market = DataAPI.MktEqudAdjGet(secID=stk_list,beginDate=begin_date.strftime('%Y%m%d'),endDate=end_date.strftime('%Y%m%d'),field=['secID','highestPrice','lowestPrice','turnoverVol'],pandas="1") avaiable_list = eq_Market['secID'].drop_duplicates().tolist() eq_Market.set_index('secID',inplace=True) for stk in avaiable_list: if len(eq_Market.ix[stk]) == (N+1): eq_mid[stk] = (np.array(eq_Market.ix[stk]['highestPrice'][1:] + eq_Market.ix[stk]['lowestPrice'][1:]) - np.array(eq_Market.ix[stk]['highestPrice'][:-1] + eq_Market.ix[stk]['lowestPrice'][:-1]))/2 eq_bro[stk] = np.array(eq_Market.ix[stk]['turnoverVol'][1:])/np.array(eq_Market.ix[stk]['highestPrice'][1:] + eq_Market.ix[stk]['lowestPrice'][1:]) eq_emv[stk] = np.mean(eq_mid[stk]/eq_bro[stk]) return eq_emv

def maemv(stk_list,current_date,N=14): cal = Calendar('China.SSE') period = '-' + str(N+1) + 'B' end_date = cal.advanceDate(current_date,'-1B',BizDayConvention.Unadjusted) start_date = cal.advanceDate(current_date,period,BizDayConvention.Unadjusted) timeSeries = cal.bizDatesList(start_date, end_date) eq_maemv = {} #初始化eq_maemv字典 eq_emv = emv(stk_list,end_date,N) for stk in eq_emv: eq_maemv[stk] = 0 #仅调用N次emv函数 for i in xrange(len(timeSeries)): eq_emv = emv(stk_list,timeSeries[i],N) for stk in eq_emv: eq_maemv[stk] = eq_maemv[stk] + eq_emv[stk] for stk in eq_maemv: eq_maemv[stk] = eq_maemv[stk]/N return eq_maemv

EMV指标基本用法


EMV 在0 以下表示弱势,在0 以上表示强势;EMV 由负转正应买进,由正转负应卖出。

import numpy as np import pandas as pd from CAL.PyCAL import * start = '2012-08-01' # 回测起始时间 end = '2015-08-01' # 回测结束时间 benchmark = 'HS300' # 策略参考标准 universe = set_universe('HS300') # 证券池,支持股票和基金 capital_base = 1000000 # 起始资金 freq = 'd' # 策略类型,'d'表示日间策略使用日线回测,'m'表示日内策略使用分钟线回测 refresh_rate = 10 # 调仓频率,表示执行handle_data的时间间隔,若freq = 'd'时间间隔的单位为交易日,若freq = 'm'时间间隔为分钟 cal = Calendar('China.SSE') def initialize(account): # 初始化虚拟账户状态 pass def handle_data(account): # 每个交易日的买入卖出指令 eq_emv = emv(account.universe,account.current_date,N=14) buylist = [] for stk in eq_emv: if eq_emv[stk] > 0: buylist.append(stk) for stk in account.valid_secpos: if stk not in eq_emv or eq_emv[stk] <= 0: order_to(stk,0) else: if stk not in buylist[:]: buylist.append(stk) for stk in buylist: order_to(stk,account.referencePortfolioValue/account.referencePrice[stk]/len(buylist))

 

EMV结合MAEMV使用


EMV 上穿MAEMV 则买入,EMV 下穿MAEMV 则卖出。

import numpy as np import pandas as pd from CAL.PyCAL import * start = '2012-08-01' # 回测起始时间 end = '2015-08-01' # 回测结束时间 benchmark = 'HS300' # 策略参考标准 universe = set_universe('HS300') # 证券池,支持股票和基金 capital_base = 1000000 # 起始资金 freq = 'd' # 策略类型,'d'表示日间策略使用日线回测,'m'表示日内策略使用分钟线回测 refresh_rate = 10 # 调仓频率,表示执行handle_data的时间间隔,若freq = 'd'时间间隔的单位为交易日,若freq = 'm'时间间隔为分钟 cal = Calendar('China.SSE') def initialize(account): # 初始化虚拟账户状态 pass def handle_data(account): # 每个交易日的买入卖出指令 eq_emv = emv(account.universe,account.current_date,14) eq_maemv = maemv(account.universe,account.current_date,14) buylist = [] for stk in eq_emv: try: if eq_emv[stk] > eq_maemv[stk]: buylist.append(stk) except: pass for stk in account.valid_secpos: if stk not in eq_emv or stk not in eq_maemv or eq_emv[stk] <= eq_maemv[stk]: order_to(stk,0) else: if stk not in buylist[:]: buylist.append(stk) for stk in buylist: order_to(stk,account.referencePortfolioValue/account.referencePrice[stk]/len(buylist))

 

 

5.KDJ是以最高价、最低价及收盘价为基本数据进行计算,得出的K值、D值和J值分别在指标的坐标上形成的一个点,连接无数个这样的点位,就形成一个完整的、能反映价格波动趋势的KDJ指标。它主要是利用价格波动的真实波幅来反映价格走势的强弱和超买超卖现象,在价格尚未上升或下降之前发出买卖信号的一种技术工具。

其实就是KDJ的取值情况,收益较MACD差点,作为摆动指标,在趋势明显的状态下表现较差,较MACD趋势指标可以比较容易看出来。

#######常用函数 def Date_type_preceding(date,period): dt = Date.fromDateTime(date) #从python标准库datetime类型转化为cal的date类型 cal = Calendar('China.SSE') #上海证券交易所交易日历 lastDay = cal.advanceDate(dt,period,BizDayConvention.Preceding) #在cal日历下当前日期的前一工作日 day = lastDay.strftime('%Y%m%d') return day

#######state函数 def Get_KDJ_state(date,universe): Factor = DataAPI.MktStockFactorsOneDayGet(tradeDate=date,secID=universe,field=['secID','KDJ_K','KDJ_D'],pandas="1") Factor.set_index('secID',inplace=True)#转换index为ticker Factor = Factor.dropna() KDJ_temp_on = Factor[Factor.KDJ_K>Factor.KDJ_D]#取K在D上的股票 KDJ_temp_on['KDJ_state'] = 1#加入signal权重 KDJ_temp_under = Factor[Factor.KDJ_K<Factor.KDJ_D] KDJ_temp_under['KDJ_state'] = -1 KDJ_temp = pd.concat([KDJ_temp_on,KDJ_temp_under],axis = 0) KDJ = KDJ_temp.dropna() KDJ = KDJ.sort_index() return KDJ.KDJ_state#serials

from CAL.PyCAL import * import numpy as np import pandas as pd start = '2014-01-01' # 回测起始时间 end = u'' # 回测结束时间 benchmark = 'HS300' # 策略参考标准 universe = set_universe('A') # 证券池,支持股票和基金 capital_base = 100000 # 起始资金 freq = 'd' # 策略类型,'d'表示日间策略使用日线回测,'m'表示日内策略使用分钟线回测 refresh_rate = 1 # 调仓频率,表示执行handle_data的时间间隔,若freq = 'd'时间间隔的单位为交易日,若freq = 'm'时间间隔为分钟 commission = Commission(buycost=0.0008, sellcost=0.0008)#调仓成本 def initialize(account): # 初始化虚拟账户状态 pass def handle_data(account): # 每个交易日的买入卖出指令 yesterday = Date_type_preceding(account.current_date,'-1B') KDJ_state = Get_KDJ_state(yesterday,account.universe) KDJ_state = pd.DataFrame(KDJ_state) buy = KDJ_state[KDJ_state.KDJ_state>0]#买卖list赋值 sell = KDJ_state[KDJ_state.KDJ_state<0] buy = buy.dropna() buy = buy.index sell = sell.dropna() sell = sell.index for stk in sell:#遍历现有头寸 if stk in account.avail_secpos: order_pct_to(stk,0) #卖出股票 for stk in buy: order_pct_to(stk,0.01)#买入股票,粗暴简陋的分散投资方式,可减少回撤率 return

 

 

6.阿隆指标(Aroon)是由图莎尔·钱德(Tushar Chande)1995 年发明的,它通过计算自价格达到近期最高值和最低值以来所经过的期间数,帮助投资者预测证券价格从趋势到区域、区域或反转的变化。

一、阿隆指标(Aroon)简介


阿隆指标(Aroon)是由图莎尔·钱德(Tushar Chande)1995 年发明的,它通过计算自价格达到近期最高值和最低值以来所经过的期间数,帮助投资者预测证券价格从趋势到区域、区域或反转的变化。在技术分析领域中,有一个说法,一个指标使用的人越多,其效力越低。这个技术指标还挺冷门的,我们一同来看看它的效果。

from CAL.PyCAL import * import numpy as np import pandas as pd from pandas import DataFrame from heapq import nlargest from heapq import nsmallest

二、Aroon计算方法


Aroon指标分为两个具体指标,分别AroonUpAroonDown。其具体计算方式为:

  • AroonUp = [(计算期天数-最高价后的天数)/计算期天数]*100
  • AroonDown = [(计算期天数-最低价后的天数)/计算期天数]*100
  • AroonOsc = AroonUp - AroonDown

计算期天数通常取20天

def aroonUp(account,timeLength=20): #运用heapq包的nlargest函数,可以轻松获得:计算期天数-最高价后的天数 eq_AroonUp = {} history = account.get_attribute_history('closePrice',timeLength) for stk in account.universe: priceSeries = pd.Series(history[stk]) eq_AroonUp[stk] = (nlargest(1,range(len(priceSeries)),key=priceSeries.get)[0]+1)*100/timeLength # eq_AroonUp[stk]范围在[5,100]之间 return eq_AroonUp

def aroonDown(account,timeLength=20): #运用heapq包的nsmallest函数,可以轻松获得:计算期天数-最低价后的天数 eq_AroonDown = {} history = account.get_attribute_history('closePrice',timeLength) for stk in account.universe: priceSeries = pd.Series(history[stk]) eq_AroonDown[stk] = (nsmallest(1,range(len(priceSeries)),key=priceSeries.get)[0]+1)*100/timeLength # eq_AroonDown[stk]范围在[5,100]之间 return eq_AroonDown

三、Aroon指标的基本用法


  • AroonUp指标向下跌破50 时,表示向上的趋势正在失去动力;当AroonDown指标向下跌破50时,表示向下的趋势正在失去动力;如果两个指标都在低位,表示股价没有明确的趋势;如果指标在70 以上,表示趋势十分强烈;如果在30 以下,表明相反的趋势正在酝酿。通常来说,AroonOsc在0附近时,是典型的无趋势特征,股票处于盘整阶段。
  • 参考研报《技术指标系列(三)——加入“二次确认”的AROON 阿隆优化指标》中的方法,我们买入AroonOsc > 50的股票。

start = '2009-08-01' # 回测起始时间 end = '2015-08-31' # 回测结束时间 benchmark = 'HS300' # 策略参考标准 universe = set_universe('HS300') # 证券池,支持股票和基金 capital_base = 100000 # 起始资金 freq = 'd' # 策略类型,'d'表示日间策略使用日线回测,'m'表示日内策略使用分钟线回测 refresh_rate = 10 # 调仓频率,表示执行handle_data的时间间隔,若freq = 'd'时间间隔的单位为交易日,若freq = 'm'时间间隔为分钟 def initialize(account): # 初始化虚拟账户状态 pass def handle_data(account): # 每个交易日的买入卖出指令 eq_AroonUp = aroonUp(account,20) eq_AroonDown = aroonDown(account,20) buyList = [] for stk in account.valid_secpos: order_to(stk, 0) for stk in account.universe: if eq_AroonUp[stk] - eq_AroonDown[stk] > 50: buyList.append(stk) for stk in buyList[:]: if stk not in account.universe or account.referencePrice[stk] == 0 or np.isnan(account.referencePrice[stk]): buyList.remove(stk) for stk in buyList: order(stk, account.referencePortfolioValue/account.referencePrice[stk]/len(buyList))

可以看出,策略在股市处于震荡市和牛市中,表现很好;而在熊市和暴跌中,表现的非常差,最大回撤很大。这从阿隆指标的构造中,就可以理解,阿隆指标是一个跟踪趋势的指标,在震荡市和牛市中,都能精选出股票,超越指数;然而在暴跌中,处于上升趋势的股票可能跌的更惨,倾巢之下,焉有完卵。

四、运用Aroon指标来择时


前文说到阿隆指标是一个跟踪趋势的指标,既然如此,我们为什么不把它用来择时呢?

def aroonIndex(account,timeLength=20): #构建指数阿隆指标 indexSeries = pd.Series(account.get_symbol_history('benchmark', timeLength)['closeIndex']) indexAronUp = (nlargest(1,range(len(indexSeries)),key=indexSeries.get)[0]+1)*100/timeLength indexAronDown = (nsmallest(1,range(len(indexSeries)),key=indexSeries.get)[0]+1)*100/timeLength indexOsc = indexAronUp - indexAronDown return indexOsc

当indexOsc > 0时,我们大致认为现在的市场环境没有那么差,可以考虑开仓,编写如下策略。

start = '2009-08-01' # 回测起始时间 end = '2015-08-31' # 回测结束时间 benchmark = 'HS300' # 策略参考标准 universe = set_universe('HS300') # 证券池,支持股票和基金 capital_base = 100000 # 起始资金 freq = 'd' # 策略类型,'d'表示日间策略使用日线回测,'m'表示日内策略使用分钟线回测 refresh_rate = 10 # 调仓频率,表示执行handle_data的时间间隔,若freq = 'd'时间间隔的单位为交易日,若freq = 'm'时间间隔为分钟 def initialize(account): # 初始化虚拟账户状态 pass def handle_data(account): # 每个交易日的买入卖出指令 eq_AroonUp = aroonUp(account,20) eq_AroonDown = aroonDown(account,20) index_osc = aroonIndex(account,20) buyList = [] for stk in account.valid_secpos: order_to(stk, 0) if index_osc > 0: for stk in account.universe: if eq_AroonUp[stk] - eq_AroonDown[stk] > 50: buyList.append(stk) for stk in buyList[:]: if stk not in account.universe or account.referencePrice[stk] == 0 or np.isnan(account.referencePrice[stk]): buyList.remove(stk) for stk in buyList: order(stk, account.referencePortfolioValue/account.referencePrice[stk]/len(buyList))

 

可以看出运用阿隆指标来择时的效果还是不错的,震荡市能跑赢指数,牛市的收益基本可以吃到,暴跌也几乎完美的规避了!缺点就是最大回测还是偏大,可以考虑让条件更严格,让indexOsc > 50。

start = '2009-08-01' # 回测起始时间 end = '2015-08-31' # 回测结束时间 benchmark = 'HS300' # 策略参考标准 universe = set_universe('HS300') # 证券池,支持股票和基金 capital_base = 100000 # 起始资金 freq = 'd' # 策略类型,'d'表示日间策略使用日线回测,'m'表示日内策略使用分钟线回测 refresh_rate = 10 # 调仓频率,表示执行handle_data的时间间隔,若freq = 'd'时间间隔的单位为交易日,若freq = 'm'时间间隔为分钟 def initialize(account): # 初始化虚拟账户状态 pass def handle_data(account): # 每个交易日的买入卖出指令 eq_AroonUp = aroonUp(account,20) eq_AroonDown = aroonDown(account,20) index_osc = aroonIndex(account,20) buyList = [] for stk in account.valid_secpos: order_to(stk, 0) if index_osc > 50: for stk in account.universe: if eq_AroonUp[stk] - eq_AroonDown[stk] > 50: buyList.append(stk) for stk in buyList[:]: if stk not in account.universe or account.referencePrice[stk] == 0 or np.isnan(account.referencePrice[stk]): buyList.remove(stk) for stk in buyList: order(stk, account.referencePortfolioValue/account.referencePrice[stk]/len(buyList))

 

将择时条件设置更严格后,最大回撤果然有所下降,但年化收益率也有大幅下降。从回测图形中,也可以明显看到,指标具有很强的滞后性,往往是指数开始涨了一段时间,策略才开始开仓买入。将indexOsc条件设置的越严格,滞后性表现的就越明显,这样虽然可以提高正确率,减小最大回撤,但有许多收益也错过了。

 

7.布林线指标,即BOLL指标,其英文全称是“Bollinger Bands”,布林线(BOLL)由约翰 布林先生创造,其利用统计原理,求出股价的标准差及其信赖区间,从而确定股价的波动范围及未来走势,利用波带显示股价的安全高低价位,因而也被称为布林带。其上下限范围不固定,随股价的滚动而变化。布林指标和麦克指标MIKE一样同属路径指标,股价波动在上限和下限的区间之内,这条带状区的宽窄,随着股价波动幅度的大小而变化,股价涨跌幅度加大时,带状区变宽,涨跌幅度狭小盘整时,带状区则变窄。

 

概念


布林线指标,即BOLL指标,其英文全称是“Bollinger Bands”,布林线(BOLL)由约翰 布林先生创造,其利用统计原理,求出股价的标准差及其信赖区间,从而确定股价的波动范围及未来走势,利用波带显示股价的安全高低价位,因而也被称为布林带。其上下限范围不固定,随股价的滚动而变化。布林指标和麦克指标MIKE一样同属路径指标,股价波动在上限和下限的区间之内,这条带状区的宽窄,随着股价波动幅度的大小而变化,股价涨跌幅度加大时,带状区变宽,涨跌幅度狭小盘整时,带状区则变窄。

算法


  • 中轨线 = N日的移动平均线
  • 上轨线 = 中轨线 + 两倍的标准差
  • 下轨线 = 中轨线 - 两倍的标准差

通信达公式

BOLL: MA(CLOSE, M); # 收盘价的M日简单移动平均,一般m为20 UB: BOLL + 2 * STD(CLOSE, M); # BOLL + 2*收盘价的M日估算标准差 LB: BOLL - 2 * STD(CLOSE, M); # BOLL - 2*收盘价的M日估算标准差 STD(X,N) 返回估算标准差,即 平方根 (最近N日累计(收盘价 - MA) * (收盘价 - MA)/N)

import pandas as pd import numpy as np # personal lib from lib import lib DataAPI.settings.cache_enabled = False

def SMA(x, n, m): """SMA(X,N,M):X的N日移动平均,M为权重,如Y=(X*M+Y'*(N-M))/N e.g SMA(dataframe['close'], 10, 1) return a list of sma """ x = list(x) if not isinstance(x, (list, tuple)) else x sma = [0] * len(x) # 初始化为0,比如说10日均线,在上市前10天是没有数据的,应该设置为0或者是nan for i in range(n-1, len(x)): # 从第 n 天开始计算移动平均,n之前都为0或nan sma[i] = (x[i] * m + sma[i-1] * (n-m)) * 1.0 / n return sma def df_round(data, columns, decimals=2): """对 data 对几个 columns 取小数点精度 """ for i in columns: data[i] = data[i].round(decimals) return data

# @lib.wrapper_func_time def cal_boll(ticker): """计算股票ticker 的 boll """ listDate = DataAPI.SecIDGet(assetClass='E', ticker=ticker, field=['listDate']).listDate[0] data = DataAPI.MktEqudAdjGet(ticker=ticker, beginDate=listDate.replace('-',''), isOpen='1', field=["secShortName", "tradeDate", "closePrice", "preClosePrice", "turnoverVol"]) data['ma_20'] = pd.rolling_mean(data.closePrice, 20) tmp = [0] * len(data) for i in range(19, len(data)): tmp[i] = data.closePrice[ max(i-19, 0) : i + 1].std() data['ma_std'] = tmp data['midBoll'] = data['ma_20'] data['upperBoll'] = data['ma_20'] + 2 * data['ma_std'] data['lowerBoll'] = data['ma_20'] - 2 * data['ma_std'] data = df_round(data, ['ma_std', 'midBoll', 'upperBoll', 'lowerBoll'], 2) return data

df = cal_boll('000002') df.tail(5)

 

作图

from lib import report df_tail = df.tail(100) # report library: https://uqer.datayes.com/community/share/56a5b221228e5b2047d916d9 ax = df_tail.plot(x=['tradeDate'], y=['closePrice'], figsize=(20, 6)) report.fig_style(ax, ['closePrice']) ax = df_tail.plot(x=['tradeDate'], y=['midBoll', 'upperBoll', 'lowerBoll'], kind='line', figsize=(20, 6), color=['r', 'g', 'b']) report.fig_style(ax, ['midBoll', 'upperBoll', 'lowerBoll'], legend_loc='best')

 

 

8.RSI(Relative Strength Index),相对强弱指数,在1978年6月由Wells Wider提出,用来衡量证券自身内在相对强度,最早被应用于期货买卖,后来人们发现在众多的图表技术分析中,RSI理论和实践极其适合于股票市场的短线投资,于是被用于股票升跌的测量和分析中。它是通过特定时期内股价的变动情况来计算市场买卖力量对比,比较一段时期内的平均收盘涨数和平均收盘跌数来分析市场买沽盘的意向和实力,进而作出对未来市场的走势并判断股票价格内部本质强弱、推测价格未来变动方向的技术指标。

1. RSI原理

  • RSI(Relative Strength Index),相对强弱指数,在1978年6月由Wells Wider提出,用来衡量证券自身内在相对强度,最早被应用于期货买卖,后来人们发现在众多的图表技术分析中,RSI理论和实践极其适合于股票市场的短线投资,于是被用于股票升跌的测量和分析中。它是通过特定时期内股价的变动情况来计算市场买卖力量对比,比较一段时期内的平均收盘涨数和平均收盘跌数来分析市场买沽盘的意向和实力,进而作出对未来市场的走势并判断股票价格内部本质强弱、推测价格未来变动方向的技术指标。
  • 简单说,RSI是以数字的方法求买卖双方的力量对比。譬如100个人面对一件商品,如果50个人以上要买,竞相抬价,商品价格必涨。相反,如果50个人以上争着卖出,价格自然下跌。RSI理论认为,任何市价的大涨或大跌,均在0-100之间变动。根据常态分配,RSI值多在30-70之间变动,通常80甚至90时被认为市场已到达超买状态,市场价格会回落调整。当价格低跌至30以下即被认为是超卖状态,市价将出现反弹回升。

2. 计算公式

  • RSI(N)=[N日上升平均数÷(N日上升平均数+N日下跌平均数)]×100
  • N日上升平均数是在某段长为N日子里升幅数的平均,N日下跌平均数则是在同一段日子里跌幅数的平均。若计算9日RSI,需要找出前10日内的上升平均数及下跌平均数(各9个),如下是某股票每日收盘价的涨跌:
  • 第一日23.70

    第二日27.90 \ + 4.20

    第三日26.50 \ - 1.40

    第四日29.60 \ + 3.10

    第五日31.10 \ + 1.50

    第六日29.40 \ - 1.70

    第七日25.50 \ - 3.90

    第八日28.90 \ + 3.40

    第九日20.50 \ - 8.40

    第十日23.20 \ + 2.80

    ──────────────────

    9日内上涨平均值:(4.20+3.10+1.50+3.40+2.80)/9 = 15.0/9 = 1.67

    9日内下跌平均值:(1.40+1.70+3.90+8.40)/9 = 15.4/9 = 1.71

    第10日:上升平均数=(4.20+3.10+1.50+3.40+2.80)/9=1.67

    :下降平均数=(1.40+1.70+3.90+8.40)/9=1.71

    :RSI=[1.67÷(1.67+1.71)]×100=49.41
  • 在行情软件中,RSI指数通常提供3条:N = [6,12,24],一般对应于[ RSI1,RSI2,RSI3 ]。
  • 文中我们使用UQER已经安装好的 TA-Lib 包计算RSI。

3. 买卖原则

  • 超买超卖界定 Wells Wider推荐的默认时间跨度是14天,他论证了应用月周期28日的一半是有效的,在TA-Lib中RSI的默认计算周期为14。在实际的操作中,RSI指标具有滞后性,且其数值变动区间与市场形态和计算周期跨度。
  • - 市场形态影响:在起伏不大的震荡市场中,通常规定70以上超买,30以下超卖;在变化比较剧烈的市场中,通常规定80以上超买,20以下超卖。
    - 计算周期跨度:对于较短计算周期的12日RSI,通常规定80以上超买,20以下超卖;对于较长计算周期的24日RSI,通常规定70以上超买,30以下超卖。
  • 买卖信号
  • - 参考方案1:根据单RSI信号超买超卖区:超买区卖出,超卖区买入。
    - 参考方案2:根据短期与长期RSI信号:
    1)短期RSI值在30以下,由下向上交叉黄色的长期RSI值时为买入信号;
    2)短期RSI值在70以上,由上向下交叉黄色的长期RSI值时为卖出信号。
  • 通常RSI在40~60之间不太具有参考价值

4. RSI示例

  • RSI数值示例

import talib as ta import pandas as pd ####get the daily data spdb = DataAPI.MktEqudAdjGet(secID='600000.XSHG',beginDate='20120101',endDate='20151229',field='tradeDate,secID,closePrice') ####change the index of the data spdb = spdb.set_index('tradeDate').sort_index() spdb.index = pd.to_datetime(spdb.index) ####timeperiod = 14 rsi = ta.RSI(spdb.closePrice.values) rsi1 = ta.RSI(spdb.closePrice.values,6) rsi2 = ta.RSI(spdb.closePrice.values,12) rsi3 = ta.RSI(spdb.closePrice.values,24) spdb['rsi'] = rsi ##timeperiod = 14 spdb['rsi1'] = rsi1 ##timeperiod = 6 spdb['rsi2'] = rsi2 ##timeperiod = 12 spdb['rsi3'] = rsi3 ##timeperiod = 24 spdb.tail()

 

####figure display spdb[['closePrice']].plot(figure = 'BenchMark',figsize=(12,4)) spdb[['rsi']].plot(figsize=(12,4), kind='bar', xticks=[], color='b') spdb[['rsi1','rsi2','rsi3']].plot(figsize=(12,4))

 

 

  • RSI信号示例
    • 参考方案1:根据RSI信号超买超卖区给出信号

buyThres = 30 sellThres = 70 rsiSig = [] for index in spdb['rsi']: if index > sellThres: rsiSig.append(-1) elif index < buyThres: rsiSig.append(1) else: rsiSig.append(0) spdb['rsiSig'] = rsiSig spdb['rsiSig'].plot(figsize=(12,4))

 

  • RSI信号示例
    • 参考方案2:根据短期与长期RSI信号

buyThres = 30 sellThres = 70 rsiSig = [] spdb['rsiDif'] = spdb['rsi1']-spdb['rsi3'] rsiSig = [0]*len(spdb['rsiDif']) for index in range(len(spdb['rsiDif'])): if index != 0 and index != len(spdb['rsiDif'])-1: if spdb['rsiDif'][index] > 0 and spdb['rsiDif'][index-1] < 0 and spdb['rsi'][index-1] < buyThres: rsiSig[index+1] = 1 elif spdb['rsiDif'][index] < 0 and spdb['rsiDif'][index-1] > 0 and spdb['rsi'][index-1] > sellThres: rsiSig[index+1] = -1 spdb['rsiSig'] = rsiSig spdb['rsiSig'].plot(figsize=(12,4))

 

5. RSI策略示例

在示例中,我们以两种信号生成方案分别对于招商银行个股和沪深300成分股进行了策略测试。

  • 信号生成方案1:根据RSI信号超买超卖区给出信号——招商银行个股

import talib as ta def initialize(account): account.buyThres = 30 account.sellThres = 70 def handle_data(account): closePrice = account.get_attribute_history('closePrice', history) rsi ={} for stock in account.universe: rsi[stock] = ta.RSI(closePrice[stock], history-1)[-1] for stock in account.universe: if (rsi[stock] < account.buyThres): order(stock, (round((1*account.cash/len(account.universe)/account.referencePrice[stock])/100)+1)*100) elif (rsi[stock] > account.sellThres): order_to(stock, 0) start = '2012-01-01' end = '2015-12-28' benchmark = 'HS300' universe = ['600000.XSHG'] capital_base = 1000000 history = 14

 

  • 信号生成方案1:根据RSI信号超买超卖区给出信号——沪深300成分股

import talib as ta import numpy as np def initialize(account): account.buyThres = 30 account.sellThres = 70 def handle_data(account): closePrice = account.get_attribute_history('closePrice', history) rsi ={} for stock in account.universe: # print closePrice[stock] try: rsi[stock] = ta.RSI(closePrice[stock],history-1)[-1] except: rsi[stock] = 50 # if closePrice[stock].tolist().count('nan') == 0: # rsi[stock] = ta.RSI(closePrice[stock], history-1)[-1] # else: # rsi[stock] = 50 for stock in account.universe: if (rsi[stock] < account.buyThres): try: order(stock, (round((1*account.cash/len(account.universe)/account.referencePrice[stock])/100)+1)*100) except: pass elif (rsi[stock] > account.sellThres): order_to(stock, 0) start = '2012-01-01' end = '2015-12-28' benchmark = 'HS300' universe = set_universe('HS300') capital_base = 1000000 history = 14

  • 信号生成方案2:根据短期与长期RSI信号——招商银行个股

import talib as ta def initialize(account): account.buyThres = 30 account.sellThres = 70 def handle_data(account): closePrice = account.get_attribute_history('closePrice', history) rsi1 ={} rsi2 ={} rsiDif = {} for stock in account.universe: rsi1[stock] = ta.RSI(closePrice[stock],6)[-2:] rsi2[stock] = ta.RSI(closePrice[stock],24)[-2] rsiDif[stock] = rsi1[stock] - rsi2[stock] for stock in account.universe: if rsiDif[stock][0] < 0 and rsiDif[stock][1]>0 and rsi1[stock][0] < account.buyThres: order(stock, (round((1*account.cash/len(account.universe)/account.referencePrice[stock])/100)+1)*100) elif rsiDif[stock][0] > 0 and rsiDif[stock][1]<0 and rsi1[stock][0] > account.sellThres: order_to(stock, 0) start = '2012-01-01' end = '2015-12-28' benchmark = 'HS300' universe = ['600000.XSHG'] # universe = ['600000.XSHG','000001.XSHE'] capital_base = 1000000 history = 30

 

  • 信号生成方案2:根据短期与长期RSI信号——沪深300成分股

import talib as ta import numpy as np def initialize(account): account.buyThres = 30 account.sellThres = 70 def handle_data(account): closePrice = account.get_attribute_history('closePrice', history) rsi1 ={} rsi2 ={} rsiDif = {} for stock in account.universe: try: rsi1[stock] = ta.RSI(closePrice[stock],6)[-2:] rsi2[stock] = ta.RSI(closePrice[stock],24)[-2:] rsiDif[stock] = rsi1[stock] - rsi2[stock] except: rsi1[stock] = np.array([50,50]) rsi2[stock] = np.array([50,50]) rsiDif[stock] = rsi1[stock] - rsi2[stock] for stock in account.universe: if rsiDif[stock][0] < 0 and rsiDif[stock][1]>0 and rsi1[stock][0] < account.buyThres: try: order(stock, (round((1*account.cash/len(account.universe)/account.referencePrice[stock])/100)+1)*100) except: pass elif rsiDif[stock][0] > 0 and rsiDif[stock][1]<0 and rsi1[stock][0] > account.sellThres: order_to(stock, 0) start = '2012-01-01' end = '2015-12-28' benchmark = 'HS300' universe = set_universe('HS300') capital_base = 1000000 history = 30

 

5. 指标评测

  • RSI信号量较少,择时效果差强人意,入场时点较好;
  • 利用短期和长期RSI线交叉择时比只根据RSI超买超卖区择时效果相对要好。

 

 

9.在传统的金融理论中,波动率是风险的度量。如果固定窗口长度,不断滚动计算这段窗口期内的指数收益率的波动率,则可以得到一系列的波动率时间序列。

使用单向波动率指标对指数进行择时

单向波动率差值指数择时

在传统的金融理论中,波动率是风险的度量。如果固定窗口长度,不断滚动计算这段窗口期内的指数收益率的波动率,则可以得到一系列的波动率时间序列。下面的代码是一个简单的实现:

import seaborn import pandas as pd import matplotlib.pyplot as plt VOL_LEN = 10 df = DataAPI.MktIdxdGet(indexID='000300.ZICN', beginDate='20120101', endDate='20160801', field=['preCloseIndex', 'closeIndex']) fig = pylab.figure(figsize=(12, 5)) df['closeIndex'].plot()

 

df['ret'] = df['closeIndex'] / df['preCloseIndex'] df['vol'] = pd.rolling_std(df['ret'], 10) fig = pylab.figure(figsize=(12, 5)) df['vol'].plot()

 

第一个图是指数的值,第二个图是短期波动率的值,对比两张图我们可以看出当波动率剧烈增加的时候,一般都是有剧烈的上涨或下跌的时候。简单来说波动率的升高意味着市场的涨跌正在变得越来越明显,更容易出现明确的单边行情。然而一个无法通过波动率看出的信息在于:到底是出现哪边的单边的行情呢?

为了从波动率中过滤出更多的信息,我们需要对波动率序列进行处理。我们这里来考虑最简单的一种处理:单向波动率

这个概念很好理解,就是带符号的波动率,如果某天指数涨了,那么波动率就是正的,反之如果跌了就是负的。

 

MA_LEN = 40 df['sym_vol'] = (df['ret'] > 1) * df['vol'] - (df['ret'] < 1) * df['vol'] fig = pylab.figure(figsize=(12, 5)) df['sym_vol'].plot()

 

收益率被成功地加上了符号,但是这些数据波动实在太大,为了更清楚地看出单向波动率的情况,直接用最简单的MA方法来平滑之。

df['sym_vol_ma'] = pd.rolling_mean(df['sym_vol'], MA_LEN) fig = pylab.figure(figsize=(12, 5)) df['sym_vol_ma'].plot()

嗯,这样一来就好看多了,从这个图上可以看出单向波动率是有“周期性”的,一段时间大于0,一段时间小于0。由于单向波动率是带符号的波动率,可以直接从中读出市场的涨跌情况,因此有一些很直观的猜测:

  • 当单向波动率小于0时,意味着市场的“负向”波动率占主体,直到变为正的为止
  • 当单向波动率大于0时,意味着市场的“正向”波动率占主体,直到变为负的为止

这个猜测可以直接转化成一个择时策略:单向波动率为正时持有指数,为负时空仓。

但显然直接使用这个论断来择时未免有点武断,因为在一般的震荡市场当中,单向波动率可能出现在0上下反复震荡的情况。为了避免这种情况,稍微再多想一想,波动率为负时持有指数是希望指数在目前下跌较多的情况下上涨,即希望出现反转;波动率为正时持有指数是希望指数在已经上涨的情况下保持上涨,即希望保持趋势。因此,可以直接把大盘分为“反转”、“趋势”、“震荡”三种情况,分别采取不同的择时思路。

那么接下来的问题是,如何把大盘分为“反转”、“趋势”、“震荡”三种情况呢?

答案可以很简单也可以很难。这里为了节省时间,直接采取最简单粗暴的方式:大盘已经跌了很多了,就归于反转;大盘已经涨了很多了,就归于趋势;其他情况都属于震荡。

尽管这样的分法很粗糙,可能会把不少“反转”和“趋势”归于“震荡”,但是由于策略是仅对指数进行择时的,在震荡的时候可以选择简单地持有指数,这样风险和收益与指数是一致的,所以不会有什么额外的损失。

基于这样的思路,可以很快地实现一个标的是指数ETF的择时策略。

from collections import deque import numpy as np IDXETF = '510300.XSHG' # 华泰柏瑞300 MA_LEN = 40 # 长期收益率及波动率平滑窗口 VOL_LEN = 10 # 波动率计算窗口 THRESHOLD = 0.05 # 简单大盘风格判断基准 start = '2013-08-01' end = '2016-08-01' benchmark = 'HS300' universe = [IDXETF] # 只择时一只 capital_base = 1000000 freq = 'd' refresh_rate = 1 def initialize(account): account.vol_diff = deque(maxlen=MA_LEN) # 定长双端队列 def handle_data(account): # 计算长期累计收益与短期波动率 if IDXETF in account.universe: history = account.get_symbol_history(IDXETF, MA_LEN) ret = history['closePrice'] / history['preClosePrice'] - 1. cum_ret = history['closePrice'][-1] / history['closePrice'][0] - 1 new_std = ret[-VOL_LEN:].std() else: ret = [0] cum_ret = new_std = 0 # 更新波动率差值 if ret[-1] > 0: account.vol_diff.append(new_std) elif ret[-1] < 0: account.vol_diff.append(-new_std) else: account.vol_diff.append(0.) if len(account.vol_diff) < MA_LEN: observe('vol_diff', 0.) observe('cum_ret', 0.) else: vol_diff_mean = sum(account.vol_diff) / MA_LEN if cum_ret <= -THRESHOLD: # 反转,单向波动率差为负时满仓 if vol_diff_mean < 0: order_pct_to(IDXETF, 1.) else: order_to(IDXETF, 0.) elif cum_ret >= THRESHOLD: # 趋势,单向波动率差为正时满仓 if vol_diff_mean > 0: order_pct_to(IDXETF, 1.) else: order_to(IDXETF, 0.) else: order_pct_to(IDXETF, 1) observe('vol_diff', vol_diff_mean) observe('cum_ret', cum_ret)

 

但从结果来看,表现还是不错的,由于满仓时也只是100%持有指数,不可能出现上涨超过大盘的情况,所以所有的超额收益的来源都是避免下跌,所有超额损失的来源都是踏空上涨,就这两点而言,这个策略的表现还是不错的。

不过就整体的分析而言,每一步都有更多的点可以挖掘:

  • 有其他的单向波动率的定义吗?
  • 单向波动率的形态可以用来择时吗?
  • 指数加权移动平均会怎么样?
  • 有其他的判断大盘趋势的方法吗?
  • 单向波动率可以和其他指标结合吗?
  • ……

这些内容会在未来慢慢地加到这个以单向波动率为核心的分析框架里来。

 

 

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

一、CCI指标简介与构造


顺势指标CCI由唐纳德拉姆伯特所创,是通过测量股价的波动是否已超出其正常范围,来预测股价变化趋势的技术分析指标。计算方法参考《技术指标系列(五)——CCI的顺势而为》。

下面描绘出CCI与股价时序图走势

def cci(stock,start_date,end_date,windows): #设置股票,起始时间,以及CCI指标多少日 import pandas as pd import numpy as np from CAL.PyCAL import * Alpha = 0.015 eq_TP = {} eq_MATP = {} eq_meanDev = {} eq_CCI = {} cal = Calendar('China.SSE') windows = '-'+str(windows)+'B' start_date = Date.strptime(start_date,"%Y%m%d") end_date = Date.strptime(end_date,"%Y%m%d") timeLength = cal.bizDatesList(start_date, end_date) for i in xrange(len(timeLength)): begin_date = cal.advanceDate(timeLength[i],windows,BizDayConvention.Unadjusted) begin_date =begin_date.strftime("%Y%m%d") timeLength[i] = timeLength[i].strftime("%Y%m%d") eq_static = DataAPI.MktEqudAdjGet(secID=stock,beginDate=begin_date,endDate=timeLength[i],field=['secID','highestPrice','lowestPrice','closePrice'],pandas="1") for stk in stock: try: eq_TP[stk] = np.array(eq_static[eq_static['secID'] == stk].mean(axis=1)) eq_MATP[stk] = sum(eq_TP[stk])/len(eq_TP[stk]) eq_meanDev[stk] = sum(abs(eq_TP[stk] - eq_MATP[stk]))/len(eq_TP[stk]) eq_CCI[stk].append((eq_TP[stk][-1] - eq_MATP[stk])/(Alpha * eq_meanDev[stk])) except: eq_CCI[stk] = [] Date = pd.DataFrame(timeLength) eq_CCI = pd.DataFrame(eq_CCI) cciSeries = pd.concat([Date,eq_CCI],axis =1) cciSeries.columns = ['Date','CCI'] return cciSeries

def cci_price_Plot(stock,start_date,end_date,windows): cciSeries = cci(stock,start_date,end_date,windows) closePrice = DataAPI.MktEqudAdjGet(secID=stock,beginDate=start_date,endDate=end_date,field=['closePrice'],pandas="1") table = pd.merge(cciSeries,closePrice, left_index=True, right_index=True, how = 'inner') return table

import seaborn import pandas as pd import numpy as np from CAL.PyCAL import * cal = Calendar('China.SSE') table = cci_price_Plot(['600000.XSHG'],'20080531','20150901',30) #绘制浦发银行的CCI与股价对比图 tableDate = table.set_index('Date') tableDate.plot(figsize=(20,8),subplots = 1)

二、CCI指标简单应用


选取CCI处于100和150之间,开始处于上涨趋势的股票。关于windows,我们用quick_backtest做一个简单的优化

def cci(account,N=20): Alpha = 0.015 eq_TP = {} eq_MATP = {} eq_meanDev = {} eq_CCI = {} eq_highPrice = account.get_attribute_history('highPrice',N) eq_closePrice = account.get_attribute_history('closePrice',N) eq_lowPrice = account.get_attribute_history('lowPrice',N) for stk in account.universe: eq_TP[stk] = (eq_highPrice[stk] + eq_closePrice[stk] + eq_lowPrice[stk])/3 eq_MATP[stk] = sum(eq_TP[stk])/len(eq_TP[stk]) eq_meanDev[stk] = sum(abs(eq_TP[stk] - eq_MATP[stk]))/len(eq_TP[stk]) eq_CCI[stk] = (eq_TP[stk][-1] - eq_MATP[stk])/(Alpha * eq_meanDev[stk]) return eq_CCI

start = '2010-08-01' # 回测起始时间 end = '2014-08-01' # 回测结束时间 benchmark = 'HS300' # 策略参考标准 universe = set_universe('HS300') # 证券池,支持股票和基金 capital_base = 100000 # 起始资金 freq = 'd' # 策略类型,'d'表示日间策略使用日线回测,'m'表示日内策略使用分钟线回测 refresh_rate = 20 # 调仓频率,表示执行handle_data的时间间隔,若freq = 'd'时间间隔的单位为交易日,若freq = 'm'时间间隔为分钟 sim_params = quartz.sim_condition.env.SimulationParameters(start, end, benchmark, universe, capital_base) idxmap_all, data_all = quartz.sim_condition.data_generator.get_daily_data(sim_params)

from CAL.PyCAL import * import pandas as pd import numpy as np def initialize(account): # 初始化虚拟账户状态 pass def handle_data(account): # 每个交易日的买入卖出指令 eq_CCI = cci(account,window) buylist = [] for stk in account.universe: try: if eq_CCI[stk] > 100 and eq_CCI[stk] < 150: buylist.append(stk) except: pass for stk in account.valid_secpos: order_to(stk, 0) for stk in buylist[:]: if stk not in account.universe or account.referencePrice[stk] == 0 or np.isnan(account.referencePrice[stk]): bulist.remove(stk) for stk in buylist: order(stk, account.referencePortfolioValue/account.referencePrice[stk]/len(buylist)) print 'window annualized_return sharpe max_drawdown' for window in range(10, 100, 5): strategy = quartz.sim_condition.strategy.TradingStrategy(initialize, handle_data) bt_test, acct = quartz.quick_backtest(sim_params, strategy, idxmap_all, data_all,refresh_rate = refresh_rate) perf = quartz.perf_parse(bt_test, acct) print ' {0:2d} {1:>7.4f} {2:>7.4f} {3:>7.4f}'.format(window, perf['annualized_return'], perf['sharpe'], perf['max_drawdown'])

 

from CAL.PyCAL import * import pandas as pd import numpy as np start = '2010-08-01' # 回测起始时间 end = '2014-08-01' # 回测结束时间 benchmark = 'HS300' # 策略参考标准 universe = set_universe('HS300') # 证券池,支持股票和基金 capital_base = 100000 # 起始资金 freq = 'd' # 策略类型,'d'表示日间策略使用日线回测,'m'表示日内策略使用分钟线回测 refresh_rate = 20 # 调仓频率,表示执行handle_data的时间间隔,若freq = 'd'时间间隔的单位为交易日,若freq = 'm'时间间隔为分钟 def initialize(account): # 初始化虚拟账户状态 pass def handle_data(account): # 每个交易日的买入卖出指令 eq_CCI = cci(account,85) buylist = [] for stk in account.universe: try: if eq_CCI[stk] > 100 and eq_CCI[stk] < 150: buylist.append(stk) except: pass for stk in account.valid_secpos: order_to(stk, 0) for stk in buylist[:]: if stk not in account.universe or account.referencePrice[stk] == 0 or np.isnan(account.referencePrice[stk]): bulist.remove(stk) for stk in buylist: order(stk, account.referencePortfolioValue/account.referencePrice[stk]/len(buylist))

 

样本外测试

from CAL.PyCAL import * import pandas as pd import numpy as np start = '2014-08-01' # 回测起始时间 end = '2015-08-01' # 回测结束时间 benchmark = 'HS300' # 策略参考标准 universe = set_universe('HS300') # 证券池,支持股票和基金 capital_base = 100000 # 起始资金 freq = 'd' # 策略类型,'d'表示日间策略使用日线回测,'m'表示日内策略使用分钟线回测 refresh_rate = 20 # 调仓频率,表示执行handle_data的时间间隔,若freq = 'd'时间间隔的单位为交易日,若freq = 'm'时间间隔为分钟 def initialize(account): # 初始化虚拟账户状态 pass def handle_data(account): # 每个交易日的买入卖出指令 eq_CCI = cci(account,85) buylist = [] for stk in account.universe: try: if eq_CCI[stk] > 100 and eq_CCI[stk] < 150: buylist.append(stk) except: pass for stk in account.valid_secpos: order_to(stk, 0) for stk in buylist[:]: if stk not in account.universe or account.referencePrice[stk] == 0 or np.isnan(account.referencePrice[stk]): bulist.remove(stk) for stk in buylist: order(stk, account.referencePortfolioValue/account.referencePrice[stk]/len(buylist))

 

 

11.佳庆离散指标(Chaikin Volatility,简称CVLT,VCI,CV)又称“佳庆变异率指数”,是通过测量一段时间内价格幅度平均值的变化来反映价格的离散程度。

import numpy as np start = datetime(2011, 1, 1) end = datetime(2015, 4, 27) benchmark = 'HS300' universe = set_universe('SH50') capital_base = 100000 short_history = 30 longest_history = 60 pos_pieces = 10 enter_window = 20 exit_window = 10 N = 4 def initialize(account): account.postion_size_hold = {} for stk in universe: account.postion_size_hold[stk] = 0 def handle_data(account): print account.current_date histCloseLong = account.get_attribute_history('closePrice', longest_history) histLowLong = account.get_attribute_history('lowPrice', longest_history) histHighLong = account.get_attribute_history('highPrice', longest_history) histTurnoverLong = account.get_attribute_history('turnoverVol', longest_history) histCloseShort = account.get_attribute_history('closePrice', short_history) histLowShort = account.get_attribute_history('lowPrice', short_history) histHighShort = account.get_attribute_history('highPrice', short_history) histTurnoverShort = account.get_attribute_history('turnoverVol', short_history) for stock in account.universe: cnt_price = account.referencePrice[stock] a1 = histCloseLong[stock] - histLowLong[stock] b1 = histCloseLong[stock] - histHighLong[stock] c1 = histHighLong[stock] - histLowLong[stock] d1 = histTurnoverLong[stock] adl = ((((a1)-(b1))/(c1)))*d1 a2 = histCloseShort[stock] - histLowShort[stock] b2 = histCloseShort[stock] - histHighShort[stock] c2 = histHighShort[stock] - histLowShort[stock] d2 = histTurnoverShort[stock] ads = ((((a2)-(b2))/(c2)))*d2 mean_cp1 = adl.mean() mean_cp2 = ads.mean() flag = mean_cp1 - mean_cp2 if flag > 0 and account.postion_size_hold[stock]<N: order_to(stock, capital_base/pos_pieces/cnt_price/N) account.postion_size_hold[stock] += 1 elif flag < 0 : order_to(stock, 0) account.postion_size_hold[stock] = 0

 

 

 

12.DMI指标又叫动向指标或趋向指标,是通过分析股票价格在涨跌过程中买卖双方力量均衡点的变化情况,即多空双方的力量的变化受价格波动的影响而发生由均衡到失衡的循环过程,从而提供对趋势判断依据的一种技术指标。其由美国技术分析大师威尔斯·威尔德(Wells Wilder)所创造,是一种中长期股市技术分析方法。

DMI指标又叫动向指标或趋向指标,是通过分析股票价格在涨跌过程中买卖双方力量均衡点的变化情况,即多空双方的力量的变化受价格波动的影响而发生由均衡到失衡的循环过程,从而提供对趋势判断依据的一种技术指标。其由美国技术分析大师威尔斯·威尔德(Wells Wilder)所创造,是一种中长期股市技术分析方法。


DMI指标体系的构建:


TR = SUM(MAX(MAX(HIGH - LOW, ABS(HIGH-REF(CLOSE,1))), ABS(LOW - REF(CLOSE, 1))), N)

HD = HIGH - REF(HIGH, 1)

LD = REF(LOW, 1) - LOW

DMP = SUM(IF(HD>0 AND HD>LD, HD, 0), N)

DMM = SUM(IF(LD>0 AND LD>HD, LD, 0), N)

PDI = DMP*100/TR

MDI = DMM*100/TR

DX = ABS(MDI - PDI)/(MDI + PDI)*100

ADX = MA(ABS(MDI - PDI)/(MDI + PDI)*100, M)

其中变量与函数定义如下:

CLOSE:引用收盘价(在盘中指最新价)

HIGH:引用最高价

LOW:引用最低价

REF(X, N):引用X在N个周期前的值

ABS(X):求X的绝对值

MAX(A, B):求A,B中的较大者

SUM(X, N):得到X在N周期内的总和

IF(C, A, B):如果C成立返回A,否则返回B

此外,PDI简记为+DI,MDI简记为-DI;参数:N=14(默认),M=14 (默认)。


实际上从数学上看,DX或ADX的构建并不一定需要PDI与MDI,有DMP和DMM就行了。计算PDI与MDI是将指标数值控制在0到100之间。

#计算某一天的股票DMP与DMM值 def eq_DMPandDMM(stk_list,current_date,N=14): cal = Calendar('China.SSE') period = '-' + str(N+1) + 'B' begin_date = cal.advanceDate(current_date,period,BizDayConvention.Unadjusted) end_date = cal.advanceDate(current_date,'-1B',BizDayConvention.Unadjusted) eq_hd = {} eq_ld = {} dmp_sum = 0 dmm_sum = 0 eq_dmp = {} eq_dmm = {} eq_Price = DataAPI.MktEqudAdjGet(secID=stk_list,beginDate=begin_date.strftime('%Y%m%d'),endDate=end_date.strftime('%Y%m%d'),field=['secID','highestPrice','lowestPrice'],pandas="1") avaiable_list = eq_Price['secID'].drop_duplicates().tolist() eq_Price.set_index('secID',inplace=True) for stk in avaiable_list: if len(eq_Price.ix[stk]) == (N+1): eq_hd[stk] = np.array(eq_Price.ix[stk]['highestPrice'][1:] - eq_Price.ix[stk]['highestPrice'][:-1]) eq_ld[stk] = np.array(eq_Price.ix[stk]['lowestPrice'][:-1] - eq_Price.ix[stk]['lowestPrice'][1:]) for i in xrange(len(eq_ld[stk])): if eq_hd[stk][i] > 0 and eq_hd[stk][i] > eq_ld[stk][i]: dmp_sum = dmp_sum + eq_hd[stk][i] if eq_ld[stk][i] > 0 and eq_ld[stk][i] > eq_hd[stk][i]: dmm_sum = dmm_sum + eq_ld[stk][i] eq_dmp[stk] = dmp_sum eq_dmm[stk] = dmm_sum dmm_sum = 0 dmp_sum = 0 return eq_dmp,eq_dmm

#计算某一天股票的TR值 def eq_TR(stk_list,current_date,N=14): cal = Calendar('China.SSE') period = '-' + str(N+1) + 'B' begin_date = cal.advanceDate(current_date,period,BizDayConvention.Unadjusted) end_date = cal.advanceDate(current_date,'-1B',BizDayConvention.Unadjusted) eq_hl = {} #HIGH - LOW eq_hc = {} #HIGH - CLOSE eq_lc = {} #LOW - CLOSE eq_tr = {} tr_sum = 0 eq_Price = DataAPI.MktEqudAdjGet(secID=stk_list,beginDate=begin_date.strftime('%Y%m%d'),endDate=end_date.strftime('%Y%m%d'),field=['secID','highestPrice','lowestPrice','closePrice'],pandas="1") avaiable_list = eq_Price['secID'].drop_duplicates().tolist() eq_Price.set_index('secID',inplace=True) for stk in avaiable_list: if len(eq_Price.ix[stk]) == (N+1): eq_hl[stk] = np.array(eq_Price.ix[stk]['highestPrice'][1:] - eq_Price.ix[stk]['lowestPrice'][1:]) eq_hc[stk] = np.array(eq_Price.ix[stk]['highestPrice'][1:] - eq_Price.ix[stk]['closePrice'][:-1]) eq_lc[stk] = np.array(eq_Price.ix[stk]['lowestPrice'][:-1] - eq_Price.ix[stk]['closePrice'][1:]) for i in xrange(len(eq_hl[stk])): tr_sum = tr_sum + max(max(eq_hl[stk][i],abs(eq_hc[stk][i])),abs(eq_lc[stk][i])) eq_tr[stk] = tr_sum tr_sum = 0 return eq_tr

#计算某一天股票的ADX def eq_ADX(stk_list,current_date,N=14): cal = Calendar('China.SSE') period = '-' + str(N) + 'B' begin_date = cal.advanceDate(current_date,period,BizDayConvention.Unadjusted) end_date = cal.advanceDate(current_date,'-1B',BizDayConvention.Unadjusted) timeSeries = cal.bizDatesList(begin_date,end_date) eq_adx = {} adx_sum = 0 #初始化eq_adx eq_Price = DataAPI.MktEqudAdjGet(secID=stk_list,beginDate=begin_date.strftime('%Y%m%d'),endDate=end_date.strftime('%Y%m%d'),field=['secID','highestPrice','lowestPrice'],pandas="1") avaiable_list = eq_Price['secID'].drop_duplicates().tolist() eq_Price.set_index('secID',inplace=True) for stk in avaiable_list: if len(eq_Price.ix[stk]) == N: eq_adx[stk] = 0 #计算ADX for i in xrange(len(timeSeries)): eq_dmp,eq_dmm = eq_DMPandDMM(stk_list,timeSeries[i],N) for stk in eq_dmp: if eq_dmp[stk] == 0 and eq_dmm[stk] == 0: #当DMP与DMM都为零时,认为无趋势DX=0 pass else: eq_adx[stk] = eq_adx[stk] + abs(eq_dmp[stk] - eq_dmm[stk])/(eq_dmp[stk] + eq_dmm[stk])*100 for stk in eq_adx: eq_adx[stk] = eq_adx[stk] / len(timeSeries) return eq_adx

简单应用:


当DMP上穿DMM时,意味着,上涨倾向强于下跌倾向,一个买入信号生成。反之则反。而ADX用于反映趋向变动的程度,在买入信号时,ADX伴随上升,则预示股价的涨势可能更强劲。

import numpy as np import pandas as pd from CAL.PyCAL import * start = '2012-08-01' # 回测起始时间 end = '2015-08-01' # 回测结束时间 benchmark = 'HS300' # 策略参考标准 universe = set_universe('HS300') # 证券池,支持股票和基金 capital_base = 1000000 # 起始资金 freq = 'd' # 策略类型,'d'表示日间策略使用日线回测,'m'表示日内策略使用分钟线回测 refresh_rate = 20 # 调仓频率,表示执行handle_data的时间间隔,若freq = 'd'时间间隔的单位为交易日,若freq = 'm'时间间隔为分钟 cal = Calendar('China.SSE') def initialize(account): # 初始化虚拟账户状态 pass def handle_data(account): # 每个交易日的买入卖出指令 eq_dmp_now,eq_dmm_now = eq_DMPandDMM(account.universe,account.current_date,14) eq_adx = eq_ADX(account.universe,account.current_date,14) yestoday = cal.advanceDate(account.current_date,'-1B',BizDayConvention.Unadjusted) eq_dmp_before,eq_dmm_before = eq_DMPandDMM(account.universe,yestoday,14) eq_adx_before = eq_ADX(account.universe,yestoday,14) long_bucket = [] short_bucket = [] for stk in account.universe: try: if eq_dmp_now[stk] > eq_dmm_now[stk] and eq_dmp_before[stk] < eq_dmm_before[stk] and eq_adx[stk] > eq_adx_before[stk]: long_bucket.append(stk) else: short_bucket.append(stk) except: pass #调仓逻辑是调仓时将所有满足条件的股票等权 stk_num = len(account.valid_secpos) + len(long_bucket) for stk in account.valid_secpos: if stk in short_bucket: order_to(stk,0) stk_num = stk_num - 1 for stk in account.valid_secpos: if stk not in short_bucket: order_to(stk,account.referencePortfolioValue/account.referencePrice[stk]/stk_num) for stk in long_bucket: if stk not in account.avail_secpos: order_to(stk,account.referencePortfolioValue/account.referencePrice[stk]/stk_num)

 

 

 

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

本指标是一项超长周期的指标,长时间按照本指标讯号交易,获利百分比大于损失百分比,利润相当可观。

前言

最近在看趋势择时,逛了下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为最重要的买入卖出信号,也是本策略的基石。
策略如下:

import pandas as pd import numpy as np start = '2011-08-01' # 回测起始时间 end = '2016-04-17' # 回测结束时间 benchmark = 'HS300' # 策略参考标准 universe = StockScreener(Factor.LCAP.nsmall(30)) # 因子选股,选取市值最小的30只股票作为备选 capital_base = 1000000 # 起始资金 freq = 'd' # 策略类型,'d'表示日间策略使用日线回测,'m'表示日内策略使用分钟线回测 refresh_rate = 5 # 调仓频率,表示执行handle_data的时间间隔,若freq = 'd'时间间隔的单位为交易日,若freq = 'm'时间间隔为分钟 def initialize(account): # 初始化虚拟账户状态 pass def handle_data(account): # 每个交易日的买入卖出指令 N = 15 # 计算TR时的N M = 45 # 计算MATRIX时的M length_of_data = 3*N+M +10 # 取closeprice的天数,为了足够计算MATRIX、TRIX all_close_prices = account.get_attribute_history('closePrice', length_of_data) # 获取历史closePrice数据 buy_list = [] # 备选买入清单 sell_list = [] # 卖出清单 for stk in account.universe: prices = all_close_prices[stk] if prices is None: continue try: TRIX = talib.TRIX(prices,timeperiod=N) # 计算TRIX MATRIX = talib.MA(TRIX,M,0) # 机选MATRIX except: continue # 买入卖出判断 if (TRIX[-1]-MATRIX[-1]) > 0 and (TRIX[-5]-MATRIX[-5]) < 0: # 认为TRIX线向上突破TRMA线 金叉 buy_list.append(stk) elif (TRIX[-1]-MATRIX[-1]) < 0 and (TRIX[-5]-MATRIX[-5]) > 0: # 认为TRIX线在高位向下突破TRMA线 死叉 sell_list.append(stk) hold = [] buy = [] # 最终买入清单 # 买入卖出 for stk in account.valid_secpos: # sell_list卖出 if stk in sell_list: order_to(stk, 0) # 其余继续持股 else: hold.append(stk) buy = hold for stk in buy_list: # 若buy_list中股票有未买入的,加入 if stk not in hold: buy.append(stk) if len(buy) > 0: # 等仓位买入 amout = account.referencePortfolioValue/len(buy) # 每只股票买入数量 for stk in buy: num = int(amout/account.referencePrice[stk] / 100.0) * 100 order_to(stk, num) return

 

关于N、M的选取

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

import pandas as pd import numpy as np start = '2011-08-01' # 回测起始时间 end = '2016-04-17' # 回测结束时间 benchmark = 'HS300' # 策略参考标准 universe = StockScreener(Factor.LCAP.nsmall(30)) # 因子选股,选取市值最小的30只股票作为备选 capital_base = 1000000 # 起始资金 freq = 'd' # 策略类型,'d'表示日间策略使用日线回测,'m'表示日内策略使用分钟线回测 refresh_rate = 5 # 调仓频率,表示执行handle_data的时间间隔,若freq = 'd'时间间隔的单位为交易日,若freq = 'm'时间间隔为分钟 def initialize(account): # 初始化虚拟账户状态 pass def handle_data(account): # 每个交易日的买入卖出指令 N = 12 # 计算TR时的N M = 9 # 计算MATRIX时的M length_of_data = 3*N+M +10 # 取closeprice的天数,为了足够计算MATRIX、TRIX all_close_prices = account.get_attribute_history('closePrice', length_of_data) # 获取历史closePrice数据 buy_list = [] # 备选买入清单 sell_list = [] # 卖出清单 for stk in account.universe: prices = all_close_prices[stk] if prices is None: continue try: TRIX = talib.TRIX(prices,timeperiod=N) # 计算TRIX MATRIX = talib.MA(TRIX,M,0) # 机选MATRIX except: continue # 买入卖出判断 if (TRIX[-1]-MATRIX[-1]) > 0 and (TRIX[-5]-MATRIX[-5]) < 0: # 认为TRIX线向上突破TRMA线 金叉 buy_list.append(stk) elif (TRIX[-1]-MATRIX[-1]) < 0 and (TRIX[-5]-MATRIX[-5]) > 0: # 认为TRIX线在高位向下突破TRMA线 死叉 sell_list.append(stk) hold = [] buy = [] # 最终买入清单 # 买入卖出 for stk in account.valid_secpos: # sell_list卖出 if stk in sell_list: order_to(stk, 0) # 其余继续持股 else: hold.append(stk) buy = hold for stk in buy_list: # 若buy_list中股票有未买入的,加入 if stk not in hold: buy.append(stk) if len(buy) > 0: # 等仓位买入 amout = account.referencePortfolioValue/len(buy) # 每只股票买入数量 for stk in buy: num = int(amout/account.referencePrice[stk] / 100.0) * 100 order_to(stk, num) return

import pandas as pd import numpy as np start = '2011-08-01' # 回测起始时间 end = '2016-04-17' # 回测结束时间 benchmark = 'HS300' # 策略参考标准 universe = StockScreener(Factor.LCAP.nsmall(30)) # 因子选股,选取市值最小的30只股票作为备选 capital_base = 1000000 # 起始资金 freq = 'd' # 策略类型,'d'表示日间策略使用日线回测,'m'表示日内策略使用分钟线回测 refresh_rate = 5 # 调仓频率,表示执行handle_data的时间间隔,若freq = 'd'时间间隔的单位为交易日,若freq = 'm'时间间隔为分钟 def initialize(account): # 初始化虚拟账户状态 pass def handle_data(account): # 每个交易日的买入卖出指令 N = 12 # 计算TR时的N M = 72 # 计算MATRIX时的M length_of_data = 3*N+M +10 # 取closeprice的天数,为了足够计算MATRIX、TRIX all_close_prices = account.get_attribute_history('closePrice', length_of_data) # 获取历史closePrice数据 buy_list = [] # 备选买入清单 sell_list = [] # 卖出清单 for stk in account.universe: prices = all_close_prices[stk] if prices is None: continue try: TRIX = talib.TRIX(prices,timeperiod=N) # 计算TRIX MATRIX = talib.MA(TRIX,M,0) # 机选MATRIX except: continue # 买入卖出判断 if (TRIX[-1]-MATRIX[-1]) > 0 and (TRIX[-5]-MATRIX[-5]) < 0: # 认为TRIX线向上突破TRMA线 金叉 buy_list.append(stk) elif (TRIX[-1]-MATRIX[-1]) < 0 and (TRIX[-5]-MATRIX[-5]) > 0: # 认为TRIX线在高位向下突破TRMA线 死叉 sell_list.append(stk) hold = [] buy = [] # 最终买入清单 # 买入卖出 for stk in account.valid_secpos: # sell_list卖出 if stk in sell_list: order_to(stk, 0) # 其余继续持股 else: hold.append(stk) buy = hold for stk in buy_list: # 若buy_list中股票有未买入的,加入 if stk not in hold: buy.append(stk) if len(buy) > 0: # 等仓位买入 amout = account.referencePortfolioValue/len(buy) # 每只股票买入数量 for stk in buy: num = int(amout/account.referencePrice[stk] / 100.0) * 100 order_to(stk, num) return

小结

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

今年的鞋市到底有多疯狂,大家的感受一定很深,去年破产的款式一路飞涨,今年新出的款式价格节节攀升,让你持币观望犹豫不决的鞋款终于也不再让你犹豫不定了,因为...我们发现,过了一个年,真的再也买不起了...

屈指一数,今年价格涨幅高于楼价涨幅的鞋款多不胜数,去年价格在2000元左右的Air Jordan 1黑红脚趾现在市价大约为3000元以上,今年春节前价格在2000元左右的Air Force 1白丝绸价格直接翻了一番到达了4000元的水平,而第一套The Ten系列的价格几乎全线翻倍,以OFFWHITE x Air Jordan 1为首,价格从去年的一万元出头直接突破两万!说到这里,你知道如今的鞋市到底有多恐怖了吧。

小编潜伏各大球鞋APP、球鞋论坛、微信群的时间也不算短了,在这些日子里,小编确实从中总结出一些值得深思的现象,至于这种现象到底是好是坏,就交给大家评判吧!或者从这些现象里,大家可以稍微揣测得到,如今大部分球鞋玩家,他们在买鞋的时候,到底买的是什么。

今年的一些鞋款在发售预热期的时候,小编收到的反馈普遍都说丑丑丑,结果发售后依然抢成狗,正如铁骨铮铮王境泽当初所说的一句“我就是饿死,死外边,跳下去,不会吃你们一粒米饭!-真香!”

随便举个例,就比如两周的OFFWHITE x Air Force 1,在发售之前大家都说这两个配色不咋地,绿色太亮了,黑色太普通,不想要,结果抽签当日抽到的欣喜若狂,没抽到的怨声载道,这其中不乏之前讨伐过这款鞋的同志。那么问题来了,售前嫌人丑,发售抢成狗,到底是为啥呢?

小编举个简单的例子,不一定对,大家可以稍作评判。

刚刚发售完不久的FOG联名Nike Air Skylon 2,从设计上来看,用料稍微与原版有点小区别,而配色确实也并不出彩,唯一能够证明它身份的,也许就是鞋后跟那个并不显眼的”Fear of God”英文刺绣了。

我们再来看看原版,用料与联名版本区别不大,而色彩的呈现甚至比联名版本更加出色,辨识度也更高,性能也并无二致。而原版的市场价在400元左右,联名版本则在1200元左右浮动,相差200%。

但是很显然,联名版本的热度是更高的,买家入手的欲望也更高。同样的一款鞋,小编潜伏在群里,他们对联名版本的评价也是相当高,例如“做工精细(这几年来Nike的鞋很少会收到这样的评价)、用料厚道、设计感很好”诸如此类,而原版的Air Skylon 2,在平时基本很少很少会被提起,也从来不会有人晒上脚。

第二个问题来了,贵的,是否就一定等于好的呢?又或者,是有另外的心理在作祟?

关于这一点,小编可以举一个最直接的例子,那就是今年的Yeezy 350 V2纯白。对于球鞋市场稍有关注的人都知道,Yeezy 350 V2,在最近两年就是牛逼的代名词,历代发售从来都是万人空巷,而这种局面,在今年下半年被打破。

Yeezy 350 V2纯白迎来了历史上,乃至是所有Yeezy系列历史上最大规模的重新发售,发售数量达到了数十万的级别,而曾经被视为宝贝的Yeezy 350 V2就此跌落神坛,纯白首当其冲迎来了一波小折扣,虽然一段时间后价格有所恢复,但是可以看到的是,曾经万人疯抢的Yeezy 350 V2不复当年之勇,价格稳定在原价到加价200元左右。

Yeezy 700后来的发售也采用了超大货量的模式,这导致曾经价格在4000元左右的初代Yeezy 700价格回落到3000元以下,第二款配色则直接破产迎来折扣,甚至沦落到货架上随便买,这在半年之前简直是不可想象的。而更加奇怪的是,曾经人人都希望可以原价入手的Yeezy系列,在经历超大规模发售后,居然折扣价也卖不掉,实在令人大跌眼镜。

在年末发售的Air Jordan 11康扣配色,同样遭遇了这样的局面,要知道,Air Jordan 11康扣在发售当年可是圣物一般的存在,几年前也是一片求复刻的声音。后来传出消息,Air Jordan 11康扣将会以大货量的发售形式归来,结果一下子又变得不香了!

“曾经的AJ11康扣是我梦想中的宝物,但是我想这一次我不会买,因为我家附近的Nike店都在卖!”这是小编亲眼看到的一个评论,简直赤裸裸地道出了如今很多球鞋玩家的心理-不玩对的,只玩贵的。

如今的球鞋市场实在火爆得有点畸形,大量玩家入场,这里面包括了真正的球鞋爱好者,他们有着最纯粹的情怀与梦想,对球鞋的热爱更加真切,同时,球鞋市场的火爆也引来了很多想从中捞一笔的商人,甚至在人均贩子的现在,只要你会用智能手机,你也能过一把贩子瘾!另外,也引来了一些以球鞋获得优越感的玩家,正如小编说到的,对于他们来说,普通款没有任何意义,只有高价、稀有的鞋款才能获得他们的注意。

至于各位读者朋友,你们又是否清楚,你们在买鞋的时候,到底买的是什么呢?

https://www.haizuanshi.com

上一篇:水利股票有哪些(水利股票什么时候会涨)

下一篇:深港通概念股有哪些(深港通百科)

相关推荐

返回顶部