diff --git a/.build/csharp.build b/.build/csharp.build deleted file mode 100755 index 9d6208d082..0000000000 --- a/.build/csharp.build +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash - -./autogen.sh \ -&& ./configure CC=clang CXX=clang++ CXXFLAGS='-O2' \ -&& make -C CSharp \ -&& make -C CSharp check diff --git a/.build/java.build b/.build/java.build deleted file mode 100755 index 339b89e629..0000000000 --- a/.build/java.build +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash - -./autogen.sh \ -&& ./configure --with-jdk-include=/usr/lib/jvm/default-java/include \ - --with-jdk-system-include=/usr/lib/jvm/default-java/include/linux \ - CC=clang CXX=clang++ CXXFLAGS='-O2' \ -&& make -C Java \ -&& make -C Java check diff --git a/.build/python3.build b/.build/python3.build deleted file mode 100755 index 2e90868c42..0000000000 --- a/.build/python3.build +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash - -./autogen.sh \ -&& ./configure PYTHON=/usr/bin/python3 CC=clang CXX=clang++ CXXFLAGS='-O1' \ -&& make -C Python \ -&& make -C Python check \ -&& make -C Python wheel \ -&& pip install Python/dist/QuantLib-*.whl \ -&& grep -v QuantLib requirements.txt > Python/examples/requirements.txt \ -&& pip install -r Python/examples/requirements.txt \ -&& for i in Python/examples/*.py ; do echo "$i" && /usr/bin/python3 "$i" || break -1 ; done - diff --git a/.build/scala.build b/.build/scala.build deleted file mode 100755 index fc38491ada..0000000000 --- a/.build/scala.build +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash - -./autogen.sh \ -&& ./configure --with-jdk-include=/usr/lib/jvm/default-java/include \ - --with-jdk-system-include=/usr/lib/jvm/default-java/include/linux CXXFLAGS='-O2' \ -&& make -C Java \ -&& make -C Scala check diff --git a/.ci/csharp.build b/.ci/csharp.build new file mode 100755 index 0000000000..e991b6a2dc --- /dev/null +++ b/.ci/csharp.build @@ -0,0 +1,6 @@ +#!/bin/bash +set -e + +./autogen.sh +./configure CC=clang CXX=clang++ CXXFLAGS='-O2' +make -C CSharp diff --git a/.ci/csharp.check b/.ci/csharp.check new file mode 100755 index 0000000000..763d849012 --- /dev/null +++ b/.ci/csharp.check @@ -0,0 +1,4 @@ +#!/bin/bash +set -e + +make -C CSharp check diff --git a/.ci/csharp.install b/.ci/csharp.install new file mode 100755 index 0000000000..2005ff9b31 --- /dev/null +++ b/.ci/csharp.install @@ -0,0 +1,2 @@ +#!/bin/bash +set -e diff --git a/.ci/java-new.build b/.ci/java-new.build new file mode 100755 index 0000000000..0cb9cc4c07 --- /dev/null +++ b/.ci/java-new.build @@ -0,0 +1,9 @@ +#!/bin/bash +set -e + +./autogen.sh +./configure --disable-java-finalizer --enable-java-autocloseable \ + --with-jdk-include=/usr/lib/jvm/default-java/include \ + --with-jdk-system-include=/usr/lib/jvm/default-java/include/linux \ + CC=clang CXX=clang++ CXXFLAGS='-O2' +make -C Java diff --git a/.ci/java-new.check b/.ci/java-new.check new file mode 100755 index 0000000000..a288402dae --- /dev/null +++ b/.ci/java-new.check @@ -0,0 +1,4 @@ +#!/bin/bash +set -e + +make -C Java check diff --git a/.ci/java-new.install b/.ci/java-new.install new file mode 100755 index 0000000000..2005ff9b31 --- /dev/null +++ b/.ci/java-new.install @@ -0,0 +1,2 @@ +#!/bin/bash +set -e diff --git a/.ci/java-old.build b/.ci/java-old.build new file mode 100755 index 0000000000..af3f87f42b --- /dev/null +++ b/.ci/java-old.build @@ -0,0 +1,8 @@ +#!/bin/bash +set -e + +./autogen.sh +./configure --with-jdk-include=/usr/lib/jvm/default-java/include \ + --with-jdk-system-include=/usr/lib/jvm/default-java/include/linux \ + CC=clang CXX=clang++ CXXFLAGS='-O2' +make -C Java diff --git a/.ci/java-old.check b/.ci/java-old.check new file mode 100755 index 0000000000..a288402dae --- /dev/null +++ b/.ci/java-old.check @@ -0,0 +1,4 @@ +#!/bin/bash +set -e + +make -C Java check diff --git a/.ci/java-old.install b/.ci/java-old.install new file mode 100755 index 0000000000..2005ff9b31 --- /dev/null +++ b/.ci/java-old.install @@ -0,0 +1,2 @@ +#!/bin/bash +set -e diff --git a/.ci/python.build b/.ci/python.build new file mode 100755 index 0000000000..aa36b29f50 --- /dev/null +++ b/.ci/python.build @@ -0,0 +1,7 @@ +#!/bin/bash +set -e + +./autogen.sh +./configure PYTHON=/usr/bin/python3 CC=clang CXX=clang++ CXXFLAGS='-O1' +make -C Python +make -C Python wheel diff --git a/.ci/python.check b/.ci/python.check new file mode 100755 index 0000000000..e8ad9d404c --- /dev/null +++ b/.ci/python.check @@ -0,0 +1,11 @@ +#!/bin/bash +set -e + +make -C Python check + +. .venv/bin/activate +for i in Python/examples/*.py +do + echo "$i" + python "$i" || break -1 +done diff --git a/.ci/python.install b/.ci/python.install new file mode 100755 index 0000000000..11b983fd59 --- /dev/null +++ b/.ci/python.install @@ -0,0 +1,8 @@ +#!/bin/bash +set -e + +python3 -m venv .venv +. .venv/bin/activate +pip install Python/dist/QuantLib-*.whl +grep -v QuantLib binder/requirements.txt > Python/examples/requirements.txt +pip install -r Python/examples/requirements.txt diff --git a/.build/r.build b/.ci/r.build similarity index 54% rename from .build/r.build rename to .ci/r.build index 5ad99bd22d..6d98589087 100755 --- a/.build/r.build +++ b/.ci/r.build @@ -1,10 +1,10 @@ #!/bin/bash +set -e mkdir -p ~/.R echo 'CC=clang' > ~/.R/Makevars echo 'CXX=clang++' >> ~/.R/Makevars -./autogen.sh \ -&& ./configure CXXFLAGS='-O0' \ -&& make -C R \ -&& make -C R check +./autogen.sh +./configure CXXFLAGS='-O0' +make -C R diff --git a/.ci/r.check b/.ci/r.check new file mode 100755 index 0000000000..b6d2aaa0c8 --- /dev/null +++ b/.ci/r.check @@ -0,0 +1,9 @@ +#!/bin/bash +set -e + +echo Execute Scripts in demo folder +for fn in ./R/demo/*.R +do + echo $fn + Rscript "$fn" || break -1 +done diff --git a/.ci/r.install b/.ci/r.install new file mode 100755 index 0000000000..8e6262c45a --- /dev/null +++ b/.ci/r.install @@ -0,0 +1,4 @@ +#!/bin/bash +set -e + +R CMD INSTALL ./R.Rcheck/QuantLib diff --git a/.ci/scala.build b/.ci/scala.build new file mode 100755 index 0000000000..a1a36510a7 --- /dev/null +++ b/.ci/scala.build @@ -0,0 +1,7 @@ +#!/bin/bash +set -e + +./autogen.sh +./configure --with-jdk-include=/usr/lib/jvm/default-java/include \ + --with-jdk-system-include=/usr/lib/jvm/default-java/include/linux CXXFLAGS='-O2' +make -C Java diff --git a/.ci/scala.check b/.ci/scala.check new file mode 100755 index 0000000000..eb0eeb228f --- /dev/null +++ b/.ci/scala.check @@ -0,0 +1,4 @@ +#!/bin/bash +set -e + +make -C Scala check diff --git a/.ci/scala.install b/.ci/scala.install new file mode 100755 index 0000000000..2005ff9b31 --- /dev/null +++ b/.ci/scala.install @@ -0,0 +1,2 @@ +#!/bin/bash +set -e diff --git a/.github/workflows/copyrights.yml b/.github/workflows/copyrights.yml index 472f495cb7..776cf32a58 100644 --- a/.github/workflows/copyrights.yml +++ b/.github/workflows/copyrights.yml @@ -7,14 +7,14 @@ jobs: copyrights: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Check run: | ./tools/check_copyrights.sh - - uses: peter-evans/create-pull-request@v3 + - uses: peter-evans/create-pull-request@v6 with: token: ${{ secrets.GITHUB_TOKEN }} - branch: update-copyright-list-${{ github.ref }} + branch: update-copyright-list-${{ github.ref_name }} delete-branch: true commit-message: 'Update copyright list in license' title: 'Update copyright list in license' diff --git a/.github/workflows/devenv-images.yml b/.github/workflows/devenv-images.yml new file mode 100644 index 0000000000..775d731540 --- /dev/null +++ b/.github/workflows/devenv-images.yml @@ -0,0 +1,92 @@ +name: Build quantlib-swig-devenv Docker images +on: + schedule: + - cron: '0 0 * * 6' + workflow_dispatch: +jobs: + docker-images-base: + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v4 + - name: Checkout latest QuantLib master + uses: actions/checkout@v4 + with: + repository: lballabio/QuantLib + path: dockerfiles/QuantLib + - name: Build Docker images + working-directory: dockerfiles + run: | + rm -rf QuantLib/.git + docker build -f ci.base.Dockerfile --build-arg tag=rolling -t ghcr.io/lballabio/quantlib-swig-devenv:base . + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GHCR_PAT }} + - name: Push Docker images + run: | + docker push ghcr.io/lballabio/quantlib-swig-devenv:base + docker-images-ql: + runs-on: ubuntu-20.04 + needs: docker-images-base + strategy: + matrix: + tag: [default, threadsafe] + steps: + - uses: actions/checkout@v4 + - name: Build Docker images + working-directory: dockerfiles + run: | + docker build -f ci.${{ matrix.tag }}.Dockerfile -t ghcr.io/lballabio/quantlib-swig-devenv:${{ matrix.tag }} . + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GHCR_PAT }} + - name: Push Docker images + run: | + docker push ghcr.io/lballabio/quantlib-swig-devenv:${{ matrix.tag }} + docker-images-base-languages: + runs-on: ubuntu-20.04 + needs: docker-images-ql + strategy: + matrix: + lang: [python, csharp, java, r] + steps: + - uses: actions/checkout@v4 + - name: Build Docker images + working-directory: dockerfiles + run: | + docker build -f ci.${{ matrix.lang }}.Dockerfile -t ghcr.io/lballabio/quantlib-swig-devenv:${{ matrix.lang }} . + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GHCR_PAT }} + - name: Push Docker images + run: | + docker push ghcr.io/lballabio/quantlib-swig-devenv:${{ matrix.lang }} + docker-images-dependent-languages: + runs-on: ubuntu-20.04 + needs: docker-images-base-languages + strategy: + matrix: + lang: [scala] + steps: + - uses: actions/checkout@v4 + - name: Build Docker images + working-directory: dockerfiles + run: | + docker build -f ci.${{ matrix.lang }}.Dockerfile -t ghcr.io/lballabio/quantlib-swig-devenv:${{ matrix.lang }} . + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GHCR_PAT }} + - name: Push Docker images + run: | + docker push ghcr.io/lballabio/quantlib-swig-devenv:${{ matrix.lang }} diff --git a/.github/workflows/link-check.yml b/.github/workflows/link-check.yml deleted file mode 100644 index cbd153f227..0000000000 --- a/.github/workflows/link-check.yml +++ /dev/null @@ -1,12 +0,0 @@ -name: Check Markdown links -on: - push: - pull_request: - schedule: - - cron: "0 2 * * *" -jobs: - markdown-link-check: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: gaurav-nelson/github-action-markdown-link-check@v1 diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 8dd7d4b8dd..6adb1bc25d 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -1,15 +1,34 @@ name: Linux build -on: [push, pull_request, workflow_dispatch] +on: + push: + pull_request: + schedule: + - cron: '0 0 * * 0' + workflow_dispatch: jobs: build: runs-on: ubuntu-latest strategy: fail-fast: false matrix: - language: [python3, java, csharp, r, scala] + include: + - language: python + - language: java + config: -old + - language: java + config: -new + - language: csharp + - language: r + - language: scala container: ghcr.io/lballabio/quantlib-swig-devenv:${{ matrix.language }} steps: - - uses: actions/checkout@v2 - - name: Build and check + - uses: actions/checkout@v4 + - name: Build run: | - ./.build/${{ matrix.language }}.build + ./.ci/${{ matrix.language }}${{ matrix.config }}.build + - name: Install + run: | + ./.ci/${{ matrix.language }}${{ matrix.config }}.install + - name: Check + run: | + ./.ci/${{ matrix.language }}${{ matrix.config }}.check diff --git a/.github/workflows/misspell.yml b/.github/workflows/misspell.yml index 5cbf54c709..75cee11917 100644 --- a/.github/workflows/misspell.yml +++ b/.github/workflows/misspell.yml @@ -7,12 +7,12 @@ jobs: check: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - uses: sobolevn/misspell-fixer-action@master - - uses: peter-evans/create-pull-request@v3 + - uses: peter-evans/create-pull-request@v6 with: token: ${{ secrets.GITHUB_TOKEN }} - branch: misspell-fixes-${{ github.ref }} + branch: misspell-fixes-${{ github.ref_name }} delete-branch: true commit-message: 'Fixes by misspell-fixer' title: 'Typos fixed by misspell-fixer' diff --git a/.github/workflows/namespaces.yml b/.github/workflows/namespaces.yml index e2ee22abbd..74feb42299 100644 --- a/.github/workflows/namespaces.yml +++ b/.github/workflows/namespaces.yml @@ -7,7 +7,7 @@ jobs: namespaces: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Check run: | sed -i -e 's/boost::shared_ptr/ext::shared_ptr/g' SWIG/* @@ -16,10 +16,12 @@ jobs: sed -i -e 's/boost::tuple/ext::tuple/g' SWIG/* sed -i -e 's/boost::get/ext::get/g' SWIG/* sed -i -e 's/boost::function/ext::function/g' SWIG/* - - uses: peter-evans/create-pull-request@v3 + sed -i -e 's/boost::optional/ext::optional/g' SWIG/* + sed -i -e 's/boost::none/ext::nullopt/g' SWIG/* + - uses: peter-evans/create-pull-request@v6 with: token: ${{ secrets.GITHUB_TOKEN }} - branch: fix-boost-namespace-${{ github.ref }} + branch: fix-boost-namespace-${{ github.ref_name }} delete-branch: true commit-message: 'Fix uses of boost namespace' title: 'Fix uses of boost namespace' diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 3097698dbd..14d8c62f78 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -6,7 +6,7 @@ jobs: staleness-check: runs-on: ubuntu-latest steps: - - uses: actions/stale@v4 + - uses: actions/stale@v9 with: repo-token: ${{ secrets.GITHUB_TOKEN }} stale-issue-message: 'This issue was automatically marked as stale because it has been open 60 days with no activity. Remove stale label or comment, or this will be closed in two weeks.' diff --git a/.gitignore b/.gitignore index 873b34c740..1d11c4af52 100644 --- a/.gitignore +++ b/.gitignore @@ -9,8 +9,8 @@ CSharp/cpp/quantlib_wrap.cpp CSharp/cpp/quantlib_wrap.h CSharp/cpp/build CSharp/csharp/obj -CSharp/examples/bin -CSharp/examples/obj +CSharp/examples/*/bin +CSharp/examples/*/obj CSharp/.vs/ Java/bin/ Java/quantlib_wrap.cpp @@ -58,6 +58,7 @@ testCaseCollection.xml *.lo *.o *.so +*.dylib .build-stamp *.pyc *.exe @@ -94,3 +95,4 @@ R/R/QuantLib.R R/QuantLib.RData .vscode +**/venv diff --git a/CSharp/Makefile.am b/CSharp/Makefile.am index e04ca17279..1e1c28a1f6 100644 --- a/CSharp/Makefile.am +++ b/CSharp/Makefile.am @@ -1,39 +1,35 @@ -CLEANFILES = cpp/quantlib_wrap.* csharp/*.cs *.so *.dll \ - examples/*.so examples/*.dll examples/*.exe +CLEANFILES = cpp/quantlib_wrap.* cpp/*.so cpp/*.dylib csharp/*.cs *.dll BUILT_SOURCES = cpp/quantlib_wrap.cpp cpp/quantlib_wrap.h -if HAVE_MCS -if BUILD_MCS +if HAVE_DOTNET +if BUILD_DOTNET -all-local: libNQuantLibc.so NQuantLib.dll +all-local: cpp/libNQuantLibc.@SHARED_LIB_EXTENSION@ csharp/bin/Release/net6.0/NQuantLib.dll -libNQuantLibc.so: cpp/quantlib_wrap.o - $(CXX) -shared cpp/quantlib_wrap.o -o libNQuantLibc.so `quantlib-config --libs` +cpp/libNQuantLibc.@SHARED_LIB_EXTENSION@: cpp/quantlib_wrap.o + $(CXX) -shared cpp/quantlib_wrap.o -o cpp/libNQuantLibc.@SHARED_LIB_EXTENSION@ `quantlib-config --libs` cpp/quantlib_wrap.o: $(BUILT_SOURCES) $(CXX) -c -fpic $(CXXFLAGS) cpp/quantlib_wrap.cpp -o cpp/quantlib_wrap.o `quantlib-config --cflags` -NQuantLib.dll: $(BUILT_SOURCES) - $(MCS) -nologo -target:library -out:NQuantLib.dll csharp/*.cs +csharp/bin/Release/net6.0/NQuantLib.dll: $(BUILT_SOURCES) + $(DOTNET) build --nologo -c Release -p:Version=$(PACKAGE_VERSION) csharp/NQuantLib.csproj -check-local: examples/BermudanSwaption.exe \ - examples/EquityOption.exe \ - examples/Times.exe \ - examples/libNQuantLibc.so examples/NQuantLib.dll - $(MONO) ./examples/BermudanSwaption.exe - $(MONO) ./examples/EquityOption.exe - $(MONO) ./examples/Times.exe +nupkg: cpp/libNQuantLibc.@SHARED_LIB_EXTENSION@ csharp/bin/Release/net6.0/NQuantLib.dll + $(DOTNET) pack --no-build -c Release -p:PackageVersion=$(PACKAGE_VERSION) --include-symbols --include-source csharp/NQuantLib.csproj -examples/%.exe: examples/%.cs - $(MCS) -nologo -target:exe -out:$@ -reference:NQuantLib.dll $< +check-local: cpp/libNQuantLibc.@SHARED_LIB_EXTENSION@ csharp/bin/Release/net6.0/NQuantLib.dll + ln -f cpp/libNQuantLibc.@SHARED_LIB_EXTENSION@ examples/ + cd examples && LD_LIBRARY_PATH=. $(DOTNET) run -c Release --project BermudanSwaption/BermudanSwaption.csproj + cd examples && LD_LIBRARY_PATH=. $(DOTNET) run -c Release --project EquityOption/EquityOption.csproj + cd examples && LD_LIBRARY_PATH=. $(DOTNET) run -c Release --project FiniteDifferenceMethods/FiniteDifferenceMethods.csproj + cd examples && LD_LIBRARY_PATH=. $(DOTNET) run -c Release --project Times/Times.csproj + rm -f examples/libNQuantLibc.@SHARED_LIB_EXTENSION@ -examples/libNQuantLibc.so: libNQuantLibc.so - cd examples && ln -sf ../libNQuantLibc.so - -examples/NQuantLib.dll: NQuantLib.dll - cd examples && ln -sf ../NQuantLib.dll +clean-local: + rm -rf csharp/bin csharp/obj examples/*/bin examples/*/obj endif endif @@ -47,14 +43,22 @@ dist-hook: $(BUILT_SOURCES) cp ./cpp/*.vcxproj $(distdir)/cpp cp ./cpp/quantlib_wrap.cpp $(distdir)/cpp cp ./cpp/quantlib_wrap.h $(distdir)/cpp - cp ./cpp/QuantlibWrapper.h ./cpp/QuantlibWrapper.cpp $(distdir)/cpp - cp ./cpp/stdafx.h $(distdir)/cpp mkdir -p $(distdir)/csharp cp ./csharp/*.csproj $(distdir)/csharp cp ./csharp/*.cs $(distdir)/csharp mkdir -p $(distdir)/examples - cp ./examples/*.csproj $(distdir)/examples - cp ./examples/*.cs $(distdir)/examples + mkdir -p $(distdir)/examples/BermudanSwaption + cp ./examples/BermudanSwaption/BermudanSwaption.csproj $(distdir)/examples/BermudanSwaption + cp ./examples/BermudanSwaption/BermudanSwaption.cs $(distdir)/examples/BermudanSwaption + mkdir -p $(distdir)/examples/EquityOption + cp ./examples/EquityOption/EquityOption.csproj $(distdir)/examples/EquityOption + cp ./examples/EquityOption/EquityOption.cs $(distdir)/examples/EquityOption + mkdir -p $(distdir)/examples/FiniteDifferenceMethods + cp ./examples/FiniteDifferenceMethods/FiniteDifferenceMethods.csproj $(distdir)/examples/FiniteDifferenceMethods + cp ./examples/FiniteDifferenceMethods/FiniteDifferenceMethods.cs $(distdir)/examples/FiniteDifferenceMethods + mkdir -p $(distdir)/examples/Times + cp ./examples/Times/Times.csproj $(distdir)/examples/Times + cp ./examples/Times/Times.cs $(distdir)/examples/Times EXTRA_DIST = \ QuantLib.sln \ diff --git a/CSharp/QuantLib.props b/CSharp/QuantLib.props index 89c72669d6..ef46bbdfdf 100644 --- a/CSharp/QuantLib.props +++ b/CSharp/QuantLib.props @@ -8,5 +8,6 @@ v140 v141 v142 + v143 diff --git a/CSharp/QuantLib.sln b/CSharp/QuantLib.sln index 734f45bbd2..e97a489e6e 100644 --- a/CSharp/QuantLib.sln +++ b/CSharp/QuantLib.sln @@ -2,29 +2,29 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.31515.178 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BermudanSwaption", "examples\BermudanSwaption.csproj", "{1BEC49E8-122D-4CC9-9DAC-DD59F551E5E9}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BermudanSwaption", "examples\BermudanSwaption\BermudanSwaption.csproj", "{1BEC49E8-122D-4CC9-9DAC-DD59F551E5E9}" ProjectSection(ProjectDependencies) = postProject {21183104-9963-4D4F-B7E8-C8A6169FD053} = {21183104-9963-4D4F-B7E8-C8A6169FD053} EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EquityOption", "examples\EquityOption.csproj", "{1FD947F1-D99E-46FB-8890-04E11E8340C2}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EquityOption", "examples\EquityOption\EquityOption.csproj", "{1FD947F1-D99E-46FB-8890-04E11E8340C2}" ProjectSection(ProjectDependencies) = postProject {21183104-9963-4D4F-B7E8-C8A6169FD053} = {21183104-9963-4D4F-B7E8-C8A6169FD053} EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FiniteDifferenceMethods", "examples\FiniteDifferenceMethods.csproj", "{EF2AFADF-B632-4E95-BEB5-7B7109A9E4FD}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FiniteDifferenceMethods", "examples\FiniteDifferenceMethods\FiniteDifferenceMethods.csproj", "{EF2AFADF-B632-4E95-BEB5-7B7109A9E4FD}" ProjectSection(ProjectDependencies) = postProject {21183104-9963-4D4F-B7E8-C8A6169FD053} = {21183104-9963-4D4F-B7E8-C8A6169FD053} EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NQuantLib", "csharp\NQuantLib.csproj", "{928F98EE-7D50-457F-9304-A6818DCF1079}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NQuantLib", "csharp\NQuantLib.csproj", "{928F98EE-7D50-457F-9304-A6818DCF1079}" ProjectSection(ProjectDependencies) = postProject {21183104-9963-4D4F-B7E8-C8A6169FD053} = {21183104-9963-4D4F-B7E8-C8A6169FD053} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NQuantLibc", "cpp\QuantLibWrapper.vcxproj", "{21183104-9963-4D4F-B7E8-C8A6169FD053}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Times", "examples\Times.csproj", "{C93F5204-5BC9-4AB3-AC06-C2CCE166CEBD}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Times", "examples\Times\Times.csproj", "{C93F5204-5BC9-4AB3-AC06-C2CCE166CEBD}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -34,38 +34,38 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {1BEC49E8-122D-4CC9-9DAC-DD59F551E5E9}.Debug|Win32.ActiveCfg = Debug|Win32 - {1BEC49E8-122D-4CC9-9DAC-DD59F551E5E9}.Debug|Win32.Build.0 = Debug|Win32 - {1BEC49E8-122D-4CC9-9DAC-DD59F551E5E9}.Debug|x64.ActiveCfg = Debug|x64 - {1BEC49E8-122D-4CC9-9DAC-DD59F551E5E9}.Debug|x64.Build.0 = Debug|x64 - {1BEC49E8-122D-4CC9-9DAC-DD59F551E5E9}.Release|Win32.ActiveCfg = Release|Win32 - {1BEC49E8-122D-4CC9-9DAC-DD59F551E5E9}.Release|Win32.Build.0 = Release|Win32 - {1BEC49E8-122D-4CC9-9DAC-DD59F551E5E9}.Release|x64.ActiveCfg = Release|x64 - {1BEC49E8-122D-4CC9-9DAC-DD59F551E5E9}.Release|x64.Build.0 = Release|x64 - {1FD947F1-D99E-46FB-8890-04E11E8340C2}.Debug|Win32.ActiveCfg = Debug|Win32 - {1FD947F1-D99E-46FB-8890-04E11E8340C2}.Debug|Win32.Build.0 = Debug|Win32 - {1FD947F1-D99E-46FB-8890-04E11E8340C2}.Debug|x64.ActiveCfg = Debug|x64 - {1FD947F1-D99E-46FB-8890-04E11E8340C2}.Debug|x64.Build.0 = Debug|x64 - {1FD947F1-D99E-46FB-8890-04E11E8340C2}.Release|Win32.ActiveCfg = Release|Win32 - {1FD947F1-D99E-46FB-8890-04E11E8340C2}.Release|Win32.Build.0 = Release|Win32 - {1FD947F1-D99E-46FB-8890-04E11E8340C2}.Release|x64.ActiveCfg = Release|x64 - {1FD947F1-D99E-46FB-8890-04E11E8340C2}.Release|x64.Build.0 = Release|x64 - {EF2AFADF-B632-4E95-BEB5-7B7109A9E4FD}.Debug|Win32.ActiveCfg = Debug|Win32 - {EF2AFADF-B632-4E95-BEB5-7B7109A9E4FD}.Debug|Win32.Build.0 = Debug|Win32 - {EF2AFADF-B632-4E95-BEB5-7B7109A9E4FD}.Debug|x64.ActiveCfg = Debug|x64 - {EF2AFADF-B632-4E95-BEB5-7B7109A9E4FD}.Debug|x64.Build.0 = Debug|x64 - {EF2AFADF-B632-4E95-BEB5-7B7109A9E4FD}.Release|Win32.ActiveCfg = Release|Win32 - {EF2AFADF-B632-4E95-BEB5-7B7109A9E4FD}.Release|Win32.Build.0 = Release|Win32 - {EF2AFADF-B632-4E95-BEB5-7B7109A9E4FD}.Release|x64.ActiveCfg = Release|x64 - {EF2AFADF-B632-4E95-BEB5-7B7109A9E4FD}.Release|x64.Build.0 = Release|x64 - {928F98EE-7D50-457F-9304-A6818DCF1079}.Debug|Win32.ActiveCfg = Debug|Win32 - {928F98EE-7D50-457F-9304-A6818DCF1079}.Debug|Win32.Build.0 = Debug|Win32 - {928F98EE-7D50-457F-9304-A6818DCF1079}.Debug|x64.ActiveCfg = Debug|x64 - {928F98EE-7D50-457F-9304-A6818DCF1079}.Debug|x64.Build.0 = Debug|x64 - {928F98EE-7D50-457F-9304-A6818DCF1079}.Release|Win32.ActiveCfg = Release|Win32 - {928F98EE-7D50-457F-9304-A6818DCF1079}.Release|Win32.Build.0 = Release|Win32 - {928F98EE-7D50-457F-9304-A6818DCF1079}.Release|x64.ActiveCfg = Release|x64 - {928F98EE-7D50-457F-9304-A6818DCF1079}.Release|x64.Build.0 = Release|x64 + {1BEC49E8-122D-4CC9-9DAC-DD59F551E5E9}.Debug|Win32.ActiveCfg = Debug|Any CPU + {1BEC49E8-122D-4CC9-9DAC-DD59F551E5E9}.Debug|Win32.Build.0 = Debug|Any CPU + {1BEC49E8-122D-4CC9-9DAC-DD59F551E5E9}.Debug|x64.ActiveCfg = Debug|Any CPU + {1BEC49E8-122D-4CC9-9DAC-DD59F551E5E9}.Debug|x64.Build.0 = Debug|Any CPU + {1BEC49E8-122D-4CC9-9DAC-DD59F551E5E9}.Release|Win32.ActiveCfg = Release|Any CPU + {1BEC49E8-122D-4CC9-9DAC-DD59F551E5E9}.Release|Win32.Build.0 = Release|Any CPU + {1BEC49E8-122D-4CC9-9DAC-DD59F551E5E9}.Release|x64.ActiveCfg = Release|Any CPU + {1BEC49E8-122D-4CC9-9DAC-DD59F551E5E9}.Release|x64.Build.0 = Release|Any CPU + {1FD947F1-D99E-46FB-8890-04E11E8340C2}.Debug|Win32.ActiveCfg = Debug|Any CPU + {1FD947F1-D99E-46FB-8890-04E11E8340C2}.Debug|Win32.Build.0 = Debug|Any CPU + {1FD947F1-D99E-46FB-8890-04E11E8340C2}.Debug|x64.ActiveCfg = Debug|Any CPU + {1FD947F1-D99E-46FB-8890-04E11E8340C2}.Debug|x64.Build.0 = Debug|Any CPU + {1FD947F1-D99E-46FB-8890-04E11E8340C2}.Release|Win32.ActiveCfg = Release|Any CPU + {1FD947F1-D99E-46FB-8890-04E11E8340C2}.Release|Win32.Build.0 = Release|Any CPU + {1FD947F1-D99E-46FB-8890-04E11E8340C2}.Release|x64.ActiveCfg = Release|Any CPU + {1FD947F1-D99E-46FB-8890-04E11E8340C2}.Release|x64.Build.0 = Release|Any CPU + {EF2AFADF-B632-4E95-BEB5-7B7109A9E4FD}.Debug|Win32.ActiveCfg = Debug|Any CPU + {EF2AFADF-B632-4E95-BEB5-7B7109A9E4FD}.Debug|Win32.Build.0 = Debug|Any CPU + {EF2AFADF-B632-4E95-BEB5-7B7109A9E4FD}.Debug|x64.ActiveCfg = Debug|Any CPU + {EF2AFADF-B632-4E95-BEB5-7B7109A9E4FD}.Debug|x64.Build.0 = Debug|Any CPU + {EF2AFADF-B632-4E95-BEB5-7B7109A9E4FD}.Release|Win32.ActiveCfg = Release|Any CPU + {EF2AFADF-B632-4E95-BEB5-7B7109A9E4FD}.Release|Win32.Build.0 = Release|Any CPU + {EF2AFADF-B632-4E95-BEB5-7B7109A9E4FD}.Release|x64.ActiveCfg = Release|Any CPU + {EF2AFADF-B632-4E95-BEB5-7B7109A9E4FD}.Release|x64.Build.0 = Release|Any CPU + {928F98EE-7D50-457F-9304-A6818DCF1079}.Debug|Win32.ActiveCfg = Debug|Any CPU + {928F98EE-7D50-457F-9304-A6818DCF1079}.Debug|Win32.Build.0 = Debug|Any CPU + {928F98EE-7D50-457F-9304-A6818DCF1079}.Debug|x64.ActiveCfg = Debug|Any CPU + {928F98EE-7D50-457F-9304-A6818DCF1079}.Debug|x64.Build.0 = Debug|Any CPU + {928F98EE-7D50-457F-9304-A6818DCF1079}.Release|Win32.ActiveCfg = Release|Any CPU + {928F98EE-7D50-457F-9304-A6818DCF1079}.Release|Win32.Build.0 = Release|Any CPU + {928F98EE-7D50-457F-9304-A6818DCF1079}.Release|x64.ActiveCfg = Release|Any CPU + {928F98EE-7D50-457F-9304-A6818DCF1079}.Release|x64.Build.0 = Release|Any CPU {21183104-9963-4D4F-B7E8-C8A6169FD053}.Debug|Win32.ActiveCfg = Debug|Win32 {21183104-9963-4D4F-B7E8-C8A6169FD053}.Debug|Win32.Build.0 = Debug|Win32 {21183104-9963-4D4F-B7E8-C8A6169FD053}.Debug|x64.ActiveCfg = Debug|x64 @@ -74,14 +74,14 @@ Global {21183104-9963-4D4F-B7E8-C8A6169FD053}.Release|Win32.Build.0 = Release|Win32 {21183104-9963-4D4F-B7E8-C8A6169FD053}.Release|x64.ActiveCfg = Release|x64 {21183104-9963-4D4F-B7E8-C8A6169FD053}.Release|x64.Build.0 = Release|x64 - {C93F5204-5BC9-4AB3-AC06-C2CCE166CEBD}.Debug|Win32.ActiveCfg = Debug|Win32 - {C93F5204-5BC9-4AB3-AC06-C2CCE166CEBD}.Debug|Win32.Build.0 = Debug|Win32 - {C93F5204-5BC9-4AB3-AC06-C2CCE166CEBD}.Debug|x64.ActiveCfg = Debug|x64 - {C93F5204-5BC9-4AB3-AC06-C2CCE166CEBD}.Debug|x64.Build.0 = Debug|x64 - {C93F5204-5BC9-4AB3-AC06-C2CCE166CEBD}.Release|Win32.ActiveCfg = Release|Win32 - {C93F5204-5BC9-4AB3-AC06-C2CCE166CEBD}.Release|Win32.Build.0 = Release|Win32 - {C93F5204-5BC9-4AB3-AC06-C2CCE166CEBD}.Release|x64.ActiveCfg = Release|x64 - {C93F5204-5BC9-4AB3-AC06-C2CCE166CEBD}.Release|x64.Build.0 = Release|x64 + {C93F5204-5BC9-4AB3-AC06-C2CCE166CEBD}.Debug|Win32.ActiveCfg = Debug|Any CPU + {C93F5204-5BC9-4AB3-AC06-C2CCE166CEBD}.Debug|Win32.Build.0 = Debug|Any CPU + {C93F5204-5BC9-4AB3-AC06-C2CCE166CEBD}.Debug|x64.ActiveCfg = Debug|Any CPU + {C93F5204-5BC9-4AB3-AC06-C2CCE166CEBD}.Debug|x64.Build.0 = Debug|Any CPU + {C93F5204-5BC9-4AB3-AC06-C2CCE166CEBD}.Release|Win32.ActiveCfg = Release|Any CPU + {C93F5204-5BC9-4AB3-AC06-C2CCE166CEBD}.Release|Win32.Build.0 = Release|Any CPU + {C93F5204-5BC9-4AB3-AC06-C2CCE166CEBD}.Release|x64.ActiveCfg = Release|Any CPU + {C93F5204-5BC9-4AB3-AC06-C2CCE166CEBD}.Release|x64.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/CSharp/cpp/QuantLibWrapper.vcxproj b/CSharp/cpp/QuantLibWrapper.vcxproj index 841207f2f1..f82e04548d 100644 --- a/CSharp/cpp/QuantLibWrapper.vcxproj +++ b/CSharp/cpp/QuantLibWrapper.vcxproj @@ -100,6 +100,9 @@ $(OutDir)NQuantLibc.lib MachineX86 + + copy "$(OutDir)NQuantLibc.dll" "$(ProjectDir)" + @@ -126,6 +129,9 @@ $(OutDir)NQuantLibc.lib + + copy "$(OutDir)NQuantLibc.dll" "$(ProjectDir)" + @@ -154,6 +160,9 @@ $(OutDir)NQuantLibc.lib MachineX86 + + copy "$(OutDir)NQuantLibc.dll" "$(ProjectDir)" + @@ -181,17 +190,18 @@ $(OutDir)NQuantLibc.lib + + copy "$(OutDir)NQuantLibc.dll" "$(ProjectDir)" + - - - + diff --git a/CSharp/cpp/QuantLibWrapper.vcxproj.filters b/CSharp/cpp/QuantLibWrapper.vcxproj.filters index 0d50081646..5891cf4c2c 100644 --- a/CSharp/cpp/QuantLibWrapper.vcxproj.filters +++ b/CSharp/cpp/QuantLibWrapper.vcxproj.filters @@ -18,19 +18,10 @@ Source Files - - Source Files - Header Files - - Header Files - - - Header Files - diff --git a/CSharp/cpp/QuantlibWrapper.cpp b/CSharp/cpp/QuantlibWrapper.cpp deleted file mode 100644 index 9f3a3c831e..0000000000 --- a/CSharp/cpp/QuantlibWrapper.cpp +++ /dev/null @@ -1,36 +0,0 @@ -// QuantlibWrapper.cpp : Defines the entry point for the DLL application. -// - -#include "stdafx.h" -#include "QuantlibWrapper.h" -BOOL APIENTRY DllMain( HANDLE hModule, - DWORD ul_reason_for_call, - LPVOID lpReserved - ) -{ - switch (ul_reason_for_call) - { - case DLL_PROCESS_ATTACH: - case DLL_THREAD_ATTACH: - case DLL_THREAD_DETACH: - case DLL_PROCESS_DETACH: - break; - } - return TRUE; -} - -// This is an example of an exported variable -QUANTLIBWRAPPER_API int nQuantlibWrapper=0; - -// This is an example of an exported function. -QUANTLIBWRAPPER_API int fnQuantlibWrapper(void) -{ - return 42; -} - -// This is the constructor of a class that has been exported. -// see QuantlibWrapper.h for the class definition -CQuantlibWrapper::CQuantlibWrapper() -{ - return; -} diff --git a/CSharp/cpp/QuantlibWrapper.h b/CSharp/cpp/QuantlibWrapper.h deleted file mode 100644 index 8e217e1d63..0000000000 --- a/CSharp/cpp/QuantlibWrapper.h +++ /dev/null @@ -1,22 +0,0 @@ -// The following ifdef block is the standard way of creating macros which make exporting -// from a DLL simpler. All files within this DLL are compiled with the QUANTLIBWRAPPER_EXPORTS -// symbol defined on the command line. this symbol should not be defined on any project -// that uses this DLL. This way any other project whose source files include this file see -// QUANTLIBWRAPPER_API functions as being imported from a DLL, whereas this DLL sees symbols -// defined with this macro as being exported. -#ifdef QUANTLIBWRAPPER_EXPORTS -#define QUANTLIBWRAPPER_API __declspec(dllexport) -#else -#define QUANTLIBWRAPPER_API __declspec(dllimport) -#endif - -// This class is exported from the QuantlibWrapper.dll -class QUANTLIBWRAPPER_API CQuantlibWrapper { -public: - CQuantlibWrapper(void); - // TODO: add your methods here. -}; - -extern QUANTLIBWRAPPER_API int nQuantlibWrapper; - -QUANTLIBWRAPPER_API int fnQuantlibWrapper(void); diff --git a/CSharp/cpp/stdafx.h b/CSharp/cpp/stdafx.h deleted file mode 100644 index f45cbcda25..0000000000 --- a/CSharp/cpp/stdafx.h +++ /dev/null @@ -1,13 +0,0 @@ -// stdafx.h : include file for standard system include files, -// or project specific include files that are used frequently, but -// are changed infrequently -// - -#pragma once - - -#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers -// Windows Header Files: -#include - -// TODO: reference additional headers your program requires here diff --git a/CSharp/csharp/NQuantLib.csproj b/CSharp/csharp/NQuantLib.csproj index dbfcbf1fba..5a832b3959 100644 --- a/CSharp/csharp/NQuantLib.csproj +++ b/CSharp/csharp/NQuantLib.csproj @@ -1,130 +1,20 @@ - - - + - Local - 9.0.30729 - 2.0 - {928F98EE-7D50-457F-9304-A6818DCF1079} - Debug - AnyCPU - - - - - NQuantLib - - - JScript - Grid - IE50 - false - Library - QuantLib - OnBuildSuccess - - - - - - - 3.5 - v4.0 - publish\ - true - Disk - false - Foreground - 7 - Days - false - false - true - 0 - 1.0.0.%2a - false - false - true - + net6.0 + QuantLib - - true - bin\$(Platform)\$(Configuration)\ - DEBUG;TRACE - 285212672 - 4096 - full - x86 - prompt - AllRules.ruleset - - - bin\$(Platform)\$(Configuration)\ - TRACE - 285212672 - true - 4096 - x86 - prompt - AllRules.ruleset - - - true - bin\$(Platform)\$(Configuration)\ - DEBUG;TRACE - 285212672 - 4096 - full - x64 - prompt - AllRules.ruleset - - - bin\$(Platform)\$(Configuration)\ - TRACE - 285212672 - true - 4096 - x64 - prompt - AllRules.ruleset - - - - System - - - System.Data - - - System.XML - - - - - - - False - .NET Framework 3.5 SP1 Client Profile - false - - - False - .NET Framework 3.5 SP1 - true - - - False - Windows Installer 3.1 - true - + + runtimes/win-x64/native + + + runtimes/linux-x64/native + + + runtimes/osx-x64/native + - - - - - - - diff --git a/CSharp/examples/BermudanSwaption.csproj b/CSharp/examples/BermudanSwaption.csproj deleted file mode 100644 index e5be3d3392..0000000000 --- a/CSharp/examples/BermudanSwaption.csproj +++ /dev/null @@ -1,137 +0,0 @@ - - - - - Local - 9.0.21022 - 2.0 - {1BEC49E8-122D-4CC9-9DAC-DD59F551E5E9} - Debug - AnyCPU - - - - - BermudanSwaption - - - JScript - Grid - IE50 - false - Exe - BermudanSwaption - OnBuildSuccess - - - - - - - 3.5 - v4.0 - publish\ - true - Disk - false - Foreground - 7 - Days - false - false - true - 0 - 1.0.0.%2a - false - false - true - - - - true - bin\$(Platform)\$(Configuration)\ - DEBUG;TRACE - 285212672 - 4096 - full - x86 - prompt - AllRules.ruleset - - - bin\$(Platform)\$(Configuration)\ - TRACE - 285212672 - true - 4096 - x86 - prompt - AllRules.ruleset - - - true - bin\$(Platform)\$(Configuration)\ - DEBUG;TRACE - 285212672 - 4096 - full - x64 - prompt - AllRules.ruleset - - - bin\$(Platform)\$(Configuration)\ - TRACE - 285212672 - true - 4096 - x64 - prompt - AllRules.ruleset - - - - System - - - System.Data - - - System.XML - - - - - Code - - - - - False - .NET Framework 3.5 SP1 Client Profile - false - - - False - .NET Framework 3.5 SP1 - true - - - False - Windows Installer 3.1 - true - - - - - {928f98ee-7d50-457f-9304-a6818dcf1079} - NQuantLib - - - - - copy "$(SolutionDir)cpp\bin\$(Platform)\$(Configuration)\NQuantLibc.dll" "$(TargetDir)" - - - - \ No newline at end of file diff --git a/CSharp/examples/BermudanSwaption.cs b/CSharp/examples/BermudanSwaption/BermudanSwaption.cs similarity index 100% rename from CSharp/examples/BermudanSwaption.cs rename to CSharp/examples/BermudanSwaption/BermudanSwaption.cs diff --git a/CSharp/examples/BermudanSwaption/BermudanSwaption.csproj b/CSharp/examples/BermudanSwaption/BermudanSwaption.csproj new file mode 100644 index 0000000000..7503700c35 --- /dev/null +++ b/CSharp/examples/BermudanSwaption/BermudanSwaption.csproj @@ -0,0 +1,17 @@ + + + + net6.0 + false + Exe + + + + + + + + + + + diff --git a/CSharp/examples/EquityOption.csproj b/CSharp/examples/EquityOption.csproj deleted file mode 100644 index 0071984049..0000000000 --- a/CSharp/examples/EquityOption.csproj +++ /dev/null @@ -1,137 +0,0 @@ - - - - - Local - 9.0.21022 - 2.0 - {1FD947F1-D99E-46FB-8890-04E11E8340C2} - Debug - AnyCPU - - - - - EquityOption - - - JScript - Grid - IE50 - false - Exe - EquityOption - OnBuildSuccess - - - - - - - 3.5 - v4.0 - publish\ - true - Disk - false - Foreground - 7 - Days - false - false - true - 0 - 1.0.0.%2a - false - false - true - - - - true - bin\$(Platform)\$(Configuration)\ - DEBUG;TRACE - 285212672 - 4096 - full - x86 - prompt - AllRules.ruleset - - - bin\$(Platform)\$(Configuration)\ - TRACE - 285212672 - true - 4096 - x86 - prompt - AllRules.ruleset - - - true - bin\$(Platform)\$(Configuration)\ - DEBUG;TRACE - 285212672 - 4096 - full - x64 - prompt - AllRules.ruleset - - - bin\$(Platform)\$(Configuration)\ - TRACE - 285212672 - true - 4096 - x64 - prompt - AllRules.ruleset - - - - System - - - System.Data - - - System.XML - - - - - Code - - - - - False - .NET Framework 3.5 SP1 Client Profile - false - - - False - .NET Framework 3.5 SP1 - true - - - False - Windows Installer 3.1 - true - - - - - {928f98ee-7d50-457f-9304-a6818dcf1079} - NQuantLib - - - - - copy "$(SolutionDir)cpp\bin\$(Platform)\$(Configuration)\NQuantLibc.dll" "$(TargetDir)" - - - - \ No newline at end of file diff --git a/CSharp/examples/EquityOption.cs b/CSharp/examples/EquityOption/EquityOption.cs similarity index 100% rename from CSharp/examples/EquityOption.cs rename to CSharp/examples/EquityOption/EquityOption.cs diff --git a/CSharp/examples/EquityOption/EquityOption.csproj b/CSharp/examples/EquityOption/EquityOption.csproj new file mode 100644 index 0000000000..7503700c35 --- /dev/null +++ b/CSharp/examples/EquityOption/EquityOption.csproj @@ -0,0 +1,17 @@ + + + + net6.0 + false + Exe + + + + + + + + + + + diff --git a/CSharp/examples/FiniteDifferenceMethods.csproj b/CSharp/examples/FiniteDifferenceMethods.csproj deleted file mode 100755 index b0c52451cd..0000000000 --- a/CSharp/examples/FiniteDifferenceMethods.csproj +++ /dev/null @@ -1,137 +0,0 @@ - - - - - Local - 9.0.21022 - 2.0 - {EF2AFADF-B632-4E95-BEB5-7B7109A9E4FD} - Debug - AnyCPU - - - - - FiniteDifferenceMethods - - - JScript - Grid - IE50 - false - Exe - FiniteDifferenceMethods - OnBuildSuccess - - - - - - - 3.5 - v4.0 - publish\ - true - Disk - false - Foreground - 7 - Days - false - false - true - 0 - 1.0.0.%2a - false - false - true - - - - true - bin\$(Platform)\$(Configuration)\ - DEBUG;TRACE - 285212672 - 4096 - full - x86 - prompt - AllRules.ruleset - - - bin\$(Platform)\$(Configuration)\ - TRACE - 285212672 - true - 4096 - x86 - prompt - AllRules.ruleset - - - true - bin\$(Platform)\$(Configuration)\ - DEBUG;TRACE - 285212672 - 4096 - full - x64 - prompt - AllRules.ruleset - - - bin\$(Platform)\$(Configuration)\ - TRACE - 285212672 - true - 4096 - x64 - prompt - AllRules.ruleset - - - - System - - - System.Data - - - System.XML - - - - - Code - - - - - False - .NET Framework 3.5 SP1 Client Profile - false - - - False - .NET Framework 3.5 SP1 - true - - - False - Windows Installer 3.1 - true - - - - - {928f98ee-7d50-457f-9304-a6818dcf1079} - NQuantLib - - - - - copy "$(SolutionDir)cpp\bin\$(Platform)\$(Configuration)\NQuantLibc.dll" "$(TargetDir)" - - - - \ No newline at end of file diff --git a/CSharp/examples/FiniteDifferenceMethods.cs b/CSharp/examples/FiniteDifferenceMethods/FiniteDifferenceMethods.cs similarity index 100% rename from CSharp/examples/FiniteDifferenceMethods.cs rename to CSharp/examples/FiniteDifferenceMethods/FiniteDifferenceMethods.cs diff --git a/CSharp/examples/FiniteDifferenceMethods/FiniteDifferenceMethods.csproj b/CSharp/examples/FiniteDifferenceMethods/FiniteDifferenceMethods.csproj new file mode 100644 index 0000000000..7503700c35 --- /dev/null +++ b/CSharp/examples/FiniteDifferenceMethods/FiniteDifferenceMethods.csproj @@ -0,0 +1,17 @@ + + + + net6.0 + false + Exe + + + + + + + + + + + diff --git a/CSharp/examples/Times.csproj b/CSharp/examples/Times.csproj deleted file mode 100644 index 6b83ca300a..0000000000 --- a/CSharp/examples/Times.csproj +++ /dev/null @@ -1,137 +0,0 @@ - - - - - Local - 9.0.21022 - 2.0 - {C93F5204-5BC9-4AB3-AC06-C2CCE166CEBD} - Debug - AnyCPU - - - - - Times - - - JScript - Grid - IE50 - false - Exe - TimesTest - OnBuildSuccess - - - - - - - 3.5 - v4.0 - publish\ - true - Disk - false - Foreground - 7 - Days - false - false - true - 0 - 1.0.0.%2a - false - false - true - - - - true - bin\$(Platform)\$(Configuration)\ - DEBUG;TRACE - 285212672 - 4096 - full - x86 - prompt - AllRules.ruleset - - - bin\$(Platform)\$(Configuration)\ - TRACE - 285212672 - true - 4096 - x86 - prompt - AllRules.ruleset - - - true - bin\$(Platform)\$(Configuration)\ - DEBUG;TRACE - 285212672 - 4096 - full - x64 - prompt - AllRules.ruleset - - - bin\$(Platform)\$(Configuration)\ - TRACE - 285212672 - true - 4096 - x64 - prompt - AllRules.ruleset - - - - System - - - System.Data - - - System.XML - - - - - Code - - - - - False - .NET Framework 3.5 SP1 Client Profile - false - - - False - .NET Framework 3.5 SP1 - true - - - False - Windows Installer 3.1 - true - - - - - {928f98ee-7d50-457f-9304-a6818dcf1079} - NQuantLib - - - - - copy "$(SolutionDir)cpp\bin\$(Platform)\$(Configuration)\NQuantLibc.dll" "$(TargetDir)" - - - - \ No newline at end of file diff --git a/CSharp/examples/Times.cs b/CSharp/examples/Times/Times.cs similarity index 91% rename from CSharp/examples/Times.cs rename to CSharp/examples/Times/Times.cs index eebfd773df..9da98ac33a 100644 --- a/CSharp/examples/Times.cs +++ b/CSharp/examples/Times/Times.cs @@ -20,7 +20,7 @@ under the terms of the QuantLib license. You should have received a using System; using System.Collections.Generic; using System.Diagnostics; -using ql = QuantLib; +using QL = QuantLib; namespace TimesTest { @@ -55,7 +55,7 @@ static void Main(string[] args) private static void RunTestCases() { - Action> writePeriods = (heading, periods2Write) => + Action> writePeriods = (heading, periods2Write) => { Console.Write($" {heading}: "); foreach (var item in periods2Write) @@ -71,15 +71,15 @@ private static void RunTestCases() if (!testResult) throw new TestCaseException(); }; - var tenorNull = null as ql.Period; - var tenor91D = new ql.Period("91D"); - var tenor03M = new ql.Period("03M"); - var tenor06M = new ql.Period("06M"); - var tenor12M = new ql.Period("12M"); - var tenor01Y = new ql.Period("01Y"); - var tenor02Y = new ql.Period("02Y"); + var tenorNull = null as QL.Period; + var tenor91D = new QL.Period("91D"); + var tenor03M = new QL.Period("03M"); + var tenor06M = new QL.Period("06M"); + var tenor12M = new QL.Period("12M"); + var tenor01Y = new QL.Period("01Y"); + var tenor02Y = new QL.Period("02Y"); - var periods = new List() { tenor01Y, tenorNull, tenor02Y, tenor06M, tenor03M }; + var periods = new List() { tenor01Y, tenorNull, tenor02Y, tenor06M, tenor03M }; Console.WriteLine("Testing sorting of a list."); writePeriods("Before sorting", periods); @@ -197,9 +197,6 @@ private static void RunTestCases() #endregion - Console.WriteLine("test Period.ToString()"); - testCase(tenor01Y.ToString() == tenor12M.ToString()); - Console.WriteLine("test Period.GetHashCode()"); testCase(tenor01Y.GetHashCode() == tenor12M.GetHashCode()); diff --git a/CSharp/examples/Times/Times.csproj b/CSharp/examples/Times/Times.csproj new file mode 100644 index 0000000000..7503700c35 --- /dev/null +++ b/CSharp/examples/Times/Times.csproj @@ -0,0 +1,17 @@ + + + + net6.0 + false + Exe + + + + + + + + + + + diff --git a/ChangeLog.txt b/ChangeLog.txt index b001e99598..b78fcb9b5c 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -1,341 +1,287 @@ -commit a8960538cec340d7f369b6995dbc1397a1ac32db +commit d47205c9603c8539e58e3eccd96cae38f1cbe6d6 Author: Luigi Ballabio -Date: Mon, 12 Apr 2021 11:12:19 +0200 +Date: Thu, 19 Oct 2023 11:41:57 +0200 - Set version to 1.25 final. + Set version to 1.33 final Python/setup.py | 2 +- configure.ac | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) -commit 33dd21a3c5ccf46322e32a5fe8eb9609c540cebe +commit f2ff537e22ba7388264fa114a7662b48bc9fe452 Author: Luigi Ballabio -Date: Sat, 15 Jan 2022 11:00:35 +0100 +Date: Fri, 12 Jan 2024 13:36:29 +0100 - Update news + Update test results - News.md | 2 ++ - 1 file changed, 2 insertions(+) - -commit 18cbbc59188ea5456f0fdd94d00180b15d334913 -Merge: a9615b1 5bc6331 -Author: Luigi Ballabio -Date: Sat, 15 Jan 2022 10:56:35 +0100 - - Merge pull request #440. - - Export `StrippedOptionlet` class - -commit 5bc633192733b01ba48a2d434281e9637ed8f32e -Author: Luigi Ballabio -Date: Sat, 15 Jan 2022 10:03:17 +0100 - - Export StrippedOptionlet class - - SWIG/old_volatility.i | 24 ++++++++++++++++++++++++ - 1 file changed, 24 insertions(+) - -commit a9615b13e3bba65d247e3876ed97d86ec8ad54f5 -Author: Luigi Ballabio -Date: Tue, 11 Jan 2022 10:06:04 +0100 - - Update version requirement for underlying C++ library - - SWIG/ql.i | 2 +- + Python/test/test_bonds.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) -commit b2396db7c114a057d512da42841dd45ea1eef20b +commit a5618dd568ffb8bc69717ebf1bd7960d85186896 Author: Luigi Ballabio -Date: Thu, 1 Apr 2021 14:55:08 +0200 +Date: Wed, 4 Oct 2023 14:18:47 +0200 - Set version to 1.25 rc + Set version to 1.33 rc Python/setup.py | 2 +- configure.ac | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) -commit a83f264af86509ef6b8c29cd95d2d29401845712 +commit a0df937f61e03287a2ad59e7f36a296949aeb5b9 Author: Luigi Ballabio -Date: Tue, 11 Jan 2022 09:35:16 +0100 +Date: Mon, 8 Jan 2024 13:13:07 +0100 - Update changelog and news + Update news and changelog - ChangeLog.txt | 526 ++++++++++++++++++---------------------------------------- - News.md | 39 ++--- - 2 files changed, 179 insertions(+), 386 deletions(-) + ChangeLog.txt | 366 +++++++++++++++++++++++++--------------------------------- + News.md | 29 +++-- + 2 files changed, 169 insertions(+), 226 deletions(-) -commit 01ac145d61f24f2fbea8b1e2d73bb0a9341aec05 -Merge: f4ed510 5984660 +commit 14d6885853693e60010c0cae983c5ae40172675b +Merge: 6b3550d a282dc1 Author: Luigi Ballabio -Date: Mon, 10 Jan 2022 11:52:27 +0100 +Date: Fri, 5 Jan 2024 21:45:52 +0100 - Merge pull request #439. - - Scheduler - Added missing methods + Export Burley 2020 Sobol generator (#604) -commit 5984660102af59afd1b4e81ae1c5adc2a0053b32 -Author: RalfKonrad -Date: Mon, 10 Jan 2022 10:49:52 +0100 +commit a282dc1f08d22149569006d6e5532ad8f9fbeee8 +Author: Luigi Ballabio +Date: Fri, 5 Jan 2024 19:40:33 +0100 - Added missing methods + Export Burley 2020 Sobol generator - SWIG/scheduler.i | 4 +++- - 1 file changed, 3 insertions(+), 1 deletion(-) + SWIG/randomnumbers.i | 35 +++++++++++++++++++++++++++++++++-- + 1 file changed, 33 insertions(+), 2 deletions(-) -commit f4ed5108b7377d45ae81ad83fb68251966b6ccbc -Merge: 62a0334 660dd01 +commit 6b3550de89b02a2daf56e24545bea00ec95c57a5 +Merge: 51baf3b f456d98 Author: Luigi Ballabio -Date: Fri, 7 Jan 2022 12:49:17 +0100 +Date: Fri, 5 Jan 2024 12:39:37 +0100 - Merge pull request #438. - - Export `CPICoupon`, `CPICashFlow`, `CPILeg` + Allow different calendars and frequencies in OISRateHelper (#600) -commit 660dd0178696fdc9c1109c82885bb70bcb607b2d +commit f69bd4569c8b1de4b891fff89d352faecce60aca Author: Luigi Ballabio -Date: Fri, 7 Jan 2022 12:08:40 +0100 +Date: Fri, 5 Jan 2024 11:38:53 +0100 - Export CPICoupon, CPICashFlow, CPILeg + Add typemap for optional - SWIG/inflation.i | 113 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- - 1 file changed, 111 insertions(+), 2 deletions(-) + SWIG/date.i | 26 ++++++++++++++++++++++++++ + 1 file changed, 26 insertions(+) -commit 62a033405fd489e92923db613d02cb22e72f474a -Merge: 6129456 4bd6335 +commit 51baf3b94d93b9212b969718b3c79a98276502d9 +Merge: 9b338b7 9e7187b Author: Luigi Ballabio -Date: Thu, 6 Jan 2022 20:38:19 +0100 +Date: Fri, 5 Jan 2024 10:31:43 +0100 - Merge pull request #437. - - Rename `WulinYongDoubleBarrierEngine` to `SuoWangDoubleBarrierEngine` + Export convex-monotone forward-rate curve (#603) -commit 6129456060c52d774c0c07176181c92c0d788111 -Merge: c5812a1 b3b92eb +commit 9e7187b55654428d0f1ed42f0c682ff5893d4315 Author: Luigi Ballabio -Date: Thu, 6 Jan 2022 20:04:54 +0100 +Date: Fri, 5 Jan 2024 08:39:21 +0100 - Merge pull request #436. - - Export new argument to `SabrSmileSection` constructor + Export convex-monotone forward-rate curve -commit 4bd633571b6df65819f0253701fad4cc9735e8bb -Author: Luigi Ballabio -Date: Thu, 6 Jan 2022 19:35:07 +0100 - - Rename WulinYongDoubleBarrierEngine to SuoWangDoubleBarrierEngine + SWIG/piecewiseyieldcurve.i | 1 + + 1 file changed, 1 insertion(+) - SWIG/barrieroptions.i | 23 ++++++++--------------- - 1 file changed, 8 insertions(+), 15 deletions(-) +commit 9b338b79e914475fc2c8d10ba32576bb379ae59c +Author: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> +Date: Thu, 21 Dec 2023 10:36:26 +0000 -commit c5812a17cc21a503676df9a6921641799131000b -Merge: eb2408d c299897 -Author: Luigi Ballabio -Date: Thu, 6 Jan 2022 19:19:37 +0100 + Update copyright list in license - Merge pull request #435. - - Export new constructor and `amount` method from `ForwardRateAgreement` + LICENSE.TXT | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) -commit eb2408df0cf9507493cca54cb81185bd6c784df6 -Merge: 7c9bdd4 3e5e6a7 +commit 5a99cf17ab29fccff39f784cfb917ab7c7f6ec50 +Merge: f6a7d43 e96ecf0 Author: Luigi Ballabio -Date: Thu, 6 Jan 2022 19:17:32 +0100 +Date: Thu, 21 Dec 2023 11:36:13 +0100 - Merge pull request #434. - - Export new constructors for `SofrFutureRateHelper` + SWIG changes for PR#1826, Heston Model: Support for Angled Contour Shift Integrals (#596) -commit b3b92eb017a249983bfe703de69203d277416de6 +commit e96ecf0519e96d82b19fcd517d33403d939a5c91 Author: Luigi Ballabio -Date: Thu, 6 Jan 2022 19:00:44 +0100 +Date: Thu, 21 Dec 2023 11:14:33 +0100 - Export new argument to SabrSmileSection constructor + Prevent generating non-existing constructor - SWIG/volatilities.i | 32 +++++++++++++++++++------------- - 1 file changed, 19 insertions(+), 13 deletions(-) + SWIG/options.i | 2 ++ + 1 file changed, 2 insertions(+) -commit 7c9bdd410f5f25cb57bf7e41f6418f405f0e8eea -Merge: 27af7b3 fe13e55 +commit f00a4c602edaf7074e76d806ca38cd079797a395 Author: Luigi Ballabio -Date: Thu, 6 Jan 2022 18:32:42 +0100 +Date: Thu, 21 Dec 2023 10:08:28 +0100 - Merge pull request #433. + Fix error on Java - Export new constructors for zero-inflation curves - -commit c299897c50bc366763caeb514aea2514bf14f8d4 -Author: Luigi Ballabio -Date: Thu, 6 Jan 2022 18:32:08 +0100 + Inner classes need to be declared before they're used - Rework FRA example in Java + SWIG/options.i | 134 ++++++++++++++++++++++++++------------------------------- + 1 file changed, 62 insertions(+), 72 deletions(-) - Java/examples/FRA.java | 15 ++++++--------- - 1 file changed, 6 insertions(+), 9 deletions(-) - -commit 3e5e6a74c62823789adbffd4db4492bb12c13276 +commit d2d80f7ce5ac128448bbd5486a88701ba3af348e Author: Luigi Ballabio -Date: Thu, 6 Jan 2022 18:15:02 +0100 +Date: Thu, 21 Dec 2023 08:52:47 +0100 - Export new constructors for SofrFutureRateHelper + Add missing inclusions - SWIG/ratehelpers.i | 43 +++++++++++++++++++++++++++---------------- - 1 file changed, 27 insertions(+), 16 deletions(-) + SWIG/asianoptions.i | 1 + + SWIG/options.i | 1 + + 2 files changed, 2 insertions(+) -commit 1af7dad10341688459b5f054b4a7c43a1befb039 -Author: Luigi Ballabio -Date: Thu, 6 Jan 2022 17:52:02 +0100 +commit fb65c39dda32b54c2be790afb1cbafc70c576224 +Author: klaus spanderen +Date: Sun, 17 Dec 2023 18:38:31 +0100 - Export new constructor and amount method from ForwardRateAgreement + eliminate separate enum - SWIG/fra.i | 29 ++++++++++++++++++----------- - 1 file changed, 18 insertions(+), 11 deletions(-) + SWIG/options.i | 5 ++--- + 1 file changed, 2 insertions(+), 3 deletions(-) -commit fe13e5596f75c859e92fd324e73073c2c086e829 -Author: Luigi Ballabio -Date: Thu, 6 Jan 2022 17:27:48 +0100 +commit f6a7d43603afbcc47cf1cbb2ff2727c78bd3abb2 +Author: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> +Date: Mon, 11 Dec 2023 18:46:45 +0000 - Export new constructors for zero-inflation curves + Bump actions/stale from 8 to 9 + + Bumps [actions/stale](https://github.com/actions/stale) from 8 to 9. + - [Release notes](https://github.com/actions/stale/releases) + - [Changelog](https://github.com/actions/stale/blob/main/CHANGELOG.md) + - [Commits](https://github.com/actions/stale/compare/v8...v9) + + --- + updated-dependencies: + - dependency-name: actions/stale + dependency-type: direct:production + update-type: version-update:semver-major + ... + + Signed-off-by: dependabot[bot] - SWIG/inflation.i | 25 +++++++++++++++++++++---- - 1 file changed, 21 insertions(+), 4 deletions(-) + .github/workflows/stale.yml | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) -commit 27af7b30de94dd814b19d2af419f8bf4a9eeec96 -Merge: 9501715 cf56571 -Author: Luigi Ballabio -Date: Thu, 30 Dec 2021 14:16:30 +0100 +commit d08a20ee1d343e4063075953b73e4ae2103f04df +Author: Eugene Toder +Date: Sat, 2 Dec 2023 07:09:03 -0500 - Merge pull request #392. + Allow different calendars and frequencies in OISRateHelper - added `to_sparse_matrix` to finite difference linear operators - -commit cf56571840158cb54669cc62fae032656e15c01c -Author: Luigi Ballabio -Date: Thu, 30 Dec 2021 12:45:58 +0100 - - Simplify interface + Allow setting different calendars and payment frequencies for fixed and + overnight legs. - SWIG/fdm.i | 32 +++++--------------------------- - 1 file changed, 5 insertions(+), 27 deletions(-) + SWIG/ratehelpers.i | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) -commit 950171513cd9adfdc6e10f8b9c4a372c8b0e23ce -Merge: 5df3d59 4fbbcc9 +commit 5babd27f2683dd798222b376f17edfe8ebf99596 +Merge: 0dba37c 403e8bd Author: Luigi Ballabio -Date: Tue, 28 Dec 2021 19:58:06 +0100 +Date: Thu, 9 Nov 2023 17:29:04 +0100 - Merge pull request #432. - - Export basis-swap rate helpers + Change payment lag from Naturals to Ints (#598) -commit 5df3d593abf8decd330d3e90db4d81d52dfca845 +commit 0dba37cf99ed25f5eff00979f684e75003ac69b0 +Merge: 84e1c52 995bdd4 Author: Luigi Ballabio -Date: Tue, 28 Dec 2021 18:30:44 +0100 +Date: Thu, 9 Nov 2023 15:23:27 +0100 - Add make target for Python wheel and use it in CI build. - - This avoids using deprecated distutils features that currently - cause the build to fail. + Expose `reset` function in calendar base class (#597) - .build/python3.build | 3 ++- - Python/Makefile.am | 3 +++ - 2 files changed, 5 insertions(+), 1 deletion(-) +commit 995bdd4fe356ce559e80dc69d9880e76a08d5011 +Author: Fredrik Gerdin Börjesson +Date: Thu, 9 Nov 2023 12:21:50 +0100 -commit 4fbbcc98732bf940a83d338efebdcc6f21a6af64 -Author: Luigi Ballabio -Date: Tue, 28 Dec 2021 16:47:50 +0100 + Expose `resetAddedAndRemovedHolidays` for calendar - Export basis-swap rate helpers + Python/test/test_calendars.py | 15 +++++++++++++++ + SWIG/calendars.i | 2 ++ + 2 files changed, 17 insertions(+) - SWIG/ratehelpers.i | 31 +++++++++++++++++++++++++++++++ - 1 file changed, 31 insertions(+) +commit 403e8bd723f01988cf6b842350c79b603feb63bf +Author: Fredrik Gerdin Börjesson +Date: Thu, 9 Nov 2023 12:16:36 +0100 -commit 74fdfa74ea1c78e4a5fdf503a2f470f6071f9a34 -Merge: b879eb8 c9aa118 -Author: Luigi Ballabio -Date: Fri, 3 Dec 2021 19:31:50 +0100 + Change payment lag from Naurals to Ints - Merge pull request #431. - - Update interface for convertible bonds and engine + SWIG/bonds.i | 4 ++-- + SWIG/cashflows.i | 16 ++++++++-------- + SWIG/ratehelpers.i | 4 ++-- + SWIG/swap.i | 8 ++++---- + 4 files changed, 16 insertions(+), 16 deletions(-) -commit c9aa1185083c902f6bee0619bb09702e8e65495f -Author: Luigi Ballabio -Date: Fri, 3 Dec 2021 17:42:49 +0100 +commit 4a1d510abefcf879eb8cab5f16fb4147e5169f82 +Author: klaus spanderen +Date: Mon, 6 Nov 2023 22:52:11 +0100 - Remove obsolete tests + make constructor explicit - Python/test/inflation.py | 128 ----------------------------------------------- - 1 file changed, 128 deletions(-) + SWIG/integrals.i | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) -commit d329b3caf316bc1e7666ef8a1b0295b8c72cbd89 +commit 84e1c52af3f726b16654ca19df1c20d7d41dd183 +Merge: 8eeed9b d96b955 Author: Luigi Ballabio -Date: Fri, 3 Dec 2021 17:42:27 +0100 +Date: Fri, 20 Oct 2023 22:08:48 +0200 - Fix inheritance declaration + Avoid features deprecated in version 1.28 and removed in 1.33 (#593) - SWIG/fra.i | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) +commit 8eeed9b624f55b8192e89c292677840afc33cdaf +Author: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> +Date: Fri, 20 Oct 2023 13:24:21 +0000 -commit 712f81d0e28352a6a1b1e6f92f0fea172106c1ef -Author: Luigi Ballabio -Date: Fri, 3 Dec 2021 17:41:55 +0100 - - Update interface for convertible bonds and engine + Update copyright list in license - SWIG/convertiblebonds.i | 17 +++++++---------- - 1 file changed, 7 insertions(+), 10 deletions(-) + LICENSE.TXT | 1 + + 1 file changed, 1 insertion(+) -commit b879eb82acd1df332df383f236703552524416b5 -Merge: e8df1a2 8780b19 +commit f2ce94460fba41c0800139170e74273025bee34d +Merge: 2a70b4c dbe9a48 Author: Luigi Ballabio -Date: Sun, 14 Nov 2021 09:50:03 +0100 +Date: Fri, 20 Oct 2023 15:24:10 +0200 - Merge pull request #424. - - Export ESTR + Add tests for ql.BondFunctions.* (#592) -commit 8780b19c2077f544dda1ae15ada6a82777f7f1c1 -Author: kismsu -Date: Sat, 13 Nov 2021 18:15:41 +0000 +commit d96b955bccb50534e86ae59fb9ebce58ec4f7e65 +Author: Luigi Ballabio +Date: Fri, 20 Oct 2023 12:16:11 +0200 - Export ESTR + Avoid features deprecated in version 1.28 and removed in 1.33 - SWIG/indexes.i | 1 + - 1 file changed, 1 insertion(+) + Python/test/test_bonds.py | 48 ++++----------- + SWIG/bonds.i | 150 ++-------------------------------------------- + SWIG/volatilities.i | 6 -- + 3 files changed, 14 insertions(+), 190 deletions(-) -commit e8df1a2f23b2f00be9d63893a35db929410905ab +commit 2a70b4c2a11dd3f2be0148643374abd0ab4db131 Author: Luigi Ballabio -Date: Thu, 15 Apr 2021 16:27:56 +0200 +Date: Fri, 20 Oct 2023 11:34:41 +0200 - Set version to 1.25-dev. + Set version to 1.33-dev Python/setup.py | 2 +- R/DESCRIPTION | 2 +- configure.ac | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) -commit 4947fdc072a089d2b72d4c022f60f7a09fa8d1ca -Author: klausspanderen -Date: Sun, 17 Oct 2021 10:25:31 +0200 - - fixed bug - - SWIG/fdm.i | 25 +++++++++---------------- - 1 file changed, 9 insertions(+), 16 deletions(-) - -commit bc67532086c9dffa2bb451ac0674a67946da0a73 -Author: klausspanderen -Date: Mon, 23 Aug 2021 21:56:45 +0200 +commit dbe9a484142577ddad970379867927a44f3fd5ac +Author: Francois Botha +Date: Wed, 18 Oct 2023 14:50:59 +0200 - added FdmMesher signature and layout method + Add tests for ql.BondFunctions.* - SWIG/fdm.i | 16 ++++++++++++++++ - 1 file changed, 16 insertions(+) + Python/test/test_bondfunctions.py | 232 ++++++++++++++++++++++++++++++++++++++ + 1 file changed, 232 insertions(+) -commit 5c53d15723657d747bdd16f3c2d16e0617bb74a5 -Author: klausspanderen -Date: Sun, 27 Jun 2021 19:50:12 +0200 +commit 0448ac9055765af678d22cde159706b45b3d2746 +Author: klaus spanderen +Date: Wed, 11 Oct 2023 22:42:16 +0200 - added to_sparse_matrix to finite difference linear operators + Heston update - SWIG/fdm.i | 40 ++++++++++++++++++++++++++++++++++++++++ - 1 file changed, 40 insertions(+) + SWIG/daycounters.i | 6 +++ + SWIG/integrals.i | 27 ++++++++++ + SWIG/interpolation.i | 2 + + SWIG/options.i | 144 +++++++++++++++++++++++++++++---------------------- + 4 files changed, 118 insertions(+), 61 deletions(-) diff --git a/Java/Makefile.am b/Java/Makefile.am index b14bd346f0..4b7164321c 100644 --- a/Java/Makefile.am +++ b/Java/Makefile.am @@ -3,7 +3,7 @@ CLEANFILES = quantlib_wrap.cpp libQuantLibJNI.@JNILIB_EXTENSION@ QuantLib.jar BUILT_SOURCES = quantlib_wrap.cpp quantlib_wrap.h -EXAMPLES = Bonds CallableBonds CallableBondsOAS DiscreteHedging EquityOptions FRA FunctionDelegates +EXAMPLES = Bonds CallableBonds CallableBondsOAS DiscreteHedging EquityOptions FRA FunctionDelegates Time SWIGFLAGS = diff --git a/Java/examples/Time.java b/Java/examples/Time.java new file mode 100644 index 0000000000..07fd5af467 --- /dev/null +++ b/Java/examples/Time.java @@ -0,0 +1,24 @@ +package examples; + +import org.quantlib.Date; +import org.quantlib.Month; +import java.time.LocalDate; + +public class Time { + + + public static void main(String[] args) throws Exception { + + Date qlDate = new Date(18, Month.April, 2023); + LocalDate localDate = LocalDate.of(2023, 4, 18); + + Date qlDateFromLocalDate = Date.of(localDate); + LocalDate localDateFromQlDate = qlDate.toLocalDate(); + + System.out.println("qlDate: " + qlDate); + System.out.println("qlDateFromLocalDate: " + qlDateFromLocalDate); + System.out.println("localDate: " + localDate); + System.out.println("localDateFromQlDate: " + localDateFromQlDate); + } +} + diff --git a/LICENSE.TXT b/LICENSE.TXT index 183c5189ba..28c610fdd6 100644 --- a/LICENSE.TXT +++ b/LICENSE.TXT @@ -1,7 +1,7 @@ QuantLib-SWIG is Copyright (C) 2000, 2001, 2002, 2003 RiskMap srl Copyright (C) 2002, 2003 Ferdinando Ametrano - Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2014, 2020 StatPro Italia srl + Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2014, 2020, 2022, 2023 StatPro Italia srl Copyright (C) 2005 Dominic Thuillier Copyright (C) 2005 Johan Witters Copyright (C) 2006, 2007, 2010 Joseph Wang @@ -14,7 +14,7 @@ QuantLib-SWIG is Copyright (C) 2009 Joseph Malicki Copyright (C) 2010 Andrea Odetti Copyright (C) 2010, 2011 Lluis Pujol Bajador - Copyright (C) 2010, 2011, 2012, 2013, 2014, 2015, 2018, 2019, 2020, 2021 Klaus Spanderen + Copyright (C) 2010, 2011, 2012, 2013, 2014, 2015, 2018, 2019, 2020, 2021, 2022, 2023 Klaus Spanderen Copyright (C) 2011, 2012 Tawanda Gwena Copyright (C) 2012 Francis Duffy Copyright (C) 2013 Simon Shakeshaft @@ -32,9 +32,12 @@ QuantLib-SWIG is Copyright (C) 2019 Pedro Coelho Copyright (C) 2019 Prasad Somwanshi Copyright (C) 2020 Gorazd Brumen - Copyright (C) 2020, 2021 Jack Gillett - Copyright (C) 2020, 2021 Marcin Rybacki + Copyright (C) 2020, 2021, 2022 Jack Gillett + Copyright (C) 2020, 2021, 2023 Marcin Rybacki Copyright (C) 2021 Ralf Konrad Eckel + Copyright (C) 2022 Ignacio Anguita + Copyright (C) 2022, 2023, 2024 Skandinaviska Enskilda Banken AB (publ) + Copyright (C) 2023 Francois Botha Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/Makefile.am b/Makefile.am index 0175e214cf..8a0efb6eec 100644 --- a/Makefile.am +++ b/Makefile.am @@ -12,4 +12,4 @@ EXTRA_DIST = \ dist-hook: mkdir -p $(distdir)/SWIG cp -p ./SWIG/*.i $(distdir)/SWIG - cp -p ./requirements.txt $(distdir)/Python/examples/ + cp -p ./binder/requirements.txt $(distdir)/Python/examples/ diff --git a/News.md b/News.md index 462d9569d9..08d0161489 100644 --- a/News.md +++ b/News.md @@ -1,31 +1,25 @@ -Main changes for QuantLib-SWIG 1.25 +Main changes for QuantLib-SWIG 1.33 =================================== More details on the changes are available in ChangeLog.txt and at -. +. -- **Breaking change:** exported updated interface for convertible bonds and their engine. +- Exported Burley 2020 Sobol generator (@lballabio). -- **Breaking change (except for Python):** renamed `WulinYongDoubleBarrierEngine` - to `SuoWangDoubleBarrierEngine`. +- Allowed different calendars and frequencies for different legs in + `OISRateHelper`; thanks to Eugene Toder (@eltoder). -- Added a few missing methods to `Schedule` (thanks to Ralf Konrad). +- Exported convex-monotone forward-rate curve (@lballabio). -- Exported `CPICoupon`, `CPICashFlow`, `CPILeg`. +- Exported support for angled contour shift integrals in Heston model; + thanks to Klaus Spanderen (@klausspanderen). -- Exported new argument to `SabrSmileSection` constructor to allow normal volatilities. +- Allowed negative payment lag in swap legs; thanks to Fredrik Gerdin + Börjesson (@gbfredrik). -- Exported new constructor and `amount` method for `ForwardRateAgreement`. +- Exported `reset` method in calendars; thanks to Fredrik Gerdin + Börjesson (@gbfredrik). -- Exported new constructors for `SofrFutureRateHelper`. - -- Exported new constructors for zero-inflation curves. - -- Exported a few more finite-difference classes (thanks to Klaus Spanderen). - -- Exported new basis-swap rate helpers. - -- Exported `ESTR` class (thanks to Kirill Egorov). - -- Exported `StrippedOptionlet` class. +- Added Python tests for `BondFunctions`; thanks to Francois Botha + (@igitur). diff --git a/Python/Makefile.am b/Python/Makefile.am index 8603e10401..6de18c7412 100644 --- a/Python/Makefile.am +++ b/Python/Makefile.am @@ -28,7 +28,7 @@ endif endif QuantLib/quantlib_wrap.cpp QuantLib/QuantLib.py: ../SWIG/*.i - $(SWIG) -python -c++ -modern -outdir QuantLib \ + $(SWIG) -python -c++ -outdir QuantLib \ -o QuantLib/quantlib_wrap.cpp ../SWIG/quantlib.i dist-hook: diff --git a/Python/QuantLib-Python.spec.in b/Python/QuantLib-Python.spec.in index c5d3280dfe..192f7732a6 100644 --- a/Python/QuantLib-Python.spec.in +++ b/Python/QuantLib-Python.spec.in @@ -8,7 +8,7 @@ Group: System Environment/Libraries Packager: Liguo Song (Leo) Vendor: QuantLib.org Source0: http://prdownloads.sourceforge.net/quantlib/QuantLib-Python-%{version}.tar.gz -URL: http://quantlib.org/ +URL: https://www.quantlib.org/ Buildroot: %{_tmppath}/%{name}-%{version}-root BuildRequires: QuantLib-devel == %{version}, python >= 2.1 diff --git a/Python/QuantLib/__init__.py b/Python/QuantLib/__init__.py index 3c32904182..b029cec0a2 100644 --- a/Python/QuantLib/__init__.py +++ b/Python/QuantLib/__init__.py @@ -16,14 +16,8 @@ FOR A PARTICULAR PURPOSE. See the license for more details. """ -import sys -if sys.version_info.major >= 3: - from .QuantLib import * - from .QuantLib import _QuantLib -else: - from QuantLib import * - from QuantLib import _QuantLib -del sys +from .QuantLib import * +from . import _QuantLib __author__ = 'The QuantLib Group' __email__ = 'quantlib-users@lists.sourceforge.net' diff --git a/Python/README.txt b/Python/README.txt index 81f9e40135..11e0762fee 100644 --- a/Python/README.txt +++ b/Python/README.txt @@ -2,7 +2,8 @@ The C++ wrappers for the QuantLib-Python extension module are created by means of SWIG (Simplified Wrapper and Interface Generator) available from ; the latest version is suggested. -At this time, we only support Python 3.x. + +Building the wrappers requires the Setuptools package. Generating the wrappers is not required if you are using a distributed tarball. If you're building from a Git checkout, instead, use the @@ -10,12 +11,11 @@ command: python setup.py wrap -The commands to be issued for building, testing and installing the +The commands to be issued for building and testing the wrappers are: python setup.py build python setup.py test - python setup.py install respectively. @@ -25,10 +25,13 @@ found by the compiler. On Unix-like platforms, this requires that it requires you to define a `QL_DIR` environment variable pointing to your QuantLib directory (e.g., `C:\Lib\QuantLib`.) -The install step might require superuser privileges. -An alternate install location can be specified with the command: +The suggested way to install the module is to run first + + python setup.py bdist_wheel - python setup.py install --prefix=/home/johndoe +which requires the wheel module to be available and which will create +a QuantLib wheel in the dist folder. The resulting wheel can then +be installed with pip in the desired environment. The test suite is implemented on top of the PyUnit framework, which is included in the Python standard library. diff --git a/Python/examples/README.md b/Python/examples/README.md index e57e92b1ff..57cb3de54f 100644 --- a/Python/examples/README.md +++ b/Python/examples/README.md @@ -1,6 +1,6 @@ # QuantLib Python examples -[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/lballabio/QuantLib-SWIG/binder?urlpath=tree/Python/examples) +[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/lballabio/QuantLib-SWIG/binder?urlpath=lab/tree/Python/examples) This directory contains a number of examples of using QuantLib from Python. They can also run as Jupyter notebooks by means of @@ -11,7 +11,7 @@ If you're seeing this file as a notebook, you're probably there already. If not, you can click the "Launch Binder" badge at the top of this file. If you want to run these examples locally, you'll need the modules listed in the -`requirements.txt` file at the root of the [QuantLib-SWIG +`requirements.txt` file in the `binder` folder at the root of the [QuantLib-SWIG repository](https://github.com/lballabio/QuantLib-SWIG); to install them, you can execute diff --git a/Python/examples/american-option.py b/Python/examples/american-option.py index 082b83734e..4f7504fef2 100644 --- a/Python/examples/american-option.py +++ b/Python/examples/american-option.py @@ -102,6 +102,25 @@ option.setPricingEngine(ql.FdBlackScholesVanillaEngine(process, timeSteps, gridPoints)) results.append(("finite differences", option.NPV())) + +# %% [markdown] +# #### Li, M. QD+ American engine + +# %% +option.setPricingEngine(ql.QdPlusAmericanEngine(process)) +results.append(("QD+", option.NPV())) + + +# %% [markdown] +# #### Leif Andersen, Mark Lake and Dimitri Offengenden high performance American engine + +# %% +option.setPricingEngine( + ql.QdFpAmericanEngine(process, ql.QdFpAmericanEngine.accurateScheme()) +) +results.append(("QD+ fixed point", option.NPV())) + + # %% [markdown] # #### Binomial method @@ -118,7 +137,7 @@ # %% df = pd.DataFrame(results, columns=["Method", "Option value"]) -df.style.hide_index() +df.style.hide(axis="index") # %% [markdown] # The following displays the results when this is run as a Python script (in which case the cell above is not displayed). diff --git a/Python/examples/basket-option.py b/Python/examples/basket-option.py index c455b24410..b43a6eeb69 100644 --- a/Python/examples/basket-option.py +++ b/Python/examples/basket-option.py @@ -83,19 +83,19 @@ basketoption.setPricingEngine( ql.MCEuropeanBasketEngine(process, "pseudorandom", timeStepsPerYear=1, requiredTolerance=0.02, seed=42) ) -print(basketoption.NPV()) +print("Maximum Basket Payoff: ", basketoption.NPV()) basketoption = ql.BasketOption(ql.MinBasketPayoff(payoff), exercise) basketoption.setPricingEngine( ql.MCEuropeanBasketEngine(process, "pseudorandom", timeStepsPerYear=1, requiredTolerance=0.02, seed=42) ) -print(basketoption.NPV()) +print("Minimum Basket Payoff: ", basketoption.NPV()) basketoption = ql.BasketOption(ql.AverageBasketPayoff(payoff, 2), exercise) basketoption.setPricingEngine( ql.MCEuropeanBasketEngine(process, "pseudorandom", timeStepsPerYear=1, requiredTolerance=0.02, seed=42) ) -print(basketoption.NPV()) +print("Average Basket Payoff: ", basketoption.NPV()) americanExercise = ql.AmericanExercise(settlementDate, ql.Date(17, ql.May, 1999)) americanbasketoption = ql.BasketOption(ql.MaxBasketPayoff(payoff), americanExercise) @@ -110,4 +110,4 @@ polynomType=ql.LsmBasisSystem.Hermite, ) ) -print(americanbasketoption.NPV()) +print("Basket American Exercise: ", americanbasketoption.NPV()) diff --git a/Python/examples/bermudan-swaption.py b/Python/examples/bermudan-swaption.py index 9345d842c4..3e04db1732 100644 --- a/Python/examples/bermudan-swaption.py +++ b/Python/examples/bermudan-swaption.py @@ -78,7 +78,7 @@ def calibrate(model, helpers, l, name): # This is a flat yield term structure implying a 1x5 swap at 5%. -rate = ql.QuoteHandle(ql.SimpleQuote(0.04875825)) +rate = ql.makeQuoteHandle(0.04875825) termStructure = ql.YieldTermStructureHandle(ql.FlatForward(settlementDate, rate, ql.Actual365Fixed())) # Define the ATM/OTM/ITM swaps: @@ -151,7 +151,7 @@ def calibrate(model, helpers, l, name): ql.SwaptionHelper( maturity, length, - ql.QuoteHandle(ql.SimpleQuote(vol)), + ql.makeQuoteHandle(vol), index, index.tenor(), index.dayCounter(), diff --git a/Python/examples/bonds.py b/Python/examples/bonds.py index 78d72f7e90..3514b28cde 100644 --- a/Python/examples/bonds.py +++ b/Python/examples/bonds.py @@ -63,7 +63,7 @@ zcHelpers = [ ql.DepositRateHelper( - ql.QuoteHandle(ql.SimpleQuote(r)), tenor, fixingDays, calendar, ql.ModifiedFollowing, True, zcBondsDayCounter + ql.makeQuoteHandle(r), tenor, fixingDays, calendar, ql.ModifiedFollowing, True, zcBondsDayCounter ) for (r, tenor) in zcQuotes ] @@ -98,7 +98,7 @@ ) bondsHelpers.append( ql.FixedRateBondHelper( - ql.QuoteHandle(ql.SimpleQuote(marketQuote)), + ql.makeQuoteHandle(marketQuote), settlementDays, 100.0, schedule, @@ -139,7 +139,7 @@ depositDayCounter = ql.Actual360() depositHelpers = [ ql.DepositRateHelper( - ql.QuoteHandle(ql.SimpleQuote(rate)), tenor, fixingDays, calendar, ql.ModifiedFollowing, True, depositDayCounter + ql.makeQuoteHandle(rate), tenor, fixingDays, calendar, ql.ModifiedFollowing, True, depositDayCounter ) for rate, tenor in dQuotes ] @@ -151,7 +151,7 @@ forwardStart = ql.Period(1, ql.Days) swapHelpers = [ ql.SwapRateHelper( - ql.QuoteHandle(ql.SimpleQuote(rate)), + ql.makeQuoteHandle(rate), tenor, calendar, swFixedLegFrequency, diff --git a/Python/examples/callablebonds.py b/Python/examples/callablebonds.py new file mode 100644 index 0000000000..75c81ad9c5 --- /dev/null +++ b/Python/examples/callablebonds.py @@ -0,0 +1,79 @@ +# --- +# jupyter: +# jupytext: +# formats: py:light +# text_representation: +# extension: .py +# format_name: light +# format_version: '1.5' +# jupytext_version: 1.14.5 +# kernelspec: +# display_name: Python 3 (ipykernel) +# language: python +# name: python3 +# --- + +# # Callable bonds +# +# This file is part of QuantLib, a free-software/open-source library +# for financial quantitative analysts and developers - https://www.quantlib.org/ +# +# QuantLib is free software: you can redistribute it and/or modify it under the +# terms of the QuantLib license. You should have received a copy of the +# license along with this program; if not, please email +# . The license is also available online at +# . +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the license for more details. + +import QuantLib as ql +import numpy as np + +calcDate = ql.Date(16, 8, 2006) +ql.Settings.instance().evaluationDate = calcDate + +dayCount = ql.ActualActual(ql.ActualActual.Bond) +rate = 0.0465 +termStructure = ql.FlatForward(calcDate, rate, dayCount, ql.Compounded, ql.Semiannual) +term_structure_handle = ql.RelinkableYieldTermStructureHandle(termStructure) + +callabilitySchedule = ql.CallabilitySchedule() +callPrice = 100.0 +callDate = ql.Date(15, ql.September, 2006); +nc = ql.NullCalendar() + +# Number of calldates is 24 +for i in range(0, 24): + callabilityPrice = ql.BondPrice(callPrice, ql.BondPrice.Clean) + callabilitySchedule.append(ql.Callability(callabilityPrice, ql.Callability.Call, callDate)) + callDate = nc.advance(callDate, 3, ql.Months) + +issueDate = ql.Date(16, ql.September, 2004) +maturityDate = ql.Date(15, ql.September, 2012) +calendar = ql.UnitedStates(ql.UnitedStates.GovernmentBond) +tenor = ql.Period(ql.Quarterly) +accrualConvention = ql.Unadjusted +schedule = ql.Schedule(issueDate, maturityDate, tenor, calendar, + accrualConvention, accrualConvention, ql.DateGeneration.Backward, False) + +settlement_days = 3 +faceAmount = 100 +accrual_daycount = ql.ActualActual(ql.ActualActual.Bond) +coupon = 0.025 +bond = ql.CallableFixedRateBond(settlement_days, faceAmount, schedule, + [coupon], accrual_daycount, ql.Following, + faceAmount, issueDate, callabilitySchedule) + +def engine(a, s, grid_points): + model = ql.HullWhite(term_structure_handle, a, s) + return ql.TreeCallableFixedRateBondEngine(model, grid_points) + +# 6% mean reversion and 20% volatility +bond.setPricingEngine(engine(0.06, 0.20, 40)) +print("Bond price: ", bond.cleanPrice()) + +# 3% mean reversion and 15% volatility +bond.setPricingEngine(engine(0.03, 0.15, 40)) +print("Bond price: ", bond.cleanPrice()) diff --git a/Python/examples/capsfloors.py b/Python/examples/capsfloors.py new file mode 100644 index 0000000000..9c2c3517c1 --- /dev/null +++ b/Python/examples/capsfloors.py @@ -0,0 +1,71 @@ +# --- +# jupyter: +# jupytext: +# formats: py:light +# text_representation: +# extension: .py +# format_name: light +# format_version: '1.5' +# jupytext_version: 1.14.5 +# kernelspec: +# display_name: Python 3 (ipykernel) +# language: python +# name: python3 +# --- + +# # Caps and Floors +# +# This file is part of QuantLib, a free-software/open-source library +# for financial quantitative analysts and developers - https://www.quantlib.org/ +# +# QuantLib is free software: you can redistribute it and/or modify it under the +# terms of the QuantLib license. You should have received a copy of the +# license along with this program; if not, please email +# . The license is also available online at +# . +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the license for more details. + +import QuantLib as ql + +calcDate = ql.Date(14, 6, 2016) +ql.Settings.instance().evaluationDate = calcDate + +dates = [ql.Date(14,6,2016), ql.Date(14,9,2016), + ql.Date(14,12,2016), ql.Date(14,6,2017), + ql.Date(14,6,2019), ql.Date(14,6,2021), + ql.Date(15,6,2026), ql.Date(16,6,2031), + ql.Date(16,6,2036), ql.Date(14,6,2046)] + +yields = [0.000000, 0.006616, 0.007049, 0.007795, + 0.009599, 0.011203, 0.015068, 0.017583, + 0.018998, 0.020080] + +dayCount = ql.ActualActual(ql.ActualActual.Bond) +calendar = ql.UnitedStates(ql.UnitedStates.GovernmentBond) +interpolation = ql.Linear() +compounding = ql.Compounded +compoundingFrequency = ql.Annual +term_structure = ql.ZeroCurve(dates, yields, dayCount, calendar, interpolation, compounding, compoundingFrequency) +ts_handle = ql.YieldTermStructureHandle(term_structure) + +start_date = ql.Date(14, 6, 2016) +end_date = ql.Date(14, 6 , 2026) +period = ql.Period(3, ql.Months) +buss_convention = ql.ModifiedFollowing +rule = ql.DateGeneration.Forward +end_of_month = False +schedule = ql.Schedule(start_date, end_date, period, calendar, buss_convention, buss_convention, rule, end_of_month) + +iborIndex = ql.USDLibor(ql.Period(3, ql.Months), ts_handle) +iborIndex.addFixing(ql.Date(10,6,2016), 0.0065560) +ibor_leg = ql.IborLeg([1000000], schedule, iborIndex) + +strike = 0.02 +cap = ql.Cap(ibor_leg, [strike]) +vols = ql.makeQuoteHandle(0.547295) +engine = ql.BlackCapFloorEngine(ts_handle, vols) +cap.setPricingEngine(engine) +print("Value of Caps given constant volatility:", cap.NPV()) diff --git a/Python/examples/cds.py b/Python/examples/cds.py index cf2828fe10..85aded6a69 100644 --- a/Python/examples/cds.py +++ b/Python/examples/cds.py @@ -50,7 +50,7 @@ instruments = [ ql.SpreadCdsHelper( - ql.QuoteHandle(ql.SimpleQuote(s)), + ql.makeQuoteHandle(s), tenor, 0, calendar, diff --git a/Python/examples/european-option.py b/Python/examples/european-option.py index a2c0126e35..d99b88b348 100644 --- a/Python/examples/european-option.py +++ b/Python/examples/european-option.py @@ -183,7 +183,7 @@ columns=["Method", "Option value", "Error estimate", "Actual error"]) # %% -df.style.hide_index() +df.style.hide(axis="index") # %% [markdown] # The following displays the results when this is run as a Python script (in which case the cell above is not displayed). diff --git a/Python/examples/gaussian1d-models.py b/Python/examples/gaussian1d-models.py index 69671f46d3..9a8e3011e6 100644 --- a/Python/examples/gaussian1d-models.py +++ b/Python/examples/gaussian1d-models.py @@ -94,7 +94,7 @@ def calibration_data(basket, volatilities): # %% refDate = ql.Date(30, 4, 2014) -ql.Settings.instance().setEvaluationDate(refDate) +ql.Settings.instance().evaluationDate = refDate # %% [markdown] # We assume a multicurve setup, for simplicity with flat yield term structures. @@ -104,9 +104,9 @@ def calibration_data(basket, volatilities): # For the volatility we assume a flat swaption volatility at 20%. # %% -forward6mQuote = ql.QuoteHandle(ql.SimpleQuote(0.025)) -oisQuote = ql.QuoteHandle(ql.SimpleQuote(0.02)) -volQuote = ql.QuoteHandle(ql.SimpleQuote(0.2)) +forward6mQuote = ql.makeQuoteHandle(0.025) +oisQuote = ql.makeQuoteHandle(0.02) +volQuote = ql.makeQuoteHandle(0.2) # %% dc = ql.Actual365Fixed() @@ -156,7 +156,7 @@ def calibration_data(basket, volatilities): underlying = ql.NonstandardSwap( ql.Swap.Payer, fixedNominal, floatingNominal, fixedSchedule, strike, - ql.Thirty360(), floatSchedule, + ql.Thirty360(ql.Thirty360.BondBasis), floatSchedule, euribor6m, gearing, spread, ql.Actual360(), False, False, ql.ModifiedFollowing) # %% @@ -172,8 +172,8 @@ def calibration_data(basket, volatilities): # %% stepDates = exerciseDates[:-1] -sigmas = [ql.QuoteHandle(ql.SimpleQuote(0.01)) for x in range(1, 10)] -reversion = [ql.QuoteHandle(ql.SimpleQuote(0.01))] +sigmas = [ql.makeQuoteHandle(0.01) for x in range(1, 10)] +reversion = [ql.makeQuoteHandle(0.01)] # %% [markdown] # The model's curve is set to the 6m forward curve. Note that the model adapts automatically to other curves where appropriate (e.g. if an index requires a different forwarding curve) or where explicitly specified (e.g. in a swaption pricing engine). @@ -182,7 +182,7 @@ def calibration_data(basket, volatilities): gsr = ql.Gsr(t0_curve, stepDates, sigmas, reversion) swaptionEngine = ql.Gaussian1dSwaptionEngine(gsr, 64, 7.0, True, False, t0_Ois) nonstandardSwaptionEngine = ql.Gaussian1dNonstandardSwaptionEngine( - gsr, 64, 7.0, True, False, ql.QuoteHandle(ql.SimpleQuote(0)), t0_Ois) + gsr, 64, 7.0, True, False, ql.makeQuoteHandle(0.0), t0_Ois) # %% swaption.setPricingEngine(nonstandardSwaptionEngine) @@ -264,7 +264,7 @@ def calibration_data(basket, volatilities): # %% underlying2 = ql.NonstandardSwap(ql.Swap.Payer, fixedNominal, floatingNominal, fixedSchedule, strike, - ql.Thirty360(), floatSchedule, + ql.Thirty360(ql.Thirty360.BondBasis), floatSchedule, euribor6m, gearing, spread, ql.Actual360(), False, False, ql.ModifiedFollowing) # %% @@ -290,7 +290,7 @@ def calibration_data(basket, volatilities): # %% underlying3 = ql.NonstandardSwap(ql.Swap.Receiver, fixedNominal2, floatingNominal2, fixedSchedule, strike, - ql.Thirty360(), floatSchedule, + ql.Thirty360(ql.Thirty360.BondBasis), floatSchedule, euribor6m, gearing, spread, ql.Actual360(), False, True, ql.ModifiedFollowing) # %% @@ -362,7 +362,7 @@ def calibration_data(basket, volatilities): Euriborspread = [0.001]*(len(floatSchedule)-1) underlying4 = ql.FloatFloatSwap(ql.Swap.Payer, CMSNominal, EuriborNominal, - fixedSchedule, swapBase, ql.Thirty360(), + fixedSchedule, swapBase, ql.Thirty360(ql.Thirty360.BondBasis), floatSchedule, euribor6m, ql.Actual360(), False, False, CMSgearing, CMSspread, [], [], Euriborgearing, Euriborspread) @@ -370,7 +370,7 @@ def calibration_data(basket, volatilities): # %% swaption4 = ql.FloatFloatSwaption(underlying4, exercise) floatSwaptionEngine = ql.Gaussian1dFloatFloatSwaptionEngine( - gsr, 64, 7.0, True, False, ql.QuoteHandle(ql.SimpleQuote(0)), t0_Ois, True) + gsr, 64, 7.0, True, False, ql.makeQuoteHandle(0.0), t0_Ois, True) swaption4.setPricingEngine(floatSwaptionEngine) # %% [markdown] @@ -379,7 +379,7 @@ def calibration_data(basket, volatilities): # %% leg0 = underlying4.leg(0) leg1 = underlying4.leg(1) -reversionQuote = ql.QuoteHandle(ql.SimpleQuote(0.01)) +reversionQuote = ql.makeQuoteHandle(0.01) swaptionVolHandle = ql.SwaptionVolatilityStructureHandle(swaptionVol) cmsPricer = ql.LinearTsrPricer(swaptionVolHandle, reversionQuote) iborPricer = ql.BlackIborCouponPricer() @@ -445,7 +445,7 @@ def calibration_data(basket, volatilities): # %% floatEngineMarkov = ql.Gaussian1dFloatFloatSwaptionEngine( - markov, 16, 7.0, True, False, ql.QuoteHandle(ql.SimpleQuote(0)), t0_Ois, True) + markov, 16, 7.0, True, False, ql.makeQuoteHandle(0.0), t0_Ois, True) # %% [markdown] # The option npv is the markov model is: diff --git a/Python/examples/global-bootstrap.py b/Python/examples/global-bootstrap.py index c9290faaa3..0c9d77a24c 100644 --- a/Python/examples/global-bootstrap.py +++ b/Python/examples/global-bootstrap.py @@ -118,7 +118,7 @@ swapTenors = [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 15, 20, 25, 30, 35, 40, 45, 50] helpers += [ ql.SwapRateHelper( - r / 100.0, ql.Period(T, ql.Years), ql.TARGET(), ql.Annual, ql.ModifiedFollowing, ql.Thirty360(), index + r / 100.0, ql.Period(T, ql.Years), ql.TARGET(), ql.Annual, ql.ModifiedFollowing, ql.Thirty360(ql.Thirty360.BondBasis), index ) for r, T in zip(refMktRates[13:32], swapTenors) ] @@ -155,7 +155,7 @@ day_counter = ql.Actual360() compounding = ql.Simple else: - day_counter = ql.Thirty360() + day_counter = ql.Thirty360(ql.Thirty360.BondBasis) compounding = ql.SimpleThenCompounded r = curve.zeroRate(pillar, day_counter, compounding, ql.Annual).rate() diff --git a/Python/examples/isda-engine.py b/Python/examples/isda-engine.py index f1c2abd0a6..c927fc51ae 100644 --- a/Python/examples/isda-engine.py +++ b/Python/examples/isda-engine.py @@ -34,7 +34,7 @@ interactive = 'get_ipython' in globals() trade_date = ql.Date(21,5,2009) -ql.Settings.instance().setEvaluationDate(trade_date) +ql.Settings.instance().evaluationDate = trade_date ql.IborCoupon.createAtParCoupons() @@ -69,7 +69,7 @@ isdaRateHelpers = isdaRateHelpers + [ ql.SwapRateHelper(swap_quotes[i],swap_tenors[i]*ql.Period(ql.Annual), ql.WeekendsOnly(),ql.Semiannual,ql.ModifiedFollowing, - ql.Thirty360(),isda_ibor) + ql.Thirty360(ql.Thirty360.BondBasis),isda_ibor) for i in range(len(swap_tenors))] spot_date = ql.WeekendsOnly().advance(trade_date, 2 * ql.Period(ql.Daily)) @@ -142,7 +142,7 @@ probabilityCurve.linkTo( ql.FlatHazardRate(0,ql.WeekendsOnly(), - ql.QuoteHandle(ql.SimpleQuote(h)), + ql.makeQuoteHandle(h), ql.Actual365Fixed())) engine = ql.IsdaCdsEngine(probabilityCurve,recovery,discountCurve) diff --git a/Python/examples/slv.py b/Python/examples/slv.py index f50e8bb43a..250f37aa90 100644 --- a/Python/examples/slv.py +++ b/Python/examples/slv.py @@ -62,7 +62,7 @@ dc = ql.Actual365Fixed() spot = 100 -underlying = ql.QuoteHandle(ql.SimpleQuote(spot)) +underlying = ql.makeQuoteHandle(spot) riskFreeRate = ql.YieldTermStructureHandle(ql.FlatForward(settlementDate, 0.05, dc)) dividendYield = ql.YieldTermStructureHandle(ql.FlatForward(settlementDate, 0.025, dc)) diff --git a/Python/examples/swap.py b/Python/examples/swap.py index 1112d57e91..9959391c1b 100644 --- a/Python/examples/swap.py +++ b/Python/examples/swap.py @@ -129,7 +129,7 @@ ql.ModifiedFollowing, True, dayCounter, - ql.QuoteHandle(ql.SimpleQuote(0.0)), + ql.makeQuoteHandle(0.0), ) for d in futures.keys() ] @@ -146,7 +146,7 @@ fixedLegFrequency = ql.Annual fixedLegTenor = ql.Period(1, ql.Years) fixedLegAdjustment = ql.Unadjusted -fixedLegDayCounter = ql.Thirty360() +fixedLegDayCounter = ql.Thirty360(ql.Thirty360.BondBasis) floatingLegFrequency = ql.Quarterly floatingLegTenor = ql.Period(3, ql.Months) floatingLegAdjustment = ql.ModifiedFollowing @@ -195,7 +195,7 @@ # %% fixedLegFrequency = ql.Annual fixedLegAdjustment = ql.Unadjusted -fixedLegDayCounter = ql.Thirty360() +fixedLegDayCounter = ql.Thirty360(ql.Thirty360.BondBasis) fixedRate = 0.04 # %% diff --git a/Python/setup.py b/Python/setup.py index 7a04f66f78..41b80800ed 100644 --- a/Python/setup.py +++ b/Python/setup.py @@ -1,4 +1,4 @@ -# -*- coding: iso-8859-1 -*- +# -*- coding: utf-8 -*- """ Copyright (C) 2000, 2001, 2002, 2003 RiskMap srl Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 StatPro Italia srl @@ -17,20 +17,19 @@ FOR A PARTICULAR PURPOSE. See the license for more details. """ -import os, sys, math -try: - from setuptools import setup, Extension - from setuptools import Command -except: - from distutils.core import setup, Extension - from distutils.cmd import Command -from distutils.command.build_ext import build_ext -from distutils.command.build import build -from distutils.ccompiler import get_default_compiler +import os, sys, math, platform +from setuptools import setup, Extension +from setuptools import Command +from setuptools.command.build_ext import build_ext +from setuptools.command.build import build +from setuptools._distutils.ccompiler import get_default_compiler +from wheel.bdist_wheel import bdist_wheel + +py_limited_api = (platform.python_implementation() == 'CPython') class test(Command): # Original version of this class posted - # by Berthold Höllmann to distutils-sig@python.org + # by Berthold Höllmann to distutils-sig@python.org description = "test the distribution prior to install" user_options = [ @@ -78,10 +77,10 @@ def run(self): ' is recommended. \nSome features may not work.' .format(swig_version)) swig_dir = os.path.join("..","SWIG") - os.system('swig -python -c++ -modern ' + + os.system('swig -python -c++ ' + '-I%s ' % swig_dir + '-outdir QuantLib -o QuantLib/quantlib_wrap.cpp ' + - 'quantlib.i') + '%s/quantlib.i' % swig_dir) class my_build(build): user_options = build.user_options + [ @@ -112,6 +111,8 @@ def finalize_options(self): self.include_dirs = self.include_dirs or [] self.library_dirs = self.library_dirs or [] self.define = self.define or [] + if py_limited_api: + self.define += [('Py_LIMITED_API', '0x03080000')] self.libraries = self.libraries or [] extra_compile_args = [] @@ -145,12 +146,12 @@ def finalize_options(self): extra_link_args = ['/subsystem:windows', machinetype] if self.debug: - if self.static: + if self.static or 'QL_STATIC_RUNTIME' in os.environ: extra_compile_args.append('/MTd') else: extra_compile_args.append('/MDd') else: - if self.static: + if self.static or 'QL_STATIC_RUNTIME' in os.environ: extra_compile_args.append('/MT') else: extra_compile_args.append('/MD') @@ -194,6 +195,17 @@ def finalize_options(self): ext.extra_link_args = ext.extra_link_args or [] ext.extra_link_args += extra_link_args + +class my_bdist_wheel(bdist_wheel): + def get_tag(self): + python, abi, plat = super().get_tag() + + if python.startswith("cp"): + return "cp38", "abi3", plat + + return python, abi, plat + + classifiers = [ 'Development Status :: 5 - Production/Stable', 'Environment :: Console', @@ -212,27 +224,30 @@ def finalize_options(self): ] setup(name = "QuantLib", - version = "1.26-dev", + version = "1.34-dev", description = "Python bindings for the QuantLib library", long_description = """ -QuantLib (http://quantlib.org/) is a C++ library for financial quantitative -analysts and developers, aimed at providing a comprehensive software -framework for quantitative finance. +QuantLib (https://www.quantlib.org/) is a free/open-source C++ library +for financial quantitative analysts and developers, aimed at providing +a comprehensive software framework for quantitative finance. """, + long_description_content_type = "text/x-rst", author = "QuantLib Team", author_email = "quantlib-users@lists.sourceforge.net", - url = "http://quantlib.org", + url = "https://www.quantlib.org", license = "BSD 3-Clause", classifiers = classifiers, py_modules = ['QuantLib.__init__','QuantLib.QuantLib'], ext_modules = [Extension("QuantLib._QuantLib", - ["QuantLib/quantlib_wrap.cpp"]) + ["QuantLib/quantlib_wrap.cpp"], + py_limited_api=py_limited_api) ], data_files = [('share/doc/quantlib', ['../LICENSE.TXT'])], cmdclass = {'test': test, 'wrap': my_wrap, 'build': my_build, - 'build_ext': my_build_ext + 'build_ext': my_build_ext, + 'bdist_wheel': my_bdist_wheel, } ) diff --git a/Python/setup.py.in b/Python/setup.py.in index 340fd0e2d7..cd8f110401 100644 --- a/Python/setup.py.in +++ b/Python/setup.py.in @@ -1,4 +1,4 @@ -# -*- coding: iso-8859-1 -*- +# -*- coding: utf-8 -*- """ Copyright (C) 2000, 2001, 2002, 2003 RiskMap srl Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 StatPro Italia srl @@ -17,20 +17,19 @@ FOR A PARTICULAR PURPOSE. See the license for more details. """ -import os, sys, math -try: - from setuptools import setup, Extension - from setuptools import Command -except: - from distutils.core import setup, Extension - from distutils.cmd import Command -from distutils.command.build_ext import build_ext -from distutils.command.build import build -from distutils.ccompiler import get_default_compiler +import os, sys, math, platform +from setuptools import setup, Extension +from setuptools import Command +from setuptools.command.build_ext import build_ext +from setuptools.command.build import build +from setuptools._distutils.ccompiler import get_default_compiler +from wheel.bdist_wheel import bdist_wheel + +py_limited_api = (platform.python_implementation() == 'CPython') class test(Command): # Original version of this class posted - # by Berthold Höllmann to distutils-sig@python.org + # by Berthold Höllmann to distutils-sig@python.org description = "test the distribution prior to install" user_options = [ @@ -78,10 +77,10 @@ class my_wrap(Command): ' is recommended. \nSome features may not work.' .format(swig_version)) swig_dir = os.path.join("..","SWIG") - os.system('swig -python -c++ -modern ' + + os.system('swig -python -c++ ' + '-I%s ' % swig_dir + '-outdir QuantLib -o QuantLib/quantlib_wrap.cpp ' + - 'quantlib.i') + '%s/quantlib.i' % swig_dir) class my_build(build): user_options = build.user_options + [ @@ -112,6 +111,8 @@ class my_build_ext(build_ext): self.include_dirs = self.include_dirs or [] self.library_dirs = self.library_dirs or [] self.define = self.define or [] + if py_limited_api: + self.define += [('Py_LIMITED_API', '0x03080000')] self.libraries = self.libraries or [] extra_compile_args = [] @@ -145,12 +146,12 @@ class my_build_ext(build_ext): extra_link_args = ['/subsystem:windows', machinetype] if self.debug: - if self.static: + if self.static or 'QL_STATIC_RUNTIME' in os.environ: extra_compile_args.append('/MTd') else: extra_compile_args.append('/MDd') else: - if self.static: + if self.static or 'QL_STATIC_RUNTIME' in os.environ: extra_compile_args.append('/MT') else: extra_compile_args.append('/MD') @@ -194,6 +195,17 @@ class my_build_ext(build_ext): ext.extra_link_args = ext.extra_link_args or [] ext.extra_link_args += extra_link_args + +class my_bdist_wheel(bdist_wheel): + def get_tag(self): + python, abi, plat = super().get_tag() + + if python.startswith("cp"): + return "cp38", "abi3", plat + + return python, abi, plat + + classifiers = [ 'Development Status :: 5 - Production/Stable', 'Environment :: Console', @@ -215,24 +227,27 @@ setup(name = "QuantLib", version = "@PACKAGE_VERSION@", description = "Python bindings for the QuantLib library", long_description = """ -QuantLib (http://quantlib.org/) is a C++ library for financial quantitative -analysts and developers, aimed at providing a comprehensive software -framework for quantitative finance. +QuantLib (https://www.quantlib.org/) is a free/open-source C++ library +for financial quantitative analysts and developers, aimed at providing +a comprehensive software framework for quantitative finance. """, + long_description_content_type = "text/x-rst", author = "QuantLib Team", author_email = "quantlib-users@lists.sourceforge.net", - url = "http://quantlib.org", + url = "https://www.quantlib.org", license = "BSD 3-Clause", classifiers = classifiers, py_modules = ['QuantLib.__init__','QuantLib.QuantLib'], ext_modules = [Extension("QuantLib._QuantLib", - ["QuantLib/quantlib_wrap.cpp"]) + ["QuantLib/quantlib_wrap.cpp"], + py_limited_api=py_limited_api) ], data_files = [('share/doc/quantlib', ['../LICENSE.TXT'])], cmdclass = {'test': test, 'wrap': my_wrap, 'build': my_build, - 'build_ext': my_build_ext + 'build_ext': my_build_ext, + 'bdist_wheel': my_bdist_wheel, } ) diff --git a/Python/test/QuantLibTestSuite.py b/Python/test/QuantLibTestSuite.py index d7abc7648a..33d3664f74 100644 --- a/Python/test/QuantLibTestSuite.py +++ b/Python/test/QuantLibTestSuite.py @@ -16,93 +16,17 @@ FOR A PARTICULAR PURPOSE. See the license for more details. """ +import os import sys import unittest -from date import DateTest -from daycounters import DayCountersTest -from instruments import InstrumentTest -from marketelements import MarketElementTest -from integrals import IntegralTest -from solvers1d import Solver1DTest -from termstructures import TermStructureTest -from bonds import FixedRateBondTest, FixedRateBondKwargsTest -from ratehelpers import ( - FixedRateBondHelperTest, - FxSwapRateHelperTest, - OISRateHelperTest, - CrossCurrencyBasisSwapRateHelperTest) -from cms import CmsTest -from assetswap import AssetSwapTest -from capfloor import CapFloorTest -from blackformula import BlackFormulaTest -from blackformula import BlackDeltaCalculatorTest -from iborindex import IborIndexTest -from sabr import SabrTest -from slv import SlvTest -from ode import OdeTest -from americanquantooption import AmericanQuantoOptionTest -from extrapolation import ExtrapolationTest -from fdm import FdmTest -from swaption import SwaptionTest -from volatilities import SwaptionVolatilityCubeTest -from inflation import InflationTest -from coupons import ( - SubPeriodsCouponTest, - IborCouponTest, - OvernightCouponTest, - FixedRateCouponTest) -from options import OptionsTest -from swap import ZeroCouponSwapTest -from currencies import CurrencyTest +import QuantLib as ql def test(): - import QuantLib - print('testing QuantLib ' + QuantLib.__version__) - - suite = unittest.TestSuite() - - suite.addTest(unittest.makeSuite(DateTest, 'test')) - suite.addTest(DayCountersTest()) - suite.addTest(unittest.makeSuite(InstrumentTest, 'test')) - suite.addTest(unittest.makeSuite(MarketElementTest, 'test')) - suite.addTest(unittest.makeSuite(IntegralTest, 'test')) - suite.addTest(Solver1DTest()) - suite.addTest(unittest.makeSuite(TermStructureTest, 'test')) - suite.addTest(unittest.makeSuite(FixedRateBondTest, 'test')) - suite.addTest(unittest.makeSuite(FixedRateBondKwargsTest, 'test')) - suite.addTest(unittest.makeSuite(FixedRateBondHelperTest, 'test')) - suite.addTest(unittest.makeSuite(CmsTest, 'test')) - suite.addTest(unittest.makeSuite(AssetSwapTest, 'test')) - suite.addTest(unittest.makeSuite(OISRateHelperTest, "test")) - suite.addTest(unittest.makeSuite(FxSwapRateHelperTest, 'test')) - suite.addTest(unittest.makeSuite(CapFloorTest, 'test')) - suite.addTest(unittest.makeSuite(BlackFormulaTest, 'test')) - suite.addTest(unittest.makeSuite(BlackDeltaCalculatorTest, 'test')) - suite.addTest(unittest.makeSuite(IborIndexTest, 'test')) - suite.addTest(unittest.makeSuite(SabrTest, 'test')) - suite.addTest(unittest.makeSuite(SlvTest, 'test')) - suite.addTest(unittest.makeSuite(OdeTest, 'test')) - suite.addTest(unittest.makeSuite(AmericanQuantoOptionTest, 'test')) - suite.addTest(unittest.makeSuite(ExtrapolationTest, 'test')) - suite.addTest(unittest.makeSuite(FdmTest, 'test')) - suite.addTest(unittest.makeSuite(SwaptionTest, "test")) - suite.addTest(unittest.makeSuite(SwaptionVolatilityCubeTest, 'test')) - suite.addTest(unittest.makeSuite(InflationTest, "test")) - suite.addTest(unittest.makeSuite(CrossCurrencyBasisSwapRateHelperTest, "test")) - suite.addTest(unittest.makeSuite(SubPeriodsCouponTest, "test")) - suite.addTest(unittest.makeSuite(IborCouponTest, "test")) - suite.addTest(unittest.makeSuite(OvernightCouponTest, "test")) - suite.addTest(unittest.makeSuite(FixedRateCouponTest, "test")) - suite.addTest(unittest.makeSuite(OptionsTest, "test")) - suite.addTest(unittest.makeSuite(ZeroCouponSwapTest, "test")) - suite.addTest(unittest.makeSuite(CurrencyTest, "test")) - - result = unittest.TextTestRunner(verbosity=2).run(suite) - - if not result.wasSuccessful(): - sys.exit(1) + print('testing QuantLib', ql.__version__) + sys.argv[1:1] = ['discover', '-s', os.path.dirname(__file__)] + unittest.main(module=None, verbosity=2) if __name__ == '__main__': diff --git a/Python/test/__init__.py b/Python/test/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Python/test/date.py b/Python/test/date.py deleted file mode 100644 index 1fdb675915..0000000000 --- a/Python/test/date.py +++ /dev/null @@ -1,77 +0,0 @@ -""" - Copyright (C) 2000, 2001, 2002, 2003 RiskMap srl - - This file is part of QuantLib, a free-software/open-source library - for financial quantitative analysts and developers - http://quantlib.org/ - - QuantLib is free software: you can redistribute it and/or modify it - under the terms of the QuantLib license. You should have received a - copy of the license along with this program; if not, please email - . The license is also available online at - . - - This program is distributed in the hope that it will be useful, but WITHOUT - ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - FOR A PARTICULAR PURPOSE. See the license for more details. -""" - -import QuantLib as ql -import unittest - - -class DateTest(unittest.TestCase): - def setUp(self): - pass - - def testArithmetics(self): - "Testing date arithmetics" - today = ql.Date.todaysDate() - date = today - ql.Period(30, ql.Years) - end_date = today + ql.Period(30, ql.Years) - - dold = date.dayOfMonth() - mold = date.month() - yold = date.year() - - while date < end_date: - date += 1 - - d = date.dayOfMonth() - m = date.month() - y = date.year() - - # check if skipping any date - if not ( - (d == dold + 1 and m == mold and y == yold) - or (d == 1 and m == mold + 1 and y == yold) - or (d == 1 and m == 1 and y == yold + 1) - ): - self.fail( - """ -wrong day, month, year increment - date: %(t)s - day, month, year: %(d)d, %(m)d, %(y)d - previous: %(dold)d, %(mold)d, %(yold)d - """ - % locals() - ) - dold = d - mold = m - yold = y - - def testHolidayList(self): - """ Testing Calendar testHolidayList() method. """ - holidayLstFunction = ql.Calendar.holidayList(ql.Poland(), ql.Date(31, 12, 2014), ql.Date(3, 4, 2015), False) - holidayLstManual = (ql.Date(1, 1, 2015), ql.Date(6, 1, 2015)) - # check if dates both from function and from manual imput are the same - self.assertTrue(all([(a == b) for a, b in zip(holidayLstFunction, holidayLstManual)])) - - def tearDown(self): - pass - - -if __name__ == "__main__": - print("testing QuantLib " + ql.__version__) - suite = unittest.TestSuite() - suite.addTest(unittest.makeSuite(DateTest, "test")) - unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/Python/test/americanquantooption.py b/Python/test/test_americanquantooption.py similarity index 91% rename from Python/test/americanquantooption.py rename to Python/test/test_americanquantooption.py index 82034b6bdf..8399b0612f 100644 --- a/Python/test/americanquantooption.py +++ b/Python/test/test_americanquantooption.py @@ -57,7 +57,7 @@ def testAmericanBSQuantoOption(self): volTS = ql.BlackConstantVol(self.today, ql.TARGET(), 0.3, self.dc) bsmProcess = ql.BlackScholesMertonProcess( - ql.QuoteHandle(ql.SimpleQuote(100)), + ql.makeQuoteHandle(100), ql.YieldTermStructureHandle(self.divYieldTS), ql.YieldTermStructureHandle(self.domesticTS), ql.BlackVolTermStructureHandle(volTS)) @@ -81,7 +81,7 @@ def testAmericanHestonQuantoOption(self): ql.HestonProcess( ql.YieldTermStructureHandle(self.domesticTS), ql.YieldTermStructureHandle(self.divYieldTS), - ql.QuoteHandle(ql.SimpleQuote(100)), + ql.makeQuoteHandle(100), 0.09, 1.0, 0.09, 1e-4, 0.0)) fdmHestonVanillaEngine = ql.FdHestonVanillaEngine( @@ -97,7 +97,5 @@ def testAmericanHestonQuantoOption(self): if __name__ == '__main__': - print('testing QuantLib ' + ql.__version__) - suite = unittest.TestSuite() - suite.addTest(unittest.makeSuite(AmericanQuantoOptionTest,'test')) - unittest.TextTestRunner(verbosity=2).run(suite) + print("testing QuantLib", ql.__version__) + unittest.main(verbosity=2) diff --git a/Python/test/assetswap.py b/Python/test/test_assetswap.py similarity index 99% rename from Python/test/assetswap.py rename to Python/test/test_assetswap.py index 4f84346393..352fcb5612 100644 --- a/Python/test/assetswap.py +++ b/Python/test/test_assetswap.py @@ -52,7 +52,7 @@ def setUp(self): self.swaptionVolatilityStructure = ql.SwaptionVolatilityStructureHandle( ql.ConstantSwaptionVolatility(self.today, ql.NullCalendar(), ql.Following, 0.2, ql.Actual365Fixed()) ) - self.meanReversionQuote = ql.QuoteHandle(ql.SimpleQuote(0.01)) + self.meanReversionQuote = ql.makeQuoteHandle(0.01) self.cmspricer = ql.AnalyticHaganPricer( self.swaptionVolatilityStructure, ql.GFunctionFactory.Standard, self.meanReversionQuote ) @@ -852,7 +852,7 @@ def testImpliedValue(self): self.faceAmount, cmsBondSchedule1, self.swapIndex, - ql.Thirty360(), + ql.Thirty360(ql.Thirty360.BondBasis), ql.Following, fixingDays, [1.0], @@ -914,7 +914,7 @@ def testImpliedValue(self): self.faceAmount, cmsBondSchedule2, self.swapIndex, - ql.Thirty360(), + ql.Thirty360(ql.Thirty360.BondBasis), ql.Following, fixingDays, [0.84], @@ -1367,7 +1367,7 @@ def testMarketASWSpread(self): self.faceAmount, cmsBondSchedule1, self.swapIndex, - ql.Thirty360(), + ql.Thirty360(ql.Thirty360.BondBasis), ql.Following, fixingDays, [1, 1.0], @@ -1442,7 +1442,7 @@ def testMarketASWSpread(self): self.faceAmount, cmsBondSchedule2, self.swapIndex, - ql.Thirty360(), + ql.Thirty360(ql.Thirty360.BondBasis), ql.Following, fixingDays, [0.84], @@ -1873,7 +1873,7 @@ def testZSpread(self): self.faceAmount, cmsBondSchedule1, self.swapIndex, - ql.Thirty360(), + ql.Thirty360(ql.Thirty360.BondBasis), ql.Following, fixingDays, [1.0], @@ -1934,7 +1934,7 @@ def testZSpread(self): self.faceAmount, cmsBondSchedule2, self.swapIndex, - ql.Thirty360(), + ql.Thirty360(ql.Thirty360.BondBasis), ql.Following, fixingDays, [0.84], @@ -2366,7 +2366,7 @@ def testGenericBondImplied(self): [self.faceAmount], cmsBondSchedule1, self.swapIndex, - ql.Thirty360(), + ql.Thirty360(ql.Thirty360.BondBasis), ql.Following, [fixingDays], [], @@ -2432,7 +2432,7 @@ def testGenericBondImplied(self): [self.faceAmount], cmsBondSchedule2, self.swapIndex, - ql.Thirty360(), + ql.Thirty360(ql.Thirty360.BondBasis), ql.Following, [fixingDays], [0.84], @@ -2910,7 +2910,7 @@ def testMASWWithGenericBond(self): [self.faceAmount], cmsBondSchedule1, self.swapIndex, - ql.Thirty360(), + ql.Thirty360(ql.Thirty360.BondBasis), ql.Following, [fixingDays], [], @@ -2990,7 +2990,7 @@ def testMASWWithGenericBond(self): [self.faceAmount], cmsBondSchedule2, self.swapIndex, - ql.Thirty360(), + ql.Thirty360(ql.Thirty360.BondBasis), ql.Following, [fixingDays], [0.84], @@ -3446,7 +3446,7 @@ def testZSpreadWithGenericBond(self): [self.faceAmount], cmsBondSchedule1, self.swapIndex, - ql.Thirty360(), + ql.Thirty360(ql.Thirty360.BondBasis), ql.Following, [fixingDays], [], @@ -3512,7 +3512,7 @@ def testZSpreadWithGenericBond(self): [self.faceAmount], cmsBondSchedule2, self.swapIndex, - ql.Thirty360(), + ql.Thirty360(ql.Thirty360.BondBasis), ql.Following, [fixingDays], [0.84], @@ -4018,7 +4018,7 @@ def testSpecializedBondVsGenericBond(self): [self.faceAmount], cmsBondSchedule1, self.swapIndex, - ql.Thirty360(), + ql.Thirty360(ql.Thirty360.BondBasis), ql.Following, [fixingDays], [], @@ -4042,7 +4042,7 @@ def testSpecializedBondVsGenericBond(self): self.faceAmount, cmsBondSchedule1, self.swapIndex, - ql.Thirty360(), + ql.Thirty360(ql.Thirty360.BondBasis), ql.Following, fixingDays, [1.0], @@ -4109,7 +4109,7 @@ def testSpecializedBondVsGenericBond(self): [self.faceAmount], cmsBondSchedule2, self.swapIndex, - ql.Thirty360(), + ql.Thirty360(ql.Thirty360.BondBasis), ql.Following, [fixingDays], [0.84], @@ -4133,7 +4133,7 @@ def testSpecializedBondVsGenericBond(self): self.faceAmount, cmsBondSchedule2, self.swapIndex, - ql.Thirty360(), + ql.Thirty360(ql.Thirty360.BondBasis), ql.Following, fixingDays, [0.84], @@ -4873,7 +4873,7 @@ def testSpecializedBondVsGenericBondUsingAsw(self): [self.faceAmount], cmsBondSchedule1, self.swapIndex, - ql.Thirty360(), + ql.Thirty360(ql.Thirty360.BondBasis), ql.Following, [fixingDays], [], @@ -4897,7 +4897,7 @@ def testSpecializedBondVsGenericBondUsingAsw(self): self.faceAmount, cmsBondSchedule1, self.swapIndex, - ql.Thirty360(), + ql.Thirty360(ql.Thirty360.BondBasis), ql.Following, fixingDays, [1.0], @@ -5011,7 +5011,7 @@ def testSpecializedBondVsGenericBondUsingAsw(self): [self.faceAmount], cmsBondSchedule2, self.swapIndex, - ql.Thirty360(), + ql.Thirty360(ql.Thirty360.BondBasis), ql.Following, [fixingDays], [0.84], @@ -5035,7 +5035,7 @@ def testSpecializedBondVsGenericBondUsingAsw(self): self.faceAmount, cmsBondSchedule2, self.swapIndex, - ql.Thirty360(), + ql.Thirty360(ql.Thirty360.BondBasis), ql.Following, fixingDays, [0.84], @@ -5351,7 +5351,5 @@ def testSpecializedBondVsGenericBondUsingAsw(self): if __name__ == "__main__": - print("testing QuantLib " + ql.__version__) - suite = unittest.TestSuite() - suite.addTest(unittest.makeSuite(AssetSwapTest, "test")) - unittest.TextTestRunner(verbosity=2).run(suite) + print("testing QuantLib", ql.__version__) + unittest.main(verbosity=2) diff --git a/Python/test/blackformula.py b/Python/test/test_blackformula.py similarity index 88% rename from Python/test/blackformula.py rename to Python/test/test_blackformula.py index 7f21b15b68..e60083ffe7 100644 --- a/Python/test/blackformula.py +++ b/Python/test/test_blackformula.py @@ -43,10 +43,10 @@ def test_blackFormula(self): self.vol, self.df, self.displacement) - self.assertAlmostEquals(expected, res, delta=1e-4, - msg="Failed to calculate simple " - "Black-Scholes-Merton price rounded to " - "four decimal places.") + self.assertAlmostEqual(expected, res, delta=1e-4, + msg="Failed to calculate simple " + "Black-Scholes-Merton price rounded to " + "four decimal places.") def test_black_formula_implied_stdev(self): """Testing implied volatility calculator""" @@ -57,9 +57,9 @@ def test_black_formula_implied_stdev(self): self.forward, black_price, self.df) - self.assertAlmostEquals(expected, res, delta=1e-4, - msg="Failed to determine Implied Vol rounded " - "to a single vol bps.") + self.assertAlmostEqual(expected, res, delta=1e-4, + msg="Failed to determine Implied Vol rounded " + "to a single vol bps.") class BlackDeltaCalculatorTest(unittest.TestCase): @@ -109,7 +109,7 @@ def test_single_spot_delta(self): strike = black_calculator.strikeFromDelta(spot_delta_level) - self.assertAlmostEquals(expected_strike, strike, delta=1e-4) + self.assertAlmostEqual(expected_strike, strike, delta=1e-4) def test_spot_atm_delta_calculator(self): """Test for 0-delta straddle strike""" @@ -134,8 +134,9 @@ def test_spot_atm_delta_calculator(self): strike = black_calculator.atmStrike(ql.DeltaVolQuote.AtmDeltaNeutral) - self.assertAlmostEquals(expected_strike, strike, delta=1e-4) + self.assertAlmostEqual(expected_strike, strike, delta=1e-4) if __name__ == '__main__': - unittest.main() + print("testing QuantLib", ql.__version__) + unittest.main(verbosity=2) diff --git a/Python/test/test_bondfunctions.py b/Python/test/test_bondfunctions.py new file mode 100644 index 0000000000..bec692b61e --- /dev/null +++ b/Python/test/test_bondfunctions.py @@ -0,0 +1,232 @@ +""" + Copyright (C) 2009 Joseph Malicki + Copyright (C) 2019 Prasad Somwanshi + Copyright (C) 2023 Francois Botha + + This file is part of QuantLib, a free-software/open-source library + for financial quantitative analysts and developers - http://quantlib.org/ + + QuantLib is free software: you can redistribute it and/or modify it + under the terms of the QuantLib license. You should have received a + copy of the license along with this program; if not, please email + . The license is also available online at + . + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the license for more details. +""" + +import QuantLib as ql +import unittest + + +class BondFunctionsTest(unittest.TestCase): + def setUp(self): + ql.Settings.instance().evaluationDate = ql.Date(2, 1, 2010) + self.settlement_days = 3 + self.face_amount = 100.0 + self.redemption = 100.0 + self.issue_date = ql.Date(2, 1, 2008) + self.maturity_date = ql.Date(2, 1, 2018) + self.calendar = ql.UnitedStates(ql.UnitedStates.GovernmentBond) + self.settlement_date = self.calendar.advance( + ql.Settings.instance().evaluationDate, self.settlement_days, ql.Days) + self.day_counter = ql.ActualActual(ql.ActualActual.Bond) + self.sched = ql.Schedule( + self.issue_date, + self.maturity_date, + ql.Period(ql.Semiannual), + self.calendar, + ql.Unadjusted, + ql.Unadjusted, + ql.DateGeneration.Backward, + False, + ) + self.coupons = [0.05] + + self.bond = ql.FixedRateBond( + self.settlement_days, + self.face_amount, + self.sched, + self.coupons, + self.day_counter, + ql.Following, + self.redemption, + self.issue_date, + ) + + self.flat_forward = ql.FlatForward( + self.issue_date, self.coupons[0], self.day_counter, ql.Compounded, ql.Semiannual + ) + self.term_structure_handle = ql.RelinkableYieldTermStructureHandle( + self.flat_forward) + bondEngine = ql.DiscountingBondEngine(self.term_structure_handle) + self.bond.setPricingEngine(bondEngine) + + def testStartDate(self): + """ Testing BondFunctions startDate. """ + self.assertEqual(ql.BondFunctions.startDate( + self.bond), self.issue_date) + + def testMaturityDate(self): + """ Testing BondFunctions maturityDate. """ + self.assertEqual(ql.BondFunctions.maturityDate( + self.bond), self.maturity_date) + + def testIsTradable(self): + """ Testing BondFunctions isTradable. """ + self.assertTrue(ql.BondFunctions.isTradable( + self.bond, ql.Date(1, 6, 2010))) + + self.assertFalse(ql.BondFunctions.isTradable( + self.bond, ql.Date(1, 1, 2028))) + + def testPreviousCashFlowDate(self): + """ Testing BondFunctions previousCashFlowDate. """ + self.assertEqual(ql.BondFunctions.previousCashFlowDate(self.bond, ql.Date(1, 6, 2010)), + ql.Date(4, 1, 2010)) + + def testNextCashFlowDate(self): + """ Testing BondFunctions nextCashFlowDate. """ + self.assertEqual(ql.BondFunctions.nextCashFlowDate(self.bond, ql.Date(1, 6, 2010)), + ql.Date(2, 7, 2010)) + + def testPreviousCashFlowAmount(self): + """ Testing BondFunctions previousCashFlowAmount. """ + self.assertEqual(round(ql.BondFunctions.previousCashFlowAmount( + self.bond, ql.Date(1, 6, 2010)), 4), 2.5) + + def testNextCashFlowAmount(self): + """ Testing BondFunctions nextCashFlowAmount. """ + self.assertEqual(round(ql.BondFunctions.nextCashFlowAmount( + self.bond, ql.Date(1, 6, 2010)), 4), 2.5) + + def testPreviousCouponRate(self): + """ Testing BondFunctions previousCouponRate. """ + self.assertEqual(ql.BondFunctions.previousCouponRate(self.bond), 0.05) + + def testNextCouponRate(self): + """ Testing BondFunctions nextCouponRate. """ + self.assertEqual(ql.BondFunctions.nextCouponRate(self.bond), 0.05) + + def testAccrualStartDate(self): + """ Testing BondFunctions accrualStartDate. """ + self.assertEqual(ql.BondFunctions.accrualStartDate(self.bond, ql.Date(1, 6, 2010)), + ql.Date(2, 1, 2010)) + + def testAccrualEndDate(self): + """ Testing BondFunctions accrualEndDate. """ + self.assertEqual(ql.BondFunctions.accrualEndDate(self.bond, ql.Date(1, 6, 2010)), + ql.Date(2, 7, 2010)) + + def testAccrualPeriod(self): + """ Testing BondFunctions accrualPeriod. """ + self.assertEqual(ql.BondFunctions.accrualPeriod(self.bond, ql.Date(1, 6, 2010)), + 0.5) + + def testAccrualDays(self): + """ Testing BondFunctions accrualDays. """ + self.assertEqual(ql.BondFunctions.accrualDays(self.bond, ql.Date(1, 10, 2010)), + 184) + + def testAccruedPeriod(self): + """ Testing BondFunctions accruedPeriod. """ + self.assertEqual(round(ql.BondFunctions.accruedPeriod(self.bond, ql.Date(1, 6, 2010)), 8), + 0.41436464) + + def testAccruedDays(self): + """ Testing BondFunctions accruedDays. """ + self.assertEqual(ql.BondFunctions.accruedDays(self.bond, ql.Date(1, 6, 2010)), + 150) + + def testAccruedAmount(self): + """ Testing BondFunctions accruedAmount. """ + self.assertEqual(round(ql.BondFunctions.accruedAmount(self.bond, ql.Date(1, 6, 2010)), 8), + 2.0718232) + + def testBps(self): + """ Testing BondFunctions bps. """ + self.assertEqual(round(ql.BondFunctions.bps(self.bond, self.flat_forward), 8), + 0.06527501) + self.assertEqual(round(ql.BondFunctions.bps(self.bond, + ql.InterestRate(0.03, self.day_counter, ql.Compounded, ql.Annual)), 8), + 0.07071951) + self.assertEqual(round(ql.BondFunctions.bps(self.bond, + 0.03, self.day_counter, ql.Compounded, ql.Annual), 8), + 0.07071951) + + def testCleanPrice(self): + """ Testing BondFunctions cleanPrice. """ + self.assertEqual(round(ql.BondFunctions.cleanPrice(self.bond, self.flat_forward), 4), + 99.9448) + self.assertEqual(round(ql.BondFunctions.cleanPrice(self.bond, + ql.InterestRate(0.03, self.day_counter, ql.Compounded, ql.Annual)), 4), + 114.2806) + self.assertEqual(round(ql.BondFunctions.cleanPrice(self.bond, + 0.03, self.day_counter, ql.Compounded, ql.Annual), 4), + 114.2806) + + def testAtmRate(self): + """ Testing BondFunctions atmRate. """ + self.assertEqual(round(ql.BondFunctions.atmRate(self.bond, self.flat_forward, + self.settlement_date, 99.94475138121548), 4), + 0.05) + + def testBondYield(self): + """ Testing BondFunctions bondYield. """ + self.assertEqual(round(ql.BondFunctions.bondYield(self.bond, 110, self.day_counter, ql.Compounded, ql.Annual), 8), + 0.03582431) + + def testDuration(self): + """ Testing BondFunctions duration. """ + self.assertEqual(round(ql.BondFunctions.duration(self.bond, + ql.InterestRate(0.03, self.day_counter, ql.Compounded, ql.Annual)), 4), + 6.5835) + self.assertEqual(round(ql.BondFunctions.duration(self.bond, + 0.03, self.day_counter, ql.Compounded, ql.Annual), 4), + 6.5835) + + def testConvexity(self): + """ Testing BondFunctions convexity. """ + self.assertEqual(round(ql.BondFunctions.convexity(self.bond, + ql.InterestRate(0.03, self.day_counter, ql.Compounded, ql.Annual)), 4), + 54.3498) + self.assertEqual(round(ql.BondFunctions.convexity(self.bond, + 0.03, self.day_counter, ql.Compounded, ql.Annual), 4), + 54.3498) + + def testBasisPointValue(self): + """ Testing BondFunctions basisPointValue. """ + self.assertEqual(round(ql.BondFunctions.basisPointValue(self.bond, + 0.03, self.day_counter, ql.Compounded, ql.Annual, + self.settlement_date), 8), + -0.07527271) + self.assertEqual(round(ql.BondFunctions.basisPointValue(self.bond, + ql.InterestRate( + 0.03, self.day_counter, ql.Compounded, ql.Annual), + self.settlement_date), 8), + -0.07527271) + + def testYieldValueBasisPoint(self): + """ Testing BondFunctions yieldValueBasisPoint. """ + self.assertEqual(round(ql.BondFunctions.yieldValueBasisPoint(self.bond, + ql.InterestRate( + 0.03, self.day_counter, ql.Compounded, ql.Annual), + ql.Date(1, 9, 2010)), 10), + -1.44145e-05) + self.assertEqual(round(ql.BondFunctions.yieldValueBasisPoint(self.bond, + 0.03, self.day_counter, ql.Compounded, ql.Annual, + ql.Date(1, 9, 2010)), 10), + -1.44145e-05) + + def testZSpread(self): + """ Testing BondFunctions zSpread. """ + self.assertEqual(round(ql.BondFunctions.zSpread(self.bond, 87.5, self.flat_forward, + self.day_counter, ql.Compounded, ql.Annual), 8), + 0.02125053) + + +if __name__ == "__main__": + print("testing QuantLib", ql.__version__) + unittest.main(verbosity=2) diff --git a/Python/test/bonds.py b/Python/test/test_bonds.py similarity index 64% rename from Python/test/bonds.py rename to Python/test/test_bonds.py index e6cd7545bb..77fa187067 100644 --- a/Python/test/bonds.py +++ b/Python/test/test_bonds.py @@ -208,7 +208,16 @@ def setUp(self): ) self.coupons = [0.05] - def check_construction(self, bond): + def testConstructor(self): + """ Testing FixedRateBond constructor with keyword arguments. """ + bond = ql.FixedRateBond( + settlementDays=self.settlement_days, + schedule=self.sched, + paymentDayCounter=self.day_counter, + issueDate=self.issue_date, + coupons=self.coupons, + faceAmount=self.face_amount, + ) self.assertTrue(type(bond) is ql.FixedRateBond) self.assertEqual(bond.dayCounter(), self.day_counter) self.assertEqual(bond.settlementDays(), self.settlement_days) @@ -219,48 +228,105 @@ def check_construction(self, bond): self.assertEqual(bond.notional(self.issue_date), 100.0) self.assertEqual(bond.notionals(), (100.0, 0)) - def testFromRates(self): - """ Testing FixedRateBond from_rates method. """ - bond = ql.FixedRateBond.from_rates( - settlementDays=self.settlement_days, - schedule=self.sched, - paymentDayCounter=self.day_counter, - issueDate=self.issue_date, - coupons=self.coupons, - faceAmount=self.face_amount, - ) - self.check_construction(bond) - def testFromInterestRates(self): - """ Testing FixedRateBond from_interest_rates method. """ - bond = ql.FixedRateBond.from_interest_rates( - settlementDays=self.settlement_days, - faceAmount=self.face_amount, - schedule=self.sched, - coupons=[ql.InterestRate(0.05, self.day_counter, ql.Continuous, ql.Annual)], - issueDate=self.issue_date, +class AmortizingFixedRateBondTest(unittest.TestCase): + def test_interest_rates(self): + # see AmortizingBondTest::testBrazilianAmortizingFixedRateBond + # in the C++ test suite + + nominals = [ + 1000 , 983.33300000, 966.66648898, 950.00019204, + 933.33338867, 916.66685434, 900.00001759, 883.33291726, + 866.66619177, 849.99933423, 833.33254728, 816.66589633, + 799.99937871, 783.33299165, 766.66601558, 749.99946306, + 733.33297499, 716.66651646, 699.99971995, 683.33272661, + 666.66624140, 649.99958536, 633.33294599, 616.66615618, + 599.99951997, 583.33273330, 566.66633377, 549.99954356, + 533.33290739, 516.66625403, 499.99963400, 483.33314619, + 466.66636930, 449.99984658, 433.33320226, 416.66634063, + 399.99968700, 383.33290004, 366.66635221, 349.99953317, + 333.33290539, 316.66626012, 299.99948151, 283.33271031, + 266.66594695, 249.99932526, 233.33262024, 216.66590450, + 199.99931312, 183.33277035, 166.66617153, 149.99955437, + 133.33295388, 116.66633464, 99.99973207, 83.33307672, + 66.66646137, 49.99984602, 33.33324734, 16.66662367 + ] + + expected_amortizations = [ + 16.66700000, 16.66651102, 16.66629694, 16.66680337, + 16.66653432, 16.66683675, 16.66710033, 16.66672548, + 16.66685753, 16.66678695, 16.66665095, 16.66651761, + 16.66638706, 16.66697606, 16.66655251, 16.66648807, + 16.66645852, 16.66679651, 16.66699333, 16.66648520, + 16.66665604, 16.66663937, 16.66678981, 16.66663620, + 16.66678667, 16.66639952, 16.66679021, 16.66663617, + 16.66665336, 16.66662002, 16.66648780, 16.66677688, + 16.66652271, 16.66664432, 16.66686163, 16.66665363, + 16.66678696, 16.66654783, 16.66681904, 16.66662777, + 16.66664527, 16.66677860, 16.66677119, 16.66676335, + 16.66662168, 16.66670502, 16.66671573, 16.66659137, + 16.66654276, 16.66659882, 16.66661715, 16.66660049, + 16.66661924, 16.66660257, 16.66665534, 16.66661534, + 16.66661534, 16.66659867, 16.66662367, 16.66662367 + ] + + expected_coupons = [ + 5.97950399, 4.85474255, 5.27619136, 5.18522454, + 5.33753111, 5.24221882, 4.91231709, 4.59116258, + 4.73037674, 4.63940686, 4.54843737, 3.81920094, + 4.78359948, 3.86733691, 4.38439657, 4.09359456, + 4.00262671, 4.28531030, 3.82068947, 3.55165259, + 3.46502778, 3.71720657, 3.62189368, 2.88388676, + 3.58769952, 2.72800044, 3.38838360, 3.00196900, + 2.91100034, 3.08940793, 2.59877059, 2.63809514, + 2.42551945, 2.45615766, 2.59111761, 1.94857222, + 2.28751141, 1.79268582, 2.19248291, 1.81913832, + 1.90625855, 1.89350716, 1.48110584, 1.62031828, + 1.38600825, 1.23425366, 1.39521333, 1.06968563, + 1.03950542, 1.00065409, 0.90968563, 0.81871706, + 0.79726493, 0.63678002, 0.57187676, 0.49829046, + 0.31177086, 0.27290565, 0.19062560, 0.08662552 + ] + + settlementDays = 0 + issueDate = ql.Date(2, ql.March, 2020) + maturityDate = ql.Date(2, ql.March, 2025) + + schedule = ql.Schedule(issueDate, + maturityDate, + ql.Period(ql.Monthly), + ql.Brazil(ql.Brazil.Settlement), + ql.Unadjusted, + ql.Unadjusted, + ql.DateGeneration.Backward, + False) + + coupons = ql.FixedRateLeg( + schedule, + nominals = nominals, + couponRates = [0.0675], + dayCount = ql.Business252(ql.Brazil()), + compounding = ql.Compounded, + compoundingFrequency = ql.Annual, + paymentAdjustment = ql.Following, ) - self.check_construction(bond) - def testFromDateInfo(self): - """ Testing FixedRateBond from_interest_rates method. """ - bond = ql.FixedRateBond.from_date_info( - settlementDays=self.settlement_days, - faceAmount=self.face_amount, - coupons=self.coupons, - issueDate=self.issue_date, - couponCalendar=ql.UnitedStates(), - startDate=ql.Date(2, 1, 2010), - maturityDate=self.maturity_date, - tenor=ql.Period(3, ql.Months), - accrualDayCounter=self.day_counter, + bond = ql.Bond( + settlementDays, + schedule.calendar(), + issueDate, + coupons ) - self.check_construction(bond) + + cashflows = bond.cashflows() + + self.assertEqual(len(cashflows), 2 * len(nominals)) + + for k in range(len(nominals)): + self.assertEqual(round(expected_coupons[k], 5), round(cashflows[2*k].amount(), 5)) + self.assertEqual(round(expected_amortizations[k], 5), round(cashflows[2*k+1].amount(), 5)) if __name__ == "__main__": - print("testing QuantLib " + ql.__version__) - suite = unittest.TestSuite() - suite.addTest(unittest.makeSuite(FixedRateBondTest, "test")) - suite.addTest(unittest.makeSuite(FixedRateBondKwargsTest, "test")) - unittest.TextTestRunner(verbosity=2).run(suite) + print("testing QuantLib", ql.__version__) + unittest.main(verbosity=2) diff --git a/Python/test/test_calendars.py b/Python/test/test_calendars.py new file mode 100644 index 0000000000..6c02eacac0 --- /dev/null +++ b/Python/test/test_calendars.py @@ -0,0 +1,66 @@ +""" + Copyright (C) 2023 Skandinaviska Enskilda Banken AB (publ) + + This file is part of QuantLib, a free-software/open-source library + for financial quantitative analysts and developers - http://quantlib.org/ + + QuantLib is free software: you can redistribute it and/or modify it + under the terms of the QuantLib license. You should have received a + copy of the license along with this program; if not, please email + . The license is also available online at + . + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the license for more details. +""" +import itertools +import unittest + +import QuantLib as ql + + +class JointCalendarTest(unittest.TestCase): + + def test_joint_calendar_holidays(self): + base_calendars = [ql.Sweden(), ql.Denmark(), ql.Finland(), ql.Norway(), ql.Iceland()] + joint_nordics = ql.JointCalendar(base_calendars) + start_date = ql.Date(1, ql.January, 2023) + end_date = ql.Date(31, ql.December, 2023) + + joint_holidays = set(joint_nordics.holidayList(start_date, end_date)) + base_holidays = set(itertools.chain.from_iterable( + calendar.holidayList(start_date, end_date) for calendar in base_calendars + )) + self.assertEqual(joint_holidays, base_holidays) + + +class BespokeCalendarTest(unittest.TestCase): + + def test_hash(self): + empty1, empty2 = ql.CalendarVector(2) + for cal1 in (ql.BespokeCalendar("one"), ql.BespokeCalendar("two"), empty1): + for cal2 in (ql.BespokeCalendar("one"), ql.BespokeCalendar("two"), empty2): + if cal1.empty() or cal2.empty(): + expected = cal1.empty() == cal2.empty() + else: + expected = cal1.name() == cal2.name() + self.assertEqual(cal1 == cal2, expected) + self.assertEqual(cal1 != cal2, not expected) + self.assertEqual(hash(cal1) == hash(cal2), expected) + + def test_reset_added_holidays(self): + calendar = ql.BespokeCalendar("bespoke thing") + test_date = ql.Date(1, ql.January, 2024) + self.assertFalse(calendar.isHoliday(test_date)) + calendar.addHoliday(test_date) + self.assertTrue(calendar.isHoliday(test_date)) + # TODO: Can extend test with this, if exposed: + # self.assertEqual(len(calendar.addedHolidays()), 1) + calendar.resetAddedAndRemovedHolidays() + self.assertFalse(calendar.isHoliday(test_date)) + + +if __name__ == "__main__": + print("testing QuantLib", ql.__version__) + unittest.main(verbosity=2) diff --git a/Python/test/capfloor.py b/Python/test/test_capfloor.py similarity index 91% rename from Python/test/capfloor.py rename to Python/test/test_capfloor.py index 99ab042032..c759381582 100644 --- a/Python/test/capfloor.py +++ b/Python/test/test_capfloor.py @@ -66,7 +66,7 @@ def setUp(self): self.cap = ql.Cap(self.ibor_leg, [self.strike]) self.cap_npv = 8.54 - self.black_vol = ql.QuoteHandle(ql.SimpleQuote(0.6)) + self.black_vol = ql.makeQuoteHandle(0.6) def tearDown(self): ql.Settings.instance().evaluationDate = ql.Date() @@ -94,8 +94,7 @@ def testBachelierCapFloorEngine(self): bpvol = self.black_vol.value() * self.flat_forward_rate bachelier_engine = ql.BachelierCapFloorEngine(self.term_structure_handle, - ql.QuoteHandle( - ql.SimpleQuote(bpvol))) + ql.makeQuoteHandle(bpvol)) self.cap.setPricingEngine(bachelier_engine) @@ -113,7 +112,5 @@ def testBachelierCapFloorEngine(self): if __name__ == '__main__': - print('testing QuantLib ' + ql.__version__) - suite = unittest.TestSuite() - suite.addTest(unittest.makeSuite(CapFloorTest,'test')) - unittest.TextTestRunner(verbosity=2).run(suite) + print("testing QuantLib", ql.__version__) + unittest.main(verbosity=2) diff --git a/Python/test/cms.py b/Python/test/test_cms.py similarity index 94% rename from Python/test/cms.py rename to Python/test/test_cms.py index 54993a0bae..03e790512e 100644 --- a/Python/test/cms.py +++ b/Python/test/test_cms.py @@ -27,7 +27,7 @@ def setUp(self): ql.Settings.instance().evaluationDate = self.referenceDate self.termStructure = ql.RelinkableYieldTermStructureHandle() self.termStructure.linkTo( - ql.FlatForward(self.referenceDate, ql.QuoteHandle(ql.SimpleQuote(0.05)), ql.Actual365Fixed()) + ql.FlatForward(self.referenceDate, ql.makeQuoteHandle(0.05), ql.Actual365Fixed()) ) self.yieldCurveModels = [] self.numericalPricers = [] @@ -95,7 +95,7 @@ def setUp(self): for i in range(self.nRows): self.volSpreadsRow = [] for j in range(self.nCols): - self.volSpreadsRow.append(ql.QuoteHandle(ql.SimpleQuote(self.volSpreadsMatrix[i][j]))) + self.volSpreadsRow.append(ql.makeQuoteHandle(self.volSpreadsMatrix[i][j])) self.volSpreads.append(self.volSpreadsRow) self.iborIndex = ql.Euribor6M(self.termStructure) @@ -104,7 +104,7 @@ def setUp(self): self.vegaWeightedSmileFit = False self.SabrVolCube2 = ql.SwaptionVolatilityStructureHandle( - ql.SwaptionVolCube2( + ql.InterpolatedSwaptionVolatilityCube( self.atmVol, self.optionTenors, self.swapTenors, @@ -133,7 +133,7 @@ def setUp(self): for i in range(self.nRows): self.guessRow = [] for j in range(4): - self.guessRow.append(ql.QuoteHandle(ql.SimpleQuote(self.guessMatrix[i][j]))) + self.guessRow.append(ql.makeQuoteHandle(self.guessMatrix[i][j])) self.guess.append(self.guessRow) self.isParameterFixed = [False, True, False, False] @@ -141,7 +141,7 @@ def setUp(self): self.isAtmCalibrated = False ## self.SabrVolCube1 = ql.SwaptionVolatilityStructureHandle( - ql.SwaptionVolCube1( + ql.SabrSwaptionVolatilityCube( self.atmVol, self.optionTenors, self.swapTenors, @@ -164,7 +164,7 @@ def setUp(self): ql.GFunctionFactory.NonParallelShifts, ] - self.zeroMeanRev = ql.QuoteHandle(ql.SimpleQuote(0.0)) + self.zeroMeanRev = ql.makeQuoteHandle(0.0) self.numericalPricers = {} self.analyticPricers = {} @@ -302,7 +302,5 @@ def testParity(self): if __name__ == "__main__": - print("testing QuantLib " + ql.__version__) - suite = unittest.TestSuite() - suite.addTest(unittest.makeSuite(CmsTest, "test")) - unittest.TextTestRunner(verbosity=2).run(suite) + print("testing QuantLib", ql.__version__) + unittest.main(verbosity=2) diff --git a/Python/test/coupons.py b/Python/test/test_coupons.py similarity index 91% rename from Python/test/coupons.py rename to Python/test/test_coupons.py index 5541a91dac..469fc3b249 100644 --- a/Python/test/coupons.py +++ b/Python/test/test_coupons.py @@ -31,7 +31,7 @@ def flat_rate(rate): return ql.FlatForward( - 2, CAL, ql.QuoteHandle(ql.SimpleQuote(rate)), ql.Actual365Fixed()) + 2, CAL, ql.makeQuoteHandle(rate), ql.Actual365Fixed()) def create_ibor_leg(ibor_idx, start, end, payment_lag=0): @@ -83,6 +83,40 @@ def create_fixed_rate_leg(start, end, payment_lag=0): paymentLag=payment_lag) +class CashFlowsTest(unittest.TestCase): + def setUp(self): + self.cash_flows = [ql.SimpleCashFlow(1.e6, ql.Date(22, 6, 2022)), + ql.SimpleCashFlow(5.e4, ql.Date(22, 6, 2022))] + + def test_previous_cash_flow_amount(self): + """Testing previous cash flows amount""" + reference_date = ql.Date(28, 6, 2022) + expected_amount = 1.05e6 + include_settlement_date_flows = False + actual_amount = ql.CashFlows.previousCashFlowAmount( + self.cash_flows, include_settlement_date_flows, reference_date) + fail_msg = """ Unable to replicate previous cash flow amount: + calculated: {actual} + expected: {expected} + """.format(actual=actual_amount, + expected=expected_amount) + self.assertEqual(actual_amount, expected_amount, msg=fail_msg) + + def test_next_cash_flow_amount(self): + """Testing next cash flows amount""" + reference_date = ql.Date(21, 6, 2022) + expected_amount = 1.05e6 + include_settlement_date_flows = False + actual_amount = ql.CashFlows.nextCashFlowAmount( + self.cash_flows, include_settlement_date_flows, reference_date) + fail_msg = """ Unable to replicate next cash flow amount: + calculated: {actual} + expected: {expected} + """.format(actual=actual_amount, + expected=expected_amount) + self.assertEqual(actual_amount, expected_amount, msg=fail_msg) + + class IborCouponTest(unittest.TestCase): def setUp(self): ql.Settings.instance().evaluationDate = VALUATION_DATE @@ -433,10 +467,5 @@ def test_sub_period_coupon_rate_spread(self): if __name__ == '__main__': - print('testing QuantLib ' + ql.__version__) - suite = unittest.TestSuite() - suite.addTest(unittest.makeSuite(SubPeriodsCouponTest, 'test')) - suite.addTest(unittest.makeSuite(IborCouponTest, 'test')) - suite.addTest(unittest.makeSuite(OvernightCouponTest, 'test')) - suite.addTest(unittest.makeSuite(FixedRateCouponTest, 'test')) - unittest.TextTestRunner(verbosity=2).run(suite) + print("testing QuantLib", ql.__version__) + unittest.main(verbosity=2) diff --git a/Python/test/currencies.py b/Python/test/test_currencies.py similarity index 71% rename from Python/test/currencies.py rename to Python/test/test_currencies.py index 15a1fe3838..a1cc027198 100644 --- a/Python/test/currencies.py +++ b/Python/test/test_currencies.py @@ -40,9 +40,18 @@ def test_bespoke_currency_constructor(self): "CCY", "CCY", 100, "#", "", 100, ql.Rounding(), "") self.assertFalse(custom_ccy.empty(), fail_msg) + def test_hash(self): + for ccy1 in (ql.EURCurrency(), ql.USDCurrency(), ql.Currency()): + for ccy2 in (ql.EURCurrency(), ql.USDCurrency(), ql.Currency()): + if ccy1.empty() or ccy2.empty(): + expected = ccy1.empty() == ccy2.empty() + else: + expected = ccy1.name() == ccy2.name() + self.assertEqual(ccy1 == ccy2, expected) + self.assertEqual(ccy1 != ccy2, not expected) + self.assertEqual(hash(ccy1) == hash(ccy2), expected) + if __name__ == '__main__': - print('testing QuantLib ' + ql.__version__) - suite = unittest.TestSuite() - suite.addTest(unittest.makeSuite(CurrencyTest, 'test')) - unittest.TextTestRunner(verbosity=2).run(suite) + print("testing QuantLib", ql.__version__) + unittest.main(verbosity=2) diff --git a/Python/test/test_date.py b/Python/test/test_date.py new file mode 100644 index 0000000000..5a08f1fad8 --- /dev/null +++ b/Python/test/test_date.py @@ -0,0 +1,117 @@ +""" + Copyright (C) 2000, 2001, 2002, 2003 RiskMap srl + + This file is part of QuantLib, a free-software/open-source library + for financial quantitative analysts and developers - http://quantlib.org/ + + QuantLib is free software: you can redistribute it and/or modify it + under the terms of the QuantLib license. You should have received a + copy of the license along with this program; if not, please email + . The license is also available online at + . + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the license for more details. +""" + +import datetime +import operator +import QuantLib as ql +import unittest + + +class DateTest(unittest.TestCase): + def testArithmetics(self): + "Testing date arithmetics" + today = ql.Date.todaysDate() + date = today - ql.Period(30, ql.Years) + end_date = today + ql.Period(30, ql.Years) + + dold = date.dayOfMonth() + mold = date.month() + yold = date.year() + + while date < end_date: + date += 1 + + d = date.dayOfMonth() + m = date.month() + y = date.year() + + # check if skipping any date + if not ( + (d == dold + 1 and m == mold and y == yold) + or (d == 1 and m == mold + 1 and y == yold) + or (d == 1 and m == 1 and y == yold + 1) + ): + self.fail( + """ +wrong day, month, year increment + date: %(t)s + day, month, year: %(d)d, %(m)d, %(y)d + previous: %(dold)d, %(mold)d, %(yold)d + """ + % locals() + ) + dold = d + mold = m + yold = y + + def test_hash(self): + for date1 in (ql.Date(1, 2, 2020), ql.Date(3, 4, 2022), ql.Date()): + for date2 in (ql.Date(1, 2, 2020), ql.Date(3, 4, 2022), ql.Date()): + expected = str(date1) == str(date2) + self.assertEqual(date1 == date2, expected) + self.assertEqual(date1 != date2, not expected) + self.assertEqual(hash(date1) == hash(date2), expected) + + def test_order(self): + ops = [operator.eq, operator.ne, operator.lt, operator.le, operator.gt, operator.ge] + for date1 in (ql.Date(1, 2, 2020), ql.Date(3, 4, 2022), ql.Date()): + for date2 in (ql.Date(1, 2, 2020), ql.Date(3, 4, 2022), ql.Date()): + for op in ops: + self.assertEqual(op(date1, date2), + op(date1.serialNumber(), date2.serialNumber())) + + def testHolidayList(self): + """ Testing Calendar testHolidayList() method. """ + holidayLstFunction = ql.Calendar.holidayList(ql.Poland(), ql.Date(31, 12, 2014), ql.Date(3, 4, 2015), False) + holidayLstManual = (ql.Date(1, 1, 2015), ql.Date(6, 1, 2015)) + # check if dates both from function and from manual input are the same + self.assertTrue(all([(a == b) for a, b in zip(holidayLstFunction, holidayLstManual)])) + + def testConversion(self): + for m in range(1, 13): + ql_date = ql.Date(m * 2, m, 2020) + py_date = datetime.date(2020, m, m * 2) + self.assertEqual(ql_date.to_date(), py_date) + self.assertEqual(ql.Date.from_date(py_date), ql_date) + # datetime works as well + py_dt = datetime.datetime(2020, m, m * 2) + self.assertEqual(ql.Date.from_date(py_dt), ql_date) + + with self.assertRaisesRegex(RuntimeError, "from_date requires a date"): + ql.Date.from_date("2020-01-02") + + +class PeriodTest(unittest.TestCase): + def test_hash(self): + for per1 in (ql.Period("1D"), ql.Period("1W"), ql.Period("12M"), ql.Period()): + for per2 in (ql.Period("1D"), ql.Period("1Y"), ql.Period()): + expected = str(per1.normalized()) == str(per2.normalized()) + self.assertEqual(per1 == per2, expected) + self.assertEqual(per1 != per2, not expected) + self.assertEqual(hash(per1) == hash(per2), expected) + + def test_order(self): + ops = [operator.eq, operator.ne, operator.lt, operator.le, operator.gt, operator.ge] + for per1 in (ql.Period("1D"), ql.Period("1W"), ql.Period("12M")): + for per2 in (ql.Period("1D"), ql.Period("1Y")): + for op in ops: + self.assertEqual(op(per1, per2), op(per2.frequency(), per1.frequency())) + + +if __name__ == "__main__": + print("testing QuantLib", ql.__version__) + unittest.main(verbosity=2) diff --git a/Python/test/daycounters.py b/Python/test/test_daycounters.py similarity index 50% rename from Python/test/daycounters.py rename to Python/test/test_daycounters.py index 3a89d7bbaf..fe9ff2a9e9 100644 --- a/Python/test/daycounters.py +++ b/Python/test/test_daycounters.py @@ -1,27 +1,33 @@ -import QuantLib as ql -import unittest - - -class DayCountersTest(unittest.TestCase): - def runTest(self): - "Testing daycounters" - - calendar = ql.UnitedStates() - - # - # Check that SWIG signature for Business252 calendar allows to - # pass custom calendar into the class constructor. Old - # QuantLib-SWIG versions allow only to create Business252 - # calendar with default constructor parameter (Brazil - # calendar), and generate an exception when trying to pass a - # custom calendar as a parameter. So we just check here that - # no exception occurs. - # - ql.Business252(calendar) - - -if __name__ == "__main__": - print("testing QuantLib " + ql.__version__) - suite = unittest.TestSuite() - suite.addTest(DayCountersTest()) - unittest.TextTestRunner(verbosity=2).run(suite) +import QuantLib as ql +import unittest + + +class DayCountersTest(unittest.TestCase): + def test_bus252(self): + """Test Business252 daycounter""" + + calendar = ql.UnitedStates(ql.UnitedStates.GovernmentBond) + + # + # Check that SWIG signature for Business252 calendar allows to + # pass custom calendar into the class constructor. Old + # QuantLib-SWIG versions allow only to create Business252 + # calendar with default constructor parameter (Brazil + # calendar), and generate an exception when trying to pass a + # custom calendar as a parameter. So we just check here that + # no exception occurs. + # + ql.Business252(calendar) + + def test_hash(self): + for dc1 in (ql.Actual360(), ql.Thirty365()): + for dc2 in (ql.Actual360(), ql.Thirty365()): + expected = dc1.name() == dc2.name() + self.assertEqual(dc1 == dc2, expected) + self.assertEqual(dc1 != dc2, not expected) + self.assertEqual(hash(dc1) == hash(dc2), expected) + + +if __name__ == "__main__": + print("testing QuantLib", ql.__version__) + unittest.main(verbosity=2) diff --git a/Python/test/test_equityindex.py b/Python/test/test_equityindex.py new file mode 100644 index 0000000000..f837197d04 --- /dev/null +++ b/Python/test/test_equityindex.py @@ -0,0 +1,67 @@ +""" + Copyright (C) 2023 Marcin Rybacki + + This file is part of QuantLib, a free-software/open-source library + for financial quantitative analysts and developers - http://quantlib.org/ + + QuantLib is free software: you can redistribute it and/or modify it + under the terms of the QuantLib license. You should have received a + copy of the license along with this program; if not, please email + . The license is also available online at + . + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the license for more details. +""" + +import QuantLib as ql +import unittest + + +EPSILON = 1.e-2 + +CAL = ql.TARGET() +DCT = ql.Actual365Fixed() +VALUATION_DATE = CAL.adjust(ql.Date(31, ql.January, 2023)) + + +def flat_rate(rate): + return ql.FlatForward( + 2, CAL, ql.makeQuoteHandle(rate), DCT) + + +class EquityIndexTest(unittest.TestCase): + def setUp(self): + ql.Settings.instance().evaluationDate = VALUATION_DATE + + self.interest_handle = ql.YieldTermStructureHandle(flat_rate(0.03)) + self.dividend_handle = ql.YieldTermStructureHandle(flat_rate(0.01)) + spot_handle = ql.makeQuoteHandle(8690.0) + + ql.IndexManager.instance().clearHistory("eq_idx") + self.equity_idx = ql.EquityIndex( + "eq_idx", CAL, self.interest_handle, self.dividend_handle, spot_handle) + + def test_equity_index_inspectors(self): + """Testing equity index inspectors""" + fail_msg = "Unable to replicate the properties of an equity index." + + self.assertEqual(self.equity_idx.name(), "eq_idx", msg=fail_msg) + self.assertEqual(self.equity_idx.fixingCalendar(), CAL, msg=fail_msg) + + def test_equity_index_projections(self): + """Testing equity index projections""" + fail_msg = "Failed to calculate the expected index projection." + + self.assertAlmostEqual( + self.equity_idx.fixing(VALUATION_DATE), 8690.0, delta=EPSILON, msg=fail_msg) + + future_dt = ql.Date(20, ql.May, 2030) + self.assertAlmostEqual( + self.equity_idx.fixing(future_dt), 10055.76, delta=EPSILON, msg=fail_msg) + + +if __name__ == '__main__': + print("testing QuantLib", ql.__version__) + unittest.main(verbosity=2) diff --git a/Python/test/extrapolation.py b/Python/test/test_extrapolation.py similarity index 88% rename from Python/test/extrapolation.py rename to Python/test/test_extrapolation.py index 6d986e7bf0..10c3a4840e 100644 --- a/Python/test/extrapolation.py +++ b/Python/test/test_extrapolation.py @@ -40,7 +40,5 @@ def testUnknownExpExtrapolation(self): if __name__ == "__main__": - print("testing QuantLib " + ql.__version__) - suite = unittest.TestSuite() - suite.addTest(unittest.makeSuite(ExtrapolationTest, "test")) - unittest.TextTestRunner(verbosity=2).run(suite) + print("testing QuantLib", ql.__version__) + unittest.main(verbosity=2) diff --git a/Python/test/fdm.py b/Python/test/test_fdm.py similarity index 98% rename from Python/test/fdm.py rename to Python/test/test_fdm.py index 40c436a4d6..388af4c56a 100644 --- a/Python/test/fdm.py +++ b/Python/test/test_fdm.py @@ -373,7 +373,7 @@ def testAmericanOptionPricing(self): exercise = ql.AmericanExercise(todaysDate, maturityDate) - spot = ql.QuoteHandle(ql.SimpleQuote(100.0)) + spot = ql.makeQuoteHandle(100.0) volatility = ql.BlackConstantVol(todaysDate, ql.TARGET(), 0.20, dc) process = ql.BlackScholesMertonProcess( @@ -522,7 +522,7 @@ def testBSMRNDCalculator(self): s0 = 100 process = ql.BlackScholesMertonProcess( - ql.QuoteHandle(ql.SimpleQuote(s0)), + ql.makeQuoteHandle(s0), ql.YieldTermStructureHandle( ql.FlatForward(todaysDate, q, dc)), ql.YieldTermStructureHandle( @@ -645,7 +645,5 @@ def testFdmZeroInnerValue(self): if __name__ == "__main__": - print("testing QuantLib " + ql.__version__) - suite = unittest.TestSuite() - suite.addTest(unittest.makeSuite(FdmTest, "test")) - unittest.TextTestRunner(verbosity=2).run(suite) + print("testing QuantLib", ql.__version__) + unittest.main(verbosity=2) diff --git a/Python/test/iborindex.py b/Python/test/test_iborindex.py similarity index 93% rename from Python/test/iborindex.py rename to Python/test/test_iborindex.py index 233d33cd14..4fdfb944ed 100644 --- a/Python/test/iborindex.py +++ b/Python/test/test_iborindex.py @@ -70,7 +70,5 @@ def testTimeSeries(self): if __name__ == "__main__": - print("testing QuantLib " + ql.__version__) - suite = unittest.TestSuite() - suite.addTest(unittest.makeSuite(IborIndexTest, "test")) - unittest.TextTestRunner(verbosity=2).run(suite) + print("testing QuantLib", ql.__version__) + unittest.main(verbosity=2) diff --git a/Python/test/inflation.py b/Python/test/test_inflation.py similarity index 74% rename from Python/test/inflation.py rename to Python/test/test_inflation.py index 5200a73f3f..3ab81f3c11 100644 --- a/Python/test/inflation.py +++ b/Python/test/test_inflation.py @@ -45,7 +45,7 @@ CAL = ql.TARGET() -DAY_COUNTER = ql.ActualActual() +DAY_COUNTER = ql.ActualActual(ql.ActualActual.ISDA) BDC = ql.ModifiedFollowing @@ -58,13 +58,14 @@ def create_inflation_swap_helper( reference_date, inflation_data, inflation_index, + interpolation, discount_curve_handle, observation_lag=OBSERVATION_LAG, calendar=CAL, business_day_convention=BDC, day_counter=DAY_COUNTER): maturity = CAL.advance(reference_date, inflation_data[0]) - quote = ql.QuoteHandle(ql.SimpleQuote(inflation_data[1])) + quote = ql.makeQuoteHandle(inflation_data[1]) return ql.ZeroCouponInflationSwapHelper( quote, observation_lag, @@ -73,6 +74,7 @@ def create_inflation_swap_helper( business_day_convention, day_counter, inflation_index, + interpolation, discount_curve_handle) @@ -87,9 +89,8 @@ def build_nominal_term_structure( def build_hicp_index( fixing_data, - inflation_crv_handle, - interpolated=False): - index = ql.EUHICP(interpolated, inflation_crv_handle) + inflation_crv_handle): + index = ql.EUHICP(inflation_crv_handle) for x in fixing_data: # force override in case of multiple use index.addFixing(x[0], x[1], True) @@ -115,23 +116,22 @@ def build_inflation_term_structure( reference_date, zero_coupon_swaps_data, inflation_index, + interpolation, nominal_term_structure_handle, observation_lag=OBSERVATION_LAG, include_seasonality=False): helpers = [create_inflation_swap_helper(reference_date, x, inflation_index, + interpolation, nominal_term_structure_handle) for x in zero_coupon_swaps_data] base_zero_rate = zero_coupon_swaps_data[0][1] cpi_term_structure = ql.PiecewiseZeroInflation( reference_date, - CAL, - DAY_COUNTER, - observation_lag, + inflation_index.lastFixingDate(), inflation_index.frequency(), - inflation_index.interpolated(), - base_zero_rate, + DAY_COUNTER, helpers) if include_seasonality: seasonality = construct_seasonality(reference_date) @@ -144,6 +144,7 @@ def create_inflation_swap( start_date, end_date, rate, + interpolation, observation_lag=OBSERVATION_LAG, nominal=1.e6, payer=ql.Swap.Payer): @@ -157,7 +158,8 @@ def create_inflation_swap( DAY_COUNTER, rate, inflation_idx, - observation_lag) + observation_lag, + interpolation) def interpolate_historic_index( @@ -190,6 +192,7 @@ def test_par_swap_pricing_fom_indexation_without_seasonality(self): VALUATION_DATE, EUR_BEI_SWAP_RATES, inflation_idx, + ql.CPI.Flat, self.nominal_ts_handle) self.inflation_ts_handle.linkTo(inflation_ts) @@ -197,7 +200,8 @@ def test_par_swap_pricing_fom_indexation_without_seasonality(self): inflation_idx, VALUATION_DATE, CAL.advance(VALUATION_DATE, ql.Period(10, ql.Years)), - 0.0355) + 0.0355, + ql.CPI.Flat) zciis.setPricingEngine(self.discount_engine) npv = zciis.NPV() # Check whether swap prices to par @@ -227,6 +231,7 @@ def test_inflation_leg_payment_fom_indexation_without_seasonality(self): VALUATION_DATE, EUR_BEI_SWAP_RATES, inflation_idx, + ql.CPI.Flat, self.nominal_ts_handle) self.inflation_ts_handle.linkTo(inflation_ts) @@ -234,7 +239,8 @@ def test_inflation_leg_payment_fom_indexation_without_seasonality(self): inflation_idx, VALUATION_DATE, CAL.advance(VALUATION_DATE, ql.Period(10, ql.Years)), - 0.0355) + 0.0355, + ql.CPI.Flat) zciis.setPricingEngine(self.discount_engine) inflation_cf = ql.as_indexed_cashflow( @@ -274,97 +280,48 @@ def test_inflation_leg_payment_fom_indexation_without_seasonality(self): actual_payment=actual_inflation_leg_payment, expected_payment=expected_inflation_leg_payment, tolerance=EPSILON) - self.assertAlmostEquals( + self.assertAlmostEqual( first=actual_inflation_leg_payment, second=expected_inflation_leg_payment, delta=EPSILON, msg=fail_msg) - def test_swap_base_fixing_linear_indexation_without_seasonality(self): - """Testing swap base fixing for linear indexation""" + def test_lagged_fixing_method(self): + """Testing lagged fixing method""" inflation_idx = build_hicp_index( - EU_FIXING_DATA, self.inflation_ts_handle, interpolated=True) + EU_FIXING_DATA, self.inflation_ts_handle) inflation_ts = build_inflation_term_structure( VALUATION_DATE, EUR_BEI_SWAP_RATES, inflation_idx, + ql.CPI.Flat, self.nominal_ts_handle) self.inflation_ts_handle.linkTo(inflation_ts) - zciis = create_inflation_swap( - inflation_idx, - ql.Date(24, ql.August, 2018), - ql.Date(24, ql.August, 2023), - 0.032) - zciis.setPricingEngine(self.discount_engine) - - inflation_cf = ql.as_indexed_cashflow( - zciis.inflationLeg()[0]) - - swap_base_dt = inflation_cf.baseDate() - swap_base_fixing = inflation_idx.fixing(swap_base_dt) - expected_swap_base_index = interpolate_historic_index( - inflation_idx, swap_base_dt) - - fail_msg = """ Failed to replicate inflation swap base index fixing - for linear indexation: - index: {inflation_idx} - end date: {end_date} - observation lag: {observation_lag} - base index fixing: {base_index} - replicated base index fixing: {expected_base_index} - tolerance: {tolerance} - """.format(inflation_idx=inflation_idx.familyName(), - end_date=zciis.maturityDate(), - observation_lag=OBSERVATION_LAG, - base_index=swap_base_fixing, - expected_base_index=expected_swap_base_index, - tolerance=EPSILON) - self.assertAlmostEquals( - first=swap_base_fixing, - second=expected_swap_base_index, - delta=EPSILON, - msg=fail_msg) - - def test_inflation_curve_base_fixing(self): - """Testing inflation curve base fixing for linear indexation""" - - inflation_idx = build_hicp_index( - EU_FIXING_DATA, self.inflation_ts_handle, interpolated=True) - inflation_ts = build_inflation_term_structure( - VALUATION_DATE, - EUR_BEI_SWAP_RATES, - inflation_idx, - self.nominal_ts_handle) - self.inflation_ts_handle.linkTo(inflation_ts) + maturity_date = ql.Date(25, ql.October, 2027) + lag = ql.Period(3, ql.Months) + indexation = ql.CPI.Flat - curve_base_dt = inflation_ts.baseDate() - curve_base_fixing = inflation_idx.fixing(curve_base_dt) - expected_curve_base_fixing = interpolate_historic_index( - inflation_idx, curve_base_dt) + actual_fixing = ql.CPI.laggedFixing(inflation_idx, maturity_date, lag, indexation) + expected_fixing = inflation_idx.fixing(ql.Date(1, ql.July, 2027)) - fail_msg = """ Failed to replicate inflation curve base index fixing - for linear indexation: + fail_msg = """ Failed to replicate lagged fixing: index: {inflation_idx} - inflation curve base date : {base_date} - inflation curve base fixing: {base_fixing} - expected base fixing: {expected_base_fixing} + actual fixing: {actual_fixing} + expected fixing: {expected_fixing} tolerance: {tolerance} """.format(inflation_idx=inflation_idx.familyName(), - base_date=curve_base_dt, - base_fixing=curve_base_fixing, - expected_base_fixing=expected_curve_base_fixing, + actual_fixing=actual_fixing, + expected_fixing=expected_fixing, tolerance=EPSILON) - self.assertAlmostEquals( - first=curve_base_fixing, - second=expected_curve_base_fixing, + self.assertAlmostEqual( + first=actual_fixing, + second=expected_fixing, msg=fail_msg, delta=EPSILON) if __name__ == '__main__': - print('testing QuantLib ' + ql.__version__) - suite = unittest.TestSuite() - suite.addTest(unittest.makeSuite(InflationTest, 'test')) - unittest.TextTestRunner(verbosity=2).run(suite) + print("testing QuantLib", ql.__version__) + unittest.main(verbosity=2) diff --git a/Python/test/instruments.py b/Python/test/test_instruments.py similarity index 90% rename from Python/test/instruments.py rename to Python/test/test_instruments.py index ab484fc899..cb16f2f687 100644 --- a/Python/test/instruments.py +++ b/Python/test/test_instruments.py @@ -62,7 +62,5 @@ def testObservable(self): if __name__ == "__main__": - print("testing QuantLib " + ql.__version__) - suite = unittest.TestSuite() - suite.addTest(unittest.makeSuite(InstrumentTest, "test")) - unittest.TextTestRunner(verbosity=2).run(suite) + print("testing QuantLib", ql.__version__) + unittest.main(verbosity=2) diff --git a/Python/test/integrals.py b/Python/test/test_integrals.py similarity index 92% rename from Python/test/integrals.py rename to Python/test/test_integrals.py index b1bf5bdbe6..8ab1e665f9 100644 --- a/Python/test/integrals.py +++ b/Python/test/test_integrals.py @@ -65,7 +65,5 @@ def testKronrod(self): if __name__ == "__main__": - print("testing QuantLib " + ql.__version__) - suite = unittest.TestSuite() - suite.addTest(unittest.makeSuite(IntegralTest, "test")) - unittest.TextTestRunner(verbosity=2).run(suite) + print("testing QuantLib", ql.__version__) + unittest.main(verbosity=2) diff --git a/Python/test/marketelements.py b/Python/test/test_marketelements.py similarity index 62% rename from Python/test/marketelements.py rename to Python/test/test_marketelements.py index 31bc691745..cd2384cf92 100644 --- a/Python/test/marketelements.py +++ b/Python/test/test_marketelements.py @@ -37,6 +37,11 @@ def testObservable(self): me.setValue(3.14) if not flag: self.fail("Observer was not notified of market element change") + flag = None + obs.unregisterWith(me) + me.setValue(2.71) + if flag: + self.fail("Observer was notified after unregistering") def testObservableHandle(self): "Testing observability of market element handles" @@ -54,10 +59,32 @@ def testObservableHandle(self): h.linkTo(me2) if not flag: self.fail("Observer was not notified of market element change") + flag = None + obs.unregisterWith(h) + me2.setValue(2.71) + if flag: + self.fail("Observer was notified after unregistering") + + def testObservableErrors(self): + class Handle: + def __init__(self, x): + self.x = x + + def asObservable(self): + return 10 / self.x + + obs = ql.Observer(raiseFlag) + self.assertRaises(TypeError, obs.registerWith, 123) + self.assertRaises(TypeError, obs.registerWith, obs) + self.assertRaises(TypeError, obs.registerWith, Handle) + self.assertRaises(ZeroDivisionError, obs.registerWith, Handle(0)) + self.assertRaises(TypeError, obs.registerWith, Handle(1)) + + def test_SimpleQuote(self): + for value in (100, 2.71, 10**100): + self.assertAlmostEqual(ql.SimpleQuote(value).value(), value) if __name__ == "__main__": - print("testing QuantLib " + ql.__version__) - suite = unittest.TestSuite() - suite.addTest(unittest.makeSuite(MarketElementTest, "test")) - unittest.TextTestRunner(verbosity=2).run(suite) + print("testing QuantLib", ql.__version__) + unittest.main(verbosity=2) diff --git a/Python/test/test_money.py b/Python/test/test_money.py new file mode 100644 index 0000000000..7230308008 --- /dev/null +++ b/Python/test/test_money.py @@ -0,0 +1,18 @@ +import QuantLib as ql +import operator +import unittest + + +class MoneyTest(unittest.TestCase): + def test_order(self): + ops = [operator.eq, operator.ne, operator.lt, operator.le, operator.gt, operator.ge] + usd = lambda v: ql.Money(v, ql.USDCurrency()) + for m1 in (usd(1), usd(2)): + for m2 in (usd(1), usd(2)): + for op in ops: + self.assertEqual(op(m1, m2), op(m1.value(), m2.value())) + + +if __name__ == "__main__": + print("testing QuantLib", ql.__version__) + unittest.main(verbosity=2) diff --git a/Python/test/ode.py b/Python/test/test_ode.py similarity index 88% rename from Python/test/ode.py rename to Python/test/test_ode.py index 139f7522fd..e14e9733a7 100644 --- a/Python/test/ode.py +++ b/Python/test/test_ode.py @@ -42,7 +42,5 @@ def test2dODE(self): if __name__ == '__main__': - print('testing QuantLib ' + ql.__version__) - suite = unittest.TestSuite() - suite.addTest(unittest.makeSuite(OdeTest,'test')) - unittest.TextTestRunner(verbosity=2).run(suite) + print("testing QuantLib", ql.__version__) + unittest.main(verbosity=2) diff --git a/Python/test/options.py b/Python/test/test_options.py similarity index 89% rename from Python/test/options.py rename to Python/test/test_options.py index c4d4ad04ef..4b4de29490 100644 --- a/Python/test/options.py +++ b/Python/test/test_options.py @@ -41,7 +41,7 @@ def testFdHestonHullWhite(self): a = 0.00883 sig = 0.00631 - underlying = ql.QuoteHandle(ql.SimpleQuote(s0)) + underlying = ql.makeQuoteHandle(s0) option = ql.VanillaOption( ql.PlainVanillaPayoff(ql.Option.Call, s0), @@ -54,8 +54,7 @@ def testFdHestonHullWhite(self): option.setPricingEngine( ql.FdHestonHullWhiteVanillaEngine( ql.HestonModel(heston_process), hull_white_process, -0.5, - 10, 200, 25, 10, - controlVariate=True + 10, 200, 25, 10, 0, True ) ) @@ -87,7 +86,7 @@ def testAnalyticHestonHullWhite(self): expected = 40.028973 s0 = 100 - underlying = ql.QuoteHandle(ql.SimpleQuote(s0)) + underlying = ql.makeQuoteHandle(s0) hull_white_model = ql.HullWhite(r, a, sig) heston_model = ql.HestonModel( @@ -106,7 +105,5 @@ def testAnalyticHestonHullWhite(self): if __name__ == '__main__': - print('testing QuantLib ' + ql.__version__) - suite = unittest.TestSuite() - suite.addTest(unittest.makeSuite(OptionsTest,'test')) - unittest.TextTestRunner(verbosity=2).run(suite) + print("testing QuantLib", ql.__version__) + unittest.main(verbosity=2) diff --git a/Python/test/ratehelpers.py b/Python/test/test_ratehelpers.py similarity index 94% rename from Python/test/ratehelpers.py rename to Python/test/test_ratehelpers.py index 0694916220..34845cf5b1 100644 --- a/Python/test/ratehelpers.py +++ b/Python/test/test_ratehelpers.py @@ -29,7 +29,7 @@ def setUp(self): self.settlement_days = 3 self.face_amount = 100.0 self.redemption = 100.0 - self.quote_handle = ql.QuoteHandle(ql.SimpleQuote(100.0)) + self.quote_handle = ql.makeQuoteHandle(100.0) self.issue_date = ql.Date(2, 1, 2008) self.maturity_date = ql.Date(2, 1, 2018) @@ -122,7 +122,7 @@ def build_eur_curve(self, quotes_date): # build rate helpers dayCounter = ql.Actual360() - # looping left if somone wants two add more deposits to tests, e.g. T/N + # looping left if someone wants two add more deposits to tests, e.g. T/N self.depositHelpers = [ ql.DepositRateHelper( @@ -140,8 +140,7 @@ def build_eur_curve(self, quotes_date): self.oisHelpers = [ ql.OISRateHelper( settlementDays, ql.Period(n, unit), - ql.QuoteHandle(self.ois[(n, unit)]), self.on_index, - self.discounting_yts_handle) + ql.QuoteHandle(self.ois[(n, unit)]), self.on_index) for n, unit in self.ois.keys() ] @@ -192,7 +191,7 @@ def test_ois_default_calendar(self): already set and additional calendar will have no impact. The test checks if the constructor exposed to Python maintains this desired property and verifies that the start date of a EUR plain vanilla OIS traded on March - 29th, 2018 is equal to April 4th, 2018 (du to holiday on March 30th, + 29th, 2018 is equal to April 4th, 2018 (due to holiday on March 30th, 2018 in TARGET calendar. """ test_date = ql.Date(29, 3, 2018) @@ -204,7 +203,6 @@ def test_ois_default_calendar(self): ql.Following) self.assertEqual(expected_date, ql.Date(4, 4, 2018)) ois = ql.MakeOIS(ql.Period('1Y'), eonia, -0.003, ql.Period(0, ql.Days)) - print(ois.startDate()) self.assertEqual(expected_date, ois.startDate()) def tearDown(self): @@ -294,7 +292,7 @@ def build_eur_curve(self, quotes_date): oisHelpers = [ ql.OISRateHelper( settlementDays, ql.Period(n, unit), - ql.QuoteHandle(ois[(n, unit)]), on_index, discounting_yts_handle + ql.QuoteHandle(ois[(n, unit)]), on_index ) for n, unit in ois.keys() ] @@ -333,7 +331,7 @@ def build_pln_fx_swap_curve(self, base_ccy_yts, fx_swaps, fx_spot): calendar = ql.JointCalendar(ql.TARGET(), ql.Poland()) spot_date_lag = 2 - trading_calendar = ql.UnitedStates() + trading_calendar = ql.UnitedStates(ql.UnitedStates.GovernmentBond) # build rate helpers @@ -341,7 +339,7 @@ def build_pln_fx_swap_curve(self, base_ccy_yts, fx_swaps, fx_spot): fxSwapHelpers = [ ql.FxSwapRateHelper( - ql.QuoteHandle(ql.SimpleQuote(fx_swaps[(n, unit)])), + ql.makeQuoteHandle(fx_swaps[(n, unit)]), ql.QuoteHandle(spotFx), ql.Period(n, unit), spot_date_lag, @@ -441,7 +439,7 @@ def testFxMarketConventionsForCrossRate(self): spot_date = ql.Date(5, 7, 2016) self.build_curves(today) - us_calendar = ql.UnitedStates() + us_calendar = ql.UnitedStates(ql.UnitedStates.GovernmentBond) joint_calendar = ql.JointCalendar(ql.TARGET(), ql.Poland()) @@ -477,15 +475,15 @@ def testFxMarketConventionsForCrossRateONPeriod(self): # dates base_ccy_yts = ql.RelinkableYieldTermStructureHandle() - us_calendar = ql.UnitedStates() + us_calendar = ql.UnitedStates(ql.UnitedStates.GovernmentBond) joint_calendar = ql.JointCalendar(ql.TARGET(), ql.Poland()) # Settlement should be on a day where all three centers are operating # and follow EndOfMonth rule on_rate_helper = ql.FxSwapRateHelper( - ql.QuoteHandle(ql.SimpleQuote(fwd_points)), - ql.QuoteHandle(ql.SimpleQuote(self.fx_spot_quote_EURPLN)), + ql.makeQuoteHandle(fwd_points), + ql.makeQuoteHandle(self.fx_spot_quote_EURPLN), on_period, fixing_days, joint_calendar, @@ -506,7 +504,7 @@ def testFxMarketConventionsForCrossRateAdjustedSpotDate(self): today = ql.Date(30, 6, 2016) spot_date = ql.Date(5, 7, 2016) self.build_curves(today) - us_calendar = ql.UnitedStates() + us_calendar = ql.UnitedStates(ql.UnitedStates.GovernmentBond) joint_calendar = ql.JointCalendar(ql.TARGET(), ql.Poland()) settlement_calendar = ql.JointCalendar(joint_calendar, us_calendar) @@ -545,11 +543,11 @@ def testFxMarketConventionsForDatesInEURUSD_ON_Period(self): # and one day in US, therefore it is sufficient to pass only Target # as a base calendar calendar = ql.TARGET() - trading_calendar = ql.UnitedStates() + trading_calendar = ql.UnitedStates(ql.UnitedStates.GovernmentBond) on_rate_helper = ql.FxSwapRateHelper( - ql.QuoteHandle(ql.SimpleQuote(fwd_points)), - ql.QuoteHandle(ql.SimpleQuote(self.fx_spot_quote_EURUSD)), + ql.makeQuoteHandle(fwd_points), + ql.makeQuoteHandle(self.fx_spot_quote_EURUSD), on_period, fixing_days, calendar, @@ -585,11 +583,11 @@ def testFxMarketConventionsForDatesInEURUSD_ShortEnd(self): # as a base calendar. Passing joint calendar would result in wrong # spot date of the trade calendar = ql.TARGET() - trading_calendar = ql.UnitedStates() + trading_calendar = ql.UnitedStates(ql.UnitedStates.GovernmentBond) rate_helper = ql.FxSwapRateHelper( - ql.QuoteHandle(ql.SimpleQuote(fwd_points)), - ql.QuoteHandle(ql.SimpleQuote(self.fx_spot_quote_EURUSD)), + ql.makeQuoteHandle(fwd_points), + ql.makeQuoteHandle(self.fx_spot_quote_EURUSD), period, fixing_days, calendar, @@ -608,7 +606,7 @@ def tearDown(self): def flat_rate(rate): return ql.FlatForward( - 0, ql.NullCalendar(), ql.QuoteHandle(ql.SimpleQuote(rate)), ql.Actual365Fixed()) + 0, ql.NullCalendar(), ql.makeQuoteHandle(rate), ql.Actual365Fixed()) class CrossCurrencyBasisSwapRateHelperTest(unittest.TestCase): @@ -651,7 +649,7 @@ def buildRateHelper( is_fx_base_ccy_collateral_ccy, is_basis_on_fx_base_ccy_leg): tenor, rate = quote_tuple - quote_handle = ql.QuoteHandle(ql.SimpleQuote(rate * self.basis_point)) + quote_handle = ql.makeQuoteHandle(rate * self.basis_point) return ql.ConstNotionalCrossCurrencyBasisSwapRateHelper( quote_handle, tenor, @@ -680,7 +678,7 @@ def assertImpliedQuotes( # Trigger bootstrap discount_at_origin = term_structure.discount(settlement_date) - self.assertAlmostEquals( + self.assertAlmostEqual( first=discount_at_origin, second=1.0, delta=eps) for q, h in zip(self.cross_currency_basis_quotes, helpers): @@ -696,7 +694,7 @@ def assertImpliedQuotes( actual_rate=actual_rate, expected_rate=expected_rate, tolerance=eps) - self.assertAlmostEquals( + self.assertAlmostEqual( first=actual_rate, second=expected_rate, delta=eps, @@ -735,11 +733,5 @@ def tearDown(self): if __name__ == "__main__": - print("testing QuantLib " + ql.__version__) - suite = unittest.TestSuite() - suite.addTest(unittest.makeSuite(FixedRateBondHelperTest, "test")) - suite.addTest(unittest.makeSuite(OISRateHelperTest, "test")) - suite.addTest(unittest.makeSuite(FxSwapRateHelperTest, "test")) - suite.addTest(unittest.makeSuite( - CrossCurrencyBasisSwapRateHelperTest, "test")) - unittest.TextTestRunner(verbosity=2).run(suite) + print("testing QuantLib", ql.__version__) + unittest.main(verbosity=2) diff --git a/Python/test/sabr.py b/Python/test/test_sabr.py similarity index 95% rename from Python/test/sabr.py rename to Python/test/test_sabr.py index 787938b4c9..050e1d60d8 100644 --- a/Python/test/sabr.py +++ b/Python/test/test_sabr.py @@ -116,7 +116,5 @@ def testSabrPdeVsCevPdeVsAnalyticCev(self): msg="Unable to match PDE CEV value with analytic CEV value") if __name__ == '__main__': - print('testing QuantLib ' + ql.__version__) - suite = unittest.TestSuite() - suite.addTest(unittest.makeSuite(SabrTest, "test")) - unittest.TextTestRunner(verbosity=2).run(suite) + print("testing QuantLib", ql.__version__) + unittest.main(verbosity=2) diff --git a/Python/test/test_settings.py b/Python/test/test_settings.py new file mode 100644 index 0000000000..3332a83bf8 --- /dev/null +++ b/Python/test/test_settings.py @@ -0,0 +1,34 @@ +import QuantLib as ql +import unittest + + +class SettingsTest(unittest.TestCase): + def test_properties(self): + settings = ql.Settings.instance() + with ql.SavedSettings(): + for v in (ql.Date(1, 1, 2023), ql.Date(2, 2, 2023)): + settings.evaluationDate = v + self.assertEqual(settings.evaluationDate, v) + for v in (True, False): + settings.enforcesTodaysHistoricFixings = v + self.assertEqual(settings.enforcesTodaysHistoricFixings, v) + for v in (True, False): + settings.includeReferenceDateEvents = v + self.assertEqual(settings.includeReferenceDateEvents, v) + for v in (True, False, None): + settings.includeTodaysCashFlows = v + self.assertEqual(settings.includeTodaysCashFlows, v) + + def test_saved_settings(self): + settings = ql.Settings.instance() + settings.evaluationDate = ql.Date(1, 1, 2023) + with ql.SavedSettings(): + settings.evaluationDate = ql.Date(2, 2, 2023) + self.assertEqual(settings.evaluationDate, ql.Date(2, 2, 2023)) + # Test that SavedSettings restores settings on exit. + self.assertEqual(settings.evaluationDate, ql.Date(1, 1, 2023)) + + +if __name__ == "__main__": + print("testing QuantLib", ql.__version__) + unittest.main(verbosity=2) diff --git a/Python/test/slv.py b/Python/test/test_slv.py similarity index 81% rename from Python/test/slv.py rename to Python/test/test_slv.py index a98abac1da..c4b6e06bd5 100644 --- a/Python/test/slv.py +++ b/Python/test/test_slv.py @@ -28,7 +28,7 @@ def setUp(self): self.dc = ql.Actual365Fixed() self.riskFreeRate = ql.YieldTermStructureHandle(ql.FlatForward(self.settlementDate, 0.05, self.dc)) self.dividendYield = ql.YieldTermStructureHandle(ql.FlatForward(self.settlementDate, 0.025, self.dc)) - self.underlying = ql.QuoteHandle(ql.SimpleQuote(100.0)) + self.underlying = ql.makeQuoteHandle(100.0) def tearDown(self): ql.Settings.instance().evaluationDate = ql.Date() @@ -147,10 +147,32 @@ def testSlvProcessAsBlackScholes(self): "double barrier option price with Black-Scholes Double Barrier Binary Engine", ) + def testFixedLocalVolSurface(self): + """ Testing FixedLocalVolSurface interpolation """ + + dc = ql.Actual365Fixed() + maturities = [ql.Date(1, 3, 2020), ql.Date(1, 6, 2020)] + strikes = [60, 100, 130] + local_vols = [[0.2, 0.3], [0.25, 0.4], [0.3, 0.4]] + + fixed_local_vol_surf = ql.FixedLocalVolSurface( + self.todaysDate, + [dc.yearFraction(self.todaysDate, d) for d in maturities], + strikes, + local_vols, + dc + ) + + fixed_local_vol_surf.setInterpolation("linear") + + self.assertEqual(60, fixed_local_vol_surf.minStrike()) + self.assertEqual(130, fixed_local_vol_surf.maxStrike()) + + self.assertAlmostEqual(0.2, fixed_local_vol_surf.localVol(ql.Date(1, 3, 2020), 60)) + self.assertAlmostEqual(0.3, fixed_local_vol_surf.localVol(ql.Date(1, 6, 2020), 60)) + self.assertAlmostEqual(0.25, fixed_local_vol_surf.localVol(ql.Date(16, 4, 2020), 60)) if __name__ == "__main__": - print("testing QuantLib " + ql.__version__) - suite = unittest.TestSuite() - suite.addTest(unittest.makeSuite(SlvTest, "test")) - unittest.TextTestRunner(verbosity=2).run(suite) + print("testing QuantLib", ql.__version__) + unittest.main(verbosity=2) diff --git a/Python/test/solvers1d.py b/Python/test/test_solvers1d.py similarity index 93% rename from Python/test/solvers1d.py rename to Python/test/test_solvers1d.py index e30f9eeb31..855da44199 100644 --- a/Python/test/solvers1d.py +++ b/Python/test/test_solvers1d.py @@ -28,7 +28,7 @@ def derivative(self, x): class Solver1DTest(unittest.TestCase): - def runTest(self): + def test_solve(self): "Testing 1-D solvers" for factory in [ql.Brent, ql.Bisection, ql.FalsePosition, ql.Ridder, ql.Secant]: solver = factory() @@ -87,7 +87,5 @@ def runTest(self): if __name__ == "__main__": - print("testing QuantLib " + ql.__version__) - suite = unittest.TestSuite() - suite.addTest(Solver1DTest()) - unittest.TextTestRunner(verbosity=2).run(suite) + print("testing QuantLib", ql.__version__) + unittest.main(verbosity=2) diff --git a/Python/test/swap.py b/Python/test/test_swap.py similarity index 53% rename from Python/test/swap.py rename to Python/test/test_swap.py index 8af8c3d325..3145c73edd 100644 --- a/Python/test/swap.py +++ b/Python/test/test_swap.py @@ -1,5 +1,6 @@ """ Copyright (C) 2021 Marcin Rybacki + Copyright (C) 2023 Marcin Rybacki This file is part of QuantLib, a free-software/open-source library for financial quantitative analysts and developers - http://quantlib.org/ @@ -25,17 +26,32 @@ DCT = ql.Actual365Fixed() -VALUATION_DATE = CAL.adjust(ql.Date(1, ql.June, 2021)) +IR_FIXINGS = [(ql.Date(3, ql.January, 2023), 0.033), + (ql.Date(4, ql.January, 2023), 0.033), + (ql.Date(5, ql.January, 2023), 0.033), + (ql.Date(6, ql.January, 2023), 0.033), + (ql.Date(9, ql.January, 2023), 0.03), + (ql.Date(10, ql.January, 2023), 0.03), + (ql.Date(11, ql.January, 2023), 0.03), + (ql.Date(12, ql.January, 2023), 0.03), + (ql.Date(13, ql.January, 2023), 0.03), + (ql.Date(17, ql.January, 2023), 0.03), + (ql.Date(20, ql.January, 2023), 0.03), + (ql.Date(23, ql.January, 2023), 0.03), + (ql.Date(24, ql.January, 2023), 0.03), + (ql.Date(25, ql.January, 2023), 0.03), + (ql.Date(26, ql.January, 2023), 0.03)] def flat_rate(rate): return ql.FlatForward( - 2, CAL, ql.QuoteHandle(ql.SimpleQuote(rate)), ql.Actual365Fixed()) + 2, CAL, ql.makeQuoteHandle(rate), ql.Actual365Fixed()) class ZeroCouponSwapTest(unittest.TestCase): def setUp(self): - ql.Settings.instance().evaluationDate = VALUATION_DATE + valuation_date = CAL.adjust(ql.Date(1, ql.June, 2021)) + ql.Settings.instance().evaluationDate = valuation_date self.nominal_ts_handle = ql.YieldTermStructureHandle(flat_rate(0.007)) self.ibor_idx = ql.Euribor6M(self.nominal_ts_handle) self.engine = ql.DiscountingSwapEngine(self.nominal_ts_handle) @@ -132,11 +148,101 @@ def test_zero_coupon_swap_legs(self): fail_msg_flt = """Floating leg cash flow type should be SubPeriodsCoupon but was {actual}. """.format(actual=type(flt_cf)) - self.assertTrue(isinstance(flt_cf, ql.SubPeriodsCoupon), msg=fail_msg_flt) + self.assertTrue(isinstance( + flt_cf, ql.SubPeriodsCoupon), msg=fail_msg_flt) + + +class EquityTotalReturnSwapTest(unittest.TestCase): + def setUp(self): + valuation_date = ql.Date(27, ql.January, 2023) + ql.Settings.instance().evaluationDate = valuation_date + + self.interest_handle = ql.YieldTermStructureHandle(flat_rate(0.03)) + self.dividend_handle = ql.YieldTermStructureHandle(flat_rate(0.0)) + equity_spot = ql.makeQuoteHandle(8690.0) + + self.equity_idx = ql.EquityIndex( + "eq_idx", + CAL, + self.interest_handle, + self.dividend_handle, + equity_spot) + ql.IndexManager.instance().clearHistory(self.equity_idx.name()) + self.equity_idx.addFixing(ql.Date(5, ql.January, 2023), 9010.0) + + self.ibor_idx = ql.USDLibor( + ql.Period(3, ql.Months), self.interest_handle) + ql.IndexManager.instance().clearHistory(self.ibor_idx.name()) + self.sofr_idx = ql.Sofr(self.interest_handle) + ql.IndexManager.instance().clearHistory(self.sofr_idx.name()) + + for f_dt, f_val in IR_FIXINGS: + self.ibor_idx.addFixing(f_dt, f_val) + self.sofr_idx.addFixing(f_dt, f_val) + + def build_trs(self, interest_idx, start, end, margin=0.025): + schedule = ql.Schedule( + start, + end, + interest_idx.tenor(), + interest_idx.fixingCalendar(), + interest_idx.businessDayConvention(), + interest_idx.businessDayConvention(), + ql.DateGeneration.Backward, + False) + return ql.EquityTotalReturnSwap(ql.Swap.Receiver, + 1.0e6, + schedule, + self.equity_idx, + interest_idx, + DCT, + margin) + + def test_trs_interest_rate_index(self): + """Testing equity total return swap interest rate index""" + start = ql.Date(5, ql.January, 2023) + end = ql.Date(5, ql.April, 2023) + + trs_vs_ibor = self.build_trs(self.ibor_idx, start, end) + trs_vs_sofr = self.build_trs(self.sofr_idx, start, end) + + fail_msg = "Incorrect interest rate index set to TRS." + + self.assertEqual(trs_vs_ibor.interestRateIndex().name(), + "USDLibor3M Actual/360", + msg=fail_msg) + self.assertEqual(trs_vs_sofr.interestRateIndex().name(), + "SOFRON Actual/360", + msg=fail_msg) + + def test_trs_npv(self): + """Testing equity total return swap NPV""" + start = ql.Date(5, ql.January, 2023) + end = ql.Date(5, ql.April, 2023) + + pricer = ql.DiscountingSwapEngine(self.interest_handle) + + trs_vs_ibor = self.build_trs(self.ibor_idx, start, end) + trs_vs_ibor.setPricingEngine(pricer) + + trs_vs_sofr = self.build_trs(self.sofr_idx, start, end) + trs_vs_sofr.setPricingEngine(pricer) + + par_trs_vs_ibor = self.build_trs( + self.ibor_idx, start, end, trs_vs_ibor.fairMargin()) + par_trs_vs_ibor.setPricingEngine(pricer) + par_trs_vs_sofr = self.build_trs( + self.sofr_idx, start, end, trs_vs_sofr.fairMargin()) + par_trs_vs_sofr.setPricingEngine(pricer) + + fail_msg = "Par TRS expected to have NPV equal to zero." + + self.assertAlmostEqual( + par_trs_vs_ibor.NPV(), 0.0, delta=EPSILON, msg=fail_msg) + self.assertAlmostEqual( + par_trs_vs_sofr.NPV(), 0.0, delta=EPSILON, msg=fail_msg) if __name__ == '__main__': - print('testing QuantLib ' + ql.__version__) - suite = unittest.TestSuite() - suite.addTest(unittest.makeSuite(ZeroCouponSwapTest, 'test')) - unittest.TextTestRunner(verbosity=2).run(suite) + print("testing QuantLib", ql.__version__) + unittest.main(verbosity=2) diff --git a/Python/test/swaption.py b/Python/test/test_swaption.py similarity index 94% rename from Python/test/swaption.py rename to Python/test/test_swaption.py index 1a6f37b5b8..3e83307e30 100644 --- a/Python/test/swaption.py +++ b/Python/test/test_swaption.py @@ -65,12 +65,12 @@ def swap_pv01(underlying): def make_const_black_vol_engine(discount_handle, volatility): - h = ql.QuoteHandle(ql.SimpleQuote(volatility)) + h = ql.makeQuoteHandle(volatility) return ql.BlackSwaptionEngine(discount_handle, h) def make_const_bachelier_vol_engine(discount_handle, volatility): - h = ql.QuoteHandle(ql.SimpleQuote(volatility)) + h = ql.makeQuoteHandle(volatility) return ql.BachelierSwaptionEngine(discount_handle, h) @@ -88,7 +88,7 @@ def setUp(self): projection_curve_handle.linkTo(projection_curve) self.discount_handle = ql.YieldTermStructureHandle(ql.FlatForward( - self.today, ql.QuoteHandle(ql.SimpleQuote(0.0085)), ql.Actual365Fixed())) + self.today, ql.makeQuoteHandle(0.0085), ql.Actual365Fixed())) self.swap_engine = ql.DiscountingSwapEngine(self.discount_handle) self.idx = ql.Euribor6M(projection_curve_handle) @@ -132,7 +132,7 @@ def _assert_swaption_annuity(self, l, self.idx, strike, ql.Period(0, ql.Days), effectiveDate=start_date, fixedLegTenor=ql.Period(1, ql.Years), - fixedLegDayCount=ql.Thirty360(), + fixedLegDayCount=ql.Thirty360(ql.Thirty360.BondBasis), floatingLegSpread=0.0, swapType=t) underlying.setPricingEngine(self.swap_engine) @@ -171,7 +171,7 @@ def _assert_swaption_annuity(self, method=SETTLEMENT_METHOD_MAP[m], annuity=annuity, expected_annuity=expected_annuity) - self.assertAlmostEquals( + self.assertAlmostEqual( first=annuity, second=expected_annuity, delta=EPSILON, @@ -191,7 +191,5 @@ def test_swaption_annuity_bachelier_model(self): if __name__ == "__main__": - print("testing QuantLib " + ql.__version__) - suite = unittest.TestSuite() - suite.addTest(unittest.makeSuite(SwaptionTest, "test")) - unittest.TextTestRunner(verbosity=2).run(suite) + print("testing QuantLib", ql.__version__) + unittest.main(verbosity=2) diff --git a/Python/test/termstructures.py b/Python/test/test_termstructures.py similarity index 62% rename from Python/test/termstructures.py rename to Python/test/test_termstructures.py index 20321570d9..5927223aa8 100644 --- a/Python/test/termstructures.py +++ b/Python/test/test_termstructures.py @@ -52,12 +52,13 @@ class TermStructureTest(unittest.TestCase): def setUp(self): self.calendar = ql.TARGET() today = self.calendar.adjust(ql.Date.todaysDate()) + ql.Settings.instance().evaluationDate = today self.settlementDays = 2 self.dayCounter = ql.Actual360() settlement = self.calendar.advance(today, self.settlementDays, ql.Days) deposits = [ ql.DepositRateHelper( - ql.QuoteHandle(ql.SimpleQuote(rate / 100)), + ql.makeQuoteHandle(rate / 100), ql.Period(n, units), self.settlementDays, self.calendar, @@ -75,12 +76,12 @@ def setUp(self): ] swaps = [ ql.SwapRateHelper( - ql.QuoteHandle(ql.SimpleQuote(rate / 100)), + ql.makeQuoteHandle(rate / 100), ql.Period(years, ql.Years), self.calendar, ql.Annual, ql.Unadjusted, - ql.Thirty360(), + ql.Thirty360(ql.Thirty360.BondBasis), ql.Euribor6M(), ) for (years, rate) in [(1, 4.54), (5, 4.99), (10, 5.47), (20, 5.89), (30, 5.96)] @@ -89,6 +90,9 @@ def setUp(self): self.termStructure = ql.PiecewiseFlatForward( settlement, deposits + swaps, self.dayCounter) + def tearDown(self): + ql.Settings.instance().evaluationDate = ql.Date() + def testImpliedObs(self): "Testing observability of implied term structure" global flag @@ -146,7 +150,7 @@ def testCompositeZeroYieldStructure(self): freq = ql.Semiannual flatTs = ql.FlatForward( settlement, - ql.QuoteHandle(ql.SimpleQuote(0.0085)), + ql.makeQuoteHandle(0.0085), self.dayCounter) firstHandle = ql.YieldTermStructureHandle(flatTs) secondHandle = ql.YieldTermStructureHandle(self.termStructure) @@ -165,7 +169,7 @@ def testCompositeZeroYieldStructure(self): actual zero rate: {actual} """.format(expected=expectedZeroRate, actual=actualZeroRate) - self.assertAlmostEquals( + self.assertAlmostEqual( first=expectedZeroRate, second=actualZeroRate, delta=1.0e-12, @@ -174,8 +178,8 @@ def testCompositeZeroYieldStructure(self): def testUltimateForwardTermStructure(self): """Testing ultimate forward term structure""" settlement = self.termStructure.referenceDate() - ufr = ql.QuoteHandle(ql.SimpleQuote(0.06)) - llfr = ql.QuoteHandle(ql.SimpleQuote(0.05)) + ufr = ql.makeQuoteHandle(0.06) + llfr = ql.makeQuoteHandle(0.05) fsp = ql.Period(20, ql.Years) alpha = 0.05 baseCrvHandle = ql.YieldTermStructureHandle(self.termStructure) @@ -197,15 +201,123 @@ def testUltimateForwardTermStructure(self): """.format(timeToMaturity=t, expected=expectedForward, actual=actualForward) - self.assertAlmostEquals( + self.assertAlmostEqual( first=expectedForward, second=actualForward, delta=1.0e-12, msg=failMsg) + def testQuantoTermStructure(self): + """Testing quanto term structure""" + today = ql.Date.todaysDate() + + dividend_ts = ql.YieldTermStructureHandle( + ql.FlatForward( + today, + ql.makeQuoteHandle(0.055), + self.dayCounter + ) + ) + r_domestic_ts = ql.YieldTermStructureHandle( + ql.FlatForward( + today, + ql.makeQuoteHandle(-0.01), + self.dayCounter + ) + ) + r_foreign_ts = ql.YieldTermStructureHandle( + ql.FlatForward( + today, + ql.makeQuoteHandle(0.02), + self.dayCounter + ) + ) + sigma_s = ql.BlackVolTermStructureHandle( + ql.BlackConstantVol( + today, + self.calendar, + ql.makeQuoteHandle(0.25), + self.dayCounter + ) + ) + sigma_fx = ql.BlackVolTermStructureHandle( + ql.BlackConstantVol( + today, + self.calendar, + ql.makeQuoteHandle(0.05), + self.dayCounter + ) + ) + rho = ql.makeQuoteHandle(0.3) + s_0 = ql.makeQuoteHandle(100.0) + + exercise = ql.EuropeanExercise(self.calendar.advance(today, 6, ql.Months)) + payoff = ql.PlainVanillaPayoff(ql.Option.Call, 95.0) + + vanilla_option = ql.VanillaOption(payoff, exercise) + quanto_ts = ql.YieldTermStructureHandle( + ql.QuantoTermStructure( + dividend_ts, + r_domestic_ts, + r_foreign_ts, + sigma_s, + ql.nullDouble(), + sigma_fx, + ql.nullDouble(), + rho.value() + ) + ) + gbm_quanto = ql.BlackScholesMertonProcess(s_0, quanto_ts, r_domestic_ts, sigma_s) + vanilla_engine = ql.AnalyticEuropeanEngine(gbm_quanto) + vanilla_option.setPricingEngine(vanilla_engine) + + quanto_option = ql.QuantoVanillaOption(payoff, exercise) + gbm_vanilla = ql.BlackScholesMertonProcess(s_0, dividend_ts, r_domestic_ts, sigma_s) + quanto_engine = ql.QuantoEuropeanEngine(gbm_vanilla, r_foreign_ts, sigma_fx, rho) + quanto_option.setPricingEngine(quanto_engine) + + quanto_option_pv = quanto_option.NPV() + vanilla_option_pv = vanilla_option.NPV() + + message = """Failed to reproduce QuantoOption / EuropeanQuantoEngine NPV: + {quanto_pv} + by using the QuantoTermStructure as the dividend together with + VanillaOption / AnalyticEuropeanEngine: + {vanilla_pv} + """.format( + quanto_pv=quanto_option_pv, + vanilla_pv=vanilla_option_pv + ) + + self.assertAlmostEqual( + quanto_option_pv, + vanilla_option_pv, + delta=1e-12, + msg=message + ) + + def testLazyObject(self): + evaluationDate = ql.Settings.instance().evaluationDate + nodes = self.termStructure.nodes() + self.termStructure.freeze() + + ql.Settings.instance().evaluationDate = self.calendar.advance(evaluationDate, 100, ql.Days) + + # Check that dates and rates are unchanged + for i in range(len(self.termStructure.nodes())): + self.assertEqual(nodes[i][0], self.termStructure.nodes()[i][0]) + self.assertEqual(nodes[i][1], self.termStructure.nodes()[i][1]) + + self.termStructure.recalculate() + + # Check that dates have changed (except the reference, which is fixed) + for i in range(1, len(self.termStructure.nodes())): + self.assertNotEqual(nodes[i][0], self.termStructure.nodes()[i][0]) + + ql.Settings.instance().evaluationDate = evaluationDate + + self.termStructure.unfreeze() if __name__ == "__main__": - print("testing QuantLib " + ql.__version__) - suite = unittest.TestSuite() - suite.addTest(unittest.makeSuite(TermStructureTest, "test")) - unittest.TextTestRunner(verbosity=2).run(suite) + print("testing QuantLib", ql.__version__) + unittest.main(verbosity=2) diff --git a/Python/test/volatilities.py b/Python/test/test_volatilities.py similarity index 79% rename from Python/test/volatilities.py rename to Python/test/test_volatilities.py index 9f29ac4f32..a3ee3664ed 100644 --- a/Python/test/volatilities.py +++ b/Python/test/test_volatilities.py @@ -1,5 +1,6 @@ """ Copyright (C) 2020 Marcin Rybacki + Copyright (C) 2022 Skandinaviska Enskilda Banken AB (publ) This file is part of QuantLib, a free-software/open-source library for financial quantitative analysts and developers - http://quantlib.org/ @@ -16,6 +17,7 @@ """ import unittest +import math import QuantLib as ql @@ -189,9 +191,9 @@ def build_linear_swaption_cube( swap_index_base, short_swap_index_base=None, vega_weighted_smile_fit=False): - vol_spreads = [[ql.QuoteHandle(ql.SimpleQuote(v)) for v in row] + vol_spreads = [[ql.makeQuoteHandle(v) for v in row] for row in vol_spreads] - cube = ql.SwaptionVolCube2( + cube = ql.InterpolatedSwaptionVolatilityCube( ql.SwaptionVolatilityStructureHandle(volatility_matrix), spread_opt_tenors, spread_swap_tenors, @@ -208,10 +210,10 @@ def sabr_parameters_guess(number_of_options, number_of_swaps): n_elements = number_of_options * number_of_swaps guess = n_elements * [0] for n in range(n_elements): - guess[n] = (ql.QuoteHandle(ql.SimpleQuote(0.2)), - ql.QuoteHandle(ql.SimpleQuote(0.5)), - ql.QuoteHandle(ql.SimpleQuote(0.4)), - ql.QuoteHandle(ql.SimpleQuote(0.0))) + guess[n] = (ql.makeQuoteHandle(0.2), + ql.makeQuoteHandle(0.5), + ql.makeQuoteHandle(0.4), + ql.makeQuoteHandle(0.0)) return guess @@ -226,11 +228,11 @@ def build_sabr_swaption_cube( vega_weighted_smile_fit=False, is_parameter_fixed=(False, False, False, False), is_atm_calibrated=True): - v_spreads = [[ql.QuoteHandle(ql.SimpleQuote(v)) for v in row] + v_spreads = [[ql.makeQuoteHandle(v) for v in row] for row in vol_spreads] guess = sabr_parameters_guess( len(spread_opt_tenors), len(spread_swap_tenors)) - cube = ql.SwaptionVolCube1( + cube = ql.SabrSwaptionVolatilityCube( ql.SwaptionVolatilityStructureHandle(volatility_matrix), spread_opt_tenors, spread_swap_tenors, @@ -262,14 +264,13 @@ def setUp(self): def tearDown(self): ql.Settings.instance().evaluationDate = ql.Date() - def _get_fair_rate(self, option_tenor, swap_tenor): - exercise_date = CAL.advance(self.today, option_tenor) + def _get_fair_rate(self, exercise_date, swap_tenor): start_date = CAL.advance(exercise_date, ql.Period(2, ql.Days)) underlying = ql.MakeVanillaSwap( swap_tenor, self.idx, 0.0, ql.Period(0, ql.Days), effectiveDate=start_date, fixedLegTenor=ql.Period(1, ql.Years), - fixedLegDayCount=ql.Thirty360(), + fixedLegDayCount=ql.Thirty360(ql.Thirty360.BondBasis), floatingLegSpread=0.0, swapType=ql.Swap.Receiver) underlying.setPricingEngine(self.swap_engine) @@ -279,9 +280,9 @@ def _assert_atm_strike( self, cube, interpolation, vol_type): opt_tenor = ql.Period(1, ql.Years) swap_tenor = ql.Period(10, ql.Years) - expected_atm_strike = self._get_fair_rate(opt_tenor, swap_tenor) - actual_atm_strike = cube.atmStrike( - cube.optionDateFromTenor(opt_tenor), swap_tenor) + exercise_date = cube.optionDateFromTenor(opt_tenor) + expected_atm_strike = self._get_fair_rate(exercise_date, swap_tenor) + actual_atm_strike = cube.atmStrike(exercise_date, swap_tenor) fail_msg = """ ATM strike test failed for: cube interpolation: {interpolation} volatility_type: {vol_type} @@ -295,7 +296,7 @@ def _assert_atm_strike( swap_tenor=swap_tenor, strike=actual_atm_strike, replicated_strike=expected_atm_strike) - self.assertAlmostEquals( + self.assertAlmostEqual( first=actual_atm_strike, second=expected_atm_strike, delta=TOLERANCE, @@ -330,7 +331,7 @@ def _assert_atm_vol( vol=actual_vol, expected_vol=expected_vol, eps=epsilon) - self.assertAlmostEquals( + self.assertAlmostEqual( first=actual_vol, second=expected_vol, delta=epsilon, @@ -366,7 +367,7 @@ def _assert_vol_spread( vol=actual_vol, expected_vol=expected_vol, eps=epsilon) - self.assertAlmostEquals( + self.assertAlmostEqual( first=actual_vol, second=expected_vol, delta=epsilon, @@ -522,8 +523,112 @@ def test_sabr_lognormal_cube_spread_vol(self): epsilon=SABR_SPREAD_TOLERANCE) +class SviSmileSectionTest(unittest.TestCase): + def setUp(self): + ql.Settings.instance().evaluationDate = ql.Date(3, ql.May, 2022) + + def tearDown(self): + # The objects created in this test are not immediately garbage-collected + # by PyPy, and can throw errors when the global evaluation date changes. + # Thus, we need to force a collection when we're done with them. + import gc + gc.collect() + + def test_svi_smile_section(self): + """Testing the SviSmileSection against already fitted parameters""" + expiry_date = ql.Date(16, ql.December, 2022) + forward = 100 + atm_vol = 0.325819 + # parameters = a, b, sigma, rho, m + svi_parameters = [-0.651304, 0.986546, 0.838493, 0.520853, 0.695177] + + smile = ql.SviSmileSection(expiry_date, forward, svi_parameters) + + self.assertAlmostEqual(smile.volatility(forward), atm_vol, places=5) + self.assertAlmostEqual(smile.volatility(257.328), 0.739775, places=5) + + def test_svi_interpolated_smile_section(self): + """Testing the SviInterpolatedSmileSection's parameter fitting against given vols""" + expiry_date = ql.Date(16, ql.December, 2022) + forward = 100 + strikes = [25.6134, 48.5585, 71.5027, 94.4478, 117.3920, 140.3372, 163.2814, 186.2265, 209.1707, 232.1149] + has_floating_strikes = False + atm_vol = 0.325819 + vols = [0.881504, 0.627807, 0.456964, 0.343740, 0.297482, 0.321816, 0.390772, 0.476758, 0.565635, 0.651507] + + a = -0.6 + b = 0.9 + sigma = 0.8 + rho = 0.5 + m = 0.6 + + interpolated_smile = ql.SviInterpolatedSmileSection( + expiry_date, forward, strikes, has_floating_strikes, atm_vol, vols, + a, b, sigma, rho, m, + False, False, False, False, False + ) + + self.assertAlmostEqual(interpolated_smile.volatility(forward), atm_vol, places=5) + self.assertAlmostEqual(interpolated_smile.volatility(257.328), 0.739775, places=5) + + +class AndreasenHugeVolatilityTest(unittest.TestCase): + def testLocalVolCalibration(self): + """ Testing Andreasen-Huge Local Volatility calibration""" + + today = ql.Settings.instance().evaluationDate + + spot = ql.makeQuoteHandle(100) + + dc = ql.Actual365Fixed() + qTS = ql.YieldTermStructureHandle(ql.FlatForward(today, 0.025, dc)) + rTS = ql.YieldTermStructureHandle(ql.FlatForward(today, 0.05, dc)) + + vol_data = [ + # maturity in days, strike, volatility + (30, 75, 0.13), + (30, 100, 0.26), + (30, 125, 0.3), + (180, 80, 0.4), + (180, 150, 0.6), + (365, 110, 0.5)] + + calibration_set = ql.CalibrationSet( + [( + ql.VanillaOption( + ql.PlainVanillaPayoff(ql.Option.Call, strike), + ql.EuropeanExercise(today + ql.Period(maturity_in_days, ql.Days)) + ), + ql.SimpleQuote(volatility) + ) for maturity_in_days, strike, volatility in vol_data] + ) + + local_vol = ql.LocalVolTermStructureHandle( + ql.AndreasenHugeLocalVolAdapter( + ql.AndreasenHugeVolatilityInterpl(calibration_set, spot, rTS, qTS) + ) + ) + + option = calibration_set[-2][0] # maturity in days: 180, strike: 150, vol: 0.6 + + dummy_vol = ql.BlackVolTermStructureHandle() + local_vol_process = ql.GeneralizedBlackScholesProcess(spot, qTS, rTS, dummy_vol, local_vol) + + option.setPricingEngine(ql.MCEuropeanEngine( + local_vol_process, "lowdiscrepancy", + timeSteps=75, brownianBridge=True, requiredSamples=16000, seed=42) + ) + + t = dc.yearFraction(today, option.exercise().lastDate()) + fwd = spot.value() * qTS.discount(t) / rTS.discount(t) + vol = calibration_set[-2][1].value() + + expected = ql.BlackCalculator( + ql.as_plain_vanilla_payoff(option.payoff()), fwd, vol * math.sqrt(t), rTS.discount(t)).value() + + self.assertAlmostEqual(expected, option.NPV(), delta=0.2) + + if __name__ == "__main__": - print("testing QuantLib " + ql.__version__) - suite = unittest.TestSuite() - suite.addTest(unittest.makeSuite(SwaptionVolatilityCubeTest, "test")) - unittest.TextTestRunner(verbosity=2).run(suite) + print("testing QuantLib", ql.__version__) + unittest.main(verbosity=2) diff --git a/R/DESCRIPTION b/R/DESCRIPTION index cb5db8d541..837cabfa15 100644 --- a/R/DESCRIPTION +++ b/R/DESCRIPTION @@ -1,6 +1,6 @@ Package: QuantLib Title: QuantLib bindings for R -Version: 1.26 +Version: 1.34 Date: $Date$ Maintainer: Dirk Eddelbuettel Author: The QuantLib Group @@ -8,4 +8,4 @@ Description: This package provides the SWIG-generated QuantLib bindings for R Depends: methods SystemRequirements: QuantLib library, Boost library License: QuantLib License -URL: http://quantlib.org +URL: https://www.quantlib.org diff --git a/R/DESCRIPTION.in b/R/DESCRIPTION.in index 9d0ac7489c..409967858a 100644 --- a/R/DESCRIPTION.in +++ b/R/DESCRIPTION.in @@ -8,4 +8,4 @@ Description: This package provides the SWIG-generated QuantLib bindings for R Depends: methods SystemRequirements: QuantLib library, Boost library License: QuantLib License -URL: http://quantlib.org +URL: https://www.quantlib.org diff --git a/R/demo/00Index b/R/demo/00Index index b85a4ded63..5c1a77a64b 100644 --- a/R/demo/00Index +++ b/R/demo/00Index @@ -1,8 +1,8 @@ european-option European Option -fd-option Finite-Differences Option +fd-option Finite-Differences Option graph Graph demo scatter Scatter plot wireframe Wireframe -bonds Zero Coupon, Fixed Rate and Floating Rate Bonds +bonds Zero Coupon, Fixed Rate and Floating Rate Bonds bates_vol_surface Bates Stochastic Volatility - +curves Sanity check for exported curves diff --git a/R/demo/american-option.R b/R/demo/american-option.R new file mode 100644 index 0000000000..4bfafb922d --- /dev/null +++ b/R/demo/american-option.R @@ -0,0 +1,81 @@ + +suppressMessages(library(QuantLib)) + +## global data +calendar <- TARGET() + +settlementDate <- Date(15, "May", 1998) +settlementDate <- Calendar_adjust(calendar, settlementDate) + +fixingDays <- 3 +settlementDays <- 3 +todaysDate <- Calendar_advance(calendar, settlementDate, -fixingDays, "Days") +invisible(Settings_instance()$setEvaluationDate(d=todaysDate)) + +cat('Today : ', todaysDate$`__str__`(), "\n") +cat('Settlement Date: ', settlementDate$`__str__`(), "\n") + + +## option +exercise = AmericanExercise(todaysDate, Date(17, "May", 1999)) +payoff = PlainVanillaPayoff("Put", 40.0) +option = VanillaOption(payoff, exercise) + + +# ### Market data + +# %% +underlying = SimpleQuote(36.0) +dividendYield = FlatForward(todaysDate, 0.00, Actual365Fixed()) +volatility = BlackConstantVol(todaysDate, calendar, 0.20, Actual365Fixed()) +riskFreeRate = FlatForward(todaysDate, 0.06, Actual365Fixed()) + +# %% +process = BlackScholesMertonProcess( + QuoteHandle(underlying), + YieldTermStructureHandle(dividendYield), + YieldTermStructureHandle(riskFreeRate), + BlackVolTermStructureHandle(volatility) +) + +# ### Pricing +# We'll collect tuples of method name, option value, and estimated error from the analytic formula. + +# %% +results = NULL + +# #### Analytic approximations + +option$setPricingEngine(BaroneAdesiWhaleyApproximationEngine(process)) +results = rbind(results, data.frame("method"="Barone-Adesi-Whaley", "NPV"=option$NPV())) + +option$setPricingEngine(BjerksundStenslandApproximationEngine(process)) +results = rbind(results, data.frame("method"="Bjerksund-Stensland", "NPV"=option$NPV())) + + +# #### Finite-difference method + +timeSteps = 801 +gridPoints = 800 + +option$setPricingEngine(FdBlackScholesVanillaEngine(process, timeSteps, gridPoints)) +results = rbind(results, data.frame("method"="finite differences", "NPV"=option$NPV())) + + +# #### Binomial method + +timeSteps = 801 + +# %% +listEngineFuncsNames = lsf.str("package:QuantLib") +listEngineFuncsNames = listEngineFuncsNames[grepl(pattern = "^Binomial", x = listEngineFuncsNames)] +listEngineFuncsNames = listEngineFuncsNames[grepl(pattern = "VanillaEngine$", x = listEngineFuncsNames)] + +for (engineFuncName in listEngineFuncsNames) { + eval(parse(text=paste0("option$setPricingEngine(", engineFuncName, "(process, timeSteps))"))) + results = rbind(results, data.frame("method"=engineFuncName, "NPV"=option$NPV())) +} + + +# ### Results +print(results) diff --git a/R/demo/basket-option.R b/R/demo/basket-option.R new file mode 100644 index 0000000000..9cd367208d --- /dev/null +++ b/R/demo/basket-option.R @@ -0,0 +1,104 @@ + +# inspired by python example with the same name + +suppressMessages(library(QuantLib)) + +## global data +calendar <- TARGET() + +todaysDate <- Date(15, "May", 1998) +invisible(Settings_instance()$setEvaluationDate(d=todaysDate)) + +settlementDays <- 3 +settlementDate <- Calendar_advance(calendar, todaysDate, settlementDays, "Days") + +cat('Today : ', todaysDate$`__str__`(), "\n") +cat('Settlement Date: ', settlementDate$`__str__`(), "\n") + +riskFreeRate = FlatForward(settlementDate, 0.05, Actual365Fixed()) + + +# ### Option parameters +exerciseDate = Date(17, "May", 1999) +exercise = EuropeanExercise(exerciseDate) +payoff = PlainVanillaPayoff("Call", 8.0) + + +# ### Market data +underlying1 = SimpleQuote(7.0) +volatility1 = BlackConstantVol(todaysDate, calendar, 0.10, Actual365Fixed()) +dividendYield1 = FlatForward(settlementDate, 0.05, Actual365Fixed()) +underlying2 = SimpleQuote(7.0) +volatility2 = BlackConstantVol(todaysDate, calendar, 0.10, Actual365Fixed()) +dividendYield2 = FlatForward(settlementDate, 0.05, Actual365Fixed()) + + +process1 = BlackScholesMertonProcess( + QuoteHandle(underlying1), + YieldTermStructureHandle(dividendYield1), + YieldTermStructureHandle(riskFreeRate), + BlackVolTermStructureHandle(volatility1) +) + +process2 = BlackScholesMertonProcess( + QuoteHandle(underlying2), + YieldTermStructureHandle(dividendYield2), + YieldTermStructureHandle(riskFreeRate), + BlackVolTermStructureHandle(volatility2) +) + +corrMatrix = Matrix(2, 2) +invisible(Matrix_setitem(corrMatrix, 0, 0, 1.0)) +invisible(Matrix_setitem(corrMatrix, 1, 1, 1.0)) +invisible(Matrix_setitem(corrMatrix, 1, 0, 0.5)) +invisible(Matrix_setitem(corrMatrix, 0, 1, 0.5)) +cat(corrMatrix$`__str__`()) + +procVector = StochasticProcess1DVector(2) +invisible(StochasticProcess1DVector___setitem__(self = procVector, i = 0, x = process1)) +invisible(StochasticProcess1DVector___setitem__(self = procVector, i = 1, x = process2)) + +process = StochasticProcessArray(array = procVector, correlation = corrMatrix) + +# ### Pricing - European + +basketoption = BasketOption(MaxBasketPayoff(payoff), exercise) +basketoption$setPricingEngine( + MCPREuropeanBasketEngine(process=process, timeSteps=NA, timeStepsPerYear=1, brownianBridge=F, antitheticVariate=F, requiredSamples=NA, requiredTolerance=0.02, maxSamples=10000, seed=42) +) +print(basketoption$NPV()) + +basketoption = BasketOption(MinBasketPayoff(payoff), exercise) +basketoption$setPricingEngine( + MCPREuropeanBasketEngine(process=process, timeSteps=NA, timeStepsPerYear=1, brownianBridge=F, antitheticVariate=F, requiredSamples=NA, requiredTolerance=0.02, maxSamples=10000, seed=42) +) +print(basketoption$NPV()) + +basketoption = BasketOption(AverageBasketPayoff(payoff, 2), exercise) +basketoption$setPricingEngine( + MCPREuropeanBasketEngine(process=process, timeSteps=NA, timeStepsPerYear=1, brownianBridge=F, antitheticVariate=F, requiredSamples=NA, requiredTolerance=0.02, maxSamples=10000, seed=42) +) +print(basketoption$NPV()) + + +# ### Pricing - American + +americanExercise = AmericanExercise(settlementDate, exerciseDate) +americanbasketoption = BasketOption(MaxBasketPayoff(payoff), americanExercise) +americanbasketoption$setPricingEngine( + MCPRAmericanBasketEngine( + process=process, + timeSteps=10, + timeStepsPerYear=NA, + brownianBridge=F, + antitheticVariate=F, + requiredSamples=50000, + requiredTolerance=0.02, + maxSamples=100000, + seed=42, + nCalibrationSamples=5000, + polynomOrder=5, + polynomType=LsmBasisSystem_Hermite_get() + ) +) +print(americanbasketoption$NPV()) diff --git a/R/demo/bonds.R b/R/demo/bonds.R index c6eecd5875..0b889bfda0 100644 --- a/R/demo/bonds.R +++ b/R/demo/bonds.R @@ -149,7 +149,6 @@ discountingTermStructure <- RelinkableYieldTermStructureHandle() ## the one used for forward rate forecasting forecastingTermStructure <- RelinkableYieldTermStructureHandle() - ######################################## ## BONDS TO BE PRICED # ######################################## @@ -237,11 +236,11 @@ invisible(setCouponPricer(Bond_cashflows(floatingRateBond), pricer)) ## Yield curve bootstrapping -invisible(RelinkableQuoteHandle_linkTo(forecastingTermStructure, depoSwapTermStructure)) -invisible(RelinkableQuoteHandle_linkTo(discountingTermStructure, bondDiscountingTermStructure)) +invisible(RelinkableYieldTermStructureHandle_linkTo(forecastingTermStructure, depoSwapTermStructure)) +invisible(RelinkableYieldTermStructureHandle_linkTo(discountingTermStructure, bondDiscountingTermStructure)) ## We are using the depo & swap curve to estimate the future Libor rates -invisible(RelinkableQuoteHandle_linkTo(liborTermStructure, depoSwapTermStructure)) +invisible(RelinkableYieldTermStructureHandle_linkTo(liborTermStructure, depoSwapTermStructure)) ## df <- data.frame(zeroCoupon=c(Instrument_NPV(zeroCouponBond), diff --git a/R/demo/cashflows.R b/R/demo/cashflows.R new file mode 100644 index 0000000000..56e019cc52 --- /dev/null +++ b/R/demo/cashflows.R @@ -0,0 +1,144 @@ + +# inspired by python example with the same name + +suppressMessages(library(QuantLib)) + +## global data +calendar <- TARGET() + +todaysDate <- Date(19, "October", 2020) +invisible(Settings_instance()$setEvaluationDate(d=todaysDate)) + +settlementDays <- 3 +settlementDate <- Calendar_advance(calendar, todaysDate, settlementDays, "Days") + +cat('Today : ', todaysDate$`__str__`(), "\n") +cat('Settlement Date: ', settlementDate$`__str__`(), "\n") + +# ### Term structure construction + +# %% +dates = DateVector() +DateVector_append(dates, DateParser_parseISO("2020-10-19")) +DateVector_append(dates, DateParser_parseISO("2020-11-19")) + +DateVector_append(dates, DateParser_parseISO("2021-01-19")) +DateVector_append(dates, DateParser_parseISO("2021-04-19")) +DateVector_append(dates, DateParser_parseISO("2021-10-19")) + +DateVector_append(dates, DateParser_parseISO("2022-04-19")) +DateVector_append(dates, DateParser_parseISO("2022-10-19")) + +DateVector_append(dates, DateParser_parseISO("2023-10-19")) +DateVector_append(dates, DateParser_parseISO("2025-10-19")) +DateVector_append(dates, DateParser_parseISO("2030-10-19")) +DateVector_append(dates, DateParser_parseISO("2035-10-19")) +DateVector_append(dates, DateParser_parseISO("2040-10-19")) + +DateVector_size(dates) + +rates = c( + -0.004, + -0.002, + 0.001, + 0.005, + 0.009, + 0.010, + 0.010, + 0.012, + 0.017, + 0.019, + 0.028, + 0.032 +) + +length(rates) + +forecast_curve = ZeroCurve(dates, rates, Actual365Fixed()) +forecast_handle = YieldTermStructureHandle(forecast_curve) + +# ### Swap construction +# +# We'll use an overnight swap as an example. +# We're keeping the initialization simple, +# but the analysis work in the same way for more complex ones, +# as well as for other kinds of swaps and bonds (once we extract the cashflows from them using the proper methods). + +# %% +swapBuilder = MakeOIS(swapTenor=Period(5, "Years"), + overnightIndex=Eonia(forecast_handle), + fixedRate=0.002) + +swap = MakeOIS_makeOIS(swapBuilder) + +# ### Cash-flow analysis +# +# The fixed-rate coupons can be extracted from the swap using the `fixedLeg` method. +# + +fixed_leg = swap$fixedLeg() +CashFlows_maturityDate(fixed_leg) + +df = NULL +for (i in seq(1, fixed_leg$size())) { + cfl = fixed_leg[i][[1]] + df = rbind(df, data.frame(date=Date_ISO(CashFlow_date(cfl)), amount=CashFlow_amount(cfl))) +} +print(df) + + +# +# If we want to extract more information, we need to upcast the coupons to a more specific class. +# This can be done by using the `as_coupon` or the `as_fixed_rate_coupon` method. +# + +df = NULL +for (i in seq(1, fixed_leg$size())) { + cfl = fixed_leg[i][[1]] + cflCoupon = as_coupon(cfl) + cflCouponFix = as_fixed_rate_coupon(cfl) + cflCouponFixIr = FixedRateCoupon_interestRate(cflCouponFix) + + df = rbind(df, data.frame(date=Date_ISO(CashFlow_date(cfl)), amount=CashFlow_amount(cfl), + accrualStartDate=Date_ISO(Coupon_accrualStartDate(cflCoupon)), + accrualEndDate=Date_ISO(Coupon_accrualEndDate(cflCoupon)), + accrualPeriod=Coupon_accrualPeriod(cflCoupon), + accrualDays=Coupon_accrualDays(cflCoupon), + accrualDayCounter=DayCounter_name(Coupon_dayCounter(cflCoupon)), + accruedAmount=Coupon_accruedAmount(self = cflCoupon, todaysDate), + rate=InterestRate_rate(cflCouponFixIr), rateDayCount=DayCounter_name(InterestRate_dayCounter(cflCouponFixIr)) + )) +} +print(df) + + + +# %% +floating_leg = swap$overnightLeg() + +# %% +df = NULL +for (i in seq(1, floating_leg$size())) { + cfl = floating_leg[i][[1]] + cflCoupon = as_coupon(cfl) + cflCouponFloat = as_floating_rate_coupon(cfl) + cflCouponFloatIdx = FloatingRateCoupon_index(cflCouponFloat) + + + df = rbind(df, data.frame(date=Date_ISO(CashFlow_date(cfl)), amount=CashFlow_amount(cfl), + accrualStartDate=Date_ISO(Coupon_accrualStartDate(cflCoupon)), + accrualEndDate=Date_ISO(Coupon_accrualEndDate(cflCoupon)), + accrualPeriod=Coupon_accrualPeriod(cflCoupon), + accrualDays=Coupon_accrualDays(cflCoupon), + accrualDayCounter=DayCounter_name(Coupon_dayCounter(cflCoupon)), + accruedAmount=Coupon_accruedAmount(self = cflCoupon, todaysDate), + spread=FloatingRateCoupon_spread(cflCouponFloat), + gearing=FloatingRateCoupon_gearing(cflCouponFloat), + adjFix=FloatingRateCoupon_adjustedFixing(cflCouponFloat), + dtFix=Date_ISO(FloatingRateCoupon_fixingDate(cflCouponFloat)), + convexityAdj=FloatingRateCoupon_convexityAdjustment(cflCouponFloat) + )) +} + +print(df) + diff --git a/R/demo/cds.R b/R/demo/cds.R new file mode 100644 index 0000000000..5cf66ff39f --- /dev/null +++ b/R/demo/cds.R @@ -0,0 +1,116 @@ + +# inspired by python example with the same name + +suppressMessages(library(QuantLib)) + +## global data +calendar <- TARGET() + +todaysDate <- DateParser_parseISO("2007-05-15") +invisible(Settings_instance()$setEvaluationDate(d=todaysDate)) + +settlementDays <- 3 +settlementDate <- Calendar_advance(calendar, todaysDate, settlementDays, "Days") + +cat('Today : ', Date_ISO(todaysDate), "\n") +cat('Settlement Date: ', Date_ISO(settlementDate), "\n") + +risk_free_rate = YieldTermStructureHandle(FlatForward(todaysDate, 0.01, Actual365Fixed())) + +# ### CDS parameters + +recovery_rate = 0.5 +quoted_spreads = c(0.0150, 0.0150, 0.0150, 0.0150) + +tenors = PeriodVector() +tenors$append(Period(3, "Months")) +tenors$append(Period(6, "Months")) +tenors$append(Period(1, "Years")) +tenors$append(Period(2, "Years")) + +maturities = DateVector() +for (i in seq(1, tenors$size())) { + maturities$append( + Calendar_adjust(calendar, + Calendar_advance(calendar, todaysDate, tenors[i][[1]]), + "Following") + ) +} + +instruments = DefaultProbabilityHelperVector() +for (i in seq(1, length(quoted_spreads))) { + s = quoted_spreads[i] + tenor = tenors[i][[1]] + + it = SpreadCdsHelper( + QuoteHandle(SimpleQuote(s)), + tenor, + 0, + calendar, + "Quarterly", + "Following", + DateGeneration_TwentiethIMM_get(), + Actual365Fixed(), + recovery_rate, + risk_free_rate + ) + + instruments$append(it) +} + +hazard_curve = PiecewiseFlatHazardRate(todaysDate, instruments, Actual365Fixed()) + +print("Calibrated hazard rate values: ") +for (i in seq(1, hazard_curve$dates()$size())) { + print(paste("hazard rate on ", + Date_ISO(hazard_curve$dates()[i][[1]]), + "is ", + HazardRateCurve_hazardRates(hazard_curve)[i])) +} + +print(paste("1Y survival probability:", + DefaultProbabilityTermStructure_survivalProbability(hazard_curve, Calendar_advance(calendar, todaysDate, Period(1, "Years"))), + "expected 0.9704 " +)) + +print(paste("2Y survival probability:", + DefaultProbabilityTermStructure_survivalProbability(hazard_curve, Calendar_advance(calendar, todaysDate, Period(2, "Years"))), + "expected 0.9418 " +)) + +# ### Reprice instruments + +nominal = 1000000.0 +probability = DefaultProbabilityTermStructureHandle(hazard_curve) + +# We'll create a cds for every maturity: + +print("Repricing of quoted CDSs employed for calibration: ") +for (i in seq(1, length(quoted_spreads))) { + maturity = maturities[i][[1]] + s = quoted_spreads[i] + tenor = tenors[i][[1]] + + schedule = Schedule( + todaysDate, + maturity, + Period(3, "Months"), # "Quarterly", + calendar, + "Following", + "Unadjusted", + DateGeneration_TwentiethIMM_get(), + F + ) + + cds = CreditDefaultSwap(Protection_Seller_get(), nominal, s, schedule, "Following", Actual365Fixed()) + engine = MidPointCdsEngine(probability, recovery_rate, risk_free_rate) + cds$setPricingEngine(engine) + + print(paste("fair spread: ", Period___str__(tenor), cds$fairSpread())) + print(paste(" NPV: ", cds$NPV())) + print(paste(" default leg: ", cds$defaultLegNPV())) + print(paste(" coupon leg: ", cds$couponLegNPV())) + print("") + +} + diff --git a/R/demo/curves.R b/R/demo/curves.R new file mode 100644 index 0000000000..5cc8af6f9a --- /dev/null +++ b/R/demo/curves.R @@ -0,0 +1,17 @@ + + +suppressMessages(library(QuantLib)) + + +bToday = QuantLib::Date(31, "August", 2022) +QuantLib::Settings_setEvaluationDate(self = QuantLib::Settings_instance(), d = bToday) +print(Settings_instance()$getEvaluationDate()) + +bufDates = QuantLib::DateVector() +QuantLib::DateVector_append(self = bufDates, x = Date(31, "August", 2022)) +QuantLib::DateVector_append(self = bufDates, x = Date(30, "September", 2022)) +QuantLib::DateVector_size(self = bufDates) + +bCurve = QuantLib::DiscountCurve(dates = bufDates , discounts = c(1.0, 1.0), dayCounter = Actual360()) + + diff --git a/R/demo/european-option.R b/R/demo/european-option.R index b2123b1537..4e8fb8b453 100644 --- a/R/demo/european-option.R +++ b/R/demo/european-option.R @@ -70,7 +70,7 @@ invisible(option$setPricingEngine(BinomialLRVanillaEngine(process,timeSteps))) report('binomial (LR)', option$NPV()) ## method: Monte Carlo -invisible(option$setPricingEngine(MCPREuropeanEngine(process, +invisible(option$setPricingEngine(MCLDEuropeanEngine(process, timeSteps = 1, timeStepsPerYear=NA, brownianBridge=FALSE, @@ -79,7 +79,7 @@ invisible(option$setPricingEngine(MCPREuropeanEngine(process, requiredTolerance = 0.02, maxSamples=NULL, seed = 42))) -report('MC (crude)', option$NPV(), option$errorEstimate()) +report('MC (crude)', option$NPV()) #, option$errorEstimate()) invisible(option$setPricingEngine(MCLDEuropeanEngine(process, timeSteps = 1, @@ -92,4 +92,3 @@ invisible(option$setPricingEngine(MCLDEuropeanEngine(process, seed=42))) report('MC (Sobol)', option$NPV()) - diff --git a/R/demo/gaussian1d-models.R b/R/demo/gaussian1d-models.R new file mode 100644 index 0000000000..f9b647e64f --- /dev/null +++ b/R/demo/gaussian1d-models.R @@ -0,0 +1,361 @@ + +# inspired by python example with the same name + +suppressMessages(library(QuantLib)) + +## global data +calendar <- TARGET() + +todaysDate <- DateParser_parseISO("2014-04-30") +invisible(Settings_instance()$setEvaluationDate(d=todaysDate)) + +settlementDays <- 3 +settlementDate <- Calendar_advance(calendar, todaysDate, settlementDays, "Days") + +cat('Today : ', Date_ISO(todaysDate), "\n") +cat('Settlement Date: ', Date_ISO(settlementDate), "\n") + +refDate = todaysDate + +# ### Calculations + +# This exercise tries to replicate the Quantlib C++ `Gaussian1dModel` example on how to use the GSR and Markov Functional model. + +# We assume a multicurve setup, for simplicity with flat yield term structures. +# +# The discounting curve is an Eonia curve at a level of 2% and the forwarding curve is an Euribor 6m curve at a level of 2.5%. +# +# For the volatility we assume a flat swaption volatility at 20%. + + +forward6mQuote = QuoteHandle(SimpleQuote(0.025)) +oisQuote = QuoteHandle(SimpleQuote(0.02)) +volQuote = QuoteHandle(SimpleQuote(0.2)) + +# %% +dc = Actual365Fixed() +yts6m = FlatForward(refDate, forward6mQuote, dc) +ytsOis = FlatForward(refDate, oisQuote, dc) +yts6m$enableExtrapolation() +ytsOis$enableExtrapolation() + +hyts6m = RelinkableYieldTermStructureHandle(yts6m) +t0_curve = YieldTermStructureHandle(yts6m) +t0_Ois = YieldTermStructureHandle(ytsOis) +euribor6m = Euribor6M(hyts6m) +swaptionVol = ConstantSwaptionVolatility(0, calendar, "ModifiedFollowing", volQuote, Actual365Fixed()) + + +# %% +effectiveDate = Calendar_advance(calendar, refDate, 2, "Days") +print(Date_ISO(effectiveDate)) + +maturityDate = Calendar_advance(calendar, effectiveDate, 10, "Years") +print(Date_ISO(maturityDate)) + +# %% +fixedSchedule = Schedule(effectiveDate, + maturityDate, + Period(1, "Years"), + calendar, + "ModifiedFollowing", + "ModifiedFollowing", + DateGeneration_Forward_get(), F) + +# %% +floatSchedule = Schedule(effectiveDate, + maturityDate, + Period(6, "Months"), + calendar, + "ModifiedFollowing", + "ModifiedFollowing", + DateGeneration_Forward_get(), F) + + +# We consider a standard 10-years Bermudan payer swaption with yearly exercises at a strike of 4%. + +# %% +fixedNominal = rep(1, fixedSchedule$size()-1) +floatingNominal = rep(1, floatSchedule$size()-1) +strike = rep(0.04, fixedSchedule$size()-1) +gearing = rep(1, floatSchedule$size()-1) +spread = rep(0, floatSchedule$size()-1) + +# %% +underlying = NonstandardSwap( + Swap_Payer_get(), + fixedNominal, floatingNominal, fixedSchedule, strike, + Thirty360(Thirty360_BondBasis_get()), floatSchedule, + euribor6m, gearing, spread, Actual360(), F, F, "ModifiedFollowing") + +# %% +exerciseDates = Schedule_dates(self = fixedSchedule, .copy = T) +for (i in seq(0, exerciseDates$size()-1)) { # C Style vector numbering + DateVector___setitem__(exerciseDates, i, Calendar_advance(calendar, DateVector___getitem__(exerciseDates, i), -2, "Days")) +} + +DateVector___delitem__(exerciseDates, 0) +DateVector___delitem__(exerciseDates, DateVector___len__(exerciseDates)-1) +for (i in seq(1, exerciseDates$size())) { # R style vector numbering + print(exerciseDates[i][[1]]) +} + +exercise = BermudanExercise(exerciseDates) +swaption = NonstandardSwaption(underlying,exercise,Settlement_Physical_get()) + +# The model is a one factor Hull White model with piecewise volatility adapted to our exercise dates. +# +# The reversion is just kept constant at a level of 1%. + +stepDates = DateVector(exerciseDates) +DateVector___delitem__(stepDates, DateVector___len__(stepDates)-1) + +sigmas = QuoteHandleVector() +for (i in seq(1, 9)) { + QuoteHandleVector_append(sigmas, QuoteHandle(SimpleQuote(0.01))) +} + +reversion = QuoteHandleVector() +QuoteHandleVector_append(reversion, QuoteHandle(SimpleQuote(0.01))) + +# +# The model's curve is set to the 6m forward curve. +# Note that the model adapts automatically to other curves where appropriate +# (e.g. if an index requires a different forwarding curve) or where explicitly specified (e.g. in a swaption pricing engine). + + +gsr = Gsr(t0_curve, stepDates, sigmas, reversion) +swaptionEngine = Gaussian1dSwaptionEngine(gsr, 64, 7.0, T, F, t0_Ois) +nonstandardSwaptionEngine = Gaussian1dNonstandardSwaptionEngine( + gsr, 64, 7.0, T, F, QuoteHandle(SimpleQuote(0)), t0_Ois) + +# %% +swaption$setPricingEngine(nonstandardSwaptionEngine) + +# %% +swapBase = EuriborSwapIsdaFixA(Period(10, 'Years'), t0_curve, t0_Ois) +basket = swaption$calibrationBasket(swapBase, swaptionVol, 'Naive') + +# %% +for (i in seq(1, basket$size())) { + basket_i = basket[i][[1]] + BlackCalibrationHelper_setPricingEngine(basket_i, swaptionEngine) +} + + +# %% +method = LevenbergMarquardt() +ec = EndCriteria(1000, 10, 1e-8, 1e-8, 1e-8) + +# %% +gsr$calibrateVolatilitiesIterative(basket, method, ec) + + +# %% [markdown] +# The engine can generate a calibration basket in two modes. +# +# The first one is called Naive and generates ATM swaptions adapted to the exercise dates of the swaption and its maturity date. The resulting basket looks as follows: + +# %% +basket_data = function(basket) { + df = NULL + for (i in seq(1, basket$size())) { + basket_i = basket[i][[1]] + h = as_swaption_helper(basket_i) + hopt = SwaptionHelper_swaption(h) + df = rbind(df, data.frame(expiry=Date_ISO(SwaptionHelper_swaptionExpiryDate(h)), + maturity=Date_ISO(SwaptionHelper_swaptionMaturityDate(h)), + nominal=SwaptionHelper_swaptionNominal(h), + strike=SwaptionHelper_swaptionStrike(h), + optType=Swaption_type(hopt) + + )) + } + df +} + +print(basket_data(basket)) + + + + +# +# Let's calibrate our model to this basket. +# We use a specialized calibration method calibrating the sigma function one by one to the calibrating vanilla swaptions. The result of this is as follows: + +# %% +calibration_data = function(basket, volatilities) { + # volatilities = gsr$volatility() + df = NULL + for (i in seq(1, basket$size())) { + basket_i = basket[i][[1]] + vola_i = volatilities[i][[1]] + h = as_swaption_helper(basket_i) + hopt = SwaptionHelper_swaption(h) + modelPrice = basket_i$modelValue() + modelImpVol = BlackCalibrationHelper_impliedVolatility(self = basket_i, targetValue = modelPrice, accuracy = 1e-6, maxEvaluations = 1000, minVol = 0.0, maxVol = 2.0) + marketPrice = basket_i$marketValue() + + df = rbind(df, data.frame(expiry=Date_ISO(SwaptionHelper_swaptionExpiryDate(h)), + modelSigma=vola_i, + modelPrice=modelPrice, + marketPrice=marketPrice, + modelImpVol=modelImpVol, + marketImpVol=QuoteHandle_value(basket_i$volatility()) + )) + } + + df +} + +print(calibration_data(basket, gsr$volatility())) + +# %% [markdown] +# Bermudan swaption NPV (ATM calibrated GSR): + +# %% +print(swaption$NPV()) + + +# +# There is another mode to generate a calibration basket called `MaturityStrikeByDeltaGamma`. +# This means that the maturity, the strike and the nominal of the calibrating swaptions are obtained matching the NPV, +# first derivative and second derivative of the swap you will exercise into at at each bermudan call date. +# The derivatives are taken with respect to the model's state variable. +# +# Let's try this in our case. + +# %% +basket = swaption$calibrationBasket(swapBase, swaptionVol, 'MaturityStrikeByDeltaGamma') +print(basket_data(basket)) + +# %% +for (i in seq(1, basket$size())) { + basket_i = basket[i][[1]] + BlackCalibrationHelper_setPricingEngine(basket_i, swaptionEngine) +} + + +# %% [markdown] +# The calibrated nominal is close to the exotics nominal. The expiries and maturity dates of the vanillas are the same as in the case above. The difference is the strike which is now equal to the exotics strike. +# +# Let's see how this affects the exotics NPV. The recalibrated model is: + +# %% +gsr$calibrateVolatilitiesIterative(basket, method, ec) +print(calibration_data(basket, gsr$volatility())) + +# %% [markdown] +# Bermudan swaption NPV (deal strike calibrated GSR): + +# %% +print(swaption$NPV()) + +# +# We can do more complicated things. +# Let's e.g. modify the nominal schedule to be linear amortizing and see what the effect on the generated calibration basket is: + +# %% +for (i in seq(1,fixedSchedule$size()-1)) { + tmp = 1.0 - i/ (fixedSchedule$size()) + fixedNominal[i] = tmp + floatingNominal[i*2-1] = tmp + floatingNominal[i*2] = tmp +} + +# %% +underlying2 = NonstandardSwap(Swap_Payer_get(), + fixedNominal, floatingNominal, fixedSchedule, strike, + Thirty360(Thirty360_BondBasis_get()), floatSchedule, + euribor6m, gearing, spread, Actual360(), F, F, "ModifiedFollowing") + +# %% +swaption2 = NonstandardSwaption(underlying2, exercise, Settlement_Physical_get()) + +# %% +swaption2$setPricingEngine(nonstandardSwaptionEngine) +basket = swaption2$calibrationBasket(swapBase, swaptionVol, 'MaturityStrikeByDeltaGamma') + +# %% +print(basket_data(basket)) + +# +# The notional is weighted over the underlying exercised into and the maturity is adjusted downwards. The rate, on the other hand, is not affected. + +# +# You can also price exotic bond's features. +# If you have e.g. a Bermudan callable fixed bond you can set up the call right as a swaption +# to enter into a one leg swap with notional reimbursement at maturity. +# The exercise should then be written as a rebated exercise paying the notional in case of exercise. The calibration basket looks like this: + +# %% +fixedNominal2 = rep(1, fixedSchedule$size()-1) +floatingNominal2 = rep(0, fixedSchedule$size()*2-2) #null the second leg + +# %% +underlying3 = NonstandardSwap(Swap_Receiver_get(), + fixedNominal2, floatingNominal2, fixedSchedule, strike, + Thirty360(Thirty360_BondBasis_get()), floatSchedule, + euribor6m, gearing, spread, Actual360(), F, T, "ModifiedFollowing") + +# %% +rebateAmount = rep(-1, exerciseDates$size()) +exercise2 = RebatedExercise(exercise, rebateAmount, 2, calendar) +swaption3 = NonstandardSwaption(underlying3, exercise2, Settlement_Physical_get()) + +# %% +oas0 = SimpleQuote(0) +oas100 = SimpleQuote(0.01) +oas = RelinkableQuoteHandle(oas0) + +# %% +nonstandardSwaptionEngine2 = Gaussian1dNonstandardSwaptionEngine( + gsr, 64, 7.0, T, F, oas, t0_curve) # Change discounting to 6m + +# %% +swaption3$setPricingEngine(nonstandardSwaptionEngine2) +basket = swaption3$calibrationBasket(swapBase, swaptionVol, 'MaturityStrikeByDeltaGamma') + +# %% +print(basket_data(basket)) + +# %% [markdown] +# Note that nominals are not exactly 1.0 here. This is because we do our bond discounting on 6m level while the swaptions are still discounted on OIS level. (You can try this by changing the OIS level to the 6m level, which will produce nominals near 1.0). +# +# The NPV of the call right is (after recalibrating the model): + +# %% +for (i in seq(1, basket$size())) { + basket_i = basket[i][[1]] + BlackCalibrationHelper_setPricingEngine(basket_i, swaptionEngine) +} + +# %% +gsr$calibrateVolatilitiesIterative(basket, method, ec) + +# %% +print(swaption3$NPV()) + +# %% [markdown] +# Up to now, no credit spread is included in the pricing. We can do so by specifying an oas in the pricing engine. Let's set the spread level to 100bp and regenerate the calibration basket. + +# %% +oas$linkTo(oas100) +basket = swaption3$calibrationBasket(swapBase, swaptionVol, 'MaturityStrikeByDeltaGamma') +print(basket_data(basket)) + +# %% [markdown] +# The adjusted basket takes the credit spread into account. This is consistent to a hedge where you would have a margin on the float leg around 100bp,too. + +# %% +for (i in seq(1, basket$size())) { + basket_i = basket[i][[1]] + BlackCalibrationHelper_setPricingEngine(basket_i, swaptionEngine) +} + +# %% +gsr$calibrateVolatilitiesIterative(basket, method, ec) + +# %% +print(swaption3$NPV()) + diff --git a/R/demo/global-bootstrap.R b/R/demo/global-bootstrap.R new file mode 100644 index 0000000000..3abe69abe5 --- /dev/null +++ b/R/demo/global-bootstrap.R @@ -0,0 +1,152 @@ + +# inspired by python example with the same name + +suppressMessages(library(QuantLib)) + +## global data +calendar <- TARGET() + +todaysDate <- DateParser_parseISO("2019-09-26") +invisible(Settings_instance()$setEvaluationDate(d=todaysDate)) + +settlementDays <- 2 +settlementDate <- Calendar_advance(calendar, todaysDate, settlementDays, "Days") + +cat('Today : ', Date_ISO(todaysDate), "\n") +cat('Settlement Date: ', Date_ISO(settlementDate), "\n") + +spot = settlementDate + +# %% [markdown] +# ### Data +# +# We'll use the following data as input: + +# %% +refMktRates = c( + -0.373, + -0.388, + -0.402, + -0.418, + -0.431, + -0.441, + -0.45, + -0.457, + -0.463, + -0.469, + -0.461, + -0.463, + -0.479, + -0.4511, + -0.45418, + -0.439, + -0.4124, + -0.37703, + -0.3335, + -0.28168, + -0.22725, + -0.1745, + -0.12425, + -0.07746, + 0.0385, + 0.1435, + 0.17525, + 0.17275, + 0.1515, + 0.1225, + 0.095, + 0.0644 +) + +# ### Market instruments + +# %% +index = Euribor6M() + +# The first market rate is for the 6-months deposit... + +# %% +helpers = RateHelperVector() + +helpers$append( + DepositRateHelper( + refMktRates[1] / 100.0, Period(6, "Months"), 2, calendar, "ModifiedFollowing", T, Actual360() + ) +) + +# ...the next 12 are for FRAs... + +# %% +for (i in seq(2, 13)) { + helpers$append( + FraRateHelper(refMktRates[i] / 100.0, i + 1, index) + ) +} + +# ...and the others are swap rates. + +# %% +swapTenors = c(2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 15, 20, 25, 30, 35, 40, 45, 50) + +for (i in seq(14, length(refMktRates))) { + r = refMktRates[i] + tenor = swapTenors[i-13] + helpers$append( + SwapRateHelper( + r / 100.0, Period(tenor, "Years"), calendar, "Annual", "ModifiedFollowing", Thirty360(Thirty360_BondBasis_get()), index + ) + ) +} + +# We'll also add a few synthetic helpers: + +# %% +additional_helpers = RateHelperVector() +for (i in seq(0, 6)){ + additional_helpers$append( + FraRateHelper(-0.004, 12 + i, index) + ) +} + +# %% +additional_dates = DateVector() +for (i in seq(0, 4)){ + additional_dates$append( + Calendar_advance(calendar, spot, Period(i+1, "Months")) + ) +} + + + +# ### Global bootstrap +# +# This curve takes into account the market instruments, as well as the passed additional ones. + +# %% +curve = GlobalLinearSimpleZeroCurve( + spot, helpers, Actual365Fixed(), GlobalBootstrap(additional_helpers, additional_dates, 1.0e-4) +) +curve$enableExtrapolation() + + +# ### Report +df = NULL +for (i in seq(1, helpers$size())) { + h = helpers[i][[1]] + pillar = RateHelper_pillarDate(h) + + day_counter = Actual360() + compounding = "Simple" + if (i > 13) { + day_counter = Thirty360(Thirty360_BondBasis_get()) + compounding = "SimpleThenCompounded" + } + + r = YieldTermStructure_zeroRate(curve, pillar, day_counter, compounding, "Annual") + + df = rbind(df, data.frame( + pillar=Date_ISO(pillar), zeroRate=InterestRate_rate(r)*100.0 + )) +} +print(df) + diff --git a/R/demo/swap.R b/R/demo/swap.R new file mode 100644 index 0000000000..7e6e300a2f --- /dev/null +++ b/R/demo/swap.R @@ -0,0 +1,340 @@ + +# inspired by python example with the same name + +suppressMessages(library(QuantLib)) + +## global data +calendar <- TARGET() + +todaysDate <- DateParser_parseISO("2001-09-06") +invisible(Settings_instance()$setEvaluationDate(d=todaysDate)) + +settlementDays = 2 +settlementDate = Calendar_advance(calendar, todaysDate, settlementDays, "Days") + +cat('Today : ', Date_ISO(todaysDate), "\n") +cat('Settlement Date: ', Date_ISO(settlementDate), "\n") + + +# ### Market quotes + +# %% +deposits = list( + "3M" = 0.0363 +) + +# %% +FRAs = list( + "3x6" = 0.037125, + "6x9" = 0.037125, + "9x12" =0.037125 +) + + +# %% +futures = list( + "2001-12-19" = 96.2875, + "2002-03-20" = 96.7875, + "2002-06-19" = 96.9875, + "2002-09-18" = 96.6875, + "2002-12-18" = 96.4875, + "2003-03-19" = 96.3875, + "2003-06-18" = 96.2875, + "2003-09-17" = 96.0875 +) + +# %% +swaps = list( +# "2Y" = 0.037125, + "3Y" = 0.0398, + "5Y" = 0.0443, + "10Y" = 0.05165, + "15Y" = 0.055175 + +) + +allHelpers_DepoFutSwap = RateHelperVector() +allHelpers_DepoFraSwap = RateHelperVector() + +# Rate Helpers - Depos + +for (it in names(deposits)) { + itVal = deposits[[it]] + + dayCounter = Actual360() + + dh = DepositRateHelper( + QuoteHandle(SimpleQuote(itVal)), + PeriodParser_parse(it), + settlementDays, + calendar, + "ModifiedFollowing", + F, + dayCounter + ) + + RateHelperVector_append(allHelpers_DepoFutSwap, dh) + RateHelperVector_append(allHelpers_DepoFraSwap, dh) +} + +# Rate Helpers - FRAs + +for (it in names(FRAs)) { + itVal = FRAs[[it]] + + itSplit = strsplit(x = it, split = "x", fixed = T)[[1]] + n = as.numeric(itSplit[1]) + m = as.numeric(itSplit[2]) + + dayCounter = Actual360() + months = 3 + + dh = FraRateHelper( + QuoteHandle(SimpleQuote(itVal)), n, m, settlementDays, calendar, "ModifiedFollowing", F, dayCounter + ) + + RateHelperVector_append(allHelpers_DepoFraSwap, dh) +} + + +# Rate Helpers - Futures + +for (it in names(futures)) { + itVal = futures[[it]] + + dayCounter = Actual360() + months = 3 + + dh = FuturesRateHelper( + QuoteHandle(SimpleQuote(itVal)), + DateParser_parseISO(it), + months, + calendar, + "ModifiedFollowing", + T, + dayCounter, + QuoteHandle(SimpleQuote(0.0)) + ) + + RateHelperVector_append(allHelpers_DepoFutSwap, dh) +} + + +# +# The discount curve for the swaps will come from elsewhere. +# -> A real application would use some kind of risk-free curve; here we're using a flat one for convenience. +# + +# %% +discountTermStructure = YieldTermStructureHandle(FlatForward(settlementDate, 0.04, Actual360())) + +# %% + +fixedLegFrequency = "Annual" +fixedLegTenor = PeriodParser_parse("1Y") +fixedLegAdjustment = "Unadjusted" +fixedLegDayCounter = Thirty360(Thirty360_BondBasis_get()) +floatingLegFrequency = "Quarterly" +floatingLegTenor = PeriodParser_parse("3M") +floatingLegAdjustment = "ModifiedFollowing" + +for (it in names(swaps)) { + itVal = swaps[[it]] + + dh = SwapRateHelper( + QuoteHandle(SimpleQuote(itVal)), + PeriodParser_parse(it), + calendar, + fixedLegFrequency, + fixedLegAdjustment, + fixedLegDayCounter, + Euribor3M(), + QuoteHandle(), + PeriodParser_parse("0D"), + discountTermStructure + ) + + RateHelperVector_append(allHelpers_DepoFutSwap, dh) + RateHelperVector_append(allHelpers_DepoFraSwap, dh) +} + +# ### Term structure construction + +printCurve <- function(curve, helpers) { + df = NULL + for (i in seq(1, helpers$size())) { + h = helpers[i][[1]] + pillar = RateHelper_pillarDate(h) + + day_counter = Actual365Fixed() + compounding = "Continuous" + + r = YieldTermStructure_zeroRate(curve, pillar, day_counter, compounding, "Annual") + disc = YieldTermStructure_discount(curve, pillar) + + df = rbind(df, data.frame( + pillar=Date_ISO(pillar), zeroRate=InterestRate_rate(r)*100.0, df=disc, impliedRate=RateHelper_impliedQuote(h) + )) + } + print(df) +} + + +# %% +forecastTermStructure = RelinkableYieldTermStructureHandle() + +# %% +depoFuturesSwapCurve = PiecewiseFlatForward(settlementDate, allHelpers_DepoFutSwap, Actual360()) +printCurve(depoFuturesSwapCurve, allHelpers_DepoFutSwap) + + +# %% +depoFraSwapCurve = PiecewiseFlatForward(settlementDate, allHelpers_DepoFraSwap, Actual360()) +printCurve(depoFraSwapCurve, allHelpers_DepoFraSwap) + + + +# ### Swap pricing + +# %% +swapEngine = DiscountingSwapEngine(discountTermStructure) + +# %% +nominal = 1000000 +length = 5 +maturity = Calendar_advance(calendar, settlementDate, length, "Years") +payFixed = T + +# %% +fixedLegFrequency = "Annual" +fixedLegAdjustment = "Unadjusted" +fixedLegDayCounter = Thirty360(Thirty360_BondBasis_get()) +fixedRate = swaps[["5Y"]] # 0.04 + +# %% +floatingLegFrequency = "Quarterly" +spread = 0.0 +fixingDays = 2 +index = Euribor3M(forecastTermStructure) +floatingLegAdjustment = "ModifiedFollowing" +floatingLegDayCounter = index$dayCounter() + +# %% +fixedSchedule = Schedule( + settlementDate, + maturity, + fixedLegTenor, + calendar, + fixedLegAdjustment, + fixedLegAdjustment, + DateGeneration_Forward_get(), + F +) +floatingSchedule = Schedule( + settlementDate, + maturity, + floatingLegTenor, + calendar, + floatingLegAdjustment, + floatingLegAdjustment, + DateGeneration_Forward_get(), + F +) + +# We'll build a 5-years swap starting spot... + +# %% +spot = VanillaSwap( + Swap_Payer_get(), + nominal, + fixedSchedule, + fixedRate, + fixedLegDayCounter, + floatingSchedule, + index, + spread, + floatingLegDayCounter +) +spot$setPricingEngine(swapEngine) + + + +# ...and one starting 1 year forward. + +# %% +forwardStart = Calendar_advance(calendar, settlementDate, 1, "Years") +forwardEnd = Calendar_advance(calendar, forwardStart, length, "Years") +fixedSchedule = Schedule( + forwardStart, + forwardEnd, + fixedLegTenor, + calendar, + fixedLegAdjustment, + fixedLegAdjustment, + DateGeneration_Forward_get(), + F +) +floatingSchedule = Schedule( + forwardStart, + forwardEnd, + floatingLegTenor, + calendar, + floatingLegAdjustment, + floatingLegAdjustment, + DateGeneration_Forward_get(), + F +) + +# %% +forward = VanillaSwap( + Swap_Payer_get(), + nominal, + fixedSchedule, + fixedRate, + fixedLegDayCounter, + floatingSchedule, + index, + spread, + floatingLegDayCounter +) +forward$setPricingEngine(swapEngine) + + +# We'll price them both on the bootstrapped curves. +# +# This is the quoted 5-years market rate; we expect the fair rate of the spot swap to match it. + +# %% +print(swaps["5Y"]) + +showSwap <- function(swap) { + print(paste("NPV = ", swap$NPV())) + print(paste("Fair spread = ", (swap$fairSpread()*100))) + print(paste("Fair rate = ", (swap$fairRate()*100))) +} + + +# %% [markdown] +# These are the results for the 5-years spot swap on the deposit/futures/swap curve... + +# %% +forecastTermStructure$linkTo(depoFuturesSwapCurve) +showSwap(spot) + +# ...and these are on the deposit/fra/swap curve. + +# %% +forecastTermStructure$linkTo(depoFraSwapCurve) +showSwap(spot) + +# %% [markdown] +# The same goes for the 1-year forward swap, except for the fair rate not matching the spot rate. + +# %% +forecastTermStructure$linkTo(depoFuturesSwapCurve) +showSwap(forward) + +# %% +forecastTermStructure$linkTo(depoFraSwapCurve) +showSwap(forward) + diff --git a/README.md b/README.md index 82061370c9..484820c818 100644 --- a/README.md +++ b/README.md @@ -7,14 +7,14 @@ QuantLib-SWIG: language bindings for QuantLib ![PRs Welcome](https://img.shields.io/badge/PRs%20-welcome-brightgreen.svg) [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.1441003.svg)](https://doi.org/10.5281/zenodo.1441003) [![Build status](https://github.com/lballabio/QuantLib-SWIG/workflows/Linux%20build/badge.svg?branch=master)](https://github.com/lballabio/QuantLib-SWIG/actions?query=workflow%3A%22Linux+build%22) -[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/lballabio/QuantLib-SWIG/binder?urlpath=tree/Python/examples) +[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/lballabio/QuantLib-SWIG/binder?urlpath=lab/tree/Python/examples) --- QuantLib-SWIG provides the means to use QuantLib from a number of -languages including Python, Ruby, Perl, C# and Java. +languages; currently their list includes Python, C#, Java and R. -The QuantLib project () is aimed at providing a +The QuantLib project () is aimed at providing a comprehensive software framework for quantitative finance. QuantLib is a free/open-source library for modeling, trading, and risk management in real-life. @@ -26,7 +26,7 @@ Software. Download and usage ------------------ -QuantLib-SWIG can be downloaded from . +QuantLib-SWIG can be downloaded from . On Linux/Unix, you can run: @@ -56,7 +56,7 @@ patch available, you can open a pull request instead (see You can also use the `quantlib-users` and `quantlib-dev` mailing lists for feedback, questions, etc. More information and instructions for -subscribing are at . +subscribing are at . Contributing diff --git a/SWIG/asianoptions.i b/SWIG/asianoptions.i index 320b9b8f95..fe6e5d6c21 100644 --- a/SWIG/asianoptions.i +++ b/SWIG/asianoptions.i @@ -2,7 +2,7 @@ Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 StatPro Italia srl Copyright (C) 2010, 2012, 2018, 2019 Klaus Spanderen Copyright (C) 2018, 2019 Matthias Lungwitz - Copyright (C) 2020 Jack Gillett + Copyright (C) 2020, 2022 Jack Gillett This file is part of QuantLib, a free-software/open-source library for financial quantitative analysts and developers - http://quantlib.org/ @@ -22,6 +22,7 @@ #define quantlib_asian_options_i %include options.i +%include grid.i %{ using QuantLib::Average; @@ -454,5 +455,15 @@ class FdBlackScholesAsianEngine : public PricingEngine { Size tGrid, Size xGrid, Size aGrid); }; +%{ +using QuantLib::TurnbullWakemanAsianEngine; +%} + +%shared_ptr(TurnbullWakemanAsianEngine) +class TurnbullWakemanAsianEngine : public PricingEngine { + public: + TurnbullWakemanAsianEngine(const ext::shared_ptr& process); +}; + #endif diff --git a/SWIG/barrieroptions.i b/SWIG/barrieroptions.i index 669c75c6b6..4bacfb8d48 100644 --- a/SWIG/barrieroptions.i +++ b/SWIG/barrieroptions.i @@ -2,6 +2,7 @@ Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 StatPro Italia srl Copyright (C) 2015 Thema Consulting SA Copyright (C) 2018, 2019 Matthias Lungwitz + Copyright (C) 2022 Ignacio Anguita This file is part of QuantLib, a free-software/open-source library for financial quantitative analysts and developers - http://quantlib.org/ @@ -21,6 +22,7 @@ #define quantlib_barrier_options_i %include options.i +%include dividends.i %{ using QuantLib::Barrier; @@ -33,6 +35,7 @@ struct Barrier { %{ using QuantLib::BarrierOption; using QuantLib::DividendBarrierOption; +using QuantLib::QuantoBarrierOption; %} %shared_ptr(BarrierOption) @@ -51,8 +54,67 @@ class BarrierOption : public OneAssetOption { Size maxEvaluations = 100, Volatility minVol = 1.0e-4, Volatility maxVol = 4.0); + Volatility impliedVolatility( + Real targetValue, + const ext::shared_ptr& process, + const DividendSchedule& dividends, + Real accuracy = 1.0e-4, + Size maxEvaluations = 100, + Volatility minVol = 1.0e-4, + Volatility maxVol = 4.0); +}; + +%shared_ptr(QuantoBarrierOption) +class QuantoBarrierOption : public BarrierOption { + public: + QuantoBarrierOption( + Barrier::Type barrierType, + Real barrier, + Real rebate, + const ext::shared_ptr& payoff, + const ext::shared_ptr& exercise); }; +%{ +using QuantLib::PartialBarrier; +%} + +struct PartialBarrier : public Barrier { + enum Range { Start, End, EndB1, EndB2 }; +}; + +%{ +using QuantLib::PartialTimeBarrierOption; +%} + +%shared_ptr(PartialTimeBarrierOption) +class PartialTimeBarrierOption : public OneAssetOption { + public: + PartialTimeBarrierOption(PartialBarrier::Type barrierType, + PartialBarrier::Range barrierRange, + Real barrier, + Real rebate, + Date coverEventDate, + const ext::shared_ptr& payoff, + const ext::shared_ptr& exercise); +}; + +%{ +using QuantLib::AnalyticPartialTimeBarrierOptionEngine; +%} + +#if defined(SWIGPYTHON) +%feature("docstring") AnalyticPartialTimeBarrierOptionEngine "Partial Time Barrier Option Engine" +#endif +%shared_ptr(AnalyticPartialTimeBarrierOptionEngine ) +class AnalyticPartialTimeBarrierOptionEngine : public PricingEngine { + public: + AnalyticPartialTimeBarrierOptionEngine ( + const ext::shared_ptr& process + ); +}; + + %shared_ptr(DividendBarrierOption) class DividendBarrierOption : public BarrierOption { public: @@ -148,6 +210,20 @@ class MCBarrierEngine : public PricingEngine { %} #endif +%{ +using QuantLib::QuantoEngine; +typedef QuantoEngine QuantoBarrierEngine; +%} + +%shared_ptr(QuantoBarrierEngine); +class QuantoBarrierEngine : public PricingEngine { + public: + QuantoBarrierEngine(ext::shared_ptr, + Handle foreignRiskFreeRate, + Handle exchangeRateVolatility, + Handle correlation); + }; + %{ using QuantLib::FdBlackScholesBarrierEngine; using QuantLib::FdBlackScholesRebateEngine; @@ -163,6 +239,12 @@ class FdBlackScholesBarrierEngine : public PricingEngine { const FdmSchemeDesc& schemeDesc = FdmSchemeDesc::Douglas(), bool localVol = false, Real illegalLocalVolOverwrite = -Null()); + FdBlackScholesBarrierEngine(const ext::shared_ptr& process, + DividendSchedule dividends, + Size tGrid = 100, Size xGrid = 100, Size dampingSteps = 0, + const FdmSchemeDesc& schemeDesc = FdmSchemeDesc::Douglas(), + bool localVol = false, + Real illegalLocalVolOverwrite = -Null()); }; %shared_ptr(FdBlackScholesRebateEngine) @@ -173,6 +255,12 @@ class FdBlackScholesRebateEngine : public PricingEngine { const FdmSchemeDesc& schemeDesc = FdmSchemeDesc::Douglas(), bool localVol = false, Real illegalLocalVolOverwrite = -Null()); + FdBlackScholesRebateEngine(const ext::shared_ptr& process, + DividendSchedule dividends, + Size tGrid = 100, Size xGrid = 100, Size dampingSteps = 0, + const FdmSchemeDesc& schemeDesc = FdmSchemeDesc::Douglas(), + bool localVol = false, + Real illegalLocalVolOverwrite = -Null()); }; %shared_ptr(FdHestonBarrierEngine) @@ -181,8 +269,13 @@ class FdHestonBarrierEngine : public PricingEngine { FdHestonBarrierEngine(const ext::shared_ptr& model, Size tGrid = 100, Size xGrid = 100, Size vGrid = 50, Size dampingSteps = 0, const FdmSchemeDesc& schemeDesc = FdmSchemeDesc::Hundsdorfer(), - const ext::shared_ptr& leverageFct - = ext::shared_ptr(), + const ext::shared_ptr& leverageFct = {}, + const Real mixingFactor = 1.0); + FdHestonBarrierEngine(const ext::shared_ptr& model, + DividendSchedule dividends, + Size tGrid = 100, Size xGrid = 100, Size vGrid = 50, Size dampingSteps = 0, + const FdmSchemeDesc& schemeDesc = FdmSchemeDesc::Hundsdorfer(), + const ext::shared_ptr& leverageFct = {}, const Real mixingFactor = 1.0); }; @@ -192,8 +285,13 @@ class FdHestonRebateEngine : public PricingEngine { FdHestonRebateEngine(const ext::shared_ptr& model, Size tGrid = 100, Size xGrid = 100, Size vGrid = 50, Size dampingSteps = 0, const FdmSchemeDesc& schemeDesc = FdmSchemeDesc::Hundsdorfer(), - const ext::shared_ptr& leverageFct - = ext::shared_ptr(), + const ext::shared_ptr& leverageFct = {}, + const Real mixingFactor = 1.0); + FdHestonRebateEngine(const ext::shared_ptr& model, + DividendSchedule dividends, + Size tGrid = 100, Size xGrid = 100, Size vGrid = 50, Size dampingSteps = 0, + const FdmSchemeDesc& schemeDesc = FdmSchemeDesc::Hundsdorfer(), + const ext::shared_ptr& leverageFct = {}, const Real mixingFactor = 1.0); }; @@ -393,8 +491,6 @@ class SuoWangDoubleBarrierEngine : public PricingEngine { int series = 5); }; -deprecate_feature(WulinYongDoubleBarrierEngine, SuoWangDoubleBarrierEngine); - %{ using QuantLib::VannaVolgaDoubleBarrierEngine; diff --git a/SWIG/basketoptions.i b/SWIG/basketoptions.i index 7b10f0339c..a7e7bfa99d 100644 --- a/SWIG/basketoptions.i +++ b/SWIG/basketoptions.i @@ -172,7 +172,7 @@ class MCAmericanBasketEngine : public PricingEngine { BigInteger seed = 0, Size nCalibrationSamples = Null(), Size polynomOrder = 2, - LsmBasisSystem::PolynomType polynomType + LsmBasisSystem::PolynomialType polynomType = LsmBasisSystem::Monomial) { return new MCAmericanBasketEngine(process, timeSteps, diff --git a/SWIG/bondfunctions.i b/SWIG/bondfunctions.i index 23efa337a4..e38f558eeb 100644 --- a/SWIG/bondfunctions.i +++ b/SWIG/bondfunctions.i @@ -69,13 +69,20 @@ class BondFunctions { static Real cleanPrice(const Bond& bond, const YieldTermStructure& discountCurve, Date settlementDate = Date()); + static Real dirtyPrice(const Bond& bond, + const YieldTermStructure& discountCurve, + Date settlementDate = Date()); static Real bps(const Bond& bond, const YieldTermStructure& discountCurve, Date settlementDate = Date()); static Rate atmRate(const Bond& bond, const YieldTermStructure& discountCurve, Date settlementDate = Date(), - Real cleanPrice = Null()); + BondPrice price = {}); + static Rate atmRate(const Bond& bond, + const YieldTermStructure& discountCurve, + Date settlementDate, + Real cleanPrice); static Real cleanPrice(const Bond& bond, const InterestRate& yield, @@ -95,6 +102,15 @@ class BondFunctions { Compounding compounding, Frequency frequency, Date settlementDate = Date()); + static Rate yield(const Bond& bond, + BondPrice price, + const DayCounter& dayCounter, + Compounding compounding, + Frequency frequency, + Date settlementDate = Date(), + Real accuracy = 1.0e-10, + Size maxIterations = 100, + Rate guess = 0.05); static Rate yield(const Bond& bond, Real cleanPrice, const DayCounter& dayCounter, @@ -143,6 +159,31 @@ class BondFunctions { Compounding compounding, Frequency frequency, Date settlementDate = Date()); + + static Real cleanPrice(const Bond& bond, + const ext::shared_ptr& discount, + Spread zSpread, + const DayCounter& dayCounter, + Compounding compounding, + Frequency frequency, + Date settlementDate = Date()); + static Real dirtyPrice(const Bond& bond, + const ext::shared_ptr& discount, + Spread zSpread, + const DayCounter& dayCounter, + Compounding compounding, + Frequency frequency, + Date settlementDate = Date()); + static Spread zSpread(const Bond& bond, + BondPrice price, + const ext::shared_ptr& discountCurve, + const DayCounter& dayCounter, + Compounding compounding, + Frequency frequency, + Date settlementDate = Date(), + Real accuracy = 1.0e-10, + Size maxIterations = 100, + Rate guess = 0.0); static Spread zSpread(const Bond& bond, Real cleanPrice, const ext::shared_ptr& discountCurve, @@ -157,6 +198,26 @@ class BondFunctions { %extend { %define DefineYieldFunctionSolver(SolverType) + static Rate yield ## SolverType(SolverType solver, + const Bond& bond, + BondPrice price, + const DayCounter& dayCounter, + Compounding compounding, + Frequency frequency, + Date settlementDate = Date(), + Real accuracy = 1.0e-10, + Rate guess = 0.05) { + return QuantLib::BondFunctions::yield( + solver, + bond, + price, + dayCounter, + compounding, + frequency, + settlementDate, + accuracy, + guess); + } static Rate yield ## SolverType(SolverType solver, const Bond& bond, Real cleanPrice, diff --git a/SWIG/bonds.i b/SWIG/bonds.i index e89c3231db..0cdf0514a8 100644 --- a/SWIG/bonds.i +++ b/SWIG/bonds.i @@ -43,6 +43,7 @@ using QuantLib::AmortizingFixedRateBond; using QuantLib::FloatingRateBond; using QuantLib::AmortizingFloatingRateBond; using QuantLib::DiscountingBondEngine; +using QuantLib::simplifyNotificationGraph; %} class BondPrice { @@ -51,6 +52,7 @@ class BondPrice { BondPrice(Real amount, Type type); Real amount() const; Type type() const; + bool isValid() const; }; %shared_ptr(Bond) @@ -102,6 +104,14 @@ class Bond : public Instrument { Frequency freq, Real accuracy = 1.0e-8, Size maxEvaluations = 100); + Real yield(BondPrice price, + const DayCounter& dc, + Compounding compounding, + Frequency freq, + const Date& settlement = Date(), + Real accuracy = 1.0e-8, + Size maxEvaluations = 100, + Real guess = 0.05); Real yield(Real cleanPrice, const DayCounter& dc, Compounding compounding, @@ -114,6 +124,8 @@ class Bond : public Instrument { Real settlementValue(Real cleanPrice) const; }; +void simplifyNotificationGraph(Bond& bond, bool unregisterCoupons = false); + %inline %{ Real cleanPriceFromZSpread( @@ -133,7 +145,19 @@ class Bond : public Instrument { %} +namespace QuantLib { + + Schedule sinkingSchedule(const Date& startDate, + const Period& bondLength, + const Frequency& frequency, + const Calendar& paymentCalendar); + std::vector sinkingNotionals(const Period& bondLength, + const Frequency& frequency, + Rate couponRate, + Real initialNotional); + +} %shared_ptr(ZeroCouponBond) class ZeroCouponBond : public Bond { @@ -154,9 +178,7 @@ class ZeroCouponBond : public Bond { %shared_ptr(FixedRateBond) class FixedRateBond : public Bond { #if !defined(SWIGJAVA) && !defined(SWIGCSHARP) - %feature("kwargs") from_rates; - %feature("kwargs") from_interest_rates; - %feature("kwargs") from_date_info; + %feature("kwargs") FixedRateBond; #endif public: FixedRateBond( @@ -173,118 +195,6 @@ class FixedRateBond : public Bond { const Calendar& exCouponCalendar = Calendar(), BusinessDayConvention exCouponConvention = Unadjusted, bool exCouponEndOfMonth = false); - //! generic compounding and frequency InterestRate coupons - FixedRateBond( - Integer settlementDays, - Real faceAmount, - const Schedule& schedule, - const std::vector& coupons, - BusinessDayConvention paymentConvention = Following, - Real redemption = 100.0, - const Date& issueDate = Date(), - const Calendar& paymentCalendar = Calendar(), - const Period& exCouponPeriod = Period(), - const Calendar& exCouponCalendar = Calendar(), - BusinessDayConvention exCouponConvention = Unadjusted, - bool exCouponEndOfMonth = false); - //! simple annual compounding coupon rates with internal schedule calculation - FixedRateBond( - Integer settlementDays, - const Calendar& couponCalendar, - Real faceAmount, - const Date& startDate, - const Date& maturityDate, - const Period& tenor, - const std::vector& coupons, - const DayCounter& accrualDayCounter, - BusinessDayConvention accrualConvention = QuantLib::Following, - BusinessDayConvention paymentConvention = QuantLib::Following, - Real redemption = 100.0, - const Date& issueDate = Date(), - const Date& stubDate = Date(), - DateGeneration::Rule rule = QuantLib::DateGeneration::Backward, - bool endOfMonth = false, - const Calendar& paymentCalendar = Calendar(), - const Period& exCouponPeriod = Period(), - const Calendar& exCouponCalendar = Calendar(), - const BusinessDayConvention exCouponConvention = Unadjusted, - bool exCouponEndOfMonth = false); - %extend { - //! convenience wrapper around constructor taking rates - static ext::shared_ptr from_rates( - Integer settlementDays, - Real faceAmount, - const Schedule &schedule, - const std::vector& coupons, - const DayCounter& paymentDayCounter, - BusinessDayConvention paymentConvention = QuantLib::Following, - Real redemption = 100.0, - Date issueDate = Date(), - const Calendar& paymentCalendar = Calendar(), - const Period& exCouponPeriod = Period(), - const Calendar& exCouponCalendar = Calendar(), - BusinessDayConvention exCouponConvention = Unadjusted, - bool exCouponEndOfMonth = false) { - return ext::shared_ptr( - new FixedRateBond(settlementDays, faceAmount, schedule, coupons, - paymentDayCounter, paymentConvention, - redemption, issueDate, paymentCalendar, - exCouponPeriod, exCouponCalendar, - exCouponConvention, exCouponEndOfMonth)); - } - //! convenience wrapper around constructor taking interest rates - static ext::shared_ptr from_interest_rates( - Integer settlementDays, - Real faceAmount, - const Schedule& schedule, - const std::vector& coupons, - BusinessDayConvention paymentConvention = Following, - Real redemption = 100.0, - const Date& issueDate = Date(), - const Calendar& paymentCalendar = Calendar(), - const Period& exCouponPeriod = Period(), - const Calendar& exCouponCalendar = Calendar(), - BusinessDayConvention exCouponConvention = Unadjusted, - bool exCouponEndOfMonth = false) { - return ext::shared_ptr( - new FixedRateBond(settlementDays, faceAmount, schedule, coupons, - paymentConvention, redemption, - issueDate, paymentCalendar, - exCouponPeriod, exCouponCalendar, - exCouponConvention, exCouponEndOfMonth)); - } - //! convenience wrapper around constructor doing internal schedule calculation - static ext::shared_ptr from_date_info( - Integer settlementDays, - const Calendar& couponCalendar, - Real faceAmount, - const Date& startDate, - const Date& maturityDate, - const Period& tenor, - const std::vector& coupons, - const DayCounter& accrualDayCounter, - BusinessDayConvention accrualConvention = QuantLib::Following, - BusinessDayConvention paymentConvention = QuantLib::Following, - Real redemption = 100.0, - const Date& issueDate = Date(), - const Date& stubDate = Date(), - DateGeneration::Rule rule = QuantLib::DateGeneration::Backward, - bool endOfMonth = false, - const Calendar& paymentCalendar = Calendar(), - const Period& exCouponPeriod = Period(), - const Calendar& exCouponCalendar = Calendar(), - const BusinessDayConvention exCouponConvention = Unadjusted, - bool exCouponEndOfMonth = false) { - return ext::shared_ptr( - new FixedRateBond(settlementDays, couponCalendar, faceAmount, - startDate, maturityDate, tenor, - coupons, accrualDayCounter, accrualConvention, - paymentConvention, redemption, issueDate, - stubDate, rule, endOfMonth, paymentCalendar, - exCouponPeriod, exCouponCalendar, - exCouponConvention, exCouponEndOfMonth)); - } - } Frequency frequency() const; DayCounter dayCounter() const; }; @@ -292,6 +202,9 @@ class FixedRateBond : public Bond { %shared_ptr(AmortizingFixedRateBond) class AmortizingFixedRateBond : public Bond { + #if !defined(SWIGJAVA) && !defined(SWIGCSHARP) + %feature("kwargs") AmortizingFixedRateBond; + #endif public: AmortizingFixedRateBond( Integer settlementDays, @@ -304,30 +217,9 @@ class AmortizingFixedRateBond : public Bond { const Period& exCouponPeriod = Period(), const Calendar& exCouponCalendar = Calendar(), const BusinessDayConvention exCouponConvention = Unadjusted, - bool exCouponEndOfMonth = false); - AmortizingFixedRateBond( - Integer settlementDays, - const Calendar& paymentCalendar, - Real faceAmount, - Date startDate, - const Period& bondTenor, - const Frequency& sinkingFrequency, - Real coupon, - const DayCounter& accrualDayCounter, - BusinessDayConvention paymentConvention = QuantLib::Following, - Date issueDate = Date()); - AmortizingFixedRateBond( - Integer settlementDays, - const std::vector& notionals, - const Schedule& schedule, - const std::vector& coupons, - BusinessDayConvention paymentConvention = QuantLib::Following, - Date issueDate = Date(), - const Calendar& paymentCalendar = Calendar(), - const Period& exCouponPeriod = Period(), - const Calendar& exCouponCalendar = Calendar(), - const BusinessDayConvention exCouponConvention = Unadjusted, - bool exCouponEndOfMonth = false); + bool exCouponEndOfMonth = false, + const std::vector& redemptions = { 100.0 }, + Integer paymentLag = 0); Frequency frequency() const; DayCounter dayCounter() const; }; @@ -356,7 +248,9 @@ class AmortizingFloatingRateBond : public Bond { const Period& exCouponPeriod = Period(), const Calendar& exCouponCalendar = Calendar(), const BusinessDayConvention exCouponConvention = Unadjusted, - bool exCouponEndOfMonth = false); + bool exCouponEndOfMonth = false, + const std::vector& redemptions = { 100.0 }, + Integer paymentLag = 0); }; @@ -390,6 +284,7 @@ class FloatingRateBond : public Bond { %{ using QuantLib::CmsRateBond; +using QuantLib::AmortizingCmsRateBond; %} %shared_ptr(CmsRateBond) @@ -415,6 +310,29 @@ class CmsRateBond : public Bond { }; +%shared_ptr(AmortizingCmsRateBond) +class AmortizingCmsRateBond : public Bond { + #if !defined(SWIGJAVA) && !defined(SWIGCSHARP) + %feature("kwargs") AmortizingCmsRateBond; + #endif + public: + AmortizingCmsRateBond( + Natural settlementDays, + const std::vector& notionals, + const Schedule& schedule, + const ext::shared_ptr& index, + const DayCounter& paymentDayCounter, + BusinessDayConvention paymentConvention = Following, + Natural fixingDays = Null(), + const std::vector& gearings = { 1.0 }, + const std::vector& spreads = { 0.0 }, + const std::vector& caps = {}, + const std::vector& floors = {}, + bool inArrears = false, + const Date& issueDate = Date()); +}; + + %shared_ptr(DiscountingBondEngine) class DiscountingBondEngine : public PricingEngine { public: @@ -469,7 +387,7 @@ class CallableBond : public Bond { public: const std::vector >& callability() const; - Volatility impliedVolatility(Real targetValue, + Volatility impliedVolatility(const BondPrice& targetPrice, const Handle& discountCurve, Real accuracy, Size maxEvaluations, diff --git a/SWIG/calendars.i b/SWIG/calendars.i index bd62fa026a..e0c2884686 100644 --- a/SWIG/calendars.i +++ b/SWIG/calendars.i @@ -4,6 +4,7 @@ Copyright (C) 2003, 2004, 2005, 2006, 2007 StatPro Italia srl Copyright (C) 2005 Johan Witters Copyright (C) 2018 Matthias Groncki + Copyright (C) 2023 Skandinaviska Enskilda Banken AB (publ) This file is part of QuantLib, a free-software/open-source library for financial quantitative analysts and developers - http://quantlib.org/ @@ -40,6 +41,7 @@ using QuantLib::Preceding; using QuantLib::ModifiedPreceding; using QuantLib::Unadjusted; using QuantLib::HalfMonthModifiedFollowing; +using QuantLib::Nearest; %} enum BusinessDayConvention { @@ -48,7 +50,8 @@ enum BusinessDayConvention { Preceding, ModifiedPreceding, Unadjusted, - HalfMonthModifiedFollowing + HalfMonthModifiedFollowing, + Nearest }; %{ @@ -60,20 +63,17 @@ using QuantLib::JoinBusinessDays; enum JointCalendarRule { JoinHolidays, JoinBusinessDays }; #if defined(SWIGPYTHON) -%typemap(in) boost::optional %{ - if($input == Py_None) - $1 = boost::none; - else if (PyInt_Check($input)) - $1 = (BusinessDayConvention) PyInt_AsLong($input); - else - $1 = (BusinessDayConvention) PyLong_AsLong($input); +%typemap(in) ext::optional %{ + if ($input == Py_None) + $1 = ext::nullopt; + else if (PyLong_Check($input)) + $1 = (BusinessDayConvention)PyLong_AsLong($input); + else + SWIG_exception(SWIG_TypeError, "int expected"); +%} +%typecheck (QL_TYPECHECK_BUSINESSDAYCONVENTION) ext::optional %{ + $1 = (PyLong_Check($input) || $input == Py_None) ? 1 : 0; %} -%typecheck (QL_TYPECHECK_BUSINESSDAYCONVENTION) boost::optional { -if (PyInt_Check($input) || PyLong_Check($input) || Py_None == $input) - $1 = 1; -else - $1 = 0; -} #endif class Calendar { @@ -87,6 +87,8 @@ class Calendar { bool isEndOfMonth(const Date&); void addHoliday(const Date&); void removeHoliday(const Date&); + void resetAddedAndRemovedHolidays(); + Date adjust(const Date& d, BusinessDayConvention convention = QuantLib::Following); Date advance(const Date& d, Integer n, TimeUnit unit, @@ -105,27 +107,29 @@ class Calendar { std::vector businessDayList(const Date& from, const Date& to); std::string name(); + bool empty(); %extend { std::string __str__() { return self->name()+" calendar"; } #if defined(SWIGPYTHON) || defined(SWIGJAVA) - bool __eq__(const Calendar& other) { + bool operator==(const Calendar& other) { return (*self) == other; } - bool __ne__(const Calendar& other) { + bool operator!=(const Calendar& other) { return (*self) != other; } + hash_t __hash__() { + return self->empty() ? 0 : std::hash()(self->name()); + } #endif } - #if defined(SWIGPYTHON) - %pythoncode %{ - def __hash__(self): - return hash(self.name()) - %} - #endif }; +namespace std { + %template(CalendarVector) vector; +} + namespace QuantLib { class Argentina : public Calendar { @@ -134,7 +138,19 @@ namespace QuantLib { Argentina(Market m = Merval); }; - class Australia : public Calendar {}; + class Australia : public Calendar { + public: + enum Market { Settlement, ASX }; + Australia(Market market = Settlement); + }; + + class Austria : public Calendar { + public: + enum Market { Settlement, Exchange }; + Austria(Market m = Settlement); + }; + + class Botswana : public Calendar {}; class Brazil : public Calendar { public: @@ -231,14 +247,18 @@ namespace QuantLib { class Norway : public Calendar {}; class Poland : public Calendar {}; + class Romania : public Calendar { + public: + enum Market { Public, BVB }; + Romania(Market m = BVB); + }; + class Russia : public Calendar { public: enum Market { Settlement, MOEX }; Russia(Market m = Settlement); }; - class Romania : public Calendar {}; - class SaudiArabia : public Calendar { public: enum Market { Tadawul }; @@ -293,8 +313,8 @@ namespace QuantLib { class UnitedStates : public Calendar { public: enum Market { Settlement, NYSE, GovernmentBond, - NERC, LiborImpact, FederalReserve }; - UnitedStates(Market m = Settlement); + NERC, LiborImpact, FederalReserve, SOFR }; + UnitedStates(Market m); }; // others @@ -312,6 +332,8 @@ namespace QuantLib { JointCalendar(const Calendar&, const Calendar&, const Calendar&, const Calendar&, JointCalendarRule rule = QuantLib::JoinHolidays); + explicit JointCalendar(const std::vector&, + JointCalendarRule = QuantLib::JoinHolidays); }; class BespokeCalendar : public Calendar { diff --git a/SWIG/calibratedmodel.i b/SWIG/calibratedmodel.i new file mode 100644 index 0000000000..206882261a --- /dev/null +++ b/SWIG/calibratedmodel.i @@ -0,0 +1,99 @@ +/* + Copyright (C) 2000, 2001, 2002, 2003 RiskMap srl + Copyright (C) 2003, 2007, 2009 StatPro Italia srl + Copyright (C) 2005 Dominic Thuillier + Copyright (C) 2007 Luis Cota + Copyright (C) 2016 Gouthaman Balaraman + Copyright (C) 2016 Peter Caspers + Copyright (C) 2018 Matthias Lungwitz + + This file is part of QuantLib, a free-software/open-source library + for financial quantitative analysts and developers - http://quantlib.org/ + + QuantLib is free software: you can redistribute it and/or modify it + under the terms of the QuantLib license. You should have received a + copy of the license along with this program; if not, please email + . The license is also available online at + . + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the license for more details. +*/ + +#ifndef quantlib_calibrated_model_i +#define quantlib_calibrated_model_i + +%include termstructures.i +%include optimizers.i +%include linearalgebra.i +%include types.i +%include vectors.i + +%{ +using QuantLib::CalibrationHelper; +%} + +// calibration helpers +%shared_ptr(CalibrationHelper) +class CalibrationHelper { + public: + Real calibrationError(); + private: + CalibrationHelper(); +}; + + +// allow use of vectors of helpers +#if defined(SWIGCSHARP) +SWIG_STD_VECTOR_ENHANCED( ext::shared_ptr ) +#endif +namespace std { + %template(CalibrationHelperVector) vector >; +} + +// the base class for calibrated models +%{ +using QuantLib::CalibratedModel; +using QuantLib::TermStructureConsistentModel; +%} + +%shared_ptr(CalibratedModel) +class CalibratedModel : public virtual Observable { + #if defined(SWIGCSHARP) + %rename("parameters") params; + #endif + public: + Array params() const; + virtual void calibrate( + const std::vector >&, + OptimizationMethod&, const EndCriteria &, + const Constraint& constraint = Constraint(), + const std::vector& weights = std::vector(), + const std::vector& fixParameters = std::vector()); + + void setParams(const Array& params); + Real value(const Array& params, + const std::vector >&); + const ext::shared_ptr& constraint() const; + EndCriteria::Type endCriteria() const; + const Array& problemValues() const; + Integer functionEvaluation() const; + private: + CalibratedModel(); +}; + +%shared_ptr(TermStructureConsistentModel) +class TermStructureConsistentModel : public virtual Observable{ + public: + const Handle& termStructure() const; + private: + TermStructureConsistentModel(); +}; + +%template(CalibratedModelHandle) Handle; +%template(RelinkableCalibratedModelHandle) RelinkableHandle; + + + +#endif diff --git a/SWIG/calibrationhelpers.i b/SWIG/calibrationhelpers.i index 4d6044998a..caab0966aa 100644 --- a/SWIG/calibrationhelpers.i +++ b/SWIG/calibrationhelpers.i @@ -24,14 +24,13 @@ #ifndef quantlib_calibration_helpers_i #define quantlib_calibration_helpers_i +%include calibratedmodel.i %include date.i %include calendars.i %include daycounters.i %include cashflows.i %include marketelements.i %include termstructures.i -%include optimizers.i -%include options.i %include linearalgebra.i %include types.i %include vectors.i @@ -39,22 +38,12 @@ %{ using QuantLib::VanillaSwap; using QuantLib::Swaption; -using QuantLib::CalibrationHelper; using QuantLib::BlackCalibrationHelper; using QuantLib::SwaptionHelper; using QuantLib::CapHelper; using QuantLib::HestonModelHelper; %} -// calibration helpers -%shared_ptr(CalibrationHelper) -class CalibrationHelper { - public: - Real calibrationError(); - private: - CalibrationHelper(); -}; - %shared_ptr(BlackCalibrationHelper) class BlackCalibrationHelper : public CalibrationHelper { public: @@ -94,12 +83,13 @@ class SwaptionHelper : public BlackCalibrationHelper { const DayCounter& fixedLegDayCounter, const DayCounter& floatingLegDayCounter, const Handle& termStructure, - BlackCalibrationHelper::CalibrationErrorType errorType - = BlackCalibrationHelper::RelativePriceError, + CalibrationErrorType errorType = RelativePriceError, const Real strike = Null(), const Real nominal = 1.0, const VolatilityType type = ShiftedLognormal, - const Real shift = 0.0); + const Real shift = 0.0, + Natural settlementDays = Null(), + RateAveraging::Type averagingMethod = RateAveraging::Compound); SwaptionHelper(const Date& exerciseDate, const Period& length, const Handle& volatility, @@ -108,12 +98,13 @@ class SwaptionHelper : public BlackCalibrationHelper { const DayCounter& fixedLegDayCounter, const DayCounter& floatingLegDayCounter, const Handle& termStructure, - BlackCalibrationHelper::CalibrationErrorType errorType - = BlackCalibrationHelper::RelativePriceError, + CalibrationErrorType errorType = RelativePriceError, const Real strike = Null(), const Real nominal = 1.0, const VolatilityType type = ShiftedLognormal, - const Real shift = 0.0); + const Real shift = 0.0, + Natural settlementDays = Null(), + RateAveraging::Type averagingMethod = RateAveraging::Compound); SwaptionHelper(const Date& exerciseDate, const Date& endDate, const Handle& volatility, @@ -122,13 +113,15 @@ class SwaptionHelper : public BlackCalibrationHelper { const DayCounter& fixedLegDayCounter, const DayCounter& floatingLegDayCounter, const Handle& termStructure, - BlackCalibrationHelper::CalibrationErrorType errorType - = BlackCalibrationHelper::RelativePriceError, + CalibrationErrorType errorType = RelativePriceError, const Real strike = Null(), const Real nominal = 1.0, const VolatilityType type = ShiftedLognormal, - const Real shift = 0.0); + const Real shift = 0.0, + Natural settlementDays = Null(), + RateAveraging::Type averagingMethod = RateAveraging::Compound); + ext::shared_ptr underlying() const; ext::shared_ptr underlyingSwap() const; ext::shared_ptr swaption() const; @@ -196,57 +189,11 @@ class HestonModelHelper : public BlackCalibrationHelper { // allow use of vectors of helpers #if defined(SWIGCSHARP) -SWIG_STD_VECTOR_ENHANCED( ext::shared_ptr ) SWIG_STD_VECTOR_ENHANCED( ext::shared_ptr ) #endif namespace std { - %template(CalibrationHelperVector) - vector >; %template(BlackCalibrationHelperVector) vector >; } -// the base class for calibrated models -%{ -using QuantLib::CalibratedModel; -using QuantLib::TermStructureConsistentModel; -%} - -%shared_ptr(CalibratedModel) -class CalibratedModel : public virtual Observable { - #if defined(SWIGCSHARP) - %rename("parameters") params; - #endif - public: - Array params() const; - virtual void calibrate( - const std::vector >&, - OptimizationMethod&, const EndCriteria &, - const Constraint& constraint = Constraint(), - const std::vector& weights = std::vector(), - const std::vector& fixParameters = std::vector()); - - void setParams(const Array& params); - Real value(const Array& params, - const std::vector >&); - const ext::shared_ptr& constraint() const; - EndCriteria::Type endCriteria() const; - const Array& problemValues() const; - Integer functionEvaluation() const; - private: - CalibratedModel(); -}; - -%shared_ptr(TermStructureConsistentModel) -class TermStructureConsistentModel : public virtual Observable{ - public: - const Handle& termStructure() const; - private: - TermStructureConsistentModel(); -}; - -%template(CalibratedModelHandle) Handle; -%template(RelinkableCalibratedModelHandle) -RelinkableHandle; - #endif diff --git a/SWIG/cashflows.i b/SWIG/cashflows.i index 10eda4ee35..aa974166ad 100644 --- a/SWIG/cashflows.i +++ b/SWIG/cashflows.i @@ -105,6 +105,8 @@ class IndexedCashFlow : public CashFlow { Real notional() const; Date baseDate() const; Date fixingDate() const; + Real baseFixing() const; + Real indexFixing() const; ext::shared_ptr index() const; bool growthOnly() const; }; @@ -238,6 +240,7 @@ class OvernightIndexedCoupon : public FloatingRateCoupon { const std::vector