// ===========================  插件加载  ===========================
try{loadPlugin("Backtest")} catch(ex){}
try{loadPlugin("MatchingEngineSimulator")} catch(ex){}
go

// =========================== 策略回调函数 ===========================
// ------------------------------
// initialize：策略初始化函数，只触发一次。
// 负责初始化自定义变量
// ------------------------------
def initialize(mutable context){
    context['front_month_contract'] = ""                      // 主力期货合约名称
    context['front_month_contract_latest_price']=double(NULL) // 主力期货合约最新价
    context['strike_price']=double(NULL)                      // 平值期权合约的行权价
    context["maturity"] = double(NULL)                        // 到期时间：距到期日的日期/365
    context["managedOrderIds"] = dict(LONG, BOOL)             // 订单管理字典，记录已处理的订单id，防止重复撤单追单
}

// ------------------------------
// beforeTrading：盘前回调函数，每日盘前触发一次。
// 负责：
//      1、初始化移仓换月和寻找平值期权相关变量；
//      2、移仓换月，标记当前主力合约标的；
//      3、计算到期期限，用于后续折现因子计算。
// ------------------------------
def beforeTrading(mutable context){
    // 每天初始化一次
    context['options'] = dict(STRING, ANY)                    // 主力期权标的与最新价映射
    context["deltas"] = dict(STRING, DOUBLE)                  // 主力期权delta值映射
    context['ATM_C']=""                                       // 平值看涨期权合约名
    context['ATM_P']=""                                       // 平值看跌期权合约名
    // 获取当前日期
    today = context['tradeTime'].date()
    print('当前时间'+context['tradeTime'])

    // ---------------------- 移仓换月 ---------------------
    // 根据当前日期获取当前到期日
    context['expiryDate'] = exec expiryDate[0] from context['expiryMap'] where month(expiryDate) =month(today)
    // 标记当前主力合约标的
    if(today<transFreq(weekBegin(context['expiryDate']) - 5, "CCFX")){ // 到期日前一周周三前，用当月合约，否则移仓换月
        context["maturity"] = temporalDiff(context['expiryDate'],today,"d")\365 
        context['front_month_contract'] = exec underlying[0] from context['expiryMap'] where expiryDate = context['expiryDate'] 
        mainOption = exec symbol[0] from context['expiryMap'] where underlying = context['front_month_contract']
        mainOptions = exec symbol from context['basicInfo'] where startsWith(symbol,mainOption)
        print("当前主力期权："+mainOption)
    }
    else{ // 否则走移仓换月,用下月合约
        context['expiryDate'] = exec expiryDate[0] from context['expiryMap'] where month(expiryDate) = month(context['expiryDate'])+1;
        context["maturity"] = temporalDiff(context['expiryDate'],today,"d")\365 
        context['front_month_contract'] = exec underlying[0] from context['expiryMap'] where expiryDate = context['expiryDate'] 
        mainOption = exec symbol[0] from context['expiryMap'] where underlying = context['front_month_contract']
        mainOptions = exec symbol from context['basicInfo'] where startsWith(symbol,mainOption)
        print("当前主力期权："+mainOption)
    }
    // 构造主力期权最新价字典
    for(option in mainOptions){ 
        px = dict(STRING, ANY)
        px["bidPrice"] = double(NULL)
        px["askPrice"] = double(NULL)
        context["options"][option] = px  
    }   
    // 构造主力期权delta字典
    for(option in mainOptions){
        context["deltas"][option] = double(NULL)
    }
}

