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

本文介绍了海龟交易策略的基本原理,并探讨了如何将其应用于股票交易。通过FMZ.CN平台,展示了如何设计一个股票的多品种海龟交易策略。策略包括下单头寸的计算调整,以适应股票交易的特性。回测结果显示,海龟交易法在趋势明显时表现良好,但止损次数多于盈利次数,强调了选股的重要性。完整策略可在FMZ.CN查看,仅供回测研究,实盘需进一步优化。
摘要由CSDN通过智能技术生成

原文地址: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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值