FICC 定价函数

1. 前言

金融产品的定价是金融工程的语言和核心。​准确的定价是市场有效运行、风险有效管理和产品持续创新的基石​​。理解定价的原理、重要性及其挑战,对于洞察金融市场、做出明智决策以及推动金融领域的健康发展都至关重要。

在无套利原则下,所有金融产品的定价公式为

其中:

  • npv 为净现值 (Net Present Value)

  • cfi 为第 i 笔现金流 (Cash Flow),E 为风险中性测度下的期望函数

  • dfi 为第 i 期的折现因子 (Discount Factor)

2. 基础定价接口

DolphinDB 提供基础定价函数统一接口设计,形式如下:

xxxPricer(instrument, pricingDate, mktData, [setting], [model], [method])

其中 xxxPricer 为占位符,实际函数名根据金融产品类型不同而变化,例如 bondPricer、irFixedFloatingSwapPricer、fxEuropeanOptionPricer 等。

参数:

  • instrument:INSTRUMENT 类型,表示需要定价的金融产品,各种金融产品的定义可参考 parseInstrument

  • pricingDate:DATE类型,表示定价日期

  • mktData:市场数据,根据不同产品的定价需要填入 Spot/Curve/Surface 等信息

  • setting:可选参数,字典类型,主要是风险计量设定

  • model:可选参数,字典类型,表示定价模型及模型的参数设定(主要针对期权产品)

  • method: 可选参数,字典类型,表示定价方法(Analytic/PDE/MonteCarlo)及方法的参数设定(主要针对期权产品)

返回值: NPV, CashFlow (可选), Greeks (可选)

DolphinDB V3.00.4 提供的基础定价函数汇总如下:

资产类别 函数名 描述
债券 bondPricer 债券定价(贴现债/零息债/固定利率债)
债券 bondFuturesPricer 国债期货定价
利率 irDepositPricer 存款定价
利率 irFixedFloatingSwapPricer 标准利率互换定价
外汇 fxForwardPricer 外汇远期定价
外汇 fxSwapPricer 外汇掉期定价
外汇 fxEuropeanOptionPricer 外汇欧式期权定价

2.1 债券定价

债券是 FICC 业务中最大的交易品种。债券定价(bondPricer)采用现金流折现模型,目前仅支持贴现债/零息债/固定利率债的定价。债券定价需要根据债券的类型,选择相应的折现曲线。这些曲线既可以用 bondYieldCurveBuilder 函数自己构建,也可以直接使用第三方即期曲线。下面分别给出一个例子。

例 1: 贴现债定价 ( 259916.IB, 259916_25贴现国债16_2025年记账式贴现(十六期)国债_中国货币网 )

//step1: 生成instrument
bond = {
    "productType": "Cash",
    "assetType": "Bond",
    "bondType": "DiscountBond",
    "instrumentId": "259916.IB",
    "start": 2025.03.13,
    "maturity": 2025.09.11,
    "issuePrice": 99.207,
    "dayCountConvention": "ActualActualISDA"
}
instrument = parseInstrument(bond)
//step2: 准备定价需要用到的曲线
/*
 因为该债券为国债,所以discoutCurve选取国债收益率曲线(即期)
 数据来源https://www.chinamoney.com.cn/chinese/bkcurvclosedyhis/index.html
*/ 
curve =  {
    "mktDataType": "Curve",
    "curveType": "IrYieldCurve",
    "referenceDate": 2025.08.18,
    "currency": "CNY",
    "curveName": "CNY_TREASURY_BOND",
    "dayCountConvention": "ActualActualISDA",
    "compounding": "Compounded",
    "interpMethod": "Linear",
    "extrapMethod": "Flat",
    "frequency": "Annual",
    "dates":[2025.09.18, 2025.11.18, 2026.02.18, 2026.08.18, 2027.08.18, 2028.08.18, 2030.08.18,
             2032.08.18, 2035.08.18, 2040.08.18, 2045.08.18, 2055.08.18,2065.08.18, 2075.08.18],
    "values":[1.3000, 1.3700, 1.3898, 1.3865, 1.4299, 1.4471, 1.6401,
              1.7654, 1.7966, 1.9930, 2.1834, 2.1397, 2.1987, 2.2225] / 100.0
}
discountCurve = parseMktData(curve)
//step3: 调用定价函数
pricingDate = 2025.08.18
npv = bondPricer(instrument, pricingDate, discountCurve)
//step4: 打印结果
print(npv)
/* output:
99.914593552908201   
*/

例2: 零息债定价 ( 250401.IB, 250401_25农发01_中国农业发展银行2025年第一期金融债券_中国货币网 )

