输入/搜索内容
3
关注
226
关注者
A股港股对冲套利策略(2)
交流分享
创建于 2021-09-09 16:04:24  更新于 2024-11-28 09:10:28
 0
 2089

img

A股港股对冲套利策略(2)

上一篇我们在回测系统里研究了「A股港股对冲套利策略」,本篇我们从实战角度出发。在富途的模拟盘中对策略进行测试,升级。

策略参数设计

策略真正创建实盘开始测试时,参数就不能再写死在策略代码中了,参数就需要配置在策略界面参数上。

var symbolH = "02333.HK" // 港股代码 var symbolA = "601633.SH" // A股代码 var isGetBaseStocks = true // 是否需要建底仓 ...

设计在策略界面参数上,这些变量在策略代码中以全局变量使用(其实就是全局变量,可以参看API文档上策略参数的说明)。

img

回测时是必须要创建底仓的,否则没法对冲。在实际模拟盘测试就不一定了,因为可能上一次运行时已经创建好底仓了。所以对于「创建底仓股数」这个参数可以让它基于「是否需要创建底仓」这个参数来显示或者隐藏。写法如下:

baseStocks@isGetBaseStocks

这样在勾选了「是否需要创建底仓」参数时「创建底仓股数」才会显示,该功能在API文档上也有描述可以查看。

在实际测试运行时,有时候还需要清空之前的所有记录,所以还需要设计个重置机制。

所以设计了isReset参数,策略代码中有对应的处理代码。

// 重置所有 if(isReset) { // 如果勾选了界面上isReset参数 _G(null) // 清空所有持久化记录的数据 LogReset(1) // 清空所有日志 LogProfitReset() // 清空所有收益图表数据 LogVacuum() // 释放数据库中日志空间 Log("重置所有数据", "#FF0000") // 输出信息 }

对冲的开仓差价、平仓差价之前是写死在代码中的。同样这些最好做成参数可以在策略界面参数上设置。

所以策略参数上又多了:

minHedgeDiffPrice 最小对冲差价 spacing 每次对冲差价间距 hedgeProfit 对冲利润差价

对应代码中修改为:

if (a2h > minHedgeDiffPrice + level_a2h * spacing) { var ret = hedge(symbolH, symbolA, tickerH.Sell, tickerA.Buy, hedgeAmount) if (ret) { level_a2h++ $.PlotFlag(ts, 'sell', 'S') Log("ret:", ret, "对冲差价:", tickerA.Buy - tickerH.Sell) } } else if (-h2a > minHedgeDiffPrice + level_h2a * spacing) { var ret = hedge(symbolA, symbolH, tickerA.Sell, tickerH.Buy, hedgeAmount) if (ret) { level_h2a++ $.PlotFlag(ts, 'buy', 'B') Log("ret:", ret, "对冲差价:", tickerA.Sell - tickerH.Buy) } } if (a2h < minHedgeDiffPrice + (level_a2h - 1) * spacing - hedgeProfit && level_a2h > 0) { var ret = hedge(symbolA, symbolH, tickerA.Sell, tickerH.Buy, hedgeAmount) if (ret) { level_a2h-- $.PlotFlag(ts, 'buy', 'B') Log("ret:", ret, "对冲差价:", tickerA.Sell - tickerH.Buy) calcProfit(initAcc, tickerA.Last, tickerH.Last) } } else if (-h2a < minHedgeDiffPrice + (level_h2a - 1) * spacing - hedgeProfit && level_h2a > 0) { var ret = hedge(symbolH, symbolA, tickerH.Sell, tickerA.Buy, hedgeAmount) if (ret) { level_h2a-- $.PlotFlag(ts, 'sell', 'S') Log("ret:", ret, "对冲差价:", tickerA.Buy - tickerH.Sell) calcProfit(initAcc, tickerA.Last, tickerH.Last) } }

策略中数据持久化记录

因为策略中对冲了需要计算盈亏、如果策略重启了需要恢复数据记录等。在策略设计中就必须有数据持久化、数据恢复机制。

例如:

  • 计算盈亏
    计算盈亏需要用当前的资产数值(钱数) 减去 最初记录的资产数值(钱数)。当前的资产数值可以随时获取,但是最初的资产数据呢?就算开始获取了,如果策略重启了呢?所以需要对于初始资产持久化记录,并且在重启时还能恢复。

    if (!initAcc) { initAcc = _G("initAcc") if (!initAcc) { initAcc = updateAcc() _G("initAcc", initAcc) } Log("初始账户数据", initAcc) Log("A股资产:", initAcc.initAcc_A.Balance + initAcc.initAcc_A.FrozenBalance, "港股资产:", initAcc.initAcc_H.Balance + initAcc.initAcc_H.FrozenBalance) }

    代码中这段主要就是用于记录最初账户和持仓数据、恢复最初记录的账户和持仓数据。

  • 恢复对冲的进度
    在代码中初始化的位置:

    var level = _G("level") if (!level) { level = {"level_a2h" : 0, "level_h2a" : 0} _G("level", level) } else { Log("载入level:", level) } level_a2h = level.level_a2h level_h2a = level.level_h2a

    有这样的一段代码,用来恢复对冲进度。和账户最初持仓、资产数据不同的是对冲进度相关的数据level_a2hlevel_h2a需要每次策略停止时更新,所以需要给策略设计上扫尾函数。

    扫尾函数:

    // 策略正常退出时扫尾 function onexit() { var level = {"level_a2h" : level_a2h, "level_h2a" : level_h2a} _G("level", level) Log("记录:", level) } // 策略异常退出时扫尾 function onerror() { var level = {"level_a2h" : level_a2h, "level_h2a" : level_h2a} _G("level", level) Log("记录:", level) }

