说说海龟交易法则的基本原理,如何实现海龟交易策略?

Posted 乐顺胖子

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了说说海龟交易法则的基本原理,如何实现海龟交易策略?相关的知识,希望对你有一定的参考价值。

原文地址:https://www.fmz.cn/digest-topic/8978
什么是海龟策略?
几乎所有的宽客(Quant)都听说过海龟交易策略,该策略以海龟交易法则为核心。海龟交易法则,起源于八十年代的美国,是一套简单有效的交易法则。这个法则以及使用这个法则的人的故事被写成了一本书——《海龟交易法则》,这是一本入门量化投资的经典书籍。

股票证券的程序化、量化交易以前门槛可不低,以前软件支持少,账户开户门槛极高。FMZ.CN(国内站)支持富途证券、中泰XTP,开通了富途证券就可以很方便的做程序化模拟盘、实盘测试。本篇我们就一起学习设计一个股票版本的多品种海龟交易策略,初期我们主要基于回测系统进行设计、研究,慢慢的扩展至富途证券的模拟盘(模拟账户)。

策略设计:
策略架构我们参考http://FMZ.CN上开源的「商品期货多品种海龟策略」。和商品期货版本一样,我们设计一个海龟交易逻辑管理对象的构造函数TTManager。构造的对象(obj)用来操作、管理每个股票的海龟交易逻辑的执行。


```javascript

var TTManager = 
    New: function(needRestore, symbol, initBalance, keepBalance, riskRatio, atrLen, enterPeriodA, leavePeriodA, enterPeriodB, leavePeriodB, useFilter,
        multiplierN, multiplierS, maxLots) 

        // subscribe
        var symbolDetail = _C(exchange.SetContractType, symbol)
        if (symbolDetail.VolumeMultiple == 0) 
            Log(symbolDetail)
            throw "股票合约信息异常"
         else 
            Log("合约", symbolDetail.InstrumentName, "一手", symbolDetail.VolumeMultiple, "股, 最大下单量", symbolDetail.MaxLimitOrderVolume, ", 最小下单量", symbolDetail.VolumeMultiple)
        
        
        var obj = 
            symbol: symbol,
            tradeSymbol: symbolDetail.InstrumentID,
            initBalance: initBalance,
            keepBalance: keepBalance,
            riskRatio: riskRatio,
            atrLen: atrLen,
            enterPeriodA: enterPeriodA,
            leavePeriodA: leavePeriodA,
            enterPeriodB: enterPeriodB,
            leavePeriodB: leavePeriodB,
            useFilter: useFilter,
            multiplierN: multiplierN,
            multiplierS: multiplierS
        
        obj.maxLots = maxLots
        obj.lastPrice = 0
        obj.symbolDetail = symbolDetail
        obj.status = 
            symbol: symbol,
            recordsLen: 0,
            vm: [],
            open: 0,
            cover: 0,
            st: 0,
            marketPosition: 0,
            lastPrice: 0,
            holdPrice: 0,
            holdAmount: 0,
            holdProfit: 0,
            switchCount: 0,
            N: 0,
            upLine: 0,
            downLine: 0,
            lastErr: "",
            lastErrTime: "",
            stopPrice: '',
            leavePrice: '',
            isTrading: false
        
        ...

股票市场和商品期货市场又有些差别,下面我们来一起分析一下这些差别,然后对于策略进行具体的修改、设计。

交易时间差别
我们需要单独设计一个函数,识别开盘休盘时间,如下函数设计,给构造函数TTManager返回的对象obj增加方法:

```javascript

