QR Yield 模块
该模块用于计算股指期货的 implied yield ,使用二次函数进行拟合:
其中:
- 为期货合约的 tenor, 使用年化值
- 为多项式系数
拟合数据点计算方法
tenor 和利率的计算方法
期货合约在当前时刻的 tenor 计算方法如下:
- 从数据库中获得期货合约的到期日 和利率曲线 ir, 其中 ir 为1维数组
- 计算期货合约到期日 和当前交易日 之间的自然日数量
- 使用 作为 ir 的index, 获得利率值
r=ir[T]*0.01, 利率的时间单位为自然日 - 使用当前时刻的 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
- 计算 tenor
t= max((T-dayfrac)/365.0, 1e-10), tenor 单位为自然日, 这里取 是为了防止 Eq.(2) 除以0导致报错
例如, 对于交易日 2024-08-27, ten_sec_seq==3500, 按照到期日由近到远排序的前4个沪深300指数期货合约 IF 到期数据表格为:
| 代码 | 到期日 | 交易日 | ten_sec_seq |
|---|---|---|---|
| IF2409 | 2024-09-20 | 2024-08-27 | 3500 |
| IF2410 | 2024-10-18 | 2024-08-27 | 3500 |
| IF2412 | 2024-12-20 | 2024-08-27 | 3500 |
| IF2503 | 2025-03-21 | 2025-08-27 | 3500 |
得到4个合约对应的 T 为 25, 53, 116, 207 天. ten_sec_seq==3500 对应的 day_frac 为 0.05555555555555555, 可得4个期货合约对应的 tenor 和利率如下表:
| 代码 | 到期日 | 交易日 | T | ten_sec_seq | tenor | 利率 |
|---|---|---|---|---|---|---|
| IF2409 | 2024-09-20 | 2024-08-27 | 25 | 3500 | 0.068341 | 0.0192 |
| IF2410 | 2024-10-18 | 2024-08-27 | 53 | 3500 | 0.145053 | 0.0188 |
| IF2412 | 2024-12-20 | 2024-08-27 | 116 | 3500 | 0.317656 | 0.0185 |
| IF2503 | 2025-03-21 | 2024-08-27 | 207 | 3500 | 0.566971 | 0.0182 |
implied yiled 计算方法
拟合该二次曲线所使用的数据点 (t_i, q_i) 的计算方式为: 使用期货合约价格 , 期货合约的 tenor , 利率 , index 的价格 , 计算implied yield : 公式如下:
其中利率 使用年化值.
例如, 沪深300股指期货 IF 和沪深300指数 000300.SH 在某个时间切片下的价格数据表为:
| 代码 | 期货价格 | 指数价格 | 期货tenor | 无风险利率 |
|---|---|---|---|---|
| IF2409 | 3312.8 | 3315.686 | 0.0685 | 0.0192 |
| IF2410 | 3312.0 | 3315.686 | 0.1452 | 0.0188 |
| IF2412 | 3307.4 | 3315.686 | 0.3178 | 0.0185 |
| IF2503 | 3302.4 | 3315.686 | 0.5671 | 0.0182 |
使用公式 (2) 计算可得4个期货合约对应 implied yield 为
汇总为:
| 代码 | 期货价格 | 指数价格 | 期货tenor | 无风险利率 | implied yiled |
|---|---|---|---|---|---|
| IF2409 | 3312.8 | 3315.686 | 0.0685 | 0.0192 | 0.0319 |
| IF2410 | 3312.0 | 3315.686 | 0.1452 | 0.0188 | 0.0265 |
| IF2412 | 3307.4 | 3315.686 | 0.3178 | 0.0185 | 0.0264 |
| IF2503 | 3302.4 | 3315.686 | 0.5671 | 0.0182 | 0.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
};
调用方法
- 使用构造函数
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].
- 创建一个类型为
std::vector<TermStructureSlice>对象:
std::vector<TermStructureSlice> priceData;
- 使用10s切片的期货数据和指数数据计算当前时刻的Yield
q, 将数据以数据结构TermStructureSlice格式push_back到priceData中:
// 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个合约
- 调用
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的数据
- 使用拟合的参数
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()的输入termStructureDataDataFrame 格式为6列:
| column number | column name | dtype | meaning |
|---|---|---|---|
| 0 | code | str | underlying Future Symbol |
| 1 | fut | float | underlying Future price |
| 2 | ten_sec_seq | int32 | 10s切片时间 |
| 3 | ind | float | underlying spot price |
| 4 | ir | float | interest rate |
| 5 | t | float | future tenor |
| 6 | q | float | future yield |
注意:
- 每一 column number 的 column name 可以不一样,但 dtype 和 meanning 必须一致
- 不建议在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)
编译与安装
- 本地 clone project
mkdir build && cd build- config cmake (以 conda 环境名为 'py_3_11_13' 为例)
- activate conda envs:
conda activate py_3_11_13 - config cmake at
builddirectory
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,而非其他地方
- activate conda envs:
make && make install- 检查安装成功
(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()