告别多系统拼凑,DolphinDB 如何一站式搞定多资产高频策略回测

在现代金融市场的量化投资实践中,多资产策略已成为机构投资者构建低波动、绝对收益产品的重要手段。随着国内衍生品市场日益成熟,期货、期权也越来越多地被纳入跨品种、跨期限的综合配置框架。

但在量化投研的日常里,最让人抓狂的往往不是策略逻辑本身,而是繁琐的工程基建——涉足期货、期权等多资产混合策略,研究人员通常需要处理跨数据库取数、跨系统切换、保证金计算、交割日处理以及多腿订单状态管理等问题。数据拼凑、系统割裂、回测跑得慢,成了策略迭代的致命瓶颈。

为应对这一需求,DolphinDB 回测引擎提供了高性能的多资产混合回测能力。本文以一个股指期货期权 PCP 平价套利策略为例,看看如何用 DolphinDB 实现数据输入、策略开发与回测执行,为多资产策略提供完整支撑。

本文策略案例所有代码基于 3.00.6 版本 DolphinDB、3.00.6.1 版本 Backtest 插件和 MatchingEngineSimulator 插件运行。
点击《多资产股指期货期权套利策略回测案例》教程,了解本案例完整实现过程及代码!

多资产回测,难点不只在策略

这个策略利用期权平价关系(Put-Call Parity, PCP)的套利策略,同时涉及股指期货、看涨期权和看跌期权。

策略以股指期货为现货标的,结合平值期权构建合成期货头寸,通过实时监控两者的价差偏离捕捉套利机会。系统内置智能移仓逻辑以应对合约到期风险,引入 Delta 动态对冲机制剥离市场方向性波动干扰,并设计了严密的订单状态管理与追单机制,确保在极端行情下仍能维持组合的闭合状态。

看起来只是几条交易规则,但真正回测时,需要解决的问题远不止这些:

  • 数据层面:期货与期权属于不同合约体系,需要同时处理不同资产的行情和合约信息。
  • 事件层面:策略基于快照行情触发信号,行情到达的时间顺序、频率和时效性都会影响回测结果。
  • 交易层面:一个套利信号往往对应多条交易腿,还涉及订单提交、成交、撤单、追单以及持仓管理。

因此,多资产回测的难点并不是把几张行情表读进来,而是让不同资产的数据、策略逻辑和交易事件在同一个回测环境中协同运行。

DolphinDB,把复杂回测拆成一条清晰的开发流程

针对多资产策略,DolphinDB 回测引擎提供了一套相对标准化的实现路径:

研究人员不需要从零开始搭建一套回测框架,而是围绕数据、回调函数和引擎参数完成策略开发。

1、策略输入:准备基本信息表和行情表

首先准备两类核心数据:基本信息表和行情表

基本信息表记录合约、乘数、保证金、手续费、到期日等信息,为期货期权的交易、保证金和交割处理提供依据;行情表则负责提供策略运行过程中所需的实时历史行情。

在本案例中,期货和期权行情都采用快照数据,在输入策略回测引擎前,需将行情处理成回测引擎要求的数据结构,并通过 appendQuotationMsg 接口向回测引擎持续注入行情。从研究数据到策略回测,中间不需要再搭建其他数据转换和传输链路。

2、策略开发:策略回调函数与回调函数参数

DolphinDB 提供了一套清晰的策略回调框架,研究人员只需要在对应的策略回调函数中编写自己的交易逻辑。回调函数包括:

  • initialize:完成策略初始化;
  • beforeTrading:进行每日盘前处理,例如移仓换月;
  • onSnapshot:行情到来时计算信号、执行开平仓;
  • onOrder、onTrade:处理订单和成交事件;
  • afterTrading、finalize:进行盘后处理和策略结束处理。

本案例的核心交易逻辑在 onSnapshot 中。行情到来后,策略读取期货与期权最新报价,计算合成价格与期货价格之间的偏离;满足条件时,通过 Backtest::submitOrder 同时提交期权交易指令。