```javascript
obj.newDate = function() 
          var timezone = 8                                
          var offset_GMT = new Date().getTimezoneOffset() 
          var nowDate = new Date().getTime()              
          var targetDate = new Date(nowDate + offset_GMT * 60 * 1000 + timezone * 60 * 60 * 1000)
          return targetDate
      

      obj.isSymbolTrading = function() 
          // 使用 newDate() 代替 new Date() 因为服务器时区问题
          var now = obj.newDate()
          var day = now.getDay()
          var hour = now.getHours()
          var minute = now.getMinutes()
          StatusMsg = "非交易时段"
          if (day === 0 || day === 6) 
              return false
          
          if((hour == 9 && minute >= 30) || (hour == 11 && minute < 30) || (hour > 9 && hour < 11)) 
              // 9:30-11:30
              StatusMsg = "交易时段"
              return true 
           else if (hour >= 13 && hour < 15) 
              // 13:00-15:00
              StatusMsg = "交易时段"
              return true 
                      
          return false 
      

交易方向的差别
商品期货有开仓、平仓。股票只有买、卖,没有开仓平仓。股票类似于现货,但是也有持仓,买入的股票会在GetPosition函数获取的持仓列表中显示。

需要我们对交易下单的部分做设计,增加函数:

```javascript
obj.sell = function(e, contractType, lots, insDetail) 
    ...


obj.buy = function(e, contractType, opAmount, insDetail) 
    ...

下单头寸计算
商品期货交易下单时是按照合约张数下单,一张合约根据其合约乘数代表一定量的商品(例如rb合约,一张代表10吨螺纹钢)。股票虽说也是有按手计算的(根据板块有的1手100股,有的500股,还有的200股)。但是下单的时候必须是股数,并且要能被一手的股数整除。不能整除的会报错。

这样就需要对海龟交易法计算头寸的部分做一定修改:

          var atrs = TA.ATR(records, atrLen)
          var N = _N(atrs[atrs.length - 1], 4)

          var account = _C(exchange.GetAccount)
          var unit = parseInt((obj.initBalance-obj.keepBalance) * (obj.riskRatio / 100) / N / obj.symbolDetail.VolumeMultiple)
          var canOpen = parseInt((account.Balance-obj.keepBalance) / (lastPrice * 1.2) / obj.symbolDetail.VolumeMultiple)
          unit = Math.min(unit, canOpen)
          unit = unit * obj.symbolDetail.VolumeMultiple
          if (unit < obj.symbolDetail.VolumeMultiple) 
              obj.setLastError("可开 " + unit + " 手 无法开仓, " + (canOpen >= obj.symbolDetail.VolumeMultiple ? "风控触发" : "资金限制") + "。 可用: " + account.Balance)
              return
          

          // 交易函数
          if (opCode == 2) 
              throw "股票不支持做空"
          

策略注释
为了方便理解策略代码,我们对策略通篇注释。

/*backtest
start: 2016-05-01 00:00:00
end: 2022-02-19 23:59:00
period: 1d
basePeriod: 1d
exchanges: ["eid":"Futures_XTP","currency":"STOCK","minFee":0]
args: [["Instruments","600519.SH,600690.SH,600006.SH,601328.SH,600887.SH,600121.SH,601633.SH"],["ATRLength",30],["EnterPeriodA",30],["LeavePeriodA",50],["EnterPeriodB",60],["LeavePeriodB",80],["KeepRatio",5]]
*/

var SlideTick = 10     // 下单滑价点数,设置10下买单时价格加10跳
var Interval = 1000    // 程序暂停毫秒数

