从量化交易到资产管理—绝对收益之CTA策略开发

Author: , Created: 2019-06-26 10:27:41, Updated: 2023-10-30 20:30:50

if (positionData[i].Type == 0) { // 如果是多单 type = 10; // 设置下单参数 } else if (positionData[i].Type == 1) { // 如果是空单 type = -10; // 设置下单参数 } // 平掉所有仓位 this.trade(positionData[i].ContractType, type, positionData[i].Amount); } } }

// 画图 Data.prototype.drawingChart = function (boll) { var nowTime = new Date().getTime(); ObjChart.add([0, [nowTime, boll.up]]); ObjChart.add([1, [nowTime, boll.middle]]); ObjChart.add([2, [nowTime, boll.down]]); ObjChart.add([3, [nowTime, this.basb]]); ObjChart.add([4, [nowTime, this.sabb]]); ObjChart.update(chart); }

// 交易条件 function onTick() { var data = new Data(tradeTypeA, tradeTypeB); // 创建一个基础数据对象 var accountStocks = data.accountData.Stocks; // 账户余额 var boll = data.boll(dataLength, timeCycle); // 获取boll指标数据 if (!boll) return; // 如果没有boll数据就返回 // 价差说明 // basb = (合约A卖一价 - 合约B买一价) // sabb = (合约A买一价 - 合约B卖一价) if (data.sabb > boll.middle && data.sabb < boll.up) { // 如果sabb高于中轨 if (data.mp(tradeTypeA, 0)) { // 下单前检测合约A是否有多单 data.trade(tradeTypeA, “closebuy”); // 合约A平多 } if (data.mp(tradeTypeB, 1)) { // 下单前检测合约B是否有空单 data.trade(tradeTypeB, “closesell”); // 合约B平空 } } else if (data.basb < boll.middle && data.basb > boll.down) { // 如果basb低于中轨 if (data.mp(tradeTypeA, 1)) { // 下单前检测合约A是否有空单 data.trade(tradeTypeA, “closesell”); // 合约A平空 } if (data.mp(tradeTypeB, 0)) { // 下单前检测合约B是否有多单 data.trade(tradeTypeB, “closebuy”); // 合约B平多 } } if (accountStocks * Math.max(data.askA, data.askB) > 1) { // 如果账户有余额 if (data.basb < boll.down) { // 如果basb价差低于下轨 if (!data.mp(tradeTypeA, 0)) { // 下单前检测合约A是否有多单 data.trade(tradeTypeA, “buy”); // 合约A开多 } if (!data.mp(tradeTypeB, 1)) { // 下单前检测合约B是否有空单 data.trade(tradeTypeB, “sell”); // 合约B开空 } } else if (data.sabb > boll.up) { // 如果sabb价差高于上轨 if (!data.mp(tradeTypeA, 1)) { // 下单前检测合约A是否有空单 data.trade(tradeTypeA, “sell”); // 合约A开空 } if (!data.mp(tradeTypeB, 0)) { // 下单前检测合约B是否有多单 data.trade(tradeTypeB, “buy”); // 合约B开多 } } } data.cancelOrders(); // 撤单 data.drawingChart(boll); // 画图 data.isEven(); // 处理持有单个合约 }

//入口函数 function main() { // 过滤控制台中不是很重要的信息 SetErrorFilter(“429|GetRecords:|GetOrders:|GetDepth:|GetAccount|:Buy|Sell|timeout|Futures_OP”); exchange.IO("currency", name + ‘_USDT’); //设置要交易的数字货币币种 ObjChart.reset(); //程序启动前清空之前绘制的图表 LogProfitReset(); //程序启动前清空之前的状态栏信息 while (true) { // 进入轮询模式 onTick(); // 执行onTick函数 Sleep(500); // 休眠0.5秒 } }


套利交易起源于摩根士丹利的股票交易策略,其理念是:两个高度相关的品种,它们的价差波动符合“爆米花过程”,即价差不断从偏离历史均值的位置回归到均值,然后又从均值进行再一次偏离。

由此我们就可以对价差进行低买高卖获取利润,那么布林带根据统计学中的标准差原理,由一根中轨和用标准差计算出的上轨和下轨,形成的三条网状带,在价差套利交易中非常实用。

经过测试,按照该策略进行操作,在不考虑手续费以及冲击成本情况下,虽然每次收益并不是很高,但整体收益相对稳定。需要注意的是,由于属于统计套利,因此价差存在反向扩大的风险,我们在设计时必须考虑止损问题。 其次,还需要关注冲击成本,当参与交易的两个合约流动性缩小时,会对收益产生很大影响,投资者应当酌情规避。





#### 4、CTA策略开发进阶迭代

##### 4.1 避免期货CTA策略的陷阱
在上两节课中,我们用麦语言写了一个趋势策略,用JavaScript写了一个套利策略,在策略回测上并没有看出什么问题。但是量化交易并不是写个程序,回测没问题就可以直接实盘了。
实际上回测只是对策略的一种模拟,仅仅用来评估这个策略在历史数据中的表现,能够让交易者快速评估以及抛弃一些交易策略。

很多情况下,在回测中看起来很棒的策略,在实盘中往往达不到回测的标准,理由有很多,其中有一部分超出了交易者的控制能力,但有些失败是因为常见的、或者潜在的错误导致的。

**静态数据与动态数据**
我们做量化首先要有一个静态数据和动态数据的概念,在回测中,我们用的都是静态的历史数据,开高低收每根K线的价格都是完整的,每一个交易信号都可以100%成交。但是在实盘中的数据是动态的。举个例子,最高价大于开盘1小时内的最高价就买入,但如果当前的K线还没有走完,那么最高价就是动态的,交易信号就可能会来回闪烁。出现这种情况,说明策略在判断买卖交易的条件中使用了未来函数。

**未来函数**
什么是未来函数?我们先来看下百度百科是怎么解释的:某一量依赖另一量,如量 A和量B,B变化使A改变,那么A是B的函数,如果B是稍后的量,A是稍早的量,A跟着B变,A是B的未来函数。大家可能会看的一头雾水。

通俗点说就是引用未来数据的函数,比如用明天的价格预测明天的价格。如果一个技术指标包含未来函数,那么它的信号是不确定的,常常是当前发出交易信号,等下根K线出现的时候,这个信号消失或者改变了位置。

收盘价就是一个未来函数,在最新的K线走完之前,收盘价一直是在变动的,你必须等到K线走完才能确定收盘价。那既然收盘价本身就是未来函数,那么所有基于收盘价的技术指标也都是未来函数。

所以,如果一个技术指标,使用已经确认的收盘价作为基础数据,无论过去了多久,买卖信号都不会改变,就可以说这个技术指标没有引用未来函数。但是它使用的基础数据是还没有确认的收盘价,那么这个技术指标就引用了未来函数,在实际应用中买卖的信号就可能会发生改变。

**过去的价格**
未来函数是用到了未来的价格,那也有可能会相反用到过去的价格,这点也是很多新手容易忽视的一个问题。未来更好的说明这个问题,我们还是举个例子:如果当前最高价大于开盘后1小时内的最高价,就以开盘价买入。很明显这个买卖信号的条件没有什么问题,但是下单的价格却使用了过去的价格。

在回测中,策略是正常的,因为基于静态数据的回测引擎,只有有买入信号,就能100%成交,但是在实盘中,当最高价大于开盘后1个小时内的最高价时,肯定就不能再用过去的价格开盘价去发单了。

**价格真空**
所谓的价格真空就是指在K线图上显示的价格,但是在实盘中不能成交的价格,主要分为下面几种情况:
1.	做过交易的人都知道,在价格涨停的时候是很难买入的,在价格跌停的时候是很难卖出的。但是在回测中是可以成交的。
2.	交易所的撮合机制是:价格优先、时间优先。有些品种盘口会经常有巨量挂单,实盘时如果你是挂单买卖,那么你必须排到别人的挂单后面,等之前别人的挂单成交后,你才能成交,甚至来不及成交价格就已经变了。但是在回测的时候,如果你的策略是挂单交易的,那么会及时成交,这就与真实的实盘环境不一样了。
3.	如果你用的是套利类策略,那么回测利润是很高的,因为回测时每次都已经假设了抢到了这些价差。真实的情况下,很多价差都抢不到,或者只抢到了一条腿,一般来说肯定是不利于你的方向的那条先成交,那么就需要马上去补另一条腿,这时候滑点已经不是1、2个点了,而套利策略本身就赚这几个点的价差,这种情况是回测中无法模拟的。真实利润完全不如回测。
4.	黑天鹅事件虽然不常用,但是对量化交易的影响还是很大的,如下面这张图,在外汇瑞郎黑天鹅事件中,从图表上看开高低收都有,其实在当天极端行情中,中间的价格是真空,大量的止损单,造成踩踏事件,流动性为零,成交难度非常大,但是在回测中却能止损。
 ![img](/upload/asset/3a14e19c6222d3cc8c85.jpg) 

**过度拟合**
过度拟合是量化交易初学者经常犯的一个错误,那什么是过度拟合呢,举一个简单的例子:上学考试的时候,有的人采用的是题海战术,把每个题背下来。在考试的时候题目稍微变换一下,他就不会做了。因为他非常复杂的记住了每道题的做法,但是没有抽象出通用的规则。
 ![img](/upload/asset/3a0fef091972348a0d72.jpg) 
 

就像上面这张图,一个模型只要足够复杂,那么就可以完美的适应数据。在量化交易中的过度拟合也是这个道理,如果你的策略本身很复杂,又有很多外置的参数,那么在有限的历史数据回测中,总会有一个或几个参数能完美的拟合历史行情。

但是,在以后的实盘中,价格的变化可能超出你的策略局限,实际上量化交易策略开发的本质就是从大量貌似随机的数据中匹配局部非随机数据的过程,因此就需要我们借助统计学知识来避开这个陷阱,怎么做呢?

折中的解决办法是:使用样本内和样本外数据。把整个数据分成两份,样本内数据作为训练集,负责数据回测。样本外数据作为测试集,负责验证。如果历史数据很少,还可以采用交叉测试的方法。

如果发现样本外数据表现不好,又觉得丢掉模型太可惜或者不愿意承认自己这个模型不行,而对着样本外数据继续做模型优化,直到样本外数据上也表现得一样好,那最后受伤的一定是你的真金白银。

**幸存者偏差**
幸存者偏差可以通过下面几个例子来解释:
1、	站在风口,猪都会飞。
2、	网上卖降落伞的都是好评,因为降落伞有问题的人不存在了。
3、	记者在车上采访是否买到车票,因为买不到车票的人根本上不了车。
4、	媒体宣传彩票可以中大奖,因为媒体不会主动宣传没有中奖的人。

上面的例子我们可以发现,通常人们接受到的信息其实是已经经过筛选之后的,这使得大量的数据或样本被选择性的忽视了,产生的结果就是基于幸存者偏差的结论已经偏离了实时。那么在量化交易中,我们也要主要回测的结果是否属于运气的成分,很多情况下回测的结果可能是在整个回测中表现较好的一次,注意看下面这张图:
 ![img](/upload/asset/396c18fbf53156a77ca6.jpg) 
 

左边这张图是一个非常棒的交易策略,资金曲线良好,没有大幅的回撤,可以获得稳定的利润回报。但是请看右边这张图,它只是这几百次交易回测中表现最好的一个而已。反过来我们在看金融市场中,始终是明星多寿星少,如果交易者的策略与市场行情契合,那么每年的行情都能造就一批批明星,但是你很难见到连续3年以上都能稳定盈利的寿星。

**成本冲击**
除非你是挂单交易,否则你在交易的时候都可能有滑价。在交易活跃的品种上,买一价和卖一价通常是一个点差,在交易不活跃的品种上,点差可能会更大。每次你想主动成交就需要至少一个点差,甚至更多。但是在回测中,我们不需要考虑成交问题,只要有信号就可以成交,所以为了模拟真实的交易环境,必须加上至少一个滑价。

尤其是交易频率比较高的策略,在策略回测的时候,如果不加上滑价,资金曲线是一直倾斜向上的,一旦加上合理的滑价就立马变为亏损。另外,造成这种现象不止是点差的问题,在真实的交易环境中,还需要考虑:网络延迟、软硬件系统、服务器响应等问题。

**策略容量**
同样的策略在高效市场和低效市场会有截然不同的差别,甚至完全相反。例如在国内股市、商品期货、国外数字货币等低效市场中,由于交易量的基数小,使得高频策略本身的容量就不是很大,用的人多了就没有利润空间了,甚至本来之前是盈利的策略变得亏损。但是在高效的外汇市场上,可以容纳很多不同种类的高频策略。

以上就是在策略开发中和使用中,可能出现的问题和陷阱,对于一个有经验的交易系统开发者来说,回测是必须要做的。因为它能告诉你一个策略的想法在历史交易中是否能被验证有效。但是很多时候回测并不代表未来能盈利。因为回测里面有太多坑了,不用钱买点教训,你是不会明白的。而这些教训都是用真金白银堆出来的。这节课程至少能让你少走很多量化的弯路和陷阱。








##### 4.2 建立最佳的头寸管理
在《股票做手回忆录》中,有一个很有意思的桥段:和主人公利弗莫尔在同一个证券公司的老火鸡(原名帕特里奇)总是大手笔买卖交易,每当别人建议他获利后先卖掉,等股票价格回调后再买入。老火鸡总是语重心长的说:不,你知道的,这是牛市!

就连利弗莫尔最后都感慨:看对走势并没有什么了不起的,在市场中总能找到很多在牛市早就看涨的人,在熊市早就看跌的人。但他们总是善于跟市场讨价还价,试图买在最低点,卖在高点。就像老火鸡一样,真正赚大钱的,正是那些看对市场,又紧握头寸的人,这也是最难学的。这不仅面临选择标的、选择时机,还面临一个更重要的问题:究竟应该持有(承担)多大头寸(风险)?

失败的交易者都有一个片面思维,在交易的时候,贪婪的人只看收益不看风险,胆小的人只看风险不看收益,既贪婪又胆小的人在上涨时忘了风险,在下跌时忘了收益。但是成功的交易者会同时考虑风险和收益,也就是说每赚一块钱承担了几块钱的风险。那么衡量收益和风险的指标就是收益风险比。

很多人都知道利润有多大风险就有多大,即:收益与风险成正比。在部分人看来,收益和风险的关系应该是下面这个样子,横轴是风险百分百,纵轴是利润百分比:
 ![img](/upload/asset/3a01d9f1ed33d2fc5c7a.jpg) 

但是在实际交易中,收益与风险远不是两点一线这么简单,至少它并不总是呈线性运动。真实的风险是在预期收益下,承担亏损的最大幅度,也就是我们所说的最大波动率。虽然有时候从交易的结果看,最大浮动亏损并不一定等于平仓亏损,但最大浮动亏损却是真实存在的。

由此,我们可以知道上图中收益与风险比并不是真实的表现,在真实的交易环境中,收益与风险比应该是下图中的样子:
 ![img](/upload/asset/3a54ee999529895168b8.jpg) 

我们看上面这张图,黄色的曲线是净值在不同风险下的波动情况,随着预期收益的不断扩大,风险也在逐渐扩大。如果我们把破产先设置在0.5,也就是最大亏损达到50%的幅度,那么这就是一个失败的交易策略。尽管从结果上看策略这个策略的最终收益是正的,但实际上中间早已破产。

哪怕你的策略是一个正期望的策略,在错误的头寸管理下,一样会爆仓。所以从这个角度讲,买卖多少比什么时候买卖更为重要,如何科学管理头寸,在金融交易中也就成了根本问题。那么在试图解决这个问题之前,我们来看在赌博中是如何科学下注的。
 ![img](/upload/asset/396b2cee9202cf597191.jpg) 

我们以抛硬币为例,假设一枚硬币的两面是一样重的,如果出现正面盈利2元,出现反面亏损1元,很明显这是一个正期望的游戏,胜率是50%,赔了是2。问题来了:现在你有100元,那么怎么重复下注,可以使100元以最快的速度达到100万元。

如果不进行严谨的思考,我们会觉得既然每次赌的收益是50%*2-50%*1,也就是50%,那么为了快速实现最大收益,应该在每次赌博中尽量投入跟多的本金,这个下注必应应该是100%才对。

但是很明显在每一局赌博中都投入100%的本金,显然是不合理的,因为只要有一次赌输了本金就没有了,哪怕这可能性非常小。因为只要你赌的次数足够多,赔钱的赌局就一定能发生。

那可能有人要问了,既然100%赌注是不合理的,那么90%或者更低的赌注会怎么样?实际上解决这个问题,我们可以做个试验模拟这个赌局,看下每次下注的结果是怎样的。如下面这张图:

 ![img](/upload/asset/3a09d2a1505fc0404a89.png) 

从图中我们可以看到,当我们把仓位逐渐降低,从90%、80%、70%、60%、50%的时候,在同样的赌局中,结果却完全不一样,细心的朋友可能已经注意到了,随着仓位的逐渐缩小,最后的资金反而是在逐渐扩大。

那可能又有人会问,是不是每次的赌注越小越好呢,比如10%,总不能把每个下注比例都计算一遍吧,这就是著名的凯利公式要解决的问题。在统计学中,凯利公式可以使一个拥有正期望重复下注的策略长期增长率最大化,它可以在每次赌博中计算出最佳的下注比例。

不仅如此,假设本金和赌局可以无尽分割的情况下,使用凯利公式可以在任何赌局中,都不可能破产。特别是在金融交易的实际应用中,是一个攻防兼备的头寸管理策略。我们来看下凯利公式是如何计算的,看下面这张图:

 ![img](/upload/asset/3963602e0d1a9d38c755.png) 

- f为现有本金的最佳下注比例
- b为赔率,在交易中也可以称为盈亏比
- p为成功率
- q为失败率

那么我们就可以根据凯利公式,计算出这节课中赌博的例子,100元的初始资金,在胜率为50%,赔率为2的情况下,使用多少的下注比例,可以使资金以最快的速度达到100万元,套入凯利公式,计算的过程就是:

(0.5*(2+1) -1)/2=0.25

50%的胜率也就是0.5,乘以赔率2加1,然后减去1,最后再除以2,计算的结果就是0.25,也就是在每一次赌局中,使用25%的本金下注,可以以最快的速度达到100万元。我们可以根据计算结果手动模拟一下,看对不对。

 ![img](/upload/asset/398660a9177c453a9a78.png) 

上面这样图是手动模拟结果,请看最后一行,同样的赌局,在经过100多次回合中,25%的仓位率先达到100万元。而90%、80%、70%、60%的仓位其结果是负的,这也充分说明,即使是一个正期望的交易策略,在错误的头寸管理下也会破产出局。

我们也可以看到,50%的仓位最后赌下来不输不赢,这也符合大数定律的结果。为了更加说明问题,在手动模拟的时候也加入了10%的仓位,虽然最后的结果是正收益,但是与25%的仓位比起来,效果差了几个数量级。

大家看到凯利公式的威力了吧,如果你在实际应用中选择10%的本金仓位,那么在100多次的赌局中,你的本金将会变成3万多,虽然收益很大,但是与25%的本金仓位结果比起来,相当于没赚钱。这就是知识的力量。

如果要把凯利公式在生活中赚钱,那就需要满足凯利公式的应用条件,毫无疑问,这个赌局一定是来自金融市场。特别是在量化交易中,通过历史数据回测,就能大概计算出相应的胜率和赔率。

当然凯利公式在金融交易中的实际应用不可能这么简单,还有许多细节需要处理,比如在杠杆交易中的资金成本、真实交易中的资金和仓位是不可能无线分割的、在交易中胜率和赔率是在动态变化的等等。但不管怎么样,凯利公式为我们指明了建立最佳的头寸管理方法。

Related

More

dianwan99 学习学习

Dady 学习

xunfeng91 学习