Skip to main content

QR Yield 模块

该模块用于计算股指期货的 implied yield q^\hat{q} ,使用二次函数进行拟合:

q^(t)=β0+β1t+β2t2\begin{equation} \hat{q}(t) = \beta_0 + \beta_1 t + \beta_2 t^2 \end{equation}

其中:

  • tt 为期货合约的 tenor, 使用年化值
  • β=[β0,β1,β2]\beta = [\beta_0, \beta_1, \beta_2] 为多项式系数

拟合数据点计算方法

tenor 和利率的计算方法

期货合约在当前时刻的 tenor 计算方法如下:

  1. 从数据库中获得期货合约的到期日 TexpT_{exp} 和利率曲线 ir, 其中 ir 为1维数组
  2. 计算期货合约到期日 TexpT_{exp} 和当前交易日 TnowT_{now} 之间的自然日数量 TT
  3. 使用 TT 作为 ir 的index, 获得利率值 r=ir[T]*0.01, 利率的时间单位为自然日
  4. 使用当前时刻的 ten_sec_seq tss 计算 day_frac:
def day_frac(tss: int):
dayfrac = max( min(((tss - 3420 - max(min(tss-4140, 540), 0))) / 1440, 1), 0)
if tss == 5400:
dayfrac -= 1e-10
return dayfrac
  1. 计算 tenor t= max((T-dayfrac)/365.0, 1e-10), tenor 单位为自然日, 这里取 t=max(t,1e10)t = max(t, 1e-10) 是为了防止 Eq.(2) 除以0导致报错

例如, 对于交易日 2024-08-27, ten_sec_seq==3500, 按照到期日由近到远排序的前4个沪深300指数期货合约 IF 到期数据表格为:

代码到期日 TexpT_{exp}交易日 TnowT_{now}ten_sec_seq
IF24092024-09-202024-08-273500
IF24102024-10-182024-08-273500
IF24122024-12-202024-08-273500
IF25032025-03-212025-08-273500

得到4个合约对应的 T 为 25, 53, 116, 207 天. ten_sec_seq==3500 对应的 day_frac0.05555555555555555, 可得4个期货合约对应的 tenor 和利率如下表:

代码到期日 TexpT_{exp}交易日 TnowT_{now}Tten_sec_seqtenor利率 rr
IF24092024-09-202024-08-272535000.0683410.0192
IF24102024-10-182024-08-275335000.1450530.0188
IF24122024-12-202024-08-2711635000.3176560.0185
IF25032025-03-212024-08-2720735000.5669710.0182

implied yiled qq 计算方法

拟合该二次曲线所使用的数据点 (t_i, q_i) 的计算方式为: 使用期货合约价格 FF , 期货合约的 tenor tt, 利率 rr , index 的价格 SS, 计算implied yield q^\hat{q}: 公式如下:

q=r+1tlogSF\begin{equation} \begin{aligned} q &= r + \frac{1}{t}\log\frac{S}{F} \end{aligned} \end{equation}

其中利率 rr 使用年化值.

例如, 沪深300股指期货 IF 和沪深300指数 000300.SH 在某个时间切片下的价格数据表为:

代码期货价格 FF指数价格 SS期货tenor tt无风险利率 rr
IF24093312.83315.6860.06850.0192
IF24103312.03315.6860.14520.0188
IF24123307.43315.6860.31780.0185
IF25033302.43315.6860.56710.0182

使用公式 (2) 计算可得4个期货合约对应 implied yield 为

q(IF2409)=0.0319q(IF2410)=0.0265q(IF2412)=0.0264q(IF2503)=0.0253q(\text{IF2409}) = 0.0319 \\ q(\text{IF2410}) = 0.0265 \\ q(\text{IF2412}) = 0.0264 \\ q(\text{IF2503}) = 0.0253 \\

汇总为:

代码期货价格 FF指数价格 SS期货tenor tt无风险利率 rrimplied yiled qq
IF24093312.83315.6860.06850.01920.0319
IF24103312.03315.6860.14520.01880.0265
IF24123307.43315.6860.31780.01850.0264
IF25033302.43315.6860.56710.01820.0182

API 使用方法

C++接口

YieldCurveFitter.hpp 中包含了一个类 YieldCurveFitter:

class YieldCurveFitter{

public:
YieldCurveFitter(const std::vector<double>& seed);

YieldCurveFitter(const std::size_t modelPower, const std::vector<double>& seed);

void fit(const std::vector<TermStructureSlice>& termStructureData, const int32_t ten_sec_seq, double alpha=1e-3);
public:
std::vector<double> beta;
...

暴露的 public 接口和成员如下:

  • 构造函数 YieldCurveFitter(const std::vector<double>& seed)
  • 构造函数 YieldCurveFitter(const std::size_t modelPower, const std::vector<double>& seed)
  • 成员变量 std::vector<double> beta
  • 成员函数 void fit(const std::vector<TermStructureSlice>& termStructureData, const int32_t ten_sec_seq, double alpha=1e-3)

其中成员函数 YieldCurveFitter::fit() 调用时需要使用的自定义结构体 TermStructureSlice 也在 YieldCurveFitter.hpp 中:

struct TermStructureSlice{
char future_code[32]; // future code e.g. "IF2409"
double future_price{}; // future price
double spot_price{}; // spot price
double ir{}; // interest rate
double tenor{}; // tenor of future
double yield{}; // yield of future
int32_t ten_sec_seq{}; // sequence of ten seconds
};

调用方法