// ------------------------------
// onSnapshot：快照行情回调函数，每条行情到来时触发一次。
// 负责：
//      1、找到当前行情下的平值期权
//      2、根据行情计算开平仓信号，根据信号下单
//      3、计算账户delta值并用期货头寸对冲
//      4、对未成交超过5s的期权单子撤单及追单
// ------------------------------
def onSnapshot(mutable context, msg, indicator){
    symbol = string(msg["symbol"])
    //判断标的是否为期货，期货端处理
    if (substr(symbol,0,2)=="IF"){
        if (symbol == context['front_month_contract']){
            context["front_month_contract_latest_price"]=msg.lastPrice
            // ---------------------更新平值期权---------------------
            // 逻辑为寻找离主力期货价格最近的行权价
            // 需要先找到主力期货对应的各个期权合约
            contracts=context['options'].keys()
            optType = regexFindStr(contracts, "[0-9]+([CP])[0-9]+$", true,0).substr(4,1)
            prices=int(contracts.substr(7))
            diff=abs(prices-msg.lastPrice)
            minDiff = min(diff)
            context["ATM_C"]=contracts[diff == minDiff and optType == "C"][0]
            context["ATM_P"]=contracts[diff == minDiff and optType == "P"][0]
            context['strike_price'] = int(context['ATM_C'].substr(7))
            //--------------------- 期货delta hedge ----------------------
            // 获取持仓
            optPos = Backtest::getPosition(context["engine"], ,"options")
            futPos = Backtest::getPosition(context['engine'], ,'futures')
            // 无期权持仓，无需hedge，直接return
            if(size(optPos)==0){
                return 
            }
            // 计算 delta值
            optDelta = exec sum((longPosition - shortPosition) * context["deltas"][symbol]) from optPos
            futDelta = exec sum((longPosition - shortPosition))[0] from futPos
            // 无期货持仓，更新futDelta为0，防止空值计算
            if(size(futPos)==0){
                futDelta = 0
            }
            totalDelta = optDelta + futDelta
            if(totalDelta == NULL){
                return
            }
            // hedge
            // 如果有未成交期货单子，return
            opens=Backtest::getOpenOrders(context["engine"], , , , accountType="futures")
            // 计算期货手数
            futMultiplier = exec multiplier[0] from context['basicInfo'] where substr(symbol,0,2)=="IF"
            optMultiplier = exec multiplier[0] from context['basicInfo'] where substr(symbol,0,2)=="IO"
            futQty = abs(int(totalDelta*optMultiplier\futMultiplier))
            // 超过Delta_Limit,下单,对手方更优价
            if(totalDelta> context['Delta_Limit'] and futQty>0){// 做空期货
                // 为避免重复下单，将futQty减去openOrders中的现有单子。
                // 计算已下单未成交头寸
                if(opens.size()>0 && opens[0]["direction"] == 2){  
                    current_open_order=opens[0]["openQty"]
                } else {current_open_order=0}
                if (futQty - current_open_order <= 0){
                    return
                }
                orderMsg = (context['front_month_contract'],    // 标的代码
                            msg["symbolSource"],                // 交易所代码
                            context['tradeTime'],               // 下单时间
                            5,                                  // 订单类型，5为限价单
                            msg["bidPrice"][0],                 // 委托订单价格
                            0.,                                 // 止损价,不需要可填0
                            0.,                                 // 止盈价
                            futQty - current_open_order,        // 委托订单数量
                            2,                                  // 买卖方向，1：买开，2：卖开，3：卖平，4：买平
                            0,                                  // 滑点
                            0,                                  // 委托订单有效性
                             )                                  // 委托订单到期时间
                Backtest::submitOrder(context['engine'], orderMsg, 'hedgeShortFut', 0, 'futures')
            } else if(totalDelta< -context['Delta_Limit'] and futQty>0) {// 做多期货
                // 计算已下单未成交头寸
                if(opens.size()>0 && opens[0]["direction"] == 1){
                    current_open_order=opens[0]["openQty"]
                } else {current_open_order=0}
                if (futQty - current_open_order <= 0){
                    return
                }
                orderMsg = (context['front_month_contract'], 
                            msg["symbolSource"], 
                            context['tradeTime'], 
                            5, 
                            msg["offerPrice"][0],
                            0., 
                            0., 
                            futQty - current_open_order,
                            1, 
                            0, 
                            0,
                             )
                Backtest::submitOrder(context['engine'], orderMsg, 'hedgeLongFut', 0, 'futures')
            }
        }
        else{
            return
        } 
    }
    //判断合约是否为期权，期权端处理
    else if (substr(symbol,0,2)=="IO"){
        // 判断合约是否为主力期权    
        if (!(symbol in context['options'].keys())){
            return 
        }
        //更新主力期权的最新买一卖一价格
        context['options'][symbol]['bidPrice']=msg.bidPrice[0]
        context['options'][symbol]['askPrice']=msg.offerPrice[0]
        //更新主力期权delta值
        context['deltas'][symbol]=msg.Delta

        //判断是否已知平值期权（如果还没接到第一条期货行情，则ATM_C和ATM_P为空，未知则return
        if (context['ATM_C'] == NULL or context['ATM_P'] == NULL or context['strike_price'] == NULL or context['front_month_contract_latest_price'] == NULL){
            return
        }
        //判断是否为平值期权
        if (symbol != context['ATM_C'] and symbol != context['ATM_P']){
            return 
        }
        // ---------------------- 期权撤单及追单 ----------------------
        // 如果有未成交的订单，判断是否超过5s，执行撤单或追单
        opens=Backtest::getOpenOrders(context["engine"], , , , accountType="option")
        if(opens.size()>0){
            order_match = dict(STRING, ANY)  // 记录所有超过5秒准备撤单的合约{期货id+'P'/'C'+行权价:[对应orders]}。能组队的直接撤；不能组队的撤单后用对手价重发订单
            for(order in opens){
                if (temporalDiff(context["tradeTime"], order["timestamp"]) > 5000){ // 超过5秒未成交，则撤单或追单
                    print("orderId: " + order["orderId"])
                    if (order["orderId"] in context["managedOrderIds"]){ // 如果已处理，不再重复处理该订单
                        print("Skip managed orderId: " + order["orderId"])
                        continue
                    }
                    order_fut = string(order["symbol"].substr(2,4))
                    order_pc = string(order["symbol"].substr(6,1))
                    order_strike = string(order["symbol"].substr(7))
                    order_key = order_fut + order_pc + order_strike
                    order_key_reversed = order_fut + iif(order_pc == "C", "P", "C") + order_strike
                    if(not order_key_reversed in order_match){
                        if (not order_key in order_match){
                            order_match[order_key] = [order]
                        } else {
                            order_match[order_key].append!(order)
                        }
                        print("Added.")
                        print(keys(order_match))
                    } else {
                        matched_order = order_match[order_key_reversed].pop!()
                        print("Matched.")

                        Backtest::cancelOrder(context["engine"], order["symbol"], [order["orderId"]], , "option")
                        context["managedOrderIds"][order["orderId"]] = true // 标记已处理的订单id
                        print("Cancelled order: " + order["symbol"] + "_" + order["label"])

                        Backtest::cancelOrder(context["engine"], matched_order["symbol"], [matched_order["orderId"]], , "option")
                        context["managedOrderIds"][matched_order["orderId"]] = true
                        print("Cancelled order: " + matched_order["symbol"] + "_" + matched_order["label"])
                        if (order_match[order_key_reversed].size() == 0){
                            order_match.erase!(order_key_reversed)
                        }
                    }
                }
            }
            // 此时，order_match中剩下的就是超过5秒且没有匹配到的订单，即另一条腿已经成交，需要撤单后用对手价重发
            for(left_key in keys(order_match)){
                original_orders = order_match[left_key]
                print("Entered for loop.")

                for (original_order in original_orders) {
                    if (original_order["orderId"] in context["managedOrderIds"]){ // 如果已处理，不再重复处理该订单
                        print("Skip managed orderId: " + original_order["orderId"])
                        continue
                    }
                    opponent_price = iif(
                        original_order["direction"] == 1 or original_order["direction"] == 3,
                        context['options'][original_order["symbol"]]['askPrice'],
                        context['options'][original_order["symbol"]]['bidPrice']
                    )

                    if (opponent_price == NULL){
                        print("Opponent price null.")
                        continue
                    }

                    Backtest::cancelOrder(context["engine"], original_order["symbol"], [original_order["orderId"]], , "option")
                    print("Cancelled order: " + original_order["symbol"] + "_" + original_order["label"] + "_" + original_order["orderId"])
                    context["managedOrderIds"][original_order["orderId"]] = true
                    Backtest::submitOrder(
                        context['engine'],
                        (original_order["symbol"], msg["symbolSource"], context['tradeTime'], 5,
                        opponent_price,
                        , , context['orderQty'], original_order["direction"], ,,),
                        "resubmit_" + original_order["label"] + original_order["orderId"],
                        0,
                        'option'
                    )
                    print('Resubmitted time :' + context['tradeTime'] + " orderID:" + original_order["orderId"])
                    print("Resubmitted order: " + original_order["symbol"] + "_" + original_order["label"] + " with opponent price: " + string(opponent_price))
                }
            }
            return
        }
        // ---------------------- 期权套利，开平仓 ---------------------- 
        // 相关变量：期货最新价、期权行权价、折现因子、套利阈值
        f_last = context['front_month_contract_latest_price']
        strike = context['strike_price']
        discount_factor = pow(exp(1),-context["rf"]*context["maturity"]) 
        threshold = context['threshold']
        // // 仓位控制
        callPos = Backtest::getPosition(context["engine"], context['ATM_C'],"options")
        callLongPos = callPos.longPosition
        callShortPos = callPos.shortPosition
        putPos = Backtest::getPosition(context['engine'], context['ATM_P'],"options")
        putLongPos = putPos.longPosition
        putShortPos = putPos.shortPosition

        //判断是否开仓：己方更优价
        //1. 判断是否做多头：(C_bid - P_ask + K * DF) < Lastprice（F）-threshold 
        //  corner case：判断 ATM_C买一 或 ATM_P卖一 是否为空
        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')
            }
        }
        // 判断是否平仓:
        // 多头平仓
        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\2)) ){
                // 下单(标的代码, 交易所代码, 时间, 订单类型, 委托订单价格, 止损价/止盈价，委托订单数量，买卖方向，委托订单有效性)
                print(context['tradeTime']+"：平仓多头") 
                print("卖出："+context['ATM_C'])
                print("买入："+context['ATM_P'])
                Backtest::submitOrder(context["engine"], 
                    (context['ATM_C'],msg["symbolSource"],context['tradeTime'],5,c_bid, , ,context['orderQty'], 3,,,),"buyCloseCall", 0, 'option')
                Backtest::submitOrder(context["engine"], 
                    (context['ATM_P'],msg["symbolSource"],context['tradeTime'],5,p_ask, , ,context['orderQty'], 4,,,),"buyClosePut", 0, 'option')
            }
        }
        // 空头平仓
        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\2) ){
                // 下单
                print(context['tradeTime']+"：平仓空头") 
                print("买入："+context['ATM_C'])
                print("卖出："+context['ATM_P'])
                Backtest::submitOrder(context["engine"], 
                    (context['ATM_C'],msg["symbolSource"],context['tradeTime'],5,c_ask, , ,context['orderQty'], 4,,,),"sellCloseCall", 0, 'option')
                Backtest::submitOrder(context["engine"], 
                    (context['ATM_P'],msg["symbolSource"],context['tradeTime'],5,p_bid, , ,context['orderQty'], 3,,,),"sellClosePut", 0, 'option')
            }
        }
    }
    else{
        return 
    }
}