//step1: 生成instrument
bond = {
    "productType": "Cash",
    "assetType": "Bond",
    "bondType": "ZeroCouponBond",
    "instrumentId": "250401.IB",
    "start": 2025.01.09,
    "maturity": 2026.02.05,
    "coupon": 0.0119,
    "dayCountConvention": "ActualActualISDA"
}
instrument = parseInstrument(bond)
//step2: 准备定价需要用到的曲线
/*
  因为该债券为农发债,所以discoutCurve选取农发债收益率曲线(即期)
  数据来源https://www.chinamoney.com.cn/chinese/bkcurvclosedyhis/index.html
*/
curve =  {
    "mktDataType": "Curve",
    "curveType": "IrYieldCurve",
    "referenceDate": 2025.08.18,
    "currency": "CNY",
    "curveName": "CNY_ADBC_BOND",
    "dayCountConvention": "ActualActualISDA",
    "compounding": "Compounded",
    "interpMethod": "Linear",
    "extrapMethod": "Flat",
    "frequency": "Annual",
    // 0.083 0.25 0.5 1.0 2.0 3.0 5.0 7.0 10.0 15.0 20.0
    "dates":[2025.09.18, 2025.11.18, 2026.02.18, 2026.08.18, 2027.08.18, 2028.08.18,
             2030.08.18, 2032.08.18, 2035.08.18, 2040.08.18, 2045.08.18],
    "values":[1.3721, 1.4354, 1.5014, 1.5543, 1.6651, 1.6994, 1.8381,
              1.9226, 1.9565, 2.1438, 2.1934] / 100.0
}
discountCurve = parseMktData(curve)
//step3: 调用定价函数
pricingDate = 2025.08.18
npv = bondPricer(instrument, pricingDate, discountCurve)
//step4: 打印结果
print(npv)
/* output:
100.574981714653844
*/

