在FMZ上回测系统分为「模拟级别回测」、「实盘级别回测」。一般来说对于趋势策略使用模拟级别回测比较合适,数据量小,回测速度快。对于伪高频策略(真正的高频是毫秒级别的)使用实盘级别回测则比较合适。在FMZ上研究学习非常方便的一点就是有开箱即用的工具,不用自己费时费力去开发了。接下来我们就一起来探讨设计伪高频策略,以及使用实盘级别回测研究伪高频策略。
我们挑选一种最简单的高频策略思路来设计。注意,本篇文章目的不是设计一个行之有效的策略。有效的高频策略确实太难以发掘,小编我在这方面的功底还十分不足。仅仅只能边学习边写下心得,希望和大家能一同进步,掌握更多学习、研究方法。言归正传,我们使用高频做市的策略思路来设计策略。策略原理就很简单,在盘口买单、卖单列表中挂单提供流动性做市,不对价格做任何预测。这样的风险在于市场单边运行时,手上会有亏损的单边头寸。有些研究思路会结合机器学习去对行情做预判,参考预判调整做市方向和一些参数,或者停止做市,规避单边市场对做市策略的伤害。当然小编我还没有研究到机器学习等这些高阶技术。所以只能用一些简单的设计来处理不利的单边头寸。
策略设计
策略源码:
/*backtest
start: 2022-03-21 09:00:00
end: 2022-03-26 09:28:00
period: 1m
basePeriod: 1m
exchanges: [{"eid":"Futures_CTP","currency":"FUTURES"}]
mode: 1
*/
var interval = 300
var symbol = "rb2205"
var priceTick = 1
var deviation = 10
var maxCoverDeviation = 15
var profit = 5
function cancelOrders(symbol, offset) {
var orders = null
while (1) {
orders = _C(exchange.GetOrders)
if (orders.length == 0) {
break
}
for (var i = 0 ; i < orders.length ; i++) {
if ((orders[i].ContractType == symbol && orders[i].Offset == offset) || typeof(offset) == "undefined" ) {
exchange.CancelOrder(orders[i].Id, orders[i])
Sleep(interval)
}
}
Sleep(interval)
}
return orders
}
function trade(distance, price, amount) {
var tradeFunc = null
if (distance == "buy") {
tradeFunc = exchange.Buy
} else if (distance == "sell") {
tradeFunc = exchange.Sell
} else if (distance == "closebuy" || distance == "closebuy_today") {
tradeFunc = exchange.Sell
} else {
tradeFunc = exchange.Buy
}
exchange.SetDirection(distance)
return tradeFunc(price, amount)
}
function getOrderBySymbol(symbol, orders) {
var ret = []
_.each(orders, function(order) {
if (order.ContractType == symbol) {
ret.push(order)
}
})
return ret
}
function hasOrder(orders, type, offset) {
var ret = false
_.each(orders, function(order) {
if (order.Offset == offset && order.Type == type) {
ret = order
}
})
return ret
}
var p = $.NewPositionManager()
function main() {
while (true) {
if (exchange.IO("status")) {
LogStatus("已经连接", _D())
exchange.SetContractType(symbol)
var t = exchange.GetTicker()
var r = exchange.GetRecords()
var orders = getOrderBySymbol(symbol, _C(exchange.GetOrders))
var positions = _C(exchange.GetPosition)
var pos = [p.GetPosition(symbol, PD_LONG, positions), p.GetPosition(symbol, PD_SHORT, positions)]
if (orders.length == 0 && (!pos[0] && !pos[1])) {
// 当前没有挂单,没有持仓,基于盘口挂单
trade("buy", t.Buy - priceTick * deviation, 1)
trade("sell", t.Sell + priceTick * deviation, 1)
} else if (pos[0] || pos[1]) {
// 只要有持仓
if ((pos[1] && hasOrder(orders, ORDER_TYPE_BUY, ORDER_OFFSET_OPEN)) || (pos[0] && hasOrder(orders, ORDER_TYPE_SELL, ORDER_OFFSET_OPEN))) {
cancelOrders(symbol, ORDER_OFFSET_OPEN)
}
var longCoverOrder = hasOrder(orders, ORDER_TYPE_SELL, ORDER_OFFSET_CLOSE)
var shortCoverOrder = hasOrder(orders, ORDER_TYPE_BUY, ORDER_OFFSET_CLOSE)
if (pos[0] && !longCoverOrder) {
// 有多头持仓
trade("closebuy", t.Sell + priceTick * profit, pos[0].Amount)
}
if (pos[1] && !shortCoverOrder) {
// 有空头持仓
trade("closesell", t.Buy - priceTick * profit, pos[1].Amount)
}
// 重新平仓条件
if (pos[0] && longCoverOrder && longCoverOrder.Price - t.Sell > priceTick * maxCoverDeviation) {
cancelOrders(symbol)
}
if (pos[1] && shortCoverOrder && t.Buy - shortCoverOrder.Price > priceTick * maxCoverDeviation) {
cancelOrders(symbol)
}
}
} else {
LogStatus("未连接", _D())
}
Sleep(interval)
}
}
由于是研究策略,参数我都硬编码在策略代码中了:
var symbol = "rb2205" // 要做的合约代码,rb就是螺纹钢
var priceTick = 1 // 价格一跳,螺纹钢价格是1元一跳
var deviation = 10 // 挂单时距离盘口的价格跳数
var maxCoverDeviation = 15 // 平仓单距离盘口跳数的最大跳数
var profit = 5 // 平仓单挂单时距离盘口跳数
image.png
image.png
使用实盘级别回测,回测时间范围随便选定了一周的时间,可能在别的单边行情比较剧烈的时间段回测结果更惨。所以,策略回测结果是没有悬念的惨!但是从回测结果数据中可以看到平仓盈亏是正数,亏损都在手续费上。果然高频策略最大的敌人还是手续费呀!
所以问题来了,如何能优化让盈利足够覆盖手续费呢?这个是个十分诱惑但又困难的问题。不过上面的亏钱策略代码倒是一个比较适合分析、研究的模型。希望各位在量化交易的道路上披荆斩棘,找到属于自己的量化密码。
网友评论