(一):Pine语言和优宽量化平台介绍
量化交易及其应用
量化投资是指以获取稳定收益为目的,通过量化方法和计算机程序发出指令的交易方式。它主要是通过数学模型把投资理念表达出来,然后由计算机生成交易策略。量化投资是多种技术的结合。在策略制定过程中,通常利用数学、几何、心理学甚至仿生学的知识来实现择时交易、统计套利和风险规避。量化交易在一定程度上克服了人性的弱点,使人更加理性。越来越多的学者认为,量化交易将是未来处理金融市场最重要的方式。
量化交易作为科学与机器的结合的产物, 正在改变着现代金融市场的格局。 目前,中国的量化投资领域仍处于起步阶段。目前,在欧美等发达经济体资本市场,机构量化交易占比已达70%左右。而在境内市场上,量化交易的行业规模已超万亿元,有研究认为,量化交易在A股市场的成交占比达两成左右。麦肯锡公司于2021年估计,以个人金融资产计算,中国已成为全球第二大财富管理市场,作为全球增长最快的资产管理市场,中国的量化交易机构市场有很大的成长空间。
作为个体投资者而言,“量化交易” 是高端大气、 一夜暴富的代名词。量化交易最大的引力在于“躺着赚钱”,通过设定好的程序语言和服务器,构建出“完美无缺” 的交易策略,实现财富的复合增长,这无疑是广大量化入门爱好者终极梦想。然而,在一定程度上,量化交易已经被神话了。如果小伙伴们不想被“量化的镰刀”割到,不妨从今天开始,做一个“量化人儿”,也许没有你想象的那么难!
中国期货市场的量化交易
期货作为相对于股票和债券的风险最大的金融产品,其收益率呈现最高水平。期货合约由于其特殊的 T+0 交易机制、杠杆交易、较强的流动性、卖空交易等特点,受到投资者的追捧,将其广泛用于投机、套利和对冲交易之中。近年来,中国期货市场的规模迅速扩大扩张中。期货市场目前发展至72个品种,投资者也在逐渐壮大,对投资的要求逐渐提高。
在期货市场中,绝大部分散户投资者目前仍采用以自身经验或基本面分析为主的主观逻辑上的人工手动交易,随着判断层面因素的复杂程度和数量繁多,宏观经济走势、热点新闻、 K 线趋势、个人经验等各方面因素都影响着投资者的价值判断,甚至无法摆脱人性的弱点,这种人工操作的情绪化问题使得投资者不能在重大损失面前理性出逃,也不能在重大利好之际见好就收。
量化交易在这个信息迅速发展的时代对人性的情绪管理、机器学习和相关因素设计等方面独具有绝对的优势。量化研究是在大数据的背景之下,通过对历史数据的归纳和分析,利用统计建模等方法不断优化改进,寻求期货市场长期可盈利的交易模式,相信这是每一位Quanter的挑战目标。
量化交易劝退(打气)指南
量化投资是一门复合型学问,涉及到数学、统计、金融、编程等多方面知识,其中编程能力是量化的门槛。因为量化是个很大的领域,不同方向有着不同的编程要求,所以不能一概而论。但最基本的需要在matlab,python,r里面至少会一门。如果要做高频方面的话,还要会C++。然而,这些语言需要极大的学习成本和时间成本,不少初入门者因此劝退。
然而,量化的门槛真的是高不可攀吗?非也非也,正如懒惰是人类第一的生产力,我们金融弄潮儿也有自己的跃龙门方式,下面将为大家介绍量化入门的重磅利器:Pine语言和优宽量化平台。
TradingviewPine语言
Pine语言,起源于TradingView平台。TradingView作为国外最热门的一个金融平台,是一个价格图表分析网站,这里有股票,外汇,数字货币等等,很多数字货币交易所都内嵌了它的图表。TradingView有一个很好的语言载体:Pine脚本,它允许任何人开发自己的量化策略应用到金融产品中并进行收益的回测展示。
Pine语言,作为一门专门为交易而生的语言,它对很多指标函数都进行了封装。相对于Python语言的复杂(代码量大)和My语言的死板(不能进行复杂的策略),它更加的简洁和灵活。所以在实现轻量化代码的过程中,降低了普通交易者的量化门槛。
优宽量化平台
Tradingview作为一个国外的交易平台,通常需要我们“跋山涉水”去使用它;在同时,Tradingview上的开源策略数量数目繁多且更新很快,很多优秀的策略、思路、指标不能实盘着实令人遗憾。
作为致力于把量化交易技术普及给众多交易者的优宽平台,目前已支持Pine语言编写策略,支持回测、实盘运行Pine语言策略,兼容Pine语言的较低版本。优宽不仅支持了Pine语言,同时也支持Pine语言强大的画图功能。优宽平台上的各项功能、丰富实用的工具、高效便捷的管理,也进一步增强了Pine策略(脚本)的实用性。优宽基于对Pine语言的兼容,同时也对Pine语言进行了一定程度的扩展、优化、裁剪。
优宽量化平台功能
优宽量化工具(https://youquant.com)对小白友好, 即便你是零基础, 也可以根据里面的工具体验量化的魅力。 该工具是面向高频交易设计, 在性能和安全上有严苛的要求。 支持高频策略、 套利策略、 趋势策略。 并且它集成了策略开发、 测试、 优化、 模拟、 实盘交易的完整流程。 在优宽量化交易平台,在初期你可以模拟、围观、回测大佬的成熟策略,在后期可以学习、编写、分享、买卖自己的量化策略,实现从量化模仿小白到市场实操大佬的转变。
优宽平台支持商品期货与易盛外盘期货, 也支持腾讯富途证券美股、港股、A股以及中泰证券XTP等实盘量化交易。优宽适用于量化交易初学者,即使无基础也可以快速入门,平台功能强大灵活,可以满足不同层次量化爱好者需要。
(二):优宽量化平台使用指南(1)
Hello, 大家好,在上节课的基础上,我们了解了量化交易的入门利器-Pine语言和优宽量化平台,相信很多小伙伴们已经迫不及待的大展身手了,现在,我们来开始吧。
本节课我将带领大家先了解一下优宽量化交易平台,作为一个宝藏网站,这里有很多资源可以免费去探索!
作为一个专业的量化社区,在这里你可以学习编写量化交易策略,也可以免费在线回撤和使用模拟盘交易,还可以围观大佬们的实盘和进行手动交易。而且不同于其他交易软件需要下载相应的客户端,优宽平台用户可以依托于云端服务器或者个人设备所部署的托管者程序,直接和优宽网站进行通信,传递日志,访问交易所获取行情和实盘交易。当然,如果各位小伙伴们成长成了大佬,也可以将自己的策略进行分享和买卖。
我相信各位小伙伴们不乏卧虎藏龙之辈,因此优宽平台不仅支持支持简单的Pine语言,可视化编程语言及文华财经的My语言等,也支持JavaScript、Python以及c++高级语言,也同时支持免费仿真交易,极大降低了各位小伙伴们的学习探索成本。在安全性上,大家大可放心,优宽量化交易平台用户开发的策略也仅账户持有者自己可见,除非用户自己公开分享策略,平台不会保存用户密码明文,从而极大提高了数据的安全性。
注册登录
首先,我们需要进行优宽量化交易平台的账号注册。首先我们进入优宽量化交易平台官网,然后看到主界面有一个右上方有一个注册按钮,点击它进入注册,在这里我们需要提前准备一个常用的邮箱,然后这边就是起一个用户名,然后设置一个密码,然后点击注册,这里会有一个授权码,我们要去设置的邮箱获取注册认证,完成平台注册之后,我们一定要把平台的密码保存好,因为后续我们在部署托管者的时候,还需要平台的登录密码进行验证。
第一个策略
注册成功之后,我们登录去熟悉一下优宽量化交易平台的界面内容及功能。首先我们看顶端栏的介绍,控制中心就是平台用户的操作界面,策略,就是策略广场 (https://www.youquant.com/square),平台用户公开和出售策略就在这里,可以点击相应的标签筛选不同种类的策略。
在这里策略广场中的策略都是公开的,各位小伙伴们可以挑选感兴趣的策略,验证其有效性。我们随机点击一个策略进入,在这里你可以看到策略的原理解释,参数设置和策略的源码,然后点击“完整复制并进行在线回测”,可以进入策略编辑界面,然后点击“开始回测”,添加平台,再点击“开始回测”,随后就可以看到模拟账户的收益,行情数据,收益概览和具体的日志信息,这里包括了具体的开仓和平仓操作。是不是很简单,大佬的起点都是从模仿开始的。大家可以挑选不同的策略,应用于自己感兴趣的证券或者期货品种进行在线回测测试,验证策略的有效性。
实盘围观
让我们回到上方顶端栏围观板块(https://www.youquant.com/live),小伙伴们会担心策略的实盘结果,在这里你就可以进行大佬的正在运行的实盘检阅,点击进入不同的策略,可以看到一直在进行的策略的运行结果和收益状况。
文库讲解
小伙伴在入门的时候肯定会有很多问题,因此可以点击后面的三个板块寻找解答。文库(https://www.youquant.com/digest)主要是官方出品的一些精品文章,包括各种策略的逻辑讲解,以及最新的行业前沿知识。
社区讲解
社区(https://www.youquant.com/bbs)主要是用户平台用户发帖提问交流的一些平台,在这里也有优宽平台使用的讲解和量化交易的教程,当然如果有一些问题实在找不到答案的话,你可以创建主题进行提交,各位热心大佬会帮你解决。
Api文档
API文档主要是一些用平台编写策略所需的API介绍(https://www.youquant.com/api),包括还有如果有一些问题,我们都可以在里面去搜索查询。
相信各位小伙伴们最感兴趣的还是如何通过优宽平台进行自己的实盘交易,下一节课我们将讲解如何搭建自己的量化Robot进行实时的交易。下节课再见!
(三):优宽量化平台使用指南(2)
大家好,上节课我们了解了优宽平台的顶端栏模块,这里有很多的量化交易策略和平台操作资源,大家可以熟悉下,在实盘操作有问题的时候可以及时查询,找到答案所在。
控制中心主页
这节课我们来熟悉下用户控制中心,这里就是你组装你的量化系统引擎的地方。首先,我们看到的是控制中心主页,概览可以快速浏览你的实盘配置,账单这里可以清楚显示你的账户金额信息,快捷方式提供了策略代码编写,数据查询和探索的快捷方式,最后是消息中心,当账户或者策略实盘模拟出现问题的时候,这里就会及时提醒。
一个完整的量化交易由三个部分组成,第一个部分是你的策略,第二个部分是你的期货交易账户,也就是交易所,第三个部分是策略托管的robot,就是这个托管者。来让我们进行逐一的设置。
策略库
策略库,就是你存放量化武器弹药的地方。通过点击新建策略,你就可以进行策略编写,在这里选择不同的语言进行代码的编写,笔记,描述,手册可以记录你的策略的灵感来源,执行逻辑和使用程序,方便策略的复盘和更新。当然,身为一个初学者,你可以直接运用策略库里的成熟的代码进行实盘模拟和回测。
当你编写好策略后,点击操作项,这里由分享和出租两个选项,分享就是分享此策略源码,分为内部分享和外部分享,内部分享会让你选择分享有效期、分享次数之后会生成该策略的复制页面地址和复制码。可以分发给指定的优宽平台用户,需求该策略的用户只需使用复制页面地址链接,登录复制页面后输入复制码即可获取该策略,获取后策略自动会在策略库中出现。
出租分为公开出售:点击「出租」按钮后会弹出对话框,可以选择「公开出售」。策略即可申请上架(需要通过审核)。内部出售:点击「出租」按钮后会弹出对话框,可以选择「内部出售」。选择使用天数、最多并发、注册码个数之后会生成该策略的注册页面地址和注册码。可以分发给指定的优宽平台用户,需求该策略的用户只需使用注册页面地址链接,登录注册页面后输入注册码即可获取策略的使用权。策略也会出现在策略库中,不过只有回测、实盘使用权,看不到策略源码等信息。重要提示,在创建、分发策略注册码时请务必仔细确认是「注册码」还是「复制码」。以免误将策略分享出去。
策略库也有分组管理功能,通过设置不同的分组,将你的策略直接拖动放入分组进行分门别类,方便以后观看。
交易所设置
策略需要和交易所对接才能进行期货的买卖。点击交易所,点击添加交易所,进行交易所的账号配置。点击期货CTP协议,当然这里也许期货外盘和证券协议,期货公司这里选择你的开户公司,身为一个量化初学者,你可以选择选择上期模拟(SimNow)账号。上期模拟需要你登陆(https://www.simnow.com.cn/),进行注册账号,注册完成后需要一次密码修改,这样才能使用。然后点击投资者登录,这里有你的investorId账号,请记录下来。填写到交易账号这里,输入交易密码,其它的信息都会自动填写完成。点击“添加交易所”,这样你的期货账户就设置成功了,当然你也可以添加真实的账户,优宽平台和宏源,国泰君安公司都有良好的合作关系,通过平台进行账户开户可享最优手续费。期货账户开户以后,进行量化交易需要进行看穿式认证。社区这里有篇文章大家可以看下( https://www.youquant.com/bbs-topic/8436)。
托管者设置
量化引擎的最后一个部分就是托管者。托管者需要在交易时间段,持续稳定的运行你的量化策略进行交易。点击进入,点击部署托管者,这里可以根据你的需要首先选择托管者服务器,你可以选择部署在云端,需要租用服务器,或者本地。首先根据你的需要linux/mac/Windows,进行云端部署或者本地部署,云端部署这里有详细的参考手册可以看( https://www.youquant.com/bbs-topic/8290)。下面以linus为例,首先需要租用一个服务器,一个轻量的价格比较低廉的服务器就可以,服务器可以选择centos系统安装完成后,
- 第一步,在服务器输入
wget https://www.youquant.com/dist/robot_linux_amd64.tar.gz
下载robot; - 第二步,输入
tar -xzvf robot_linux_amd64.tar.gz
进行robot解压; - 第三步,测试托管者运行
chmod +x robot
./robot -s node.youquant.com/xxxxxx -p your优宽password
xxxxxx为node,-p后面添加密码; - 第四步,当出现
Login OK
就代表云端托管者设置成功。
通过身为初学者,你可以选择部署在本地。windows这里以64位界面版进行操作,点击下载,进行解压安装,点击打开robot,输入地址,和你的优宽平台登录密码,当显示login Ok的时候,你的托管者已经部署好了。在策略运行的期间,请确保这个窗口不要关闭。
优宽平台同时也有一键租用托管者入口,大家可以根据自己的需要进行选择。
创建实盘
现在让我们来创建一个实盘看看,点击实盘,点击创建实盘,命名你的策略名称,期货模拟test,托管主机选择你刚才的托管者,选择运行策略,点击交易平台,点击创建实盘,进入。可以看到你的日志信息,当显示连接成功,你的实盘就已经运行起来了。温馨提示,当你的实盘出现login error的时候,你可以检查下是否是交易所设置的问题,比如交易账户和交易密码是否填写有问题。实盘运行起来后,点击“交易终端”,你就可以看到实盘运行情况。优宽量化交易平台提供模块化、定制化的交易终端页面。可以自由添加各种数据模块、交易功能模块,甚至可以自己编写代码开发模块(交易终端插件)。凭借着高度灵活自由的使用方式也极大方便了手动交易、半程序化交易的用户。交易终端页面上的各种模块均可以拖动、缩放,可以修改模块绑定的交易对、交易所等设置,可以添加多个同类型的模块。如果你想通过手机或邮箱接受实盘运行错误,你可以选择监控,你的手机和邮箱可以及时接到报警错误。当然,非交易时段内你也可以停止实盘,直接点击停止就好,后续也可以重启。当然实盘运行的时候,是需要付费的,0.125一个小时,每天交易时间段(八个小时)不间断运行每月花费30元左右,加上每月的服务器租用费用,一个量化交易系统搭建的费用在50元左右。在实盘运行过程中,有任何问题你都可以通过“发起工单”向平台工作人员发起提问获得及时的回复。
对比于其它量化平台,优宽省略了复杂的API索取和调用,托管者的设置也更为简便,策略的编写具有更多成熟的框架参考,因此使用起来对量化爱好入门者是十分贴心的。如果你感兴趣的话,可以立即动手尝试。当然如果你有任何的问题,都可以在下方评论板块提出问题,我也将及时答复。我们下节课再见!
(四):Pine语言编辑器窗口和代码结构
大家好,从今天起,我们就要学习一门新的语言-Pine语言。如果你是java或者python用户,Pine语言的灵活简洁一定会让你的量化入门之路格外轻松。在保留完整语言和交易逻辑的基础上,Pine语言对很多交易的指标和函数都进行了封装,不需要让你重新import packages,轻松的指标的计算,清晰的图表展示,简洁的交易逻辑的判断,直到最后和市场对接的交易指令,可以这样说,Pine语言是一门专门为交易而生的语言。
如果你是一个量化新手,犹豫在Pine语言和文化财经麦语言之间,害怕高额的时间成本和学习成本的无用功,这里也请不用担心。相对于麦语言,Pine语言能实现的量化策略更多也更为灵活,并且在掌握Pine语言交易逻辑的基础上,对于麦语言的入门会起到事半功倍的效果。
Pine语言作为国外平台tradingview的交易脚本语言,需要我们跋山涉水的翻墙使用,而庆幸的是,优宽量化交易平台对Pine语言进行了完美的移植,在保留Pine语言简洁优雅属性的同时,优宽平台又对Pine语言进行了进一步的封装和改动,让量化爱好入门者使用起来更为轻松简便。
当然,熟练掌握一门完整的语言需要大家长久的努力,很高兴成为大家Pine语言教学的领路人,也许我们都习惯于害怕未知山峰的高度和自己残余勇气的不足,但请相信,努力终会有回报。让我们开始今天的课程吧!
编辑器窗口
本节课的课程是Pine语言的代码编辑器窗口和代码结构的讲解。作为一门计算机语言,一千个人都一千个钟爱的哈姆雷特编辑器。幸好,优宽平台解决了这个问题。不需要部署任何软件和服务器,优宽提供了在线的回测系统,通过丰富的图表和数据统计功能,及时的对量化策略进行收益的回测展示,减轻了大家的学习压力和理解难度。
首先我们登录了优宽量化交易平台,然后可以在控制中心快捷方式点击“新建策略”。这样我们进入了策略编辑页面,语言我们选择Pinescript,界面中显示了一个简单的例子,通过定义策略名称,我们就可以创建这个策略。
在编辑器的右上角,有一些按钮可以方便我们进行策略编辑:
-
- “保存”,“下载源码”,“导入/导出源码”可以帮助我们迅速的进行策略的导入导出和保存。
-
- “皮肤-eclipse”提供了一系列的皮肤选项,大家可以根据自己的编辑习惯进行选择;
-
- “开启Vim模式”提供了Vim的开启和关闭按钮,大家也可以根据自己的代码书写习惯进行开关。
脚本结构
策略编辑页面是编写这个派语言策略代码的地方,我们可以先删掉这个默认的代码。
Tv上Pine中代码遵循的一般结构:
<version>
<declaration_statement>
<code>
优宽对tv上的pine语言的脚本结构做了更为简洁的改进。在tv中,我们需要在首行指定脚本的版本(//@version=5),在优宽平台上不用,因为优宽平台的默认脚本即是version 5,当然你也可以指定其它版本的脚本(//@version=4),优宽也支持运行。
对于第二部分declaration statement,声明语句确定脚本的类型,这又决定了其中允许哪些内容,以及如何使用和执行。设置脚本的关键属性,比如它的名称,当它被添加到图表中时,它将出现在哪里,它所显示的数值的精度和格式,以及管理其运行时行为的某些数值,比如它将在图表中显示的最大绘图对象数量。对于策略,属性包括控制回测的参数,如初始资本、佣金、滑点等。而这些优宽平台封装在了Pine语言交易类库里,点击开始回测,通过设置不同的参数,你就可以对交易的参数进行设置,比如交易的设置,期货品种的选项,实盘的选项和现货的交易等,这些参数我将在后续Pine语言交易逻辑里面进行详细的讲解。
通过优宽对于Pine语言的封装,因此Pine语言不要求一个策略代码必须包含indicator()或者strategy()声明语句。因为优宽量化上本身就是可以实盘,因此也不区分这个画图和实盘的声明。
第三部分是代码板块,脚本中不是注释或编译器指令的行是语句,它实现了脚本的算法。脚本语句可以分为
-
- 变量声明
-
- 变量的重新赋值
-
- 函数声明
-
- 内置函数调用,用户定义的函数调用
-
- if,for,while或switch等结构
单行语句
有些语句可以用一行来表达,比如大多数变量声明、只包含一个函数调用的行或单行函数声明。多个单行语句可以通过使用逗号(,)作为分隔符在一行中串联起来。
var a = close[1]
var a = close[1], var b = open[1]
局部块语句
其他的,像结构,总是需要多行,因为它们需要一个局部的块。脚本的全局范围内的语句(即不属于局部块的部分)不能以空格或制表符(tab键)开始。它们的第一个字符也必须是该行的第一个字符。在行的第一个位置开始的行,根据定义成为脚本的全局范围的一部分。结构或多行函数声明总是需要一个local block。一个本地块必须缩进一个制表符或四个空格(否则,会被解析为上一行的串联代码,即被判定为上一行代码的连续内容),每个局部块定义了一个不同的局部范围。
plotColor = if barIsUp() // 变量声明 (全局范围)
color.green // 本地块 (本地范围)
else
color.red // 本地块 (本地范围)
换行代码
长行可以被分割在多行上,或被 "包裹"起来。被包裹的行必须缩进任何数量的空格,只要它不是4的倍数(这些边界用于缩进局部块)。
a = open + high + low + close
可以被包装成(注意每行缩进的空格数量都不是4的倍数):
a = open +
high +
low +
close
注释
一行中可以包含注释,也可以只是注释。
[macdLine, signalLine, histLine] = ta.macd(close, 12, 26, 9) // 计算MACD指标
/*
plot函数在图表上画出指标线
*/
好了,本节课Pine语言的编辑器介绍和脚本结构介绍就到此为此了,我们下节课再见!
(五):Pine语言类型系统讲解(1):Forms
大家好,上节课我们了解了在优宽平台上Pine语言的脚本结构,本节课我们的内容集中于Pine语言的类型系统讲解。Pine Script的类型系统很重要,因为它决定了在调用Pine Script函数时可以使用什么样的值,这是在Pine语言中做任何事情的必要条件。 虽然可以在不了解类型系统的情况下编写非常简单的脚本, 然而对它的合理理解对于实现Pine语言的熟练应用是必要的, 深入了解其微妙之处将使您能够充分发挥 Pine语言的潜力。
吸收了面向对象语言的优点,Pine语言的类型系统的定义和使用也是清晰明了。Pine语言是一门专门为交易而生的语言,因此Pine语言类型系统的设定也与交易逻辑的设定一致。交易是流动的,而策略就是使用参数构建的算法去捕捉交易的流动趋势. 因此不仅需要type确定参数的性质,还需要form描述一个参数定义的时间。Pine语言使用form-type对来描述所有的值。通过不同的参数时间(form)和性质(type)的确定,一个优秀的策略尝试着去拟合千变万化的金融市场,发现本质规律,进而预见可能性的收益机会。
Forms
当Pine脚本被加载到图表上时,它在每个历史条上执行一次,使用每个条的可用OHLCV(开盘open、最高high、最低low、收盘close、成交量volumn)值。一旦脚本的执行到达数据集的最右边的条形,如果目前交易活跃,那么Pine 指标将在每次更新发生时执行一次,即价格或成交量变化。Pine 策略默认只在最右边的条形图收盘时执行,但它们也可以被配置为在每次更新时执行,就像指标那样。
所有的符号/时间框架对都有一个数据集包括有限数量的条形图。当你向左滚动图表以查看数据集的早期条形时,相应的条形会在图表上加载。当脚本第一次在图表上运行时,数据集中的所有条形图都是历史条形图,除了最右边的条形图,如果交易时段是活跃的。当交易进行时候在最右边的条形图上,它被称为实时条形图。当检测到价格或成交量的变化时,实时条就会更新。当实时条形图关闭时,它将成为一个失效的实时条形图,并打开一个新的实时条形图。
我们如今尝试的交易系统,是利用历史信息,运用算法寻找其中的规律搭建模型。利用大数据的优势,模型搭建完成之后,我们需要不断调整模型的参数,而输入的数据信息也是伴随着市场源源不断整体流动的,因此在模型设定初期,我们需要设定不同模型参数确定的时间。
Pine语言forms确定了变量的值是何时已知的。共分四种类型:
-
“const”表示编译时已知的值(向图表添加指示符或将其保存在Pine脚本中时确定的)。
-
“input”表示输入时已知的值(当脚本的“设置/输入”选项卡中的值发生更改时设定)。
-
“simple”表示柱线 0 处已知的值(当脚本开始在图表的第一个历史柱上执行时设定)。
-
“series”表示每个柱上已知的值(在任何柱上执行脚本期间的任何时间设定)。
Forms按以下层次结构进行组织:const < simple < series < input,例如,其中“const”被认为是比“input”弱的形式,而“series”比"simple"强。 表达式的结果始终是表达式计算中使用的最强形式。此外,一旦变量获得更强的形式,该状态是不可逆的; 它永远无法转换回较弱的形式。因此,“series形式的变量永远不能转换回“simple"形式,以便与需要该形式参数的函数一起使用。
请注意,在所有这些形式中,只有“series”形式允许值在脚本执行期间动态更改图表历史记录的每个柱线。一旦脚本开始执行,“const”、“input”或“simple”形式的变量就不能更改。
Const
“Const”形式的值必须在编译时知道,然后脚本才能访问与其正在运行的交易品种/时间帧信息相关的任何信息。 当您在Pine脚本编辑器中保存脚本™时,就会进行编译,这甚至不需要它已经在图表上运行。“Const”变量在脚本执行期间不能更改。
整形 int: 1, -1, 42
浮点型 float: 1., 1.0, 3.14, 6.02E-23, 3e8
布尔型 bool: true, false
字符 string: "优宽"
颜色 color: #FF55C6, #FF55C6ff
Input
当确定通过函数初始化的值时,“输入”形式的值是已知的。 这些函数确定脚本用户可以在脚本的“设置/输入”选项卡中修改的值。 当这些值发生更改时,这总是会触发从图表历史的开头(柱线零)重新执行脚本, 因此,“input”形式的变量在脚本开始执行时始终是已知的,并且在脚本执行期间它们不会更改。凡是需要“input”形式的地方,也可以使用“const”。
在优宽上的input函数和在Trading View上的有些不同,不过该函数都是作为策略参数的赋值输入使用。input函数的几个主要参数如下所示:
- defval :作为input函数设置的策略参数选项的默认值,支持Pine语言的内置变量、数值、字符串
- title :策略在实盘/回测的策略界面上显示的参数名称。
- tooltip :策略参数的提示信息,当鼠标悬停在策略参数上会显示出这个参数设置的文本信息。
- group :策略参数分组名称,可以给参数分组。
在声明变量时给变量赋值,经常使用的就是input函数,在优宽上input函数会在优宽策略界面自动画出用于设置策略参数的控件。优宽上支持的控件目前有数值输入框、文本输入框、下拉框、布尔值勾选。并且可以设置策略参数分组、设置参数的提示文本信息等功能。下面我们来通过一个例子详细说明input函数在优宽上的使用:
param1 = input(10, title="参数1名称", tooltip="参数1的描述信息", group="分组名称A")
param2 = input("close", title="参数2名称", tooltip="参数2的描述信息", group="分组名称A")
param3 = input(color.red, title="参数3名称", tooltip="参数3的描述信息", group="分组名称B")
param4 = input(close, title="参数4名称", tooltip="参数4的描述信息", group="分组名称B")
param5 = input(true, title="参数5名称", tooltip="参数5的描述信息", group="分组名称C")
ma = ta.ema(param4, param1)
plot(ma, title=param2, color=param3, overlay=param5)
Simple
只有当脚本在图表历史记录的第一个栏上开始执行时,“simple” forms的值才是已知的,并且在脚本执行过程中这些值永远不会改变。例如,syminfo.*等系列的内置变量都返回“simple”形式的结果,因为它们的值取决于图表的品种,只有在脚本对其执行时才能检测到。在需要“simple”格式的地方,也可以使用“const”或“input”格式。
runtime.log(syminfo.ticker)
runtime.log(timeframe.period)
runtime.error('stop')
Series
“Series ”形式的值提供了最大的灵活性,因为它们可以在任何柱上更改, 甚至在同一根柱线中多次,例如循环。 开盘价(open)、收盘价(close)、最高价(high)、时间(time)或交易量(volume)等内置变量是“series”形式, 就像使用它们计算的表达式的结果一样. “Series ”会改变柱线, 用于访问时间序列的过去值的 [] 历史引用运算符也是如此。凡是需要“series ”形式的地方,也可以使用其他三种形式代替。
strategy(overlay=true)
a = close + 100
b = close[1] + 100
plot(a, title='close', color=color.black)
plot(b, title='close[1]', color=color.blue)
本节课,我们学习了Pine语言的类型系统的forms。可以发现,对比于其它多功能的语言(如python,c++等),Pine语言更加纯粹与精巧。因此对于Pine语言的学习,在除去死板的语法结构之外,我希望和大家一起探讨的是,对于整个市场和交易的理解,运用代码去尝试庖丁解牛,即将自身的交易逻辑和心得用Pine语言的形式进行交易的梳理和建构。我们下节课再见!
(六):Pine语言类型系统讲解(2):Types
大家好,上节课我们了解了Pine语言类型系统的Forms,Forms确定了Pine语言参数确定的时间,本节课我们将继续讲述Pine语言的类型系统之Types。Types确定参数的性质,types有基本类型(int, float, bool, color and string),还有特殊类型(array, na, 和元组tuples等)。本节课我们就这些常用的参数types和使用方法展开介绍。
优宽平台对Pine语言是进行了一定程度的封装,因此它和Tradingview上的Pine语言有所不同,请大家注意辨别。如果大家在优宽平台模仿tv的策略时,发生报错的情况下,大家可以查阅下https://www.youquant.com/bbs-topic/9326这本pine语言使用手册,检查一下优宽是否对某些函数进行了封装,然后对应修改下tv上的pine语言脚本。当然,在后续的时期,我们也会继续完善优宽上的Pine语言,使其更加适合量化爱好入门者使用。
首先我们讲解下Pine语言的基本type类型:
Int
整数值必须写成十进制,例如:
1
-1
750
bar_index、 time、 timenow等内置变量 都返回“int”类型的值。
Float
浮点数包含一个小数点,也可能包含符号e或者E (表示“乘以 10 的 X 次方”,其中 X 是符号之后的数字e),例如:
3.14159
-3.0
6.02e23 // 6.02 * 10^23
1.6e-19 // 1.6 * 10^-19
String
字符串文字可以用单引号或双引号括起来,单引号和双引号在功能上是等价的。你可以使用运算符“+”连接字符串。
A = "优宽"
B = '优宽'
//C = '优宽" //error
C = '优宽"'
D = A + B
runtime.log(A, B, C, D)
runtime.error('stop')
布尔值
只有两个文字表示bool值:
true // 真值
false // 假值
类型转换
Pine语言中有一个自动类型转换机制,可以将某些类型转换(或转换)为另一种类型。自动转换规则是:“int”🠆“float”🠆“bool”,这意味着当需要“float”时,可以使用“int”代替它,当需要“bool”值时,可以使用“int”或“float”值代替它。在优宽平台对Pine语言进行了一定程度的封装和改进,例如在tv上这段代码会报错,而在优宽平台,float/string类型会自动转换为int运行。
indicator("")
len = 10.0
len1 = '10'
s = ta.sma(close, len) // tv报错
s = ta.sma(close, len1) // tv报错
plot(s)
Color
颜色文字具有以下格式:#RRGGBB或#RRGGBBAA。其中:RR,GG和BB是红色、绿色和蓝色分量的值。
AA是颜色透明度(或alpha分量)的可选值,其中00是不可见,FF是完全可见的。
例子:
#000000 // black color
#FF0000 // red color
#00FF00 // green color
#FF000050 // 50% transparent red color
#FF000000 // completely transparent color
Pine Script还具有内置颜色常量,例如 color.green、 color.red、 color.orange、 color.blue (plot()和其他绘图函数中使用的默认颜色)等。使用内置颜色时,可以使用 color.new向它们添加透明度信息。
#FF000050 == color.new(#FF0000, 50) // 50% transparent red color
strategy(overlay=false)
p1 = plot(high)
p2 = plot(low)
fill(p1, p2, color=color.new(#FF0000, 80))
fill(p1, p2, color=#FF000080)
下面我们介绍一些特殊的type类型:
'na'值
在 Pine Script™ 中有一个名为na的特殊值,它是not available的首字母缩写词,表示表达式或变量的值未定义。它类似于Java中的null 或Python 中的None值。
Tuple值
元组是一组以逗号分隔的表达式,括在方括号中,当函数或局部块必须返回多个变量作为结果时可以使用它们。例如我们熟悉的MACD指标,就可以使用元组的形式,代表MACD的三个指标(MACD线、信号线和直方图线),下面我为大家画图展示下。
[macdLine, signalLine, histLine] = ta.macd(close, 12, 26, 9)
plot(macdLine, title = 'macdLine', color=color.blue)
plot(signalLine, title = 'signalLine', color=color.orange)
plot(histLine, title = 'histLine', color=color.red, style=plot.style_histogram)
当然,如果你只需要一个值,请使用像这样的占位符'_':
[_, signalLine, _] = ta.macd(close, 12, 26, 9)
plot(signalLine, color=color.orange)
Array
用于显式声明变量或参数的“阵列”类型的关键字。可以使用array.new_type,array.from函数创建阵列对象。Array对象总是“series”形式。 Array.new_int/float/bool/string(),第一个值确定重复的次数,第二个值确定重复的对象。Array.from()从调用函数时提供给函数的参数,推断出数组的大小及其元素的类型。
m1 = array.new_int(10,12)
m2 = array.new_string(10, '优宽')
m3 = array.from(2, 12, 20, 50, 100, 200, '优宽')
m4 = array.from(close > open, high != close)
runtime.log(m1, m2, m3, m4)
runtime.error('stop')
标识符
上述我们讲解了变量的类型结构form和type,通常情况下在变量设定以后,我们需要对变量进行命名。通俗的讲“标识符”是用来当做函数和变量的名称的(用于命名变量、函数)。函数在我们之后的教程中会了解到,我们首先学习一下“标识符”。
- 1、标识符必须以大写(A-Z)或小写(a-z)字母或下划线(_)开头,作为标识符的第一个字符。
- 2、标识符第一个字符之后的下一个字符可以是字母、下划线或数字。
- 3、标识符的命名是区分大小写的。
youquantVar
_youquantVar
youquant666Var
MAX_LEN
3barsDown //错误命名
如同大部分的编程语言一样,Pine语言也有书写建议。通常建议对标识符的命名时:全部字母大写用于命名常量。使用小驼峰规则(小写/大写单词)用于其它标识符命名。
LEN = 10 //全部字母大写用于命名常量
zeroOne(boolValue) => boolValue ? 1 : 0 //使用小驼峰规则用于其它标识符命名
通过对Pine语言第一个板块“类型系统”的学习,可以看出来相对于tradingview,在优宽平台上的Pine语言的语法规则比较宽松。因此在优宽上可以跑通的策略,在tradingview上可能会报错,希望大家仔细辨别。本节课的内容介绍完毕,我们下节课再见!
(七): 运算符
大家好,本节课我们讲解的内容为运算符。在量化交易中,如何对指标进行加减乘除操作,如何判断入场信号和离场信号,对交易逻辑的处理必然少不了运算符的操作。在本节课的最后,我将带领大家运用本节课的知识打造一个简易的量化策略,大家请安心等待。
在Pine语言中,运算符主要分为以下几种:
- 赋值运算符
- 算术运算符
- 比较运算符
- 逻辑运算符
- ? :三元运算符
- [ ]历史引用运算符
下面我将为大家逐一讲解。
赋值运算符
= 用于给变量赋值,但仅在声明变量时(第一次使用)。基本作为常量赋值,在函数体外使用。
:= 赋值运算符,给左侧变量赋值。用于为先前声明的变量赋值。基本在函数内使用,随着函数逻辑的判断对变量进行重新赋值。
算数运算符
| 运算符 | 功能 |
|---|---|
| /+ | 加法和字符串连接 |
| /- | 减法 |
| /* | 乘法 |
| / | 除法 |
| % | 取模(除法后的余数) |
runtime.log('加法', 1+1)//2
runtime.log('减法', 1-1)//0
runtime.log('乘法', 1*2)//2
runtime.log('除法', 1/2)//0.5
runtime.log('取模', 1%2)//1
runtime.error('STOP')
运算符指派
a = a 符号(+-*/%) b
a 符号(+-*/%)= b
上面的算术运算符都是二进制的。如果两个操作数都是数字,但其中至少一个是float类型,则结果也将是float。如果两个操作数都是int类型,则结果也将是int。如果至少一个操作数是string,则结果也是string。
runtime.log(1+0.1) // 1.1
runtime.log(1+0.1+'AA') //1.1AA
runtime.log('优宽'+'AA') //优宽AA
runtime.error('STOP')
“+”号运算符还用作字符串的连接运算符。"优宽"+"PINE"产生"优宽PINE"字符串。
比较运算符
在Pine Script中,有六个比较运算符。
| 运算符 | 功能 |
|---|---|
| < | 小于 |
| <= | 小于或等于 |
| ! = | 不等于 |
| == | 等于 |
| > | 大于 |
| >= | 大于或等于 |
比较操作是二进制的。如果两个操作数都是数值,结果将是bool类型,即 "true"、"false "。
a = 2
b = 3
runtime.log('等号',a==b)
runtime.log('不等号',a!=b)
runtime.error('stop')
逻辑运算符
在Pine Script中,有三个逻辑运算符(not, and, or)。
not 假 = 真
not 真 = 假
| a | b | a and b | a or b |
|---|---|---|---|
| true | true | true | true |
| true | false | true | true |
| false | true | true | true |
| false | false | false | false |
逻辑运算符常用于Pine语言逻辑的判断,比如交易信号的判断,比如价格连续上涨加上交易量的连续上涨,两个条件都需要满足,就可以使用'and',作为你可以判断为进场的信号;价格连续下跌或者交易量的连续下跌,两个条件满足其一,可以使用'or',作为出场的信号。
?: 三元运算符
?:三元运算符用于为符合条件的信号创造表达式的,相当于一个ifelse的简写:
条件 ? 如果为真返回值 : 如果为假返回值
三元运算符返回的结果取决于条件的值。如果它是 "真",那么返回第二个参数的值。如果条件是假或na,那么将返回第三个参数的值。
conditionsig = close > open ? 1 : 0 // if close > open, 返回1, 否则返回0
runtime.log(conditionsig)
[]历史引用操作符
可以使用[]历史引用操作符来引用时间序列的过去值。过去的数值是指变量在当前脚本执行的条形图之前的数值,即当前条形图。在变量、表达式或函数调用之后使用[]操作符。操作符的方括号内使用的值是我们要引用的过去的偏移量。要引用volume内置变量的值,离当前柱状体有两个柱状体,我们可以使用volume[2]。
strategy(overlay=true)
plot(volume)
plot(volume[1])
plot(volume[2])
运算符优先级
计算的顺序是由运算符的优先级决定的。优先级较高的运算符会先被计算。下面是一个按优先级递减排序的运算符列表。
| 优先级 | 操作符 |
|---|---|
| 9 | [] |
| 8 | +, -, not |
| 7 | *, % |
| 6 | +, - |
| 5 | >, <, >=, <= |
| 4 | ==, != |
| 3 | and |
| 2 | or |
| 1 | ?: |
如果在一个表达式中,有几个具有相同优先级的运算符,那么它们将被从左到右计算。如果表达式必须以不同于优先级的顺序进行计算,那么表达式的部分内容可以用圆括号分组。请注意优先级为8的'+-'(一元运算符)和优先级为6的'+-'(二元运算符)并不一致。
简易策略
通过上述的讲解,我相信大家对于Pine语言的运算符系统有了一个清晰的认识。下面我将用上述的运算符打造一个简单的量化策略,只是为了说明运算符的应用,大家对于实盘操作要谨慎尝试。
首先我们来定义一个hloc值,这个值是在一个策略周期内,高低开收的平均值,当然也****可以使用内置变量OHLC4值,我们后续会介绍
接下来我们来定义一个价格连续上涨的指标,利用三元表达式,判断当满足,条件1:前日的价格大于大前日的价格,条件2:昨日的价格大于前日的价格,两个条件都满足时,设定价格连续上涨的指标为1,否则为0;因为需要两个条件都满足,所以需要'and'操作符;
成交量连续上涨的指标也一样,利用'and'判断前日大于大前日,昨日大于大前日。这里提醒以下哈,为了避免未来函数,我们使用的都是过去的指标。
下面我们来使用if逻辑判断,这个后续我们也会介绍。因为入场信号比较谨慎,所以我们需要判断的是当价格和成交量都要连续上涨的时候,使用'and'符号确定满足两个条件, 我们进场, 利用stategy交易函数开多仓;
因为我们比较谨慎,所以感觉有风吹草动时,就是当价格或者交易量其中之一出现下降的时候,我们就要及时止盈,迅速离场,平掉多仓。因此这里使用'or'判断满足一个条件(价格下跌 or 成交量下跌)就可以。
最后,我们打印出价格连续上涨和交易量连续上涨的指标的日志。
hloc = (high + low + open + close)/4 //可以使用内置变量hlcc4,后续介绍
pricesignal = (hloc[2] > hloc[3] and hloc[1] > hloc[2]) ? 1 : 0
volumesignal = (volume[2] > volume[3] and volume[2] > volume[1]) ? 1 : 0
if pricesignal == 1 and volumesignal ==1
strategy.entry("buy", strategy.long)
if pricesignal == 0 or volumesignal ==0
strategy.close_all("buy", comment = "close long")
runtime.log('pricesignal',pricesignal)
runtime.log('volumesignal',volumesignal)
通过策略回测,我们看到本策略的收益为正。在策略回测日志中,可以看到只有在条件满足的情况下会进行开平仓的操作。通过运算符的学习,我们可以构建一些基础的交易逻辑,对我们日后进行交易逻辑的梳理有着很大的作用,但是大家不要轻易应用于实盘,因为本策略的适用范围为具有较长时间的趋势策略,因此在震荡行情中可能不太适用。所以“市场有风险,入市需谨慎!”。我们下节课再见!
(八): 条件和循环结构
大家好,本节课我们要讲述的内容为Pine语言语法结构的最后一块拼图--条件和循环结构。假设通过你在期货市场上通过多年的学习,形成了一套完整的指标信号计算方法,和使用指标信号进行期货市场进场出场的判断方法,但是这套交易方法是基于手工操作的,大量的准确的指标运算和交易判断方法需要耗费你极大的精神成本,如果有一套代码可以胜任这项工作,仅仅需要你少量的精力去学习,可以大大减轻你的时间压力和工作压力,如此性价比的投入,你愿意尝试一下吗?
上节课的内容我们学习了运算符,可以帮助你进行准确的指标计算。本节课我们所学的内容是条件和循环结构,通过学习本节内容,可以帮助你利用指标进行交易逻辑的判断。在学完本节内容后,基本上Pine语言交易逻辑的梳理和代码的编写工作你都可以胜任了。
条件结构
Pine语言中的条件结构是 if 和 switch。它们可用于:
- 返回一个值或元组,然后可以将其分配给一个或多个(元组)变量。
- 不返回值,重新赋值变量或者执行函数(比如交易函数)。
if语句
if语言相信大家都很熟悉,“如果,就”。在if条件里准确的对信号加以描述,模型就可以执行相应的操作。其基本语法结构为:
单层判断
if condition1 \\conditon后不需要加任何符号
action1
双层判断
if condition1
action1
else
action2
多层判断
if condition1
action1
else if condition2
action2
else
action3
if语句可用来给变量赋值或者执行逻辑操作,下面给大家举例示范下:
\\ 赋值
x = if close > close[1]
'涨'
else if close == close[1]
'平'
else
'跌'
runtime.log(x)
\\ 操作
if close > close[1]
runtime.log('涨')
else if close == close[1]
runtime.log('平')
else
runtime.log('跌')
需要注意的是:
- 在tradingview上,某些 Pine语言内置函数无法从条件结构的本地块中调用,比如一些画图函数 plot(), plotbar(), plotcandle(), plotchar(), plotshape()等。 优宽上限制不是那么严苛,但是也建议遵循Trading View上的规范书写。例如这样虽然在优宽上不报错,不过不建议这样写。
- 虽然允许在本地块中进行函数调用, 它们的功能与在脚本的全局范围内的功能相同(比如input)。
- 条件结构中的局部块必须缩进四个空格或制表符。
- 在赋值判断时,所有分支表达式都不为真,也没有else分支,则返回na。
x = if close > open
close
runtime.log('x:',x)
- 条件结构和循坏结构都是可以根据你的交易逻辑互相嵌入的。
switch语句
switch可以来说是if语句的另外一种呈现形式。switch也可以用来赋值或者进行逻辑操作。一般来说switch语句之后的分支条件必须是互斥的。就是说各分支的条件不能同时成立。因为switch只能执行一个分支的本地代码块。
switch的每个分支都可以写一个本地代码块,这个本地代码块的最后一行即为返回值(它可以是一个值的元组)。如果没有任何分支被的本地代码块被执行,则返回na。
带有表达式的switch
switch结构中的表达式判断位置,可以写字符串、变量、表达式或函数调用。变量indic的值就为一个字符串,变量indic作为switch的表达式(可以是变量、函数调用、表达式),来确定执行switch中的哪个分支。如果变量func无法和switch中的任一个分支上的表达式匹配(即相等),则执行默认的分支代码块,会执行runtime.error("error")函数导致策略抛出异常停止。
indic = input('close', title="指标名称", tooltip="选择要使用的指标函数名称", options=['close', 'open', 'high', 'low', 'null'])
switch indic
'close' =>
runtime.log('收盘价:', close)
'open' =>
runtime.log('开盘价:', open)
'high' =>
runtime.log('最高价:', high)
'low' =>
runtime.log('最低价:', low)
=>
runtime.error('没有指标')
不带有表达式的switch
接下来我们看switch的另一种使用方式,即不带表达式的写法。
up = close > open
down = close < open
switch
up =>
runtime.log('涨')
down =>
runtime.log('跌')
测试代码例子就可以看到,switch会匹配执行分支条件上为真的本地代码块。一般来说switch语句之后的分支条件必须是互斥的。就是说例子中up和down不能同时为true。因为switch只能执行一个分支的本地代码块。除此之外还需要注意尽量不要把函数调用写在switch的分支中,函数无法在每个BAR上被调用可能引起一些数据计算的问题(除非如同「带有表达式的 switch」例子中,执行分支是确定的,在策略运行中是不会被更改的)。
请注意,在tradingview上,各个分支的返回值需要是一致的,比如统一的变量type类型(数字,字符,元组等),但是在优宽上没有明确的要求,可以根据需要设置需要的返回值,优宽实现了类型的兼容。
循环结构
虽然Pine语言中有很多内置函数可以帮助我们进行循环的计算,但是循环的存在是有充分理由的,因为即使在 Pine 语言中,它们在某些情况下也是必需的。这些情况通常包括:
- 数组(array)的操作。
- 回顾历史,使用只能 在当前柱上已知,例如,找出有多少过去的高点高于当前柱线的高点。由于当前柱线的高点仅在运行脚本的柱上已知,循环是必要的,可以回到过去并分析过去的柱线。
- 使用Pine语言的内置函数无法完成对过去BAR的计算的情况。
for语句
for语句使用非常简单。for语句之后跟随一个「计数」变量用于控制循环次数、引用其它值等,「计数」变量在循环开始之前被赋值为「初始计数」,然后根据「步长」设置递增,当「计数」变量大于「最终计数」时循环停止。for循环可以最终返回一个值(或者返回多个值,以[a, b, c]这样的形式),或者进行循环的操作。
for 起始计数 to 最终计数 by 步长 //优宽暂时不支持反向操作
语句 // 注释:语句里可以有break、continue
操作
x = for 起始计数 to 最终计数 by 步长
语句
返回值//循坏过后最终的值
下面举例示范下:
//操作
for i = 0 to 10
runtime.log("i:", i)
runtime.error("stop")
//赋值
ret = for i = 0 to 10
runtime.log("i:", i)
i // 如果这行不写,就返回空值,因为没有可返回的变量
runtime.log("ret:", ret)
runtime.error("stop")
在if语句中是可以插入break(跳出整个循环)这个循环和continue(跳过本次条件)。
for i = 0 to 10
runtime.log("i:", i)
if i == 3
break
for i = 0 to 10
runtime.log("i:", i)
if i == 3
continue
for...in 语句
for.in语句主要针对对象为数组,主要有以下两种形式:
for 数组元素 in 数组
语句 // 注释:语句里可以有break,continue
for [索引变量, 索引变量对应的数组元素] in 数组
语句 // 注释:语句里可以有break,continue
可以看到两种形式的主要差别就在于for关键字之后跟随的内容,一种是使用一个变量作为引用数组元素的变量。一种是使用一个包含索引变量,数组元素变量的元组的结构来引用[索引i, 元素ele]。其它的返回值规则,使用break、continue等规则和for循环一致。我们也通过一个简单的例子来说明使用。
testArray = array.from(10, 20, 30, 40, 50, 60, 70, 80, 90, 100)
for ele in testArray
runtime.log("ele:", ele)
runtime.error("stop")
for [i, ele] in testArray
runtime.log("ele:", ele, ", i:", i)
runtime.error("stop")
while语句
while语句让循环部分的代码一直执行,直到while结构中的判断条件为假(false)。和if语句一致,while语句也可以进行赋值或者逻辑操作,while语句的基本结构为
while 判断条件
操作 // 注释:语句里可以有break,continue
返回值 = while 判断条件
语句 // 注释:语句里可以有break,continue
语句 // 注释:最后一条语句为返回值
while的其它规则和for循环类似,循环体本地代码块最后一行是返回值,可以返回多个值。当「循环条件」为真时执行循环,条件为假时停止循环。循环体中也可以使用break、continue语句。
i = 0
while i <= 10
runtime.log('i',i)
i += 1
runtime.error("stop")
x = while i <= 10
runtime.log('i',i)
i += 1
i//循环体结束后最后的值
runtime.log('x:',x)
runtime.error("stop")
Pine语言最后一块语法拼图已经讲解完毕,我们下节课再见。
(九):时间序列
你做过交易吗,连续高长时间的紧盯着一块屏幕,密切的观察指标的变化,不断地在衡量交易的规律和趋势,决定交易操作,执行并继续观察交易结果,不断地调仓补仓平仓,直至最后交易时间段结束,还需要你进行更为深刻的自身,复盘总结今日某一瞬间的得失。这就是一个交易人,与市场,与大盘,与庄家,与自己的战斗。每天都在不断上演。交易可以量化吗?有时候你会怀疑,怎样利用语言去模拟一个金融市场,无数伴随着每豪每秒的纷繁信号,眼花缭乱的涨跌趋势,怎样去抓取其中的规律,预测未来?这就需要我们深入了解Pine语言对于交易逻辑的处理--时间序列和模型执行。
这两部分的内容其实应该在Pine语言,入门的时候为大家讲解,可是理解起来确实有些困难。因此在为大家讲述完,Pine语言基本语法结构的基础上编写简单的代码,为大家进行更好的展示。
时间序列
首先,我们讲解时间序列,在类型系统中我们粗略的介绍过。时间序列并不是一种数据类型或者格式,时间序列是PINE语言中一种基本结构的概念。用来储存时间上连续变动的值,每个值都对应一个时间点。时间序列这种概念的结构很适合应用于处理、记录随时间变化的一系列数据。
我们知道Pine脚本是基于图表的,图表中展示的最基本的内容就是K线图。时间序列其中每个值都与一个K线Bar的时间戳关联。PINE语言这样设计时间序列,可以在策略代码中很轻松地计算收盘价的累计值,而且不需要使用for之类的循环结构,只用使用PINE语言的内置函数ta.cum()。下面我们举例进行一下解释:
这段代码的意思是,在一个策略周期内,我们设置两个变量,第一个变量v1,赋值为整数1,第二个变量v2是伴随策略周期累加的v1值,然后我们使用plot函数将v1,v2和k线bar的索引,这个内置变量bar.index在图表中展示出来。
v1 = 1
v2 = ta.cum(v1)
plot(v1, title="固定v1")
plot(v2, title="累计v1")
plot(bar_index+1, title="bar序列")
有很多类似ta.cum这样的内置函数可以直接处理时间序列上的数据,例如ta.cum就是把传入的变量在每个K线Bar上对应的值累加起来,接下来我们使用一个图表来方便理解。
| 策略运行时间段 | 内置变量bar_index | 内置变量bar_index + 1 | 固定v1 | 累计v2 |
|---|---|---|---|---|
| 策略运行第一根K线Bar: 0 到 1 分钟 | 0 | 1 | 1 | 1 |
| 策略运行第一根K线Bar: 1 到 2 分钟 | 1 | 2 | 1 | 2 |
| 策略运行第一根K线Bar: 2 到 3 分钟 | 2 | 3 | 1 | 3 |
| 策略运行第一根K线Bar: 3 到 4 分钟 | 3 | 4 | 1 | 4 |
| 策略运行第一根K线Bar: 4 到 5 分钟 | 4 | 5 | 1 | 5 |
可以看到,他们都是时间序列结构,在每根Bar上都有对应的数据。
因为固定v1这个变量在每一根Bar上都是1,ta.cum(v1)函数在第一根K线Bar上执行时由于只有第一根Bar,所以计算结果为1,赋值给变量累计v2。
当ta.cum(v1)在第二根K线Bar上执行时,已经有2根K线Bar了(第一根对应的内置变量bar.index是0,第二根对应的内置变量bar.index是1),所以计算结果为2,赋值给变量v2,以此类推。实际上可以观察到v2就是图表中K线Bar的数量,由于K线的索引bar.index是从0开始递增,那么bar.index + 1实际上也就是K线Bar的数量。观察图表也可以看到v2和bar.index确实是重合的。
需要注意的是,虽然时间序列很容易让人想起「数组」这种数据结构,虽然PINE语言也有数组类型。但是它们和时间序列是完全不同的概念。
在时间序列上调用函数的结果也会在时间序列上留下痕迹,同样可以使用[]历史操作符引用之前的值。例如,计算最后10根K线BAR中的最高价的最大值时(不包括当前的K线BAR)。我们可以写为 ta.highest(close, 10)[1],同样也可以写成 ta.highest(close[1], 10)。两者是等价的。
可以用以下代码验证:
strategy("test pine", "test", true)
a = ta.highest(close, 10)[1]
b = ta.highest(close[1], 10)
runtime.log("a",a)
runtime.log("b",b)
PINE语言这样设计时间序列,可以在策略代码中很轻松地进行时间逻辑的运算。但是需要了解其具体的运行机制,我们可以进行一个展示。
pricecum1 = ta.cum(close)
pricecum2 = close // pricecum2 = close+close[1]+close[2]
for i=1 to 2
pricecum2 += close[i]
i += 1
runtime.log("pricecum1:", pricecum1)
runtime.log("pricecum2:", pricecum2)
策略是从09:00准时开始的,用一分钟为K线周期,我们进行五分钟的策略回测(09:00至09:05),因为最后的K线是09:05还没有更新完,所以09:05的数据是不显示的,只能展示四个值(0到1,1到2,2到3,3到4,四个时间段)。在回测日志中,我们可以观察到pricecum1使用ta.cum函数,从策略起始处(0到1)就开始计算累加值,直到(0到4),所以计算了四段k线的累加值。而pricecum2,以三分钟的k线累加值为一个轮回进行循环计算,因为在0到1和1到2处,没有前两个值,即close[1]和close[2],所以第一分钟,和第二分钟的pricecum2为空值,到第三分钟时,pricucum2 = close + close[1] + close[2], 三个值都是实值了,可以进行计算,并且和pricecum1相等。因为pricecum2以3为周期,所以pricecum1和pricecum2在第四分钟两者时不相等的,这个时候pricecum1计算的是0到1,1到2,2到3,3到4,四个时间段的收盘价累加值;而pricecum2计算的是1到2,2到3,3到4,三个时间段的收盘价累加值。这是一个时间序列的例子,大家可以回味下。
我们可以看到策略从初始值实时更新的时候,是不会使用过去的历史bar,因此如果在策略中你使用了循环语句,去利用过去的指标去判断交易的逻辑。在策略开始的时候,你需要等待循环逻辑走完,才能进行交易的处理。第二点请注意,这里我们选择的是收盘价模型,如果我们这里选择的是实时价模型,进行策略的运行,日志的结果将会是这样。这两者的区别涉及到模型执行机制,我们下节课为大家继续讲解。
(十):模型执行
今天我们继续学习Pine语言的模型执行。在了解Pine语言时间序列基础上,你的交易逻辑是想基于实时价模型或者收盘价模型?收盘价模型对于趋势策略比较友好,而实时价模型更多的适用于震荡策略,当然这并没有固定的限制。但是如果你想更加准确的用Pine语言模拟你的交易思想,那么这部分内容你一定不要错过。
模型执行
在入门学习Pine语言时,是非常有必要了解Pine语言脚本程序执行过程等相关概念的。Pine语言策略是基于图表运行的,可以理解为Pine语言策略为一系列的计算和操作,在图表上以时间序列的先后顺序从图表已经加载的最早数据开始执行。图表中加载的Bar有两个部分,策略开始前的历史Bar和策略开始后的实时Bar。
图表中历史Bar初始加载的数据量是有限的。实盘时通常这个数据量上限是基于交易所接口返回的最大数据量决定,回测时数据量上限是基于回测系统数据源提供的数据决定。
收盘价模型 vs. 实时价模型
根据策略的设置不同,策略的模型执行方式也不同,分为收盘价模型和实时价模型。
-
收盘价模型
策略代码执行时,当前K线Bar的周期完全执行完成,K线闭合时即K线周期已经走完。此时执行一遍Pine策略逻辑,触发的交易信号将在下一根K线Bar开始时执行。 -
实时价模型
策略代码执行时,当前K线Bar不论是否闭合,每次行情变动就执行一遍Pine策略逻辑,触发的交易信号立即执行。
当Pine语言策略在图表上从左至右执行时,图表上的K线Bar是分为历史Bar和实时Bar的:
-
历史Bar
策略设置为「实盘价模型」开始执行时,图表上除了最右侧的那一根K线Bar之外所有K线Bar都是历史Bar。策略逻辑在每根历史Bar上仅执行一次。策略设置为「收盘价模型」开始执行时,图表上所有Bar都是历史Bar。策略逻辑在每根历史Bar上仅执行一次。
基于历史Bar的计算:策略代码在历史Bar收盘状态下执行一次,然后策略代码继续在下一个历史Bar执行,直到所有历史Bar都执行一次。 -
实时Bar
当策略执行到最右边的最后一根K线Bar上时,该Bar为实时Bar。当实时Bar闭合之后,这根Bar就变成了一个经过的实时Bar(变成了历史Bar)。图表最右侧会产生新的实时Bar。策略设置为「实时价模型」开始执行时,在实时Bar上每次行情变动都会执行一次策略逻辑。
策略设置为「收盘价模型」开始执行时,图表上不显示实时Bar。如果设置策略为「收盘价模型」图表不显示实时Bar,策略代码只在当前Bar收盘时执行一次。
如果设置策略为「实盘价模型」在实时Bar上的计算和历史Bar就完全不同了。例如内置变量high、low、close在历史Bar上是确定的,在实时Bar上可能每次行情变动时这些值是会发生变化的。所以基于这些值计算的指标等数据也是会实时变动的。在实时Bar上open是不变的,close始终代表当前最新价格,high和low始终代表自当前实时Bar开始以来达到的最高点和最低点。这些内置变量代表实时Bar最后一次更新时的最终值。
策略代码在历史Bar收盘状态下执行一次,然后策略代码继续在下一个历史Bar执行,直到所有历史Bar都执行一次。如果设置策略为「实盘价模型」在实时Bar上的计算和历史Bar就完全不同了,在实盘Bar上每次行情变动都会执行一次策略代码。
下面我们来举例说明下:
if open > close [1]
runtime.log("涨")
if open < close [1]
runtime.log("跌")
本策略以天为周期,打印出每个策略周期的涨跌值。当用收盘价为模型的时候,打印操作是在历史Bar收盘状态下执行的,所以以天为单位进行打印;而用实时价为模型,打印操作是在实时Bar上进行,每次行情变动都会打印一次。
实时Bar上执行策略时的回滚机制
在实时Bar执行时,策略的每次新迭代执行前重置用户定义的变量称为回滚。我们来用声明变量var和varip理解回滚机制,其中声明变量应该在赋值运算符处讲解,但是在不了解时间序列和模型执行的基础上,对于大家理解起来比较困难,因此放在这里讲解,并以此用来了解策略的回滚机制。
-
声明模式:
其实变量在命名的时候,在声明变量时最先写的就是「声明模式」,变量的声明模式有三种即:
1、使用关键字var。
2、使用关键字varip。
3、什么都不写。 -
var
var 是用于分配和一次性初始化变量的关键字。通常,不包含关键字var的变量赋值语法会导致每次更新数据时都会覆盖变量的值。 与此相反,当使用关键字var分配变量时,尽管数据更新,它们仍可以“保持状态”。 -
varip
varip(var intrabar persist)是用于分配和一次性初始化变量的关键词。它与var关键词相似,但是使用varip声明的变量在实时K线更新之间保留其值。
其实说起来解释还是比较晦涩,我们举例示范下。
strategy(overlay=true)
var i = 0
varip ii = 0
plotchar(true, title = 'i', char=str.tostring(i), location=location.abovebar, color=color.aqua)
plotchar(true, title = 'ii', char=str.tostring(ii), location=location.belowbar, color= color.blue)
i := i+1
ii := ii+1
- 收盘价模型
我们看到在收盘价模型中, var 和 varip 没有区别,只是在策略周期更新时进行一次更新。由于收盘价模型是每根K线BAR走完时才执行一次策略逻辑。所以在收盘价模型时,历史K线阶段和实时K线阶段,var、varip声明的变量在以上例子中递增表现完全一致,都是每根K线BAR递增1。
- 实时价模型
当在实时价模型,历史K线阶段时var、varip声明的变量i、ii在策略代码每轮执行时都会执行递增操作。所以可以看到回测结果K线BAR上显示的数字逐个都是递增1的。当历史K线阶段结束,开始实时K线阶段。var、varip声明的变量则开始发生不同的变化。因为是实时价模型,在一根K线BAR内每次价格变动都会执行一遍策略代码,i := i + 1和ii := ii + 1都会执行一次。区别是ii每次都修改。i虽然每次也修改,但是下一轮执行策略逻辑时会恢复之前的值,就是回滚,直到当前K线BAR走完才更新确定i的值(即下一轮执行策略逻辑时不再恢复之前的值)。所以可以看到变量i依然是每根BAR增加1。但是变量ii每根BAR就累加了好几次。
转变执行方式为实时价模型,其实策略更新就是跟随者tick的数据,进行策略逻辑的运行。在收盘价模型里,我们策略的更新,是上一个周期的一整个k线bar。而在实时价模型,伴随每次行情变动进行策略的运行,其实就是对比交易所返回的tick数据。这里我们选择模拟级tick,就是每分钟根据k线图模拟四个左右的tick数据。回到图表里,我们看到不带有关键字的变量每次都是计算加1,然后在下一次tick数据返回时,返回到原来的值。带有关键字var在每个tick周期内,虽然每次都计算,但是在一个策略周期内,都会保留原有的值;直到下一次策略周期的更新。而带有关键字varip的变量的变动是伴随着tick周期的,每次tick变动,都会进行计算和保留。
在实时价模型中,有模拟级tick和实盘级tick的返回机制。
模拟级tick: 一分钟四个模拟tick,所以每分钟varip增加4个左右。
实盘级tick: 根据交易所反馈的信息,针对于螺纹钢品种(其他品种可能不一样),每秒两个tick,所以每分钟varip增加120个。
总体来说,这部分比较困难。因为理解起来比较晦涩,所以需要简单的代码例子进行详细的说明和解释。这部分内容需要在较好的理解“时间序列”概念上,理解策略背后运行的机制--模型执行。了解收盘价模型和实时价模型的区别,通过var和varip理解实时价模型背后的回滚机制。只有在此基础上,才能对策略模型的原理有一个清楚的理解。当然,这也对理解“偷价”和“未来函数”,这些策略背后的陷阱有一个清楚的认识。
(十一):画图(1)
大家好,今天我们来学习Pine语言的画图功能。Pine语言是一门基于图表的语言,而在期货交易中,我们做的最多的事情就是看盘。众所周知,看盘是一件很累的事情,K线的不断涨跌彷佛牵动我们的每一刻神经。因此,如果能使用Pine语言,根据我们的交易理解,对我们的K线图进行私人优化,对指标进行更好的呈现,帮助我们快速进行决策的判断,这就是程序化交易的方便之处。在前面的课程中,我们使用了一些基本的画图函数,但是对于画图的参数我们并没有详细的解释。Pine语言具有丰富的图表功能,今天我们将使用代码范例为大家一一呈现。
plot
首先,我们学习最基本的plot函数,它的描述也很朴素:将一系列的数据在图表中进行展示。
strategy(overlay=true)
plot(series, title, color, linewidth, style, trackprice, histbase, offset, join, editable, show_last, display)
它里面有很多参数,我将挑些重点的为大家讲述下:
-
overlay (const bool) 优宽平台扩展的参数,用于设置当前函数在主图(设置true)或者副图(设置false)上画图显示,默认值为false。
-
series (series int/float) 要绘制的数据系列。 必要参数。
-
title (const string) 绘图标题。
-
color (series color) 绘图的颜色。您可以使用如'color = red'或'color =#ff001a'的常量。颜色的使用也可以用表达式,例如下例中这里的颜色使用是一个一个三元表达式,在抽盘价大于开盘价的时候,使用的是绿色,否则使用红色。三元表达式的使用很灵活,添加不同字符或者图形都可以根据你的需要进行设置。
-
style (plot_style) plot类型。可能的值有:plot.style_line、plot.style_stepline、plot.style_stepline_diamond、plot.style_histogram、plot.style_cross、plot.style_area、plot.style_columns、plot.style_circles、plot.style_linebr、plot.style_areabr。默认值为plot.style_line。在下一部分,将为大家一一进行展示。
-
linewidth (input int) 绘制线的宽度。默认值为1。它不适用于每种样式。
-
trackprice (input bool) 如果为true,则水平价格线将显示在最后一个指标值的水平。默认为false。
-
histbase (input int/float) 以plot.style_histogram,plot.style_columns或plot.style_area样式绘制图时,用作参考水平的价格值。默认值为0.0。
-
offset (series int) 在k线特定数量上向左或向右移动绘图。 默认值为0。
-
join (input bool) 如果为true,则绘图点将与线连接,仅适用于plot.style_cross和plot.style_circles样式。 默认值为false。
-
editable (const bool) 如果为true,则绘图样式可在格式对话框中编辑。 默认值为true。
-
show_last (input int) 如已设置,则定义在图表上绘制的k线数(从最后k线返回过去)。
-
display (plot_display) 控制显示绘图的位置。可能的值为:display.none、display.all。预设值为display.all。
大家可以感觉到Pine语言画图的参数很灵活,可以根据你的需要进行设置。下面我就plot.style中的不同形状为大家展示下。
plot.style
-
plot.style_line
'Line'直线样式的命名常量,用作plot函数中style参数的参数。 -
plot.style_linebr
'Line With Breaks'样式的命名常量,用作plot函数中style参数的参数。类似于plot.style_line,除了数据中的空白没有被填充。 -
plot.style_histogram
'Histogram'柱状图样式的命名常量,用作plot函数中style参数的参数。在这里可以为大家展示刚才plot中histbase参数的用法。我们看到当histbase设置为2000时。图像由下所示。
plot(close, title='Title', color=color.new(#00ffaa, 70), linewidth=5, style=plot.style_histogram,histbase=2400)
-
plot.style_columns
'Columns' 样式的命名常量,和histgram区别不大。 -
plot.style_circles
'Circles' 样式的命名常量,用作plot函数中的style参数的参数。 -
plot.style_area
'Area'样式的命名常量,用作plot函数中style参数的参数。 -
plot.style_areabr
'Area With Breaks'样式的命名常量,用作plot函数中style参数的参数。类似于plot.style_area,除了数据中的空白没有被填充。
plot(close> open ? close : na , title='Title', color=color.new(#00ffaa, 70), linewidth=2, style=plot.style_area)
plot(close> open ? close : na , title='Title', color=color.new(#00ffaa, 70), linewidth=2, style=plot.style_areabr)
-
plot.style_cross
'Cross' 乘号样式的命名常量,用作plot函数中style参数的参数。 -
plot.style_stepline
'Step Line'样式的命名常量,用作plot函数中style参数的参数。
fill
使用提供的颜色填充两个绘图或hline之间的背景。本例中使用fill画图画出了close和open之间的差距。
fill(plot1, plot2, color, title, editable, show_last, fillgaps, display)
p1 = plot(open)
p2 = plot(close)
fill(p1, p2, color=color.new(color.green, 90))
hline
在给定的固定价格水平上呈现水平线。其中参数linestyle (hline_style) 渲染线的样式。 可能的值有:solid(实线),dashed(虚线),dotted(点)。 注意,这里的hline是固定的,不能设置为随着策略周期的变化而变化。
strategy(overlay=true)
hline(2500, linestyle = hline.style_solid)
hline(3000, linestyle = hline.style_dashed)
bgcolor
设置背景颜色,可以根据你的交易理念设置不同的背景颜色。
bgcolor(color, offset, editable, show_last, title, display, overlay)
// bgcolor example
bgcolor(close < open ? color.new(color.red,70) : color.new(color.green, 70))
barcolor
设置K线颜色。在国际上,默认绿色代表的上涨,红色是下跌;而在中国,恰好相反。在优宽平台,k线使用的是国际标准,如果想进行更改,使用barcolor函数就可以。
barcolor(close < open ? color.black : color.white)
本节课的内容讲授结束,下节课我们将继续为大家讲解Pine语言画图的其他功能。
今天编写了Pine语言画图课程教案第一部分。Pine语言是一门基于图表的语言,而在期货交易中,交易者做的最多的事情就是看盘。看盘是一件很累的事情。因此,如果能使用Pine语言,根据交易理解,对K线图进行私人优化,对指标进行更好的呈现,帮助快速进行决策的判断,可以更加展示出Pine语言的优秀之处。画图部分内容比较多,需要大量的画图例子进行呈现,因此将画图部分讲解分为两个部分,第一部分讲解Pine语言plot函数及其重要的内置参数,ploy.style图表形状,以及其它画图函数fill,hline等,辅以代码和图像帮助学员进行更好的了解和使用。
(十二):画图(2)
大家好,今天我们继续来学习画图函数。在本节课的最后,我将利用画图函数打造一个简易的指标参考系统,帮助交易者在实盘交易中,及时检阅出市场信号,进而迅速进行交易操作,这对于半程序化交易者十分友好。
plotshape
首先我们学习plotshape函数,plotshape函数在图表上绘制可视形状。它的基本语法结构为:
plotshape(series, title, style, location, color, offset, text, textcolor, editable, size, show_last, display)
其重要的内置参数如下所示,基本上伴随策略周期变化的参数,都可以使用三元表达式,根据你的交易逻辑,在图中进行不同的展示:
- series (series bool) 作为形状绘制的一系列数据 。 除了交易指标外,这里也可以添加交易指标布尔值的形式(只显示布尔值判断为真的指标)。
data = close >= open
plotshape(data, style=shape.xcross)
这里我们看到只显示收盘价大于开盘价时的乘号标志。
- title (const string) 绘图标题。
- style (input string) 绘图类型。可能的值有:shape.xcross(乘号),shape.cross(加号),shape.triangleup(上三角),shape.triangledown(下三角),shape.flag(旗帜),shape.circle(圆圈),shape.arrowup(上箭头),shape.arrowdown(下箭头),shape.labelup(上升标签),shape.labeldown(下降标签),shape.square(方块),shape.diamond(菱形)。 默认值为shape.xcross。同样,这里也可以使用三元表达式的形式,对不同条件选择不同的图案。
plotshape(close, style=close > open ? shape.arrowup: shape.arrowdown)
这里我们看到如果收盘价大于开盘价,使用上升箭头,否则使用下降箭头。
- location (input string) 形状在图表上的位置。 可能的值有:location.abovebar(k线上),location.belowbar(k线上),location.top(图表最上方),location.bottom(图表最下方),location.absolute(贴合k线)。 默认值为location.abovebar。同样的,可以使用三元表达式。
- color (series color) 形状的颜色。 可以使用如'color = color.red'或'color =#ff001a'的常量以及三元表达式复杂表达式。 可选参数。
- offset (series int) 在k线特定数量上向左或向右移动形状。 默认值为0。
- text (const string) 文字以形状显示。 您可以使用多行文本,分隔行使用'\n'转义序列。示例:'line one\nline two'。同样可以使用三元表达式。
plotshape(close, style=close > open ? shape.arrowup: shape.arrowdown, text = close > open ? '涨': '跌', textcolor = close > open ? color.red: color.green)
- textcolor (series color) 文字的颜色。 和上方的颜色设置一样。
- size (const string) 图表上字符的大小。 可能的值有: size.auto, size.tiny, size.small, size.normal, size.large, size.huge。默认值为size.auto。同样可以使用三元表达式。
plotchar
该函数在图表上使用任何给定的Unicode字符绘制可视形状。
plotchar(series, title, char, location, color, offset, text, textcolor, editable, size, show_last, display)
重要的参数和上述讲解的基本一致:
- series (series bool) 作为形状绘制的一系列数据。 除了location.absolute之外,系列被视为所有位置值的一系列布尔值。 必要参数。
- title (const string) 绘图标题。
- char (input string) 作为视觉形状使用的字符
- location (input string) 形状在图表上的位置。 可能的值有:location.abovebar,location.belowbar,location.top,location.bottom,location.absolute。 默认值为location.abovebar。
- color (series color) 形状的颜色。 您可以使用如'color = red'或'color =#ff001a'的常量以及如 'color = close >= open ? green : red'的复杂表达式。 可选参数。
- offset (series int) 在k线特定数量上向左或向右移动形状。 默认值为0。
- text (const string) 文字以形状显示。 您可以使用多行文本,分隔行使用'\n'转义序列。示例:'line one\nline two'。
- textcolor (series color) 文字的颜色。 您可以使用如 'textcolor=red' 或'textcolor=#ff001a' 的常量,以及如'textcolor = close >= open ? green : red'的复杂表达式。 可选参数。
- size (const string) 图表上字符的大小。 可能值有:size.auto,size.tiny,size.small,size.normal,size.large,size.huge。 默认值为size.auto。
plotchar有很多灵活的运用,比如在k线图中标注“十字星”。十字星是一种趋势信号判断的一个重要指标,十字星出现说明多空双方争夺激烈,互不相让。其基本定义为如果一日内开盘价和收盘价的差距很少,或者相等。因此,代码中使用数学绝对值(math.abs)的形式判断开盘价和收盘价的差距,如果差距小于等于5,则定义为十字星出现,判断为true。然后使用plotchar在图表中展示。
data = math.abs(close - open) <=5
plotchar(data, char='十字星')
plotcandle
在图表上绘制蜡烛。
plotcandle(open, high, low, close, title, color, wickcolor, editable, show_last, bordercolor, display)
例子
indicator("plotcandle example", overlay=true)
plotcandle(open, high, low, close, title='Title', color = open < close ? color.green : color.red, wickcolor=color.black)
其重要的参数如下所示:
- open (series int/float) 数据开放系列用作蜡烛开盘值。必要参数。
- high (series int/float) 高系列数据用作蜡烛的高值。必要参数。
- low (series int/float) 低系列数据被用作蜡烛的低值。 必要参数。
- close (series int/float) 关闭系列数据作为关闭k线的值。 必要参数。
- wickcolor (series color) 蜡烛灯芯的颜色。一个可选参数。
plotarrow
在图表上绘制向上和向下箭头:向上箭头绘制在每个正值指标上,而向下箭头绘制在每个负值上。 如果指标返回na,则不会绘制箭头。 箭头具有不同的高度,指标的绝对值越大,绘制箭头越长。
plotarrow(series, title, colorup, colordown, offset, minheight, maxheight, editable, show_last, display)
codiff = close - open
plotarrow(codiff, colorup=color.new(color.teal,40), colordown=color.new(color.orange, 40), overlay=true)
其重要参数如下所示,其他参数按照默认值即可:
- series (series int/float) 要绘制成箭头的数据系列。 必要参数。
- colorup (series color) 向上箭头的颜色。可选参数。
- colordown (series color) 向下箭头的颜色。可选参数。
- offset (series int) 在K线特定数量上向左或向右移动箭头。 默认值为0。
- minheight (input int) 以像素为单位最小可能的箭头高度。默认值为5。
- maxheight (input int) 以像素为单位的最大可能的箭头高度。默认值为100

顶/底背离指标观察系统
顶背离:K线图上的价格走势一峰比一峰高,价格一直在向上涨,而MACD指标图形上的由红柱构成的图形的走势是一峰比一峰低,即当价格的高点比前一次的高点高、而MACD指标的高点比指标的前一次高点低,这叫顶背离现象。这是一个有“多”转“空”的参考信号。
底背离:K线图上的价格走势一峰比一峰低,价格一直在向下跌,而MACD指标图形上的由绿构成的图形的走势是一峰比一峰高,即当价格的低点比前一次的低点低、而MACD指标的低点比指标的前一次低点高,这叫底背离现象。这是一个有“空”转“多”的参考信号。
MACD指标
12日EMA的计算:EMA12 = 前一日EMA12 * 11/13 + 今日收盘 * 2/13
26日EMA的计算:EMA26 = 前一日EMA26 * 25/27 + 今日收盘 * 2/27
差离值(DIF)的计算: DIF = EMA12 - EMA26
9日DEA = 前一日DEA * 8/10 + 今日DIF * 2/10BAR=(DIF-DEA)*2
MACD=(DIF-DEA)*2
fastline = ta.ema(close,12)
slowline = ta.ema(close,26)
diff = fastline - slowline
dea = ta.ema(diff,9)
macd = 2*(diff - dea)
顶背离:
快线下穿慢线: MACD 从正转为负(下穿0线)
价格逐渐上涨:今日价格大于昨日
bot_diver = ta.crossunder(macd,0) and close[1] < close
plotchar(top_diver, char='顶背离', location = location.abovebar, size = size.normal, overlay=true)
底背离:
快线上穿慢线: MACD 从负转为正(上穿0线)
价格逐渐上跌:今日价格小于昨日
bot_diver = ta.crossover(macd,0) and close[1] > close
plotchar(bot_diver, char='底背离', location = location.belowbar, size = size.normal, overlay=true)
(十三):自定义函数和内置变量
大家好,今天我们来学习Pine语言“量化策略弹药组装”的载体--函数。基本上所有信号指标的计算和交易逻辑的判断,使用的都是函数。在Pine语言中,函数的使用贯彻了一贯的简洁优雅的特点。通过准确的使用各个逻辑代码块,可以实现量化策略的“所写即所得”。如果你想偷懒,不想重复的编写成熟的指标计算的轮子,例如MACD,布林带,超级趋势等指标,Pine的内置变量和内置函数一行代码就可以帮你搞定。
自定义函数
Pine语言可以设计自定义函数,首先需要设置函数的名称,和以前的变量命名的规范是一样的;名称过后需要添加一个“()”,里面可以添加需要的参数个数,当然0个也是可以的;“()”后面添加一个箭头“=>”标志;最后书写本地代码块,定义函数的返回内容。
一般来说Pine语言的自定义函数有以下规则:
-
- 所有函数都在脚本的全局范围内定义。不能在另一个函数中声明一个函数,当然可以在另一个函数中使用一个函数。
-
- 不允许函数在自己的代码中调用自己(递归)。
-
- 函数主要是用来编写逻辑策略的,原则上所有PINE语言内置画图函数,不能在自定义函数内调用。
-
- 函数可以写成单行、多行。最后一条语句的返回值为当前函数返回值,返回值可以返回元组形式。
单行自定义函数:
barIsUp() => close > open
该函数返回当前BAR是否为阳线。这里如果要打印该函数的话,需要引用的方式如下,这里不能省略括号。
runtime.log(barIsUp())
多行自定义函数
自定义函数也可以设计成多行的自定义函数,本地块语句需要以四个空格或者一个tab键换行;伴随下一个逻辑块,继续进行标准缩进。
该函数的意思是计算所需交易指标(data)的指定期间(length)的平均数。在函数体内,设置本地变量(i和sum),然后伴随while的循环,本地变量不断更新,直至循环结束,返回平均数(sum / length)。
sma(data, length) =>
i = 0
sum = 0
while i < 10
sum += data[i]
i += 1
sum / length
还有,也可以实现多变量的返回,使用一个元祖“[]”就可以。
twoEMA(data, fastPeriod, slowPeriod) =>
fast = ta.ema(data, fastPeriod)
slow = ta.ema(data, slowPeriod)
[fast, slow]
请注意,这里自定义函数内部,使用了一个内置函数(ta.ema),下部分我将为大家讲解。
这个函数通过三个参数(交易指标,快线周期,慢线周期),最后返回的是每个策略周期内,快线和慢线的平均数,以元组的形式。
内置变量
在介绍内置函数之前,我先为大家介绍一下Pine语言中的内置变量,这类变量不需要使用函数的形式,即不用添加参数,可以直接使用。内置变量数目有很多,在以往的课程中某些会涉及到,在这里为大家查漏补缺下,挑选一下重要的内置变量为大家讲解下。
指标计算类
指标计算类是量化策略中的常用的计算指标,Pine语言对此实现了封装,大家可以直接使用。
数学指标类
math.e
//是欧拉数的命名常数。它等于2.7182818284590452。
math.phi
//是黄金分割的命名常数。等于1.6180339887498948。
math.pi
//是阿基米德常数的命名常数。它等于3.1415926535897932。
math.rphi
//是黄金分割率的命名常数。它等于0.6180339887498948。
简单指标类
Pine语言对关键词赋予了交易指标的函数,可以直接调用计算,并且简单的合成指标也可以直接引用。
close
open
high
low
volume
hl2 //(最高价 + 最低价)/2的快捷键
hlc3 //(最高价+最低价+收盘价)/3的快捷键
hlcc4 //(高+低+收+收)/4的快捷键
ohlc4 //(开盘价 + 最高价 + 最低价 + 收盘价)/4的快捷键
复杂指标类
量化策略中,具有很多成熟的指标,然而计算公式比较复杂,在Pine语言中实现了直接调用。请注意,这类指标是不需要参数的,如有需要参数,可以参考内置函数内容,我们后续会为大家讲述。
ta.accdist
累积/分布指数
ta.iii
盘中强度指数
ta.nvi
负量指标。
ta.pvi
正量指标
ta.obv
能量潮指标
ta.pvt
价量趋势指标
ta.wad
威廉多空力度线
ta.wvad
威廉变异离散量
这类变量都可以使用自定义函数进行计算,比如最后威廉变异离散量,使用的自定义函数如下:
plot(ta.wvad, title = '内置wvad', color=color.yellow)
f_wvad() =>
(close - open) / (high - low) * volume
plot(f_wvad(), title = '自定义wvad')
时间序列类
时间序列类变量是确定策略执行的时间或者查询bar状态。
bar_index
//目前的价格棒指数。 编号从零开始,第一个条的索引为0。
last_bar_index
//最后一根图表K线的索引。K线索引以第一根K线为零开始。请注意,使用此变量可能会导致指标重绘。
time
//UNIX格式的当前k线时间。 这是自1970年1月1日00:00:00 UTC以来的毫秒数。
year
//交易所时区的当前年份
month
//交易所时区的当前月数
hour
//交易所时区的当前小时数
minute
//交易所时区的当前分钟数
weekofyear
//交易所时区的当前k线时段的周数
dayofmonth
//交易所时区的当前k线时间的日期
dayofweek
//交易所时区的当前k线时间的星期
barstate.ishistory
//如果当前k线为历史k线,则返回true,否则返回false。
barstate.isnew
//如果脚本目前在新k线上计算着,则返回true,否则返回false。使用此变量的PineScript代码可以对历史记录和实时数据进行不同的计算。请注意,使用此变量/函数可能会导致指标重新绘制。
这些内置变量主要是限制策略执行的时间,比如在早上开盘前十五分钟小时内,大盘波动比较剧烈,匆忙的进行开平仓操作可能造成不必要的损失。例如可用以下限制,在早上9点前十五分钟内(hour == 9 and minute <= 15),不进行策略运行。具体的代码设置如下:
if not (hour == 9 and minute <= 15)
runtime.log('策略开始')
Pine语言还有一些其他内置变量,我们没有讲解到。大家可以在优宽社区Pine语言帮助文档查询其他内置变量的功能和使用方法。
(十四):内置函数:math.系列和ta.系列
大家好,在上节课的自定义函数课程中,我们进行的都是简单的逻辑判断或者指标的计算,然而通常情况下,在实际的量化测量策略中,我们通常需要多个成熟的指标进行综合应用和逻辑比较。因此,在策略函数中可以使用成熟的内置函数,可以避免代码的臃肿,减轻我们的工作量。
内置函数
Pine语言内置函数分类有以下几种:
- 1、字符串处理函数str.系列。
- 2、颜色值处理函数color.系列。
- 3、参数输入函数input.系列。
- 4、指标计算函数ta.系列。
- 5、画图函数plot.系列。
- 6、数组处理函数array.系列。
- 7、交易相关函数strategy.系列。
- 8、数学运算相关函数math.系列。
- 9、其它函数(时间处理、非plot系列画图函数、request.系列函数、类型处理函数等)。
可以看到,内置函数确实有很多类,如果你想实现一个函数功能,但是又不想重复造轮子,你可以很方便地在优宽社区帮助文档中查询。在这其中,color.系列,input.系列,和plot.系列我们在以前的课程都讲述过。本节课的内容,我将集中于数学运算相关函数math.系列和指标计算函数ta.系列内置函数的讲解。
math.系列内置函数
math.系列内置函数涉及到对交易指标的原始计算,基本上所有基础的对于数字和集合的处理,math系列函数都可以满足。如果你觉得成熟的指标过于老旧,喜欢挑战原创交易指标的同学,你一定不要错过。
math系列内置函数有很多,因此我进行了分类帮助大家更方便的记忆。
基本上可以分为三类,第一种处理单个数字,比如取整,取不同对数,或者获得数字符号等:
- math.abs(number) 绝对值
- math.ceil(number) 大于等于取整整数
- math.floor(number) 小于等于取整整数
- math.round(number) 四舍五入到最接近的整数; 如果使用了 precision 参数,则返回一个四舍五入到小数位数的浮点值
- math.exp(number) e的number次方
- math.log(number) 自然对数
- math.log10(number) 以10为底的对数
- math.pow(base, exponent) 数学幂函数
- math.sign(number) >0: 1; =0: 0; <0: -1
- math.sqrt(number) 平方根
runtime.log('绝对值',math.abs(-5)) // 5
runtime.log('⼤于等于取整',math.ceil(3.14)) //4
runtime.log('⼩于等于取整',math.floor(3.14)) //3
runtime.log('四舍五⼊',math.round(3.14,precision= 1) ) //3.1
runtime.log('e的number次⽅ ',math.exp(1)) //2.718281828459045
runtime.log('⾃然对数',math.log(math.e)) //1
runtime.log('以10为底的对数 ',math.log10(10)) //1
runtime.log('数学幂函数 ',math.pow(math.e, 1)) //2.718281828459045
runtime.log('符号',math.sign(-5) ) //-1
runtime.log('开方',math.sqrt(9) ) //3
第二种处理角度,根据三角函数,获得不同角度对应值。
- math.acos() 数字的反余弦
- math.asin() 反正弦
- math.atan() 反正切
- math.cos() 余弦
- math.sin() 正弦
- math.tan() 三角正切
- math.todegrees(radians) 返回以度为单位的近似等效角度
- math.toradians(degrees) 返回以弧度为单位的近似等效角度
math.sin(math.pi/6) // 0.4999
math.asin(0.5) // 0.52 = math.pi/6
math.todegrees(math.pi)// 180
第三种处理数据集合,比如取最大值,最小值和随机数等,这部分的内容会和array函数有些重合,我们后续会为大家讲解。
- math.max(number0, number1, ...) 返回多个值中最大值
- math.min(number0, number1, ...) 最小值
- math.avg(number0, number1, ...) 平均值
- math.sum(source, length) 返回x的最后y值的滑动综合
- math.random(min, max, seed) 返回伪随机值
math.max(1, 2, 3, 4, 5) //5
math.min(1, 2, 3, 4, 5) //1
math.avg(1, 2, 3, 4, 5) //3
math.sum(close, 3) //以3为周期的close滑动综合值
math.random(1, 5, 2) // 设定最小值为1,最大值为5,seed为2,每次随机返回值都一致
ta.系列内置函数
TA,全称“Technical Analysis”, 即技术分析指标,是Pine语言封装的金融量化的高级库,涵盖了50多种股票、期货交易软件中常用的技术分析指标,如 MACD、RSI、KDJ、动量指标、布林带等等。下面我为大家初步介绍下。
经过整理,ta.系列内置函数可分为以下子板块:
均线处理函数
研究发现,资产的长期价格呈现均值回复的特征,即从长期来看,资产的价格会回归均值。这也是均线理论被广泛应用的前提。因此,在量化指标中,方便的均线处理内置函数必不可少。
- ta.alma(series, length, offset, sigma, floor) Arnaud Legoux移动平均线。它使用高斯分布作为移动平均值的权重。
series (series int/float) 待执行的系列值。
length (series int) K线数量(长度).
offset (simple int/float) 控制平滑度(更接近1)和响应性(更接近0)之间的权衡。
sigma (simple int/float) 改变ALMA的平滑度。Sigma越大,ALMA越平滑。
floor (simple bool) 可选参数。在计算ALMA之前,指定偏移量计算是否为下限。默认值为false。 - ta.sma(source, length) 移动平均值
- ta.ema(source, length) 指数加权移动平均线
- ta.wma(source, length) 返回length K线的 source 的加权移动平均值
- ta.swma(source) 具有固定长度的对称加权移动平均线
- ta.hma(source, length) 船体移动平均线HMA
- ta.rma(source, length) RSI中使用的移动平均线
- ta.vwap(source) 成交量加权平均
- ta.vwma(source, length) length K线的 source 的成交量加权移动平均值
plot(ta.sma, 'sma')
plot(ta.ema, 'ema')
plot(ta.wma, 'wma')
指标计算
量化投资起源于上世纪60年代,而下面的每一个指标都曾“各领风骚”过,当然有些指标如今还在使用。如果你看到了某些指标符合你的交易思路,你可以在优宽平台一台代码尝试下。
- ta.rsi(source, length) 相对强度指数
- ta.tsi(source, short_length, long_length) 真实强弱指数
- ta.roc(source, length) lengthK线返回的source的变化率。
- ta.macd(source, fastlen, slowlen, siglen) MACD(平滑异同平均线)
- ta.bb(series, length, mult) 布林带。mult (simple int/float) 标准差因子。
- ta.bbw(series, length, mult) 布林带宽度。
- ta.cci(source, length) lengthK线返回的source的商品渠道指数。
- ta.cmo(series, length) 钱德动量摆动指标。
- ta.tr(handle_na) 真实范围
- ta.mfi(series, length) 资金流量指标。
- ta.kc(series, length, mult) 肯特纳通道。
- ta.kcw(series, length, mult) 肯特纳通道宽度。
- ta.atr(length) 真实波动幅度均值(ATR)
- ta.sar(start, inc, max) 抛物线转向指标。
start (simple int/float) 开始。
inc (simple int/float) 增加
max (simple int/float) 最大. - ta.dmi(diLength, adxSmoothing) dmi函数返回动向指数DMI。
diLength (simple int) DI Period。
adxSmoothing (simple int) ADX平滑周期 - ta.pivothigh(source, leftbars, rightbars) 此函数返回枢轴高点的价格。 如果没有枢轴高点,则返回“NaN”。
- ta.pivotlow(source, leftbars, rightbars) 此函数返回枢轴低点的价格。 如果没有枢轴低点,它返回“NaN”。
- ta.stoch(source, high, low, length) 随机指标。
- ta.supertrend(factor, atrPeriod) 超级趋势指标。
- ta.wpr(length) 威廉姆斯指标Williams %R。
- ta.cog(source, length) 基于统计学和斐波那契黄金比例的指标
pine_rsi(x, y) =>
u = math.max(x - x[1], 0) // upward ta.change
d = math.max(x[1] - x, 0) // downward ta.change
rs = ta.rma(u, y) / ta.rma(d, y)
res = 100 - 100 / (1 + rs)
res
if ta.rsi <= 20
strategy.entry("buy", strategy.long, qty=1)
if ta.rsi >= 30
strategy.close_all("buy", comment = "close long")
if ta.rsi >= 80
strategy.entry("short", strategy.short, qty=1)
if ta.rsi <= 70
strategy.close_all("short", comment = "close short")
plot(ta.rsi(close, 7), 'rsi值')
相对强弱指标(RSI)是技术指标中的一种,强弱指标理论认为,任何市价的大涨或者大跌,均在0—100之间波动。 根据常态分析,认为RSI值在30-70之间的变动属正常情况,在80—90时,被认为市场已达超买状态,至此市场价格自然面临回落调整;而在10—20时,被认为市场已达超卖状态,至此市场价格自然面临企稳回升。
统计计算
期货作为时间序列的数据,当然少不了对它的统计计算。基本的指标,平均数,中位数,方差,百分位数等,内置函数都有包含;初步的统计分析,相关分析,线性回归分析等,pine语言也可以满足。
- ta.highest(source, length) 过去k线的给定数目的最高值。
- ta.highestbars(source, length) 偏移到最高k线。
- ta.lowest(source, length) 过去k线的给定数目的最低值。
- ta.lowestbars(source, length) 过去k线的给定数目的最低值偏移。
- ta.dev(source, length) 衡量系列与其ta.sma之间的差异
- ta.stdev(source, length, biased) 标准差
- ta.variance(source, length, biased) length K线返回的source的方差
- ta.range(source, length) 序列中最小值和最大值之间的差。
- ta.mode(source, length) 序列的众数
- ta.median(source, length) 序列的中位数
- ta.cum(source) source 的累积(全部的)总和。
- ta.correlation(source1, source2, length) 相关系数。
- ta.linreg(source, length, offset) 线性回归曲线。一条最符合用户定义时间段内指定价格的线。它是使用最小二乘法计算的。此函数的结果使用以下公式计算:linreg = intercept + slope * (length - 1 - offset),其中 intercept 和 slope 是使用 source 系列的最小二乘法计算的值。
- ta.change(source, length) 当前值与前一个值之间的差分
- ta.mom(source, length) source价格和source价格lengthK线之前的动量。
- ta.percentile_linear_interpolation(source, length, percentage) lengthK线返回的source系列的第P个百分位数。
- ta.percentile_nearest_rank(source, length, percentage) lengthK线返回的source系列的第P个百分位数。
- ta.percentrank(source, length) lengthK线返回的source百分比排名。
- ta.barssince(condition) 如状况为true的k线数目。
runtime.log('最⾼值',ta.highest(close, 10) )
runtime.log('最⾼k线',ta.highestbars(close, 10) )
runtime.log('衡量均值差异', ta.dev(close, 10) )
runtime.log('标准差', ta.stdev(close, 10))
runtime.log('⽅差',ta.variance(close, 10))
runtime.log('range',ta.range(close, 10))
runtime.log('众数', ta.mode(close, 10))
runtime.log('中位数', ta.median(close, 10))
runtime.log('总和',ta.cum(close,10))
runtime.log('差分',ta.change(close,10))
runtime.log('百分比排名',ta.percentrank(close,10))
runtime.log(ta.barssince(close>open) )
plot(ta.correlation(close, volume, 14),'相关系数',overlay= false)
plot(ta.linreg(close, 14,0) ,'线性回归预测值',overlay= true)
逻辑判断
最后,ta系列内置函数还有一些帮助逻辑判断的函数
- ta.cross(source1, source2) 如果两个系列相互交叉则为true,否则为false。
- ta.crossover(source1, source2) 如果source1穿过source2则为true,否则为false。
- ta.crossunder(source1, source2) 如果source1在source2下交叉,则为true,否则为false。
- ta.falling(source, length) 如果当前 source 值小于 length K线返回的任何先前 source 值,则为true,否则为false。
- ta.rising(source, length) 如果当前 source 值大于 length K线返回的任何先前 source 值,则为true,否则为false。
- ta.valuewhen(condition, source, occurrence) 返回第n次最近出现的“condition”为true的K线的“source”系列值。
设置condition条件为'close>open',即为阳线,设置返回值为close,出现次数为n:
runtime.log('第一根阳线收盘价', ta.valuewhen(close > open , close, 0))
runtime.log('第二根阳线收盘价', ta.valuewhen(close > open , close, 1))
总体而言,本节课的内容确实比较多,其实大家也不必太过担心记忆这些函数。你可以首先记住大致的概念,而在你对大盘市场观察过程中,如果发现某些交易思路或者交易指标比较适合,你可以重新回想起来这些指标,去使用它,或者在此基础上编写新的量化指标,添加进入你的量化策略,也许下一个量化指标就是由你来命名的。
(十五):数组
大家好,今天我们来学习Pine语言array数组的操作。数组作为具有某一长度的元素集合,你可以添加,删除,修改你需要的元素,并进行相关的计算展示工作。尽管内置函数可以帮助我们进行某些数组集合的操作,但是很多时候,内置函数并不能满足我们所有的需要,因此我们需要创建数组进行我们所需要的操作。
数组创建
在前面的章节,我们学习过数组可由以下两种方式进行创建:
a = array.from(ele1,ele2...)
b = array.new(n,ele)
array.from不限制元素的类型,布尔值,字符型,数字都可以添加;array.from第一个值填写重复的个数,第二个值添加重复的元素(也可以不写,这样创建一个数量为n,元素为null的数组)。Pine语言中还有很多和类型相关的与array.new类似的函数:array.new_int()、array.new_bool()、array.new_color()、array.new_string()等。
数组可以在脚本的全局范围内声明,也可以在函数或if分支的本地范围内声明。var关键字也可以作用与数组的声明模式,使用var关键字声明的数组在每个策略周期执行完成后,会保留在这个策略周期内array的变动,而没有添加关键词var的array在每次策略周期内,都会重新更新一次。
var a = array.from(0)
b = array.from(0)
if bar_index == 1
array.push(a, bar_index)
array.push(b, bar_index)
else if bar_index == 2
array.push(a, bar_index)
array.push(b, bar_index)
else ibar_index == 3
runtime.log("a:", a)
runtime.log("b:", b)
runtime.error("stop")
可以看到a数组的变动都持续确定了下来,没有被重置过。b数组则在每个BAR上都被初始化。最终打印的时候仍然只有一个元素,数值0。
数组的增加和删除
数组的增加操作相关函数:
array.unshift():数组起始位置增加
array.insert():数组设定位置增加
array.push():数组末尾增加
a = array.from(1, 2, 3)
array.unshift(a, "X")
runtime.log("数组a:", a)
a2 = array.from(1, 2, 3)
array.insert(a2, 1, "Y")
runtime.log("数组a2:", a2)
a3 = array.from(1, 2, 3)
array.push(a3, "D")
runtime.log("数组a3:", a3)
runtime.error("stop")
数组的删除操作相关函数:
array.shift():删除数组起始位置
array.remove():删除数组设定位置元素
array.pop():删除数组末尾位置元素
array.clear():删除数组全部元素
a = array.from(1, 2, 3)
array.shift(a)
runtime.log("数组a:", a)
a2 = array.from(1, 2, 3)
array.remove(a2, 1)
runtime.log("数组a2:", a2)
a3 = array.from(1, 2, 3)
array.pop(a3)
runtime.log("数组a3:", a3)
a4 = array.from(1, 2, 3)
array.clear(a4)
runtime.log("数组a4:", a4)
runtime.error("stop")
数组的查询和改动
使用array.get获取数组中指定索引位置的元素,使用array.set修改数组中指定索引位置的元素。
array.get的第一个参数为要处理的数组,第二个参数为指定的索引。
array.set的第一个参数为要处理的数组,第二个参数为指定的索引,第三个参数为要写入的元素。
a = array.from(1,2,3,4,5)
runtime.log('元素1', array.get(a,0))
array.set(a,0,100)
runtime.log('元素1', array.get(a,0))
runtime.error('stop')
通过举例证明,array.get帮助我们获取所需位置的元素,array.set修改所需位置的元素。
队列应用
使用数组,以及数组的一些增加、删除函数我们可以构造出「队列」数据结构。队列是一种在编程领域经常使用的结构,队列的特点就是:先进先出。这样就可以确保队列中存在的数据都是最新的数据。
移动平均值sma大家都很熟悉,sma以固定周期为窗口,计算窗口内的平均值。使用array数组,我们可以构建sma计算的原始代码:
var a = array.new_float(0)
length = 10
if not barstate.ishistory
array.push(a, close)
if array.size(a) > length
array.shift(a)
sum = 0.0
for [index, ele] in a
sum += ele
avgPrice = array.size(a) == length ? sum / length : na
plot(avgPrice, title="avgPrice")
plot(ta.sma(close, length), title="ta.sma")
通过将10设为固定周期,在策略开始,首先使用push往array里添加元素,直到数组的长度大于10,使用shift删除数组第一个元素,确保窗口内的数字都是最新10个周期内的收盘价。接着利用一个for循环,计算最近10个周期内的收盘价总和。最后计算平均值,这里我们使用了一个三元操作符,确保收集够10个周期的收盘价时,才返回平均值。请注意,我们array创建需要声明是var,确保每个周期内数组的变动会记录下来。
对比于内置函数ta.sma,可以发现两个值是一致的。只是我们构建的函数是从10个周期后才开始呈现。
历史数据引用
数组同样可以使用历史引用操作符进行历史的引用。
a = array.new_float(1,close)
runtime.log(a[1])
runtime.log(close[1])
代码发现,数组a和close使用历史引用后,两个值是一致的。
数组计算
以数组为集合,可以进行统计特征平均值,最小值,最大值,标准差等的获取。
array.avg()求数组中所有元素的平均值array.min()求数组中最小的元素array.max()求数组中最大的元素array.stdev()求数组中所有元素的标准差
array.sum()求数组中所有元素的和
array.median()求数组中元素的中位数
array.mode()求数组中元素的众数
a = array.from(3, 2, 1, 4, 5, 6, 7, 8, 9)
runtime.log("数组a的算数平均:", array.avg(a))
runtime.log("数组a中的最小元素:", array.min(a))
runtime.log("数组a中的最大元素:", array.max(a))
runtime.log("数组a中的标准差:", array.stdev(a))
runtime.log("数组a的所有元素总和:", array.sum(a))
runtime.error("stop")
数组操作
array.concat(array1,array2): 合并或连接两个数组。
array.copy(): 复制数组。
array.join(array,"连接符号"): 将数组中的所有元素连接成一个字符串。
array.sort(array, order.ascending/order.descending): 按升序或降序排序。
array.reverse(): 反转数组(会修改原始数组)。
array.slice(数组,n1,n2): 对数组进行切片,以n1为开始,以n2为结束,遵循左闭右开的原则进行数据切片。
array.includes(数组,元素): 判断元素。
array.indexof(数组,元素): 返回参数传入的值首次出现的索引。如果找不到该值,则返回 -1。
array.lastindexof(数组,元素): 找到最后一次出现的值。
a = array.from(1, 2, 3, 4, 5, 6)
b = array.from(11, 2, 13, 4, 15, 6)
runtime.log("数组a:", a, ", 数组b:", b)
runtime.log("数组a,数组b连接在一起:", array.concat(a, b))
c = array.copy(b)
runtime.log("复制一个数组b,赋值给变量c,变量c:", c)
runtime.log("使用array.join处理数组c,给每个元素中间增加符号+,连接所有元素结果为字符串:", array.join(c, "+"))
runtime.log("排序数组b,按从小到大顺序,使用参数order.ascending:", array.sort(b, order.ascending)) // array.sort函数修改原数组
runtime.log("排序数组b,按从大到小顺序,使用参数order.descending:", array.sort(b, order.descending)) // array.sort函数修改原数组
runtime.log("数组a:", a, ", 数组b:", b)
array.reverse(a) // 此函数修改原数组
runtime.log("反转数组a中的所有元素顺序,反转之后数组a为:", a)
runtime.log("截取数组a,索引0 ~ 索引3,遵循左闭右开区间规则:", array.slice(a, 0, 3))
runtime.log("在数组b中搜索元素11:", array.includes(b, 11))
runtime.log("在数组a中搜索元素100:", array.includes(a, 100))
runtime.log("将数组a和数组b连接,搜索其中第一次出现元素2的索引位置:", array.indexof(array.concat(a, b), 2), " , 参考观察 array.concat(a, b):", array.concat(a, b))
runtime.log("将数组a和数组b连接,搜索其中最后一次出现元素6的索引位置:", array.lastindexof(array.concat(a, b), 6), " , 参考观察 array.concat(a, b):", array.concat(a, b))
runtime.error("stop")
array函数中还有一些其他的内置函数的用法,大家可以在优宽的Pine语言帮助文档中查询获得。
(十六):交易函数:strategy系列
大家好,今天我们来学习Pine语言中交易函数strategy系列。在学习完所有指标计算和信号判断语法结构后,我们对接真实的市场面对最后一道关卡:交易。期货交易之所以引人入胜,与其独有的T+0交易机制密切相关。一个点的波动就是盈利或者损失,成功的进行交易操作就可以将盈利立即收入囊中。所以怎样在合适的点位进行正确交易方向的操作,就需要strategy系列函数的应用。
开仓
在交易信号判断确定后,我们需要立即进行开仓操作,以免错过黄金交易时间。
strategy.entry
strategy.entry函数是我们写策略时比较重要的一个下单函数,该函数比较重要的几个参数为:id, direction, qty, when等。
参数:
- id:可以理解为给某个交易头寸起个名字用于引用。可以引用这个id撤销、修改订单、平仓。
- direction:如果下单方向是做多(买入)该参数就传strategy.long这个内置变量,如果要做空(卖出)就传strategy.short这个变量。
- qty:指定下单的量,如果不传这个参数使用的就是默认下单量。
- when:执行条件,可以指定这个参数来控制当前这个下单操作是否触发。
- pyramiding: 同一个方向交易交易的次数。
strategy(pyramiding=2)
strategy.entry("enter long1", strategy.long, 1, when = bar_index==1)
strategy.entry("enter long2", strategy.long, 2, when = bar_index==2)
strategy.entry("enter long3", strategy.long, 3, when = bar_index==3)
设置pyramiding=2,因此只能进行两次的加仓,第三次开仓不成功,设置条件when,在第一根k线,第二根k线开仓,这里也可以设置价格比如open>close等,qty设置开仓的数量。
我们再加上一个开空仓,看看有什么变化。
strategy.entry("enter short1", strategy.short, 1, when = bar_index==4)
可以发现,在开空仓之前,会平掉所有的多头仓位。由于Pine语言脚本持仓只能有一个方向,即如果有和当前持仓方向相反的信号触发会平掉当前持仓再根据信号触发开仓。
strategy.entry函数的具体执行细节受strategy函数调用时的参数设置控制,也可以通过「Pine语言交易类库模版参数」设置控制,Pine语言交易类库模版参数控制的交易细节更多,具体可以查看链接的文档。
开仓信息查询
-
strategy.opentrades
未关闭或者继续持有的交易数量。如果没有,则显示0。 -
strategy.opentrades.entry_bar_index(trade_num)
返回未平仓交易的入场的ID。trade_num (series int)是未平仓交易的交易编号。第一笔交易的编号为零。所以可以用strategy.opentrades-1代替。 -
strategy.opentrades.entry_price(trade_num)
返回未平仓交易的入场价格。 -
strategy.opentrades.profit(trade_num)
返回未平仓交易的盈亏。损失表示为负值。 -
strategy.opentrades.size(trade_num)
返回未平仓交易中的交易方向和合约数量。如果该值>0,则市场仓位为多头。如果该值<0,则市场仓位为空头。 -
strategy.opentrades.entry_time(trade_num)
返回未平仓交易入场的UNIX时间。
if bar_index == 1
strategy.entry("Long1", strategy.long)
runtime.log('opentrades',strategy.opentrades)
runtime.log('bar_index',strategy.opentrades.entry_bar_index(0))
runtime.log('entry_price',strategy.opentrades.entry_price(0))
runtime.log('entry_profit',strategy.opentrades.profit(0))
runtime.log('entry_size',strategy.opentrades.size(0))
runtime.log('entry_time',strategy.opentrades.entry_time(0))
平仓
strategy.close
strategy.close函数用于平仓指定标识ID的入场持仓仓位。主要参数有:id,when,qty。
参数:
- id:需要平仓的入场ID,就是我们使用strategy.entry等入场下单函数开仓时指定的ID。
- when:执行条件。
- qty:平仓数量。
通过一个例子来熟悉这个函数的使用细节:
strategy("close Demo", pyramiding=3)
strategy.entry("long1", strategy.long, 1)
if strategy.opentrades >= 3
// strategy.close("long1") // 多个入场订单,不指定qty参数,全部平仓
// strategy.close() // 不指定id参数,会平掉当前的持仓
// strategy.close("long2") // 如果指定一个不存在的id则什么都不操作
// strategy.close("long1", qty=1) // 指定qty参数平仓
// strategy.close("long1", qty_percent=70) // qty_percent设置50即为平掉long1标识仓位的50%持仓
// strategy.close("long1", qty_percent=80, when=close>open) // 指定when参数
测试策略展示了开始连续三次做多入场,入场标识ID均为“long1”,然后使用strategy.close函数的不同参数设置平仓时回测出的不同结果。可以发现strategy.close这个函数没有参数可以指定平仓下单价格,这个函数主要用于立即以当前市场价格平仓。
strategy.close_all
strategy.close_all函数用于平掉当前所有持仓。所以strategy.close_all被调用时会平掉当前方向上的所有持仓。strategy.close_all函数的主要参数为:when。
参数:
- when:执行条件。
我们使用一个例子来观察:
strategy.entry("long1", strategy.long, 1, when=bar_index==1)
strategy.entry("long2", strategy.long, 1, when=bar_index==2)
strategy.close_all(when=bar_index==3)
可以发现strategy.close_all这个函数没有参数可以指定平仓下单价格,这个函数主要用于立即以当前市场价格平仓。
strategy.exit
strategy.exit函数被用于入场持仓的平仓操作,与该函数不同的是strategy.close和strategy.close_all函数是以当前市场价格立即平仓。strategy.exit函数会根据参数设置进行计划平仓。
参数:
- id:当前这个平仓条件单的订单标识符ID。
- from_entry:用于指定要进行平仓操作的入场ID。
- qty:平仓数量。
- qty_percent:平仓百分比,范围:0 ~ 100。
- profit:利润目标,以点数表示。
- loss:止损目标,以点数表示。
- limit:利润目标,以价格指定。
- stop:止损目标,以价格指定。
- when:执行条件。
使用一个测试策略来理解各个参数使用。
strategy("strategy.exit Demo")
strategy.entry("long1", strategy.long, 1, limit=1)
strategy.entry("long2", strategy.long, 1)
strategy.exit("exit1", "long1", profit=50) // 由于long1入场订单没有成交,因此ID为exit1的出场订单也处于暂待状态,直到对应的入场订单成交才会放置exit1
//strategy.exit("exit2", "long2", qty=1, profit=20) // 指定参数qty,当盈利20的时候,平掉ID为long2的持仓
strategy.exit("exit2", "long2", qty=1), profit=20, loss=20) // 指定参数qty,当盈利20或者亏损20的时候,平掉ID为long2的持仓
strategy.exit("exit2", "long2", qty=1) // 所有参数 'profit', 'limit', 'loss', 'stop', 'trail_points', 'trail_offset' 皆为“NaN”,则命令将失败。
使用实时价模型回测测试,这个测试策略开始执行了2个入场操作(strategy.entry函数),“long1”故意设置了limit参数,挂单价格为1使其无法成交。然后测试条件出场函数strategy.exit。使用了按点数止盈、按价格止盈。价格一跳乘以10作为止盈价差,价格一跳即内置变量syminfo.mintick。例如玻璃和纯碱的价格一跳是20元,而螺纹钢和甲醇等价格一跳是10元。
当然还有strategy.closetrades查询已平仓交易的一些信息,大家也可以尝试下。
- strategy.closedtrades.entry_bar_index:已平仓进场bar_index
- strategy.closedtrades.exit_price:已平仓出场价格
- strategy.closedtrades.exit_bar_index:已平仓出场bar_index
- strategy.closedtrades.entry_id:已平仓进场id
- strategy.closedtrades.entry_price:已平仓入场价格
- strategy.closedtrades.entry_time:已平仓入场时间
- strategy.closedtrades.profit:已平仓利润
- strategy.closedtrades.size:已平仓数量
- strategy.closedtrades.exit_time:已平仓出场时间
strategy.cancel
strategy.cancel函数用来取消/停用所有预挂单的命令。这些函数strategy.order, strategy.entry , strategy.exit可以产生入场ID。该函数主要参数为:id、when。
参数:
- id:所要取消的入场ID。
- when:执行条件。
这个函数很好理解,就是用来取消没有成交的入场命令的。
strategy("strategy.cancel Demo",)
strategy.entry("long1", strategy.long, 0.1, limit=1)
strategy.cancel("long1")
strategy.cancel_all
strategy.cancel_all函数和strategy.cancel函数类似。取消/停用所有预挂单命令。可以指定when参数。
参数:
- when:执行条件。
strategy("strategy.cancel Demo", pyramiding=3)
strategy.entry("long1", strategy.long, 0.1, limit=1)
strategy.entry("long2", strategy.long, 0.2, limit=2)
strategy.entry("long3", strategy.long, 0.3, limit=3)
strategy.cancel_all()
strategy.order
strategy.order函数的功能、参数设置等几乎与strategy.entry一致,区别为strategy.order函数不受strategy函数的pyramiding参数设置影响,没有下单次数限制。
参数:
- id:可以理解为给某个交易头寸起个名字用于引用。可以引用这个id撤销、修改订单、平仓。
- direction:下单方向
- qty:指定下单的量,如果不传这个参数使用的就是默认下单量。
- when:执行条件,可以指定这个参数来控制当前这个下单操作是否触发。
- limit:指定订单限价价格。
- stop:止损价格。
我们就使用strategy.order没有下单次数限制这个特性,构造一个脚本帮助我们理解:
strategy(title = "simple strategy order example")
strategy.order("buy", strategy.long, 1, when = open > high[1]) // buy by market if current open great then previous high
strategy.order("sell", strategy.short, 1, when = open < low[1]) // sell by market if current open less then previous low
交易是一门高深的学问,需要考验你的认知,耐心,毅力和决心。你需要根据你的交易理念和策略,选择做日内短线交易或者长期趋势交易,,因此反应在代码里就是你的交易函数的选择和交易参数的使用。本章内容一开始听起来确实让人比较迷惑,因为内容很多,灵活运用比较困难。幸好我们具有优宽平台,免费的代码回测可以帮助你进行足够多次数的试错,从而了解到每个函数的用法,进而不断优化你的量化策略系统。我们下节课再见!
(十七):量化交易指标:趋势指标
大家好,在前面的课程中。我们完整的学习了Pine语言的语法结构及其交易函数。至此,Pine语言的理论讲解部分告一段落,从今天起,我们开始转向对接真实的期货交易市场,研究怎样利用Pine语言帮助进行量化交易,搭建自己的量化交易系统。
身为一个新手,对于大盘的理解可以用四个字来形容,眼花缭乱。为什么在自己不交易的时候,指标嗖嗖的上涨或者下跌,而自己一旦买入或者卖出,那指标就磨磨蹭蹭,逐笔浮盈的颜色始终在黑色(平价)或者绿色(亏损)转换,有时候会怀疑是否几个亿的资金大盘是否都是在针对自己?于是,寻找不确定性中的确定性变成了我们盈利的突破点所在。
指标作为从历史数据中统计出来的结果,代表着一定的惯性。如果趋势继续存在,那么指标就是在不确定的海洋中,一盏弱小的指引方向的灯塔。纵使灯塔也可能存在于海市蜃楼中,指标也可以作为辅助判断的依据。因此,从今天起,我将带领大家学习量化指标。因此,本部分的内容将对量化指标系统进行一个较为系统的讲解。当然,这部分的内容一方面是在学习怎样利用Pine语言构造量化指标,更重要的是学习这些量化指标的理念。在参考了各类金融软件系统对指标的讲解和网络上各路视频的讲解,可以发现指标的使用方法很多很杂,各类策略有可能还存在冲突的地方,没有一以贯之的黄金策略。因此,本部分的内容将以讲解指标概念和计算方法为关键点,怎样利用指标编写一个完美的交易策略还需要大家对市场有着更为深刻的理解,所以在这部分的课程中,对于指标的策略展示,教学展示的策略都比较简单,仅为教学展示用,不可轻易应用于实盘,仅仅起到一个抛砖引玉的作用。在真实的市场中,需要根据自己的理解,对指标进行更多的优化,才能真正的将这些指标应用于自己的量化交易系统中。
指标系统可以分为趋势指标,反趋势指标,压力支撑指标,量价指标,能量指标和成交量指标等。趋势交易策略,也被称为趋势跟随策略,旨在延续现有的趋势。本节课的内容,我们将以趋势指标为讲述对象,讲解几个示范指标的概念及其在Pine语言中的使用方法。
BBI
BBI指标,即多空指标,英文全名为"Bull And Bear lndex",简称BBI,是一种将不同日数移动平均线加权平均之后的综合指标,属于均线型指标,一般选用3日、6日、12日、24日等4条平均线。在使用移动平均线时,投资者往往对参数值选择有不同的偏好,而多空指标恰好解决了中短期移动平均线的期间长短合理性问题。很明显,在BBI指标中,近期数据较多,远期数据利用次数较少,因而是一种变相的加权计算。由于多空指标是一条混合平均线,所以既有短期移动平均线的灵敏,又有明显的中期趋势特征,适于稳健的投资者。
计算公式
- 3日均价=3日收盘价之和/3
- 6日均价=6日收盘价之和/6
- 12日均价=12日收盘价之和/12
- 24日均价=24日收盘价之和/24
- BBI=(3日均价+6日均价+12日均价+24日均价)/4
基本用法
1.价格位于BBI上方,视为多头市场。
2.价格位于BBI下方,视为空头市场。
3.下跌行情中,若当日收盘价跌破BBI曲线,表示多转空,为卖出信号。
4.上涨行情中,若当日收盘价升越BBI曲线,表示空转多,为买入信号。
5.上升回档时,BBI为支持线,可以发挥支撑作用。
6.下跌反弹时,BBI为压力线,可以发挥阻力作用。
BBI的周期也有多种选择,短线选择较短周期,长线选择较长周期。
Pine语言代码
首先计算四个均价,然后平均计算BBI,接着利用和close价格的“黄金”或者“死亡”交叉进行买卖信号的判断。
BBI = (ta.sma(close,3)/3 + ta.sma(close,6)/6 + ta.sma(close,12)/12 + ta.sma(close,24)/24)/4
DIF= BBI - close
plot(BBI, title = 'BBI', color=color.blue)
plot(DIF, title = 'DIF', color=histLine > 0 ? color.red : color.green, style=plot.style_histogram)
if ta.crossover(DIF,0)
strategy.entry('long',strategy.long,comment = 'long')
if ta.crossunder(DIF,0)
strategy.entry('short',strategy.short,comment = 'short')
优势
BBI指标,可以综合不同时间段的移动平均线的具体数值,数值更加具体和客观。
使用简单,可以轻松的判断多空趋势。
缺点
BBI的本质上是对MA的一种改进,所以,也具有一些类似于MA的缺点:
1、指标信号的滞后性。
2、指标信号的频发现象,特别在趋势不明朗时,这种现象更为严重。
3、指标单一。
相似指标:TRIX
三重指数平滑平均线(TRIX)属于中长线指标。它过滤掉许多不必要的波动来反映股价的长期波动趋势。TRIX指标又叫三重指数平滑移动平均指标,其英文全名为“Triple Exponentially Smoothed Average”,是一种研究股价趋势的长期技术分析工具。与BBI理念类似,TRIX使用EMA作为均值的计算方法。
MACD
概念
MACD(Moving Average Convergence and Divergence)在我们前面的课程中多次被提到,它 是利用收盘价的短期(常用为12日)指数移动平均线与长期(常用为26日)指数移动平均线之间的聚合与分离状况,对买进、卖出时机作出研判的技术指标。
计算方法
一、差离值(DIF值):
先利用收盘价的移动平均值(12日/26日)计算出差离值。EMA是指数平均数指标。
DIF的计算方法为:
DIF = EMA(close,12) - EMA(close,26)
二、讯号线(DEM值,又称MACD值):
计算出DIF后,会再画一条“讯号线”,通常是DIF的9日移动平均值。
DEA = EMA(DIF,9)
三、柱形图或棒形图(histogram / bar graph):
接着,将DIF与DEM的差画成“柱形图”(MACD bar / OSC)。
OSC = DIF - DEM
基本用法
- MACD金叉:DIF由下向上突破 DEA,OSC>0为买入信号。
- MACD死叉:DIF由上向下突破 DEA,OSC<0为卖出信号。
- DEA 在盘整局面时失误率较高,但如果配合RSI 及KDJ指标可适当弥补缺点。
Pine语言代码
首先利用内置函数ta.macd计算出来三个指标值,macdLine(DIF), signalLine(DEA), histLine(OSC)并进行画图展示。接着利用histLine进行开平仓的信号判断,当上穿0线(crossover)进行买入,当下穿(crossunder)0线进行卖出。
[macdLine, signalLine, histLine] = ta.macd(close, 12, 26, 9)
plot(macdLine, title = 'macdLine', color=color.blue)
plot(signalLine, title = 'signalLine', color=color.orange)
plot(histLine, title = 'histLine', color=histLine > 0 ? color.red : color.green, style=plot.style_histogram)
if ta.crossover(histLine,0)
strategy.entry('long',strategy.long,comment = 'long')
if ta.crossunder(histLine,0)
strategy.entry('short',strategy.short,comment = 'short')
缺点
具有滞后性。当行情忽上忽下幅度太小或盘整时,按照信号进场后随即又要出场,买卖之间可能没有利润,也许还要赔点价差或手续费。在同时,一旦行情迅速大幅涨跌,MACD不会立即产生信号,此时,MACD无法发生作用。
相似指标:DMA
DMA指标是指平行线差指标,是依据快慢两条移动平均线的差值情况来分析价格趋势的一种技术分析指标。DMA只是以收盘价(CLOSE)作为唯一的数据源,进行平均算数。DMA和MACD的区别是DMA利用MA计算,而MACD利用EMA计算。
DMA指标的公式:
DIF:MA(CLOSE,N1)-MA(CLOSE,N2);
DIFMA:MA(DIF,M);
输出DIF:收盘价的N1日简单移动平均-收盘价的N2日简单移动平均;
输出均线DIF:DIF的M日简单移动平均;
应用
1、DIF线向上穿破DIFMA,即买入信号;
2、DIF线向下穿破DIFMA,即卖出信号。
SAR
概念
SAR指标又叫抛物线指标或停损转向操作点指标,其全称叫“Stop and Reverse,缩写SAR”,是一种简单易学、比较准确的中短期技术分析工具。
这种指标与移动平均线的原理颇为相似,属于价格与时间并重的分析工具。由于组成SAR的点以弧形的方式移动,故称“抛物转向”。
从SAR指标英文全称知道它有两层含义。一是“stop”,即停损、止损之意,这就要求投资者在买卖某个品种之前,先要设定一个止损价位,以减少投资风险。而这个止损价位也不是一直不变的,它是随着价格的波动止损位也要不断的随之调整。目标是既可以有效地控制住潜在的风险,又不会错失赚取更大收益的机会。
SAR指标的英文全称的第二层含义是“Reverse”,即反转、反向操作之意,这要求投资者在决定投资前先设定个止损位,当价格达到止损价位时,投资者不仅要对前期买入的品种进行平仓,而且在平仓的同时可以进行反向做空操作,以谋求收益的最大化。
计算方法
以计算日SAR为例,计算方法如下:
先选定时间,判断价格是在上涨还是在下跌:
若是看涨,则进场第一天的SAR必须是近期内的最低价,若是看跌则进场第一天的SAR必须是近期内的最高价;
第二天的SAR则为第一天的最高价(看涨时)或最低价(看跌时)与第一天的SAR的差距乘上调整系数,再加上第一天的SAR就可求得。
按逐步递推的方法,每日的SAR可日纳如下:
SAR(N)= SAR(N-1)+ AF * [ EP(N-1)-SAR(N-1)]
其中SAR(N)为第N日的SAR值,AF是调整系数,EP为极点价;
第一个调整系数AF为0.02,若每隔一天的最高价比前一天的最高价还高,则AF递增0.02,若未创新高,则AF沿用前一天的数值,但调整系数最高不超过0.2;
若是买进期间,计算出某日的SAR比当日或前一日的最低价还高,则应以当日或者前一日的最低价为某日之SAR,卖出期间也对应服从类似原则。
基本用法
- 收盘价跌破SAR,多头停损,开始空头交易;
- 收盘价突破SAR,空头停损,开始多头交易。
代码
可以使用Pine语言内置函数或者自定义函数编写一下:
sar = ta.sar(0.02, 0.02, 0.2)
plot(sar, style=plot.style_cross, linewidth=3)
if ta.crossover(close,sar)
strategy.entry('long',strategy.long,comment = 'long')
if ta.crossunder(close,sar)
strategy.entry('short',strategy.short,comment = 'short')
缺点
同样,SAR也具有滞后性,不能及时处理突发的大盘变化。
在我们上述的指标之外,趋势指标还包括动量指标(MTM),TWR宝塔线指标,DDI
方向标准离差指数,大家有兴趣都可以研究一下。总体来说,这些指标都试图寻找某一时间段内的惯性趋势,在大盘突破这些确定性趋势的时候,做出进场的信号。因为这些指标都是以一定时间段反应后,才做出的决断,在一方面,都存在滞后性的问题;在另一方面,也对市场的突发情形不能做出及时的判断。因此,针对不同的品种需要设置不同的策略周期,比如沥青日内波动比较剧烈,策略周期可以设置小一点;
使用MACD策略,对于沥青以小时为策略周期的回测收益:
对于沥青以天为策略周期的回测收益:
而玻璃的日内波动比较小,策略周期可以设置大一点。
使用MACD策略,对于玻璃以小时为策略周期的回测收益:
对于玻璃以天为策略周期的回测收益:
当然,这些品种的特点并不是固定不变的,你需要及时跟踪市场趋势,做出及时的参数调整和变化。正如传言而言,一个好的量化工程师每天的代码量不超过三行,但是“行行千金”。努力吧,少年!我们下节课再见!
(十八):量化交易指标:反趋势指标
大家好,今天我们继续量化指标的学习。在金融技术分析指标中,反趋向类指标,也就是震荡指标,是一个重要的分类。其中KDJ(随机指标)、RSI(相对强弱指标)、CCI(顺势指标)等指标都是各类证券分析软件中的常用指标。其实反趋向类指标和趋向类指标一样,都是判断价格走势趋向的指标,但是使用了逆向思维。反趋势提供一种与趋势跟踪同样有效的系统性的、保守的交易框架,但使用完全相反的方法。与趋势跟踪系统相比,反趋势系统通常交易区间更短,成功率更高一些,成功/失败比率更小一些。经过统计发现,一个典型的反趋势策略将会比趋势跟踪策略交易更频繁一些,成功率在55%-60%之间,成功交易与失败交易的比率小于1.5。因此,在一定程度上可以认为,在长期趋势较为明显的情况下,趋势指标是比较适合的;而长期趋势不明显,日内波动比较大的情况下,反趋势策略比较适合。
BIAS
概念
BIAS乖离率是用价格指数与移动平均线的比值关系,来描述价格与移动平均线之间的偏离程度。乖离率功能主要是通过测算价格在波动过程中与移动平均线出现偏离程度,从而得出价格在剧烈波动时因偏离移动平均趋势而造成可能的回挡或反弹,以及价格在正常波动范围内移动而形成继续原有势的可信度。
计算方法
BIAS(N) = (收盘价 - N周期移动平均价) / N周期移动平均价 * 100
应用规则
(1)若价格在移动平均线之上,乖离率为正,反之为负;当价格与平均线相同,乖离率为零;
(2)正乖离率值越大,说明价格向上偏离移动平均线的程度越大,有可能回档下调;
(3)负乖离值越小,表明价格向下偏离移动平均线的程度越大,随时可能反弹;
(4)多头市场的狂涨与空头市场的狂跌会使乖高率达到意想不到的百分比,但是出现次数极少,时间亦短。
Pine代码
我们这优宽平台进行一下乖离率指标的复现,由于Pine语言没有现成的内置函数,我们可以自己计算。首先设置研究周期n,接着计算今日收盘价和N个周期收盘价的移动平均值之差,然后除以周期移动平均值就可以获得,然后利用画图函数在图表中呈现出来。
bias5 = (close - ta.sma(close,5))/ta.sma(close,5)*100
bias10 = (close - ta.sma(close,10))/ta.sma(close,10)*100
bias20 = (close - ta.sma(close,20))/ta.sma(close,20)*100
plot(bias5, title = 'bias5')
plot(bias10, title = 'bias10')
plot(bias20, title = 'bias20')
相似指标:B3612
36乖离指标考虑不同周期的移动平均线之间的差距,衡量短期和中期投资者获利的相互状态,进而评估短、中期投资者对后市的看法。不同周期移动平均线之间的差距称为乖离,3日平均数值与6日平均数值之间的差值为B36,6日平均数值与12日平均数值之间的差值为B612。
【计算方法】
B36 = 最近3日的收盘价之和 / 3 - 最近6日的收盘价值和 / 6
B612 = 最近3日的收盘价之和 / 6 - 最近6日的收盘价值和 / 12
相似指标ROC
ROC(RATE OF CHANGE),中文名称:变动率指标。该指标测量价格动量,可以用来监视常态性和极端性两种行情,对买卖信号提供强有力的参考。
计算方法
ROC = (今收盘 - 前N周期收盘) / 前N周期收盘 * 100
相似指标OSC
OSC摆动量,反映的是价格与移动平均价的偏离的绝对距离,OSC的构造思想同Bias是相同的。
计算方法
OSC = 当周期收盘价 - N周期的平均价
CCI
概念
CCI(Commodity Channel Index),中文名称:顺势指标。CCI指标主要是用来对付极端行情的,适合于快进快出的短线。
计算方法
计算方法是商品的典型价格与其简单移动平均线之间的差值除以典型价格的平均绝对偏差。该指数按0.015的倒数进行缩放,以提供更多可读的数字。
TP = (最高价 + 最低价 +收盘价) / 3
MA = 最近N周期TP的累计和 / N
MD = (最近N周期(MA - TP)累计和) / N
CCI(N) = (TP - MA) / 0.015*MD
说明:N值为14
应用规则
(1)CCI的常态区为 -100至+100;
(2)当CCI从0~+100的正常范围内,由下往上突破+100时,价格有可能出现强势上涨,是买入的时机;当CCI从+100之上,由上往下跌破+100,价格短线有可能出现回调,是卖出的时机;
(3)当CCI从0~-100的正常范围内,由上往下跌破-100时,价格有可能出现弱势下跌,是抛出的时机。当CCI从-100的下方,由下往上突破-100时,有可能出现反弹,可逢低买入;
(4)当价格创出新高,而CCI没有同步创出新高时,顶背离出现,短线价格有可能出现回挡,可逢高卖出;
(5)当价格创出新低,而CCI没有同步创出新低时,底背离出现,短线价格有可能出现反弹,可逢低买入。
代码
cci = ta.cci(close, 14)
plot(cci, title = 'cci')
hline(-100, title = 'bottom_line')
hline(0, title = 'horizon_line')
hline(100, title = 'up_line')
KDJ
KDJ指标是目前中国金融市场最普及的指标之一,全称为随机指标(Stochastics),它综合了动量观念、强弱指标及移动平均线的优点,是波段操作的有力武器。
计算方法
以计算日KDJ为例,计算方法如下:
对每一交易日求RSV(未成熟随机值)
RSV = (收盘价-最近N日最低价) / (最近N日最高价-最近N日最低价) × 100
K线 : K值 = (M1-1)/M1 * 前一日K值 + 1/M1 * 当日RSV ;
D线 :K值 = (M2-1)/M2 * 前一日D值 + 1/M2 * 当日K值 ;
J线 : 3 × 当日K值 - 2 × 当日D值
参数:N、M1、M2 天数,一般取9、3、3
应用规则
(1)D > 80,超买;D < 20,超卖;J > 100%超买;J < 10%超卖;
(2)线K向上突破线D,买进信号;线K向下跌破线D,卖出信号;
(3)线K与线D的交叉发生在70以上,30以下,才有效;
(4)KD指标不适于发行量小,交易不活跃的品种;
(5)KD指标对大盘和热门大盘品种有极高准确性。
Pine代码
hgst = ta.highest(high,9)
lwst =ta.lowest(low,9)
rsv=(close-lwst)*100/(hgst-lwst)
k = 0.0
d = 0.0
j = 0.0
k := (rsv+2*nz(k[1]))/3
d := (k+2*nz(d[1]))/3
j := 3*k - 2*d
//Step Three: plotting
plot(k,title = 'k', color=color.blue)
plot(d,title = 'd',color = color.orange)
plot(j,title = 'j',color =color.black)
hline(80,color=color.red,linestyle = hline.style_dashed, linewidth =1)
hline(20,color=color.green, linestyle = hline.style_dashed,linewidth = 1)
if (k <20 and d <20 and k>d and k[1] <d[1])
strategy.entry('long', strategy.long)
if (k >80 and d >80 and k<d and k[1] > d[1])
strategy.close('long')
相似指标:SLOWKD(K线)
指标介绍
Slowed KD是在KD的基础上,对D再进行一次平滑,选择的平滑工具是移动平均,而不是指数平滑。Slowed KD中的K就是KD中的D,Slowed KD中的D是KD中的D的值的移动平均;经过平滑的比未经过平滑的慢,所以Slowed KD同KD的本质没有什么不同。
总结
依据前人经验,大部分反趋势模型寻找卖掉短期内超买的和买入短期内超卖的机会。这些目标有点象在等待橡皮筋拉伸到它的极限的时机,然后打赌他们会回弹到一个相对松弛的状态。这些行为使得反趋势交易模型在市场缺乏方向性或者波动性很大时斩获颇丰,并能够在市场拐点出现的时候迅速反应。反趋势模型的缺点是在稳定的、趋势性较强的市场环境中他们经常交易不顺,也就是常说的‘赢小钱亏大钱’。
一个短期的反趋势系统想要成功,价格必须要在指数的长期趋势或者某些其他的显著的价格点附近不断的前前后后摇摆。这种市场反应意味着市场价格移动包含足够的噪音以及波动性,从而带来反趋势交易的盈利机会。
(十九):量化交易指标:压力支撑指标
大家好,今天我们继续量化指标的学习:压力支撑指标。我们常常可以看见价格波动至某一位置,就不再按原有趋势运动,好像存在着某种力量,这就是我们经常讲到的压力和支撑,股价波动的趋势往往会在这些位置发生变化,例如趋势的延缓、加速或反转等。压力和支撑实际上受供求关系的影响,在上涨的过程中,随着获利盘的增多卖盘的供应逐渐大于买盘的需求,从而影响了价格的继续上行,形成了压力;而下跌的过程中,随着价格的降低买盘的需求逐渐强于卖盘的供应,从而价格上形成向上的支撑推动力。
压力线:当价格上涨到某一个价位时,就会产生对价格起到压制作用,这些高点价位之间的可连续称之为压力线。
支撑线:当价格下跌到某一低点后开始反弹,在该价位附近有支撑,这些低点之间的连续称之为支撑线。
压力和支撑的互相转化,在支撑位附近,如果有利空消息或有大资金撤离等因素刺激,跌破有效支撑后,将会上顺势寻找下一个支撑位,而该支撑位将转换成下跌后的上涨阻力位。同样的阻力位附近,如果利好消息出台或有大资金进场等因素刺激,形成有效的突破后,该阻力位将转化为上涨后一个下跌的支撑位。压力与支撑的判断分析是技术分析中非常重要的依据之一。阻力的有效突破往往是行的上涨阶段的开始,支撑的有效跌破也是新的下跌的阶段的开始。如果把握好压力和支撑的运动规律,可以较好的的把握每一次的买入和卖出的时机,从而得到较好的回报,适合各种行情。
Bollin布林线
指标介绍
Bollin布林线是一个路径型指标,由上限和下限两条线,构成一个带状的路径。价格超越上限时,代表超买,价格超越下限时,代表超卖。布林线指标的超买超卖作用,只能运用在横向整理的行情。
计算方法
中线MB = N周期移动平均收盘价
偏移值 = P * N周期收盘价的标准差
上轨UP(阻力线) = 中线 + 偏移值
下轨DOWN(支撑线) = 中线 - 偏移值
参数:N为周期数;P为乘数,如果假设价格波动服从正态分布,P为设置为3。
应用规则
(1)布林线利用波带显示价格的安全高低价位;
(2)价格向上穿越支撑线,买入信号;
(3)价格向上穿越阻力线,卖出信号;
(4)当易变性变小,而波带变窄时,激烈的价格波动有可能马上发生;
(5)高低点穿越上轨或下轨,立刻又回到波带内会有短期回档或短期反弹发生;
(6)波带的移动对寻找目标值有很大帮助。
Pine代码
在优宽平台进行一下指标的复现,首先使用参数设置周期和乘数,接着使用布林带的内置函数计算中线,上线阻力位和下线支撑位,使用plot函数在图表中进行三条线的呈现;来看一下指标的使用,当收盘价突破阻力位时,代表上涨趋势的来临,可以开多仓;反之,收盘价跌破支撑位,可以开空仓。
看一下回测结果,回测收益为正,证明该指标具有一定的参考意义。在图像中,可以看到它的作用周期是在横盘时期,该指标具有良好的入场判断时机。
strategy(overlay = true)
len = input.int(1,min = 3, minval=1, maxval=20)
multi = input.int(1,min = 3, minval=1, maxval=5)
[middle, upper, lower] = ta.bb(close, len, multi)
plot(middle, title = 'middle')
plot(upper, title = 'upper')
plot(lower, title = 'lower')
if close > upper[1]
strategy.entry('long',strategy.long)
if close < lower[1]
strategy.entry('short',strategy.short)
延伸指标:布林宽度
布林宽度衍生于布林带指标,计算的是布林上下轨之间的距离。布林宽度基于标准差计算,布林宽度下降意味着波动率的降低。一般而言,窄幅的布林通道运动一旦出现价格突破上下轨的情况下,常常会伴随着后续走势的转向。
计算方法
上轨UP(阻力线) - 下轨DOWN(支撑线)
ta.bbw(close, 5, 4)
延伸指标:BBIBOLL多空布林带
多空布林线(BBIBOLL)是以多空线为中心线,多空线的标准差为带宽的轨道线。UPR线为压力线,对股价有压制作用,DWN线为支撑线,对股价具有支撑作用,BBIBOLL线为中轴线。
中轨计算:
BBIBOLL=(3日简单移动平均价+6日简单移动平均价+12日简单移动平均价+24日简单移动平均价)/4
肯特纳通道
指标介绍
肯特纳通道是一个技术指标,包含了中间的移动平均线以及上下轨的通道。它是基于平均真实波幅(ATR)原理而形成的指标,对价格波动反应灵敏,它可以和布林线或百分比通道作为判断市场走向的重要工具。
相对于布林带使用的是标准差,肯特纳通道使用的是ATR进行通道宽度计算。与布林带(BOLL)相比,肯特纳通道更加平滑。
计算方法
中线:指定周期指数移动平均数
上带:中线+mult*ATR(N)
下带:中线-mult*ATR(N)
TR=math.max(high - low, math.abs(high - close[1]), math.abs(low - close[1]));
ATR:MA(TR,N)
参数:
N: K线数量(长度).
mult: 标准差因子。
应用规则
(1)入场
中轨向上,并且价格升破上轨,开多单;
中轨向下,并且价格跌破下轨,开空单。
(2)出场
当持有多单时,价格跌破中轨,平多单;
当持有空单时,价格升破中轨,平空单。
Pine代码
[middle, upper, lower] = ta.kc(close, 20, 2)
if close > upper[1]
strategy.entry('long',strategy.long)
if close < middle[1]
strategy.close('long')
if close < lower[1]
strategy.entry('short',strategy.short)
if close > middle[1]
strategy.close('short')
相似指标:Envelopes轨道线
和肯特纳通道理念一致,轨道线以20天移动平均为均线,以+/-3%的波动计算上下轨,确定轨道线的宽度。
CDP
指标介绍
CDP指标对广大的投资者来说是一个比较陌生的老指标,实际上CDP技术指标在盘中是一个较好的高抛低吸的短线操作工具。CDP是超级短线指标,假设今日价格的波动幅度不会超过昨天,由此计算出今天的最高价、最低价、次高价、次低价。
计算方法
CDP 为最高价、最低价、收盘价的均值,称中价;中价与前一天的振幅的和、差分别记为AH(最高值)、AL(最低值);两倍中价与最低价的差称NH(近高值),与最高价的差称NL(近低值)。
CDP=(high[1]+low[1]+close[1]*2)/4
AH=CDP+(high[1]-low[1])//最高
NH=CDP*2-low[1]//近高
AL=CDP-(high[1]-low[1])//最低
NL=CDP*2-high[1] //近低
应用规则
- 价格波动不大时,开盘价位于近高值与近低值间,可在近低值价位买进,近高值价位卖出;
- 开盘价位于最高值或最低值附近,意味着跳空,是大行情发动的开始,可在最高值价位追买,或最低值价位追卖。
Pine代码
CDP=(high[1]+low[1]+close[1]*2)/4
AH=CDP+(high[1]-low[1])//最高
NH=CDP*2-low[1] //近高
AL=CDP-(high[1]-low[1])//最低
NL=CDP*2-high[1] //近低
plot(CDP,title='CDP')
plot(AH,title='AH')
plot(NH,title='NH')
plot(AL,title='AL')
plot(NL,title='NL')
相似指标:MIKE
指标介绍
与BOLL指标一样,MIKE指标是随价格波动幅度大小而变动的压力支撑指标,它是一种专门研究价格各种压力和支撑的中长期技术分析工具。它设有初级、中级、强力三种不同级别的支撑和压力,用图标方式直接显示压力、支撑的位置。
计算方法
MIKE指标的计算方法比较复杂,其中涉及到指标计算的起初价——TYP和六个辅助指标,即三个压力价和三个支撑价。对于振幅的计算,它使用的是一定周期内的最高价和最低价。
另外,和其他指标的计算一样,由于选用的计算周期的不同,MIKE指标也包括日MIKE指标、周MIKE指标、月MIKE指标年MIKE指标以及分钟MIKE指标等各种类型。
应用规则
1、 Weak-s、Medium-s、Strong-s三条线代表初级、中级及强力支撑;
2、 Weak-r、Medium-r、Strong-r三条线代表初级、中级及强力压力;
3、 MIKE Base是一种路径指标,依据Typical Price计算,包含三条带状支撑与压力。
- TYP=(H+L+2C)÷4
- 初级压力——WR、中级压力——MR和强力压力——SR。它们的计算公式为:
WR(N日)=TYP+(TYP-LN)
MR(N日)=TYP+(HN-LN)
SR(N日)=2*HN-LN
式中,TYP=起始价
LN=N日的最低价
HN=N日的最高价 - 初级支撑——WS、中级支撑——MS和强力支撑——SS。它们的计算公式为:
WS(N日)=TYP-(HN-TYP)
MS(N日)=TYP-(HN-LN)
SS(N日)=2*LN-HN
从交易心理学分析,压力支撑的形成很大一部份的因素是因为交易者的心理产生的一种共振或者说是共识所产生的作用,大幅下跌后,在“惜售”心理下很少有人愿意割肉,支撑这时候就产生了!大幅上涨后,在“获利了结”的心理下大部分人想套现时,压力就产生了。而关键点位的突破大概率会带来一波行情的走势,因此可利用技术指标判断关键拐点,提前布局。
(二十):量化交易指标:量价指标
大家好,今天我们来学习量化指标系统中的量价指标。量价指标是金融市场技术分析中分析成交量与成交价格关系的指标。量价关系进行分析时要考虑两个要素:成交量和价格。成交量包括放量和缩量两种状态,价格则包括上涨和下跌两种状态,有时还会有平价的状态。成交量的变化与价格涨跌之间有着非常密切的联系。量价分析,就是研究成交量与成交价的相关性,并以此来预测价格运动的未来趋势。
成交量是金融市场供求关系的表现形式,它的大小表明了买卖双方对某一品种即时价格的认同程度,记录了交易者在不同价位上买卖的数量,代表着品种的活跃程度和流通性,并由此透露出市场的人气买卖意愿。人气越旺盛,则交易者进出场越自由,同时也意味着人场资金越充沛,盈利的可能性要大于亏损的可能性。因此,成交量重要的价值,是从市场人气的角度透露了市场的参与意愿和参与深度,为交易者的交易提供了参考依据。
OBV
指标介绍
OBV(On Balance Volume),中文名称:累积能量线,俗名:能量潮。其主要理论基础是市场价格的变化必须有成交量的配合,价格的波动与成交量的扩大或萎缩有密切的关连。通常价格上升所需的成交量总是较大;下跌时,则成交量总是较小。价格升降而成交量不相应升降,则市场价格的变动难以为继。
OBV的基本观点为:先见量,后见价,量是价的先行指标。当投资者价的认同愈不一致时,则成交量愈大,正是这种成交量涌动的能量及人气,将价推向新的位置。
计算方法
当周期收盘价比前一周期收盘价高,其成交量记为正数
当周期收盘价较前一周期收盘价低,其成交量记为负数
累计每周期之正或负成交量,即得OBV值
应用规则
(1)OBV线下降,价格上升,表示买盘无力,是卖出信号;
(2)OBV线上升,价格下降,表示逢低承接强,是买进信号;
(3)投资者使用OBV时,注意力应集中在OBV的形态上,其具体数值意义不大。事实上,只是一个能量潮并不能产生有效的买卖信号,因为价格在上涨下跌的初期或中期时,会出现较大幅度的变化,价格趋势一旦成立,大多数交易者的想法会趋于一致,此时该买的都已经买了,该卖的也都卖了,这时候虽然价格是上涨的,但成交量会逐渐萎缩,也就是说成交量萎缩并不代表市场趋势行情即将结束。
Pine代码
obv = ta.obv
WVAD
指标介绍
WVAD(William's Variable Accumulation Distribution),中文名称:威廉变异离散量;它是将成交量加权的量价指标,其作用在测量从开盘至收盘期间,买方与卖方各自爆发力的程度。
计算方法
V = 当周期成交量
A = 当周期收盘价 - 当周期开盘价
B = 当周期最高价 - 当周期最低价
C = A / B * V
WVAD = 累计N周期的C值
应用规则
- WVAD是测量价格由开盘至收盘期间,多空两方的战斗力平衡;
- 当WVAD指标处于上升初期时,可考虑买进;
- 当WVAD指标处于下降初期时,可考虑卖出;
- 当WVAD长期为负值时,价格也处于下跌尾声,而某周期出现正值,可以考虑建仓;
- 当WVAD长期为正值时,价格也处于高位,而某周期出现负值,可以考虑平仓。
Pine代码
wvad = ta.wvad
资金流量指标MFI
指标介绍
MFI指标也可以叫资金流量指标,英文全名(Money Flow Index)。MFI指标实际是将RSI加以修改后,演变而来。RSI以成交价为计算基础;MFI指标则结合价和量,将其列入综合考虑的范围。可以说,MFI指标是成交量的RSI指标。
计算方法
1.TYP=(high+low+close)/3
2. upper = math.sum(volume * (ta.change(TYP) <= 0.0 ? 0.0 : TYP), length)
3. lower = math.sum(volume * (ta.change(TYP) >= 0.0 ? 0.0 : TYP), length)
3.mfi = 100-(100/(1+upper/lower)
应用规则
- 市价上升成交量增加,是上升市趋势。
- 市价上升成交量缩减,当心市场变天。
- 市价下跌,但成交量增加,表示多空争斗。
- 市价下跌,但成交量减少,反映市场为横盘局。
- 高过80为超买,低于20时为超卖。
Pine代码
mfi = ta.mfi(hlc3, 14)
plot(mfi, title = 'mfi', color = color.blue)
if mfi < 20
strategy.entry('long',strategy.long)
if mfi > 80
strategy.entry('short',strategy.short)
价/量趋势 PVT
指标介绍
英文名为Price/Volume Trend 。即从上市第一天起,对每一交易日先求收盘价与昨收的差,再求差值与昨收的比, 最后求比值与当日成交量的乘积。将每天算得的这个值逐日累加。PVT指标可以与其他技术指标一起使用,如移动平均线、相对强弱指数等,以提高其预测能力。
计算方法
PVT =(今日收盘价-昨日收盘价)/昨日收盘价×今日成交量
应用规则
- 价格上升,PVT指标下降,为卖出信号
- 价格下降,PVT指标上升,为买入信号
- PVT的用法基本同OBV,但PVT比OBV更快地反应趋势。
Pine代码
pvt = ta.pvt
plot(pvt, title = 'pvt', color = color.blue)
if pvt > pvt[1] and open < close[1]
strategy.entry('long',strategy.long)
if pvt < pvt[1] and open > close[1]
strategy.entry('short',strategy.short)
EMV
指标介绍
EMV(Ease Of Movement Value),中文名称:简易波动指标,运用成交量和人气的旺盛和枯竭,构成一个完整的价格系统循环,指示投资者在人气汇集且成交热络的时候买进品种,并且在成交量逐渐展现无力,而狂热的投资者尚未觉察能量即将用尽时卖出品种。
计算方法
以计算日EMV为例,计算方法如下:
A = (今日最高价 + 今日最低价) / 2
B = (前日最高价 + 前日最低价) / 2
C = 今日最高价 - 今日最低价
EM = (A - B) * C / 今日成交额
EMV = 累计N天的EM值
应用规则
(1)EMV由下往上穿越0轴时,视为中期买进信号;
(2)EMV由上往下穿越0轴时,视为中期卖出信号;
(3)EMV属于中长线指标,须长期使用EMV,才能获得最佳利润;
(4)当趋向指标DMI中的ADX低于±DI时,本指标失去效用。
Pine代码
len = input.int(5,minval=1,maxval=40)
A = (high+low) / 2
B = (high[1]+low[1]) / 2
C = high - low
EM = (A-B) * C / volume*1000
EMV = ta.sma(EM,len)
plot(EMV, title = 'EMV', color = color.blue)
if ta.crossover(EMV,0)
strategy.entry('long',strategy.long)
if ta.crossunder(EMV,0)
strategy.entry('short',strategy.short)
成交量和成交价是市场提供给我们最基本的资料,量价关系的研究是一切技术分析的基础。价格的涨跌来自于多空双方每时每刻的力量对比,某一时点的量和价就是他们在这一时点上共同市场行为的反映。我们可以从价格涨跌和成交量变化来考察多空双方的态度和意图,从而判明价格后期可能的走势。量价关系是一个重要的指标,可用于波段性操作的买入与卖出之中,属于技术面判断的一种,是判断市场长期趋势关键的指标。因为短期的量价可以被操作,长期的量价关系则反应市场中真实的趋势。因此,通过量价指标,可以将市场真实走向以量化的方式进行呈现,为我们提供判断市场信号的参考依据。
(二十一):量化交易指标:能量指标
大家好,今天我们将学习量化交易指标的最后一个部分--能量指标。在金融市场上,每一天都是多空力量的较量。在一个交易日或某一段时间内,多空双方的优势会不断地交替出现,因此,双方都有可能在一定时期内占据优势。
如果多方力量在一定时期内占据优势,价格将不断上涨;反之,如果空方力量在一定时期内占据优势,价格将不断下跌;如果多空双方的力量大致平衡,价格会在某一区间内窄幅波动。通过量与价的分析计算,能量指标能够正确、全面地反映每一天或某一段时间内多空双方力量的对比情况。通过使用数值指标的形式对多空双方的力量进行量化比较,能量指标帮助投资者提前进行信号判断,把握最佳的入场和出场时机。
VR指标
指标介绍
VR(Volitility Volume Ratio),中文名称:成交量变异率。该指标是成交量的强弱指标,运用在过热市场及低速盘局中,对辨别头部及认错部的形成有很重要的作用。
计算方法
VR = (N周期中上涨日成交量 + 1/2价格不变周期成交量) / (N日中下跌周期成交量 + 1/2价格不变周期成交量) * 100
参数N为24
应用规则
1.VR指标区域分为四个部分:低价区域(40~70)、安全区域(80~150)、获利区域(160~350)和警戒区域(350以上)。
2. VR下跌至40以下时,市场极易形成底部,应积极买入;
3. VR值一般分布在150左右最多,一旦越过250时市场极易产生一段多头行情;
4. VR超过350以上,应有高档危机意识,随时注意反转之可能;
5. VR运用在寻找底部时较可靠,确认头部时,要配合其它指标使用。
6. 相较而言,由于大盘品种不易被人为操控,因此用VR指标进行判研比较合适。
Pine代码
针对VR指标,我们需要计算价格上涨日,不变日,和下跌日成交量。然而,针对期货品种,价格不变的可能性较小,因此我们定义为针对螺纹钢基数较大的品种,如果价格上涨超过20或者下跌超过20,定义为价格明显变化,否则为平价。当然,针对基数比较小的品种,例如玻璃和甲醛,上涨下跌阈值可以设置小一点。设定固定周期为24,然后我们使用for函数循环,使用if语句今日价格是否大于,小于或者等于昨天,当满足某一条件时,上涨日,下跌日和不变日成交量分别汇总计算。最后利用VR公式计算出来所需的值。
upvolume = 0
downvolome = 0
evenvolume = 0
for i = 1 to 24
if close[i-1] - close[i] >= 20
upvolume += volume[i]
else if close[i-1] - close[i] <= -20
downvolome += volume[i]
else
evenvolume += volume[i]
VR = (upvolume + 0.5*evenvolume)/(downvolome + 0.5*evenvolume)*100
MAVR = ta.sma(VR,5)
plot(VR)
BRAR
指标介绍
AR人气指标和BR意愿指标都是以分析历史价格为手段的技术指标,AR人气指标重视开盘价,反映市场买卖的人气;BR意愿指标重视收盘价,反映市场买卖意愿的程度;两项指标分别从不同的角度对价格波动进行分析,进而达到追踪价格未来动向的目的。
计算方法
AR = ((最高价 - 开盘价)的N周期之和) / ((开盘价 - 最低价)的N周期之和) * 200
BR = ((最高价 - 前收盘价)的N周期之和) / ((前收盘价 - 最低价)的N周期之和) * 200
应用规则
1.一般情况下,AR指标可以单独使用,BR指标则需与AR指标并用,才能发挥效用。该指标虽不适合捕捉到大底部,但是灵活运用该指标,却能够抓住局部底部,特别适合做反弹。
2.BR < AR,且BR < 100,可考虑逢低买进。
3.BR < AR,而BRAR,再转为BR < AR时,也可买进。
4.AR和BR同时急速上升,意味着价格已近顶部,持股者应逢高卖出。
5.BR急速上升,而AR处在盘整或小跌时,应逢高卖出。
6.BR从高峰回跌,跌幅达1至2倍时,若AR无警戒讯号出现,应逢低买进。
Pine代码
AR和BR值每个都具有两个部分,都是N周期之和,所以可以用移动平均值代替(移动平均值就是周期内累加和除以周期),使用sma内置函数计算出来AR和BR两个部分,然后相除就是AR和BR值。关于开仓信号,我们确定的是当br小于ar时,并且br小于100的时候,进行开多仓;空仓信号是当AR和BR急速上升,这里使用的是当AR和BR大于两个周期内的平均值时,进行开空。根据回测结果,针对品种,取得了一个正的预估收益,证明该指标具有一定的参考意义。
ar1 = ta.sma(high-open,26)
ar2 = ta.sma(open-low,26)
br1 = ta.sma(high-close[1],26)
br2 = ta.sma(close[1]-low,26)
ar = ar1/ar2*100
br = br1/br2*100
plot(ar,'ar')
plot(br,'br')
n1 = input.int(3,minval = 2, minva = 25)
if br < ar and br < 100
strategy.entry("buy", strategy.long, qty=1)
if ar > ta.sma(ar,n1) and br > ta.sma(br,n1)
strategy.entry("short", strategy.short, qty=1)
CR
指标介绍
CR综合人气指标,与BR、AR最大的不同在于采用中间价作为计算的基准,CR能够测量价格动量的潜能,又能测量人气热度,同时还能显示压力带和支撑带可以补充BR、AR的不足。
计算方法
中价 = (最高价 + 最低价) / 2
上升值 = 今最高价 - 昨中价
下跌值 = 昨中价 - 今最低价
CR(N) = N周期上升值之和 / N周期下跌值之和 * 100
参数N一般取26。
应用规则
- CR下跌至40以下时,价格形成底部的机会相当高
- CR寻找底部效果不错,但逃顶则功效不明显。
- CR指标还具有不同均线(10,20,40,62)作为支撑压力线,当CR上穿或下穿“地震带”将遇到不同级别的阻力和支撑。
Pine代码
设置固定周期数为26,根据公式我们首先计算中价,上升值和下跌值。N周期内的值的和,可以用移动平均值代替,利用移动平均数计算26日内的上升值均值和下跌值均值,两者相除就是CR值。
midprice = (high+low)/2
upprice = high-midprice[1]
downprice = midprice[1]-low
CR = ta.sma(upprice,26)/ta.sma(downprice,26)*100
plot(CR,'CR')
PSY
指标介绍
PSY(Psychology Line),中文名称:心理线。它是研究某段时间的投资人趋向于买方或卖方的心理与事实,是考察市场中群体心理变化的依据。
计算方法
PSY = N周期内上涨的周期数 / N * 100
应用规则
- PSY在25~75之间是常态分布;
- PSY超过75或低于25时,就是超买或超卖关牛市中可设超买点为83,在熊市中可将超卖17;
- 一段上升行情展开前,通常超卖之低点会出现两次。同样,一段下跌行情展开前,超买的最高点也会出现两次;
- 心理线与价量线配合使用,一般效果较好。
Pine代码
首先我们设置固定周期为12,在12个周期这一时间段内,统计upcount(N周期内上涨的周期数)。这里我们使用一个for循环和if语句就可以将upcount计算出来。接着我们计算psy值,用upcount除以周期数。关于开平仓信号psy值的判断,这里我们可以使用一个参数调试的形式,确定出最好的psy值上限和下限。经过调参,对于玻璃期货,在2022至2023年间,可以确定最好的psy值上限和下限是80和20。最终取得的预估收益为,证明该指标对于玻璃期货具有良好的参考意义。
int upcount = 0
for i = 1 to 12
if close[i] > close[i-1]
downcount += 1
psy = upcount/12*100
plot(psy,'psy')
i_len1 = input.int(25, "Length 1", minval=5, maxval=25, step=5)
i_len2 = input.int(75, "Length 2", minval=60, maxval=85, step=5)
if psy <= i_len1
strategy.entry("buy", strategy.long, qty=1)
if psy >= i_len2
strategy.entry("short", strategy.short, qty=1)
总体而言,可以发现,能量指标和量价指标具有很多相似的地方,都是使用价和量的变化确定多空双方的力量对比。金融市场存在明显的“羊群效应”--追涨杀跌,然而对于信息不对称的散户,有可能空在低谷,多在山顶。套句西方的分析观点,就是以“反市场心理”的立场为基础,当众人一窝蜂的买,市场上充斥着大大小小的好消息,报纸自媒体纷纷报道经济增长率大幅上扬,刹那间,前途似乎一片光明,此时,你应该断然离开市场。能量指标可以帮助我们识别市场的强弱,从而判断是否应该进场或出场。当能量指标显示市场处于高能量状态时,意味着市场处于强势,可以考虑进场。相反,当能量指标显示市场处于低能量状态时,意味着市场处于弱势,可以考虑出场。因此,能量指标可以作为离场信号的参考指标,帮助我们及时确定出场时机,从而避免过度持仓或错失良机。本节课的内容讲述完毕,我们下节课再见!
(二十二):量化交易回测系统
大家好,在上述的课程中,我们详细介绍了如何使用Pine语言开发一个交易策略。 然而一个新开发出来的交易策略, 需要全方位检测才能应用于实战,同样一个优秀的策略也是在试错中不断改进得以产生。量化交易回测的目的是还原交易过程,进而验证策略的逻辑和可行性,所以回测的准确性尤为重要。在以往的课程中,我们对于回测的解读只有一个指标,预估收益,然而,预估收益并不是唯一的指标。在实际的交易中,我们需要考虑多个指标来评估一个策略的优劣。今天我们来详细了解一下量化交易的回测系统。
回测报告
量化交易与主观交易的重要区别之一是量化交易能够通过历史数据回测得出绩效报告,交易者可以从中发现策略的缺点并进行优化改进。然而,要正确评估策略并进行优化改进,必须了解回测参数设置和绩效报告,并正确解读它们。只有这样才能知道策略需要改进的方向。
回测配置参数
即使是相同的策略,回测配置参数不一样,也会影响最终的回测结果。在解读回测绩效报告之前,先来看下回测配置,上图是优宽量化软件的回测界面, 这里我们需要注意的几个参数的解释说明如下:

- 回测开始时间和回测终止时间,这两个时间段确定回测的时间,对于实盘策略的确定具有重要的意义,因为在疫情时代和后疫情时代,策略的功效可能具有极大的差别。
- 数据周期, 可以选择: 天、 小时及分钟的任意数值,这个确定了策略的执行周期,对于日内的交易策略,可以选择分钟或者小时,而对于长期的趋势策略,可以选择小时或者天数。
- 回测数据级别,可以选择: 模拟级 Tick 和实盘级 Tick;执行方式可以选择:实时价模型和收盘价模型,这部分的内容我们在模型执行那里进行了详细的介绍,大家可以重温下。
- 策略外部参数:参数调参的时候会使用到,最小值,最大值,步长。
- 滑价点数,我们也提到过,因为期货实盘是瞬息万变的,确定好交易的信号,但是交易的价格可能和需要的不一致,所以回测系统中通常会设置一个滑价点数,表示交易价格允许偏离预期价格的最大点数。
- 品种代码:期货的品种代码是用来区分不同的期货合约的标识符。比如玻璃的代码为FG,螺纹钢的代码为rb等等,这里需要注意的是,代码是区分大小写的,郑商所的期货品种都需要大写;品种代码字母过后还需要填写一定的数字,因为期货是按照周期交割的,比如FG305是玻璃期货的2023年5月份的合约,如果日志信息报告期货品种错误,可以打开期货交易软件,查询准确的合约代码。如果想省力,可以直接填写后缀888,代表主力合约,在策略执行期间,系统会帮你自动移仓。
回测报告参数
一个盈利率高、胜率高的模型并不一定就是一个好模型。衡量策略好坏的指标需要考虑多方面因素,以下是一些重要的参考指标:
- 累计收益
累计收益是指在一段时间内某个投资产品或投资组合的总收益。它是通过将所有的收益加起来计算得出的。
- 年化收益率
年化收益率表示投资期限为一年的理论收益率,日收益率、 月收益率、 季度收益率都可以换算成年收益率。如果一个策略回测的日收益率是 0.01%, 那么年化收益率是3.65%。其计算公式为: (收益 / 本金) / 投资天数 * 252 ×100%。
- 年化波动率
波动率是衡量策略风险的指标之一,它是描述策略资金曲线的涨跌幅程度,是对策略稳健性的衡量,也反映了策略风险水平。 其计算方式为:最高价减去最低价的值再除以最低价所得到的比率,年化波动率就是每日波动标准差的年化。
- 最大回撤比率
除了波动率外,更能直观反映风险绩效指标就是最大回撤率。 它是统计资金曲线任意周期内最高点到最低点时的回撤幅度的最大值。它是描述策略可能出现的最糟糕情况。最大回撤是一个重要的风险指标,对于量化交易而言,该指标甚至比波动率还重要。
- 夏普比率
回撤意味着风险,也意味着波动,正确的方式是将收益率和风险都考虑在内,也就是说不但要考虑收益率,更要考虑每承担每一单位风险所产生的超额收益。夏普比率就是一个对收益和风险综合考虑的指标。其公式为:(策略收益率-无风险利率)/策略收益率的标准差。每个策略回测都可以计算夏普比率, 如果值为正数, 则表示策略收益大于策略波动风险; 如果值为负数, 则表示策略波动风险大于策略收益。也就是说在设计策略时要考虑风险,尽量用最小的风险换取最大的回报。
回测陷阱
量化交易回测虽然可以快速验证策略在历史数据中是否有效,但很多时候回测并不代表未来能盈利,回测看起来非常好的策略,往往实盘表现不佳。因此需要在策略设计过程中规避回测的陷阱, 才能让策略回测反映真实的结果。
- 未来函数
未来函数就是利用了未来的价格,交易策略如果包含未来函数,在实盘运行时回造成信号闪烁的问题。比如有一个策略逻辑时这样的:当收盘价大于开盘价就买入,当收盘价小于开盘价就卖出。这在回测时是没有问题的,因为收盘价是已经完成,固定不变的数据。但是在实盘交易中,收盘价只有在收盘的时候才能固定下来,所以程序会把当前的最新价格当作收盘价,这种利用未来价格的策略,会导致买卖信号频繁出现和消失。如果一个策略的买卖点不是固定的,回测的数据也是没有意义的。如何避免使用未来函数?最简单的办法是使用滞后的价格,可以把这个策略条件修改为:当上根K线收盘价大于开盘价就买入,当上根K线收盘价小于开盘价就卖出。因为无论是在回测中还是在实盘中,上根K线始终是已经完成的,这样就可以保证回测与实盘保持一致。
- 偷价
相反偷价是利用了已经过去的价格,偷价并不会造成信号频繁出现和消失,但是会造成信号无效。比如有一个策略逻辑是:当收盘价大于开盘价就在开盘时买入,当收盘价小于开盘价就在开盘时卖出。显然这个策略条件在实盘时是不能成交的,当收盘价出现时,开盘价早就过去了。但是在回测中,程序是会以开盘价买入卖出的,这相当于在原本的资金曲线上叠加了一条斜率为正的直线会造成一种非常夸张的回测资金曲线。为避免这种情况发生,编写完策略首先要检查策略逻辑,如果策略回测的收益曲线非常平滑,回撤极小,就要警惕了。尤其是策略逻辑存在隐蔽性偷价行为,务必在实盘之前先用仿真交易测试一段时间。
- 成本冲击
实盘交易中为了保证订单能及时成交,通常需要用对手价或者市价下单,商品期货买一价和卖一价至少相差一个点差,如果是交易不活跃的期货合约就需要更多的点差成本。或者当自己的订单量超过市场现有的订单量时,就会造成自己的订单消耗了市场流动性,触动价格朝向不利于自己的方向移动,使交易成本进一步上升。不仅如此,手续费、极端行情、软硬件系统、服务器响应、网络延迟都会增加实盘的交易成本。尤其是交易频率比较高的策略对受市场冲击成本更大,为了让回测更接近实盘环境,折中的办法是在回测时加上固定2跳左右的滑点。
- 幸存者偏差
回测也有运气的成分,有时候的回测结果可能这个策略刚好适应了历史数据,再换几个参数或者回测品种就不一定有这么好的结果了。因此回测良好的策略,需要在实盘上进行模拟,确定效果较好才可真正应用于实盘。
- 过拟合
过拟合是统计学中的术语,它是指过于精确地匹配数据特征, 以至于无法在其他数据中良好地拟合,量化交易中的过拟合是一种回测时表现很好, 实盘中表现较差的现象。
由于商品期货历史数据有限,所以过拟合问题就更加严重,尤其是对于中低频策略来说,几乎不可能完全避免过拟合,但可以利用下面几种方法来减少拟合:
- 减少核心参数
- 简化处理逻辑
- 增加数据样本
- 样本内外测试
(二十三):量化实战:吊灯退出策略
大家好,今天我们进入Pine语言期货市场量化交易最后一个板块:实战策略编写。Pine语言的功能确实很强大,里面的内置函数基本涵盖了金融常用的量化指标和函数,但是直接使用成熟的指标还是不能灵活的应对中国的期货市场的不同品种。每个品种都有它不同的品性,应该结合自己的理念,运用更加细腻的逻辑编写不同品种的交易策略。
目前对大家来说,可能灵活的使用Pine语言还具有一定的距离。大家通过对于期货大盘的熏陶,可能摸索了一些不成熟的交易理念与想法,但是想真正的转化成为成熟的策略,依然觉得细腻度和成熟度还具有差距。因此,本部分的结尾课程,我将带领大家细细剖析一些量化策略,了解怎样使用Pine语言将自己的交易理念进行代码化,帮助大家真正使用Pine语言对照中国的期货市场,编写出合适的量化策略。本部分的内容初步计划为两个实战策略的讲解:第一节内容为成熟的指标的变化策略;第二节内容为多个指标结合的策略,帮助大家更好的理解指标的变化以及灵活运用。
策略介绍
本节课介绍的策略为吊灯退出策略。吊灯退出指标在某种程度上可视为ATR指标的延伸。吊灯退出指标是一个基于波动性的指标,使交易者能够等到出现明确趋势反转时再进行交易。对于信息不透明的散户来说,期货趋势中明显的反转需要人为的判断,所以只能被动的开展“追涨杀跌”,稍有不慎,下单将会被套牢,出于不想亏损的心理,扛单占据了大部分的时间,而一旦扛单被解套,一点点小盈利散户就抓紧时间平仓,错过了更大盈利的可能性。通过使用吊灯退出指标,交易者将能够避免过早平仓,实现最大的回报。
吊灯退出指标由全球公认平仓策略专家 Chuck Le Beau 创造。 但此指标和与之相关的交易系统却由 Alexander Elder 通过其在 2002 年出版的《Come Into My Trading Room》一书介绍给交易者和投资者。此指标因其与房间天花板上的吊灯相似而得名。
策略原理
吊灯退出指标基于以下原理:当资产价格背离主要趋势变动的距离达到平均波动幅度的三倍时就很可能发生趋势反转。该指标是利用指定时间范围内的 ATR(真实波动幅度均值) 值创建的。 除了ATR以外,在计算中还使用了资产在特定时间范围内最高的高点和最低的地点价格。在使用移动窗口周期进行分析时,建议使用 22 天的时间范围输入。 这是因为金融市场每个月有 22 个交易日。 为了确保平滑的效果并过滤短期波动或噪声,建议在计算中使用22天的价格水平。对于其他移动窗口周期,我们可以根据反复参数试验的结果确定使用的时间范围输入。 这个方面并没有硬性的规则。就像前面描述的一样,吊灯退出指标的主要目标是在正确的时间提醒交易者即将发生趋势反转。因此,对于多头头寸和空头头寸,吊灯退出指标的计算方式不同。
在上行趋势中,可使用如下公式计算吊灯退出指标:
- 多头突破平仓 = 特定时间范围内最高的高点 – 3*特定时间范围内的ATR。
在下行趋势中,可使用如下公式计算灯笼止损指标:
- 空头突破平仓 = 特定时间范围内最低的低点 + 3*特定时间范围 内的ATR。
倍数值3是指吊灯退出指标的倍数,交易者可以更改此倍数。虽然并没有禁止使用不同的时间范围,但对于上涨的移动周期窗口和下跌的移动周期窗口,大部分交易者还是会使用相同的时间范围。计算出的值(日间、每日、每周或每月)在价格图表上绘制成一条线。 根据上述公式,可以推测出在下行趋势中,吊灯退出线将高于该价格线。相反,在上行趋势中,吊灯退出线将低于价格线。如果考虑了资产隐含的波动幅度,则可以更改吊灯退出指标的倍数。更大的时间范围则会延迟平仓,因而会稀释指标的作用。在交易隐含波动幅度(市场预期的资产价格涨跌幅度)较大的期货基数时,需要更大的倍数。 在金融市场上,交易者在下行趋势中使用较小倍数的情况并不罕见,因为价格下跌的速度一般会更快。
计算方法
- 计算真实波幅倍数(ATR*乘数);
- 计算longstop和shortstop;
- 根据收盘价和longstop、shortstop关系,判断突破信号;
- 根据突破信号,进行开平仓操作。
策略源码
study("吊灯退出", overlay=true)
length = input.int(12, title="ATR周期", minval=11, maxval=33)
mult = input.int(3, title="ATR乘数", minval=1, maxval=5)
showLabels = input.bool(title="显示 Buy/Sell 标签 ?", defval=true)
useClose = input.bool(title="使用收盘价作为极值点 ?", defval=true)
//计算真实波幅倍数
atr = mult * ta.atr(length)
//使用收盘价作为极值点,返回移动窗口周期内的收盘价最大值,不使用的话,返回移动窗口周期内的最高值(high)的最大值,然后减去atr
longStop = (useClose ? ta.highest(close, length) : ta.highest(length)) - atr
//如果再开始的时候,longStop[1]是null,返回longStop
longStopPrev = nz(longStop[1], longStop)
//昨日的收盘价大于longStopPrev,说明没有突破longStopPrev,继续上升的趋势,返回longStop和longStopPrev的最大值(为下一日做准备),如果昨日向下突破longStopPrev,返回最新的longstop,这是一个不断更新longstop的过程
longStop := close[1] > longStopPrev ? math.max(longStop, longStopPrev) : longStop
//使用收盘价作为极值点,返回移动窗口周期内的收盘价最小值,不使用的话,返回移动窗口周期内的最低值(low)的最小值,然后加上atr
shortStop = (useClose ? ta.lowest(close, length) : lowest(length)) + atr
//如果再开始的时候,shortStop[1]是null,返回shortStop
shortStopPrev = nz(shortStop[1], shortStop)
//昨日的收盘价小于shortStopPrev,说明没有突破shortStopPrev,继续下降的趋势,返回shortStop和shortStopPrev的最小值(为下一日做准备),如果昨日向上突破shortStopPrev,返回最新的shortStop,这是一个不断更新shortStop的过程
shortStop := close[1] < shortStopPrev ? math.min(shortStop, shortStopPrev) : shortStop
var int dir = 1
//两个三元表达式,下降过程中,如果close向上突破shortStopPrev,应该空头反转开多,返回1;
//否则如果close没有突破shortStopPrev,应该继续空头持有,返回-1;
//上升过程中,如果close向下突破longStopPrev,应该多头反转开空,返回-1;
//否则close没有突破longStopPrev,应该继续多头持有,返回1
//精髓就在于后续是一个var dir,会保留先前的dir状态,只要不进行突破,就会保留原始的var值
//转空是一个1到-1;转多是一个-1到1
dir := close > shortStopPrev ? 1 : close < longStopPrev ? -1 : dir
var color longColor = color.green
var color shortColor = color.red
//空头反转开多,dir由-1转为1,多头止损开启
//画线
longStopPlot = plot(dir == 1 ? longStop : na, title="Long Stop", style=plot.style_linebr, linewidth=2, color=longColor)
buySignal = dir == 1 and dir[1] == -1
//画圆圈
plotshape(buySignal ? longStop : na, title="Long Stop Start", location=location.absolute, style=shape.circle, size=size.tiny, color=longColor, transp=0)
//画箭头
plotshape(buySignal and showLabels ? longStop : na, title="Buy Label", text="Buy", location=location.absolute, style=shape.labelup, size=size.tiny, color=longColor, textcolor=color.white, transp=0)
//多头反转开空,dir由1转为-1,空头止损开启
//画线
shortStopPlot = plot(dir == 1 ? na : shortStop, title="Short Stop", style=plot.style_linebr, linewidth=2, color=shortColor)
sellSignal = dir == -1 and dir[1] == 1
//画圆圈
plotshape(sellSignal ? shortStop : na, title="Short Stop Start", location=location.absolute, style=shape.circle, size=size.tiny, color=shortColor, transp=0)
//画箭头
plotshape(sellSignal and showLabels ? shortStop : na, title="Sell Label", text="Sell", location=location.absolute, style=shape.labeldown, size=size.tiny, color=shortColor, textcolor=color.white, transp=0)
//画出中间价
midPricePlot = plot(ohlc4, title="ohlc4", style=plot.style_circles, linewidth=0, display=display.none, editable=false)
//多头信号,开多仓;空头信号,开空仓
if buySignal
strategy.entry("Enter Long", strategy.long)
else if sellSignal
strategy.entry("Enter Short", strategy.short)
策略评价
-
优点
允许交易者以可以忽略的风险等到最后一个点再交易。
允许使用多个指标创建先进的策略。
经验丰富的交易者可以过滤掉错误的信号,从而放心地交易货币。 -
缺点
需要有足够的知识和实践经验才能更改吊灯退出的倍数值。
通过细细剖析策略源码,可以感到一个形容词“精巧”。通过简单的代码编写,对于突破信号的设计和转换,完成对多头和空头的对调开仓。策略原理我相信大家都可以理解,重要的是怎样使用精确简洁的语言讲自己的交易理念进行代码化呈现,这是我希望大家和我一起学习的事情。本节课的内容讲述完毕,我们下节课再见!
(二十四):量化实战:BB-RSI-ADX指标
大家好,欢迎大家来到Pine语言商品期货量化最后一节课程,本课程的内容为量化实战之组合指标策略的讲解。如果说一个指标代表衡量趋势的一个维度,使用多个指标则意味着从多个维度,立体捕捉市场的发展趋势。组合指标策略的核心思想是将不同类型、不同特性的指标进行有效的组合,从而达到更准确的市场预测和交易决策。常见的组合指标策略包括趋势型、震荡型和能量型等,具体选用哪种类型要根据市场情况灵活调整。同时,在实际应用中还需要考虑各指标间的权重,以及如何对参数进行优化和调整,从而让策略始终保持优异的表现。
策略介绍
BB-RSI-ADX是一种技术指标组合,用于确定股票或其他交易资产的入场点。BB代表布林带指标(Bollinger Bands),RSI代表相对强弱指标(Relative Strength Index),ADX代表平均趋向指标(Average Directional Index)。这三个指标结合在一起可以提供更全面的市场分析。
策略原理
基于Bollinger Bands、RSI和ADX指标的交易策略定义了三个指标的参数和输入条件,根据这些条件确定是否进入多头或者空头。具体来说,该策略使用Bollinger Bands指标来确定价格波动的上下限,使用RSI指标来确定价格是否处于超买或超卖状态,使用ADX指标来确定趋势强度。
具体而言,当BB指标和RSI指标同时发出买入信号,并且ADX指标显示市场正处于强劲的趋势中时,这被认为是一个较好的入场点。通过综合三个指标,我们构建出了一个从多方条件约束开平仓信号的震荡策略,然而不可忽视的是,策略的参数越多,不免会陷入过拟合的风险。并且对于不同类别的品种,参数则需要重新调试,因此这就是使用这类综合指标策略需要注意的地方。
计算方法
- 根据布林带公式,计算布林带上限,布林带下限和布林带宽度的百分比B;
- 计算RSI值;
- 计算ADX值;
- 当最低价小于BBI下限,bbr大于进多阈值,rsi处于多头范围,adx大于参数阈值,进行多头开仓;
- 当最高价大于BBI上限,bbr小于进空阈值,rsi处于空头范围,adx大于参数阈值,进行空头开仓;
策略源码
//@version=5
indicator(shorttitle="BB-RSI-ADX", title="BB-RSI-ADX Entry Points", overlay=true, timeframe="", timeframe_gaps=true)
// Bollinger Bands Setup
//定义布林带周期的输入变量,并设置最小值、显示标题和所属分组。
bbLength = input.int(9, minval=1, title="BB 周期", group="BB Settings")
//定义数据源的输入变量,并设置显示标题和所属分组。这里使用收盘价作为数据源。
src = input(close, title="数据源", group="BB Settings")
//定义标准差的倍数,并设置最小值、最大值、显示标题和所属分组。
stDev = input.float(2.0, minval=1, maxval=3, title="标准差", group="BB Settings")
//计算布林带的中轨,使用简单移动平均线。
basis = ta.sma(src, bbLength)
//计算布林带的标准差的倍数,使用收盘价作为标准差输入变量,乘以倍数。
dev = stDev * ta.stdev(src, bbLength)
//计算布林带的另一个标准差,使用收盘价和标准差输入变量。这里对标准差倍数进行了一些调整,以使其不小于1。
devMinOne = (stDev > 1 ? stDev - 1 : 1) * ta.stdev(src, bbLength)
//计算上限,中轨加上标准差的倍数
upper = basis + dev
//计算下限,中轨减去标准差的倍数
lower = basis - dev
//计算次上限,中轨加上标准差乘以减一的倍数
upperMinOne = basis + devMinOne
//计算次下限,中轨减去标准差乘以减一的倍数
lowerMinOne = basis - devMinOne
//画出中轨,上限和下限,并使用颜色填充上限和下限
plot(basis, "Basis", color=#FF6D00)
p1 = plot(upper, "BB Upper", color=#2962FF)
p2 = plot(lower, "BB Lower", color=#2962FF)
fill(p1, p2, title = "BB Background", color=color.rgb(33, 150, 243, 95))
// 利用次上限和次下限,计算布林带宽度的百分比B。
bbr = (src - lowerMinOne)/(upperMinOne - lowerMinOne)
// RSI
//RSI周期数参数
rsiLengthInput = input.int(14, minval=1, title="RSI 周期", group="RSI Settings")
//RSI数据源参数
rsiSourceInput = input.source(close, "数据源", group="RSI Settings")
//计算RSI的上升部分,使用RSI数据源的变化值(差分)和RSI周期输入变量,并使用RMA(指数加权移动平均线)进行平滑。
rsiUp = ta.rma(math.max(ta.change(rsiSourceInput), 0), rsiLengthInput)
//计算RSI的下降部分,使用RSI数据源的变化值和RSI周期输入变量,并使用RMA进行平滑。
rsiDown = ta.rma(-math.min(ta.change(rsiSourceInput), 0), rsiLengthInput)
//计算rsi的值,两个三元表达式
//如果rsiDown == 0,rsi=100
//在rsiDown不等于0的情况下,如果rsiUp == 0,rsi=0
//如果rsidown和rsiup都不等于0,则rsi=100 - (100 / (1 + rsiUp / rsiDown))
rsi = rsiDown == 0 ? 100 : rsiUp == 0 ? 0 : 100 - (100 / (1 + rsiUp / rsiDown))
// ADX
//ADX平滑周期
adxlen = input(14, title="ADX 平滑周期", group="ADX Settings")
//ADX DI 周期
dilen = input(14, title="ADX DI 周期", group="ADX Settings")
//dirmov自定义函数
[_, _, adxStr] = ta.dmi(dilen, adxlen)
// Entry condition inputs
//开多仓的参数阈值
c_enter_long_bbr = input(0, title="最小值 BB %B", group="Enter LONG Conditions", tooltip="The Minimum required BB %B required to enter a LONG position. RECOMMENDED: 0")
c_enter_long_rsi_min = input(30, title="最小 RSI", group="Enter LONG Conditions", tooltip="The Minimum RSI value to enter a LONG position. RECOMMENDED: 30", inline="rsi_long")
c_enter_long_rsi_max = input(50, title="最大 RSI", group="Enter LONG Conditions", tooltip="The Maximum RSI value to enter a LONG position. RECOMMENDED: 50", inline="rsi_long")
//开空仓的参数阈值
c_enter_short_bbr = input(1, title="最大值 BB %B", group="Enter SHORT Conditions", tooltip="The Highest required BB %B required to enter a SHORT position. RECOMMENDED: 1")
c_enter_short_rsi_min = input(50, title="最小 RSI", group="Enter SHORT Conditions", tooltip="The Minimum RSI value to enter a SHORT position. RECOMMENDED: 50", inline="rsi_short")
c_enter_short_rsi_max = input(70, title="最大 RSI", group="Enter SHORT Conditions", tooltip="The Maximum RSI value to enter a SHORT position. RECOMMENDED: 70", inline="rsi_short")
//衡量趋势的强度
c_adx_min_str = input(25, title="ADX 最小力度", group="ADX Settings")
// Enter Long Conditions
enter_long = (low < lower) and (bbr > c_enter_long_bbr) and (rsi > c_enter_long_rsi_min) and (rsi < c_enter_long_rsi_max) and (adxStr > c_adx_min_str) ? 1 : 0
// Enter Short Conditions
enter_short = (high > upper) and (bbr < c_enter_short_bbr) and (rsi > c_enter_short_rsi_min) and (rsi < c_enter_short_rsi_max) and (adxStr > c_adx_min_str) ? -1 : 0
if enter_long
strategy.entry("Enter Long", strategy.long)
else if enter_short
strategy.entry("Enter Short", strategy.short)
策略评价
-
优点:
多重指标确认:该策略综合了多个指标,可以减少单一指标造成的误判,提高交易准确性;
应用范围广:适用于不同市场和不同品种的交易;
策略灵活调整:可根据实际市场情况进行参数调整。 -
缺点:
参数需要多次调整;
过拟合风险。
Pine语言的量化课程到目前为止就全部结束了,很感谢大家这段时间的陪伴。我们后续会推出更多的有关金融量化的课程,也很感谢大家对于优宽平台的关注,我们有缘再见!
- 1





























































































