// ===================================================================== // 固定利率互换合约定价 DEMO // ===================================================================== // 功能说明: // 本脚本演示 DolphinDB 中固定利率互换(IRS)的流式定价流程:通过模拟回放实时行情, // 驱动市场数据引擎重建人民币 FR_007 利率互换曲线,并联动定价引擎对固定利率互换合约估值。 // // 核心链路: // 模拟行情 marketQuotes // -> 市场数据引擎 FICC_MKT_ENGINE(构建/更新 CNY_FR_007 曲线) // -> 定价引擎 FICC_PRICING_ENGINE(计算 IRS NPV) // -> 结果写入 curveResult / pricingResult // // 主要步骤: // 1. 设置定价基准日 referenceDate,并清理旧任务、订阅、流表和引擎,支持重复运行。 // 2. 创建行情流表 marketQuotes、曲线结果流表 curveResult、定价结果流表 pricingResult。 // 3. 定义固定利率互换合约模板,创建定价引擎,并注册定价结果输出回调 pricingHandler。 // 4. 配置 CNY_FR_007 单货币利率互换曲线,创建市场数据引擎,并注册曲线结果输出回调 mktDataHandler。 // 5. 通过 appendMktData 预注入 FR_007 历史定盘利率,用于计算定价日后首笔浮动现金流。 // 6. 订阅 marketQuotes,行情到达后批量追加到市场数据引擎,实现“行情 -> 曲线 -> 定价”的实时级联。 // 7. 使用 mockQuotes 模拟逐笔行情,并通过 submitJob("replayQuotesJob", ...) 回放行情,模拟实时定价。 // // 运行结果: // - curveResult:记录每次行情触发的曲线构建结果及系统时间; // - pricingResult:记录 IRS 合约的估值日期、NPV、事件时间、系统时间和延迟起始时间。 // // 参数说明: // - nTicks:行情回放轮数; // - intervalMs:每笔模拟行情之间的间隔(毫秒); // - 每轮包含 7d、1M、3M、6M、9M、1y、2y、3y、4y、5y、7y、10y 共 12 个期限点。 // ===================================================================== // 函数:模拟方式构建固定利率对浮动利率 IRS 合约,用于定价引擎计算测试 // nSize : 工具数量 def buildIrSwapInstruments(nSize) { idx = 0..(nSize - 1) instTb = table( take("Swap", nSize) as productType, take("IrSwap", nSize) as swapType, take("IrFixedFloatingSwap", nSize) as irSwapType, "FR_007_" + format(2028.06.16, "yyyyMM") + "_" + lpad(string(idx), 8, "0") as instrumentId, take(2025.06.16, nSize) as start, take(2028.06.16, nSize) as maturity, take("Quarterly", nSize) as frequency, take(0.018, nSize) as fixedRate, take("CFET", nSize) as calendar, take("Actual365", nSize) as fixedDayCountConvention, take("Actual365", nSize) as floatingDayCountConvention, take("Pay", nSize) as payReceive, take("FR_007", nSize) as iborIndex, take(0.0001, nSize) as spread, take("CNY", nSize) as notionalCurrency, take(1E8, nSize) as notionalAmount ) return parseInstrument(instTb) } // ---- 0. 清理旧任务、订阅、流表和引擎,支持重复运行 ---- try { cancelJob("replayQuotesJob") } catch(ex) {} try { dropStreamEngine("FICC_MKT_ENGINE") } catch(ex) {} try { dropStreamEngine("FICC_PRICING_ENGINE") } catch(ex) {} try { dropStreamTable("pricingResult") } catch(ex) {} try { dropStreamTable("curveResult") } catch(ex) {} try { unsubscribeTable(tableName="marketQuotes", actionName="AppendQuotesToMktDataEngine") } catch(ex) {} try { dropStreamTable("marketQuotes") } catch(ex) {} go; // ---- 1. 创建需要的流表 ---- // a.IRS行情流表(用于接收模拟IRS行情数据,供MktDataEngine订阅) share streamTable(100000:0, ["timestamp","type","name","term","price"], [TIMESTAMP,STRING,STRING,STRING,DOUBLE]) as marketQuotes // b.定价结果流表,用于接收定价引擎输出的定价结果 share streamTable(1:0, `name`date`npv`eventTime`sysTime`delayStart, [STRING, DATE, DOUBLE, NANOTIMESTAMP, NANOTIMESTAMP, NANOTIMESTAMP]) as pricingResult // c.曲线和曲面构建结果流表,用于接收市场数据引擎输出的曲线和曲面构建结果 share streamTable(1:0, `eventTime`kind`date`name`data`sysTime, [NANOTIMESTAMP, STRING, DATE, STRING, MKTDATA, NANOTIMESTAMP]) as curveResult // 定价日期,作为 IRS 曲线构建、历史定盘和合约估值的基准日。 referenceDate = 2025.08.18 // ---- 2. 构建市场数据引擎 + 定价引擎 ---- // 定价引擎自定义函数 def pricingHandler(eventTime, name, date, npv, delayStart) { tableInsert(pricingResult, name, date, npv, eventTime, now(true), delayStart) } // 市场数据引擎自定义函数 def mktDataHandler(mutable pricEngine, eventTime, kind, date, name, data) { appendMktData(pricEngine, data, eventTime); tableInsert(curveResult, eventTime, kind, date, name, data, now(true)) } // 定价引擎运行配置参数 priceEngCfg = { "numThreads": 8, "useSystemTime": true, "outputTime": true, "outputDelayStart": true } // 创建待定价利率互换金融工具(固定利率互换) nPricing = 10 // 模拟目标金融工具数量 irSwapInst = buildIrSwapInstruments(nPricing) // 创建定价引擎 https://docs.dolphindb.cn/zh/funcs/c/createpricingengine.html pricingEngine = createPricingEngine("FICC_PRICING_ENGINE", irSwapInst, pricingHandler, priceEngCfg) // 单货币利率互换收益率曲线构建(当前Demo中,构建一条以人民币计价、参考 FR007 浮动利率的利率互换曲线) curveCfg = { 'name': "CNY_FR_007", 'type': "IrSingleCurrencyCurve", 'currency': "CNY", 'insts': [ ("FR_007", "Deposit", "7d"), ("CNY_FR_007", "IrVanillaSwap", "1M"), ("CNY_FR_007", "IrVanillaSwap", "3M"), ("CNY_FR_007", "IrVanillaSwap", "6M"), ("CNY_FR_007", "IrVanillaSwap", "9M"), ("CNY_FR_007", "IrVanillaSwap", "1y"), ("CNY_FR_007", "IrVanillaSwap", "2y"), ("CNY_FR_007", "IrVanillaSwap", "3y"), ("CNY_FR_007", "IrVanillaSwap", "4y"), ("CNY_FR_007", "IrVanillaSwap", "5y"), ("CNY_FR_007", "IrVanillaSwap", "7y"), ("CNY_FR_007", "IrVanillaSwap", "10y") ], 'dayCountConvention': "Actual365" } // 市场数据引擎运行配置参数 mktEngCfg = { "numThreads": 1, "useSystemTime" : true, // 是否使用系统时间作为事件时间 // "timeColumn": "timestamp", "outputTime" : true // 是否输出 eventTime 列表示触发的事件时间 } // 创建市场数据引擎(模式一、引擎级联模式,级联定价引擎pricingEngine) mktEngine = createMktDataEngine("FICC_MKT_ENGINE", referenceDate, [curveCfg], pricingEngine, NULL, mktEngCfg) // 创建市场数据引擎(模式二、非级联模式,通过 mktDataHandler appendMktData 市场数据到定价引擎pricingEngine) // mktEngine = createMktDataEngine("FICC_MKT_ENGINE", referenceDate, [curveCfg], mktDataHandler{ pricingEngine }, NULL, mktEngCfg) // ---- 3. 为了计算定价日后面的第一笔现金流用到的浮动利率,需要传入参考利率的历史数据, 并通过 appendMktData 预先传入定价引擎 pricingEngine ---- // 因为 FR_007 利率互换的付息频率为季度,建议传入的 FR_007 的历史定盘数据不少于 70 个交易日 fr007HistCurve = { "mktDataType": "Curve", "curveType": "AssetPriceCurve", "curveName": "PRICE_FR_007", "referenceDate": referenceDate, "currency": "CNY", "dates":[2025.05.09, 2025.05.12, 2025.05.13, 2025.05.14, 2025.05.15, 2025.05.16, 2025.05.19, 2025.05.20, 2025.05.21, 2025.05.22, 2025.05.23, 2025.05.26, 2025.05.27, 2025.05.28, 2025.05.29, 2025.05.30, 2025.06.03, 2025.06.04, 2025.06.05, 2025.06.06, 2025.06.09, 2025.06.10, 2025.06.11, 2025.06.12, 2025.06.13, 2025.06.16, 2025.06.17, 2025.06.18, 2025.06.19, 2025.06.20, 2025.06.23, 2025.06.24, 2025.06.25, 2025.06.26, 2025.06.27, 2025.06.30, 2025.07.01, 2025.07.02, 2025.07.03, 2025.07.04, 2025.07.07, 2025.07.08, 2025.07.09, 2025.07.10, 2025.07.11, 2025.07.14, 2025.07.15, 2025.07.16, 2025.07.17, 2025.07.18, 2025.07.21, 2025.07.22, 2025.07.23, 2025.07.24, 2025.07.25, 2025.07.28, 2025.07.29, 2025.07.30, 2025.07.31, 2025.08.01, 2025.08.04, 2025.08.05, 2025.08.06, 2025.08.07, 2025.08.08, 2025.08.11, 2025.08.12, 2025.08.13, 2025.08.14, 2025.08.15 ], "values":[1.6000, 1.5600, 1.5300, 1.5500, 1.5500, 1.6300, 1.6500, 1.6000, 1.5900, 1.5800, 1.6300, 1.7000, 1.7000, 1.7000, 1.7500, 1.7500, 1.5900, 1.5800, 1.5700, 1.5600, 1.5500, 1.5500, 1.5600, 1.5900, 1.5900, 1.5700, 1.5500, 1.5600, 1.5679, 1.6000, 1.5700, 1.8500, 1.8300, 1.8400, 1.8500, 1.9500, 1.6036, 1.5800, 1.5200, 1.5000, 1.5000, 1.5100, 1.5100, 1.5300, 1.5200, 1.5500, 1.6000, 1.5400, 1.5400, 1.5000, 1.5000, 1.4800, 1.5000, 1.6000, 1.7500, 1.6400, 1.6200, 1.6300, 1.6000, 1.5000, 1.4800, 1.4700, 1.4800, 1.4900, 1.4600, 1.4600, 1.4600, 1.4800, 1.4800, 1.4900 ] \ 100 } appendMktData(pricingEngine, parseMktData(fr007HistCurve)) // 订阅行情流表,通过行情数据驱动市场数据引擎构建曲线和曲面、驱动定价引擎定价 subscribeTable(tableName="marketQuotes", actionName="AppendQuotesToMktDataEngine", offset=-1, handler=append!{mktEngine}, msgAsTable=true, batchSize=1) // ---- 4. 模拟播放实时行情到行情流表 marketQuotes, 按时间间隔推送行情,模拟实时构建曲线和定价---- // 自定义模拟行情函数, 参数 nTicks 模拟行情数量, 参数 intervalMs 模拟逐笔行情流间隔(毫秒) def mockQuotes(nTicks, intervalMs) { streamQuoteTb = objByName("marketQuotes") terms = [ 7d, 1M, 3M, 6M, 9M, 1y, 2y, 3y, 4y, 5y, 7y, 10y ] instTypes = ["Deposit", "IrVanillaSwap", "IrVanillaSwap", "IrVanillaSwap", "IrVanillaSwap", "IrVanillaSwap", "IrVanillaSwap", "IrVanillaSwap", "IrVanillaSwap", "IrVanillaSwap", "IrVanillaSwap", "IrVanillaSwap"] instNames = ["FR_007", "CNY_FR_007", "CNY_FR_007", "CNY_FR_007", "CNY_FR_007", "CNY_FR_007", "CNY_FR_007", "CNY_FR_007", "CNY_FR_007", "CNY_FR_007", "CNY_FR_007", "CNY_FR_007"] quotes007 = [2.3500, 2.3396, 2.3125, 2.3613, 2.4075, 2.4513, 2.5750, 2.6763, 2.7650, 2.8463, 2.9841, 3.1350] \ 100 for (i in 0..(nTicks - 1)) { quotes = quotes007 + (i % 10) * 0.00001 // 每个价格微调,模拟行情变化 for (j in 0..(instTypes.size() - 1)) { tableInsert(streamQuoteTb, now(true), instTypes[j], instNames[j], terms[j], quotes[j]) sleep(intervalMs) } } } // 启动行情播放任务,驱动市场数据构建和IRS定价。 nTicks = 100 // 模拟行情数量 intervalMs = 10 // 模拟逐笔行情流间隔(毫秒) submitJob("replayQuotesJob", "replayQuotesJob", mockQuotes, nTicks, intervalMs)