设计伪高频策略以及使用实盘级别回测来研究伪高频策略

Author: 雨幕, Created: 2022-04-22 17:55:06, Updated: 2023-11-20 20:39:25

img

设计伪高频策略以及使用实盘级别回测来研究伪高频策略

在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                // 平仓单挂单时距离盘口跳数

img

img

使用实盘级别回测,回测时间范围随便选定了一周的时间,可能在别的单边行情比较剧烈的时间段回测结果更惨。所以,策略回测结果是没有悬念的惨!但是从回测结果数据中可以看到平仓盈亏是正数,亏损都在手续费上。果然高频策略最大的敌人还是手续费呀!

所以问题来了,如何能优化让盈利足够覆盖手续费呢?这个是个十分诱惑但又困难的问题。不过上面的亏钱策略代码倒是一个比较适合分析、研究的模型。希望各位在量化交易的道路上披荆斩棘,找到属于自己的量化密码。


更多内容