目錄
- QuantLib 金融計算——自己動手封裝 Python 接口(1)
- 概述
- QuantLib 如何封裝 Python 接口?
- 自己封裝 Python 接口
- 封裝 Array 和 Matrix 類
- QuantLibEx 和官方包混合使用
- 附錄:接口檔案、setup.py 和 init.py
- quantlibex.i
- ql.i
- types.i
- common.i
- linearalgebra.i
- setup.py
- init.py
- 擴充閱讀
QuantLib 已經開始在 PyPi 上釋出封裝好的 Python 接口,安裝和使用非常友善,與普通的包别無二緻。并且更新及時,保持對應最新版本的 QuantLib。
官方釋出的 Python 接口,其優點是廣度和全面,缺點是深度不足。有時候使用者需要的功能恰好沒有被封裝(《收益率曲線之建構曲線(3)》一文中曾經提到過),希望重新封裝接口,添加自己需要的功能;亦或是使用者已經在 C++ 源代碼層面上擴充或修複了 QuantLib,希望包裝擴充的新功能,并與官方的 Python 接口聯合使用。
無論是上述哪種情況,都需要使用者自己動手封裝 Python 接口。
QuantLib 使用 swig 來封裝 Python 接口(其他語言的接口也是用 swig 封裝的),是以,要動手封裝自己的 Python 接口需要了解一點 swig 的用法(看這裡,或這裡)。
swig 封裝 C++ 的流程大體如下:
- 編寫若幹“接口檔案”(檔案擴充名是
),告知 swig 如何封裝 C++ 源代碼;.i
- 在接口檔案上運作 swig 指令,這會産生一個
檔案(描述封裝好的 Python 接口,包含了若幹函數或類的定義),以及一個.py
檔案(接口背後的計算引擎将由該檔案生成);.cpp
- 編寫并運作
,這将編譯setup.py
檔案,并将編譯得到的.cpp
檔案(動态庫)與.so
檔案綁定起來,貫通表面的 Python 接口和背後的 C++ 計算引擎;.py
- 最終得到一個 Python 包(将包含在系統目錄)。
不同版本 QuantLib 的 swig 接口檔案可以在這裡獲得。所有接口檔案可以分為三部分:
-
是最頂端的接口檔案,swig 将依據此檔案生成接口代碼(quantlib.i
和.py
);.cpp
-
是中間層檔案,用來彙集其他接口檔案;ql.i
-
、bonds.i
等等是封裝具體接口的檔案。date.i
了解一點 swig 的原理之後會發現,swig 在封裝好的 Python 接口背後隐藏了一個個真實的 C++ 對象,實際的計算任務、類型檢查和異常處理等等其實是委托給這些 C++ 對象。
是以可以猜測,将同一段 C++ 代碼封裝成兩個不同的 Python 接口,這兩個接口應該可以混用,因為這僅僅是“同一個人穿了不同的衣服”。
下面用實驗驗證這種想法。
封裝 Array
Matrix
類
Array
Matrix
以 QuantLib 中的兩個類
Array
Matrix
為例,将它們獨立出來,封裝成名為 QuantLibEx 的包。具體的接口檔案沒有必要自己寫,直接沿用官方釋出的版本(我的名言:學習,從模仿開始)。
在官方釋出的 swig 接口檔案中,
Array
Matrix
對應的檔案是
linearalgebra.i
,該檔案同時包含(
%include
)了
common.i
types.i
兩個檔案。
将上述三個檔案連同
quantlib.i
(重命名為
quantlibex.i
)和
ql.i
獨立出來,删除掉一些和封裝 Python 接口無關的代碼,作為建構 QuantLibEx 的接口檔案。
在包含這五個接口檔案的目錄下建立一個
QuantLibEx
目錄,然後運作 swig 指令,生成必需的
.py
.cpp
檔案:
swig -c++ -python -outdir QuantLibEx -o QuantLibEx/qlx_wrap.cpp quantlibex.i
QuantLibEx
目錄下将出現兩個檔案:
QuantLibEx.py
qlx_wrap.cpp
。為了使 QuantLibEx 成為一個 Python 包,需要添加一個
__init__.py
檔案(内容見附錄)。
QuantLibEx.py
qlx_wrap.cpp
準備就緒之後就可以運作事先編寫好的
setup.py
檔案(内容見附錄),編譯
.cpp
檔案,并打包進 Python 的系統目錄。
首先,建構(
build
指令)QuantLibEx 包:
python3 setup.py build
running build
running build_py
creating build
creating build/lib.linux-x86_64-3.6
creating build/lib.linux-x86_64-3.6/QuantLibEx
copying QuantLibEx/__init__.py -> build/lib.linux-x86_64-3.6/QuantLibEx
copying QuantLibEx/QuantLibEx.py -> build/lib.linux-x86_64-3.6/QuantLibEx
running build_ext
building 'QuantLibEx._QuantLibEx' extension
creating build/temp.linux-x86_64-3.6
creating build/temp.linux-x86_64-3.6/QuantLibEx
x86_64-linux-gnu-gcc -pthread -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC -I/usr/include/ql -I/usr/include/python3.6m -c QuantLibEx/qlx_wrap.cpp -o build/temp.linux-x86_64-3.6/QuantLibEx/qlx_wrap.o
x86_64-linux-gnu-g++ -pthread -shared -Wl,-O1 -Wl,-Bsymbolic-functions -Wl,-Bsymbolic-functions -Wl,-z,relro -Wl,-Bsymbolic-functions -Wl,-z,relro -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 build/temp.linux-x86_64-3.6/QuantLibEx/qlx_wrap.o -L/usr/lib/ -lQuantLib -o build/lib.linux-x86_64-3.6/QuantLibEx/_QuantLibEx.cpython-36m-x86_64-linux-gnu.so
建構成功之後會出現一個
build
目錄,裡面包含了若幹檔案,包括已經建構好的 QuantLibEx 包。然後,進入安裝(
install
指令)環節,打包進 Python 的系統目錄(需要 sudo 權限)。
sudo python3 setup.py install
running install
running build
running build_py
running build_ext
running install_lib
copying build/lib.linux-x86_64-3.6/QuantLibEx/QuantLibEx.py -> /usr/local/lib/python3.6/dist-packages/QuantLibEx
copying build/lib.linux-x86_64-3.6/QuantLibEx/_QuantLibEx.cpython-36m-x86_64-linux-gnu.so -> /usr/local/lib/python3.6/dist-packages/QuantLibEx
byte-compiling /usr/local/lib/python3.6/dist-packages/QuantLibEx/QuantLibEx.py to QuantLibEx.cpython-36.pyc
running install_egg_info
Writing /usr/local/lib/python3.6/dist-packages/QuantLibEx-0.1.egg-info
運作
pip3 list
就可以看到 QuantLibEx 了。
...
QuantLib 1.17
QuantLibEx 0.1
...
下面簡單驗證一下 QuantLibEx(基于 QuantLib-1.15)和官方包(基于 QuantLib-1.17)是否可以混合使用:
import QuantLib as ql
import QuantLibEx as qlx
array = ql.Array(5,0.2)
print(type(array))
print(array)
arrayX = qlx.Array(5,0.3)
print(type(arrayX))
print(arrayX)
print(array + arrayX)
<class 'QuantLib.QuantLib.Array'>
[ 0.2; 0.2; 0.2; 0.2; 0.2 ]
<class 'QuantLibEx.QuantLibEx.Array'>
[ 0.3; 0.3; 0.3; 0.3; 0.3 ]
[ 0.5; 0.5; 0.5; 0.5; 0.5 ]
更複雜一點的例子——二維插值:
xVec = [float(i) for i in range(10)]
yVec = [float(i) for i in range(10)]
m = ql.Matrix(len(xVec), len(yVec))
mX = qlx.Matrix(len(xVec), len(yVec))
for rowIt in range(len(xVec)):
for colIt in range(len(yVec)):
m[rowIt][colIt] = scipy.sin(xVec[rowIt]) + scipy.sin(yVec[colIt])
mX[rowIt][colIt] = scipy.sin(xVec[rowIt]) + scipy.sin(yVec[colIt])
print(type(m))
print(m)
print(type(mX))
print(mX)
bicubIntp = ql.BicubicSpline(
xVec, yVec, m)
bicubIntpX = ql.BicubicSpline(
xVec, yVec, mX)
x = 0.5
y = 4.5
print("Analytical Value: ", scipy.sin(x) + scipy.sin(y))
print("Bicubic Value(base on ql): ", bicubIntp(x, y))
print("Bicubic Value(base on qlx): ", bicubIntpX(x, y))
<class 'QuantLib.QuantLib.Matrix'>
| 0 0.841471 0.909297 0.14112 -0.756802 -0.958924 -0.279415 0.656987 0.989358 0.412118 |
| 0.841471 1.68294 1.75077 0.982591 0.0846685 -0.117453 0.562055 1.49846 1.83083 1.25359 |
| 0.909297 1.75077 1.81859 1.05042 0.152495 -0.0496268 0.629882 1.56628 1.89866 1.32142 |
| 0.14112 0.982591 1.05042 0.28224 -0.615682 -0.817804 -0.138295 0.798107 1.13048 0.553238 |
| -0.756802 0.0846685 0.152495 -0.615682 -1.5136 -1.71573 -1.03622 -0.0998159 0.232556 -0.344684 |
| -0.958924 -0.117453 -0.0496268 -0.817804 -1.71573 -1.91785 -1.23834 -0.301938 0.030434 -0.546806 |
| -0.279415 0.562055 0.629882 -0.138295 -1.03622 -1.23834 -0.558831 0.377571 0.709943 0.132703 |
| 0.656987 1.49846 1.56628 0.798107 -0.0998159 -0.301938 0.377571 1.31397 1.64634 1.06911 |
| 0.989358 1.83083 1.89866 1.13048 0.232556 0.030434 0.709943 1.64634 1.97872 1.40148 |
| 0.412118 1.25359 1.32142 0.553238 -0.344684 -0.546806 0.132703 1.06911 1.40148 0.824237 |
<class 'QuantLibEx.QuantLibEx.Matrix'>
| 0 0.841471 0.909297 0.14112 -0.756802 -0.958924 -0.279415 0.656987 0.989358 0.412118 |
| 0.841471 1.68294 1.75077 0.982591 0.0846685 -0.117453 0.562055 1.49846 1.83083 1.25359 |
| 0.909297 1.75077 1.81859 1.05042 0.152495 -0.0496268 0.629882 1.56628 1.89866 1.32142 |
| 0.14112 0.982591 1.05042 0.28224 -0.615682 -0.817804 -0.138295 0.798107 1.13048 0.553238 |
| -0.756802 0.0846685 0.152495 -0.615682 -1.5136 -1.71573 -1.03622 -0.0998159 0.232556 -0.344684 |
| -0.958924 -0.117453 -0.0496268 -0.817804 -1.71573 -1.91785 -1.23834 -0.301938 0.030434 -0.546806 |
| -0.279415 0.562055 0.629882 -0.138295 -1.03622 -1.23834 -0.558831 0.377571 0.709943 0.132703 |
| 0.656987 1.49846 1.56628 0.798107 -0.0998159 -0.301938 0.377571 1.31397 1.64634 1.06911 |
| 0.989358 1.83083 1.89866 1.13048 0.232556 0.030434 0.709943 1.64634 1.97872 1.40148 |
| 0.412118 1.25359 1.32142 0.553238 -0.344684 -0.546806 0.132703 1.06911 1.40148 0.824237 |
Analytical Value: -0.498104579060894
Bicubic Value(base on ql): -0.49656170664824184
Bicubic Value(base on qlx): -0.49656170664824184
到目前為止,一切都能按照預期運作,自定義的封裝确實能夠和官方釋出的包混合使用。不過,類型判定有些詭異:
b = array + arrayX
c = arrayX + array
print(type(b))
print(type(c))
<class 'QuantLibEx.QuantLibEx.Array'>
<class 'QuantLibEx.QuantLibEx.Array'>
為什麼
b
c
都被判定為 QuantLibEx 中的
Array
?
附錄:接口檔案、 setup.py
__init__.py
setup.py
__init__.py
quantlibex.i
quantlibex.i
%module QuantLibEx
%include exception.i
%exception {
try {
$action
} catch (std::out_of_range& e) {
SWIG_exception(SWIG_IndexError,const_cast<char*>(e.what()));
} catch (std::exception& e) {
SWIG_exception(SWIG_RuntimeError,const_cast<char*>(e.what()));
} catch (...) {
SWIG_exception(SWIG_UnknownError,"unknown error");
}
}
//#if defined(SWIGPYTHON)
%{
#include <ql/version.hpp>
const int __hexversion__ = QL_HEX_VERSION;
const char* __version__ = QL_VERSION;
%}
const int __hexversion__;
%immutable;
const char* __version__;
%mutable;
//#endif
%include ql.i
ql.i
ql.i
//#if defined(SWIGPYTHON)
%{
#ifdef barrier
#undef barrier
#endif
%}
//#endif
%{
#include <ql/quantlib.hpp>
#if QL_HEX_VERSION < 0x011400f0
#error using an old version of QuantLib, please update
#endif
#ifdef BOOST_MSVC
#ifdef QL_ENABLE_THREAD_SAFE_OBSERVER_PATTERN
#define BOOST_LIB_NAME boost_thread
#include <boost/config/auto_link.hpp>
#undef BOOST_LIB_NAME
#define BOOST_LIB_NAME boost_system
#include <boost/config/auto_link.hpp>
#undef BOOST_LIB_NAME
#endif
#endif
// add here SWIG version check
%}
//#ifdef SWIGPYTHON
%{
#if PY_VERSION_HEX < 0x02010000
#error Python version 2.1.0 or later is required
#endif
%}
//#endif
// common name mappings
%include common.i
%include linearalgebra.i
%include types.i
types.i
types.i
#ifndef quantlib_types_i
#define quantlib_types_i
%include common.i
%include std_common.i
%{
using QuantLib::Integer;
using QuantLib::BigInteger;
using QuantLib::Natural;
using QuantLib::BigNatural;
using QuantLib::Real;
using QuantLib::Decimal;
using QuantLib::Time;
using QuantLib::Rate;
using QuantLib::Spread;
using QuantLib::DiscountFactor;
using QuantLib::Volatility;
using QuantLib::Probability;
using QuantLib::Size;
%}
typedef int Integer;
typedef long BigInteger;
typedef unsigned int Natural;
typedef unsigned long BigNatural;
typedef double Real;
typedef Real Decimal;
typedef Real Time;
typedef Real Rate;
typedef Real Spread;
typedef Real DiscountFactor;
typedef Real Volatility;
typedef Real Probability;
//#if defined(SWIGPYTHON)
// needed for those using SWIG 1.3.21 in order to compile with VC++6
%typecheck(SWIG_TYPECHECK_INTEGER) std::size_t {
$1 = (PyInt_Check($input) || PyLong_Check($input)) ? 1 : 0;
}
//#endif
typedef std::size_t Size;
#endif
common.i
common.i
#ifndef quantlib_common_i
#define quantlib_common_i
%include stl.i
%include exception.i
%define QL_TYPECHECK_BOOL 7210 %enddef
%{
// This is necessary to avoid compile failures on
// GCC 4
// see http://svn.boost.org/trac/boost/ticket/1793
#if defined(NDEBUG)
#define BOOST_DISABLE_ASSERTS 1
#endif
#include <boost/algorithm/string/case_conv.hpp>
%}
//#if defined(SWIGPYTHON)
%typemap(in) boost::optional<bool> %{
if($input == Py_None)
$1 = boost::none;
else if ($input == Py_True)
$1 = true;
else
$1 = false;
%}
%typecheck (QL_TYPECHECK_BOOL) boost::optional<bool> {
if (PyBool_Check($input) || Py_None == $input)
$1 = 1;
else
$1 = 0;
}
//#endif
%{
// generally useful classes
using QuantLib::Error;
using QuantLib::Handle;
using QuantLib::RelinkableHandle;
%}
namespace boost {
template <class T>
class shared_ptr {
public:
T* operator->();
//#if defined(SWIGPYTHON)
%extend {
bool __nonzero__() {
return !!(*self);
}
bool __bool__() {
return !!(*self);
}
}
//#endif
};
}
template <class T>
class Handle {
public:
Handle(const boost::shared_ptr<T>& = boost::shared_ptr<T>());
boost::shared_ptr<T> operator->();
//#if defined(SWIGPYTHON)
%extend {
bool __nonzero__() {
return !self->empty();
}
bool __bool__() {
return !self->empty();
}
}
//#endif
};
template <class T>
class RelinkableHandle : public Handle<T> {
public:
RelinkableHandle(const boost::shared_ptr<T>& = boost::shared_ptr<T>());
void linkTo(const boost::shared_ptr<T>&);
};
%define swigr_list_converter(ContainerRType,
ContainerCType, ElemCType)
%enddef
%define deprecate_feature(OldName, NewName)
//#if defined(SWIGPYTHON)
%pythoncode %{
def OldName(*args, **kwargs):
from warnings import warn
warn('%s is deprecated; use %s' % (OldName.__name__, NewName.__name__))
return NewName(*args, **kwargs)
%}
//#endif
%enddef
#endif
linearalgebra.i
linearalgebra.i
#ifndef quantlib_linear_algebra_i
#define quantlib_linear_algebra_i
%include common.i
%include types.i
%include stl.i
%{
using QuantLib::Array;
using QuantLib::Matrix;
%}
%define QL_TYPECHECK_ARRAY 4210 %enddef
%define QL_TYPECHECK_MATRIX 4220 %enddef
//#if defined(SWIGPYTHON)
%{
bool extractArray(PyObject* source, Array* target) {
if (PyTuple_Check(source) || PyList_Check(source)) {
Size size = (PyTuple_Check(source) ?
PyTuple_Size(source) :
PyList_Size(source));
*target = Array(size);
for (Size i=0; i<size; i++) {
PyObject* o = PySequence_GetItem(source,i);
if (PyFloat_Check(o)) {
(*target)[i] = PyFloat_AsDouble(o);
Py_DECREF(o);
} else if (PyInt_Check(o)) {
(*target)[i] = Real(PyInt_AsLong(o));
Py_DECREF(o);
} else {
Py_DECREF(o);
return false;
}
}
return true;
} else {
return false;
}
}
%}
%typemap(in) Array (Array* v) {
if (extractArray($input,&$1)) {
;
} else {
SWIG_ConvertPtr($input,(void **) &v, $&1_descriptor,1);
$1 = *v;
}
};
%typemap(in) const Array& (Array temp) {
if (extractArray($input,&temp)) {
$1 = &temp;
} else {
SWIG_ConvertPtr($input,(void **) &$1,$1_descriptor,1);
}
};
%typecheck(QL_TYPECHECK_ARRAY) Array {
/* native sequence? */
if (PyTuple_Check($input) || PyList_Check($input)) {
Size size = PySequence_Size($input);
if (size == 0) {
$1 = 1;
} else {
PyObject* o = PySequence_GetItem($input,0);
if (PyNumber_Check(o))
$1 = 1;
else
$1 = 0;
Py_DECREF(o);
}
} else {
/* wrapped Array? */
Array* v;
if (SWIG_ConvertPtr($input,(void **) &v,
$&1_descriptor,0) != -1)
$1 = 1;
else
$1 = 0;
}
}
%typecheck(QL_TYPECHECK_ARRAY) const Array & {
/* native sequence? */
if (PyTuple_Check($input) || PyList_Check($input)) {
Size size = PySequence_Size($input);
if (size == 0) {
$1 = 1;
} else {
PyObject* o = PySequence_GetItem($input,0);
if (PyNumber_Check(o))
$1 = 1;
else
$1 = 0;
Py_DECREF(o);
}
} else {
/* wrapped Array? */
Array* v;
if (SWIG_ConvertPtr($input,(void **) &v,
$1_descriptor,0) != -1)
$1 = 1;
else
$1 = 0;
}
}
%typemap(in) Matrix (Matrix* m) {
if (PyTuple_Check($input) || PyList_Check($input)) {
Size rows, cols;
rows = (PyTuple_Check($input) ?
PyTuple_Size($input) :
PyList_Size($input));
if (rows > 0) {
// look ahead
PyObject* o = PySequence_GetItem($input,0);
if (PyTuple_Check(o) || PyList_Check(o)) {
cols = (PyTuple_Check(o) ?
PyTuple_Size(o) :
PyList_Size(o));
Py_DECREF(o);
} else {
PyErr_SetString(PyExc_TypeError, "Matrix expected");
Py_DECREF(o);
return NULL;
}
} else {
cols = 0;
}
$1 = Matrix(rows,cols);
for (Size i=0; i<rows; i++) {
PyObject* o = PySequence_GetItem($input,i);
if (PyTuple_Check(o) || PyList_Check(o)) {
Size items = (PyTuple_Check(o) ?
PyTuple_Size(o) :
PyList_Size(o));
if (items != cols) {
PyErr_SetString(PyExc_TypeError,
"Matrix must have equal-length rows");
Py_DECREF(o);
return NULL;
}
for (Size j=0; j<cols; j++) {
PyObject* d = PySequence_GetItem(o,j);
if (PyFloat_Check(d)) {
$1[i][j] = PyFloat_AsDouble(d);
Py_DECREF(d);
} else if (PyInt_Check(d)) {
$1[i][j] = Real(PyInt_AsLong(d));
Py_DECREF(d);
} else {
PyErr_SetString(PyExc_TypeError,"doubles expected");
Py_DECREF(d);
Py_DECREF(o);
return NULL;
}
}
Py_DECREF(o);
} else {
PyErr_SetString(PyExc_TypeError, "Matrix expected");
Py_DECREF(o);
return NULL;
}
}
} else {
SWIG_ConvertPtr($input,(void **) &m,$&1_descriptor,1);
$1 = *m;
}
};
%typemap(in) const Matrix & (Matrix temp) {
if (PyTuple_Check($input) || PyList_Check($input)) {
Size rows, cols;
rows = (PyTuple_Check($input) ?
PyTuple_Size($input) :
PyList_Size($input));
if (rows > 0) {
// look ahead
PyObject* o = PySequence_GetItem($input,0);
if (PyTuple_Check(o) || PyList_Check(o)) {
cols = (PyTuple_Check(o) ?
PyTuple_Size(o) :
PyList_Size(o));
Py_DECREF(o);
} else {
PyErr_SetString(PyExc_TypeError, "Matrix expected");
Py_DECREF(o);
return NULL;
}
} else {
cols = 0;
}
temp = Matrix(rows,cols);
for (Size i=0; i<rows; i++) {
PyObject* o = PySequence_GetItem($input,i);
if (PyTuple_Check(o) || PyList_Check(o)) {
Size items = (PyTuple_Check(o) ?
PyTuple_Size(o) :
PyList_Size(o));
if (items != cols) {
PyErr_SetString(PyExc_TypeError,
"Matrix must have equal-length rows");
Py_DECREF(o);
return NULL;
}
for (Size j=0; j<cols; j++) {
PyObject* d = PySequence_GetItem(o,j);
if (PyFloat_Check(d)) {
temp[i][j] = PyFloat_AsDouble(d);
Py_DECREF(d);
} else if (PyInt_Check(d)) {
temp[i][j] = Real(PyInt_AsLong(d));
Py_DECREF(d);
} else {
PyErr_SetString(PyExc_TypeError,"doubles expected");
Py_DECREF(d);
Py_DECREF(o);
return NULL;
}
}
Py_DECREF(o);
} else {
PyErr_SetString(PyExc_TypeError, "Matrix expected");
Py_DECREF(o);
return NULL;
}
}
$1 = &temp;
} else {
SWIG_ConvertPtr($input,(void **) &$1,$1_descriptor,1);
}
};
%typecheck(QL_TYPECHECK_MATRIX) Matrix {
/* native sequence? */
if (PyTuple_Check($input) || PyList_Check($input)) {
$1 = 1;
/* wrapped Matrix? */
} else {
Matrix* m;
if (SWIG_ConvertPtr($input,(void **) &m,
$&1_descriptor,0) != -1)
$1 = 1;
else
$1 = 0;
}
}
%typecheck(QL_TYPECHECK_MATRIX) const Matrix & {
/* native sequence? */
if (PyTuple_Check($input) || PyList_Check($input)) {
$1 = 1;
/* wrapped Matrix? */
} else {
Matrix* m;
if (SWIG_ConvertPtr($input,(void **) &m,
$1_descriptor,0) != -1)
$1 = 1;
else
$1 = 0;
}
}
//#endif
class Array {
//#if defined(SWIGPYTHON) || defined(SWIGRUBY)
%rename(__len__) size;
//#endif
public:
Array();
Array(Size n, Real fill = 0.0);
Array(const Array&);
Size size() const;
%extend {
std::string __str__() {
std::ostringstream out;
out << *self;
return out.str();
}
//#if defined(SWIGPYTHON) || defined(SWIGRUBY) || defined(SWIGR)
Array __add__(const Array& a) {
return Array(*self+a);
}
Array __sub__(const Array& a) {
return Array(*self-a);
}
Array __mul__(Real a) {
return Array(*self*a);
}
Real __mul__(const Array& a) {
return QuantLib::DotProduct(*self,a);
}
Array __mul__(const Matrix& a) {
return *self*a;
}
Array __div__(Real a) {
return Array(*self/a);
}
//#endif
//#if defined(SWIGPYTHON)
Array __rmul__(Real a) {
return Array(*self*a);
}
Array __getslice__(Integer i, Integer j) {
Integer size_ = static_cast<Integer>(self->size());
if (i<0)
i = size_+i;
if (j<0)
j = size_+j;
i = std::max(0,i);
j = std::min(size_,j);
Array tmp(j-i);
std::copy(self->begin()+i,self->begin()+j,tmp.begin());
return tmp;
}
void __setslice__(Integer i, Integer j, const Array& rhs) {
Integer size_ = static_cast<Integer>(self->size());
if (i<0)
i = size_+i;
if (j<0)
j = size_+j;
i = std::max(0,i);
j = std::min(size_,j);
QL_ENSURE(static_cast<Integer>(rhs.size()) == j-i,
"arrays are not resizable");
std::copy(rhs.begin(),rhs.end(),self->begin()+i);
}
bool __nonzero__() {
return (self->size() != 0);
}
bool __bool__() {
return (self->size() != 0);
}
//#endif
//#if defined(SWIGPYTHON) || defined(SWIGRUBY)
Real __getitem__(Integer i) {
Integer size_ = static_cast<Integer>(self->size());
if (i>=0 && i<size_) {
return (*self)[i];
} else if (i<0 && -i<=size_) {
return (*self)[size_+i];
} else {
throw std::out_of_range("array index out of range");
}
}
void __setitem__(Integer i, Real x) {
Integer size_ = static_cast<Integer>(self->size());
if (i>=0 && i<size_) {
(*self)[i] = x;
} else if (i<0 && -i<=size_) {
(*self)[size_+i] = x;
} else {
throw std::out_of_range("array index out of range");
}
}
//#endif
}
};
// 2-D view
%{
typedef QuantLib::LexicographicalView<Array::iterator> DefaultLexicographicalView;
typedef QuantLib::LexicographicalView<Array::iterator>::y_iterator DefaultLexicographicalViewColumn;
%}
//#if defined(SWIGPYTHON) || defined(SWIGRUBY) || defined(SWIGR)
class DefaultLexicographicalViewColumn {
private:
// access control - no constructor exported
DefaultLexicographicalViewColumn();
public:
%extend {
Real __getitem__(Size i) {
return (*self)[i];
}
void __setitem__(Size i, Real x) {
(*self)[i] = x;
}
}
};
//#endif
%rename(LexicographicalView) DefaultLexicographicalView;
class DefaultLexicographicalView {
public:
Size xSize() const;
Size ySize() const;
%extend {
DefaultLexicographicalView(Array& a, Size xSize) {
return new DefaultLexicographicalView(a.begin(),a.end(),xSize);
}
std::string __str__() {
std::ostringstream s;
for (Size j=0; j<self->ySize(); j++) {
s << "\n";
for (Size i=0; i<self->xSize(); i++) {
if (i != 0)
s << ",";
Array::value_type value = (*self)[i][j];
s << value;
}
}
s << "\n";
return s.str();
}
//#if defined(SWIGPYTHON) || defined(SWIGRUBY) || defined(SWIGR)
DefaultLexicographicalViewColumn __getitem__(Size i) {
return (*self)[i];
}
//#endif
}
};
%{
typedef QuantLib::Matrix::row_iterator MatrixRow;
using QuantLib::outerProduct;
using QuantLib::transpose;
using QuantLib::SVD;
%}
//#if defined(SWIGPYTHON) || defined(SWIGRUBY)
class MatrixRow {
private:
MatrixRow();
public:
%extend {
Real __getitem__(Size i) {
return (*self)[i];
}
void __setitem__(Size i, Real x) {
(*self)[i] = x;
}
}
};
//#endif
class Matrix {
public:
Matrix();
Matrix(Size rows, Size columns, Real fill = 0.0);
Matrix(const Matrix&);
Size rows() const;
Size columns() const;
%extend {
std::string __str__() {
std::ostringstream out;
out << *self;
return out.str();
}
//#if defined(SWIGPYTHON) || defined(SWIGRUBY)
Matrix __add__(const Matrix& m) {
return *self+m;
}
Matrix __sub__(const Matrix& m) {
return *self-m;
}
Matrix __mul__(Real x) {
return *self*x;
}
Array __mul__(const Array& x) {
return *self*x;
}
Matrix __mul__(const Matrix& x) {
return *self*x;
}
Matrix __div__(Real x) {
return *self/x;
}
//#endif
//#if defined(SWIGPYTHON) || defined(SWIGRUBY)
MatrixRow __getitem__(Size i) {
return (*self)[i];
}
//#endif
//#if defined(SWIGPYTHON)
Matrix __rmul__(Real x) {
return x*(*self);
}
Array __rmul__(const Array& x) {
return x*(*self);
}
Matrix __rmul__(const Matrix& x) {
return x*(*self);
}
//#endif
}
};
// functions
%{
using QuantLib::pseudoSqrt;
using QuantLib::SalvagingAlgorithm;
%}
struct SalvagingAlgorithm {
//#if defined(SWIGPYTHON)
%rename(NoAlgorithm) None;
//#endif
enum Type { None, Spectral };
};
Matrix transpose(const Matrix& m);
Matrix outerProduct(const Array& v1, const Array& v2);
Matrix pseudoSqrt(const Matrix& m, SalvagingAlgorithm::Type a);
class SVD {
public:
SVD(const Matrix&);
const Matrix& U() const;
const Matrix& V() const;
Matrix S() const;
const Array& singularValues() const;
};
#endif
setup.py
setup.py
"""
setup.py file for QuantLibEx
"""
from distutils.core import setup, Extension
qlx_module = Extension(
name='QuantLibEx._QuantLibEx',
sources=['QuantLibEx/qlx_wrap.cpp'],
include_dirs=['/usr/include/ql'], # QuantLib 頭檔案所在的目錄
library_dirs=['/usr/lib/'], # QuantLib 庫所在的目錄
libraries=['QuantLib'] # QuantLib 庫的名字
)
setup(
name = 'QuantLibEx',
version = '0.1',
author = "xrl",
description = "Python bindings for the QuantLibEx library",
ext_modules = [qlx_module],
py_modules = ['QuantLibEx.__init__','QuantLibEx.QuantLibEx'])
__init__.py
__init__.py
import sys
if sys.version_info.major >= 3:
from .QuantLibEx import *
from .QuantLibEx import _QuantLibEx
else:
from QuantLibEx import *
from QuantLibEx import _QuantLibEx
del sys
__author__ = 'xrl'
if hasattr(_QuantLibEx,'__version__'):
__version__ = _QuantLibEx.__version__
elif hasattr(_QuantLibEx.cvar,'__version__'):
__version__ = _QuantLibEx.cvar.__version__
else:
print('Could not find __version__ attribute')
if hasattr(_QuantLibEx,'__hexversion__'):
__hexversion__ = _QuantLibEx.__hexversion__
elif hasattr(_QuantLibEx.cvar,'__hexversion__'):
__hexversion__ = _QuantLibEx.cvar.__hexversion__
else:
print('Could not find __hexversion__ attribute')
__license__ = """
QuantLibEx ...
"""
《QuantLib 金融計算》系列合集
★ 持續學習 ★ 堅持創作 ★