100字范文,内容丰富有趣,生活中的好帮手!
100字范文 > 说说海龟交易法则的基本原理 如何实现海龟交易策略?

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

时间:2023-06-01 17:32:37

相关推荐

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

原文地址:/digest-topic/8978

什么是海龟策略?

几乎所有的宽客(Quant)都听说过海龟交易策略,该策略以海龟交易法则为核心。海龟交易法则,起源于八十年代的美国,是一套简单有效的交易法则。这个法则以及使用这个法则的人的故事被写成了一本书——《海龟交易法则》,这是一本入门量化投资的经典书籍。

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

策略设计:

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

```javascriptvar TTManager = {New: function(needRestore, symbol, initBalance, keepBalance, riskRatio, atrLen, enterPeriodA, leavePeriodA, enterPeriodB, leavePeriodB, useFilter,multiplierN, multiplierS, maxLots) {// subscribevar 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 = maxLotsobj.lastPrice = 0obj.symbolDetail = symbolDetailobj.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```javascriptobj.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:30StatusMsg = "交易时段"return true } else if (hour >= 13 && hour < 15) {// 13:00-15:00StatusMsg = "交易时段"return true } return false }

交易方向的差别商品期货有开仓、平仓。股票只有买、卖,没有开仓平仓。股票类似于现货,但是也有持仓,买入的股票会在GetPosition函数获取的持仓列表中显示。需要我们对交易下单的部分做设计,增加函数:```javascriptobj.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.VolumeMultipleif (unit < obj.symbolDetail.VolumeMultiple) {obj.setLastError("可开 " + unit + " 手 无法开仓, " + (canOpen >= obj.symbolDetail.VolumeMultiple ? "风控触发" : "资金限制") + "。 可用: " + account.Balance)return}// 交易函数if (opCode == 2) {throw "股票不支持做空"}

策略注释

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

/*backteststart: -05-01 00:00:00end: -02-19 23:59:00period: 1dbasePeriod: 1dexchanges: [{"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) {// subscribevar 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 = maxLotsobj.lastPrice = 0obj.symbolDetail = symbolDetailobj.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 = errobj.status.lastErrTime = t.toLocaleString()}// 获取指定股票代码的持仓数据obj.getPosition = function(e, contractTypeName) {var allAmount = 0var allProfit = 0var allFrozen = 0var posMargin = 0var price = 0var direction = nullpositions = _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].MarginLevelallAmount += positions[i].AmountallProfit += positions[i].ProfitallFrozen += positions[i].FrozenAmountprice = positions[i].Pricedirection = 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:30StatusMsg = "交易时段"return true } else if (hour >= 13 && hour < 15) {// 13:00-15:00StatusMsg = "交易时段"return true } return false }// 买入函数obj.buy = function(e, contractType, opAmount, insDetail) {var initPosition = obj.getPosition(e, contractType) // 获取初始时的持仓var isFirst = truevar initAmount = initPosition ? initPosition.Amount : 0 // 设置初始持仓数量var positionNow = initPosition // 设置当前持仓数据if(!IsVirtual() && opAmount % insDetail.LotSize != 0) {// 判断需要交易的数量opAmount(股数)是否符合整手数throw "每手数量不匹配"}while (true) {var needOpen = opAmountif (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 = needOpene.SetDirection("buy")var orderId = e.Buy(depth.Asks[0].Price + (insDetail.PriceSpread * SlideTick), amount, contractType, 'Ask', depth.Asks[0])// CancelPendingOrderswhile (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 = nullif (!positionNow) {return ret}ret = positionNowreturn ret}// 卖出函数obj.sell = function(e, contractType, lots, insDetail) {var initAmount = 0var firstLoop = trueif(!IsVirtual() && lots % insDetail.LotSize != 0) {throw "每手数量不匹配"}while (true) {var n = 0var total = 0var positions = _C(e.GetPosition)var nowAmount = 0for (var i = 0; i < positions.length; i++) {if (positions[i].ContractType != contractType) {continue}nowAmount += positions[i].Amount}if (firstLoop) {initAmount = nowAmountfirstLoop = false}var amountChange = initAmount - nowAmountif (typeof(lots) == 'number' && amountChange >= lots) {break} for (var i = 0; i < positions.length; i++) {if (positions[i].ContractType != contractType) {continue}var amount = positions[i].Amountvar depthvar opAmount = 0var opPrice = 0if (positions[i].Type == PD_LONG) {depth = _C(e.GetDepth)// 需要检测是否涨跌停opAmount = amountopPrice = 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 alwaysif (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 = marketPositionobj.openPrice = openPriceobj.preBreakoutFailure = preBreakoutFailureobj.N = Nobj.leavePeriod = leavePeriodvar pos = obj.getPosition(exchange, obj.tradeSymbol)if (pos) {obj.holdPrice = pos.Priceobj.holdAmount = pos.AmountLog(obj.symbol, "仓位", pos)} else {throw "恢复" + obj.symbol + "的持仓状态出错, 没有找到仓位信息"}Log("恢复", obj.symbol, "加仓次数", obj.marketPosition, "持仓均价:", obj.holdPrice, "持仓数量:", obj.holdAmount, "最后一次加仓价", obj.openPrice, "N值", obj.N, "离市周期:", leavePeriod, "上次突破:", obj.preBreakoutFailure ? "失败" : "成功")obj.status.open = 1obj.status.vm = [obj.marketPosition, obj.openPrice, obj.N, obj.leavePeriod, obj.preBreakoutFailure]} else {obj.marketPosition = 0obj.holdPrice = 0obj.openPrice = 0obj.holdAmount = 0obj.holdProfit = 0obj.preBreakoutFailure = true// test system Aobj.N = 0obj.leavePeriod = leavePeriodA}obj.holdProfit = 0obj.lastErr = ""obj.lastErrTime = ""}// 获取控制对象状态信息obj.Status = function() {obj.status.N = obj.Nobj.status.marketPosition = obj.marketPositionobj.status.holdPrice = obj.holdPriceobj.status.holdAmount = obj.holdAmountobj.status.lastPrice = obj.lastPriceif (obj.lastPrice > 0 && obj.holdAmount > 0 && obj.marketPosition !== 0) {obj.status.holdProfit = _N((obj.lastPrice - obj.holdPrice) * obj.holdAmount * obj.symbolDetail.VolumeMultiple, 4) * (obj.marketPosition > 0 ? 1 : -1)} else {obj.status.holdProfit = 0}obj.status.symbolDetail = obj.symbolDetailreturn obj.status}// 执行海龟交易逻辑obj.Poll = function() {obj.status.isTrading = obj.isSymbolTrading(obj.symbol)if (!obj.status.isTrading) {return}var suffix = WXPush ? '@' : ''// switch symbolvar insDetail = exchange.SetContractType(obj.symbol)if (!insDetail) {return}// 获取tick数据var ticker = exchange.GetTicker()if (!ticker) {obj.setLastError("获取tick失败")return}if (IsVirtual()) {ticker.Info = {}ticker.Info.LotSize = obj.symbolDetail.VolumeMultipleticker.Info.PriceSpread = 0.01}var records = exchange.GetRecords()if (!records) {obj.setLastError("获取K线失败")return}obj.status.recordsLen = records.lengthif (records.length < obj.atrLen) {obj.setLastError("K线长度小于 " + obj.atrLen)return}var opCode = 0 // 0: IDLE, 1: LONG, 3: CoverALLvar lastPrice = records[records.length - 1].Closeobj.lastPrice = lastPriceif (obj.marketPosition === 0) {obj.status.stopPrice = '--'obj.status.leavePrice = '--'obj.status.upLine = 0obj.status.downLine = 0for (var i = 0; i < 2; i++) {if (i == 0 && obj.useFilter && !obj.preBreakoutFailure) {continue}var enterPeriod = i == 0 ? obj.enterPeriodA : obj.enterPeriodBif (records.length < (enterPeriod + 1)) {continue}var highest = TA.Highest(records, enterPeriod, 'High')var lowest = TA.Lowest(records, enterPeriod, 'Low')obj.status.upLine = obj.status.upLine == 0 ? highest : Math.min(obj.status.upLine, highest)obj.status.downLine = obj.status.downLine == 0 ? lowest : Math.max(obj.status.downLine, lowest)if (lastPrice > highest) {opCode = 1}if (opCode != 0) {obj.leavePeriod = (enterPeriod == obj.enterPeriodA) ? obj.leavePeriodA : obj.leavePeriodBbreak}}} else {var spread = obj.marketPosition > 0 ? (obj.openPrice - lastPrice) : (lastPrice - obj.openPrice)obj.status.stopPrice = _N(obj.openPrice + (obj.N * StopLossRatio * (obj.marketPosition > 0 ? -1 : 1)))if (spread > (obj.N * StopLossRatio)) {opCode = 3obj.preBreakoutFailure = trueLog(obj.symbolDetail.InstrumentName, "止损平仓", suffix)obj.status.st++} else if (-spread > (IncSpace * obj.N) && Math.abs(obj.marketPosition) < obj.maxLots) {opCode = obj.marketPosition > 0 ? 1 : 2}if (opCode == 0 && records.length > obj.leavePeriod) {obj.status.leavePrice = obj.marketPosition > 0 ? TA.Lowest(records, obj.leavePeriod, 'Low') : TA.Highest(records, obj.leavePeriod, 'High')if ((obj.marketPosition > 0 && lastPrice < obj.status.leavePrice) ||(obj.marketPosition < 0 && lastPrice > obj.status.leavePrice)) {obj.preBreakoutFailure = falseLog(obj.symbolDetail.InstrumentName, "正常平仓", suffix)opCode = 3obj.status.cover++}}}if (opCode == 0) {return}if (opCode == 3) {var pos = obj.getPosition(exchange, obj.tradeSymbol)obj.sell(exchange, obj.tradeSymbol, pos.Amount, ticker.Info)obj.reset()_G(obj.symbol, null)var account = _C(exchange.GetAccount)return}// Openif (Math.abs(obj.marketPosition) >= obj.maxLots) {obj.setLastError("禁止开仓, 超过最大持仓 " + obj.maxLots)return}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.VolumeMultipleif (unit < obj.symbolDetail.VolumeMultiple) {obj.setLastError("可开 " + unit + " 手 无法开仓, " + (canOpen >= obj.symbolDetail.VolumeMultiple ? "风控触发" : "资金限制") + "。 可用: " + account.Balance)return}// 交易函数if (opCode == 2) {throw "股票不支持做空"}var ret = obj.buy(exchange, obj.tradeSymbol, unit, ticker.Info)if (ret) {Log(obj.symbolDetail.InstrumentName, obj.marketPosition == 0 ? "开仓" : "加仓", "离市周期", obj.leavePeriod, suffix)obj.N = Nobj.openPrice = ticker.Lastobj.holdPrice = ret.Priceif (obj.marketPosition == 0) {obj.status.open++}obj.holdAmount = ret.Amountobj.marketPosition += opCode == 1 ? 1 : -1obj.status.vm = [obj.marketPosition, obj.openPrice, N, obj.leavePeriod, obj.preBreakoutFailure]_G(obj.symbol, obj.status.vm)} else {obj.setLastError("下单失败")return}}var vm = nullif (RMode === 0) {vm = _G(obj.symbol)} else {vm = JSON.parse(VMStatus)[obj.symbol]}if (vm) {Log("准备恢复进度, 当前合约状态为", vm)obj.reset(vm[0], vm[1], vm[2], vm[3], vm[4])} else {if (needRestore) {Log("没有找到" + obj.symbol + "的进度恢复信息")}obj.reset()}return obj}}function onexit() {Log("已退出策略...")}function main() {if((!IsVirtual() && exchange.GetCurrency() != "STOCK" && exchange.GetName() != "Futures_Futu") || (IsVirtual() && exchange.GetCurrency() != "STOCK_CNY" && exchange.GetName() != "Futures_XTP")) {Log("currency:", exchange.GetCurrency(), "name:", exchange.GetName())throw "不支持"}SetErrorFilter("login|ready|流控|连接失败|初始|Timeout|market not ready")while (!exchange.IO("status")) {Sleep(3000)LogStatus("正在等待与交易服务器连接, " + _D())}var positions = _C(exchange.GetPosition)if (positions.length > 0) {Log("检测到当前持有仓位, 系统将开始尝试恢复进度...")Log("持仓信息", positions)}Log("风险系数:", RiskRatio, "N值周期:", ATRLength, "系统1: 入市周期", EnterPeriodA, "离市周期", LeavePeriodA, "系统二: 入市周期", EnterPeriodB, "离市周期", LeavePeriodB, "加仓系数:", IncSpace, "止损系数:", StopLossRatio, "单品种最多开仓:", MaxLots, "次")var initAccount = _C(exchange.GetAccount)var realInitBalance = initAccount.Balanceif (CustomBalance) {realInitBalance = InitBalanceLog("自定义启动资产为", realInitBalance)}var keepBalance = _N(realInitBalance * (KeepRatio/100), 3)Log("当前资产信息", initAccount, "保留资金:", keepBalance)var tts = []var filter = []var arr = Instruments.split(',')for (var i = 0; i < arr.length; i++) {var symbol = arr[i].replace(/^\s+/g, "").replace(/\s+$/g, "");if (typeof(filter[symbol]) !== 'undefined') {Log(symbol, "已经存在, 系统已自动过滤")continue}filter[symbol] = truevar hasPosition = falsefor (var j = 0; j < positions.length; j++) {if (positions[j].ContractType == symbol) {hasPosition = truebreak}}var obj = TTManager.New(hasPosition, symbol, realInitBalance, keepBalance, RiskRatio, ATRLength, EnterPeriodA, LeavePeriodA, EnterPeriodB, LeavePeriodB, UseEnterFilter, IncSpace, StopLossRatio, MaxLots)tts.push(obj)}var tblAssets = nullvar nowAccount = nullvar lastStatus = ''while (true) {if (GetCommand() === "暂停/继续") {Log("暂停交易中...")while (GetCommand() !== "暂停/继续") {Sleep(1000)}Log("继续交易中...")}while (!exchange.IO("status")) {Sleep(3000)LogStatus("正在等待与交易服务器连接, " + _D() + "\n" + lastStatus)}var tblStatus = {type: "table",title: "持仓信息",cols: ["合约名称", "持仓方向", "持仓均价", "持仓数量", "持仓盈亏", "加仓次数", "开仓次数", "止损次数", "成功次数", "当前价格", "N"],rows: []}var tblMarket = {type: "table",title: "运行状态",cols: ["合约名称", "合约乘数", "保证金率", "交易时间", "移仓次数", "柱线长度", "上线", "下线", "止损价", "离市价", "异常描述", "发生时间"],rows: []}var totalHold = 0var vmStatus = {}var ts = new Date().getTime()var holdSymbol = 0var tradingCount = 0for (var i = 0; i < tts.length; i++) {tts[i].Poll()var d = tts[i].Status()if (d.holdAmount > 0) {vmStatus[d.symbol] = d.vmholdSymbol++}if (d.isTrading) {tradingCount++}tblStatus.rows.push([d.symbolDetail.InstrumentID + "/" + d.symbolDetail.InstrumentName, d.holdAmount == 0 ? '--' : (d.marketPosition > 0 ? '多' : '空'), d.holdPrice, d.holdAmount, d.holdProfit, Math.abs(d.marketPosition), d.open, d.st, d.cover, d.lastPrice, d.N])tblMarket.rows.push([d.symbolDetail.InstrumentID + "/" + d.symbolDetail.InstrumentName, d.symbolDetail.VolumeMultiple, _N(d.symbolDetail.LongMarginRatio, 4) + '/' + _N(d.symbolDetail.ShortMarginRatio, 4), (d.isTrading ? '是#0000ff' : '否#ff0000'), d.switchCount, d.recordsLen, d.upLine, d.downLine, d.stopPrice, d.leavePrice, d.lastErr, d.lastErrTime])totalHold += Math.abs(d.holdAmount)}var now = new Date()var elapsed = now.getTime() - tslastStatus = '`' + JSON.stringify([tblStatus, tblMarket]) + '`\n轮询耗时: ' + elapsed + ' 毫秒, 当前时间: ' + _D() + ', 星期' + ['日', '一', '二', '三', '四', '五', '六'][now.getDay()] + ", 持有品种个数: " + holdSymbol + ", 手动恢复字符串: " + JSON.stringify(vmStatus)LogStatus(lastStatus)Sleep(LoopInterval * 1000)}}

回测测试、研究

我们选择几只股票回测:600519.SH,600690.SH,600006.SH,601328.SH,600887.SH,600121.SH,601633.SH。

其它参数设置:

回测时状态栏信息输出:

可以观察到,海龟交易法这种趋势跟踪策略需要在有较大的行情时才会有较好的盈利。在行情反复震荡时可能会有一定回撤。

涨幅较大的贵州茅台贡献了整体收益的绝大部分,看来选股也是十分重要的因素。并且根据状态栏中显示的统计数据来看,海龟交易法的止损次数要远高于策略成功盈利次数。这也是策略的思路核心,用较小的头寸试错。一旦抓住趋势突破加仓,抓住肥尾。创造震荡期损失数倍的盈利。

完整策略:/strategy/346551

该策略仅用于回测研究,实盘请自行优化、修改。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。