  1. 使用构造函数 YieldCurveFitter(const std::vector<double>& seed) 创建一个对象 ycf:
std::vector<double> seed = {seed0, seed1, seed2}; // seed size == 3
YieldCurveFitter ycf = YieldCurveFitter(seed);

注意 seed 是一个 size 为3的 std::vector<double> 类型,其值为该品种上个交易日收盘前最后一次计算的Yield参数 [beta0, beta1, beta2].

  1. 创建一个类型为 std::vector<TermStructureSlice> 对象:
std::vector<TermStructureSlice> priceData;
  1. 使用10s切片的期货数据和指数数据计算当前时刻的Yield q, 将数据以数据结构 TermStructureSlice 格式 push_backpriceData 中:
// double rf := risk free rate
// double S := index price
// double F := future price
// double t := future tenor
// double tss := ten second sequence
// char code[32] := future code e.g. "IF2409"
const double future_yield = rf + std::log(S/F)/t;
TermStructureSlice ts_data;
std::memcpy(ts_data.future_code, code, sizeof(ts_data.future_code));
ts_data.future_price = F;
ts_data.spot_price = S;
ts_data.ir = rf;
ts_data.tenor = t;
ts_data.yield = future_yield;
ts_data.ten_sec_seq{} = tss;
priceData.push_back(ts_data);

注意: 上面的代码只 push 了该品种10s切片的一个期货合约,在调用 fit() 前需要将该品种到期前4个月期货合约的数据都push进去,合约的顺序没有强制要求. 例如对于交易日 20250929, 对应的到期前4个月期货合约为 IF2510, IF2511, IF2512, IF2601,具体实现时,建议在开盘前按照期货合约tenor 对合约进行升序排序,取前4个合约

  1. 调用 ycf.fit(tss) 函数,在结束后将结果ycf.beta取出:
// double tss := ten second sequence
// std::vector<double> newBeta := results
// newBeta.size() == 3
ycf.fit(priceData, tss);
newBeta = ycf.beta;

注意:

  • 每次 fit(tss) 结束后不要 clear 掉 priceData,下一个 tss 继续push_back()数据,保留从当前 tss 之前5分钟的priceData
  • 可以先 priceData.reserve(buffer_size) 以增加效率
  • 每一个品种单独使用一个YieldCurveFitter不要混用,例如创建一个 ycf_IF 拟合 IF 的数据,创建一个 ycf_IM 拟合IM的数据
  1. 使用拟合的参数 newBeta = [beta0, beta1, beta2] 计算该品种10s切片的yield:
// double t := tenor
double yield = newBeta[0] + newBeta[1] * t + newBeta[2] * t * t;

使用计算得到的 yield 进入后续的波动率曲面拟合模块.

Python 接口

python 接口使用pybind11将C++类 class YieldCurveFitter, 对应python3中为模块 YieldCurveModel ,包含构造函数和成员

class YieldCurveFitter:
def __init__(self, seed:numpy.ndarray, modelPower=2):
...

def fitDay(termStructureData: pandas.DataFrame, alpha=1e-3) -> numpy.ndarray:

def setSeed(seed: numpy.ndarray) -> None :

def fitTSS(termStructureData: pandas.DataFrame, tss: int, alpha=1e-3) -> numpy.ndarray :

  • 构造函数中的 seed 必须为 1D array, shape 为 (n,),其中 n=modelPower+1,其值为上一个交易日 ten_sec_seq == 5382 的fit值, 也就是倒数第19行的 [beta0, beta1, beta2]
  • setSeed() 函数可以更新seed值
  • fitTSS() 函数会输出一个 numpy.ndarray , shape 为 (modelPower+1, ) 为该10s切片 tss 的fit值
  • fitDay() 函数会输出一个 numpy.ndarray, shape 为 (1442, modelPower+1, ), 为整个交易日按照10s切片时间排列的fit值。其中,fitDay()fitTss() 的输入 termStructureData DataFrame 格式为6列:
column numbercolumn namedtypemeaning
0codestrunderlying Future Symbol
1futfloatunderlying Future price
2ten_sec_seqint3210s切片时间
3indfloatunderlying spot price
4irfloatinterest rate
5tfloatfuture tenor
6qfloatfuture yield

注意:

  1. 每一 column number 的 column name 可以不一样,但 dtype 和 meanning 必须一致
  2. 不建议在Python中手写一个交易日的循环使用fitTss(), 而是一次性 fitDay()

使用例

import YieldCurveModel as ycm
import numpy as np
seed = np.array([0, 0, 0])
ycf =ycm.YieldCurveFitter(seed, modelPower=2)
out = ycf.fitDay(df)

编译与安装

  1. 本地 clone project
  2. mkdir build && cd build
  3. config cmake (以 conda 环境名为 'py_3_11_13' 为例)
    1. activate conda envs: conda activate py_3_11_13
    2. config cmake at build directory
    cmake ..                          \
    -DPython3_FIND_STRATEGY=LOCATION \
    -DCMAKE_PREFIX_PATH=$CONDA_PREFIX \
    -DCMAKE_BUILD_TYPE=Release
    • 若想编译testcase:
    cmake ..                           \
    -DPython3_FIND_STRATEGY=LOCATION \
    -DCMAKE_PREFIX_PATH=$CONDA_PREFIX \
    -DCMAKE_BUILD_TYPE=Release \
    -DBUILD_TESTS=ON
    • 确认依赖包 lapack 为 /usr/local 中编译安装的lapack,而非其他地方
  4. make && make install
  5. 检查安装成功
(py_3_11_13) zhuchenyu@yd-3-12:~/QR_Math$ python
Python 3.11.13 (main, Jun 5 2025, 13:12:00) [GCC 11.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import YieldCurveModel as ycm
>>> exit()