def onOrder(mutable context, orders){
}

def onTrade(mutable context, trades){
}

def afterTrading(mutable context){
}

def finalized(mutable context){
}

// =========================== 基本信息表 ===========================
// 期权基本表
// 从 io 表取
optCodes = exec distinct symbol from loadTable("dfs://fut_opt_snapshot", "io") 
optSecurityRef = select symbol,
                take(3,size(optCodes)) as assetType,                         // 股指期权
                last(underlying) as underlyingCode,                          // 标的资产代码
                last(iif(type == "CALL", 1, 2)) as optType,
                last(strike_price) as strikePrice,
                take(100.0,size(optCodes)) as multiplier,                    // 股指期权合约乘数
                take(0.12,size(optCodes)) as marginRatio,                    // 参考值0.05-0.12
                take(1., size(optCodes)) as tradeUnit,                       // 合约单位，股指期权为1
                take(1.,size(optCodes)) as priceUnit,                        // 报价单位，指数点
                take(0.2, size(optCodes)) as priceTick,                      // 最小变动价位，指数点
                take(15.,size(optCodes)) as commission,                      // 每手合约手续费
                take(1,size(optCodes)) as deliveryCommissionMode,            // 交割手续费模式：1=每手固定金额，2=按成交金额比例
                last(date(expiry_datetime)) as lastTradingDay,
                last(date(expiry_datetime)) as exerciseDate,
                last(date(expiry_datetime)) as exerciseSettlementDate
            from loadTable("dfs://fut_opt_snapshot", "io") 
            group by symbol