交互命令

回测了一下和A股港股对冲套利策略(1)文章中的回测结果一样。回测是策略在历史数据中的快速运行,但是使用富途模拟盘测试的时候等待差价触发开仓对冲、平仓对冲一个周期可能要等很久。所以给策略加上交互按钮让策略可以手动对冲。

img

策略代码中就需要对交互控件响应处理:

// 处理交互命令 if (cmd) { Log("接收到命令:", cmd) var arr = cmd.split(":") if (arr[0] == "buyH_sellA") { var amount = parseFloat(arr[1]) var ret = hedge(symbolH, symbolA, tickerH.Sell, tickerA.Buy, amount) if (ret) { $.PlotFlag(ts, 'sell', 'S') Log("ret:", ret, "对冲差价:", tickerA.Buy - tickerH.Sell) calcProfit(initAcc, tickerA.Last, tickerH.Last) } } else if (arr[0] == "buyA_sellH") { var amount = parseFloat(arr[1]) var ret = hedge(symbolA, symbolH, tickerA.Sell, tickerH.Buy, amount) if (ret) { $.PlotFlag(ts, 'buy', 'B') Log("ret:", ret, "对冲差价:", tickerA.Sell - tickerH.Buy) calcProfit(initAcc, tickerA.Last, tickerH.Last) } } }

那么这就在富途模拟盘上跑起来!

img

卖出A股,买入港股的开仓对冲差价:34.17,平仓对冲差价:33.51。34.17-33.51理论上是一股赚取了0.66的差价,那么1000股就是0.66*1000=660元,当然还要减去手续费之类的。

使用盈亏计算函数计算盈亏

function calcProfit(initAcc, priceA, priceH) { Sleep(5000) // 需要等待,否则拿到的是旧数据,会引起计算错误 var acc0 = _C(exchanges[0].GetAccount) var acc1 = _C(exchanges[1].GetAccount) var pos0 = GetPosition(exchanges[0], symbolA) var pos1 = GetPosition(exchanges[1], symbolH) var holdA = pos0 ? pos0.Amount : 0 var holdH = pos1 ? pos1.Amount : 0 var holdA_DiffBalance = (holdA - initAcc.holdA) * priceA var holdH_DiffBalance = (holdH - initAcc.holdH) * priceH LogProfit((acc0.Balance + acc1.Balance) - (initAcc.initAcc_A.Balance + initAcc.initAcc_H.Balance) + (holdA_DiffBalance + holdH_DiffBalance), acc0.Balance + acc0.FrozenBalance, acc1.Balance + acc1.FrozenBalance) }

img

图中的持仓盈亏就不是我们的考察对象了,我们只考察对冲带来的盈亏。算上底仓建仓净值是亏的。

持仓、价格等信息实时显示

把策略持仓、价格等数据实时显示在状态栏,通过以下代码把数据写入tbl结构中。

// tbl var acc0 = exchanges[0].GetAccount() var balance = frozenBalance = "--" if (acc0) { balance = acc0.Balance frozenBalance = acc0.FrozenBalance } var pos0 = GetPosition(exchanges[0], symbolA) var holdPrice = holdAmount = holdProfit = holdCanCoverAmount = "--" if (pos0) { holdPrice = pos0.Price holdAmount = pos0.Amount holdProfit = pos0.Profit holdCanCoverAmount = pos0.CanCoverAmount } tbl.rows.push([symbolA, tickerA.Last, balance, frozenBalance, holdPrice, holdAmount, holdProfit, holdCanCoverAmount]) var acc1 = exchanges[1].GetAccount() var balance = frozenBalance = "--" if (acc1) { balance = acc1.Balance frozenBalance = acc1.FrozenBalance } var pos1 = GetPosition(exchanges[1], symbolH) var holdPrice = holdAmount = holdProfit = holdCanCoverAmount = "--" if (pos1) { holdPrice = pos1.Price holdAmount = pos1.Amount holdProfit = pos1.Profit holdCanCoverAmount = pos1.CanCoverAmount } tbl.rows.push([symbolH, tickerH.Last, balance, frozenBalance, holdPrice, holdAmount, holdProfit, holdCanCoverAmount]) lastTbl = tbl if (acc0 && acc1) { floatProfit += (acc0.Balance + acc1.Balance) - (initAcc.initAcc_A.Balance + initAcc.initAcc_H.Balance) + (((pos0 ? pos0.Amount : 0) - initAcc.holdA) * tickerA.Last + ((pos1 ? pos1.Amount : 0) - initAcc.holdH) * tickerH.Last) }

使用LogStatus函数把tbl结构输出在状态栏上显示成表格。

LogStatus(_D(), statusMsg, "对冲的浮动盈亏:", floatProfit, "\n", "`" + JSON.stringify(tbl) + "`")

我们用回测再测试下,这样策略运行时就和上一篇文章中状态栏的显示不同了。

升级后:

img

升级前:

img

策略用于学习、教学。
股市有风险,入市需谨慎。

策略源码:https://www.youquant.com/strategy/312721

相关推荐
评论
全部评论 (0)
暂无数据
暂无数据
  • 1
iPhone 下载
社区
回测系统
© 2015 - ∞ YouQuant 豫ICP备19046564号