def onSnapshot(mutable context, msg, indicator){
    ......
    //1. 判断是否做多头:(C_bid - P_ask + K * DF) < Lastprice(F)-threshold 
    if (context['options'][context['ATM_C']]['bidPrice'] != NULL and context['options'][context['ATM_P']]['askPrice'] != NULL){
            c_bid = context['options'][context['ATM_C']]['bidPrice']
            p_ask = context['options'][context['ATM_P']]['askPrice']  
            if (callLongPos<context['maxQty'] && putShortPos<context['maxQty'] && 
                ((c_bid-p_ask+strike*discount_factor) < (f_last-threshold)) ){
                // 下单(标的代码, 交易所代码, 时间, 订单类型, 委托订单价格, 止损价/止盈价,委托订单数量,买卖方向,委托订单有效性,委托订单到期时间)
                print(context['tradeTime']+":开仓多头") // 买call卖put
                print("买入:"+context['ATM_C'])
                print("卖出:"+context['ATM_P'])
                Backtest::submitOrder(context["engine"], 
                    (context['ATM_C'],msg["symbolSource"],context['tradeTime'],5,c_bid, , ,context['orderQty'], 1,,,),"buyOpenCall", 0, 'option')
                Backtest::submitOrder(context["engine"], 
                    (context['ATM_P'],msg["symbolSource"],context['tradeTime'],5,p_ask, , ,context['orderQty'], 2,,,),"buyOpenPut",0, 'option')
            }
        }
        //2. 判断是否做空头:(C_ask - P_bid + K * DF) > Lastprice(F)+threshold
        if (context['options'][context['ATM_C']]['askPrice'] != NULL and context['options'][context['ATM_P']]['bidPrice'] != NULL){
            c_ask = context['options'][context['ATM_C']]['askPrice']
            p_bid = context['options'][context['ATM_P']]['bidPrice']
            if (callShortPos<context['maxQty'] && putLongPos<context['maxQty'] &&
                (c_ask-p_bid+strike*discount_factor) > (f_last+threshold) ){
                //下单
                print(context['tradeTime']+":开仓空头") // 卖call买put
                print("卖出:"+context['ATM_C'])
                print("买入:"+context['ATM_P'])
                Backtest::submitOrder(context["engine"], 
                    (context['ATM_C'],msg["symbolSource"],context['tradeTime'],5,c_ask, , ,context['orderQty'], 2,,,),"sellOpenCall", 0, 'option')
                Backtest::submitOrder(context["engine"], 
                    (context['ATM_P'],msg["symbolSource"],context['tradeTime'],5,p_bid, , ,context['orderQty'], 1,,,),"sellOpenPut", 0, 'option')
            }
        }
        ......
}

研究人员只需要关注“什么时候交易、交易什么”,而订单提交、行情触发等回测流程由引擎负责承接。

3、策略配置:设置策略参数

策略逻辑写好之后,还需要告诉回测引擎:从什么时候开始跑?用多少资金?采用什么行情频率?如何进行撮合?这些内容可以通过引擎配置参数统一设置。

例如,本案例配置了开始日期、结束日期、初始资金、行情类型、回调模式、订单延时以及多资产行情输入方式等参数。同时,策略中需要频繁调整的参数也可以独立配置,例如套利阈值、无风险利率、单笔下单量、最大下单量以及 Delta 风险上限等。

这样一来,策略逻辑和回测参数可以分离。研究人员修改参数后,可以直接开展下一轮回测,而不需要反复修改核心策略代码。

4、回测结果:调用接口获取策略结果

DolphinDB 提供了不同接口,获取不同维度的回测结果,包括:交易明细、账户持仓、每日权益以及收益概述。

在本案例中,通过 Backtest::getTradeDetails 获取交易明细:

期货交易明细(部分)

Backtest::getPosition 获取账户每日持仓:

Backtest::getDailyTotalPortfolios 获取账户每日权益:

至此,一套从数据输入到策略运行,再到结果输出的完整回测链路就跑通了。

把复杂留给引擎,把简单留给开发者

在传统量化研发中,数据库、回测系统是割裂的,而 DolphinDB 将这一切整合在统一平台内——把复杂留给引擎,把简单留给开发者。研究人员可以把更多精力放在策略研究本身,而不是回测基础设施的搭建上。

回到这个案例。我们真正想展示的,并不是 PCP 套利策略本身,而是:当一个涉及期货、期权、快照行情、多腿订单和风险控制的复杂策略进入回测阶段时,DolphinDB 能够如何承载它

如果你正在为多资产策略回测的复杂性苦恼,不妨试试来 DolphinDB。

posted @ 2026-09-22 09:36  DolphinDB  阅读(7)  评论(0)    收藏  举报