// 期货、期权合约到期日映射
expiryMap = select distinct
            substr(string(symbol), 0, 6) as symbol,
            date(expiry_datetime) as expiryDate,
            underlying
        from loadTable("dfs://fut_opt_snapshot", "io")

// 期货基本表
futCodes = select distinct symbol from loadTable("dfs://fut_opt_snapshot", "if")
futSecurityRef = select symbol,
                take(1,size(futCodes)) as assetType,                         // 期货
                symbol as underlyingCode,                                    // 无效字段，仅用于对齐基本表
                take(2,size(futCodes)) as optType,                           // 无效字段，仅用于对齐基本表
                take(2.,size(futCodes)) as strikePrice,                      // 无效字段，仅用于对齐基本表
                take(300.0,size(futCodes)) as multiplier,                    // 股指期货合约乘数
                take(0.12,size(futCodes)) as marginRatio,                    // 参考值0.08-0.15
                take(1.,size(futCodes)) as tradeUnit,                        // 合约单位
                take(1.,size(futCodes)) as priceUnit,                        // 报价单位，指数点
                take(0.2,size(futCodes)) as priceTick,                       // 最小变动价位，指数点
                take(0.000023,size(futCodes))  as commission,                // 手续费
                take(2, size(futCodes)) as deliveryCommissionMode,           // 交割手续费模式：1=每手固定金额，2=按成交金额比例
                expiryDate as lastTradingDay,
                expiryDate as exerciseDate,
                expiryDate as exerciseSettlementDate
            from lj(futCodes,expiryMap,`symbol,`underlying)

