数学工具


求解器

QuantLib 提供了多种一维求解器类型,用于求解单参数函数的根。

f(x) = 0

其中 f: R \to R 是定义在实数域上的函数。

QuantLib提供的求解器类型包括:

  • 布伦特

  • 二分法

  • 正割法

  • 里德尔

  • 牛顿法(需要成员函数derivative,计算导数)

  • FalsePosition

这些求解器的构造函数是默认构造函数,不接受参数。 例如,构建Brent求解器实例的语句如下:

mySolv = Brent()

有两种方式可以调用求解器的成员函数:

mySolv.solve(f, accuracy, guess, step)
mySolv.solve(f, accuracy, guess, xMin, xMax)

f

单参数函数或函数对象,返回值为浮点数

accuracy

浮点数,表示用于停止计算时的求解精度

guess

浮点数,根的初始猜测值

step

浮点数。在第一种调用方法中,根的范围没有限制。算法需要自行搜索来确定范围。step指定搜索算法的步长。

xMin, xMax

浮点数,左右区间范围

ql.Settings.instance().evaluationDate = ql.Date(15,6,2020)
crv = ql.FlatForward(2, ql.TARGET(), 0.05, ql.Actual360())
yts = ql.YieldTermStructureHandle(crv)
engine = ql.DiscountingSwapEngine(yts)

schedule = ql.MakeSchedule(ql.Date(15,9,2020), ql.Date(15,9,2021), ql.Period('6M'))
index = ql.Euribor3M(yts)
floatingLeg = ql.IborLeg([100], schedule, index)

def swapFairRate(rate):
    fixedLeg = ql.FixedRateLeg(schedule, ql.Actual360(), [100.], [rate])
    swap = ql.Swap(fixedLeg, floatingLeg)
    swap.setPricingEngine(engine)
    return swap.NPV()

solver = ql.Brent()

accuracy = 1e-5
guess = 0.0
step = 0.001
solver.solve(swapFairRate, accuracy, guess, step)

集成

高斯求积法

高斯求积法用于计算特定区间上的积分。例如,高斯-勒让德方法计算区间[-1,1]上的定积分。

import numpy as np
import QuantLib as ql

f = lambda x: x**2
g = lambda x: np.exp(x)

quad_ql_legendre = ql.GaussLegendreIntegration(128)
quad_ql_legendre(f), quad_ql_legendre(g)

Scipy也有一个我们可以比较的实现:

from scipy.integrate import quad
quad(f, -1, 1)[0], quad(g, -1, 1)[0]

Scipy要求一个区间[a,b]来进行运算。我们可以通过使用包装函数缩放输入参数,在quantlib中实现相同的功能:

def quad_ql_ab(f, a, b, quad):
    multiplier, ratio = (b+a) / 2, (b-a) / 2
    y = lambda x: f(ratio*x + multiplier)
    return quad(y) * ratio

quad_ql = ql.GaussLegendreIntegration(128)
quad_ql_ab(f, -2, 2, quad_ql), quad_ql_ab(g, -2, 2, quad_ql), quad(f, -2, 2)[0], quad(g, -2, 2)[0]

其他支持的求积方法包括:

  • 高斯-勒让德积分,

  • 高斯-切比雪夫积分,

  • 高斯-切比雪夫第二类积分,

  • 高斯-拉盖尔积分,

  • 高斯-埃尔米特积分,

  • GaussJacobiIntegration,

  • 高斯双曲积分,

  • 高斯-盖根鲍尔积分