例 3:固定利率债定价 ( 240021.IB 240021_24附息国债21_2024年记账式附息(二十一期)国债_中国货币网

//step1: 生成instrument
bond = {
    "productType": "Cash",
    "assetType": "Bond",
    "bondType": "FixedRateBond",
    "instrumentId": "240021.IB",
    "start": 2024.10.25,
    "maturity": 2025.10.25,
    "issuePrice": 100,
    "coupon": 0.0133,
    "frequency": "Annual",
    "dayCountConvention": "ActualActualISDA"
}
instrument = parseInstrument(bond)
//step2: 准备定价需要用到的曲线
/*
 因为该债券为国债,所以discoutCurve选取国债收益率曲线(即期)
 数据来源https://www.chinamoney.com.cn/chinese/bkcurvclosedyhis/index.html
*/
curve =  {
    "mktDataType": "Curve",
    "curveType": "IrYieldCurve",
    "referenceDate": 2025.08.18,
    "currency": "CNY",
    "curveName": "CNY_TREASURY_BOND",
    "dayCountConvention": "ActualActualISDA",
    "compounding": "Compounded",
    "interpMethod": "Linear",
    "extrapMethod": "Flat",
    "frequency": "Annual",
    // 0.083 0.25 0.5 1.0 2.0 3.0 5.0 7.0 10.0 15.0 20.0 30.0 40.0 50.0
    "dates":[2025.09.18, 2025.11.18, 2026.02.18, 2026.08.18, 2027.08.18, 2028.08.18, 2030.08.18,
             2032.08.18, 2035.08.18, 2040.08.18, 2045.08.18, 2055.08.18,2065.08.18, 2075.08.18],
    "values":[1.3000, 1.3700, 1.3898, 1.3865, 1.4299, 1.4471, 1.6401,
              1.7654, 1.7966, 1.9930, 2.1834, 2.1397, 2.1987, 2.2225] / 100.0
}
discountCurve = parseMktData(curve)
//step3: 调用定价函数
pricingDate = 2025.08.18
npv = bondPricer(instrument, pricingDate, discountCurve)
//step4: 打印结果
print(npv)
/* output:
101.076528630342707 
*/

2.2 国债期货定价

国债期货定价(bondFuturesPricer)使用持有成本模型。该模型的基本思想是,购买现货国债并持有至期货交割日的总成本,应等于直接购买国债期货的成本,否则市场就会出现无风险的套利机会。其最核心的公(参考[1])可以概括为:

​ 国债期货价格 = (现货价格 + 融资成本 - 持有收益) / 转换因子

这里假设定价日为 2025 年 8 月 18 日, 2025 年 9 月份到期的十年期国债期货 T2509 的最便宜交割券为 220010.IB, 该国债期货的定价过程如下:

//step1: 生成instrument
//交割券信息
bond = {
    "productType": "Cash",
    "assetType": "Bond",
    "bondType": "FixedRateBond",
    "instrumentId": "220010.IB",
    "start": "2022.05.15",
    "maturity": "2032.05.15",
    "issuePrice": 100.0,
    "coupon": 0.0276,
    "frequency": "Semiannual",
    "dayCountConvention": "ActualActualISDA"
}
//国债期货信息
bondFutures = { 
    "productType": "Futures",
    "futuresType": "BondFutures",
    "instrumentId": "T2509",
    "nominal": 100.0,
    "maturity": "2025.09.12",
    "settlement": "2025.09.16",
    "underlying": bond,
    "nominalCouponRate": 0.03
}
instrument = parseInstrument(bondFutures)
//step2: 准备定价需要用到的曲线
//国债收益率曲线(即期)
curve =  {
    "mktDataType": "Curve",
    "curveType": "IrYieldCurve",
    "referenceDate": 2025.08.18,
    "currency": "CNY",
    "curveName": "CNY_TREASURY_BOND",
    "dayCountConvention": "ActualActualISDA",
    "compounding": "Compounded",
    "interpMethod": "Linear",
    "extrapMethod": "Flat",
    "frequency": "Annual",
    // 0.083 0.25 0.5 1.0 2.0 3.0 5.0 7.0 10.0 15.0 20.0 30.0 40.0 50.0
    "dates":[2025.09.18, 2025.11.18, 2026.02.18, 2026.08.18, 2027.08.18, 2028.08.18, 2030.08.18,
             2032.08.18, 2035.08.18, 2040.08.18, 2045.08.18, 2055.08.18,2065.08.18, 2075.08.18],
    "values":[1.3000, 1.3700, 1.3898, 1.3865, 1.4299, 1.4471, 1.6401,
              1.7654, 1.7966, 1.9930, 2.1834, 2.1397, 2.1987, 2.2225] / 100.0
}
discountCurve = parseMktData(curve)
//step3: 调用定价函数
pricingDate = 2025.08.18
npv = bondFuturesPricer(instrument, pricingDate, discountCurve)
//step4: 打印结果
print(npv)
// output:107.940924852555937

2.3 存款定价

存款定价(irDepositPricer)相当于只有一期现金流的债券定价。需要注意的是,存款的计息惯例一般为 Actual360,可以在构建 instrument 的时候指定。简单示例如下:

//step1: 生成instrument
deposit =  {
    "productType": "Cash",
    "assetType": "Deposit",
    "start": 2025.07.15,
    "maturity": 2025.10.15,
    "rate": 0.02,
    "dayCountConvention": "Actual360",
    "notional":["CNY", 1E6],
    "payReceive": "Receive"
}
instrument = parseInstrument(deposit)
//step2: 准备定价需要用到的曲线
curve = {
    "mktDataType": "Curve",
    "curveType": "IrYieldCurve",
    "referenceDate": 2025.08.18,
    "currency": "CNY",
    "curveName": "CNY_FR_007",
    "dayCountConvention": "Actual365",
    "compounding": "Continuous",     
    "interpMethod": "Linear",
    "extrapMethod": "Flat",
    "frequency": "Annual",
    "dates":[2025.08.21, 2025.08.27, 2025.09.03, 2025.09.10, 2025.09.22, 2025.10.20, 2025.11.20, 
             2026.02.24, 2026.05.20, 2026.08.20, 2027.02.22, 2027.08.20,2028.08.21],
    "values":[1.4759, 1.5331, 1.5697, 1.5239, 1.4996, 1.5144, 1.5209,
              1.5539, 1.5461, 1.5316, 1.5376, 1.5435,1.5699] / 100
}
discountCurve = parseMktData(curve)
//step3: 调用定价函数
pricingDate = 2025.08.18
irDepositPricer(instrument, pricingDate, discountCurve)
// output:1002699.4865622655

2.4 标准利率互换定价

利率互换是比较常见的交易类型,目前仅支持以 FR_007 和 SHIBOR_3M 为浮动参考利率的人民币利率互换合约,两者合计占人民币利率互换九成以上的交易量。

利率互换的两条腿相当于两个债券(一个固定利率债,一个浮动利率债)。对于一个接受固定利率,支付浮动利率的利率互换持有方,其定价公式为

npv = npv_fixed - npv_floating

利率互换定价(irFixedFloatingSwapPricer)需要提供浮动利率参考的历史数据,需要传入浮动参考利率的历史数据,用于计算定价日后的第一期浮动利率。示例如下:

//step1: 生成instrument
irs =  {
    "productType": "Swap",
    "swapType": "IrSwap",
    "irSwapType": "IrFixedFloatingSwap",
    "start": 2025.06.16,
    "maturity": 2028.06.16,
    "frequency": "Quarterly",
    "fixedRate": 0.018,
    "calendar": "CFET", 
    "fixedDayCountConvention": "Actual365",
    "floatingDayCountConvention": "Actual365",
    "payReceive": "Pay",
    "iborIndex": "FR_007",
    "spread": 0.0001,
    "notional":["CNY", 1E8]
}
instrument = parseInstrument(irs)
//step2: 准备定价需要用到的曲线
curve = {
    "mktDataType": "Curve",
    "curveType": "IrYieldCurve",
    "referenceDate": 2025.08.18,
    "currency": "CNY",
    "curveName": "CNY_FR_007",
    "dayCountConvention": "Actual365",
    "compounding": "Continuous",     
    "interpMethod": "Linear",
    "extrapMethod": "Flat",
    "frequency": "Annual",
    "dates":[2025.08.21, 2025.08.27, 2025.09.03, 2025.09.10, 2025.09.22, 2025.10.20, 2025.11.20, 
             2026.02.24, 2026.05.20, 2026.08.20, 2027.02.22, 2027.08.20,2028.08.21],
    "values":[1.4759, 1.5331, 1.5697, 1.5239, 1.4996, 1.5144, 1.5209,
              1.5539, 1.5461, 1.5316, 1.5376, 1.5435,1.5699] / 100
}
/*
为了计算定价日后面的第一笔现金流用到的浮动利率,需要传入参考利率的历史数据
因为 FR_007 利率互换的付息频率为季度,建议传入的 FR_007 的历史定盘数据不少于 70 个交易日
*/
fr007HistCurve = {
    "mktDataType": "Curve",
    "curveType": "AssetPriceCurve",
    "referenceDate": 2025.08.18,
    "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
}
curve = parseMktData(curve)
discountCurve = curve
forwardCurve = curve
assetPriceCurve = parseMktData(fr007HistCurve)
//step3: 调用定价函数
// 仅计算 npv
pricingDate = 2025.08.18
npv = irFixedFloatingSwapPricer(instrument, pricingDate, discountCurve, forwardCurve, assetPriceCurve)
print(npv)
// 计算包括 npv、现金流在内的各类指标
setting = {
    "calcCashFlow": true
}
results = irFixedFloatingSwapPricer(instrument, pricingDate, discountCurve, forwardCurve, assetPriceCurve, setting)
print(results)

2.5 外汇远期定价

外汇远期交易是一种基础的金融衍生工具,主要用于管理汇率风险。外汇远期定价需要传入两种货币的折现曲线和即期汇率作为市场数据。这里给出一个外汇远期定价(fxForwardPricer)的例子:

//step1: 生成instrument
fxForward = {
    "productType": "Forward",
    "forwardType": "FxForward",
    "expiry": 2025.12.16,
    "delivery": 2025.12.18,
    "currencyPair": "USDCNY",
    "direction": "Buy",
    "notional": ["USD", 1E6],
    "strike": 7.1
}
instrument = parseInstrument(fxForward)
//step2: 准备定价需要用到的曲线和即期
curveDates = [2025.08.21, 2025.08.27, 2025.09.03, 2025.09.10, 2025.09.22, 2025.10.20,
  2025.11.20, 2026.02.24, 2026.05.20, 2026.08.20, 2027.02.22, 2027.08.20,2028.08.21]
domesticCurveInfo = {
    "mktDataType": "Curve",
    "curveType": "IrYieldCurve",
    "referenceDate": 2025.08.18,
    "currency": "CNY",
    "dayCountConvention": "Actual365",
    "compounding": "Continuous",  
    "interpMethod": "Linear",
    "extrapMethod": "Flat",
    "frequency": "Annual",
    "dates": curveDates,
    "values":[1.5113, 1.5402, 1.5660, 1.5574, 1.5556, 1.5655, 1.5703, 
              1.5934, 1.6040, 1.6020, 1.5928, 1.5842, 1.6068]/100
}
foreignCurveInfo = {
    "mktDataType": "Curve",
    "curveType": "IrYieldCurve",
    "referenceDate": 2025.08.18,
    "currency": "USD",
    "dayCountConvention": "Actual365",
    "compounding": "Continuous",  
    "interpMethod": "Linear",
    "extrapMethod": "Flat",
    "frequency": "Annual",
    "dates": curveDates,
    "values":[4.3345, 4.3801, 4.3119, 4.3065, 4.2922, 4.2196, 4.1599, 
              4.0443, 4.0244, 3.9698, 3.7740, 3.6289, 3.5003]/100
}
domesticCurve = parseMktData(domesticCurveInfo)
foreignCurve = parseMktData(foreignCurveInfo)
spot = 7.1627
//step3: 调用定价函数
pricingDate = 2025.08.18
npv = fxForwardPricer(instrument, pricingDate, spot, domesticCurve, foreignCurve)
//step4: 打印结果
print(npv)
// output: 1919.8118

2.6 外汇掉期定价

外汇掉期交易是指交易者​​同时进行两笔金额相同、货币相同、但交割期限不同、方向相反​​的外汇交易,相当于两个外汇远期交易组合。外汇掉期定价也需要传入两种货币的折现曲线和即期汇率作为市场数据。这里给出一个外汇掉期定价(fxSwapPricer)的例子:

//step1: 生成instrument
pricingDate = 2025.08.18
fxSwap = {
    "productType": "Swap",
    "swapType": "FxSwap",
    "currencyPair": "USDCNY",
    "direction": "Buy",
    "notional": ["USD", 1E6],
    "nearStrike": 7.15,
    "nearExpiry": pricingDate + 60,
    "nearDelivery": pricingDate + 62,
    "farStrike": 7.18,
    "farExpiry": pricingDate + 180,
    "farDelivery": pricingDate + 182
}
instrument = parseInstrument(fxSwap)
//step2: 准备定价需要用到的曲线和即期
curveDates = [2025.08.21, 2025.08.27, 2025.09.03, 2025.09.10, 2025.09.22, 2025.10.20,
  2025.11.20, 2026.02.24, 2026.05.20, 2026.08.20, 2027.02.22, 2027.08.20,2028.08.21]
domesticCurveInfo = {
    "mktDataType": "Curve",
    "curveType": "IrYieldCurve",
    "referenceDate": pricingDate,
    "currency": "CNY",
    "dayCountConvention": "Actual365",
    "compounding": "Continuous",  
    "interpMethod": "Linear",
    "extrapMethod": "Flat",
    "frequency": "Annual",
    "dates": curveDates,
    "values":[1.5113, 1.5402, 1.5660, 1.5574, 1.5556, 1.5655, 1.5703, 
              1.5934, 1.6040, 1.6020, 1.5928, 1.5842, 1.6068]/100
}
foreignCurveInfo = {
    "mktDataType": "Curve",
    "curveType": "IrYieldCurve",
    "referenceDate": pricingDate,
    "currency": "USD",
    "dayCountConvention": "Actual365",
    "compounding": "Continuous",  
    "interpMethod": "Linear",
    "extrapMethod": "Flat",
    "frequency": "Annual",
    "dates": curveDates,
    "values":[4.3345, 4.3801, 4.3119, 4.3065, 4.2922, 4.2196, 4.1599, 
              4.0443, 4.0244, 3.9698, 3.7740, 3.6289, 3.5003]/100
}
domesticCurve = parseMktData(domesticCurveInfo)
foreignCurve = parseMktData(foreignCurveInfo)
spot = 7.1627
//step3: 调用定价函数
npv = fxSwapPricer(instrument, pricingDate, spot, domesticCurve, foreignCurve)
//step4: 打印结果
print(npv)
// output: 84379.328782705269986

2.7 外汇欧式期权定价

外汇欧式期权​​是一种金融衍生工具,它赋予持有者在​​未来某个特定日期(到期日)​​,以​​事先约定的汇率(行权价)​​,买入或卖出一定数量某种货币对的​​权利​​,但​​没有义务​。外汇欧式期权定价除了需要传入两种货币的折现曲线和即期汇率外,还需要传入波动率曲面作为市场数据。

目前仅支持了 BlackScholes 模型的解析解 (Analytic) 方法。这里给出一个外汇欧式期权定价(fxEuropeanOptionPricer)的例子:

//step1: 生成instrument
pricingDate = 2025.08.18
ccyPair = "USDCNY"
option = {
    "productType": "Option",
    "optionType": "EuropeanOption",
    "assetType": "FxEuropeanOption",
    "notional": ["USD", 1E6],
    "strike": 7.2,
    "maturity": 2025.10.28,
    "payoffType": "Call",
    "dayCountConvention": "Actual365",
    "underlying": ccyPair
}
instrument = parseInstrument(option)
//step2: 准备定价需要用到的曲面、曲线和即期
quoteTerms = ['1d', '1w', '2w', '3w', '1M', '2M', '3M', '6M', '9M', '1y', '18M', '2y', '3y']
quoteNames = ["ATM", "D25_RR", "D25_BF", "D10_RR", "D10_BF"]
quotes = [0.030000, -0.007500, 0.003500, -0.010000, 0.005500, 
          0.020833, -0.004500, 0.002000, -0.006000, 0.003800, 
          0.022000, -0.003500, 0.002000, -0.004500, 0.004100, 
          0.022350, -0.003500, 0.002000, -0.004500, 0.004150, 
          0.024178, -0.003000, 0.002200, -0.004750, 0.005500, 
          0.027484, -0.002650, 0.002220, -0.004000, 0.005650, 
          0.030479, -0.002500, 0.002400, -0.003500, 0.005750, 
          0.035752, -0.000500, 0.002750,  0.000000, 0.006950, 
          0.038108,  0.001000, 0.002800,  0.003000, 0.007550, 
          0.039492,  0.002250, 0.002950,  0.005000, 0.007550, 
          0.040500,  0.004000, 0.003100,  0.007000, 0.007850, 
          0.041750,  0.005250, 0.003350,  0.008000, 0.008400, 
          0.044750,  0.006250, 0.003400,  0.009000, 0.008550]
quotes = reshape(quotes, size(quoteNames):size(quoteTerms)).transpose()
curveDates = [2025.08.21, 2025.08.27, 2025.09.03, 2025.09.10, 2025.09.22, 2025.10.20,
  2025.11.20, 2026.02.24, 2026.05.20, 2026.08.20, 2027.02.22, 2027.08.20,2028.08.21]
domesticCurveInfo = {
    "mktDataType": "Curve",
    "curveType": "IrYieldCurve",
    "referenceDate": pricingDate,
    "currency": "CNY",
    "dayCountConvention": "Actual365",
    "compounding": "Continuous",  
    "interpMethod": "Linear",
    "extrapMethod": "Flat",
    "frequency": "Annual",
    "dates": curveDates,
    "values":[1.5113, 1.5402, 1.5660, 1.5574, 1.5556, 1.5655, 1.5703, 
              1.5934, 1.6040, 1.6020, 1.5928, 1.5842, 1.6068]/100
}
foreignCurveInfo = {
    "mktDataType": "Curve",
    "curveType": "IrYieldCurve",
    "referenceDate": pricingDate,
    "currency": "USD",
    "dayCountConvention": "Actual365",
    "compounding": "Continuous",  
    "interpMethod": "Linear",
    "extrapMethod": "Flat",
    "frequency": "Annual",
    "dates": curveDates,
    "values":[4.3345, 4.3801, 4.3119, 4.3065, 4.2922, 4.2196, 4.1599, 
              4.0443, 4.0244, 3.9698, 3.7740, 3.6289, 3.5003]/100
}
surf = fxVolatilitySurfaceBuilder(pricingDate, ccyPair, quoteNames, quoteTerms, quotes, spot, domesticCurve, foreignCurve)
domesticCurve = parseMktData(domesticCurveInfo)
foreignCurve = parseMktData(foreignCurveInfo)
spot = 7.1627
//step3: 调用定价函数
npv = fxEuropeanOptionPricer(instrument, pricingDate, spot, domesticCurve, foreignCurve, surf)
//step4: 打印结果
print(npv)
//output: 1693.99192059959023

3. 特殊定价接口

金融机构每天需要对持有的交易头寸进行定价。对于持有的不同资产,金融机构需要根据不同交易类型分别调用对应的定价函数,如 IRS 交易需要去调用利率互换定价函数,FxSwap 交易需要去调用外汇掉期定价函数,需要写很多 if-else 判断。除此之外,不同的定价函数需要的市场数据(Spot/Curve/VolSurf)也不一样。同一种交易类型,入参类型相同,也需要根据标的等信息适配不同的市场数据。用户在使用的时候需要做很多配置,随着交易种类和数量的增加,这种配置方法很不方便而且容易出错。

针对上述问题,DolphinDB 创新性地提出统一定价接口 instrumentPricer。为了方便用户对组合进行定价,我们在前者的基础上,加入头寸信息,提供 portfolioPricer 接口。下面分别介绍这两个接口。

3.1 instrumentPricer

instrumentPricer 是 DolphinDB 推出的统一定价接口。对于常见的交易产品和市场数据,系统自动提供 instrument 与 marketData 的默认匹配方案,无需额外操作。在特殊场景下,也可自定义匹配,大大提升了灵活性。用户只需要把交易封装成 INSTRUMENT 对象,统一放到一个向量中,将市场数据(曲线/曲面)封装成 MKTDATA 对象,放到一个向量或字典中,系统就能对金融工具进行批量定价,输出 npv 向量。

3.1.1 instrument 和 marketData 的匹配规则

在定价过程中,系统会按照以下优先级确定市场数据:

  1. instrument 中已显式指定市场数据,则直接使用该数据;

  2. 若未指定,则系统根据预定义规则自动匹配合适的市场数据。

以下将对不同类型金融工具的匹配规则进行说明。

债券(Bond)

债券定价需要用到折现曲线。可以通过 discountCurve 字段指定折现曲线名称,例如:

bond = {
    "productType": "Cash",
    "assetType": "Bond",
    "bondType": "FixedRateBond",
    "instrumentId": "1382011.IB",
    "start": "2013.01.14",
    "maturity": "2028.01.14",
    "issuePrice": 100.0,
    "coupon": 0.058,
    "frequency": "Annual",
    "dayCountConvention": "ActualActualISDA",
    "currency": "CNY",              //可选字段 
    "subType": "MTN",               //可选字段
    "creditRating": "AAA",          //可选字段
    "discountCurve": "CNY_MTN_AAA"  //可选字段
}

折现曲线选择规则如下:

  • 如果 discountCurve 已指定,定价函数会直接在 marketData 中查找对应曲线。

  • 如果 discountCurve 未指定:
    • 指定了 currency,subType 和 creditRating,系统会选择名为 currency + "_" + subType + "_" + creditRating 的折现曲线。

    • 指定了 currency,且 subType 为 TREASURY_BONDCENTRAL_BANK_BILLCDB_BONDEIBC_BONDADBC_BOND 之一,则不需要 creditRating,系统会选择名为 currency + "_" + subType 的折现曲线。

    • 指定了 currency,且 subType 不为 TREASURY_BONDCENTRAL_BANK_BILLCDB_BONDEIBC_BONDADBC_BOND 之一,同时未指定 creditRating,系统会选择名为 currency + "_TREASURY_BOND" 的折现曲线。

    • 指定了 currency,且未指定 subType 和 creditRating,系统会选择名为 currency + "_TREASURY_BOND" 的折现曲线。

    • currency,subType 和 creditRating 均未指定,系统会选择名为 "CNY_TREASURY_BOND" 的折现曲线。

国债期货(BondFutures)

无需指定 discountCurve,函数会使用其标的债券(underlying)的 dicountCurve。

存款(Deposit)

存款定价仅需指定折现曲线 discountCurve:

  • 如果 discountCurve(各个币种的无风险曲线)已指定,则使用指定的曲线进行定价。

  • 如果未指定,则根据币种自动匹配折现曲线,规则如下:

currency discountCurve
CNY CNY_FR_007
USD USD_SOFR
EUR EUR_ESTR

利率互换(IrFixedFloatingSwap)

利率互换定价需要传入三条曲线:discountCurve、forwardCurve 和 assetPriceCurve。目前仅支持以 FR_007 和 SHIBOR_3M 作为浮动参考利率的利率互换。

  • 如果在 instrument 中指定了相应曲线,则使用指定的曲线进行定价。

  • 如果未指定,则根据币种和浮动利率基准自动匹配三条默认曲线,如下表所示:

currency iborIndex discountCurve forwardCurve assetPriceCurve
CNY FR_007 CNY_FR_007 CNY_FR_007 PRICE_FR_007
CNY SHIBOR_3M CNY_FR_007 CNY_SHIBOR_3M PRICE_SHIBOR_3M

其中 assetPriceCurve 填入的是浮动参考利率的历史数据,用于计算定价日起第一笔现金流的浮动利率。

外汇远期(FxForward)/ 外汇掉期(FxSwap)

这两类线性产品定价需要绑定 domesticCurve 和 foreignCurve,并基于 currencyPair 获取相应的 FxSpot。

  • 若在 instrument 中指定了 domesticCurve 和 foreignCurve,则直接使用用户指定的曲线。

  • 若未指定,则系统会根据货币对自动匹配默认曲线,如下表所示:

currencyPair domesticCurve foreignCurve
USDCNY CNY_FR_007 USD_USDCNY_FX
EURCNY CNY_FR_007 EUR_EURCNY_FX
EURUSD USD_SOFR EUR_EURUSD_FX

其中 foreignCurve 是根据外汇掉期交易,并结合利率平价公式推导得到的外币隐含即期曲线。

外汇欧式期权(FxEuropeanOption)

外汇期权定价除需使用 domesticCurve 与 foreignCurve 外,还依赖 fxSpot 和 volSurf。

这两个市场数据均可根据期权的 underlying(货币对) 自动匹配。

  • 若在 instrument 中明确指定,则优先使用用户提供的 domesticCurve、foreignCurve。

  • 若未指定,则系统会根据货币对自动匹配,规则如下:

currencyPair fxSpot domesticCurve foreignCurve volSurf
USDCNY USDCNY CNY_FR_007 USD_USDCNY_FX USDCNY
EURCNY EURCNY CNY_FR_007 EUR_EURCNY_FX EURCNY
EURUSD EURUSD USD_SOFR EUR_EURUSD_FX EURUSD

3.1.2 使用场景

instrumentPricer 适合批量定价的场景。

(1)投资组合批量定价

投资组合中通常会有不同类型的交易,例如对于固收交易员来说,他的交易类型可能有债券、国债期货、利率互换等。如何方便又快速地对投资组合中的每一笔交易进行定价,是我们要处理的问题。传统的做法是先按照交易类型分类,然后对每一类采用相应的定价函数逐个进行定价,这种方式繁琐且低效。instrumentPricer 可以把每笔交易都封装成一个 INSTRUMENT 对象,所有交易都放到一个向量中。用户只需一次函数调用,就能输出所有交易的定价结果。这里给出一个例子:

bond1 = {
    "productType": "Cash",
    "assetType": "Bond",
    "bondType": "FixedRateBond",
    "instrumentId": "220010.IB",
    "start": 2020.12.25,
    "maturity": 2031.12.25,
    "coupon": 0.0149,
    "frequency": "Annual",
    "dayCountConvention": "ActualActualISDA",
    "discountCurve": "CNY_TREASURY_BOND"
}
bond = parseInstrument(bond1)
bondFut1 = {
    "productType": "Futures",
    "futuresType": "BondFutures",
    "instrumentId": "T2509",
    "nominal": 100.0,
    "maturity": "2025.09.12",
    "settlement": "2025.09.16",
    "underlying": bond1,
    "nominalCouponRate": 0.03
}
bondFut = parseInstrument(bondFut1)
irs1 =  {
    "productType": "Swap",
    "swapType": "IrSwap",
    "irSwapType": "IrFixedFloatingSwap",
    "start": 2025.06.16,
    "maturity": 2028.06.16,
    "frequency": "Quarterly",
    "fixedRate": 0.018,
    "calendar": "CFET", 
    "fixedDayCountConvention": "Actual365",
    "floatingDayCountConvention": "Actual365",
    "payReceive": "Pay",
    "iborIndex": "FR_007",
    "spread": 0.0001,
    "notional":["CNY", 1E8]
}
irs = parseInstrument(irs1)
pricingDate = 2025.08.18
curve1 = {
    "mktDataType": "Curve",
    "curveType": "IrYieldCurve",
    "curveName": "CNY_FR_007",
    "referenceDate": pricingDate,
    "currency": "CNY",
    "dayCountConvention": "ActualActualISDA",
    "compounding": "Continuous",
    "interpMethod": "Linear",
    "extrapMethod": "Flat",
    "dates":[2025.08.21, 2025.08.27, 2025.09.03, 2025.09.10, 2025.09.22, 2025.10.20, 2025.11.20, 
             2026.02.24,2026.05.20, 2026.08.20, 2027.02.22, 2027.08.20, 2028.08.21],
    "values":[1.4759, 1.5331, 1.5697, 1.5239, 1.4996, 1.5144, 1.5209, 
              1.5539, 1.5461, 1.5316, 1.5376, 1.5435, 1.5699] / 100.0
}
curveCnyFr007 = parseMktData(curve1)
bondCurve =  {
    "mktDataType": "Curve",
    "curveType": "IrYieldCurve",
    "referenceDate": pricingDate,
    "currency": "CNY",
    "curveName": "CNY_TREASURY_BOND",
    "dayCountConvention": "ActualActualISDA",
    "compounding": "Compounded",
    "interpMethod": "Linear",
    "extrapMethod": "Flat",
    "frequency": "Annual",
    "dates":[2025.09.18, 2025.11.18, 2026.02.18, 2026.08.18, 2027.08.18, 2028.08.18, 2030.08.18,
             2032.08.18, 2035.08.18, 2040.08.18, 2045.08.18, 2055.08.18,2065.08.18, 2075.08.18],
    "values":[1.3000, 1.3700, 1.3898, 1.3865, 1.4299, 1.4471, 1.6401,
              1.7654, 1.7966, 1.9930, 2.1834, 2.1397, 2.1987, 2.2225] / 100.0
}
curveCnyTreasuryBond = parseMktData(bondCurve)
fr007HistCurve = {
    "mktDataType": "Curve",
    "curveType": "AssetPriceCurve",
    "curveName": "PRICE_FR_007",
    "referenceDate": pricingDate,
    "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
}
priceCurveFr007 = parseMktData(fr007HistCurve)
instrument = [bond, bondFut, irs]
mktData= [curveCnyFr007, curveCnyTreasuryBond, priceCurveFr007]
results = instrumentPricer(instrument, pricingDate, mktData)
print(results)
/*
[99.600846811350891,107.77642440859519,-651143.525606005452573]
*/

更多交易类型的例子见 instrumentPricer 中的例子,也可以参考 instrumentPricer.dos

(2)VaR 计算

VaR(风险价值)计算是金融风险管理中的重要工具,主要用于评估在一定置信度下,特定时间内可能遭受的最大损失。通常采用历史模拟法计算 VaR,模拟历史 250 或者 500 个场景。这种计算密集型任务就可以使用 instrumentPricer。下面给一个例子的伪代码

/*
 目标:计算一万只国债的 VaR, 采用历史模拟法构建 500 个场景,置信区间为 99%
 database:
   Instrument: 金融工具表, 保存所有债券的基础信息, 其中的 instrument 字段为 INSTRUMENT 类型
   MarketData:市场数据表,保存定价需要用到的曲线/曲面数据,其中的 data 字段为 MKTDATA 类型       
 计算日:2025.08.18
*/
// Step1:计算参考日的估值,计为 P0
pricingDate = 2025.08.18
curve0 = select data from MarketData where date = pricingDate and name = "CNY_TREASURY_BOND"
P0 = select instrumentPricer(t1.instrument, pricingDate, [curve0]) from Instrument as t1 where t1.subType = "TREASURY_BOND" limit 10000;
nextBusiDate = temporalAdd(pricingDate, 1d, "CFET") // CFET 为中国外汇交易中心交易日历
// Step2:计算情景的估值,第一个情景计为 P1
// 假设第一个历史情景是 2023.08.18 到 2023.08.19 的曲线变化
curve11 = select extraMktData(data) from MarketData where date = 2023.08.18 and name = "CNY_TREASURY_BOND"
curve12 = select extraMktData(data) from MarketData where date = 2023.08.19 and name = "CNY_TREASURY_BOND"
curve1 = extraMktData(curve0)
curve1["referenceDate"] = nextBusiDate
curve1["values"] += curve12["values"] - curve11["values"] 
curve1["dates"] = temporalAdd(curve1["dates"], 1d, "CFET")
curve = parseMktData(curve1)
P1 = select instrumentPricer(t1.instrument, nextBusiDate, [curve]) from Instrument as t1 where t1.subType = "TREASURY_BOND" limit 10000;
// 同P1的计算过程,可以求出P2,P3,...,P500
// Step3:计算 VaR
pnls = [P1, P2,...,P500] - take(P0, 500)
// 取每个合约左侧 99% 值,即可得到 VaR
valueAtRisk = percentile(pnls, 1)

3.2 portfolioPricer

portfolioPricer 可以对一个投资组合进行定价。不同于 instrumentPricer,其参数中多了一个 amount,可以输入 instrument 中各个元素的头寸(合约数量,可正可负),得到的结果是整个投资组合的 npv。

例子见 portfolioPricer 中的示例,也可以参考 portfolioPricer.dos

4. 总结和展望

此教程对基于 INSTRUMENT 类型金融工具的定价函数做了简要介绍,包括定价原理和示例。目前只推出了部分线性产品定价函数和基于 BlackScheles 公式的外汇欧式期权定价函数。

后续我们会从以下几个方面不断扩充定价函数库:

  • 资产类别。目前只支持固收和外汇,后续我们会扩充到大宗商品、权益、加密货币等。

  • 衍生品类型。不仅支持场内的欧式期权和美式期权,也支持更多的场外期权和结构化产品。

  • 定价模型。目前仅支持 BlackScholes 模型,后续会增加局部波动率模型(Dupire)、随机波动率模型(Heston)、利率模型(Hull-White)等。

  • 定价方法。目前仅支持解析解(Analytic),后续会增加有限差分法(PDE) 和 蒙特卡洛法(MonteCarlo) 等方法。

  • 风险计量。除了计算期权基本的 Greeks(Delta/Gamma/Vega 等),还可以精细到满足巴塞尔Ⅲ的程度,如 Bucked Delta / Bucked Vega 。

敬请期待!

参考

[1] 中国期货业协会. 国债期货[M]. 北京: 中国财政经济出版社, 2013.

[2] 中国外汇交易中心. 产品指引(外汇市场)V4.2:Product Guide (FX Market)[EB/OL]. 上海:中国外汇交易中心,2023: 1-110 [2023-10]