securityReference = unionAll(futSecurityRef, optSecurityRef)


// ====================== 策略参数与引擎配置 ======================
startDate = 2026.01.01
endDate = 2026.02.28

userConfig = dict(STRING, ANY)
userConfig["startDate"] = startDate
userConfig["endDate"] = endDate
userConfig["strategyGroup"] = "multiAsset"
userConfig["frequency"] = 0
cashDict = dict(STRING, DOUBLE)
cashDict["futures, options"] = 100000000.0
userConfig["cash"] = cashDict
userConfig["dataType"] = 1        // snapshot 模式，触发 onSnapshot；若需 onBar 则设 frequency>0
userConfig["latency"] = 0
userConfig["commission"] = 0.0
userConfig["tax"] = 0.0
userConfig["msgAsTable"] = false
userConfig["multiAssetQuoteUnifiedInput"] = false
userConfig["depth"] = 5
userConfig["matchingMode"] = 1
userConfig["isBacktestMode"] = false
userConfig['outputOrderInfo'] = true // 输出风控日志

// 用户自定义参数，传入 context
userParam = dict(STRING, ANY)
userParam['basicInfo'] = securityReference  // 基本信息表
userParam['expiryMap'] = expiryMap          // 到期日映射表
userParam['threshold'] = 0.5                // 套利阈值
userParam["rf"] = 0.02                      // 无风险利率
userParam['orderQty'] = 1                   // 开平仓下单手数
userParam['maxQty'] = 3                     // 期权开平仓最大下单手数
userParam['Delta_Limit'] = 1.0              // delta风险上限
userConfig["context"] = userParam

// 回调函数注册
callbacks = dict(STRING, ANY)
callbacks["initialize"] = initialize
callbacks["beforeTrading"] = beforeTrading
callbacks["onSnapshot"] = onSnapshot
callbacks["onOrder"] = onOrder
callbacks["onTrade"] = onTrade
callbacks["afterTrading"] = afterTrading
callbacks["finalize"] = finalized

strategyName = "futOptArbitrage"

