zoukankan      html  css  js  c++  java
  • QuantLib 金融计算——数学工具之优化器

    如果未做特别说明,文中的程序都是 Python3 代码。

    QuantLib 金融计算——数学工具之优化器

    载入模块

    import QuantLib as ql
    import scipy
    
    print(ql.__version__)
    
    1.12
    

    概述

    在量化金融的模型校准过程中,最重要的工具是对函数 (f : R^n o R) 的优化器。通常遇到的最优化问题是一个最小二乘问题。例如,寻找一个模型的参数使得某些损失函数最小化。

    quantlib-python 中的最优化计算委托给 Optimizer 类,用户需要配置合适的参数以描述最优化问题,需要注意的是 Optimizer 对象默认求解的是某个函数“最小化”问题。

    Optimizer

    Optimizer 类的构造函数不接受参数,求解最优化问题的方式也非常简单,仅需调用 solve 函数即可:

    solve(function,
          c,
          m,
          e,
          iv)
    
    • function:函数或函数对象,返回一个浮点数,所接受的参数是若干独立的浮点数;
    • cConstraint 对象,描述优化问题的约束条件;
    • mOptimizationMethod 对象,优化算法引擎;
    • eEndCriteria 对象,描述优化问题的终止条件;
    • ivArray 对象,优化计算的初始值。

    solve 函数返回一个 Array 对象,存储找到的最小值点。

    Constraint

    quantlib-python 提供的具体约束条件均继承自 Constraint 类,有如下几种:

    • NoConstraint:无约束
    • PositiveConstraint:要求所有参数为正数
    • BoundaryConstraint:要求所有参数在某个区间内
    • CompositeConstraint:要求所有参数同时满足两个约束条件
    • NonhomogeneousBoundaryConstraint:对每个参数分别约束,要求其在某个区间内

    OptimizationMethod

    quantlib-python 提供的具体优化算法均继承自 OptimizationMethod 类,有如下几种:

    • LevenbergMarquardt:Levenberg-Marquardt 算法,实现基于 MINPACK;
    • Simplex:单纯形法;
    • ConjugateGradient:共轭梯度法;
    • SteepestDescent:最速下降法;
    • BFGS:Broyden-Fletcher-Goldfarb-Shanno 算法;
    • DifferentialEvolution:微分进化算法;
    • GaussianSimulatedAnnealing:高斯模拟退火算法;
    • MirrorGaussianSimulatedAnnealing:镜像高斯模拟退火算法;
    • LogNormalSimulatedAnnealing:对数高斯模拟退火算法。

    EndCriteria

    最优化计算通常是一个迭代过程,我们需要定义一个终止条件以引导最优化计算结束,否则可能一直计算下去。终止条件由 EndCriteria 类参数化,其构造函数如下

    EndCriteria(maxIteration,
                maxStationaryStateIterations,
                rootEpsilon,
                functionEpsilon,
                gradientNormEpsilon)
    
    • maxIteration:整数,最大迭代次数;
    • maxStationaryStateIterations:整数,稳定点(函数值和根同时稳定)的最大迭代次数;
    • rootEpsilon:浮点数,当前根与最新根的绝对差小于 rootEpsilon 时停止计算;
    • functionEpsilon:浮点数,当前函数值与最新函数值的绝对差小于 functionEpsilon 时停止计算;
    • gradientNormEpsilon:浮点数,当前梯度与最新梯度差的范数小于 gradientNormEpsilon 时停止计算;

    注意,对于每种优化器来讲,并不是所有参数多是必须的。

    示例

    Rosenbrock 问题

    我们以 Rosenbrock 函数(也简称为香蕉函数)为例测试优化器,这是一个经典的优化问题。函数定义如下:

    [f(x,y) = (1-x)^2 + 100(y-x^2)^2 ]

    最小值点落在 ((x,y)=(1, 1)),此时的函数值 (f(x,y)=0)

    首先定义 Rosenbrock 函数,注意,每个参数是独立的浮点数。

    def RosenBrockFunction(x0, x1):
        res = (1 - x0) * (1 - x0) + 100.0 * (x1 - x0 * x0) * (x1 - x0 * x0)
    
        return res
    

    接着,配置优化器,并测试 SimplexConjugateGradient 算法。初始值设定为 ((x, y) = (0.1, 0.1)),最优化类型为“无约束”的。

    例子 1

    def testOptimizer1():
        maxIterations = 1000
        minStatIterations = 100
        rootEpsilon = 1e-8
        functionEpsilon = 1e-9
        gradientNormEpsilon = 1e-5
    
        myEndCrit = ql.EndCriteria(
            maxIterations,
            minStatIterations,
            rootEpsilon,
            functionEpsilon,
            gradientNormEpsilon)
    
        constraint = ql.NoConstraint()
    
        solver1 = ql.Simplex(0.1)
        solver2 = ql.ConjugateGradient()
    
        minimize = ql.Optimizer()
    
        min1 = minimize.solve(
            function=RosenBrockFunction,
            c=constraint,
            m=solver1,
            e=myEndCrit,
            iv=ql.Array(2, 0.1))
    
        min2 = minimize.solve(
            function=RosenBrockFunction,
            c=constraint,
            m=solver2,
            e=myEndCrit,
            iv=ql.Array(2, 0.1))
    
        print('{0:<30}{1}'.format('Root Simplex', min1))
        print('{0:<30}{1}'.format('Root ConjugateGradient', min2))
        print('{0:<40}{1}'.format(
            'Min F Value Simplex',
            RosenBrockFunction(min1[0], min1[1])))
        print('{0:<40}{1}'.format(
            'Min F Value ConjugateGradient',
            RosenBrockFunction(min2[0], min2[1])))
    
    
    testOptimizer1()
    
    Root Simplex                  [ 1; 1 ]
    Root ConjugateGradient        [ 0.998904; 0.995025 ]
    Min F Value Simplex                     2.929205541302239e-17
    Min F Value ConjugateGradient           0.0007764961476745887
    

    校准问题

    下面虚拟一个模型校准问题。假设已知 4 个看涨期权的价格 (C_1 , C_2 , C_3 , C_4),以及对应的敲定价 (K_i),未知量是股票价格 (S_0) 和波动率 (sigma),通过解决下面的最小二乘问题来求解出 ((sigma, S_0))

    [f(sigma, S_0) = sum_{i=1}^4 (C(K_i, sigma, S_0) - C_i)^2 ]

    首先定义损失函数(函数对象),

    class CallProblemFunction(object):
        def __init__(self,
                     rd, rf, tau, phi,
                     K1, K2, K3, K4,
                     C1, C2, C3, C4):
            self.rd_ = rd
            self.rf_ = rf
            self.tau_ = tau
            self.phi_ = phi
            self.K1_ = K1
            self.K2_ = K2
            self.K3_ = K3
            self.K4_ = K4
            self.C1_ = C1
            self.C2_ = C2
            self.C3_ = C3
            self.C4_ = C4
    
        @staticmethod
        def blackScholesPrice(spot, strike,
                              rd, rf,
                              vol, tau,
                              phi):
            domDf = scipy.exp(-rd * tau)
            forDf = scipy.exp(-rf * tau)
            fwd = spot * forDf / domDf
            stdDev = vol * scipy.sqrt(tau)
    
            dp = (scipy.log(fwd / strike) + 0.5 * stdDev * stdDev) / stdDev
            dm = (scipy.log(fwd / strike) - 0.5 * stdDev * stdDev) / stdDev
    
            res = phi * domDf * (fwd * norm.cdf(phi * dp) - strike * norm.cdf(phi * dm))
    
            return res
    
        def values(self,
                   x0,
                   x1):
            res = ql.Array(4)
            res[0] = self.blackScholesPrice(
                x0, self.K1_, self.rd_, self.rf_, x1, self.tau_, self.phi_) - self.C1_
            res[1] = self.blackScholesPrice(
                x0, self.K2_, self.rd_, self.rf_, x1, self.tau_, self.phi_) - self.C2_
            res[2] = self.blackScholesPrice(
                x0, self.K3_, self.rd_, self.rf_, x1, self.tau_, self.phi_) - self.C3_
            res[3] = self.blackScholesPrice(
                x0, self.K4_, self.rd_, self.rf_, x1, self.tau_, self.phi_) - self.C4_
    
            return res
    
        def __call__(self,
                     x0,
                     x1):
            tmpRes = self.values(x0, x1)
    
            res = tmpRes[0] * tmpRes[0]
            res += tmpRes[1] * tmpRes[1]
            res += tmpRes[2] * tmpRes[2]
            res += tmpRes[3] * tmpRes[3]
    
            return res
    

    例子 2

    def testOptimizer2():
        spot = 98.51
        vol = 0.134
        K1 = 87.0
        K2 = 96.0
        K3 = 103.0
        K4 = 110.0
        rd = 0.002
        rf = 0.01
        phi = 1
        tau = 0.6
    
        C1 = CallProblemFunction.blackScholesPrice(
            spot, K1, rd, rf, vol, tau, phi)
        C2 = CallProblemFunction.blackScholesPrice(
            spot, K2, rd, rf, vol, tau, phi)
        C3 = CallProblemFunction.blackScholesPrice(
            spot, K3, rd, rf, vol, tau, phi)
        C4 = CallProblemFunction.blackScholesPrice(
            spot, K4, rd, rf, vol, tau, phi)
    
        optFunc = CallProblemFunction(
            rd, rf, tau, phi, K1, K2, K3, K4, C1, C2, C3, C4)
    
        maxIterations = 1000
        minStatIterations = 100
        rootEpsilon = 1e-5
        functionEpsilon = 1e-5
        gradientNormEpsilon = 1e-5
    
        myEndCrit = ql.EndCriteria(
            maxIterations,
            minStatIterations,
            rootEpsilon,
            functionEpsilon,
            gradientNormEpsilon)
    
        startVal = ql.Array(2)
        startVal[0] = 80.0
        startVal[1] = 0.20
    
        constraint = ql.NoConstraint()
        solver = ql.BFGS()
    
        minimize = ql.Optimizer()
    
        min1 = minimize.solve(
            function=optFunc,
            c=constraint,
            m=solver,
            e=myEndCrit,
            iv=startVal)
    
        print('Root', min1)
        print('Min Function Value', optFunc(min1[0], min1[1]))
    
    Root [ 98.51; 0.134 ]
    Min Function Value 5.979965971506814e-22
    
  • 相关阅读:
    50种方法优化SQL Server
    VS2015在Windows 10 下面安装经验
    python中yield的用法详解——最简单,最清晰的解释
    Mac安装Allure
    Python之pymysql数据库操作
    Python操作Excel神器-openpyxl之写入
    Python OS 模块处理路径
    Python操作Excel神器-openpyxl之读取
    Python中的if __name__ == '__main__'
    Python元组常用方法及汇总
  • 原文地址:https://www.cnblogs.com/xuruilong100/p/9919091.html
Copyright © 2011-2022 走看看