The intervals and additional parameters for each quadrature varies (see eg. https://mathworld.wolfram.com/GaussianQuadrature.html)


插值

插值是量化金融中最常用的工具之一。标准应用场景包括收益率曲线的插值、波动率微笑曲线的插值以及波动率曲面的插值。quantlib-python提供了以下一维和二维插值方法:

XXXInterpolation(x, y)
  • x : 浮点数序列,多个离散参数

  • y : 浮点数序列,表示与参数x对应的函数值,长度与x相同

插值类定义了__call__方法。插值类对象的使用方式如下,可以像函数一样调用

i(x, allowExtrapolation)
  • x : 浮点数,需要插值的点

  • allowExtrapolation : boolean。将allowExtrapolation设置为True表示允许外推。默认值为False。

一维插值方法

  • LinearInterpolation (一维)

  • LogLinearInterpolation (一维)

  • CubicInterpolation (一维)

  • LogCubicInterpolation (一维)

  • ForwardFlatInterpolation (一维)

  • BackwardFlatInterpolation (一维)

  • LogParabolicInterpolation (一维)

import QuantLib as ql
import numpy as np
import matplotlib.pyplot as plt

X = [1., 2., 3., 4., 5.]
Y = [0.5, 0.6, 0.7, 0.8, 0.9]

methods = {
    'Linear Interpolation': ql.LinearInterpolation(X, Y),
    'LogLinearInterpolation': ql.LogLinearInterpolation(X, Y),
    'CubicNaturalSpline': ql.CubicNaturalSpline(X, Y),
    'LogCubicNaturalSpline': ql.LogCubicNaturalSpline(X, Y),
    'ForwardFlatInterpolation': ql.ForwardFlatInterpolation(X, Y),
    'BackwardFlatInterpolation': ql.BackwardFlatInterpolation(X, Y),
    'LogParabolic': ql.LogParabolic(X, Y)
}

xx = np.linspace(1, 10)
fig = plt.figure(figsize=(15,4))
plt.scatter(X, Y, label='Original Data')
for name, i in methods.items():
    yy = [i(x, allowExtrapolation=True) for x in xx]
    plt.plot(xx, yy, label=name);
plt.legend();

二维插值方法

  • 双线性插值 (二维)

  • BicubicSpline (二维)

import pandas as pd
X = [1., 2., 3., 4., 5.]
Y = [0.6, 0.7, 0.8, 0.9]
Z = [[(x-3)**2 + y for x in X] for y in Y]
df = pd.DataFrame(Z, columns=X, index=Y)

i = ql.BilinearInterpolation(X, Y, Z)

XX = np.linspace(0, 5, 9)
YY = np.linspace(0.55, 1.0, 10)

extrapolated = pd.DataFrame(
    [[i(x,y, True) for x in XX] for y in YY],
    columns=XX,
    index=YY)


from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
from matplotlib import cm

fig = plt.figure()
ax = fig.gca(projection='3d')
ax.set_title("Surface Interpolation")

Xs, Ys = np.meshgrid(XX, YY)
surf = ax.plot_surface(
    Xs, Ys, extrapolated, rstride=1, cstride=1, cmap=cm.coolwarm
)
fig.colorbar(surf, shrink=0.5, aspect=5);

优化


随机数生成器

Quantlib-Python 提供以下三种均匀分布的(伪)随机数生成器:

  • ql.KnuthUniformRng, Knuth算法

  • ql.LecuyerUniformRng, L'Ecuyer算法

  • ql.MersenneTwisterUniformRng,著名的Mersenne-Twister算法

随机数生成器的构造函数,

RandomNumberGenerator(seed)

其中seed是一个整数,默认值为0,用作初始化相应确定性序列的种子

随机数生成器的成员函数:

  • next() : 返回一个SampleNumber对象作为模拟的结果。

r = rng.next()
v = r.value(r)

用户通过重复调用成员函数next()获取一系列随机数。 需要注意的是,r的类型为SampleNumber,对应的浮点数需要通过调用value()来获取。

在随机模拟中最常见的分布是正态分布。quantlib-python提供了四种类型的正态分布随机数生成器:

  • CentralLimit[X]GaussianRng

  • BoxMuller[X]GaussianRng

  • MoroInvCumulative[X]GaussianRng

  • InvCumulative[X]GaussianRng

其中[X]指代均匀随机数生成器。

具体来说,有12种类型的4种生成器:

  • CentralLimitLecuyerGaussianRng

  • CentralLimitKnuthGaussianRng

  • CentralLimitMersenneTwisterGaussianRng

  • BoxMullerLecuyerGaussianRng

  • BoxMullerKnuthGaussianRng

  • BoxMullerMersenneTwisterGaussianRng

  • Moro逆累积Lecuyer高斯随机数生成器

  • Moro逆累积Knuth高斯随机数生成器

  • MoroInvCumulativeMersenneTwisterGaussianRng

  • 逆累积Lecuyer高斯随机数生成器

  • InvCumulativeKnuthGaussianRng

  • InvCumulativeMersenneTwisterGaussianRng

随机数生成器的构造函数:

GaussianRandomNumberGenerator(RandomNumberGenerator)

BoxMullerMersenneTwisterGaussianRng 分布式随机数生成器接受对应的均匀分布随机数生成器作为输入源。

BoxMullerMersenneTwisterGaussianRng为例,您需要配置一个MersenneTwisterUniformRng对象作为随机数源,并使用经典的Box-Muller算法来获取正态分布的随机数。

seed = 12324
unifMt = ql.MersenneTwisterUniformRng(seed)
bmGauss = ql.BoxMullerMersenneTwisterGaussianRng(unifMt)

for i in range(5):
    print(bmGauss.next().value())

与之前描述的“伪”随机数相比,随机模拟中另一种重要的随机数类型是“准”随机数,也称为低偏差序列。由于具有更好的收敛性,准随机数常用于高维随机变量的模拟。quantlib-python提供了两种准随机数类型,

  • HaltonRsg : Halton序列

  • SobolRsg : Sobol序列

HaltonRsg

HaltonRsg(dimensionality, seed, randomStart, randomShift)

其中,

  • dimensionality : 整数,设置维度;

  • seed : 一个整数,默认值为0,用作初始化相应确定性序列的种子;

  • randomStart : boolean, 默认为 True, 是否随机开始;

  • randomShift : Boolean, 默认为 False , 是否随机偏移。

HaltonRsg的成员函数,

  • nextSequence() : 返回一个SampleRealVector对象作为模拟结果;

  • lastSequence() : 返回一个SampleRealVector对象作为前一次模拟的结果;

  • dimension() : 返回维度。

SobolRsg

SobolRsg(dimensionality, seed, directionIntegers=Jaeckel)

其中,

  • dimensionality : 整数,设置维度;

  • seed : 一个整数,默认值为0,用作初始化相应确定性序列的种子;

  • directionIntegers : quantlib-python的内置变量。默认值为SobolRsg.Jaeckel,用于初始化Sobol序列。

SobolRsg的成员函数,

  • nextSequence() : 返回一个SampleRealVector对象作为模拟结果;

  • lastSequence() : 返回一个SampleRealVector对象作为前一次模拟的结果;

  • dimension() : 返回维度。

  • skipTo(n) : n为整数,跳转到采样结果的第n个维度;

  • nextInt32Sequence() : 返回一个IntVector对象。


路径生成器

QuantLib 提供了多个封装函数,用于从给定的随机过程生成样本路径

高斯多路径生成器

使用伪随机数从任意随机过程生成路径

ql.GaussianMultiPathGenerator(stochasticProcess, times, sequenceGenerator, brownianBridge=False)
timestep, length, numPaths = 24, 2, 2**15

today = ql.Date().todaysDate()
riskFreeTS = ql.YieldTermStructureHandle(ql.FlatForward(today, 0.05, ql.Actual365Fixed()))
dividendTS = ql.YieldTermStructureHandle(ql.FlatForward(today, 0.01, ql.Actual365Fixed()))
initialValue = ql.QuoteHandle(ql.SimpleQuote(100))

v0, kappa, theta, rho, sigma = 0.005, 0.8, 0.008, 0.2, 0.1
hestonProcess = ql.HestonProcess(riskFreeTS, dividendTS, initialValue, v0, kappa, theta, sigma, rho)

times = ql.TimeGrid(length, timestep)
dimension = hestonProcess.factors()

rng = ql.UniformRandomSequenceGenerator(dimension * timestep, ql.UniformRandomGenerator())
sequenceGenerator = ql.GaussianRandomSequenceGenerator(rng)
pathGenerator = ql.GaussianMultiPathGenerator(hestonProcess, list(times), sequenceGenerator, False)

# paths[0] will contain spot paths, paths[1] will contain vol paths
paths = [[] for i in range(dimension)]
for i in range(numPaths):
    samplePath = pathGenerator.next()
    values = samplePath.value()
    spot = values[0]

    for j in range(dimension):
        paths[j].append([x for x in values[j]])

GaussianSobolMultiPathGenerator

使用低差异数从任意随机过程生成路径

ql.GaussianSobolMultiPathGenerator(stochasticProcess, times, sequenceGenerator, brownianBridge=False)
timestep, length, numPaths = 24, 2, 2**15

today = ql.Date().todaysDate()
riskFreeTS = ql.YieldTermStructureHandle(ql.FlatForward(today, 0.05, ql.Actual365Fixed()))
dividendTS = ql.YieldTermStructureHandle(ql.FlatForward(today, 0.01, ql.Actual365Fixed()))
initialValue = ql.QuoteHandle(ql.SimpleQuote(100))

v0, kappa, theta, rho, sigma = 0.005, 0.8, 0.008, 0.2, 0.1
hestonProcess = ql.HestonProcess(riskFreeTS, dividendTS, initialValue, v0, kappa, theta, sigma, rho)

times = ql.TimeGrid(length, timestep)
dimension = hestonProcess.factors()

rng = ql.UniformLowDiscrepancySequenceGenerator(dimension * timestep)
sequenceGenerator = ql.GaussianLowDiscrepancySequenceGenerator(rng)
pathGenerator = ql.GaussianSobolMultiPathGenerator(hestonProcess, list(times), sequenceGenerator, False)

# paths[0] will contain spot paths, paths[1] will contain vol paths
paths = [[] for i in range(dimension)]
for i in range(numPaths):
    samplePath = pathGenerator.next()
    values = samplePath.value()
    spot = values[0]

    for j in range(dimension):
        paths[j].append([x for x in values[j]])

统计


惯例计算器

BlackDelta计算器

一个计算器类,用于计算外汇风格delta-期限-波动率报价的相关执行价(参见Reiswich D., Wystup U., 2010. A Guide to FX Options Quoting Conventions

ql.BlackDeltaCalculator(optionType, deltaType, spot, domesticDcf, foreignDcf, volRootT)
import numpy as np

today = ql.Date().todaysDate()
dayCounter = ql.Actual365Fixed()
spot = 100
rd, rf = 0.02, 0.05

ratesTs = ql.YieldTermStructureHandle(ql.FlatForward(today, rd, dayCounter))
dividendTs = ql.YieldTermStructureHandle(ql.FlatForward(today, rf, dayCounter))

# Details about the delta quote
optionType = ql.Option.Put
vol = 0.07
maturity = 1.0
deltaType = ql.DeltaVolQuote.Fwd      # Also supports: Spot, PaSpot, PaFwd

# Set up the calculator
localDcf, foreignDcf = ratesTs.discount(maturity), dividendTs.discount(maturity)
stdDev = np.sqrt(maturity) * vol
calc = ql.BlackDeltaCalculator(optionType, deltaType, spot, localDcf, foreignDcf, stdDev)

计算给定看涨/看跌期权delta对应的执行价(看跌期权的delta为负值)

delta = -0.3
calc.strikeFromDelta(delta)

或者如果这是一个ATM报价,请指定ATM惯例

atmType = ql.DeltaVolQuote.AtmFwd     # Also supports: AtmSpot, AtmDeltaNeutral, AtmVegaMax, AtmGammaMax, AtmPutCall50
calc.atmStrike(atmType)