// =========================== 创建回测引擎 ===========================
try{Backtest::dropBacktestEngine(strategyName)}catch(ex){print ex}
engine = Backtest::createBacktester(strategyName, userConfig, callbacks, false, securityReference)
go
Backtest::triggerDailySettlement(engine)

//============================插入行情执行回测=========================
def appendData(startDate,endDate){
    engine=Backtest::getBacktestEngineList()["futOptArbitrage"]
    // 按天分批加载行情数据并推入引擎 
    dates = exec distinct date(datetime) from loadTable("dfs://fut_opt_snapshot", "if") 
            where date(datetime) between startDate:endDate order by date(datetime)

    for(aDate in dates){
        dayStart = timestamp(aDate)
        dayEnd = timestamp(aDate + 1)

        // --- 当日 IF 期货 tick ---
        ifTicks = select
            string(symbol) as symbol,
            "CCFX" as symbolSource,
            date(datetime) as tradingDay,
            datetime as timestamp,
            last_price as lastPrice,
            limit_up as upLimitPrice,
            limit_down as downLimitPrice,
            long(volume) as totalBidQty,                            // 区间买量
            long(volume) as totalOfferQty,                          // 区间卖量
            fixedLengthArrayVector([bid_price_1]) as bidPrice,
            fixedLengthArrayVector([long(bid_volume_1)]) as bidQty,
            fixedLengthArrayVector([ask_price_1]) as offerPrice,
            fixedLengthArrayVector([long(ask_volume_1)]) as offerQty,
            last_price as highPrice,
            last_price as lowPrice
            from loadTable("dfs://fut_opt_snapshot", "if")
            where datetime >= dayStart and datetime < dayEnd
            order by datetime

        // --- 当日 IO 期权 tick ---
        ioTicks = select
            string(symbol) as symbol,
            "CCFX" as symbolSource,
            date(datetime) as tradingDay,
            datetime as timestamp,
            last_price as lastPrice,
            ask_price_1 * 1.10 as upLimitPrice,
            bid_price_1 * 0.90 as downLimitPrice,
            long(volume) as totalBidQty,
            long(volume) as totalOfferQty,
            fixedLengthArrayVector([bid_price_1]) as bidPrice,
            fixedLengthArrayVector([long(bid_volume_1)]) as bidQty,
            fixedLengthArrayVector([ask_price_1]) as offerPrice,
            fixedLengthArrayVector([long(ask_volume_1)]) as offerQty,
            last_price as highPrice,
            last_price as lowPrice,
            underlying_price as underlyingPrice,
            double(theta) as Theta,
            double(vega) as Vega,
            double(gamma) as Gamma,
            double(delta) as Delta,
            double(0.0) as IV
            from loadTable("dfs://fut_opt_snapshot", "io")
            where datetime >= dayStart and datetime < dayEnd
            order by datetime

        // 推入引擎
        if(ifTicks.size() > 0 or ioTicks.size() > 0){
            dictMsg = dict(STRING, ANY)
            dictMsg["futures"] = ifTicks
            dictMsg["options"] = ioTicks
            Backtest::appendQuotationMsg(engine, dictMsg)
        }
    }
    print("行情数据全部推送完毕")
}

// 提交后台任务，开始插入行情执行回测
submitJob("backtest_append_data","append data to backtest",appendData{startDate,endDate})

// =========================== 获取作业结果 ==========================
// getRecentJobs() // 获取批处理作业状态
// getHomeDir() // 查询本地节点的主目录
// getJobMessage("backtest_append_data") // 获取批处理任务的中间信息，或在<HomeDir>/batchJobs目录查看
// Backtest::getBacktestEngineStat(engine) // 查询引擎状态

// =========================== 获取回测结果 ===========================
engine=Backtest::getBacktestEngineList()["futOptArbitrage"]
futuresTradeDetails = Backtest::getTradeDetails(engine, "futures")
optionsTradeDetails = Backtest::getTradeDetails(engine, "options")
TotalPortfolios = Backtest::getDailyTotalPortfolios(engine)
Dailyposition = Backtest::getDailyPosition(engine)
ReturnSummary = Backtest::getReturnSummary(engine)