/*
TTManager : 海龟交易逻辑对象的构造函数
参数:needRestore, symbol, initBalance, keepBalance, riskRatio, atrLen, enterPeriodA, leavePeriodA, enterPeriodB, leavePeriodB, useFilter, multiplierN, multiplierS, maxLots
     需要恢复持仓、交易品种代码、初始资产、保留资产、风险系数、ATR参数、入市周期A,离市周期A、入市周期B、离市周期B、是否使用入市过滤、加仓间隔(N的倍数)、止损系数(N的倍数)、最大加仓次数
*/
var TTManager = 
    New: function(needRestore, symbol, initBalance, keepBalance, riskRatio, atrLen, enterPeriodA, leavePeriodA, enterPeriodB, leavePeriodB, useFilter,
        multiplierN, multiplierS, maxLots) 

        // subscribe
        var symbolDetail = _C(exchange.SetContractType, symbol)       // 切换合约代码,合约代码为symbol的值
        if (symbolDetail.VolumeMultiple == 0)                        // SetContractType会返回切换的品种的一些信息,检测返回的数据中的VolumeMultiple字段是否正常
            Log(symbolDetail)
            throw "股票合约信息异常"
         else 
            Log("合约", symbolDetail.InstrumentName, "一手", symbolDetail.VolumeMultiple, "股, 最大下单量", symbolDetail.MaxLimitOrderVolume, ", 最小下单量", symbolDetail.VolumeMultiple)  // 输出相关信息
        
        
        // 声明当前构造函数TTManager返回的对象obj,该对象记录每个海龟交易逻辑的相关信息,例如执行的品种(股票代码)、ATR参数、加仓、止损N值系数等
        var obj = 
            symbol: symbol,
            tradeSymbol: symbolDetail.InstrumentID,
            initBalance: initBalance,
            keepBalance: keepBalance,
            riskRatio: riskRatio,
            atrLen: atrLen,
            enterPeriodA: enterPeriodA,
            leavePeriodA: leavePeriodA,
            enterPeriodB: enterPeriodB,
            leavePeriodB: leavePeriodB,
            useFilter: useFilter,
            multiplierN: multiplierN,
            multiplierS: multiplierS
        
        obj.maxLots = maxLots
        obj.lastPrice = 0
        obj.symbolDetail = symbolDetail
        obj.status = 
            symbol: symbol,
            recordsLen: 0,
            vm: [],
            open: 0,
            cover: 0,
            st: 0,
            marketPosition: 0,
            lastPrice: 0,
            holdPrice: 0,
            holdAmount: 0,
            holdProfit: 0,
            switchCount: 0,
            N: 0,
            upLine: 0,
            downLine: 0,
            lastErr: "",
            lastErrTime: "",
            stopPrice: '',
            leavePrice: '',
            isTrading: false
        

        // 用于记录错误的函数,记录的信息会在状态栏上显示
        obj.setLastError = function(err) 
            if (typeof(err) === 'undefined' || err === '') 
                obj.status.lastErr = ""
                obj.status.lastErrTime = ""
                return
            
            var t = new Date()
            obj.status.lastErr = err
            obj.status.lastErrTime = t.toLocaleString()
        

        // 获取指定股票代码的持仓数据
        obj.getPosition = function(e, contractTypeName) 
            var allAmount = 0
            var allProfit = 0
            var allFrozen = 0
            var posMargin = 0
            var price = 0
            var direction = null
            positions = _C(e.GetPosition)   // 根据参数e调用指定的交易所对象的获取持仓函数GetPosition,e即代表一个配置的账户,e.GetPosition即代表获取这个账户目前的持仓数据
            for (var i = 0; i < positions.length; i++) 
            	// 遍历持仓数据,找到指定的股票
                if (positions[i].ContractType != contractTypeName) 
                    continue
                
                if (positions[i].Type == PD_LONG) 
                    posMargin = positions[i].MarginLevel
                    allAmount += positions[i].Amount
                    allProfit += positions[i].Profit
                    allFrozen += positions[i].FrozenAmount
                    price = positions[i].Price
                    direction = positions[i].Type
                
            
            if (allAmount === 0) 
                return null
            
            return 
                MarginLevel: posMargin,
                FrozenAmount: allFrozen,
                Price: price,
                Amount: allAmount,
                Profit: allProfit,
                Type: direction,
                ContractType: contractTypeName,
                CanCoverAmount: allAmount - allFrozen
            
        

        // 获取当前时间对象
        obj.newDate = function() 
            var timezone = 8                                
            var offset_GMT = new Date().getTimezoneOffset() 
            var nowDate = new Date().getTime()              
            var targetDate = new Date(nowDate + offset_GMT * 60 * 1000 + timezone * 60 * 60 * 1000)
            return targetDate
        

        // 判断是否开市
        obj.isSymbolTrading = function() 
            // 使用 newDate() 代替 new Date() 因为服务器时区问题
            var now = obj.newDate()
            var day = now.getDay()
            var hour = now.getHours()
            var minute = now.getMinutes()
            StatusMsg = "非交易时段"
            if (day === 0 || day === 6) 
                return false
            
            if((hour == 9 && minute >= 30) || (hour == 11 && minute < 30) || (hour > 9 && hour < 11)) 
                // 9:30-11:30
                StatusMsg = "交易时段"
                return true 
             else if (hour >= 13 && hour < 15) 
                // 13:00-15:00
                StatusMsg = "交易时段"
                return true 
                        
            return false 
        

        // 买入函数
        obj.buy = function(e, contractType, opAmount, insDetail) 
            var initPosition = obj.getPosition(e, contractType)   // 获取初始时的持仓
            var isFirst = true
            var initAmount = initPosition ? initPosition.Amount : 0   // 设置初始持仓数量
            var positionNow = initPosition                            // 设置当前持仓数据
            if(!IsVirtual() && opAmount % insDetail.LotSize != 0)    // 判断需要交易的数量opAmount(股数)是否符合整手数
                throw "每手数量不匹配"
            
            while (true) 
                var needOpen = opAmount
                if (isFirst) 
                    isFirst = false
                 else 
                    Sleep(Interval*20)
                    positionNow = obj.getPosition(e, contractType)
                    if (positionNow) 
                        needOpen = opAmount - (positionNow.Amount - initAmount)
                    
                        
                
                // 需要交易的量如果小于一手数量,或者不符合整手数跳出循环
                if (needOpen < insDetail.LotSize || (needOpen % insDetail.LotSize != 0 && !IsVirtual())) 
                    break
                        

                var depth = _C(e.GetDepth)
                // 需要检测是否涨跌停
                var amount = needOpen
                e.SetDirection("buy")
                var orderId = e.Buy(depth.Asks[0].Price + (insDetail.PriceSpread * SlideTick), amount, contractType, 'Ask', depth.Asks[0])
                // CancelPendingOrders
                while (true) 
                    Sleep(Interval*20)
                    var orders = _C(e.GetOrders)
                    if (orders.length === 0) 
                        break
                    
                    for (var j = 0; j < orders.length; j++) 
                        e.CancelOrder(orders[j].Id)
                        if (j < (orders.length - 1)) 
                            Sleep(Interval*20)
                        
                    
                
            
            var ret = null
            if (!positionNow) 
                return ret
            
            ret = positionNow
            return ret
        
        
        // 卖出函数
        obj.sell = function(e, contractType, lots, insDetail) 
            var initAmount = 0
            var firstLoop = true
            if(!IsVirtual() && lots % insDetail.LotSize != 0) 
                throw "每手数量不匹配"
            
            while (true) 
                var n = 0
                var total = 0
                var positions = _C(e.GetPosition)
                var nowAmount = 0
                for (var i = 0; i < positions.length; i++) 
                    if (positions[i].ContractType != contractType) 
                        continue
                    
                    nowAmount += positions[i].Amount
                
                if (firstLoop) 
                    initAmount = nowAmount
                    firstLoop = false
                
                var amountChange = initAmount - nowAmount
                if (typeof(lots) == 'number' && amountChange >= lots) 
                    break
                        

                for (var i = 0; i < positions.length; i++) 
                    if (positions[i].ContractType != contractType) 
                        continue
                    
                    var amount = positions[i].Amount
                    var depth
                    var opAmount = 0
                    var opPrice = 0
                    if (positions[i].Type == PD_LONG) 
                        depth = _C(e.GetDepth)
                        // 需要检测是否涨跌停
                        opAmount = amount
                        opPrice = depth.Bids[0].Price - (insDetail.PriceSpread * SlideTick)
                    
                    if (typeof(lots) === 'number') 
                        opAmount = Math.min(opAmount, lots - (initAmount - nowAmount))
                    
                    if (opAmount > 0) 
                        if (positions[i].Type == PD_LONG) 
                            e.SetDirection("closebuy")
                            e.Sell(opPrice, opAmount, contractType, "平仓", 'Bid', depth.Bids[0])
                        
                        n++
                    
                    // break to check always
                    if (typeof(lots) === 'number') 
                        break
                    
                
                if (n === 0) 
                    break
                
                while (true) 
                    Sleep(Interval*20)
                    var orders = _C(e.GetOrders)
                    if (orders.length === 0) 
                        break
                    
                    for (var j = 0; j < orders.length; j++) 
                        e.CancelOrder(orders[j].Id)
                        if (j < (orders.length - 1)) 
                            Sleep(Interval*20)
                        
                    
                
            
        

        // 恢复控制对象数据
        obj.reset = function(marketPosition, openPrice, N, leavePeriod, preBreakoutFailure) 
            if (typeof(marketPosition) !== 'undefined') 
                obj.marketPosition = marketPosition
                obj.openPrice = openPrice
                obj.preBreakoutFailure = preBreakoutFailure
                obj.N = N
                obj.leavePeriod = leavePeriod
                var pos = obj.getPosition(exchange, obj.tradeSy

海龟交易法则

海龟交易法则

文章目录

第一章 玩风险游戏的交易者

投资者:买企业,低估值买入,高估值卖出

交易者:只关注价格

市场允许风险转移

对冲:买卖期货合约抵消原材料价格变化或外汇波动带来的风险

流动性风险和价格风险

流动性风险:价差,在两个市场套利

价格风险:价格变化

第二章 揭秘海龟思维

大多数人不会理性行事。

下面几种认知偏差

损失厌恶:不赔钱比赚钱重要

沉没成本效应:更重视已经花掉的钱

处置效应:早早兑现利润,却让亏损持续下去

结果偏好:根据一个决策的结果判断它的好坏

近期偏好:更重视近期的数据

锚定效应:过于依赖容易获得的信息

潮流效应:从众

小数定律:从太少的信息中得出没有依据的结论

海龟交易策略

趋势跟踪

大趋势很少出现,在没有趋势、趋势翻转时失效,较大资金量

反趋势交易

新高的突破都不会引发市场趋势:市场的阻力和支撑机制

波段交易

与趋势跟踪差不多,时间更短

当日交易

极端短期交易

市场状态

稳定平静,稳定波动(反趋势交易),平静趋势(趋势跟踪),波动趋势

优秀的交易者不预测走向,而是判断市场处于什么状态。

第三章 海龟培训课程

掌握优势,管理风险,坚定不移,简单明了

破产风险:随堵住增加而迅速增大

资金管理:控制风险,根据波动性(ATR指标)调整交易量

优势:限价订单好于市价订单,系统要有优势

风险投入:成交价与止损价所对应资金的差值

期望值:每笔交易平均盈利除以每笔交易平均风险投入

趋势跟踪策略

突破法(唐奇安通道法):通过过去特定时期内的最高点买入,损失超过 2ATR 时推出。

坚定不移:坚定执行策略,方能真正获得正期望值

第四章 像海龟一样思考

避免结果偏好:个别交易的结果无关紧要,运气成分

避免近期偏好:过于看重市场近期的情况,错误的认为这些市场不能碰

避免预测未来:应从概率的角度考虑未来

从概率角度考虑问题:我们不知道交易是否盈利,只知道其概率大致分布形状,亏钱的交易数量多但每笔损失少

第五章 发现系统优势

系统优势的三大要素:资产组合的选择,入市信号,出市信号。

优势比率

优势比率:En:为每个入市信号计算n天内的MFE(好的价格变动),MAE(坏的价格变动),然后除以入市时ATR后求和再取平均,最后用平均MFE除以平均MAE。

对于随机入市策略,n越大,En趋于1

唐奇安趋势系统:

1、价格突破20日最高点买入,低于20日最低点卖出

2、只在50均线高于300均线时交易

3、系统适用于中期趋势,所以E70才能看出明显效果(E70=1.2)

退出策略

衡量退出策略的优势不容易,因为他跟入市策略和退出信号都有关系。

第六章 寻找交易时机

支撑和阻力

指价格有一种不突破前期水平的倾向

这种市场行为来源于三种认知偏差:锚定效应,近期偏好,处置效应

锚定效应+近期偏好:在价格位于近期低点/高点价格处买入/卖出,形成支撑/阻力,

处置效应+近期偏好:在价格再次达到近期高点时卖出,锁定利润,形成阻力。

支撑和阻力机制是反趋势交易法优势的源泉

支撑位与阻力位的突破

向下突破支撑位时,没有人愿意在这个价格买入,所以市场不断下挫,惯性前行的趋势比其他时候更显著。

价格不稳定点

接近支撑位、阻力位边缘的价位被称作价格不稳定点。如果市场突破到一个很久没出现的新价位,锚定效应不复存在,没有任何潜在的转折点。

第七章 如何衡量风险

四大风险

衰落:回撤,大衰落会促使新手放弃系统以降低风险,然而却可能错失赚钱的机会。以回报率换低衰落。

低回报:追求稳定收益

价格动荡:指股市崩溃,可能迅速造成巨大回撤。在价格动荡前,历史检验可能会把衰落风险低估一倍。

系统死亡:过拟合,或者因市场变化而失效

风险的量化

最大衰落:最大回撤

最大衰落期:从一个顶峰到下一个顶峰的最长周期,衡量恢复速度

回报标准差:低标准差表示大多时候回报率接近平均值

R平方值:衡量实际投资回报率与平静复合增长率的吻合程度

回报的量化

平均复合增长率:平均复利率(年化收益)

滚动平均一年期回报率、平均月度回报率、净值曲线图

衡量风险和回报的综合指标

夏普比率

Sharpe Ratio:期内超额回报率(平均复合增长率-无风险回报率如国债)/ 期间回报率的标准差

是比较股票组合的一个好的指标,对于期货不是

稳定 不等于 低风险。风险很高的投资也可能在有限时期创造稳定回报。

MAR比率

年均回报率除以最大衰落幅度

模仿效应和系统死亡风险

使用特定策略的人越多,该策略的效应消失的越快。

第八章 风险与资金管理

把衰落控制在可以接受的水平,历史回测的衰落幅度不能超过承受力的1/2(呼应上文中动荡产生的回撤是历史的一倍)

在每个市场中安全的买入一定数量的股票,这个数量可以是0。

一些失败的原因:没有计划,风险过大,不切实际的期望(盲目自信)

生存第一,利用资金管理减小不确定性。

对每个一笔交易的期望和投入都相同,防止某些市场上的交易权重过高令其他市场变得无足轻重。

头寸单位规模限制罚则

仅以波动性为基础决定头村单位。把头寸分为一个个小块头村单位,每一个头寸单位:让1ATR的价格变动等于账户规模的1%,以此计算出在此市场下头寸单位的资金量。

另外,

1、每个市场最多投入4个头寸单位

2、在高度相关的多个市场中,头寸单位不超过6个

3、在任何一个方向(一个方向的很多市场)的总交易量不超过10个,若这些市场没有相关性,则放宽到12个

4、在一个方向上的市场上,同一时间只持有其中最先发出入市信号的几个市场的头寸(龙头)

第九章 海龟式积木

积木:用来指示市场状态的变化

不是万能的,仅仅是一种概率罢了。

几种积木:突破、移动平均价、波幅通道、定时退出、简单回顾

突破:市场创新高

移动平均价:20日均线上穿70日均线,指示向上趋势的开始

波幅通道:价格超越一个特定的移动平均值和另一个特定数值之和,指示有上涨趋势

定时退出:可以帮助避开趋势衰竭导致的衰落。

简单回顾法:简单回顾特定天之前的价位,如当价格超过100天前的价格与2ATR之和就买入。

第十章 海龟式交易系统

历史测试:避免过拟合

资金管理法则:1ATR=0.5%

六个系统

ATR通道突破系统

350日移动平均收盘价+7ATR为通道顶部,350日移动平均收盘价-3ATR为通道底部。若前一日收盘价穿越顶部顶部则买入,若前一日收盘价反向穿越移动平均线则卖出。

布林格突破系统

通道为350日平均收盘价加减2.5倍标准差。前一日收盘价穿越通道顶部买入

唐奇安趋势系统

20日高点突破买入,10日低点突破卖出,或在低于建仓价2ATR处止损,只在25日均线高于350日均线时交易。

四根线分别为:20日高点线,短期平均线,10日低点线,长期平均线。

4月10日买入,七月初卖出。

定时退出唐奇安趋势系统

唐奇安趋势系统的变体,改为买入80日后退出。

双重移动均线系统

100日均线穿越350日均线时买入或卖出。

三重移动均线系统

150日均线穿越250日均线1是买入或卖出,只在150日均线和250日均线均高于350日均线时进行交易。

测试结果

唐奇安定时好于唐奇安突破,说明系统的威力在于其退出策略。

没有止损退出的系统也有好的风险回报率。

测试结果发生变化

修改测试的截止日期,将后六个月的新数据加入测试,结果发生变化。在新的六个月中,除定时退出系统外,其余系统都表现得很糟糕。

第十一章 历史测试的谎言

交易者效应

其他交易者模仿,导致这种方法的效果不像原来一样好。

其他交易者利用知道的系统信息抢先行动。可以发出相反方向的假订单迷惑其他交易者。

随机效应

随机入市也可能产生优秀的回报率。1000个随机入市策略者中可能有的表现好于真正好的系统。

最优化矛盾

最优化提高了系统的预期表现,降低了历史模拟指标的预测价值。虽然如此,最优化的参数仍然最有可能带来理想的结果。

过度拟合和曲线拟合

峭壁现象:某个参数变化很小却令系统表现出很大的差异,则可能出现了过拟合。

小的样本规模是得到过拟合的重要原因。

第十二章 历史测试的统计学基础

测试样本的有效性

若只测试一个较短时期,这段时期可能只包含四种市场状态中的一两种:如稳定波动,这样反趋势策略会表现较好。若市场状态改变,则测试的方法就没那么有效了。

衡量指标的稳健性

MAR比率,平均复合增长率等指标不稳健,因为他们对测试期的起止日期敏感。若起止日期刚好有大衰落,这些指标就会对起止日期极其敏感。

回归年度回报率

称为 RAR

用线性回归线代表的回报率比平均复合回报率在起止日期上稳健的多

稳健性可以从程度和时间两个角度考虑。程度:每次衰落都很大,为35%;衰落一直很小,只有一次很大,为35%,这两个系统得出的最大衰落相同,这不合理。

稳健风险回报比率

R立方:回归年度回报率/长度调整平均最大衰落

长度调整平均最大衰落:平均最大衰落(5个最大衰落期的平均衰落)*平均衰落天数(这5次的平均衰落天数)/365

R立方与MAR比率类似,但更稳健。

稳健夏普比率

回归年度回报率/年度化的月度回报标准差

样本的代表性

市场数量多,测试时间长

样本规模

有的时候无法获得大规模样本。比如想要测试应用于大泡沫破裂时的法则,只有区区几次样本,这个法则在整个测试期内只有寥寥几次生效,所以看到的效果很可能是随机的。应该将法则一般化,提高其应用频率。

两个做法可能将小样本产生的问题放大:单一市场最优化(单个市场的交易机会少),过于复杂的系统(法则太多,难以判断某条法则发挥作用的频率)。

从虚拟测试到实战检验

参数调整检验

在实盘前,先体验一下参数得大幅变化对系统收益的影响。

滚动最优化窗口

从多年前开始,每次选取一段时间,找出这段时间前数据得出的最优化参数进行测试,然后滚动进行下一次测试。

蒙特卡洛检验

把历史稍作变化,来判断系统的稳健性。

交易调整:对于已经进行过的测试,随机改变其交易命令和起始日(如将大衰落的日期随机改变)

净值曲线调整:在初始净值曲线中随机选择一些部分(每部分为一段时间而非一个点,考虑到时间序列特征),然后组合成新的净值曲线。

第十三章 防卫系统

稳健交易策略的两大特征

分散化

选择多个不相关的市场,选择具备流动性的市场

简化

简单的法则提高系统的稳健性,能在各种不同境况下发挥作用。

使用多个系统,互补提高

第十四章 掌控自己的心魔

克服自负心理

根据机械的法则进行交易,而不是自主交易。

谦虚为上

切莫妄自尊大

坚定不移

再次强调成功海龟交易心得

  1. 掌握优势
  2. 管理风险
  3. 坚定不移
  4. 简单明了

附 原版海龟交易法

一个完整的交易系统:市场、头寸规模、入市、止损、退出、战术

市场

大账户应选择成交量大的市场,不然会引起市场动荡。

头寸规模

N:ATR

头寸规模单位:把头寸分为一个个单位,让一个单位的N(平均波动幅度)相当于总账户的1%(股票则为10%)

限制头寸大小:

  1. 单个市场:4个
  2. 高度关联的多个市场:6个
  3. 松散关联的多个市场:10个
  4. 单个方向:12个

入市策略

两个系统:

  1. 系统1:以20日突破为基础的短期系统
  2. 系统2:以55日突破为基础的长期系统

系统1入市法则

突破20日高点则买入1个头寸单位。

若上次突破是盈利性的(有机会根据10日突破退出法则退出获利),则忽略这次入市信号(在空仓情况下),并将在55日突破点入市。

若上次突破是亏损性的(在按10日突破法则退出前,发生了2N幅度的不利变动)

系统2入市法则

突破55日高点买入1个头寸单位,所有突破都有效。

逐步建仓

在突破点简历1个单位的头寸,然后按0.5N的价格间隔分批买入头寸,知道到达4个为止。(N为突破点的值)

止损点

1N价格变动代表账户的1%,在2%的风险限制下,价格变动上限为2N。

止损策略1

每个头寸的风险上限为2%

逐步买入头寸单位后,止损价也整体依次上涨0.5N。若市场变化过快,标准也会随之改变。

止损策略2:双重损失

因为这个策略的胜负比率低,其每个头寸的风险上限为0.5%

退出策略

系统1:跌破10日最低点退出。

系统2:跌破20日最低点退出。

战术

下单

限价单好于市价单。更容易以更有利得价格成交,并且对市场的触动较小。

急变市场

市场变化速度太快,顷刻间跳过订单价,没有机会成交。这时应保持冷静,等市场稳定下来再做打算,不然很容易买/卖在高/低点。

买强卖弱

多个同类市场同时出现入市信号,在最强势的市场做多,在最弱势的市场做空。

以上是关于说说海龟交易法则的基本原理,如何实现海龟交易策略?的主要内容,如果未能解决你的问题,请参考以下文章

海龟汤策略反趋势交易策略源代码分享(基于BOTVS)

海龟交易

量化投资学习常见策略海龟交易系统

量化投资学习常见策略海龟交易系统

海龟交易法操作商品期货

day33 Python与金融量化分析