python代码改写_QuantLib 金融计算——C++ 代码改写成 Python 程序的一些经验

QuantLib 金融计算——C++ 代码改写成 Python 程序的一些经验

概述

Python 在科学计算、数据分析和可视化等方面已经形成了非常强大的生态。而且,作为一门时尚的脚本语言,易学易用。因此,对于量化分析和风险管理的从业者来说,将某些 QuantLib 的历史代码转换成 Python 程序是一件值得尝试的工作。

Python 本身的面向对象机制非常完善,借助 SWIG 的包装,由 C++ 代码转换而成的 Python 程序基本上可以完整地保留原本的类架构。对于用户来说,应用层面的历史代码几乎可以平行的进行移植,只需稍加修改即可。

本文将以 QuantLib 官方网站上的 EquityOption.cpp 为例,展示如何将应用层面的 C++ 代码转换成 Python 程序,并总结出一般的转换方法和注意事项。

4ef1b99c1baa0754b7894a2cb9c89f58.png

将 C++ 代码改写成 Python 程序

下面,我将逐句把 C++ 代码改写成 Python 程序。

C++ 代码:

#include

#ifdef BOOST_MSVC

# include

#endif

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

using namespace QuantLib;

#if defined(QL_ENABLE_SESSIONS)

namespace QuantLib {

Integer sessionId() { return 0; }

}

#endif

Python 代码:

import QuantLib as ql

import prettytable as pt

首先,引入必要的模块,对 C++ 来说是一组头文件。Python 的优势显而易见。

C++ 代码:

// set up dates

Calendar calendar = TARGET();

Date todaysDate(15, May, 1998);

Date settlementDate(17, May, 1998);

Settings::instance().evaluationDate() = todaysDate;

Python 代码:

# set up dates

calendar = ql.TARGET()

todaysDate = ql.Date(15, ql.May, 1998)

settlementDate = ql.Date(17, ql.May, 1998)

ql.Settings.instance().evaluationDate = todaysDate

C++ 中对象的声明有两种常见的方式:

BaseClass object = Class(...),其中 Class 可以是 BaseClass 本身,或者其派生类。示例中的 TARGET 正是 Calendar 的派生类;

Class object(...)。

Python 中无需声明对象类型,而是以赋值的形式创建一个对象,所以对于上述两类格式的代码,统一改写成 object = Class(...)。

经验 1:对象声明语句 BaseClass object = Class(...) 和 Class object(...) 统一改写成 object = Class(...)。

Settings 是 QuantLib 中的一个“单体模式”的实现,通常用来为整个程序设置统一的估值日期,几乎每个应用程序中都会出现。通过调用 Settings 的静态方法 instance(),用户可以修改单体实例的某些属性,其中 evaluationDate() 方法可以把存储估值日期的成员变量地址暴露出来,让用户进行设置。

不过,Python 中的类没有 :: 运算符,类的方法也不能暴露成员变量的地址。所以,原本的静态方法一律通过 . 运算符调用,同时 evaluationDate() 方法被重定义为类的 property,这就是为什么 Python 语句中 evaluationDate 后面没有 ()。注意,instance() 后面的 () 不能丢。

经验 2:用来对 Settings::instance() 进行配置的成员函数,例如 evaluationDate(),在 Python 中以类的 property 形式出现,不过名称不变。

C++ 代码:

// our options

Option::Type type(Option::Put);

Real underlying = 36;

Real strike = 40;

Spread dividendYield = 0.00;

Rate riskFreeRate = 0.06;

Volatility volatility = 0.20;

Date maturity(17, May, 1999);

DayCounter dayCounter = Actual365Fixed();

Python 代码:

# our options

optType = ql.Option.Put

underlying = 36.0

strike = 40.0

dividendYield = 0.00

riskFreeRate = 0.06

volatility = 0.20

maturity = ql.Date(17, ql.May, 1999)

dayCounter = ql.Actual365Fixed()

C++ 中类内部枚举类型的对象声明和类对象声明相似,采用 Class::Enum object(Class::element) 的形式。枚举元素本质上是一些整数常量。

SWIG 在包装 QuantLib 的 Python 接口时会把 C++ 类内部的枚举类型转换成 Python 类中的公有属性,其值依然是一些整数值。所以,枚举类型对象的声明就直接改写成赋值语句。因此,Class::Enum object(Class::element) 语句统一改写成 object = Class.element。

示例中的 Type 是 Option 类内部的一个枚举型,而 Put 是 Type 中的一个元素,另一个是 Call。因为 type 是 Python 的关键字,改写时一定要重命名。

经验 3:对于类中的枚举类型,Class::Enum object(Class::element) 语句统一改写成 object = Class.element。

对于基本类型(整数、浮点数、字符、字符串)来说,改写非常容易。由于 Python 无需声明类型,Type object = value 语句统一改写成赋值语句——object = value。

经验 4:对于基本类型,Type object = value 语句统一改写成 object = value。

C++ 代码:

std::cout << "Option type = " << type << std::endl;

std::cout << "Maturity = " << maturity << std::endl;

std::cout << "Underlying price = " << underlying << std::endl;

std::cout << "Strike = " << strike << std::endl;

std::cout << "Risk-free interest rate = " << io::rate(riskFreeRate) << std::endl;

std::cout << "Dividend yield = " << io::rate(dividendYield) << std::endl;

std::cout << "Volatility = " << io::volatility(volatility) << std::endl;

std::cout << std::endl;

std::string method;

std::cout << std::endl ;

// write column headings

Size widths[] = { 35, 14, 14, 14 };

std::cout << std::setw(widths[0]) << std::left << "Method"

<< std::setw(widths[1]) << std::left << "European"

<< std::setw(widths[2]) << std::left << "Bermudan"

<< std::setw(widths[3]) << std::left << "American"

<< std::endl;

Python 代码:

print('Option type =', optType)

print('Maturity =', maturity)

print('Underlying price =', underlying)

print('Strike =', strike)

print('Risk-free interest rate =', '{0:%}'.format(riskFreeRate))

print('Dividend yield =', '{0:%}'.format(dividendYield))

print('Volatility =', '{0:%}'.format(volatility))

print()

# show table

tab = pt.PrettyTable(['Method', 'European', 'Bermudan', 'American'])

字符串输出部分没什么好说的,我使用了 prettytable 包来美化输出结果。

C++ 代码:

std::vector exerciseDates;

for (Integer i = 1; i <= 4; i++)

exerciseDates.push_back(settlementDate + 3 * i * Months);

Python 代码:

exerciseDates = ql.DateVector()

for i in range(1, 5):

exerciseDates.push_back(settlementDate + ql.Period(3 * i, ql.Months))

Python 本身没有“模板”的概念,因此 SWIG 只能对模板的实例化进行包装(模板的实例化就是一个具体的类),进而得到一些 Python 类。对于某些常用类型,例如 Date,QuantLib 的 Python 接口包装了对应的 std::vector 模板的实例化,包装后得到的 Python 类有一致的命名格式——ClassVector,对于 std::vector 而言就是 DateVector。

因为模板的实例化实际上就是一个具体的类,因此,这部分代码的改写方法遵循经验 1。

和 C++ 完全不同,Python 不是一个“强类型”的语言,在改写涉及隐式转换的代码时要格外注意。Months 是 QuantLib 中的枚举类型 TimeUnit 的元素,SWIG 在包装枚举类型时会将元素转换成 Python 中的整数,丢失了 TimeUnit 的类型信息。由于 Python 不是强类型的,被包装的枚举类型会丢失类型信息,因此,3 * i * Months 在 C++ 中可以顺利地隐式转换成一个 Period 对象——Period(

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值