From 40e5175ca7aefcae83c1fbb1d68f316a12c84f1c Mon Sep 17 00:00:00 2001 From: Andrea Maggiulli Date: Wed, 24 Nov 2021 18:24:52 +0100 Subject: [PATCH 01/35] Merge branch 'master' [skip ci] (#268) * Fixing Nuget publishing. * Fixing GitHub publishing. --- appveyor.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 8848486a..2d21d508 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -31,13 +31,13 @@ release: QLNet Version 1.12.0 description: QLNet 1.12.0 auth_token: - secure: rK95hgNgZt9ybzXBJ/1W0nbqsG/aENQ22eyY2qxn5xjbi7ZEjv+5BmNjY/l1cAYH + secure: yPDUXqBIfD2IB3l3KMeERPZjvD7jFC7spFIkbE92e1uWcJ4j3XSei/a966C5Aa+M artifact: src\QLNet\bin\Release\netstandard2.1\QLNet.dll draft: false force_update: false - provider: NuGet api_key: - secure: PfSWPcbGqOhlEhGG74+J7tmJMa5RU/N19DEyjVP2GUXGQG4MWBeTmrKE/W4A70dh + secure: f2WH9fKVEnIKoXOYcDXDkR8C35WgQFvLD4xQ/SzfhkZAnXhdjyUU/3MD+OQAqNDZ skip_symbols: true artifact: ng From 25a6dfd21c00910e13e76c114ecd40ca06f67dd9 Mon Sep 17 00:00:00 2001 From: Andrea Maggiulli Date: Wed, 24 Nov 2021 18:28:55 +0100 Subject: [PATCH 02/35] Bumping to version 1.12.1 for next release [skip ci] --- appveyor.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 2d21d508..a0dc1c88 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -6,7 +6,7 @@ branches: only: - master - version: 1.12.0 + version: 1.12.1 configuration: Release platform: Any CPU image: Visual Studio 2019 @@ -27,9 +27,9 @@ name: ng deploy: - provider: GitHub - tag: QLNet-v1.12.0 - release: QLNet Version 1.12.0 - description: QLNet 1.12.0 + tag: QLNet-v1.12.1 + release: QLNet Version 1.12.1 + description: QLNet 1.12.1 auth_token: secure: yPDUXqBIfD2IB3l3KMeERPZjvD7jFC7spFIkbE92e1uWcJ4j3XSei/a966C5Aa+M artifact: src\QLNet\bin\Release\netstandard2.1\QLNet.dll @@ -47,7 +47,7 @@ branches: only: - develop - version: 1.12.0-preview.{build} + version: 1.12.1-preview.{build} configuration: Release platform: Any CPU image: Visual Studio 2019 @@ -96,7 +96,7 @@ branches: only: - /feature/ - version: 1.12.0-{build} + version: 1.12.1-{build} configuration: Release platform: Any CPU image: Visual Studio 2019 @@ -105,7 +105,7 @@ init: - cmd: set JAVA_HOME=C:\Program Files\Java\jdk11 - cmd: "set branch=%APPVEYOR_REPO_BRANCH%\necho branch:%branch%\nset gitVersion=%branch:/=.%\necho gitversion:%gitVersion%\nset newVersion=%gitVersion%.%APPVEYOR_BUILD_NUMBER%\necho %newVersion%" - - cmd: appveyor UpdateBuild -Version "1.12.0-%newVersion%" + - cmd: appveyor UpdateBuild -Version "1.12.1-%newVersion%" before_build: - cmd: dotnet restore qlnet.sln build_script: From 6ee6eb248b98edde0ab06f18233ef018a7f95444 Mon Sep 17 00:00:00 2001 From: Mogens Heller Grabe Date: Mon, 6 Dec 2021 13:39:47 +0100 Subject: [PATCH 03/35] Fixed MCDiscreteAveragingAsianEngine timeGrid and ArithmeticAPOPathPricer path value retrieval (#269) * don't add to InitializedList, put :) * actually use the 'value' function --- .../asian/McDiscreteAsianEngine.cs | 16 ++++++++-------- .../asian/Mc_Discr_Arith_Av_Price.cs | 3 +-- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/QLNet/Pricingengines/asian/McDiscreteAsianEngine.cs b/src/QLNet/Pricingengines/asian/McDiscreteAsianEngine.cs index 5174a6aa..ee7a673f 100644 --- a/src/QLNet/Pricingengines/asian/McDiscreteAsianEngine.cs +++ b/src/QLNet/Pricingengines/asian/McDiscreteAsianEngine.cs @@ -29,10 +29,10 @@ namespace QLNet */ public class MCDiscreteAveragingAsianEngine : McSimulation, IGenericEngine - //DiscreteAveragingAsianOption.Engine, - //McSimulation - where RNG : IRSG, new () - where S : IGeneralStatistics, new () + //DiscreteAveragingAsianOption.Engine, + //McSimulation + where RNG : IRSG, new() + where S : IGeneralStatistics, new() { // data members protected GeneralizedBlackScholesProcess process_; @@ -77,8 +77,8 @@ public void calculate() protected override TimeGrid timeGrid() { Date referenceDate = process_.riskFreeRate().link.referenceDate(); - DayCounter voldc = process_.blackVolatility().link.dayCounter() ; - List fixingTimes = new InitializedList(arguments_.fixingDates.Count); + DayCounter voldc = process_.blackVolatility().link.dayCounter(); + List fixingTimes = new InitializedList(arguments_.fixingDates.Count); for (int i = 0; i < arguments_.fixingDates.Count; i++) { @@ -86,7 +86,7 @@ protected override TimeGrid timeGrid() { double t = voldc.yearFraction(referenceDate, arguments_.fixingDates[i]); - fixingTimes.Add(t); + fixingTimes[i] = t; } } // handle here maxStepsPerYear @@ -97,7 +97,7 @@ protected override IPathGenerator pathGenerator() { TimeGrid grid = this.timeGrid(); - IRNG gen = (IRNG)new RNG().make_sequence_generator(grid.size() - 1, seed_); + IRNG gen = (IRNG)new RNG().make_sequence_generator(grid.size() - 1, seed_); return new PathGenerator(process_, grid, gen, brownianBridge_); } diff --git a/src/QLNet/Pricingengines/asian/Mc_Discr_Arith_Av_Price.cs b/src/QLNet/Pricingengines/asian/Mc_Discr_Arith_Av_Price.cs index 9f7be9e7..d87c9012 100644 --- a/src/QLNet/Pricingengines/asian/Mc_Discr_Arith_Av_Price.cs +++ b/src/QLNet/Pricingengines/asian/Mc_Discr_Arith_Av_Price.cs @@ -153,8 +153,7 @@ public double value(Path path) public double value(IPath path) { - Utils.QL_REQUIRE(path.length() > 0, () => "the path cannot be empty"); - return payoff_.value(((Path) path).back()) * discount_; + return value((Path)path); } } // From 413ab5dd285942876eb10c06395ac691b24ac14f Mon Sep 17 00:00:00 2001 From: Andrea Maggiulli Date: Tue, 7 Dec 2021 18:53:41 +0100 Subject: [PATCH 04/35] Updated editorconfig settings. Close #262 [skip ci] --- .editorconfig | 249 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 241 insertions(+), 8 deletions(-) diff --git a/.editorconfig b/.editorconfig index 290b31ec..1fb8ca4f 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,16 +1,249 @@ -# EditorConfig is awesome: http://EditorConfig.org +; EditorConfig to support per-solution formatting. +; Use the EditorConfig VS add-in to make this work. +; http://editorconfig.org/ +; +; Here are some resources for what's supported for .NET/C# +; https://kent-boogaart.com/blog/editorconfig-reference-for-c-developers +; https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-code-style-settings-reference?view=vs-2017 +; +; Be **careful** editing this because some of the rules don't support adding a severity level +; For instance if you change to `dotnet_sort_system_directives_first = true:warning` (adding `:warning`) +; then the rule will be silently ignored. -# top-most EditorConfig file +; This is the default for the codeline. root = true -# Unix-style newlines with a newline ending every file [*] -# end_of_line = lf -insert_final_newline = true +indent_style = space +charset = utf-8 trim_trailing_whitespace = true +insert_final_newline = true -# 3 space indentation [*.cs] -indent_style = space -indent_size = 3 +indent_size = 4 +dotnet_sort_system_directives_first = true + +# Don't use this. qualifier +dotnet_style_qualification_for_field = false:suggestion +dotnet_style_qualification_for_property = false:suggestion + +# use int x = .. over Int32 +dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion + +# use int.MaxValue over Int32.MaxValue +dotnet_style_predefined_type_for_member_access = true:suggestion + +# Require var all the time. +csharp_style_var_for_built_in_types = true:suggestion +csharp_style_var_when_type_is_apparent = true:suggestion +csharp_style_var_elsewhere = true:suggestion + +# Disallow throw expressions. +csharp_style_throw_expression = false:suggestion + +# Newline settings +csharp_new_line_before_open_brace = all +csharp_new_line_before_else = true +csharp_new_line_before_catch = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_members_in_anonymous_types = true + +[*.{xml,config,*proj,nuspec,props,resx,targets,yml,tasks}] +indent_size = 2 + +# Xml config files +[*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}] +indent_size = 2 + +[*.json] +indent_size = 2 + +[*.{ps1,psm1}] +indent_size = 4 + +[*.sh] +indent_size = 4 +end_of_line = lf + +[*.{razor,cshtml}] +charset = utf-8-bom + +[*.{cs,vb}] +# CA1018: Mark attributes with AttributeUsageAttribute +dotnet_diagnostic.CA1018.severity = warning + +# CA1047: Do not declare protected member in sealed type +dotnet_diagnostic.CA1047.severity = warning + +# CA1305: Specify IFormatProvider +dotnet_diagnostic.CA1305.severity = error + +# CA1507: Use nameof to express symbol names +dotnet_diagnostic.CA1507.severity = warning + +# CA1725: Parameter names should match base declaration +dotnet_diagnostic.CA1725.severity = suggestion + +# CA1802: Use literals where appropriate +dotnet_diagnostic.CA1802.severity = warning + +# CA1805: Do not initialize unnecessarily +dotnet_diagnostic.CA1805.severity = warning + +# CA1810: Do not initialize unnecessarily +dotnet_diagnostic.CA1810.severity = suggestion + +# CA1821: Remove empty Finalizers +dotnet_diagnostic.CA1821.severity = warning + +# CA1822: Make member static +dotnet_diagnostic.CA1822.severity = suggestion + +# CA1823: Avoid unused private fields +dotnet_diagnostic.CA1823.severity = warning + +# CA1825: Avoid zero-length array allocations +dotnet_diagnostic.CA1825.severity = warning + +# CA1826: Do not use Enumerable methods on indexable collections. Instead use the collection directly +dotnet_diagnostic.CA1826.severity = warning + +# CA1827: Do not use Count() or LongCount() when Any() can be used +dotnet_diagnostic.CA1827.severity = warning + +# CA1828: Do not use CountAsync() or LongCountAsync() when AnyAsync() can be used +dotnet_diagnostic.CA1828.severity = warning + +# CA1829: Use Length/Count property instead of Count() when available +dotnet_diagnostic.CA1829.severity = warning + +# CA1830: Prefer strongly-typed Append and Insert method overloads on StringBuilder +dotnet_diagnostic.CA1830.severity = warning + +# CA1831: Use AsSpan or AsMemory instead of Range-based indexers when appropriate +# CA1832: Use AsSpan or AsMemory instead of Range-based indexers when appropriate +# CA1833: Use AsSpan or AsMemory instead of Range-based indexers when appropriate +dotnet_diagnostic.CA1831.severity = warning +dotnet_diagnostic.CA1832.severity = warning +dotnet_diagnostic.CA1833.severity = warning + +# CA1834: Consider using 'StringBuilder.Append(char)' when applicable +dotnet_diagnostic.CA1834.severity = warning + +# CA1835: Prefer the 'Memory'-based overloads for 'ReadAsync' and 'WriteAsync' +dotnet_diagnostic.CA1835.severity = warning + +# CA1836: Prefer IsEmpty over Count +dotnet_diagnostic.CA1836.severity = warning + +# CA1837: Use 'Environment.ProcessId' +dotnet_diagnostic.CA1837.severity = warning + +# CA1838: Avoid 'StringBuilder' parameters for P/Invokes +dotnet_diagnostic.CA1838.severity = warning + +# CA1839: Use 'Environment.ProcessPath' +dotnet_diagnostic.CA1839.severity = warning + +# CA1840: Use 'Environment.CurrentManagedThreadId' +dotnet_diagnostic.CA1840.severity = warning + +# CA1841: Prefer Dictionary.Contains methods +dotnet_diagnostic.CA1841.severity = warning + +# CA1842: Do not use 'WhenAll' with a single task +dotnet_diagnostic.CA1842.severity = warning + +# CA1843: Do not use 'WaitAll' with a single task +dotnet_diagnostic.CA1843.severity = warning + +# CA1845: Use span-based 'string.Concat' +dotnet_diagnostic.CA1845.severity = warning + +# CA1846: Prefer AsSpan over Substring +dotnet_diagnostic.CA1846.severity = warning + +# CA2008: Do not create tasks without passing a TaskScheduler +dotnet_diagnostic.CA2008.severity = warning + +# CA2009: Do not call ToImmutableCollection on an ImmutableCollection value +dotnet_diagnostic.CA2009.severity = warning + +# CA2011: Avoid infinite recursion +dotnet_diagnostic.CA2011.severity = warning + +# CA2012: Use ValueTask correctly +dotnet_diagnostic.CA2012.severity = warning + +# CA2013: Do not use ReferenceEquals with value types +dotnet_diagnostic.CA2013.severity = warning + +# CA2014: Do not use stackalloc in loops. +dotnet_diagnostic.CA2014.severity = warning + +# CA2016: Forward the 'CancellationToken' parameter to methods that take one +dotnet_diagnostic.CA2016.severity = warning + +# CA2200: Rethrow to preserve stack details +dotnet_diagnostic.CA2200.severity = warning + +# CA2208: Instantiate argument exceptions correctly +dotnet_diagnostic.CA2208.severity = warning + +# IDE0035: Remove unreachable code +dotnet_diagnostic.IDE0035.severity = warning + +# IDE0036: Order modifiers +csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:suggestion +dotnet_diagnostic.IDE0036.severity = warning + +# IDE0043: Format string contains invalid placeholder +dotnet_diagnostic.IDE0043.severity = warning + +# IDE0044: Make field readonly +dotnet_diagnostic.IDE0044.severity = warning + +[**/{test,samples,perf}/**.{cs,vb}] +# CA1018: Mark attributes with AttributeUsageAttribute +dotnet_diagnostic.CA1018.severity = suggestion +# CA1507: Use nameof to express symbol names +dotnet_diagnostic.CA1507.severity = suggestion +# CA1802: Use literals where appropriate +dotnet_diagnostic.CA1802.severity = suggestion +# CA1805: Do not initialize unnecessarily +dotnet_diagnostic.CA1805.severity = suggestion +# CA1823: Avoid zero-length array allocations +dotnet_diagnostic.CA1825.severity = suggestion +# CA1826: Do not use Enumerable methods on indexable collections. Instead use the collection directly +dotnet_diagnostic.CA1826.severity = suggestion +# CA1827: Do not use Count() or LongCount() when Any() can be used +dotnet_diagnostic.CA1827.severity = suggestion +# CA1829: Use Length/Count property instead of Count() when available +dotnet_diagnostic.CA1829.severity = suggestion +# CA1834: Consider using 'StringBuilder.Append(char)' when applicable +dotnet_diagnostic.CA1834.severity = suggestion +# CA1835: Prefer the 'Memory'-based overloads for 'ReadAsync' and 'WriteAsync' +dotnet_diagnostic.CA1835.severity = suggestion +# CA1837: Use 'Environment.ProcessId' +dotnet_diagnostic.CA1837.severity = suggestion +# CA1838: Avoid 'StringBuilder' parameters for P/Invokes +dotnet_diagnostic.CA1838.severity = suggestion +# CA1841: Prefer Dictionary.Contains methods +dotnet_diagnostic.CA1841.severity = suggestion +# CA1844: Provide memory-based overrides of async methods when subclassing 'Stream' +dotnet_diagnostic.CA1844.severity = suggestion +# CA1845: Use span-based 'string.Concat' +dotnet_diagnostic.CA1845.severity = suggestion +# CA1846: Prefer AsSpan over Substring +dotnet_diagnostic.CA1846.severity = suggestion +# CA2008: Do not create tasks without passing a TaskScheduler +dotnet_diagnostic.CA2008.severity = suggestion +# CA2012: Use ValueTask correctly +dotnet_diagnostic.CA2012.severity = suggestion +# IDE0044: Make field readonly +dotnet_diagnostic.IDE0044.severity = suggestion + +# CA2016: Forward the 'CancellationToken' parameter to methods that take one +dotnet_diagnostic.CA2016.severity = suggestion From 8bfee0f3ec000e17ab0fdbdcfdf67087e2edcf25 Mon Sep 17 00:00:00 2001 From: Andrea Maggiulli Date: Tue, 7 Dec 2021 19:04:21 +0100 Subject: [PATCH 05/35] Removed AStyle formatting [skip ci] --- format_code.bat | 2 -- qlnet.astyle | 28 ---------------------------- tools/AStyle.exe | Bin 595456 -> 0 bytes 3 files changed, 30 deletions(-) delete mode 100644 format_code.bat delete mode 100644 qlnet.astyle delete mode 100644 tools/AStyle.exe diff --git a/format_code.bat b/format_code.bat deleted file mode 100644 index 98128d86..00000000 --- a/format_code.bat +++ /dev/null @@ -1,2 +0,0 @@ -tools\AStyle.exe --options=qlnet.astyle --recursive src/*.cs tests/*.cs -pause \ No newline at end of file diff --git a/qlnet.astyle b/qlnet.astyle deleted file mode 100644 index c2012140..00000000 --- a/qlnet.astyle +++ /dev/null @@ -1,28 +0,0 @@ -# QLNet Coding Style Options - -# braces and indent -style=allman -indent=spaces=3 - -# indentation -min-conditional-indent=0 -max-continuation-indent=80 -indent-classes -indent-namespaces -indent-switches - -# padding -pad-oper -pad-header -unpad-paren -align-pointer=type - -# formatting -break-one-line-headers -keep-one-line-blocks -keep-one-line-statements -convert-tabs - -# other options -suffix=none -exclude=obj \ No newline at end of file diff --git a/tools/AStyle.exe b/tools/AStyle.exe deleted file mode 100644 index 2dc842c25335d69372b1b49d8f3cd3b060eed8d6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 595456 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
  • Easter Monday
  • ANZAC Day. April 25th
  • Queen's Birthday, first Monday in June
  • +
  • Matariki Holiday Date, based on Maori lunar calendar, always a Friday
  • Labour Day, fourth Monday in October
  • Christmas, December 25th (possibly moved to Monday or Tuesday)
  • Boxing Day, December 26th (possibly moved to Monday or @@ -52,8 +54,43 @@ public class NewZealand : Calendar { public NewZealand() : base(Impl.Singleton) { } - class Impl : Calendar.WesternImpl + class Impl : WesternImpl { + // https://www.beehive.govt.nz/release/matariki-holiday-dates-next-thirty-years-announced + public Date[] MatarikiHolidays = new[] { + new Date(24, 6, 2022), + new Date(14, 7, 2023), + new Date(28, 6, 2024), + new Date(20, 6, 2025), + new Date(10, 7, 2026), + new Date(25, 6, 2027), + new Date(14, 7, 2028), + new Date(6, 7, 2029), + new Date(21, 6, 2030), + new Date(11, 7, 2031), + new Date(2, 7, 2032), + new Date(24, 6, 2033), + new Date(7, 7, 2034), + new Date(29, 6, 2035), + new Date(18, 7, 2036), + new Date(10, 7, 2037), + new Date(25, 6, 2038), + new Date(15, 7, 2039), + new Date(6, 7, 2040), + new Date(19, 7, 2041), + new Date(11, 7, 2042), + new Date(3, 7, 2043), + new Date(24, 6, 2044), + new Date(7, 7, 2045), + new Date(29, 6, 2046), + new Date(19, 7, 2047), + new Date(3, 7, 2048), + new Date(25, 6, 2049), + new Date(15, 7, 2050), + new Date(30, 6, 2051), + new Date(21, 6, 2052) + }; + public static readonly Impl Singleton = new Impl(); private Impl() { } @@ -85,6 +122,8 @@ public override bool isBusinessDay(Date date) || (d == 25 && m == Month.April) // Queen's Birthday, first Monday in June || (d <= 7 && w == DayOfWeek.Monday && m == Month.June) + // Matariki Holiday + || MatarikiHolidays.Any(x => x == date) // Labour Day, fourth Monday in October || ((d >= 22 && d <= 28) && w == DayOfWeek.Monday && m == Month.October) // Christmas, December 25th (possibly Monday or Tuesday) diff --git a/tests/QLNet.Tests/T_Calendars.cs b/tests/QLNet.Tests/T_Calendars.cs index 3cda1f4d..8a446155 100644 --- a/tests/QLNet.Tests/T_Calendars.cs +++ b/tests/QLNet.Tests/T_Calendars.cs @@ -1221,6 +1221,26 @@ public void testChinaIB() + " calculated holidays"); } + [Theory] + [InlineData("2022-06-24", false, true)] // matarikiDay holiday + [InlineData("2022-06-25", false, true)] // a sat + [InlineData("2022-07-08", true, false)] // a business day + public void testNZIB_matarikiDay(string day, bool expectedIsBusinessDay, bool expectedIsHoliday) + { + // arrange + var testDay = new Date(DateTime.Parse(day)); + + var nzCalendar = new NewZealand(); + var isNzBusinessDay = nzCalendar.isBusinessDay(testDay); + + if (expectedIsBusinessDay != isNzBusinessDay) + QAssert.Fail($"expectedIsBusinessDay is {expectedIsBusinessDay}, but the test result is {isNzBusinessDay}"); + + var isHoliday = nzCalendar.isHoliday(testDay); + if (expectedIsHoliday != isHoliday) + QAssert.Fail($"expectedIsHoliday is {expectedIsHoliday}, but the test result is {isNzBusinessDay}"); + } + [Fact] public void testEndOfMonth() { From b057161a5dab98360602fd54a3c61012824024ff Mon Sep 17 00:00:00 2001 From: Andrea Maggiulli Date: Sun, 10 Jul 2022 13:35:46 +0200 Subject: [PATCH 09/35] Added SOFR Index and OvernightIndexFuture instrument with helpers and tests. --- src/QLNet/Cashflows/IborCoupon.cs | 6 +- src/QLNet/Cashflows/OvernightIndexedCoupon.cs | 112 +++++++- src/QLNet/Cashflows/RateAveragingType.cs | 31 +++ src/QLNet/Indexes/Ibor/Sofr.cs | 26 ++ src/QLNet/Instruments/OvernightIndexFuture.cs | 145 ++++++++++ .../Yield/BasisSwapRateHelpers.cs | 213 +++++++++++++++ .../Yield/OvernightIndexFutureRateHelper.cs | 137 ++++++++++ tests/QLNet.Tests/T_BasisSwapRateHelpers.cs | 241 +++++++++++++++++ tests/QLNet.Tests/T_OvernightIndexedCoupon.cs | 250 ++++++++++++++++++ tests/QLNet.Tests/T_SofrFutures.cs | 104 ++++++++ 10 files changed, 1263 insertions(+), 2 deletions(-) create mode 100644 src/QLNet/Cashflows/RateAveragingType.cs create mode 100644 src/QLNet/Indexes/Ibor/Sofr.cs create mode 100644 src/QLNet/Instruments/OvernightIndexFuture.cs create mode 100644 src/QLNet/Termstructures/Yield/BasisSwapRateHelpers.cs create mode 100644 src/QLNet/Termstructures/Yield/OvernightIndexFutureRateHelper.cs create mode 100644 tests/QLNet.Tests/T_BasisSwapRateHelpers.cs create mode 100644 tests/QLNet.Tests/T_OvernightIndexedCoupon.cs create mode 100644 tests/QLNet.Tests/T_SofrFutures.cs diff --git a/src/QLNet/Cashflows/IborCoupon.cs b/src/QLNet/Cashflows/IborCoupon.cs index 3f4dae2e..efcfadaa 100644 --- a/src/QLNet/Cashflows/IborCoupon.cs +++ b/src/QLNet/Cashflows/IborCoupon.cs @@ -1,7 +1,7 @@ /* Copyright (C) 2008 Siarhei Novik (snovik@gmail.com) Copyright (C) 2008 Toyin Akin (toyin_akin@hotmail.com) - Copyright (C) 2008, 2009 , 2010 Andrea Maggiulli (a.maggiulli@gmail.com) + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) This file is part of QLNet Project https://github.com/amaggiulli/qlnet @@ -124,6 +124,10 @@ public override CashFlow factory(double nominal, Date paymentDate, Date startDat (IborIndex)index, gearing, spread, refPeriodStart, refPeriodEnd, dayCounter, isInArrears); } + //! End of the deposit period underlying the coupon fixing + /*! This might be not the same as fixingMaturityDate if par coupons are used. */ + public Date fixingEndDate() {return fixingEndDate_;} + private IborIndex iborIndex_; private Date fixingDate_, fixingValueDate_, fixingEndDate_; private double spanningTime_; diff --git a/src/QLNet/Cashflows/OvernightIndexedCoupon.cs b/src/QLNet/Cashflows/OvernightIndexedCoupon.cs index 4f8c7f1b..4e97a90b 100644 --- a/src/QLNet/Cashflows/OvernightIndexedCoupon.cs +++ b/src/QLNet/Cashflows/OvernightIndexedCoupon.cs @@ -1,5 +1,5 @@ /* - Copyright (C) 2008, 2009 , 2010 Andrea Maggiulli (a.maggiulli@gmail.com) + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) * This file is part of QLNet Project https://github.com/amaggiulli/qlnet @@ -32,6 +32,89 @@ public override void initialize(FloatingRateCoupon coupon) Utils.QL_REQUIRE(coupon_ != null, () => "wrong coupon type"); } + public double averageRate(Date date) + { + Date today = Settings.evaluationDate(); + + OvernightIndex index = coupon_.index() as OvernightIndex; + var pastFixings = IndexManager.instance().getHistory(index?.name()); + + List fixingDates = coupon_.fixingDates(); + List valueDates = coupon_.valueDates(); + List dt = coupon_.dt(); + + int i = 0; + int n = valueDates.FindIndex(x => date <= x); // std::lower_bound(valueDates.begin(), valueDates.end(), date) - valueDates.begin(); + double compoundFactor = 1.0; + + // already fixed part + while (i < n && fixingDates[i] < today) + { + // rate must have been fixed + double? fixing = pastFixings[fixingDates[i]]; + Utils.QL_REQUIRE(fixing != null, ()=> "Missing " + index.name() + " fixing for " + fixingDates[i]); + double span = (date >= valueDates[i + 1] ? dt[i] : index.dayCounter().yearFraction(valueDates[i], date)); + compoundFactor *= (1.0 + fixing.GetValueOrDefault() * span); + ++i; + } + + // today is a border case + if (i < n && fixingDates[i] == today) + { + // might have been fixed + try + { + double? fixing = pastFixings[fixingDates[i]]; + if (fixing != null) + { + double span = (date >= valueDates[i + 1] ? dt[i] : index.dayCounter().yearFraction(valueDates[i], date)); + compoundFactor *= (1.0 + fixing.GetValueOrDefault() * span); + ++i; + } + else + { + ; // fall through and forecast + } + } + catch (Exception _) + { + ; // fall through and forecast + } + } + + // forward part using telescopic property in order + // to avoid the evaluation of multiple forward fixings + if (i < n) + { + var curve = index.forwardingTermStructure(); + Utils.QL_REQUIRE(!curve.empty(),()=> + "null term structure set to this instance of " + index.name()); + + double startDiscount = curve.link.discount(valueDates[i]); + if (valueDates[n] == date) + { + // full telescopic formula + double endDiscount = curve.link.discount(valueDates[n]); + compoundFactor *= startDiscount / endDiscount; + } + else + { + // The last fixing is not used for its full period (the date is between its + // start and end date). We can use the telescopic formula until the previous + // date, then we'll add the missing bit. + var endDiscount = curve.link.discount(valueDates[n - 1]); + compoundFactor *= startDiscount / endDiscount; + + var fixing = index.fixing(fixingDates[n - 1]); + var span = index.dayCounter().yearFraction(valueDates[n - 1], date); + compoundFactor *= (1.0 + fixing * span); + } + } + + var rate = (compoundFactor - 1.0) / coupon_.accruedPeriod(date); + return coupon_.gearing() * rate + coupon_.spread(); + } + public override double swapletRate() { OvernightIndex index = coupon_.index() as OvernightIndex; @@ -178,6 +261,33 @@ public List indexFixings() //! value dates for the rates to be compounded public List valueDates() { return valueDates_; } + public override double accruedAmount(Date d) + { + if (d <= accrualStartDate_ || d > paymentDate_) + { + // out of coupon range + return 0.0; + } + else if (tradingExCoupon(d)) { + return nominal() * averageRate(d) * accruedPeriod(d); + } + else + { + // usual case + return nominal() * averageRate(Date.Min(d, accrualEndDate_)) * accruedPeriod(d); + } + } + + private double averageRate(Date d) + { + Utils.QL_REQUIRE(pricer_!=null, ()=> "pricer not set"); + pricer_.initialize(this); + if (pricer_ is OvernightIndexedCouponPricer overnightIndexPricer) + return overnightIndexPricer.averageRate(d); + + return pricer_.swapletRate(); + } + private List valueDates_, fixingDates_; private List fixings_; int n_; diff --git a/src/QLNet/Cashflows/RateAveragingType.cs b/src/QLNet/Cashflows/RateAveragingType.cs new file mode 100644 index 00000000..281f7f39 --- /dev/null +++ b/src/QLNet/Cashflows/RateAveragingType.cs @@ -0,0 +1,31 @@ +// Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) +// +// This file is part of QLNet Project https://github.com/amaggiulli/qlnet +// QLNet is free software: you can redistribute it and/or modify it +// under the terms of the QLNet license. You should have received a +// copy of the license along with this program; if not, license is +// available at . +// +// QLNet is a based on QuantLib, a free-software/open-source library +// for financial quantitative analysts and developers - http://quantlib.org/ +// The QuantLib license is available online at http://quantlib.org/license.shtml. +// +// 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. +namespace QLNet +{ + public enum RateAveragingType + { + Simple, /* Under the simple convention the amount of + interest is calculated by applying the + sub-rate to the principal, and the payment + due at the end of the period is the sum of + those amounts. */ + Compound /* Under the compound convention, the + additional amount of interest owed each + period is calculated by applying the rate + both to the principal and the accumulated + unpaid interest. */ + } +} diff --git a/src/QLNet/Indexes/Ibor/Sofr.cs b/src/QLNet/Indexes/Ibor/Sofr.cs new file mode 100644 index 00000000..1b4a90bd --- /dev/null +++ b/src/QLNet/Indexes/Ibor/Sofr.cs @@ -0,0 +1,26 @@ +// Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) +// +// This file is part of QLNet Project https://github.com/amaggiulli/qlnet +// QLNet is free software: you can redistribute it and/or modify it +// under the terms of the QLNet license. You should have received a +// copy of the license along with this program; if not, license is +// available at . +// +// QLNet is a based on QuantLib, a free-software/open-source library +// for financial quantitative analysts and developers - http://quantlib.org/ +// The QuantLib license is available online at http://quantlib.org/license.shtml. +// +// 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. +namespace QLNet +{ + // Sofr (Secured Overnight Financing Rate) index. + public class Sofr : OvernightIndex + { + public Sofr(Handle h = null) + : base("SOFR", 0, new USDCurrency(), new UnitedStates(UnitedStates.Market.GovernmentBond), new Actual360(), + h ?? new Handle()) + {} + } +} diff --git a/src/QLNet/Instruments/OvernightIndexFuture.cs b/src/QLNet/Instruments/OvernightIndexFuture.cs new file mode 100644 index 00000000..04ca315b --- /dev/null +++ b/src/QLNet/Instruments/OvernightIndexFuture.cs @@ -0,0 +1,145 @@ +// Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) +// +// This file is part of QLNet Project https://github.com/amaggiulli/qlnet +// QLNet is free software: you can redistribute it and/or modify it +// under the terms of the QLNet license. You should have received a +// copy of the license along with this program; if not, license is +// available at . +// +// QLNet is a based on QuantLib, a free-software/open-source library +// for financial quantitative analysts and developers - http://quantlib.org/ +// The QuantLib license is available online at http://quantlib.org/license.shtml. +// +// 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. +namespace QLNet +{ + /// + /// Future on a compounded overnight index investment. + /// + /// Compatible with SOFR futures and Sonia futures available on + /// CME and ICE exchanges. + /// + public class OvernightIndexFuture : Instrument + { + private OvernightIndex overnightIndex_; + private Date valueDate_, maturityDate_; + private Handle convexityAdjustment_; + private RateAveragingType averagingMethod_; + + public OvernightIndexFuture(OvernightIndex overnightIndex, Date valueDate, Date maturityDate, + Handle convexityAdjustment = null, RateAveragingType averagingMethod = RateAveragingType.Compound) + { + overnightIndex_ = overnightIndex; + valueDate_ = valueDate; + maturityDate_ = maturityDate; + convexityAdjustment_ = convexityAdjustment ?? new Handle(); + averagingMethod_ = averagingMethod; + + Utils.QL_REQUIRE(overnightIndex_ != null, () => "null overnight index"); + overnightIndex_?.registerWith(update); + } + + public double convexityAdjustment() + { + return convexityAdjustment_.empty() ? 0.0 : convexityAdjustment_.link.value(); + } + + public override bool isExpired() + { + return new simple_event(maturityDate_).hasOccurred(); + } + + public OvernightIndex overnightIndex() { return overnightIndex_; } + public Date valueDate() { return valueDate_; } + public Date maturityDate() { return maturityDate_; } + + protected override void performCalculations() + { + double? R = convexityAdjustment() + rate(); + NPV_ = 100.0 * (1.0 - R); + } + + private double? rate() + { + switch (averagingMethod_) + { + case RateAveragingType.Simple: + return averagedRate(); + case RateAveragingType.Compound: + return compoundedRate(); + default: + Utils.QL_FAIL("unknown compounding convention (" + averagingMethod_ + ")"); + break; + } + + return null; + } + + private double? averagedRate() + { + Date today = Settings.evaluationDate(); + Calendar calendar = overnightIndex_.fixingCalendar(); + DayCounter dayCounter = overnightIndex_.dayCounter(); + Handle forwardCurve = overnightIndex_.forwardingTermStructure(); + double? avg = 0; + Date d1 = valueDate_; + TimeSeries history = IndexManager.instance().getHistory(overnightIndex_.name()); + double? fwd; + while (d1 < maturityDate_) + { + Date d2 = calendar.advance(d1, 1, TimeUnit.Days); + if (d1 < today) + { + fwd = history[d1]; + Utils.QL_REQUIRE(fwd != null, () =>"missing rate on " + d1 + " for index " + overnightIndex_.name()); + } + else + { + fwd = forwardCurve.link.forwardRate(d1, d2, dayCounter, Compounding.Simple).rate(); + } + avg += fwd * dayCounter.yearFraction(d1, d2); + d1 = d2; + } + + return avg / dayCounter.yearFraction(valueDate_, maturityDate_); + } + + private double? compoundedRate() + { + Date today = Settings.evaluationDate(); + Calendar calendar = overnightIndex_.fixingCalendar(); + DayCounter dayCounter = overnightIndex_.dayCounter(); + Handle forwardCurve = overnightIndex_.forwardingTermStructure(); + double? prod = 1; + if (today > valueDate_) + { + // can't value on a weekend inside reference period because we + // won't know the reset rate until start of next business day. + // user can supply an estimate if they really want to do this + today = calendar.adjust(today); + // for valuations inside the reference period, index quotes + // must have been populated in the history + TimeSeries history = IndexManager.instance().getHistory(overnightIndex_.name()); + Date d1 = valueDate_; + while (d1 < today) + { + double? r = history[d1]; + Utils.QL_REQUIRE(r != null, ()=> "missing rate on " + d1 + " for index " + overnightIndex_.name()); + Date d2 = calendar.advance(d1, 1, TimeUnit.Days); + prod *= 1 + r * dayCounter.yearFraction(d1, d2); + d1 = d2; + } + } + double forwardDiscount = forwardCurve.link.discount(maturityDate_); + if (valueDate_ > today) + { + forwardDiscount /= forwardCurve.link.discount(valueDate_); + } + prod /= forwardDiscount; + + return (prod - 1) / dayCounter.yearFraction(valueDate_, maturityDate_); + } + } +} diff --git a/src/QLNet/Termstructures/Yield/BasisSwapRateHelpers.cs b/src/QLNet/Termstructures/Yield/BasisSwapRateHelpers.cs new file mode 100644 index 00000000..6ef0f329 --- /dev/null +++ b/src/QLNet/Termstructures/Yield/BasisSwapRateHelpers.cs @@ -0,0 +1,213 @@ +// Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) +// +// This file is part of QLNet Project https://github.com/amaggiulli/qlnet +// QLNet is free software: you can redistribute it and/or modify it +// under the terms of the QLNet license. You should have received a +// copy of the license along with this program; if not, license is +// available at . +// +// QLNet is a based on QuantLib, a free-software/open-source library +// for financial quantitative analysts and developers - http://quantlib.org/ +// The QuantLib license is available online at http://quantlib.org/license.shtml. +// +// 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.using System; +using System.Linq; +namespace QLNet +{ + // Rate helper for bootstrapping over ibor-ibor basis swaps + /* The swap is assumed to pay baseIndex + basis and receive + otherIndex. The helper can be used to bootstrap the forecast + curve for baseIndex (in which case you'll have to pass + bootstrapBaseCurve = true and provide otherIndex with a + forecast curve) or the forecast curve for otherIndex (in which + case bootstrapBaseCurve = false and baseIndex will need a + forecast curve). + In both cases, an exogenous discount curve is required. + */ + public class IborIborBasisSwapRateHelper : RelativeDateRateHelper + { + private Period tenor_; + private int settlementDays_; + private Calendar calendar_; + private BusinessDayConvention convention_; + private bool endOfMonth_; + private IborIndex baseIndex_; + private IborIndex otherIndex_; + private Handle discountHandle_; + private bool bootstrapBaseCurve_; + private Swap swap_; + private RelinkableHandle termStructureHandle_ = new RelinkableHandle(); + + public IborIborBasisSwapRateHelper(Handle basis, Period tenor, int settlementDays, Calendar calendar, + BusinessDayConvention convention, + bool endOfMonth, IborIndex baseIndex, IborIndex otherIndex, Handle discountHandle, + bool bootstrapBaseCurve) : base(basis) + { + tenor_ = tenor; + settlementDays_ = settlementDays; + calendar_ = calendar; + convention_ = convention; + endOfMonth_ = endOfMonth; + discountHandle_ = discountHandle; + bootstrapBaseCurve_ = bootstrapBaseCurve; + + // we need to clone the index whose forecast curve we want to bootstrap + // and copy the other one + if (bootstrapBaseCurve_) + { + baseIndex_ = baseIndex.clone(termStructureHandle_); + baseIndex_.unregisterWith(update); + otherIndex_ = otherIndex; + } + else + { + baseIndex_ = baseIndex; + otherIndex_ = otherIndex.clone(termStructureHandle_); + otherIndex_.unregisterWith(update); + } + + baseIndex_.registerWith(update); + otherIndex_.registerWith(update); + discountHandle_.registerWith(update); + + initializeDates(); + } + + public override double impliedQuote() + { + swap_.recalculate(); + return (double)(-(swap_.NPV() / swap_.legBPS(0)) * 1.0e-4); + } + + protected sealed override void initializeDates() + { + Date today = Settings.evaluationDate(); + earliestDate_ = calendar_.advance(today, new Period(settlementDays_, TimeUnit.Days), BusinessDayConvention.Following); + maturityDate_ = calendar_.advance(earliestDate_, tenor_, convention_); + + Schedule baseSchedule = + new MakeSchedule().from(earliestDate_).to(maturityDate_) + .withTenor(baseIndex_.tenor()) + .withCalendar(calendar_) + .withConvention(convention_) + .endOfMonth(endOfMonth_) + .forwards().value(); + var baseLeg = new IborLeg(baseSchedule, baseIndex_).withNotionals(100.0); + var lastBaseCoupon = baseLeg.value().LastOrDefault() as IborCoupon; + + Schedule otherSchedule = + new MakeSchedule().from(earliestDate_).to(maturityDate_) + .withTenor(otherIndex_.tenor()) + .withCalendar(calendar_) + .withConvention(convention_) + .endOfMonth(endOfMonth_) + .forwards().value(); + var otherLeg = new IborLeg(otherSchedule, otherIndex_).withNotionals(100.0); + var lastOtherCoupon = otherLeg.value().LastOrDefault() as IborCoupon; + + latestRelevantDate_ = Date.Max(maturityDate_, Date.Max(lastBaseCoupon.fixingEndDate(), lastOtherCoupon.fixingEndDate())); + pillarDate_ = latestRelevantDate_; + + swap_ = new Swap(baseLeg, otherLeg); + swap_.setPricingEngine(new DiscountingSwapEngine(discountHandle_)); + } + + public override void setTermStructure(YieldTermStructure t) + { + // do not set the relinkable handle as an observer - + // force recalculation when needed---the index is not lazy + bool observer = false; + termStructureHandle_.linkTo(t, observer); + base.setTermStructure(t); + } + } + + //! Rate helper for bootstrapping over overnight-ibor basis swaps + /*! The swap is assumed to pay baseIndex + basis and receive + otherIndex. This helper can be used to bootstrap the forecast + curve for otherIndex; baseIndex will need an existing forecast + curve. An exogenous discount curve can be passed; if not, + the overnight-index curve will be used. + */ + public class OvernightIborBasisSwapRateHelper : RelativeDateRateHelper + { + private Period tenor_; + private int settlementDays_; + private Calendar calendar_; + private BusinessDayConvention convention_; + private bool endOfMonth_; + private OvernightIndex baseIndex_; + private IborIndex otherIndex_; + private Handle discountHandle_; + private Swap swap_; + private RelinkableHandle termStructureHandle_ = new RelinkableHandle(); + + public OvernightIborBasisSwapRateHelper(Handle basis, Period tenor, int settlementDays, Calendar calendar, + BusinessDayConvention convention, bool endOfMonth, OvernightIndex baseIndex, IborIndex otherIndex, + Handle discountHandle = null) + :base(basis) + { + tenor_ = tenor; + settlementDays_ = settlementDays; + calendar_ = calendar; + convention_ = convention; + endOfMonth_ = endOfMonth; + discountHandle_ = discountHandle; + + // we need to clone the index whose forecast curve we want to bootstrap + // and copy the other one + baseIndex_ = baseIndex; + otherIndex_ = otherIndex.clone(termStructureHandle_); + otherIndex_.unregisterWith(update); + + baseIndex_.registerWith(update); + otherIndex_.registerWith(update); + discountHandle_.registerWith(update); + + initializeDates(); + } + + protected override void initializeDates() + { + Date today = Settings.evaluationDate(); + earliestDate_ = calendar_.advance(today, new Period(settlementDays_,TimeUnit.Days), BusinessDayConvention.Following); + maturityDate_ = calendar_.advance(earliestDate_, tenor_, convention_); + + Schedule schedule = + new MakeSchedule().from(earliestDate_).to(maturityDate_) + .withTenor(otherIndex_.tenor()) + .withCalendar(calendar_) + .withConvention(convention_) + .endOfMonth(endOfMonth_) + .forwards().value(); + + var baseLeg = new OvernightLeg(schedule, baseIndex_).withNotionals(100.0); + + var otherLeg = new IborLeg(schedule, otherIndex_).withNotionals(100.0); + var lastOtherCoupon = otherLeg.value().LastOrDefault() as IborCoupon; + + latestRelevantDate_ = Date.Max(maturityDate_, lastOtherCoupon.fixingEndDate()); + pillarDate_ = latestRelevantDate_; + + swap_ = new Swap(baseLeg, otherLeg); + swap_.setPricingEngine(new DiscountingSwapEngine(discountHandle_.empty() ? termStructureHandle_ : discountHandle_)); + } + + public override void setTermStructure(YieldTermStructure t) + { + // do not set the relinkable handle as an observer - + // force recalculation when needed---the index is not lazy + bool observer = false; + termStructureHandle_.linkTo(t, observer); + base.setTermStructure(t); + } + + public override double impliedQuote() + { + swap_.recalculate(); + return (double)(- (swap_.NPV() / swap_.legBPS(0)) * 1.0e-4); + } + } +} diff --git a/src/QLNet/Termstructures/Yield/OvernightIndexFutureRateHelper.cs b/src/QLNet/Termstructures/Yield/OvernightIndexFutureRateHelper.cs new file mode 100644 index 00000000..a916a548 --- /dev/null +++ b/src/QLNet/Termstructures/Yield/OvernightIndexFutureRateHelper.cs @@ -0,0 +1,137 @@ +// Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) +// +// This file is part of QLNet Project https://github.com/amaggiulli/qlnet +// QLNet is free software: you can redistribute it and/or modify it +// under the terms of the QLNet license. You should have received a +// copy of the license along with this program; if not, license is +// available at . +// +// QLNet is a based on QuantLib, a free-software/open-source library +// for financial quantitative analysts and developers - http://quantlib.org/ +// The QuantLib license is available online at http://quantlib.org/license.shtml. +// +// 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. + +using System; + +namespace QLNet +{ + // RateHelper for bootstrapping over overnight compounding futures + public class OvernightIndexFutureRateHelper : RateHelper + { + private OvernightIndexFuture future_; + RelinkableHandle termStructureHandle_ = new RelinkableHandle(); + + public OvernightIndexFutureRateHelper(Handle price, + // first day of reference period + Date valueDate, + // delivery date + Date maturityDate, + OvernightIndex overnightIndex, + Handle convexityAdjustment = null, + RateAveragingType averagingMethod = RateAveragingType.Compound) + :base(price) + { + OvernightIndex index = overnightIndex.clone(termStructureHandle_); + future_ = new OvernightIndexFuture(index, valueDate, maturityDate, convexityAdjustment ?? new Handle(), averagingMethod); + earliestDate_ = valueDate; + latestDate_ = maturityDate; + } + + // RateHelper interface + public override double impliedQuote() + { + future_.recalculate(); + return future_.NPV(); + } + + public override void setTermStructure(YieldTermStructure t) + { + // do not set the relinkable handle as an observer - + // force recalculation when needed + bool observer = false; + termStructureHandle_.linkTo(t, observer); + base.setTermStructure(t); + } + + public double convexityAdjustment() + { + return future_.convexityAdjustment(); + } + + } + + // RateHelper for bootstrapping over CME SOFR futures + /* It compounds overnight SOFR rates from the third Wednesday + of the reference month/year (inclusive) to the third Wednesday + of the month one Month/Quarter later (exclusive). + + It requires the index history to be populated when the + reference period starts in the past. + */ + public class SofrFutureRateHelper : OvernightIndexFutureRateHelper + { + public SofrFutureRateHelper(Handle price, Month referenceMonth, int referenceYear, Frequency referenceFreq, + Handle convexityAdjustment = null) + : base(price, + getValidSofrStart((int)referenceMonth, referenceYear, referenceFreq), + getValidSofrEnd((int)referenceMonth, referenceYear, referenceFreq), new Sofr(), + convexityAdjustment ?? new Handle(), + referenceFreq == Frequency.Quarterly ? RateAveragingType.Compound : RateAveragingType.Simple) + { + Utils.QL_REQUIRE(referenceFreq == Frequency.Quarterly || referenceFreq == Frequency.Monthly, () => + "only monthly and quarterly SOFR futures accepted"); + if (referenceFreq == Frequency.Quarterly) + { + Utils.QL_REQUIRE(referenceMonth == Month.Mar || referenceMonth == Month.Jun || + referenceMonth == Month.Sep || + referenceMonth == Month.Dec, + () => "quarterly SOFR futures can only start in Mar,Jun,Sep,Dec"); + } + } + + public SofrFutureRateHelper(double price, Month referenceMonth, int referenceYear, Frequency referenceFreq, + double convexityAdjustment = 0.0) + : base(new Handle(new SimpleQuote(price)), + getValidSofrStart((int)referenceMonth, referenceYear, referenceFreq), + getValidSofrEnd((int)referenceMonth, referenceYear, referenceFreq), new Sofr(), + new Handle(new SimpleQuote(convexityAdjustment)), + referenceFreq == Frequency.Quarterly ? RateAveragingType.Compound : RateAveragingType.Simple) + { + Utils.QL_REQUIRE(referenceFreq == Frequency.Quarterly || referenceFreq == Frequency.Monthly, () => + "only monthly and quarterly SOFR futures accepted"); + if (referenceFreq == Frequency.Quarterly) + { + Utils.QL_REQUIRE(referenceMonth == Month.Mar || referenceMonth == Month.Jun || + referenceMonth == Month.Sep || + referenceMonth == Month.Dec, + () => "quarterly SOFR futures can only start in Mar,Jun,Sep,Dec"); + } + } + + + private static Date getValidSofrStart(int referenceMonth, int referenceYear, Frequency referenceFreq) + { + return referenceFreq == Frequency.Monthly + ? new UnitedStates(UnitedStates.Market.GovernmentBond).adjust(new Date(1, referenceMonth, referenceYear)) + : Date.nthWeekday(3, DayOfWeek.Wednesday, referenceMonth, referenceYear); + } + + private static Date getValidSofrEnd(int referenceMonth, int referenceYear, Frequency referenceFreq) + { + if (referenceFreq == Frequency.Monthly) + { + Calendar dc = new UnitedStates(UnitedStates.Market.GovernmentBond); + Date d = dc.endOfMonth(new Date(1, referenceMonth, referenceYear)); + return dc.advance(d, new Period(1, TimeUnit.Days)); + } + else + { + Date d = getValidSofrStart(referenceMonth, referenceYear, referenceFreq) + new Period(referenceFreq); + return Date.nthWeekday(3, DayOfWeek.Wednesday, d.month(), d.year()); + } + } + } +} diff --git a/tests/QLNet.Tests/T_BasisSwapRateHelpers.cs b/tests/QLNet.Tests/T_BasisSwapRateHelpers.cs new file mode 100644 index 00000000..88e531f6 --- /dev/null +++ b/tests/QLNet.Tests/T_BasisSwapRateHelpers.cs @@ -0,0 +1,241 @@ +// Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) +// +// This file is part of QLNet Project https://github.com/amaggiulli/qlnet +// QLNet is free software: you can redistribute it and/or modify it +// under the terms of the QLNet license. You should have received a +// copy of the license along with this program; if not, license is +// available at . +// +// QLNet is a based on QuantLib, a free-software/open-source library +// for financial quantitative analysts and developers - http://quantlib.org/ +// The QuantLib license is available online at http://quantlib.org/license.shtml. +// +// 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. +using System; +using System.Collections.Generic; +using QLNet; +using Xunit; + +namespace TestSuite +{ + [Collection("QLNet CI Tests")] + public class T_BasisSwapRateHelpers + { + private struct BasisSwapQuote + { + public int n { get; set; } + public TimeUnit units { get; set; } + public double basis { get; set; } + } + + private void testIborIborBootstrap(bool bootstrapBaseCurve) + { + BasisSwapQuote[] quotes = { + new BasisSwapQuote{ n = 1, units = TimeUnit.Years, basis = 0.0010 }, + new BasisSwapQuote{ n = 2, units = TimeUnit.Years, basis = 0.0012 }, + new BasisSwapQuote{ n = 3, units = TimeUnit.Years, basis = 0.0015 }, + new BasisSwapQuote{ n = 5, units = TimeUnit.Years, basis = 0.0015 }, + new BasisSwapQuote{ n = 8, units = TimeUnit.Years, basis = 0.0018 }, + new BasisSwapQuote{ n = 10, units = TimeUnit.Years, basis = 0.0020 }, + new BasisSwapQuote{ n = 15, units = TimeUnit.Years, basis = 0.0021 }, + new BasisSwapQuote{ n = 20, units = TimeUnit.Years, basis = 0.0021 }, + }; + + var settlementDays = 2; + var calendar = new UnitedStates(UnitedStates.Market.GovernmentBond); + var convention = BusinessDayConvention.Following; + var endOfMonth = false; + + var knownForecastCurve = new Handle(Utilities.flatRate(0.01, new Actual365Fixed())); + var discountCurve = new Handle(Utilities.flatRate(0.005, new Actual365Fixed())); + IborIndex baseIndex, otherIndex; + + if (bootstrapBaseCurve) + { + baseIndex = new USDLibor(new Period(3 , TimeUnit.Months)); + otherIndex = new USDLibor(new Period(6, TimeUnit.Months), knownForecastCurve); + } + else + { + baseIndex = new USDLibor(new Period(3, TimeUnit.Months), knownForecastCurve); + otherIndex = new USDLibor(new Period(6, TimeUnit.Months)); + } + + var helpers = new List(); + + foreach (var q in quotes) + { + var h = new IborIborBasisSwapRateHelper( + new Handle(new SimpleQuote(q.basis)), + new Period(q.n, q.units), settlementDays, calendar, convention, endOfMonth, + baseIndex, otherIndex, discountCurve, bootstrapBaseCurve); + helpers.Add(h); + } + + var bootstrappedCurve = new PiecewiseYieldCurve (0, calendar, helpers, new Actual365Fixed()); + + var today = Settings.evaluationDate(); + var spot = calendar.advance(today, settlementDays, TimeUnit.Days); + + if (bootstrapBaseCurve) + { + baseIndex = new USDLibor(new Period(3 , TimeUnit.Months), new Handle(bootstrappedCurve)); + otherIndex = new USDLibor(new Period(6, TimeUnit.Months), knownForecastCurve); + } + else + { + baseIndex = new USDLibor(new Period(3, TimeUnit.Months), knownForecastCurve); + otherIndex = new USDLibor(new Period(6, TimeUnit.Months), new Handle(bootstrappedCurve)); + } + + foreach (var q in quotes) + { + // create swaps and check they're fair + var maturity = calendar.advance(spot, q.n, q.units, convention); + + Schedule s1 = + new MakeSchedule() + .from(spot).to(maturity) + .withTenor(baseIndex.tenor()) + .withCalendar(calendar) + .withConvention(convention) + .withRule(DateGeneration.Rule.Forward).value(); + var leg1 = new IborLeg(s1, baseIndex) + .withSpreads(q.basis) + .withNotionals(100.0); + + Schedule s2 = + new MakeSchedule() + .from(spot).to(maturity) + .withTenor(otherIndex.tenor()) + .withCalendar(calendar) + .withConvention(convention) + .withRule(DateGeneration.Rule.Forward).value(); + var leg2 = new IborLeg(s2, otherIndex) + .withNotionals(100.0); + + var swap = new Swap(leg1, leg2); + swap.setPricingEngine(new DiscountingSwapEngine(discountCurve)); + + double NPV = swap.NPV(); + double tolerance = 1e-8; + if (Math.Abs(NPV) > tolerance) + { + QAssert.Fail("Failed to price fair " + q.n + "-year(s) swap:" + "\n calculated: " + NPV); + } + } + + } + + private void testOvernightIborBootstrap(bool externalDiscountCurve) + { + BasisSwapQuote[] quotes = { + new BasisSwapQuote{ n = 1, units = TimeUnit.Years, basis= 0.0010 }, + new BasisSwapQuote{ n = 2, units = TimeUnit.Years, basis= 0.0012 }, + new BasisSwapQuote{ n =3, units = TimeUnit.Years, basis= 0.0015 }, + new BasisSwapQuote{ n =5, units = TimeUnit.Years, basis= 0.0015 }, + new BasisSwapQuote{ n =8, units = TimeUnit.Years, basis= 0.0018 }, + new BasisSwapQuote{ n =10, units = TimeUnit.Years, basis= 0.0020 }, + new BasisSwapQuote{ n =15, units = TimeUnit.Years, basis= 0.0021 }, + new BasisSwapQuote{ n =20, units = TimeUnit.Years, basis= 0.0021 } + }; + + var settlementDays = 2; + var calendar = new UnitedStates(UnitedStates.Market.GovernmentBond); + var convention = BusinessDayConvention.Following; + var endOfMonth = false; + + Handle knownForecastCurve = new Handle(Utilities.flatRate(0.01, new Actual365Fixed())); + RelinkableHandle discountCurve = new RelinkableHandle(); + if (externalDiscountCurve) + discountCurve.linkTo(Utilities.flatRate(0.005, new Actual365Fixed())); + + var baseIndex = new Sofr(knownForecastCurve); + var otherIndex = new USDLibor(new Period(6 , TimeUnit.Months)); + + var helpers = new List(); + foreach (var q in quotes) + { + var h = new OvernightIborBasisSwapRateHelper(new Handle(new SimpleQuote(q.basis)), + new Period(q.n, q.units), settlementDays, calendar, convention, endOfMonth, + baseIndex, otherIndex, discountCurve); + helpers.Add(h); + } + + var bootstrappedCurve = new PiecewiseYieldCurve(0, calendar, helpers, new Actual365Fixed()); + + Date today = Settings.evaluationDate(); + Date spot = calendar.advance(today, settlementDays, TimeUnit.Days); + + otherIndex = new USDLibor(new Period(6 , TimeUnit.Months), new Handle(bootstrappedCurve)); + + foreach (var q in quotes) + { + // create swaps and check they're fair + Date maturity = calendar.advance(spot, q.n, q.units, convention); + + Schedule s = + new MakeSchedule() + .from(spot).to(maturity) + .withTenor(otherIndex.tenor()) + .withCalendar(calendar) + .withConvention(convention) + .withRule(DateGeneration.Rule.Forward).value(); + + var leg1 = new OvernightLeg(s, baseIndex) + .withSpreads(q.basis) + .withNotionals(100.0); + var leg2 = new IborLeg(s, otherIndex) + .withNotionals(100.0); + + var swap = new Swap(leg1, leg2); + if (externalDiscountCurve) + { + swap.setPricingEngine(new DiscountingSwapEngine(discountCurve)); + } + else + { + swap.setPricingEngine(new DiscountingSwapEngine(new Handle(bootstrappedCurve))); + } + + double NPV = swap.NPV(); + double tolerance = 1e-8; + if (Math.Abs(NPV) > tolerance) + { + QAssert.Fail("Failed to price fair " + q.n + "-year(s) swap:" + "\n calculated: " + NPV); + } + } + } + + [Fact] + public void testIborIborBaseCurveBootstrap() + { + // Testing IBOR-IBOR basis-swap rate helpers (base curve bootstrap) + testIborIborBootstrap(true); + } + + [Fact] + public void testIborIborOtherCurveBootstrap() + { + // Testing IBOR-IBOR basis-swap rate helpers (other curve bootstrap) + testIborIborBootstrap(false); + } + + [Fact] + public void testOvernightIborBootstrapNoDiscountCurve() + { + // Testing overnight-IBOR basis-swap rate helpers + testOvernightIborBootstrap(false); + } + + [Fact] + public void testOvernightIborBootstrapWithDiscountCurve() + { + // Testing overnight-IBOR basis-swap rate helpers with external discount curve + testOvernightIborBootstrap(true); + } + + } +} diff --git a/tests/QLNet.Tests/T_OvernightIndexedCoupon.cs b/tests/QLNet.Tests/T_OvernightIndexedCoupon.cs new file mode 100644 index 00000000..814e0223 --- /dev/null +++ b/tests/QLNet.Tests/T_OvernightIndexedCoupon.cs @@ -0,0 +1,250 @@ +// Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) +// +// This file is part of QLNet Project https://github.com/amaggiulli/qlnet +// QLNet is free software: you can redistribute it and/or modify it +// under the terms of the QLNet license. You should have received a +// copy of the license along with this program; if not, license is +// available at . +// +// QLNet is a based on QuantLib, a free-software/open-source library +// for financial quantitative analysts and developers - http://quantlib.org/ +// The QuantLib license is available online at http://quantlib.org/license.shtml. +// +// 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.using System; + +using System; +using System.Linq; +using QLNet; +using Xunit; +namespace TestSuite +{ + [Collection("QLNet CI Tests")] + public class T_OvernightIndexedCoupon + { + private class CommonVars + { + // cleanup + //SavedSettings backup; + //IndexHistoryCleaner cleaner; + + public Date today; + public double notional = 10000.0; + public OvernightIndex sofr; + public RelinkableHandle forecastCurve = new RelinkableHandle(); + + public OvernightIndexedCoupon makeCoupon(Date startDate, Date endDate) + { + return new OvernightIndexedCoupon(endDate, notional, startDate, endDate, sofr); + } + + public CommonVars() + { + today = new Date(23, Month.November, 2021); + + Settings.setEvaluationDate(today); + + sofr = new Sofr(forecastCurve); + + Date[] pastDates = + { + new Date(18, Month.October, 2021), new Date(19, Month.October, 2021), new Date(20, Month.October, 2021), + new Date(21, Month.October, 2021), new Date(22, Month.October, 2021), new Date(25, Month.October, 2021), + new Date(26, Month.October, 2021), new Date(27, Month.October, 2021), new Date(28, Month.October, 2021), + new Date(29, Month.October, 2021), new Date(1, Month.November, 2021), new Date(2, Month.November, 2021), + new Date(3, Month.November, 2021), new Date(4, Month.November, 2021), new Date(5, Month.November, 2021), + new Date(8, Month.November, 2021), new Date(9, Month.November, 2021), new Date(10, Month.November, 2021), + new Date(12, Month.November, 2021), new Date(15, Month.November, 2021), + new Date(16, Month.November, 2021), + new Date(17, Month.November, 2021), new Date(18, Month.November, 2021), + new Date(19, Month.November, 2021), + new Date(22, Month.November, 2021) + }; + + double[] pastRates = + { + 0.0008, 0.0009, 0.0008, + 0.0010, 0.0012, 0.0011, + 0.0013, 0.0012, 0.0012, + 0.0008, 0.0009, 0.0010, + 0.0011, 0.0014, 0.0013, + 0.0011, 0.0009, 0.0008, + 0.0007, 0.0008, 0.0008, + 0.0007, 0.0009, 0.0010, + 0.0009 + }; + + sofr.addFixings(pastDates.ToList(), pastRates.ToList()); + } + } + private void CHECK_OIS_COUPON_RESULT(string what, double calculated, double expected, double tolerance) + { + if (Math.Abs(calculated - expected) > tolerance) + { + QAssert.Fail("Failed to reproduce " + what + ":" + + "\n expected: " + expected + + "\n calculated: " + calculated + + "\n error: " + Math.Abs(calculated - expected)); + } + } + + [Fact] + public void testPastCouponRate() + { + // Testing rate for past overnight-indexed coupon + var vars = new CommonVars(); + + // coupon entirely in the past + var pastCoupon = vars.makeCoupon(new Date(18, Month.October, 2021), new Date(18, Month.November, 2021)); + + // expected values here and below come from manual calculations based on past dates and rates + double expectedRate = 0.000987136104; + double expectedAmount = vars.notional * expectedRate * 31.0 / 360; + CHECK_OIS_COUPON_RESULT("coupon rate", pastCoupon.rate(), expectedRate, 1e-12); + CHECK_OIS_COUPON_RESULT("coupon amount", pastCoupon.amount(), expectedAmount, 1e-8); + } + + [Fact] + public void testCurrentCouponRate() + { + // Testing rate for current overnight-indexed coupon + var vars = new CommonVars(); + vars.forecastCurve.linkTo(Utilities.flatRate(0.0010, new Actual360())); + + // coupon partly in the past, today not fixed + var currentCoupon = vars.makeCoupon(new Date(10, Month.November, 2021),new Date(10, Month.December, 2021)); + + double expectedRate = 0.000926701551; + double expectedAmount = vars.notional * expectedRate * 30.0 / 360; + CHECK_OIS_COUPON_RESULT("coupon rate", currentCoupon.rate(), expectedRate, 1e-12); + CHECK_OIS_COUPON_RESULT("coupon amount", currentCoupon.amount(), expectedAmount, 1e-8); + + // coupon partly in the past, today fixed + vars.sofr.addFixing(new Date(23, Month.November, 2021), 0.0007); + + expectedRate = 0.000916700760; + expectedAmount = vars.notional* expectedRate * 30.0/360; + CHECK_OIS_COUPON_RESULT("coupon rate", currentCoupon.rate(), expectedRate, 1e-12); + CHECK_OIS_COUPON_RESULT("coupon amount", currentCoupon.amount(), expectedAmount, 1e-8); + } + + [Fact] + public void testFutureCouponRate() + { + // Testing rate for future overnight-indexed coupon + var vars = new CommonVars(); + vars.forecastCurve.linkTo(Utilities.flatRate(0.0010, new Actual360())); + + // coupon entirely in the future + var futureCoupon = vars.makeCoupon(new Date(10, Month.December, 2021), new Date(10, Month.January, 2022)); + + double expectedRate = 0.001000043057; + double expectedAmount = vars.notional * expectedRate * 31.0 / 360; + CHECK_OIS_COUPON_RESULT("coupon rate", futureCoupon.rate(), expectedRate, 1e-12); + CHECK_OIS_COUPON_RESULT("coupon amount", futureCoupon.amount(), expectedAmount, 1e-8); + } + + [Fact] + public void testRateWhenTodayIsHoliday() + { + // Testing rate for overnight-indexed coupon when today is a holiday + var vars = new CommonVars(); + + Settings.setEvaluationDate(new Date(20, Month.November, 2021)); + vars.forecastCurve.linkTo(Utilities.flatRate(0.0010, new Actual360())); + + var coupon = vars.makeCoupon(new Date(10, Month.November, 2021), new Date(10, Month.December, 2021)); + + double expectedRate = 0.000930035180; + double expectedAmount = vars.notional * expectedRate * 30.0 / 360; + CHECK_OIS_COUPON_RESULT("coupon rate", coupon.rate(), expectedRate, 1e-12); + CHECK_OIS_COUPON_RESULT("coupon amount", coupon.amount(), expectedAmount, 1e-8); + } + + [Fact] + public void testAccruedAmountInThePast() + { + // Testing accrued amount in the past for overnight-indexed coupon + var vars = new CommonVars(); + + var coupon = vars.makeCoupon(new Date(18, Month.October, 2021), new Date(18, Month.January, 2022)); + + double expectedAmount = vars.notional * 0.000987136104 * 31.0 / 360; + CHECK_OIS_COUPON_RESULT("coupon amount", coupon.accruedAmount(new Date(18, Month.November, 2021)), expectedAmount, 1e-8); + } + + [Fact] + public void testAccruedAmountSpanningToday() + { + // Testing accrued amount spanning today for current overnight-indexed coupon + var vars = new CommonVars(); + + vars.forecastCurve.linkTo(Utilities.flatRate(0.0010, new Actual360())); + + // coupon partly in the past, today not fixed + + var coupon = vars.makeCoupon(new Date(10, Month.November, 2021), new Date(10, Month.January, 2022)); + + var expectedAmount = vars.notional * 0.000926701551 * 30.0 / 360; + CHECK_OIS_COUPON_RESULT("coupon amount", coupon.accruedAmount( new Date(10, Month.December, 2021)), expectedAmount, 1e-8); + + // coupon partly in the past, today fixed + vars.sofr.addFixing(new Date(23, Month.November, 2021), 0.0007); + + expectedAmount = vars.notional* 0.000916700760 * 30.0/360; + CHECK_OIS_COUPON_RESULT("coupon amount", coupon.accruedAmount(new Date(10, Month.December, 2021)), expectedAmount, 1e-8); + } + + [Fact] + public void testAccruedAmountInTheFuture() + { + // Testing accrued amount in the future for overnight-indexed coupon + var vars = new CommonVars(); + + vars.forecastCurve.linkTo(Utilities.flatRate(0.0010, new Actual360())); + + // coupon entirely in the future + + var coupon = vars.makeCoupon(new Date(10, Month.December, 2021), new Date(10, Month.March, 2022)); + + var accrualDate = new Date(10, Month.January, 2022); + var expectedRate = 0.001000043057; + var expectedAmount = vars.notional * expectedRate * 31.0 / 360; + CHECK_OIS_COUPON_RESULT("coupon amount", coupon.accruedAmount(accrualDate), expectedAmount, 1e-8); + } + + [Fact] + public void testAccruedAmountOnPastHoliday() + { + // Testing accrued amount on a past holiday for overnight-indexed coupon + var vars = new CommonVars(); + + // coupon entirely in the past + var coupon = vars.makeCoupon(new Date(18, Month.October, 2021), + new Date(18, Month.January, 2022)); + + var accrualDate = new Date(13, Month.November, 2021); + var expectedAmount = vars.notional * 0.000074724810; + CHECK_OIS_COUPON_RESULT("coupon amount", coupon.accruedAmount(accrualDate), expectedAmount, 1e-8); + } + + [Fact] + public void testAccruedAmountOnFutureHoliday() + { + // Testing accrued amount on a future holiday for overnight-indexed coupon + var vars = new CommonVars(); + + vars.forecastCurve.linkTo(Utilities.flatRate(0.0010, new Actual360())); + + // coupon entirely in the future + + var coupon = vars.makeCoupon(new Date(10, Month.December, 2021), + new Date(10, Month.March, 2022)); + + var accrualDate = new Date(15, Month.January, 2022); + var expectedAmount = vars.notional * 0.000100005012; + CHECK_OIS_COUPON_RESULT("coupon amount", coupon.accruedAmount(accrualDate), expectedAmount, 1e-8); + } + } +} diff --git a/tests/QLNet.Tests/T_SofrFutures.cs b/tests/QLNet.Tests/T_SofrFutures.cs new file mode 100644 index 00000000..17154ce9 --- /dev/null +++ b/tests/QLNet.Tests/T_SofrFutures.cs @@ -0,0 +1,104 @@ +// Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) +// +// This file is part of QLNet Project https://github.com/amaggiulli/qlnet +// QLNet is free software: you can redistribute it and/or modify it +// under the terms of the QLNet license. You should have received a +// copy of the license along with this program; if not, license is +// available at . +// +// QLNet is a based on QuantLib, a free-software/open-source library +// for financial quantitative analysts and developers - http://quantlib.org/ +// The QuantLib license is available online at http://quantlib.org/license.shtml. +// +// 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. +using System; +using System.Collections.Generic; +using TestSuite; +using Xunit; + +namespace QLNet.Tests +{ + [Collection("QLNet CI Tests")] + public class T_SofrFutures + { + private struct SofrQuotes + { + public Frequency freq { get; set; } + public Month month { get; set; } + public int year { get; set; } + public double price { get; set; } + public RateAveragingType averagingMethod { get; set; } + }; + + [Fact] + public void testBootstrap() + { + // Testing bootstrap over SOFR futures + + var today = new Date(26, Month.Oct, 2018); + Settings.setEvaluationDate(today); + + SofrQuotes[] sofrQuotes = { + new SofrQuotes{ freq = Frequency.Monthly, month = Month.Oct, year = 2018, price = 97.8175, averagingMethod = RateAveragingType.Simple}, + new SofrQuotes{ freq = Frequency.Monthly, month = Month.Nov, year = 2018, price = 97.770, averagingMethod = RateAveragingType.Simple}, + new SofrQuotes{ freq = Frequency.Monthly, month = Month.Dec, year = 2018, price = 97.685, averagingMethod = RateAveragingType.Simple}, + new SofrQuotes{ freq = Frequency.Monthly, month = Month.Jan, year = 2019, price = 97.595, averagingMethod = RateAveragingType.Simple}, + new SofrQuotes{ freq = Frequency.Monthly, month = Month.Feb, year = 2019, price = 97.590, averagingMethod = RateAveragingType.Simple}, + new SofrQuotes{ freq = Frequency.Monthly, month = Month.Mar, year = 2019, price = 97.525, averagingMethod = RateAveragingType.Simple}, + new SofrQuotes{ freq = Frequency.Quarterly, month = Month.Mar, year = 2019, price = 97.440, averagingMethod = RateAveragingType.Compound}, + new SofrQuotes{ freq = Frequency.Quarterly, month = Month.Jun, year = 2019, price = 97.295, averagingMethod = RateAveragingType.Compound}, + new SofrQuotes{ freq = Frequency.Quarterly, month = Month.Sep, year = 2019, price = 97.220, averagingMethod = RateAveragingType.Compound}, + new SofrQuotes{ freq = Frequency.Quarterly, month = Month.Dec, year = 2019, price = 97.170, averagingMethod = RateAveragingType.Compound}, + new SofrQuotes{ freq = Frequency.Quarterly, month = Month.Mar, year = 2020, price = 97.160, averagingMethod = RateAveragingType.Compound}, + new SofrQuotes{ freq = Frequency.Quarterly, month = Month.Jun, year = 2020, price = 97.165, averagingMethod = RateAveragingType.Compound}, + new SofrQuotes{ freq = Frequency.Quarterly, month = Month.Sep, year = 2020, price = 97.175, averagingMethod = RateAveragingType.Compound}, + }; + + OvernightIndex index = new Sofr(); + index.addFixing(new Date(1, Month.October, 2018), 0.0222); + index.addFixing(new Date(2, Month.October, 2018), 0.022); + index.addFixing(new Date(3, Month.October, 2018), 0.022); + index.addFixing(new Date(4, Month.October, 2018), 0.0218); + index.addFixing(new Date(5, Month.October, 2018), 0.0216); + index.addFixing(new Date(9, Month.October, 2018), 0.0215); + index.addFixing(new Date(10, Month.October, 2018), 0.0215); + index.addFixing(new Date(11, Month.October, 2018), 0.0217); + index.addFixing(new Date(12, Month.October, 2018), 0.0218); + index.addFixing(new Date(15, Month.October, 2018), 0.0221); + index.addFixing(new Date(16, Month.October, 2018), 0.0218); + index.addFixing(new Date(17, Month.October, 2018), 0.0218); + index.addFixing(new Date(18, Month.October, 2018), 0.0219); + index.addFixing(new Date(19, Month.October, 2018), 0.0219); + index.addFixing(new Date(22, Month.October, 2018), 0.0218); + index.addFixing(new Date(23, Month.October, 2018), 0.0217); + index.addFixing(new Date(24, Month.October, 2018), 0.0218); + index.addFixing(new Date(25, Month.October, 2018), 0.0219); + + List helpers = new List(); + foreach (var sofrQuote in sofrQuotes) { + helpers.Add(new SofrFutureRateHelper(sofrQuote.price, sofrQuote.month, sofrQuote.year, sofrQuote.freq)); + } + + var curve = new PiecewiseYieldCurve(today, helpers,new Actual365Fixed()); + + // test curve with one of the futures + OvernightIndex sofr = new Sofr(new Handle(curve)); + OvernightIndexFuture sf = new OvernightIndexFuture(sofr, new Date(20, Month.March, 2019), new Date(19, Month.June, 2019)); + + double expected_price = 97.44; + double tolerance = 1.0e-9; + + double error = Math.Abs(sf.NPV() - expected_price); + if (error > tolerance) + { + QAssert.Fail("sample futures:\n" + + "\n estimated price: " + sf.NPV() + + "\n expected price: " + expected_price + + "\n error: " + error + + "\n tolerance: " + tolerance); + } + } + } +} From bcb57944fe3aeb77ae24336eed5f3e8cd8c5aed0 Mon Sep 17 00:00:00 2001 From: Andrea Maggiulli Date: Tue, 12 Jul 2022 17:10:20 +0200 Subject: [PATCH 10/35] Fixing tests. --- tests/QLNet.Tests/T_OvernightIndexedCoupon.cs | 22 ++++++++++++++----- tests/QLNet.Tests/T_SofrFutures.cs | 4 ++-- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/tests/QLNet.Tests/T_OvernightIndexedCoupon.cs b/tests/QLNet.Tests/T_OvernightIndexedCoupon.cs index 814e0223..4510c495 100644 --- a/tests/QLNet.Tests/T_OvernightIndexedCoupon.cs +++ b/tests/QLNet.Tests/T_OvernightIndexedCoupon.cs @@ -21,14 +21,26 @@ namespace TestSuite { [Collection("QLNet CI Tests")] - public class T_OvernightIndexedCoupon + public class T_OvernightIndexedCoupon : IDisposable { - private class CommonVars + private SavedSettings backup; + private IndexHistoryCleaner cleaner; + + public T_OvernightIndexedCoupon() { - // cleanup - //SavedSettings backup; - //IndexHistoryCleaner cleaner; + backup = new SavedSettings(); + cleaner = new IndexHistoryCleaner(); + } + + public void Dispose() + { + backup.Dispose(); + cleaner.Dispose(); + } + + private class CommonVars + { public Date today; public double notional = 10000.0; public OvernightIndex sofr; diff --git a/tests/QLNet.Tests/T_SofrFutures.cs b/tests/QLNet.Tests/T_SofrFutures.cs index 17154ce9..0d3df903 100644 --- a/tests/QLNet.Tests/T_SofrFutures.cs +++ b/tests/QLNet.Tests/T_SofrFutures.cs @@ -15,10 +15,10 @@ // FOR A PARTICULAR PURPOSE. See the license for more details. using System; using System.Collections.Generic; -using TestSuite; +using QLNet; using Xunit; -namespace QLNet.Tests +namespace TestSuite { [Collection("QLNet CI Tests")] public class T_SofrFutures From 29c5ef1225a157859091262963c065da232989c6 Mon Sep 17 00:00:00 2001 From: Andrea Maggiulli Date: Tue, 12 Jul 2022 17:46:53 +0200 Subject: [PATCH 11/35] Updated to .NET 6.0 --- src/QLNet/Cashflows/OvernightIndexedCoupon.cs | 2 +- src/QLNet/QLNet.csproj | 6 ++---- tests/QLNet.Tests/QLNet.Tests.csproj | 2 +- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/QLNet/Cashflows/OvernightIndexedCoupon.cs b/src/QLNet/Cashflows/OvernightIndexedCoupon.cs index 4e97a90b..b0f10197 100644 --- a/src/QLNet/Cashflows/OvernightIndexedCoupon.cs +++ b/src/QLNet/Cashflows/OvernightIndexedCoupon.cs @@ -76,7 +76,7 @@ public double averageRate(Date date) ; // fall through and forecast } } - catch (Exception _) + catch (Exception) { ; // fall through and forecast } diff --git a/src/QLNet/QLNet.csproj b/src/QLNet/QLNet.csproj index 914e6b26..7bd77e94 100644 --- a/src/QLNet/QLNet.csproj +++ b/src/QLNet/QLNet.csproj @@ -2,7 +2,8 @@ 1.12.0 - netstandard2.1 + net6.0 + latest $(DefineConstants);QL_NEGATIVE_RATES QLNet QLNet @@ -17,8 +18,5 @@ False - - - diff --git a/tests/QLNet.Tests/QLNet.Tests.csproj b/tests/QLNet.Tests/QLNet.Tests.csproj index d1ac2178..53be817c 100644 --- a/tests/QLNet.Tests/QLNet.Tests.csproj +++ b/tests/QLNet.Tests/QLNet.Tests.csproj @@ -1,7 +1,7 @@  - net5.0 + net6.0 latest false From cc974824a0e73df122c04403e6c9d2945e975df1 Mon Sep 17 00:00:00 2001 From: Andrea Maggiulli Date: Tue, 12 Jul 2022 17:51:49 +0200 Subject: [PATCH 12/35] Updated appveyor.yml --- appveyor.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 942bd139..094b680d 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -9,7 +9,7 @@ version: 1.12.1 configuration: Release platform: Any CPU - image: Visual Studio 2019 + image: Visual Studio 2022 test: off skip_tags: true pull_requests: @@ -21,7 +21,7 @@ after_build: - cmd: dotnet pack src/qlnet/qlnet.csproj -c Release -o ./ artifacts: - - path: src\QLNet\bin\Release\netstandard2.1\QLNet.dll + - path: src\QLNet\bin\Release\net6.0\QLNet.dll name: Windows - path: .\*.nupkg name: ng @@ -32,7 +32,7 @@ description: QLNet 1.12.1 auth_token: secure: yPDUXqBIfD2IB3l3KMeERPZjvD7jFC7spFIkbE92e1uWcJ4j3XSei/a966C5Aa+M - artifact: src\QLNet\bin\Release\netstandard2.1\QLNet.dll + artifact: src\QLNet\bin\Release\net6.0\QLNet.dll draft: false force_update: false - provider: NuGet @@ -50,7 +50,7 @@ version: 1.12.1-preview.{build} configuration: Release platform: Any CPU - image: Visual Studio 2019 + image: Visual Studio 2022 skip_tags: true clone_folder: c:\projects\qlnet_develop pull_requests: @@ -99,7 +99,7 @@ version: 1.12.1-{build} configuration: Release platform: Any CPU - image: Visual Studio 2019 + image: Visual Studio 2022 skip_tags: true clone_folder: c:\projects\qlnet_feature init: From dcb271097e5cc3ad5674791a1772f45aef22d103 Mon Sep 17 00:00:00 2001 From: Andrea Maggiulli Date: Tue, 12 Jul 2022 18:19:09 +0200 Subject: [PATCH 13/35] Fixing AppVeyor Java Home --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 094b680d..1a4ca9fe 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -58,7 +58,7 @@ nuget: disable_publish_on_pr: true init: - - cmd: set JAVA_HOME=C:\Program Files\Java\jdk11 + #- cmd: set JAVA_HOME=C:\Program Files\Java\jdk11 before_build: - cmd: "dotnet tool install --global dotnet-sonarscanner" - cmd: "SET runner_args=\"\"" From dbe969b82ba11d13bdf325e3d7259786bf4bb252 Mon Sep 17 00:00:00 2001 From: Andrea Maggiulli Date: Tue, 12 Jul 2022 18:45:39 +0200 Subject: [PATCH 14/35] Fixing AppVeyor Java Home --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 1a4ca9fe..9c7dae78 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -58,7 +58,7 @@ nuget: disable_publish_on_pr: true init: - #- cmd: set JAVA_HOME=C:\Program Files\Java\jdk11 + - cmd: set JAVA_HOME=C:\Program Files\Java\jdk17 before_build: - cmd: "dotnet tool install --global dotnet-sonarscanner" - cmd: "SET runner_args=\"\"" From cd4d7fbbc06134ce6265ae0d09917079013c6fff Mon Sep 17 00:00:00 2001 From: Andrea Maggiulli Date: Fri, 26 Aug 2022 20:02:00 +0200 Subject: [PATCH 15/35] Added batch calculations (#272) * Added batch yield calculation with benchmark tests. * Added batch Weighted Average Life calculation with benchmark test. * Added batch Duration and Accrued calculation with benchmark test. --- .../Pricingengines/Bond/BondFunctions.cs | 64 +++++++++++- src/QLNet/Requests/AccruedRequest.cs | 27 +++++ src/QLNet/Requests/DurationRequest.cs | 32 ++++++ src/QLNet/Requests/WalRequest.cs | 31 ++++++ src/QLNet/Requests/YieldRequest.cs | 34 +++++++ src/QLNet/Responses/AccruedResponse.cs | 27 +++++ src/QLNet/Responses/DurationResponse.cs | 26 +++++ src/QLNet/Responses/WalResponse.cs | 28 ++++++ src/QLNet/Responses/YieldResponse.cs | 26 +++++ .../QLNet.Tests/Fakers/AccruedRequestFaker.cs | 42 ++++++++ .../Fakers/DurationRequestFaker.cs | 47 +++++++++ .../QLNet.Tests/Fakers/FixedRateBondFaker.cs | 34 +++++++ tests/QLNet.Tests/Fakers/ScheduleFaker.cs | 38 +++++++ tests/QLNet.Tests/Fakers/WalRequestFaker.cs | 44 +++++++++ tests/QLNet.Tests/Fakers/YieldRequestFaker.cs | 48 +++++++++ tests/QLNet.Tests/QLNet.Tests.csproj | 1 + tests/QLNet.Tests/T_Batch.cs | 99 +++++++++++++++++++ 17 files changed, 646 insertions(+), 2 deletions(-) create mode 100644 src/QLNet/Requests/AccruedRequest.cs create mode 100644 src/QLNet/Requests/DurationRequest.cs create mode 100644 src/QLNet/Requests/WalRequest.cs create mode 100644 src/QLNet/Requests/YieldRequest.cs create mode 100644 src/QLNet/Responses/AccruedResponse.cs create mode 100644 src/QLNet/Responses/DurationResponse.cs create mode 100644 src/QLNet/Responses/WalResponse.cs create mode 100644 src/QLNet/Responses/YieldResponse.cs create mode 100644 tests/QLNet.Tests/Fakers/AccruedRequestFaker.cs create mode 100644 tests/QLNet.Tests/Fakers/DurationRequestFaker.cs create mode 100644 tests/QLNet.Tests/Fakers/FixedRateBondFaker.cs create mode 100644 tests/QLNet.Tests/Fakers/ScheduleFaker.cs create mode 100644 tests/QLNet.Tests/Fakers/WalRequestFaker.cs create mode 100644 tests/QLNet.Tests/Fakers/YieldRequestFaker.cs create mode 100644 tests/QLNet.Tests/T_Batch.cs diff --git a/src/QLNet/Pricingengines/Bond/BondFunctions.cs b/src/QLNet/Pricingengines/Bond/BondFunctions.cs index 9da14be4..539744f0 100644 --- a/src/QLNet/Pricingengines/Bond/BondFunctions.cs +++ b/src/QLNet/Pricingengines/Bond/BondFunctions.cs @@ -20,6 +20,7 @@ under the terms of the QLNet license. You should have received a using System; using System.Collections.Generic; using System.Linq; +using System.Threading.Tasks; namespace QLNet { @@ -33,6 +34,7 @@ These adapters calls into CashFlows functions passing as input the Prices are always clean, as per market convention. */ + public class BondFunctions { #region Date inspectors @@ -202,6 +204,31 @@ public static double accruedPeriod(Bond bond, Date settlementDate = null) return CashFlows.accruedPeriod(bond.cashflows(), false, settlementDate); } + + public static async Task calculateAccruedDaysAndAmountAsync(AccruedRequest[] accruedRequest) + { + var dictOfTasks = accruedRequest.ToDictionary(request => request.Id, + request => Task.Run(() => accruedDaysAndAmount(request.Bond, request.SettlementDate))); + + await Task.WhenAll(dictOfTasks.Values); + + return dictOfTasks.Select(task => new AccruedResponse { Id = task.Key, Days = task.Value.Result.accruedDays, + Amount = task.Value.Result.accruedAmount}).ToArray(); + } + + public static (int accruedDays, double accruedAmount) accruedDaysAndAmount(Bond bond, Date settlementDate = null) + { + if (settlementDate == null) + settlementDate = bond.settlementDate(); + + Utils.QL_REQUIRE(BondFunctions.isTradable(bond, settlementDate), () => + "non tradable at " + settlementDate + + " (maturity being " + bond.maturityDate() + ")", + QLNetExceptionEnum.NotTradableException); + + return CashFlows.accruedDaysAndAmount(bond.cashflows(), false, settlementDate); + } + public static double accruedDays(Bond bond, Date settlementDate = null) { if (settlementDate == null) @@ -325,6 +352,18 @@ public static double bps(Bond bond, double yield, DayCounter dayCounter, Compoun { return bps(bond, new InterestRate(yield, dayCounter, compounding, frequency), settlementDate); } + + public static async Task calculateYieldsAsync(YieldRequest[] yieldRequest) + { + var dictOfTasks = yieldRequest.ToDictionary(request => request.Id, + request => Task.Run(() => yield(request.Bond, request.CleanPrice, request.DayCounter, request.Compounding, request.Frequency, + request.SettlementDate ?? null, request.Accuracy ?? 1.0e-10, request.MaxIterations ?? 100, request.Guess ?? 0.05))); + + await Task.WhenAll(dictOfTasks.Values); + + return dictOfTasks.Select(task => new YieldResponse { Id = task.Key, Yield = task.Value.Result }).ToArray(); + } + public static double yield(Bond bond, double cleanPrice, DayCounter dayCounter, Compounding compounding, Frequency frequency, Date settlementDate = null, double accuracy = 1.0e-10, int maxIterations = 100, double guess = 0.05) { @@ -357,11 +396,24 @@ public static double duration(Bond bond, InterestRate yield, Duration.Type type return CashFlows.duration(bond.cashflows(), yield, type, false, settlementDate); } + public static double duration(Bond bond, double yield, DayCounter dayCounter, Compounding compounding, Frequency frequency, Duration.Type type = Duration.Type.Modified, Date settlementDate = null) { return duration(bond, new InterestRate(yield, dayCounter, compounding, frequency), type, settlementDate); } + + public static async Task calculateDurationAsync(DurationRequest[] durationRequest) + { + var dictOfTasks = durationRequest.ToDictionary(request => request.Id, + request => Task.Run(() => duration(request.Bond, request.Yield, request.DayCounter, request.Compounding,request.Frequency, + request.Type, request.SettlementDate))); + + await Task.WhenAll(dictOfTasks.Values); + + return dictOfTasks.Select(task => new DurationResponse { Id = task.Key, Duration = task.Value.Result }).ToArray(); + } + public static double convexity(Bond bond, InterestRate yield, Date settlementDate = null) { if (settlementDate == null) @@ -460,6 +512,16 @@ public static double zSpread(Bond bond, double cleanPrice, YieldTermStructure di #region Raw Functions + public static async Task calculateWalAsync(WalRequest[] walRequest) + { + var dictOfTasks = walRequest.ToDictionary(request => request.Id, + request => Task.Run(() => WeightedAverageLife(request.Today, request.Amounts, request.Schedule))); + + await Task.WhenAll(dictOfTasks.Values); + + return dictOfTasks.Select(task => new WalResponse { Id = task.Key, Wal = task.Value.Result }).ToArray(); + } + public static DateTime WeightedAverageLife(DateTime today, List amounts, List schedule) { Utils.QL_REQUIRE(amounts.Count == schedule.Count, () => "Amount list is incompatible with schedule"); @@ -484,8 +546,6 @@ public static DateTime WeightedAverageLife(DateTime today, List amounts, return today.AddDays(wal * 365).Date; } - #endregion - } } diff --git a/src/QLNet/Requests/AccruedRequest.cs b/src/QLNet/Requests/AccruedRequest.cs new file mode 100644 index 00000000..fdf78877 --- /dev/null +++ b/src/QLNet/Requests/AccruedRequest.cs @@ -0,0 +1,27 @@ +/* + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) + + This file is part of QLNet Project https://github.com/amaggiulli/qlnet + + QLNet is free software: you can redistribute it and/or modify it + under the terms of the QLNet license. You should have received a + copy of the license along with this program; if not, license is + available at . + + QLNet is a based on QuantLib, a free-software/open-source library + for financial quantitative analysts and developers - http://quantlib.org/ + The QuantLib license is available online at http://quantlib.org/license.shtml. + + 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. +*/ +namespace QLNet +{ + public class AccruedRequest + { + public int Id { get; set; } + public Bond Bond { get; set; } + public Date SettlementDate { get; set; } + } +} diff --git a/src/QLNet/Requests/DurationRequest.cs b/src/QLNet/Requests/DurationRequest.cs new file mode 100644 index 00000000..786ee103 --- /dev/null +++ b/src/QLNet/Requests/DurationRequest.cs @@ -0,0 +1,32 @@ +/* + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) + + This file is part of QLNet Project https://github.com/amaggiulli/qlnet + + QLNet is free software: you can redistribute it and/or modify it + under the terms of the QLNet license. You should have received a + copy of the license along with this program; if not, license is + available at . + + QLNet is a based on QuantLib, a free-software/open-source library + for financial quantitative analysts and developers - http://quantlib.org/ + The QuantLib license is available online at http://quantlib.org/license.shtml. + + 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. +*/ +namespace QLNet +{ + public class DurationRequest + { + public int Id { get; set; } + public Bond Bond { get; set; } + public double Yield { get; set; } + public DayCounter DayCounter { get; set; } + public Compounding Compounding { get; set; } + public Frequency Frequency { get; set; } + public Duration.Type Type { get; set; } + public Date SettlementDate { get; set; } + } +} diff --git a/src/QLNet/Requests/WalRequest.cs b/src/QLNet/Requests/WalRequest.cs new file mode 100644 index 00000000..112f4ec9 --- /dev/null +++ b/src/QLNet/Requests/WalRequest.cs @@ -0,0 +1,31 @@ +/* + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) + + This file is part of QLNet Project https://github.com/amaggiulli/qlnet + + QLNet is free software: you can redistribute it and/or modify it + under the terms of the QLNet license. You should have received a + copy of the license along with this program; if not, license is + available at . + + QLNet is a based on QuantLib, a free-software/open-source library + for financial quantitative analysts and developers - http://quantlib.org/ + The QuantLib license is available online at http://quantlib.org/license.shtml. + + 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. +*/ +using System; +using System.Collections.Generic; + +namespace QLNet +{ + public class WalRequest + { + public int Id { get; set; } + public DateTime Today { get; set; } + public List Amounts { get; set; } + public List Schedule { get; set; } + } +} diff --git a/src/QLNet/Requests/YieldRequest.cs b/src/QLNet/Requests/YieldRequest.cs new file mode 100644 index 00000000..95afe682 --- /dev/null +++ b/src/QLNet/Requests/YieldRequest.cs @@ -0,0 +1,34 @@ +/* + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) + + This file is part of QLNet Project https://github.com/amaggiulli/qlnet + + QLNet is free software: you can redistribute it and/or modify it + under the terms of the QLNet license. You should have received a + copy of the license along with this program; if not, license is + available at . + + QLNet is a based on QuantLib, a free-software/open-source library + for financial quantitative analysts and developers - http://quantlib.org/ + The QuantLib license is available online at http://quantlib.org/license.shtml. + + 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. +*/ +namespace QLNet +{ + public class YieldRequest + { + public int Id { get; set; } + public Bond Bond { get; set; } + public double CleanPrice { get; set; } + public DayCounter DayCounter { get; set; } + public Compounding Compounding { get; set; } + public Frequency Frequency { get; set; } + public Date SettlementDate { get; set; } + public double? Accuracy { get; set; } + public int? MaxIterations { get; set; } + public double? Guess { get; set; } + } +} diff --git a/src/QLNet/Responses/AccruedResponse.cs b/src/QLNet/Responses/AccruedResponse.cs new file mode 100644 index 00000000..9c691cd7 --- /dev/null +++ b/src/QLNet/Responses/AccruedResponse.cs @@ -0,0 +1,27 @@ +/* + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) + + This file is part of QLNet Project https://github.com/amaggiulli/qlnet + + QLNet is free software: you can redistribute it and/or modify it + under the terms of the QLNet license. You should have received a + copy of the license along with this program; if not, license is + available at . + + QLNet is a based on QuantLib, a free-software/open-source library + for financial quantitative analysts and developers - http://quantlib.org/ + The QuantLib license is available online at http://quantlib.org/license.shtml. + + 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. +*/ +namespace QLNet +{ + public class AccruedResponse + { + public int Id { get; set; } + public int Days { get; set; } + public double Amount { get; set; } + } +} diff --git a/src/QLNet/Responses/DurationResponse.cs b/src/QLNet/Responses/DurationResponse.cs new file mode 100644 index 00000000..c4b7c9e7 --- /dev/null +++ b/src/QLNet/Responses/DurationResponse.cs @@ -0,0 +1,26 @@ +/* + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) + + This file is part of QLNet Project https://github.com/amaggiulli/qlnet + + QLNet is free software: you can redistribute it and/or modify it + under the terms of the QLNet license. You should have received a + copy of the license along with this program; if not, license is + available at . + + QLNet is a based on QuantLib, a free-software/open-source library + for financial quantitative analysts and developers - http://quantlib.org/ + The QuantLib license is available online at http://quantlib.org/license.shtml. + + 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. +*/ +namespace QLNet +{ + public class DurationResponse + { + public int Id { get; set; } + public double Duration { get; set; } + } +} diff --git a/src/QLNet/Responses/WalResponse.cs b/src/QLNet/Responses/WalResponse.cs new file mode 100644 index 00000000..b6d38c56 --- /dev/null +++ b/src/QLNet/Responses/WalResponse.cs @@ -0,0 +1,28 @@ +/* + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) + + This file is part of QLNet Project https://github.com/amaggiulli/qlnet + + QLNet is free software: you can redistribute it and/or modify it + under the terms of the QLNet license. You should have received a + copy of the license along with this program; if not, license is + available at . + + QLNet is a based on QuantLib, a free-software/open-source library + for financial quantitative analysts and developers - http://quantlib.org/ + The QuantLib license is available online at http://quantlib.org/license.shtml. + + 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. +*/ +using System; + +namespace QLNet +{ + public class WalResponse + { + public int Id { get; set; } + public DateTime Wal { get; set; } + } +} diff --git a/src/QLNet/Responses/YieldResponse.cs b/src/QLNet/Responses/YieldResponse.cs new file mode 100644 index 00000000..176edbd4 --- /dev/null +++ b/src/QLNet/Responses/YieldResponse.cs @@ -0,0 +1,26 @@ +/* + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) + + This file is part of QLNet Project https://github.com/amaggiulli/qlnet + + QLNet is free software: you can redistribute it and/or modify it + under the terms of the QLNet license. You should have received a + copy of the license along with this program; if not, license is + available at . + + QLNet is a based on QuantLib, a free-software/open-source library + for financial quantitative analysts and developers - http://quantlib.org/ + The QuantLib license is available online at http://quantlib.org/license.shtml. + + 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. +*/ +namespace QLNet +{ + public class YieldResponse + { + public int Id { get; set; } + public double Yield { get; set; } + } +} diff --git a/tests/QLNet.Tests/Fakers/AccruedRequestFaker.cs b/tests/QLNet.Tests/Fakers/AccruedRequestFaker.cs new file mode 100644 index 00000000..6a2baa2d --- /dev/null +++ b/tests/QLNet.Tests/Fakers/AccruedRequestFaker.cs @@ -0,0 +1,42 @@ +/* + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) + + This file is part of QLNet Project https://github.com/amaggiulli/qlnet + + QLNet is free software: you can redistribute it and/or modify it + under the terms of the QLNet license. You should have received a + copy of the license along with this program; if not, license is + available at . + + QLNet is a based on QuantLib, a free-software/open-source library + for financial quantitative analysts and developers - http://quantlib.org/ + The QuantLib license is available online at http://quantlib.org/license.shtml. + + 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. +*/ +using System.Collections.Generic; +using AutoBogus; + +namespace QLNet.Tests.Fakers +{ + internal class AccruedRequestFaker + { + public static AccruedRequest[] CreateAccruedRequest(int count = 1) + { + var list = new List(); + + for (var i = 0; i < count; i++) + { + list.Add(new AutoFaker() + .RuleFor(x => x.Id, i) + .RuleFor(x => x.Bond, FixedRateBondFaker.CreateFixedRateBond) + .RuleFor(x => x.SettlementDate, x => new Date(x.Date.Future())) + .Generate()); + } + + return list.ToArray(); + } + } +} diff --git a/tests/QLNet.Tests/Fakers/DurationRequestFaker.cs b/tests/QLNet.Tests/Fakers/DurationRequestFaker.cs new file mode 100644 index 00000000..c70c72bb --- /dev/null +++ b/tests/QLNet.Tests/Fakers/DurationRequestFaker.cs @@ -0,0 +1,47 @@ +/* + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) + + This file is part of QLNet Project https://github.com/amaggiulli/qlnet + + QLNet is free software: you can redistribute it and/or modify it + under the terms of the QLNet license. You should have received a + copy of the license along with this program; if not, license is + available at . + + QLNet is a based on QuantLib, a free-software/open-source library + for financial quantitative analysts and developers - http://quantlib.org/ + The QuantLib license is available online at http://quantlib.org/license.shtml. + + 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. +*/ + +using System.Collections.Generic; +using AutoBogus; + +namespace QLNet.Tests.Fakers +{ + public class DurationRequestFaker + { + public static DurationRequest[] CreateDurationRequest(int count = 1) + { + var list = new List(); + + for (var i = 0; i < count; i++) + { + list.Add(new AutoFaker() + .RuleFor(x => x.Id, i) + .RuleFor(x => x.Bond, FixedRateBondFaker.CreateFixedRateBond) + .RuleFor(x => x.Yield, x => x.Random.Double(0, 100)) + .RuleFor(x => x.DayCounter, x => new Thirty360(Thirty360.Thirty360Convention.BondBasis)) + .RuleFor(x => x.Compounding, x => Compounding.Compounded) + .RuleFor(x => x.Frequency, x => Frequency.Semiannual) + .RuleFor(x => x.SettlementDate, x => null) + .Generate()); + } + + return list.ToArray(); + } + } +} diff --git a/tests/QLNet.Tests/Fakers/FixedRateBondFaker.cs b/tests/QLNet.Tests/Fakers/FixedRateBondFaker.cs new file mode 100644 index 00000000..b8d9938a --- /dev/null +++ b/tests/QLNet.Tests/Fakers/FixedRateBondFaker.cs @@ -0,0 +1,34 @@ +/* + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) + + This file is part of QLNet Project https://github.com/amaggiulli/qlnet + + QLNet is free software: you can redistribute it and/or modify it + under the terms of the QLNet license. You should have received a + copy of the license along with this program; if not, license is + available at . + + QLNet is a based on QuantLib, a free-software/open-source library + for financial quantitative analysts and developers - http://quantlib.org/ + The QuantLib license is available online at http://quantlib.org/license.shtml. + + 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. +*/ +using Bogus; + +namespace QLNet.Tests.Fakers +{ + public class FixedRateBondFaker + { + public static FixedRateBond CreateFixedRateBond() + { + var faker = new Faker() + .CustomInstantiator(f => new FixedRateBond(0, 1000, ScheduleFaker.CreateSchedule(), new InitializedList(1, f.Random.Double(0.1)), + new Thirty360(Thirty360.Thirty360Convention.BondBasis),BusinessDayConvention.Unadjusted)); + + return faker.Generate(); + } + } +} diff --git a/tests/QLNet.Tests/Fakers/ScheduleFaker.cs b/tests/QLNet.Tests/Fakers/ScheduleFaker.cs new file mode 100644 index 00000000..386a3e38 --- /dev/null +++ b/tests/QLNet.Tests/Fakers/ScheduleFaker.cs @@ -0,0 +1,38 @@ +/* + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) + + This file is part of QLNet Project https://github.com/amaggiulli/qlnet + + QLNet is free software: you can redistribute it and/or modify it + under the terms of the QLNet license. You should have received a + copy of the license along with this program; if not, license is + available at . + + QLNet is a based on QuantLib, a free-software/open-source library + for financial quantitative analysts and developers - http://quantlib.org/ + The QuantLib license is available online at http://quantlib.org/license.shtml. + + 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. +*/ +using System; +using Bogus; + +namespace QLNet.Tests.Fakers +{ + public class ScheduleFaker + { + public static Schedule CreateSchedule() + { + var faker = new Faker() + .CustomInstantiator(f => new Schedule(DateTime.Now, DateTime.Now.AddYears(f.Random.Int(1,30)), + new Period(Frequency.Semiannual), new TARGET(), + BusinessDayConvention.Unadjusted, BusinessDayConvention.Unadjusted, DateGeneration.Rule.Backward, + false)); + + + return faker.Generate(); + } + } +} diff --git a/tests/QLNet.Tests/Fakers/WalRequestFaker.cs b/tests/QLNet.Tests/Fakers/WalRequestFaker.cs new file mode 100644 index 00000000..0d4978c4 --- /dev/null +++ b/tests/QLNet.Tests/Fakers/WalRequestFaker.cs @@ -0,0 +1,44 @@ +/* + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) + + This file is part of QLNet Project https://github.com/amaggiulli/qlnet + + QLNet is free software: you can redistribute it and/or modify it + under the terms of the QLNet license. You should have received a + copy of the license along with this program; if not, license is + available at . + + QLNet is a based on QuantLib, a free-software/open-source library + for financial quantitative analysts and developers - http://quantlib.org/ + The QuantLib license is available online at http://quantlib.org/license.shtml. + + 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. +*/ +using System; +using System.Collections.Generic; +using AutoBogus; + +namespace QLNet.Tests.Fakers +{ + public class WalRequestFaker + { + public static WalRequest[] CreateWalRequest(int count = 1) + { + var list = new List(); + + for (var i = 0; i < count; i++) + { + list.Add(new AutoFaker() + .RuleFor(x => x.Id, i) + .RuleFor(x => x.Today, DateTime.Today) + .RuleFor(x => x.Amounts, f => f.Make(10, () => f.Random.Double(0,100))) + .RuleFor(x => x.Schedule, f => f.Make(10, () => f.Date.Between(DateTime.Today, DateTime.Today.AddYears(30)))) + .Generate()); + } + + return list.ToArray(); + } + } +} diff --git a/tests/QLNet.Tests/Fakers/YieldRequestFaker.cs b/tests/QLNet.Tests/Fakers/YieldRequestFaker.cs new file mode 100644 index 00000000..c8087c24 --- /dev/null +++ b/tests/QLNet.Tests/Fakers/YieldRequestFaker.cs @@ -0,0 +1,48 @@ +/* + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) + + This file is part of QLNet Project https://github.com/amaggiulli/qlnet + + QLNet is free software: you can redistribute it and/or modify it + under the terms of the QLNet license. You should have received a + copy of the license along with this program; if not, license is + available at . + + QLNet is a based on QuantLib, a free-software/open-source library + for financial quantitative analysts and developers - http://quantlib.org/ + The QuantLib license is available online at http://quantlib.org/license.shtml. + + 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. +*/ +using System.Collections.Generic; +using AutoBogus; + +namespace QLNet.Tests.Fakers +{ + public class YieldRequestFaker + { + public static YieldRequest[] CreateYieldRequest(int count = 1) + { + var list = new List(); + + for (var i = 0; i < count; i++) + { + list.Add(new AutoFaker() + .RuleFor(x => x.Id, i) + .RuleFor(x => x.Bond, FixedRateBondFaker.CreateFixedRateBond) + .RuleFor(x => x.CleanPrice, x => x.Random.Double(0, 100)) + .RuleFor(x => x.DayCounter, x => new Thirty360(Thirty360.Thirty360Convention.BondBasis)) + .RuleFor(x => x.Compounding, x => Compounding.Compounded) + .RuleFor(x => x.Frequency, x => Frequency.Semiannual) + .RuleFor(x => x.SettlementDate, x => null) + .RuleFor(x => x.Accuracy, x => null) + .RuleFor(x => x.MaxIterations, x => null) + .RuleFor(x => x.Guess, x => null).Generate()); + } + + return list.ToArray(); + } + } +} diff --git a/tests/QLNet.Tests/QLNet.Tests.csproj b/tests/QLNet.Tests/QLNet.Tests.csproj index 53be817c..0cc5a0fc 100644 --- a/tests/QLNet.Tests/QLNet.Tests.csproj +++ b/tests/QLNet.Tests/QLNet.Tests.csproj @@ -15,6 +15,7 @@ + diff --git a/tests/QLNet.Tests/T_Batch.cs b/tests/QLNet.Tests/T_Batch.cs new file mode 100644 index 00000000..5675c3cd --- /dev/null +++ b/tests/QLNet.Tests/T_Batch.cs @@ -0,0 +1,99 @@ +/* + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) + + This file is part of QLNet Project https://github.com/amaggiulli/qlnet + + QLNet is free software: you can redistribute it and/or modify it + under the terms of the QLNet license. You should have received a + copy of the license along with this program; if not, license is + available at . + + QLNet is a based on QuantLib, a free-software/open-source library + for financial quantitative analysts and developers - http://quantlib.org/ + The QuantLib license is available online at http://quantlib.org/license.shtml. + + 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. +*/ + +using System; +using System.Diagnostics; +using System.Threading.Tasks; +using QLNet; +using QLNet.Tests.Fakers; +using Xunit; +using Xunit.Abstractions; + +namespace TestSuite +{ + [Collection("QLNet CI Tests")] + public class T_Batch + { + private readonly ITestOutputHelper output; + + public T_Batch(ITestOutputHelper output) + { + this.output = output; + } + + [Fact] + public async Task testBatchYieldCalculation() + { + const int size = 2; // increase size to benchmark yield calculation + var request = YieldRequestFaker.CreateYieldRequest(size); + + var stopwatch = Stopwatch.StartNew(); + var response = await BondFunctions.calculateYieldsAsync(request); + output.WriteLine($"Calculated {size} yields in {stopwatch.ElapsedMilliseconds}ms"); + + Assert.NotEmpty(response); + Assert.Equal(size, response.Length); + Assert.All(response, item => Assert.NotEqual(0,item.Yield)); + } + + [Fact] + public async Task testBatchWalCalculation() + { + const int size = 2; // increase size to benchmark wal calculation + var request = WalRequestFaker.CreateWalRequest(size); + + var stopwatch = Stopwatch.StartNew(); + var response = await BondFunctions.calculateWalAsync(request); + output.WriteLine($"Calculated {size} wal in {stopwatch.ElapsedMilliseconds}ms"); + + Assert.NotEmpty(response); + Assert.Equal(size, response.Length); + Assert.All(response, item => Assert.NotEqual(DateTime.MinValue, item.Wal)); + } + + [Fact] + public async Task testBatchDurationCalculation() + { + const int size = 2; // increase size to benchmark duration calculation + var request = DurationRequestFaker.CreateDurationRequest(size); + + var stopwatch = Stopwatch.StartNew(); + var response = await BondFunctions.calculateDurationAsync(request); + output.WriteLine($"Calculated {size} duration in {stopwatch.ElapsedMilliseconds}ms"); + + Assert.NotEmpty(response); + Assert.Equal(size, response.Length); + Assert.All(response, item => Assert.NotEqual(0, item.Duration)); + } + + [Fact] + public async Task testBatchAccruedDaysAndAmountCalculation() + { + const int size = 2; // increase size to benchmark duration calculation + var request = AccruedRequestFaker.CreateAccruedRequest(size); + + var stopwatch = Stopwatch.StartNew(); + var response = await BondFunctions.calculateAccruedDaysAndAmountAsync(request); + output.WriteLine($"Calculated {size} accrued days and amount in {stopwatch.ElapsedMilliseconds}ms"); + + Assert.NotEmpty(response); + Assert.Equal(size, response.Length); + } + } +} From 66d3bac82dcc5be296e61cb551a55ec879b7e1b1 Mon Sep 17 00:00:00 2001 From: Andrea Maggiulli Date: Fri, 26 Aug 2022 20:27:30 +0200 Subject: [PATCH 16/35] [skip ci] Updated status badge --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ff7e40be..401034df 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ QLNet is a financial library written in C# for the Windows enviroment derived pr which has been used as a base reference for modelling various financial instruments. QLNet also contains new developments on the bond market like MBS, Amortized Cost, PSA Curve and others. -[![Build status](https://ci.appveyor.com/api/projects/status/iii1m7n3cdq3v5xm?svg=true)](https://ci.appveyor.com/project/amaggiulli/qlnet) +[![Build status](https://ci.appveyor.com/api/projects/status/nn0a2mw6qu8mg481?svg=true)](https://ci.appveyor.com/project/amaggiulli/qlnet-p0t4r) [![NuGet](https://buildstats.info/nuget/qlnet)](https://www.nuget.org/packages/qlnet/) [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?item_name=Donation+to+QLNet&cmd=_donations&business=a.maggiulli%40gmail.com) From cfc7964ef5e90dcb6b6d6a53310ed8362861858d Mon Sep 17 00:00:00 2001 From: Andrea Maggiulli Date: Mon, 12 Sep 2022 16:04:50 +0200 Subject: [PATCH 17/35] Added batch Bond Functions calculation with benchmark test. --- .../Pricingengines/Bond/BondFunctions.cs | 44 +++++++++++++++++++ src/QLNet/Requests/BondFunctionsRequest.cs | 38 ++++++++++++++++ src/QLNet/Responses/BondFunctionsResponse.cs | 32 ++++++++++++++ .../Fakers/BondFunctionRequestFaker.cs | 33 ++++++++++++++ tests/QLNet.Tests/T_Batch.cs | 14 ++++++ 5 files changed, 161 insertions(+) create mode 100644 src/QLNet/Requests/BondFunctionsRequest.cs create mode 100644 src/QLNet/Responses/BondFunctionsResponse.cs create mode 100644 tests/QLNet.Tests/Fakers/BondFunctionRequestFaker.cs diff --git a/src/QLNet/Pricingengines/Bond/BondFunctions.cs b/src/QLNet/Pricingengines/Bond/BondFunctions.cs index 539744f0..b3f694a9 100644 --- a/src/QLNet/Pricingengines/Bond/BondFunctions.cs +++ b/src/QLNet/Pricingengines/Bond/BondFunctions.cs @@ -21,6 +21,8 @@ under the terms of the QLNet license. You should have received a using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using QLNet.Requests; +using QLNet.Responses; namespace QLNet { @@ -547,5 +549,47 @@ public static DateTime WeightedAverageLife(DateTime today, List amounts, return today.AddDays(wal * 365).Date; } #endregion + + #region Get all bonfunctions + + public static async Task getAllBondFunctions(BondFunctionsRequest[] requests) + { + var dictOfTasks = requests.ToDictionary(request => request.Id, + request => Task.Run(() => getBondFunctions(request))); + + await Task.WhenAll(dictOfTasks.Values); + + return dictOfTasks.Select(task => new BondFunctionsResponse + { + Id = task.Key, + AccruedDays = task.Value.Result.AccruedDays, + AccruedAmount = task.Value.Result.AccruedAmount, + WeightedAverageLife = task.Value.Result.WeightedAverageLife, + Yield = task.Value.Result.Yield, + ModifiedDuration = task.Value.Result.ModifiedDuration + }).ToArray(); + } + + public static BondFunctionsResponse getBondFunctions(BondFunctionsRequest request) + { + var response = new BondFunctionsResponse + { + Id = request.Id + }; + var accrual = accruedDaysAndAmount(request.Bond, request.SettlementDate); + response.AccruedAmount = accrual.accruedAmount; + response.AccruedDays = accrual.accruedDays; + response.WeightedAverageLife = WeightedAverageLife(request.SettlementDate, request.SinkAmounts, request.SinkDates); + response.Yield = request.Bond.yield(request.Price, request.DayCounter, request.Comp, request.Frequency, request.SettlementDate, request.Accuracy); + response.ModifiedDuration = duration(request.Bond, response.Yield.GetValueOrDefault(), request.DayCounter, request.Comp, request.Frequency, + Duration.Type.Modified, request.SettlementDate); + return response; + } + #endregion + } + + + + } diff --git a/src/QLNet/Requests/BondFunctionsRequest.cs b/src/QLNet/Requests/BondFunctionsRequest.cs new file mode 100644 index 00000000..d56c9a09 --- /dev/null +++ b/src/QLNet/Requests/BondFunctionsRequest.cs @@ -0,0 +1,38 @@ +/* + Copyright (C) 2008-2013 Andrea Maggiulli (a.maggiulli@gmail.com) + + This file is part of QLNet Project https://github.com/amaggiulli/qlnet + + QLNet is free software: you can redistribute it and/or modify it + under the terms of the QLNet license. You should have received a + copy of the license along with this program; if not, license is + available at . + + QLNet is a based on QuantLib, a free-software/open-source library + for financial quantitative analysts and developers - http://quantlib.org/ + The QuantLib license is available online at http://quantlib.org/license.shtml. + + 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. +*/ + +using System; +using System.Collections.Generic; + +namespace QLNet.Requests +{ + public class BondFunctionsRequest + { + public int Id { get; set; } + public Bond Bond { get; set; } + public Date SettlementDate { get; set; } + public List SinkAmounts { get; set; } + public List SinkDates { get; set; } + public double Price { get; set; } + public DayCounter DayCounter { get; set; } + public Compounding Comp { get; set; } + public Frequency Frequency { get; set; } + public double Accuracy { get; set; } + } +} diff --git a/src/QLNet/Responses/BondFunctionsResponse.cs b/src/QLNet/Responses/BondFunctionsResponse.cs new file mode 100644 index 00000000..abacc7b0 --- /dev/null +++ b/src/QLNet/Responses/BondFunctionsResponse.cs @@ -0,0 +1,32 @@ +/* + Copyright (C) 2008-2013 Andrea Maggiulli (a.maggiulli@gmail.com) + + This file is part of QLNet Project https://github.com/amaggiulli/qlnet + + QLNet is free software: you can redistribute it and/or modify it + under the terms of the QLNet license. You should have received a + copy of the license along with this program; if not, license is + available at . + + QLNet is a based on QuantLib, a free-software/open-source library + for financial quantitative analysts and developers - http://quantlib.org/ + The QuantLib license is available online at http://quantlib.org/license.shtml. + + 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. +*/ +using System; + +namespace QLNet.Responses +{ + public class BondFunctionsResponse + { + public int Id { get; set; } + public int? AccruedDays { get; set; } + public double? AccruedAmount { get; set; } + public DateTime? WeightedAverageLife { get; set; } + public double? Yield { get; set; } + public double? ModifiedDuration { get; set; } + } +} diff --git a/tests/QLNet.Tests/Fakers/BondFunctionRequestFaker.cs b/tests/QLNet.Tests/Fakers/BondFunctionRequestFaker.cs new file mode 100644 index 00000000..4c581715 --- /dev/null +++ b/tests/QLNet.Tests/Fakers/BondFunctionRequestFaker.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using AutoBogus; +using QLNet.Requests; + +namespace QLNet.Tests.Fakers +{ + internal class BondFunctionRequestFaker + { + public static BondFunctionsRequest[] CreateBondFunctions(int count = 1) + { + var list = new List(); + + for (var i = 0; i < count; i++) + { + list.Add(new AutoFaker() + .RuleFor(x => x.Id, i) + .RuleFor(x => x.Bond, FixedRateBondFaker.CreateFixedRateBond) + .RuleFor(x => x.SettlementDate, x => new Date(x.Date.Future())) + .RuleFor(x => x.SinkAmounts, f => f.Make(10, () => f.Random.Double(0, 100))) + .RuleFor(x => x.SinkDates, f => f.Make(10, () => f.Date.Between(DateTime.Today, DateTime.Today.AddYears(30)))) + .RuleFor(x => x.Price, x => x.Random.Double(0, 100)) + .RuleFor(x => x.DayCounter, x => new Thirty360(Thirty360.Thirty360Convention.BondBasis)) + .RuleFor(x => x.Comp, x => Compounding.Compounded) + .RuleFor(x => x.Frequency, x => Frequency.Semiannual) + .RuleFor(x => x.Accuracy, x => 1.0e-8) + .Generate()); + } + + return list.ToArray(); + } + } +} diff --git a/tests/QLNet.Tests/T_Batch.cs b/tests/QLNet.Tests/T_Batch.cs index 5675c3cd..a6c5d433 100644 --- a/tests/QLNet.Tests/T_Batch.cs +++ b/tests/QLNet.Tests/T_Batch.cs @@ -95,5 +95,19 @@ public async Task testBatchAccruedDaysAndAmountCalculation() Assert.NotEmpty(response); Assert.Equal(size, response.Length); } + + [Fact] + public async Task testBatchAllBondFunctionsCalculation() + { + const int size = 2; // increase size to benchmark bond functions calculation + var request = BondFunctionRequestFaker.CreateBondFunctions(size); + + var stopwatch = Stopwatch.StartNew(); + var response = await BondFunctions.getAllBondFunctions(request); + output.WriteLine($"Calculated {size} bond functions in {stopwatch.ElapsedMilliseconds}ms"); + + Assert.NotEmpty(response); + Assert.Equal(size, response.Length); + } } } From e2ec6a666b0e54bcf2ab6b0caaf5309aecc48d7f Mon Sep 17 00:00:00 2001 From: Andrea Maggiulli Date: Thu, 22 Sep 2022 20:11:25 +0200 Subject: [PATCH 18/35] Added back multi-target with netstandard 2.0 for .NET framework users. Should close #273. --- src/QLNet/QLNet.csproj | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/QLNet/QLNet.csproj b/src/QLNet/QLNet.csproj index 7bd77e94..faccdce0 100644 --- a/src/QLNet/QLNet.csproj +++ b/src/QLNet/QLNet.csproj @@ -2,7 +2,7 @@ 1.12.0 - net6.0 + net6.0;netstandard2.0 latest $(DefineConstants);QL_NEGATIVE_RATES QLNet @@ -18,5 +18,8 @@ False + + + From c523a9d2026604cc219661ca0d7797eddc0c3524 Mon Sep 17 00:00:00 2001 From: Andrea Maggiulli Date: Wed, 16 Nov 2022 23:28:16 +0100 Subject: [PATCH 19/35] Refactoring DayCounters : - Refactored and cleanup DayCounter Bridge pattern - Updated all daycounters. --- src/QLNet/Cashflows/CPICoupon.cs | 4 +- src/QLNet/Instruments/Bonds/PSACurve.cs | 4 +- src/QLNet/Time/DayCounter.cs | 91 ++++---- src/QLNet/Time/DayCounters/Actual360.cs | 44 ++-- src/QLNet/Time/DayCounters/Actual365Fixed.cs | 85 +++++++- src/QLNet/Time/DayCounters/Actual365NoLeap.cs | 67 ------ src/QLNet/Time/DayCounters/ActualActual.cs | 89 +++----- src/QLNet/Time/DayCounters/Business252.cs | 147 ++++++++++++- src/QLNet/Time/DayCounters/OneDayCounter.cs | 10 +- .../Time/DayCounters/SimpleDayCounter.cs | 34 +-- src/QLNet/Time/DayCounters/Thirty360.cs | 197 +++++++++++++----- tests/QLNet.Tests/T_AssetSwap.cs | 42 ++-- tests/QLNet.Tests/T_Bermudanswaption.cs | 3 +- tests/QLNet.Tests/T_Bonds.cs | 14 +- tests/QLNet.Tests/T_CapFlooredCoupon.cs | 4 +- tests/QLNet.Tests/T_CreditDefaultSwap.cs | 8 +- tests/QLNet.Tests/T_DayCounters.cs | 55 ++++- tests/QLNet.Tests/T_Inflation.cs | 6 +- tests/QLNet.Tests/T_InflationCapFloorTest.cs | 2 +- .../T_InflationCapFlooredCouponTest.cs | 4 +- tests/QLNet.Tests/T_OvernightIndexedSwap.cs | 6 +- tests/QLNet.Tests/T_ShortRateModels.cs | 6 +- tests/QLNet.Tests/T_Swaps.cs | 3 +- tests/QLNet.Tests/T_Swaption.cs | 11 +- tests/QLNet.Tests/T_TermStructures.cs | 4 +- 25 files changed, 598 insertions(+), 342 deletions(-) delete mode 100644 src/QLNet/Time/DayCounters/Actual365NoLeap.cs diff --git a/src/QLNet/Cashflows/CPICoupon.cs b/src/QLNet/Cashflows/CPICoupon.cs index cfc8aa33..136d0f71 100644 --- a/src/QLNet/Cashflows/CPICoupon.cs +++ b/src/QLNet/Cashflows/CPICoupon.cs @@ -1,5 +1,5 @@ /* - Copyright (C) 2008, 2009 , 2010, 2011, 2012 Andrea Maggiulli (a.maggiulli@gmail.com) + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) This file is part of QLNet Project https://github.com/amaggiulli/qlnet @@ -249,7 +249,7 @@ public CPILeg(Schedule schedule, index_ = index; baseCPI_ = baseCPI; observationLag_ = observationLag; - paymentDayCounter_ = new Thirty360(); + paymentDayCounter_ = new Thirty360(Thirty360.Thirty360Convention.BondBasis); paymentAdjustment_ = BusinessDayConvention.ModifiedFollowing; paymentCalendar_ = schedule.calendar(); fixingDays_ = new List() { 0 }; diff --git a/src/QLNet/Instruments/Bonds/PSACurve.cs b/src/QLNet/Instruments/Bonds/PSACurve.cs index 0f5a549e..5657d4ae 100644 --- a/src/QLNet/Instruments/Bonds/PSACurve.cs +++ b/src/QLNet/Instruments/Bonds/PSACurve.cs @@ -1,5 +1,5 @@ /* - Copyright (C) 2008, 2009 , 2010, 2011, 2012 Andrea Maggiulli (a.maggiulli@gmail.com) + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) This file is part of QLNet Project https://github.com/amaggiulli/qlnet @@ -34,7 +34,7 @@ public PSACurve(Date startdate, double multiplier) public double getCPR(Date valDate) { - Thirty360 dayCounter = new Thirty360(); + Thirty360 dayCounter = new Thirty360(Thirty360.Thirty360Convention.BondBasis); int d = dayCounter.dayCount(_startDate, valDate) / 30 + 1; return (d <= 30 ? 0.06 * (d / 30d) : 0.06) * _multi; diff --git a/src/QLNet/Time/DayCounter.cs b/src/QLNet/Time/DayCounter.cs index f59b771b..f3944f73 100644 --- a/src/QLNet/Time/DayCounter.cs +++ b/src/QLNet/Time/DayCounter.cs @@ -1,5 +1,5 @@ /* - Copyright (C) 2008 Siarhei Novik (snovik@gmail.com) + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) This file is part of QLNet Project https://github.com/amaggiulli/qlnet @@ -16,69 +16,78 @@ under the terms of the QLNet license. You should have received a ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the license for more details. */ + using System; namespace QLNet { + /// + /// Bridge Implementor + /// + public abstract class DayCounterImpl + { + public abstract string name(); + public virtual int dayCount(Date d1,Date d2) + { + return d2-d1; + } + + public abstract double yearFraction(Date d1,Date d2,Date refPeriodStart,Date refPeriodEnd); + } + // This class provides methods for determining the length of a time period according to given market convention, // both as a number of days and as a year fraction. + /// + /// This class provides methods for determining the length of a time period according to given market convention, + /// both as a number of days and as a year fraction. + /// + /// The Bridge pattern is used to provide the base behavior of the day counter. + /// public class DayCounter { - // this is a placeholder for actual day counters for Singleton pattern use - protected DayCounter dayCounter_; - public DayCounter dayCounter - { - get - { - return dayCounter_; - } - set - { - dayCounter_ = value; - } - } + protected DayCounterImpl _impl; + protected DayCounter(DayCounterImpl impl) {_impl = impl;} - // constructors - /*! The default constructor returns a day counter with a null implementation, which is therefore unusable except as a - placeholder. */ - public DayCounter() { } - public DayCounter(DayCounter d) { dayCounter_ = d; } + public DayCounter() + {} - // comparison based on name - // Returns true iff the two day counters belong to the same derived class. - public static bool operator ==(DayCounter d1, DayCounter d2) + public bool empty() { - return ((Object)d1 == null || (Object)d2 == null) ? - ((Object)d1 == null && (Object)d2 == null) : - (d1.empty() && d2.empty()) || (!d1.empty() && !d2.empty() && d1.name() == d2.name()); + return _impl == null; } - public static bool operator !=(DayCounter d1, DayCounter d2) { return !(d1 == d2); } - - public bool empty() { return dayCounter_ == null; } - - public virtual string name() + public string name() { - if (empty()) - return "No implementation provided"; - return dayCounter_.name(); + Utils.QL_REQUIRE(_impl!= null,()=> "no day counter implementation provided"); + return _impl!.name(); } - public virtual int dayCount(Date d1, Date d2) + public int dayCount(Date d1,Date d2) { - Utils.QL_REQUIRE(!empty(), () => "No implementation provided"); - return dayCounter_.dayCount(d1, d2); + Utils.QL_REQUIRE(_impl != null,()=> "no day counter implementation provided"); + return _impl!.dayCount(d1,d2); } public double yearFraction(Date d1, Date d2) { return yearFraction(d1, d2, d1, d2); } - public virtual double yearFraction(Date d1, Date d2, Date refPeriodStart, Date refPeriodEnd) + public double yearFraction(Date d1, Date d2, Date refPeriodStart, Date refPeriodEnd) + { + Utils.QL_REQUIRE(_impl!=null,()=> "no day counter implementation provided"); + return _impl!.yearFraction(d1,d2,refPeriodStart,refPeriodEnd); + } + + public static bool operator ==(DayCounter d1, DayCounter d2) { - Utils.QL_REQUIRE(!empty(), () => "No implementation provided"); - return dayCounter_.yearFraction(d1, d2, refPeriodStart, refPeriodEnd); + return d1 is null || d2 is null ? + d1 is null && d2 is null : + (d1.empty() && d2.empty()) || (!d1.empty() && !d2.empty() && d1.name() == d2.name()); + } + public static bool operator!=(DayCounter d1, DayCounter d2) + { + return !(d1 == d2); } + public override string ToString() { return this.name(); } public override bool Equals(object o) { return this == (DayCounter)o; } public override int GetHashCode() { return 0; } - public override string ToString() { return this.name(); } } -} \ No newline at end of file +} diff --git a/src/QLNet/Time/DayCounters/Actual360.cs b/src/QLNet/Time/DayCounters/Actual360.cs index fc55769b..2121afae 100644 --- a/src/QLNet/Time/DayCounters/Actual360.cs +++ b/src/QLNet/Time/DayCounters/Actual360.cs @@ -1,6 +1,6 @@ /* Copyright (C) 2008 Siarhei Novik (snovik@gmail.com) - Copyright (C) 2008-2017 Andrea Maggiulli (a.maggiulli@gmail.com) + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) This file is part of QLNet Project https://github.com/amaggiulli/qlnet @@ -20,48 +20,28 @@ under the terms of the QLNet license. You should have received a namespace QLNet { - //! Actual/360 day count convention - /*! Actual/360 day count convention, also known as "Act/360", or "A/360". */ + /// + /// Actual/360 day count convention + /// public class Actual360 : DayCounter { - public enum Actual360Convention { excludeLastDay, includeLastDay } + public Actual360(bool includeLastDay = false) : base(new Impl(includeLastDay)) { } - public Actual360(bool c = false) : base(conventions(c)) { } - - private static DayCounter conventions(bool c) - { - if (c) - return IncludedImpl.Singleton; - - return Impl.Singleton; - } - - class Impl : DayCounter + private class Impl : DayCounterImpl { - public static readonly Impl Singleton = new Impl(); - private Impl() { } + private readonly bool includeLastDay_; - public override string name() { return "Actual/360"; } - public override int dayCount(Date d1, Date d2) { return (d2 - d1); } - public override double yearFraction(Date d1, Date d2, Date refPeriodStart, Date refPeriodEnd) + public Impl(bool includeLastDay) { - return Date.daysBetween(d1, d2) / 360.0; + includeLastDay_ = includeLastDay; } - } - - class IncludedImpl : DayCounter - { - public static readonly IncludedImpl Singleton = new IncludedImpl(); - private IncludedImpl() { } - - public override string name() { return "Actual/360 (inc)"; } - public override int dayCount(Date d1, Date d2) { return (d2 - d1) + 1; } + public override string name() { return includeLastDay_ ? "Actual/360 (inc)" : "Actual/360"; } + public override int dayCount(Date d1, Date d2) { return (d2-d1) + (includeLastDay_ ? 1 : 0); } public override double yearFraction(Date d1, Date d2, Date refPeriodStart, Date refPeriodEnd) { - return (Date.daysBetween(d1, d2) + 1) / 360.0; + return (Date.daysBetween(d1, d2) + (includeLastDay_ ? 1.0 : 0.0))/ 360.0; } - } } } diff --git a/src/QLNet/Time/DayCounters/Actual365Fixed.cs b/src/QLNet/Time/DayCounters/Actual365Fixed.cs index efbc6c16..9c5483b1 100644 --- a/src/QLNet/Time/DayCounters/Actual365Fixed.cs +++ b/src/QLNet/Time/DayCounters/Actual365Fixed.cs @@ -1,5 +1,6 @@ /* Copyright (C) 2008 Siarhei Novik (snovik@gmail.com) + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) This file is part of QLNet Project https://github.com/amaggiulli/qlnet @@ -17,6 +18,8 @@ under the terms of the QLNet license. You should have received a FOR A PARTICULAR PURPOSE. See the license for more details. */ +using System; + namespace QLNet { /* "Actual/365 (Fixed)" day count convention, also know as "Act/365 (Fixed)", "A/365 (Fixed)", or "A/365F". @@ -25,20 +28,88 @@ namespace QLNet * you might want to double-check its meaning. */ public class Actual365Fixed : DayCounter { - public Actual365Fixed() : base(Impl.Singleton) { } + public enum Convention { Standard, Canadian, NoLeap }; + public Actual365Fixed(Convention c = Convention.Standard) : base(Implementation(c)) { } - class Impl : DayCounter + private static DayCounterImpl Implementation(Convention c) + { + switch (c) { + case Convention.Standard: + return new Impl(); + case Convention.Canadian: + return new CA_Impl(); + case Convention.NoLeap: + return new NL_Impl(); + default: + Utils.QL_FAIL("unknown Actual/365 (Fixed) convention"); + break; + } + return null; + } + private class Impl : DayCounterImpl { - public static readonly Impl Singleton = new Impl(); - private Impl() { } - public override string name() { return "Actual/365 (Fixed)"; } - public override int dayCount(Date d1, Date d2) { return (d2 - d1); } public override double yearFraction(Date d1, Date d2, Date refPeriodStart, Date refPeriodEnd) { return Date.daysBetween(d1, d2) / 365.0; } } + private class CA_Impl : DayCounterImpl + { + public override string name() { return "Actual/365 (Fixed) Canadian Bond"; } + public override double yearFraction(Date d1, Date d2, Date refPeriodStart, Date refPeriodEnd) + { + if (d1 == d2) + return 0.0; + + // We need the period to calculate the frequency + Utils.QL_REQUIRE(refPeriodStart != null, ()=> "invalid refPeriodStart"); + Utils.QL_REQUIRE(refPeriodEnd != null, ()=> "invalid refPeriodEnd"); + + var dcs = Date.daysBetween(d1,d2); + var dcc = Date.daysBetween(refPeriodStart,refPeriodEnd); + var months = (int)(Math.Round(12 * dcc / 365)); + Utils.QL_REQUIRE(months != 0, ()=> "invalid reference period for Act/365 Canadian; must be longer than a month"); + var frequency = (int)(12 / months); + + if (dcs < (int)(365/frequency)) + return dcs/365.0; + + return 1.0/frequency - (dcc-dcs)/365.0; + } + + } + private class NL_Impl : DayCounterImpl + { + public override string name() { return "Actual/365 (No Leap)"; } + public override int dayCount(Date d1, Date d2) + { + int[] monthOffset = { + 0, 31, 59, 90, 120, 151, // Jan - Jun + 181, 212, 243, 273, 304, 334 // Jun - Dec + }; + + var s1 = d1.Day + monthOffset[d1.month()-1] + (d1.year() * 365); + var s2 = d2.Day + monthOffset[d2.month()-1] + (d2.year() * 365); + + if (d1.month() == (int)Month.Feb && d1.Day == 29) + { + --s1; + } + + if (d2.month() == (int)Month.Feb && d2.Day == 29) + { + --s2; + } + + return s2 - s1; + } + public override double yearFraction(Date d1, Date d2, Date refPeriodStart, Date refPeriodEnd) + { + return dayCount(d1, d2)/365.0; + } + + } } -} \ No newline at end of file +} diff --git a/src/QLNet/Time/DayCounters/Actual365NoLeap.cs b/src/QLNet/Time/DayCounters/Actual365NoLeap.cs deleted file mode 100644 index ca91d815..00000000 --- a/src/QLNet/Time/DayCounters/Actual365NoLeap.cs +++ /dev/null @@ -1,67 +0,0 @@ -/* - Copyright (C) 2008-2014 Andrea Maggiulli (a.maggiulli@gmail.com) - - This file is part of QLNet Project https://github.com/amaggiulli/qlnet - - QLNet is free software: you can redistribute it and/or modify it - under the terms of the QLNet license. You should have received a - copy of the license along with this program; if not, license is - available at . - - QLNet is a based on QuantLib, a free-software/open-source library - for financial quantitative analysts and developers - http://quantlib.org/ - The QuantLib license is available online at http://quantlib.org/license.shtml. - - 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. -*/ - -namespace QLNet -{ - //! Actual/365 (No Leap) day count convention - /*! "Actual/365 (No Leap)" day count convention, also known as - "Act/365 (NL)", "NL/365", or "Actual/365 (JGB)". - - \ingroup daycounters - */ - public class Actual365NoLeap : DayCounter - { - public Actual365NoLeap() : base(Impl.Singleton) { } - - class Impl : DayCounter - { - public static readonly Impl Singleton = new Impl(); - private static int[] MonthOffset = { 0, 31, 59, 90, 120, 151, // Jan - Jun - 181, 212, 243, 273, 304, 334 // Jun - Dec - }; - private Impl() { } - - public override string name() { return "Actual/365 (NL)"; } - public override int dayCount(Date d1, Date d2) - { - - int s1, s2; - - s1 = d1.Day + MonthOffset[d1.month() - 1] + (d1.year() * 365); - s2 = d2.Day + MonthOffset[d2.month() - 1] + (d2.year() * 365); - - if (d1.month() == (int)Month.Feb && d1.Day == 29) - { - --s1; - } - - if (d2.month() == (int)Month.Feb && d2.Day == 29) - { - --s2; - } - - return s2 - s1; - } - public override double yearFraction(Date d1, Date d2, Date refPeriodStart, Date refPeriodEnd) - { - return dayCount(d1, d2) / 365.0; - } - } - } -} diff --git a/src/QLNet/Time/DayCounters/ActualActual.cs b/src/QLNet/Time/DayCounters/ActualActual.cs index 64337a4d..90a35b4d 100644 --- a/src/QLNet/Time/DayCounters/ActualActual.cs +++ b/src/QLNet/Time/DayCounters/ActualActual.cs @@ -1,5 +1,6 @@ /* Copyright (C) 2008 Siarhei Novik (snovik@gmail.com) + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) This file is part of QLNet Project https://github.com/amaggiulli/qlnet @@ -37,50 +38,41 @@ public class ActualActual : DayCounter { public enum Convention { ISMA, Bond, ISDA, Historical, Actual365, AFB, Euro } - public ActualActual() : base(ISDA_Impl.Singleton) { } - public ActualActual(Convention c, Schedule schedule = null) : base(conventions(c, schedule)) { } + public ActualActual(Convention c = Convention.ISDA, Schedule schedule = null) : base(Conventions(c, schedule)) { } - private static DayCounter conventions(Convention c, Schedule schedule) + private static DayCounterImpl Conventions(Convention c, Schedule schedule) { switch (c) { case Convention.ISMA: case Convention.Bond: - if (schedule != null) - return ISMA_Impl.Singleton(schedule); - else - return Old_ISMA_Impl.Singleton; + if (schedule != null && !schedule.empty()) + return new ISMA_Impl(schedule); + return new Old_ISMA_Impl(); case Convention.ISDA: case Convention.Historical: case Convention.Actual365: - return ISDA_Impl.Singleton; + return new ISDA_Impl(); case Convention.AFB: case Convention.Euro: - return AFB_Impl.Singleton; + return new AFB_Impl(); default: - throw new ArgumentException("Unknown day count convention: " + c); + throw new ArgumentException("Unknown act/act convention: " + c); } } - private class ISMA_Impl : DayCounter + private class ISMA_Impl : DayCounterImpl { private Schedule schedule_; - public static ISMA_Impl Singleton(Schedule schedule) - { - return new ISMA_Impl(schedule); - } - - private ISMA_Impl(Schedule schedule) + public ISMA_Impl(Schedule schedule) { schedule_ = schedule; } public override string name() { return "Actual/Actual (ISMA)"; } - public override int dayCount(Date d1, Date d2) { return (d2 - d1); } - public override double yearFraction(Date d1, Date d2, Date d3, Date d4) { if (d1 == d2) @@ -89,14 +81,19 @@ public override double yearFraction(Date d1, Date d2, Date d3, Date d4) if (d2 < d1) return -yearFraction(d2, d1, d3, d4); - List couponDates = - getListOfPeriodDatesIncludingQuasiPayments(schedule_); + var couponDates = getListOfPeriodDatesIncludingQuasiPayments(schedule_); - double yearFractionSum = 0.0; - for (int i = 0; i < couponDates.Count - 1; i++) + var firstDate = couponDates.Min(); + var lastDate = couponDates.Max(); + + Utils.QL_REQUIRE(d1 >= firstDate && d2 <= lastDate, ()=>"Dates out of range of schedule: " + + "date 1: " + d1 + ", date 2: " + d2 + ", first date: " + + firstDate + ", last date: " + lastDate); + var yearFractionSum = 0.0; + for (var i = 0; i < couponDates.Count - 1; i++) { - Date startReferencePeriod = couponDates[i]; - Date endReferencePeriod = couponDates[i + 1]; + var startReferencePeriod = couponDates[i]; + var endReferencePeriod = couponDates[i + 1]; if (d1 < endReferencePeriod && d2 > startReferencePeriod) { yearFractionSum += @@ -162,7 +159,7 @@ private List getListOfPeriodDatesIncludingQuasiPayments(Schedule schedule) } private double yearFractionWithReferenceDates(T impl, - Date d1, Date d2, Date d3, Date d4) where T: DayCounter + Date d1, Date d2, Date d3, Date d4) where T: DayCounterImpl { Utils.QL_REQUIRE(d1 <= d2, () => "This function is only correct if d1 <= d2\n" + @@ -183,7 +180,7 @@ private double yearFractionWithReferenceDates(T impl, return impl.dayCount(d1, d2) / (referenceDayCount * couponsPerYear); } - private int findCouponsPerYear(T impl, Date refStart, Date refEnd) where T : DayCounter + private int findCouponsPerYear(T impl, Date refStart, Date refEnd) where T : DayCounterImpl { // This will only work for day counts longer than 15 days. int months = (int)(0.5 + 12 * (double)(impl.dayCount(refStart, refEnd)) / 365.0); @@ -191,16 +188,9 @@ private int findCouponsPerYear(T impl, Date refStart, Date refEnd) where T : } } - private class Old_ISMA_Impl : DayCounter + private class Old_ISMA_Impl : DayCounterImpl { - public static readonly Old_ISMA_Impl Singleton = new Old_ISMA_Impl(); - - private Old_ISMA_Impl() {} - public override string name() { return "Actual/Actual (ISMA)"; } - - public override int dayCount(Date d1, Date d2) { return (d2 - d1); } - public override double yearFraction(Date d1, Date d2, Date d3, Date d4) { if (d1 == d2) @@ -209,26 +199,26 @@ public override double yearFraction(Date d1, Date d2, Date d3, Date d4) return -yearFraction(d2, d1, d3, d4); // when the reference period is not specified, try taking it equal to (d1,d2) - Date refPeriodStart = (d3 ?? d1); - Date refPeriodEnd = (d4 ?? d2); + var refPeriodStart = (d3 ?? d1); + var refPeriodEnd = (d4 ?? d2); Utils.QL_REQUIRE(refPeriodEnd > refPeriodStart && refPeriodEnd > d1, () => "Invalid reference period: date 1: " + d1 + ", date 2: " + d2 + ", reference period start: " + refPeriodStart + ", reference period end: " + refPeriodEnd); // estimate roughly the length in months of a period - int months = (int)(0.5 + 12 * (refPeriodEnd - refPeriodStart) / 365.0); + var months = (int)Math.Round(12 * (double)(refPeriodEnd - refPeriodStart) / 365); // for short periods... if (months == 0) { // ...take the reference period as 1 year from d1 refPeriodStart = d1; - refPeriodEnd = d1 + TimeUnit.Years; + refPeriodEnd = d1 + new Period(1, TimeUnit.Years); months = 12; } - double period = months / 12.0; + var period = months / 12.0; if (d2 <= refPeriodEnd) { @@ -248,7 +238,7 @@ public override double yearFraction(Date d1, Date d2, Date d3, Date d4) // this case is long first coupon // the last notional payment date - Date previousRef = refPeriodStart - new Period(months, TimeUnit.Months); + var previousRef = refPeriodStart - new Period(months, TimeUnit.Months); if (d2 > refPeriodStart) return yearFraction(d1, refPeriodStart, previousRef, refPeriodStart) + yearFraction(refPeriodStart, d2, refPeriodStart, refPeriodEnd); @@ -291,16 +281,10 @@ public override double yearFraction(Date d1, Date d2, Date d3, Date d4) } } - private class ISDA_Impl : DayCounter + private class ISDA_Impl : DayCounterImpl { - public static readonly ISDA_Impl Singleton = new ISDA_Impl(); - - private ISDA_Impl() { } - public override string name() { return "Actual/Actual (ISDA)"; } - public override int dayCount(Date d1, Date d2) { return (d2 - d1); } - public override double yearFraction(Date d1, Date d2, Date refPeriodStart, Date refPeriodEnd) { if (d1 == d2) @@ -319,16 +303,9 @@ public override double yearFraction(Date d1, Date d2, Date refPeriodStart, Date } } - private class AFB_Impl : DayCounter + private class AFB_Impl : DayCounterImpl { - public static readonly AFB_Impl Singleton = new AFB_Impl(); - - private AFB_Impl() { } - public override string name() { return "Actual/Actual (AFB)"; } - - public override int dayCount(Date d1, Date d2) { return (d2 - d1); } - public override double yearFraction(Date d1, Date d2, Date refPeriodStart, Date refPeriodEnd) { if (d1 == d2) diff --git a/src/QLNet/Time/DayCounters/Business252.cs b/src/QLNet/Time/DayCounters/Business252.cs index d10ae0cf..54270d7a 100644 --- a/src/QLNet/Time/DayCounters/Business252.cs +++ b/src/QLNet/Time/DayCounters/Business252.cs @@ -1,6 +1,6 @@ /* Copyright (C) 2008 Siarhei Novik (snovik@gmail.com) - Copyright (C) 2008-2016 Andrea Maggiulli (a.maggiulli@gmail.com) + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) This file is part of QLNet Project https://github.com/amaggiulli/qlnet @@ -18,22 +18,149 @@ under the terms of the QLNet license. You should have received a FOR A PARTICULAR PURPOSE. See the license for more details. */ +using System.Collections.Generic; +using Cache = System.Collections.Generic.SortedDictionary>; +using OuterCache = System.Collections.Generic.SortedDictionary; + namespace QLNet { - //! Business/252 day count convention + /// + /// Business/252 day count convention + /// public class Business252 : DayCounter { - public Business252(Calendar c = null) + public Business252(Calendar c = null) : + base(new Impl(c)) + {} + + private class Impl : DayCounterImpl { - calendar_ = c ?? new Brazil(); - dayCounter_ = this; - } + private readonly Calendar calendar_; + readonly SortedDictionary monthlyFigures_ = new SortedDictionary(); + readonly SortedDictionary yearlyFigures_ = new SortedDictionary(); + + public Impl(Calendar c) + { + calendar_ = c ?? new Brazil(); + } + public override string name() { return "Business/252(" + calendar_.name() + ")"; } + + private static bool SameMonth(Date d1, Date d2) + { + return d1.year() == d2.year() && d1.month() == d2.month(); + } + + private static bool SameYear(Date d1, Date d2) + { + return d1.year() == d2.year(); + } + + private static int BusinessDays(Cache cache, Calendar calendar, Month month, int year) + { + if (!cache.ContainsKey(year)) + cache.Add(year, new SortedDictionary()); + if (!cache[year].ContainsKey(month)) + { + // calculate and store. + Date d1 = new Date(1,month,year); + Date d2 = d1 + new Period(1,TimeUnit.Months); + cache[year].Add(month, calendar.businessDaysBetween(d1, d2)); + } + return cache[year][month]; + } - public override string name() { return "Business/252(" + calendar_.name() + ")"; } - public override int dayCount(Date d1, Date d2) { return calendar_.businessDaysBetween(d1, d2); } - public override double yearFraction(Date d1, Date d2, Date d3, Date d4) { return dayCount(d1, d2) / 252.0; } + private static int BusinessDays(OuterCache outerCache, Cache cache, Calendar calendar, int year) + { + if (!outerCache.ContainsKey(year)) + { + // calculate and store. + var total = 0; + for (var i=1; i<=12; ++i) + { + total += BusinessDays(cache, calendar, (Month)i, year); + } + outerCache.Add(year, total); + } + return outerCache[year]; + } - private Calendar calendar_; + public override int dayCount(Date d1, Date d2) + { + if (SameMonth(d1, d2) || d1 >= d2) + { + // we treat the case of d1 > d2 here, since we'd need a + // second cache to get it right (our cached figures are + // for first included, last excluded and might have to be + // changed going the other way.) + return calendar_.businessDaysBetween(d1, d2); + } + else if (SameYear(d1, d2)) + { + if (!monthlyFigures_.ContainsKey(calendar_.name())) + monthlyFigures_.Add(calendar_.name(),new Cache()); + var cache = monthlyFigures_[calendar_.name()]; + var total = 0; + // first, we get to the beginning of next month. + var d = new Date(1, d1.month(), d1.year()) + new Period(1, TimeUnit.Months); + total += calendar_.businessDaysBetween(d1, d); + // then, we add any whole months (whose figures might be + // cached already) in the middle of our period. + while (!SameMonth(d, d2)) + { + total += BusinessDays(cache, calendar_, (Month)d.month(), d.year()); + d += new Period(1, TimeUnit.Months); + } + + // finally, we get to the end of the period. + total += calendar_.businessDaysBetween(d, d2); + return total; + } + else + { + if (!monthlyFigures_.ContainsKey(calendar_.name())) + monthlyFigures_.Add(calendar_.name(),new Cache()); + var cache = monthlyFigures_[calendar_.name()]; + if (!yearlyFigures_.ContainsKey(calendar_.name())) + yearlyFigures_.Add(calendar_.name(),new OuterCache()); + var outerCache = yearlyFigures_[calendar_.name()]; + var total = 0; + // first, we get to the beginning of next year. + // The first bit gets us to the end of this month... + var d = new Date(1, d1.month(), d1.year()) + new Period(1, TimeUnit.Months); + total += calendar_.businessDaysBetween(d1, d); + // ...then we add any remaining months, possibly cached + for (var m = (d1.month()) + 1; m <= 12; ++m) + { + total += BusinessDays(cache, calendar_, (Month)m, d.year()); + } + + // then, we add any whole year in the middle of our period. + d = new Date(1, Month.January, d1.year() + 1); + while (!SameYear(d, d2)) + { + total += BusinessDays(outerCache, cache, calendar_, d.year()); + d += new Period(1, TimeUnit.Years); + } + + // finally, we get to the end of the period. + // First, we add whole months... + for (var m = 1; m < (d2.month()); ++m) + { + total += BusinessDays(cache, calendar_, (Month)m, d2.year()); + } + + // ...then the last bit. + d = new Date(1, d2.month(), d2.year()); + total += calendar_.businessDaysBetween(d, d2); + return total; + } + } + + public override double yearFraction(Date d1, Date d2, Date d3, Date d4) + { + return dayCount(d1, d2) / 252.0; + } + } } } diff --git a/src/QLNet/Time/DayCounters/OneDayCounter.cs b/src/QLNet/Time/DayCounters/OneDayCounter.cs index 3111818e..19ad3c0f 100644 --- a/src/QLNet/Time/DayCounters/OneDayCounter.cs +++ b/src/QLNet/Time/DayCounters/OneDayCounter.cs @@ -1,5 +1,6 @@ /* Copyright (C) 2008 Siarhei Novik (snovik@gmail.com) + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) This file is part of QLNet Project https://github.com/amaggiulli/qlnet @@ -22,13 +23,10 @@ namespace QLNet //! 1/1 day count convention public class OneDayCounter : DayCounter { - public OneDayCounter() : base(Impl.Singleton) { } + public OneDayCounter() : base(new Impl()) { } - class Impl : DayCounter + private class Impl : DayCounterImpl { - public static readonly Impl Singleton = new Impl(); - private Impl() { } - public override string name() { return "1/1"; } public override int dayCount(Date d1, Date d2) { @@ -42,4 +40,4 @@ public override double yearFraction(Date d1, Date d2, Date refPeriodStart, Date } } } -} \ No newline at end of file +} diff --git a/src/QLNet/Time/DayCounters/SimpleDayCounter.cs b/src/QLNet/Time/DayCounters/SimpleDayCounter.cs index cdd0a596..d0774a1c 100644 --- a/src/QLNet/Time/DayCounters/SimpleDayCounter.cs +++ b/src/QLNet/Time/DayCounters/SimpleDayCounter.cs @@ -1,5 +1,6 @@ /* Copyright (C) 2008 Siarhei Novik (snovik@gmail.com) + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) This file is part of QLNet Project https://github.com/amaggiulli/qlnet @@ -20,23 +21,29 @@ under the terms of the QLNet license. You should have received a namespace QLNet { //! Simple day counter for reproducing theoretical calculations. - /*! This day counter tries to ensure that whole-month distances are returned as a simple fraction, i.e., 1 year = 1.0, 6 months = 0.5, 3 months = 0.25 and so forth. - this day counter should be used together with NullCalendar, which ensures that dates at whole-month distances share the same day of month. It is not guaranteed to work with any other calendar. */ + /*! This day counter tries to ensure that whole-month distances + are returned as a simple fraction, i.e., 1 year = 1.0, + 6 months = 0.5, 3 months = 0.25 and so forth. + this day counter should be used together with NullCalendar, + which ensures that dates at whole-month distances share the same day of month. + It is not> guaranteed to work with any other calendar. */ public class SimpleDayCounter : DayCounter { - public SimpleDayCounter() : base(Impl.Singleton) { } + public SimpleDayCounter() : base(new Impl()) { } - class Impl : DayCounter + private class Impl : DayCounterImpl { - public static readonly Impl Singleton = new Impl(); - private Impl() { } - public override string name() { return "Simple"; } - public override int dayCount(Date d1, Date d2) { return Thirty360.US_Impl.Singleton.dayCount(d1, d2); } + + public override int dayCount(Date d1, Date d2) + { + DayCounter fallback = new Thirty360(Thirty360.Thirty360Convention.BondBasis); + return fallback.dayCount(d1, d2); + } + public override double yearFraction(Date d1, Date d2, Date d3, Date d4) { - int dm1 = d1.Day, - dm2 = d2.Day; + int dm1 = d1.Day, dm2 = d2.Day; if (dm1 == dm2 || // e.g., Aug 30 -> Feb 28 ? @@ -44,12 +51,11 @@ public override double yearFraction(Date d1, Date d2, Date d3, Date d4) // e.g., Feb 28 -> Aug 30 ? (dm1 < dm2 && Date.isEndOfMonth(d1))) { - return (d2.Year - d1.Year) + (d2.Month - d1.Month) / 12.0; } - else - return Thirty360.US_Impl.Singleton.yearFraction(d1, d2, d3, d4); + DayCounter fallback = new Thirty360(Thirty360.Thirty360Convention.BondBasis); + return fallback.yearFraction(d1, d2, d3, d4); } } } -} \ No newline at end of file +} diff --git a/src/QLNet/Time/DayCounters/Thirty360.cs b/src/QLNet/Time/DayCounters/Thirty360.cs index c75b271e..6a76516d 100644 --- a/src/QLNet/Time/DayCounters/Thirty360.cs +++ b/src/QLNet/Time/DayCounters/Thirty360.cs @@ -1,5 +1,6 @@ /* Copyright (C) 2008 Siarhei Novik (snovik@gmail.com) + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) This file is part of QLNet Project https://github.com/amaggiulli/qlnet @@ -20,105 +21,203 @@ under the terms of the QLNet license. You should have received a namespace QLNet { - //! 30/360 day count convention - /*! The 30/360 day count can be calculated according to US, European, or Italian conventions. - US (NASD) convention: if the starting date is the 31st of a month, it becomes equal to the 30th of the same month. If the ending date is the 31st of a month and the starting - date is earlier than the 30th of a month, the ending date becomes equal to the 1st of the next month, otherwise the ending date becomes equal to the 30th of the same month. - Also known as "30/360", "360/360", or "Bond Basis" - - European convention: starting dates or ending dates that occur on the 31st of a month become equal to the 30th of the same month. Also known as "30E/360", or "Eurobond Basis" - - Italian convention: starting dates or ending dates that occur on February and are grater than 27 become equal to 30 for computational sake. */ - + //! 30/360 day count convention + /*! The 30/360 day count can be calculated according to a + number of conventions. + + US convention: if the starting date is the 31st of a month or + the last day of February, it becomes equal to the 30th of the + same month. If the ending date is the 31st of a month and the + starting date is the 30th or 31th of a month, the ending date + becomes equal to the 30th. If the ending date is the last of + February and the starting date is also the last of February, + the ending date becomes equal to the 30th. + Also known as "30/360" or "360/360". + + Bond Basis convention: if the starting date is the 31st of a + month, it becomes equal to the 30th of the same month. + If the ending date is the 31st of a month and the starting + date is the 30th or 31th of a month, the ending date + also becomes equal to the 30th of the month. + Also known as "US (ISMA)". + + European convention: starting dates or ending dates that + occur on the 31st of a month become equal to the 30th of the + same month. + Also known as "30E/360", or "Eurobond Basis". + + Italian convention: starting dates or ending dates that + occur on February and are greater than 27 become equal to 30 + for computational sake. + + ISDA convention: starting or ending dates on the 31st of the + month become equal to 30; starting dates or ending dates that + occur on the last day of February also become equal to 30, + except for the termination date. Also known as "30E/360 + ISDA", "30/360 ISDA", or "30/360 German". + + NASD convention: if the starting date is the 31st of a + month, it becomes equal to the 30th of the same month. + If the ending date is the 31st of a month and the starting + date is earlier than the 30th of a month, the ending date + becomes equal to the 1st of the next month, otherwise the + ending date becomes equal to the 30th of the same month. + + */ public class Thirty360 : DayCounter { + private static bool IsLastOfFebruary(int d, int m, int y) + { + return m == 2 && d == 28 + (Date.IsLeapYear(y) ? 1 : 0); + } - public enum Thirty360Convention { USA, BondBasis, European, EurobondBasis, Italian } + public enum Thirty360Convention { USA, BondBasis, European, EurobondBasis, Italian, German, ISMA, ISDA, NASD } - public Thirty360() : base(US_Impl.Singleton) { } - public Thirty360(Thirty360Convention c) : base(conventions(c)) { } + public Thirty360(Thirty360Convention c, Date terminationDate = null) : base(Implementation(c, terminationDate)) { } - private static DayCounter conventions(Thirty360Convention c) + private static DayCounterImpl Implementation(Thirty360Convention c, Date terminationDate) { switch (c) { case Thirty360Convention.USA: - case Thirty360Convention.BondBasis: - return US_Impl.Singleton; + return new UsImpl(); case Thirty360Convention.European: case Thirty360Convention.EurobondBasis: - return EU_Impl.Singleton; + return new EuImpl(); case Thirty360Convention.Italian: - return IT_Impl.Singleton; + return new ItImpl(); + case Thirty360Convention.ISMA: + case Thirty360Convention.BondBasis: + return new IsmaImpl(); + case Thirty360Convention.ISDA: + case Thirty360Convention.German: + return new IsdaImpl(terminationDate); + case Thirty360Convention.NASD: + return new NasdImpl(); default: throw new ArgumentException("Unknown 30/360 convention: " + c); } } - public class US_Impl : DayCounter + private class UsImpl : DayCounterImpl { - public static readonly US_Impl Singleton = new US_Impl(); - private US_Impl() { } - - public override string name() { return "30/360 (Bond Basis)"; } + public override string name() { return "30/360 (US)"; } public override int dayCount(Date d1, Date d2) { int dd1 = d1.Day, dd2 = d2.Day; int mm1 = d1.Month, mm2 = d2.Month; int yy1 = d1.Year, yy2 = d2.Year; - if (dd2 == 31 && dd1 < 30) - { - dd2 = 1; - mm2++; - } + if (dd1 == 31) { dd1 = 30; } + if (dd2 == 31 && dd1 >= 30) { dd2 = 30; } - return 360 * (yy2 - yy1) + 30 * (mm2 - mm1 - 1) + System.Math.Max(0, 30 - dd1) + System.Math.Min(30, dd2); - } + if (IsLastOfFebruary(dd2, mm2, yy2) && IsLastOfFebruary(dd1, mm1, yy1)) { dd2 = 30; } + if (IsLastOfFebruary(dd1, mm1, yy1)) { dd1 = 30; } - public override double yearFraction(Date d1, Date d2, Date d3, Date d4) { return dayCount(d1, d2) / 360.0; } + return 360*(yy2-yy1) + 30*(mm2-mm1) + (dd2-dd1); + } + public override double yearFraction(Date d1, Date d2, Date d3, Date d4) { return dayCount(d1,d2)/360.0; } } - private class EU_Impl : DayCounter + private class IsmaImpl : DayCounterImpl { - public static readonly EU_Impl Singleton = new EU_Impl(); - private EU_Impl() { } + public override string name() { return "30/360 (Bond Basis)"; } + public override int dayCount(Date d1, Date d2) + { + int dd1 = d1.Day, dd2 = d2.Day; + int mm1 = d1.month(), mm2 = d2.month(); + int yy1 = d1.year(), yy2 = d2.year(); + + if (dd1 == 31) { dd1 = 30; } + if (dd2 == 31 && dd1 == 30) { dd2 = 30; } + + return 360*(yy2-yy1) + 30*(mm2-mm1) + (dd2-dd1); + } + public override double yearFraction(Date d1, Date d2, Date d3, Date d4) { return dayCount(d1,d2)/360.0; } + } + private class EuImpl : DayCounterImpl + { public override string name() { return "30E/360 (Eurobond Basis)"; } public override int dayCount(Date d1, Date d2) { int dd1 = d1.Day, dd2 = d2.Day; - int mm1 = d1.Month, mm2 = d2.Month; - int yy1 = d1.Year, yy2 = d2.Year; + int mm1 = d1.month(), mm2 = d2.month(); + int yy1 = d1.year(), yy2 = d2.year(); - return 360 * (yy2 - yy1) + 30 * (mm2 - mm1 - 1) + System.Math.Max(0, 30 - dd1) + System.Math.Min(30, dd2); + if (dd1 == 31) { dd1 = 30; } + if (dd2 == 31) { dd2 = 30; } + + return 360*(yy2-yy1) + 30*(mm2-mm1) + (dd2-dd1); } public override double yearFraction(Date d1, Date d2, Date d3, Date d4) { return dayCount(d1, d2) / 360.0; } } - private class IT_Impl : DayCounter + private class ItImpl : DayCounterImpl { - public static readonly IT_Impl Singleton = new IT_Impl(); - private IT_Impl() { } - public override string name() { return "30/360 (Italian)"; } public override int dayCount(Date d1, Date d2) { int dd1 = d1.Day, dd2 = d2.Day; - int mm1 = d1.Month, mm2 = d2.Month; - int yy1 = d1.Year, yy2 = d2.Year; + int mm1 = d1.month(), mm2 = d2.month(); + int yy1 = d1.year(), yy2 = d2.year(); + + if (dd1 == 31) { dd1 = 30; } + if (dd2 == 31) { dd2 = 30; } - if (mm1 == 2 && dd1 > 27) - dd1 = 30; - if (mm2 == 2 && dd2 > 27) - dd2 = 30; + if (mm1 == 2 && dd1 > 27) { dd1 = 30; } + if (mm2 == 2 && dd2 > 27) { dd2 = 30; } - return 360 * (yy2 - yy1) + 30 * (mm2 - mm1 - 1) + System.Math.Max(0, 30 - dd1) + System.Math.Min(30, dd2); + return 360*(yy2-yy1) + 30*(mm2-mm1) + (dd2-dd1); } public override double yearFraction(Date d1, Date d2, Date d3, Date d4) { return dayCount(d1, d2) / 360.0; } } + private class IsdaImpl : DayCounterImpl + { + public IsdaImpl(Date terminationDate) + { + terminationDate_ = terminationDate; + } + private readonly Date terminationDate_; + public override string name() { return "30E/360 (ISDA)"; } + public override int dayCount(Date d1, Date d2) + { + int dd1 = d1.Day, dd2 = d2.Day; + int mm1 = d1.month(), mm2 = d2.month(); + int yy1 = d1.year(), yy2 = d2.year(); + + if (dd1 == 31) { dd1 = 30; } + if (dd2 == 31) { dd2 = 30; } + + if (IsLastOfFebruary(dd1, mm1, yy1)) { dd1 = 30; } + + if (d2 != terminationDate_ && IsLastOfFebruary(dd2, mm2, yy2)) { dd2 = 30; } + + return 360*(yy2-yy1) + 30*(mm2-mm1) + (dd2-dd1); + } + public override double yearFraction(Date d1, Date d2, Date d3, Date d4) { return dayCount(d1, d2) / 360.0; } + } + + private class NasdImpl : DayCounterImpl + { + public override string name() { return "30/360 (NASD)"; } + public override int dayCount(Date d1, Date d2) + { + int dd1 = d1.Day, dd2 = d2.Day; + int mm1 = d1.month(), mm2 = d2.month(); + int yy1 = d1.year(), yy2 = d2.year(); + + if (dd1 == 31) { dd1 = 30; } + if (dd2 == 31 && dd1 >= 30) { dd2 = 30; } + if (dd2 == 31 && dd1 < 30) { dd2 = 1; mm2++; } + + return 360*(yy2-yy1) + 30*(mm2-mm1) + (dd2-dd1); + } + + public override double yearFraction(Date d1, Date d2, Date d3, Date d4) { return dayCount(d1, d2) / 360.0; } + } } -} \ No newline at end of file +} diff --git a/tests/QLNet.Tests/T_AssetSwap.cs b/tests/QLNet.Tests/T_AssetSwap.cs index 4dda9b63..6f468190 100644 --- a/tests/QLNet.Tests/T_AssetSwap.cs +++ b/tests/QLNet.Tests/T_AssetSwap.cs @@ -1,5 +1,5 @@ /* - Copyright (C) 2008-2013 Andrea Maggiulli (a.maggiulli@gmail.com) + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) This file is part of QLNet Project https://github.com/amaggiulli/qlnet @@ -597,7 +597,7 @@ public void testImpliedValue() DateGeneration.Rule.Backward, false); Bond cmsBond1 = new CmsRateBond(settlementDays, vars.faceAmount, cmsBondSchedule1, - vars.swapIndex, new Thirty360(), + vars.swapIndex, new Thirty360(Thirty360.Thirty360Convention.BondBasis), BusinessDayConvention.Following, fixingDays, new List() {1.0}, new List() {0.0}, @@ -635,7 +635,7 @@ public void testImpliedValue() BusinessDayConvention.Unadjusted, BusinessDayConvention.Unadjusted, DateGeneration.Rule.Backward, false); Bond cmsBond2 = new CmsRateBond(settlementDays, vars.faceAmount, cmsBondSchedule2, - vars.swapIndex, new Thirty360(), + vars.swapIndex, new Thirty360(Thirty360.Thirty360Convention.BondBasis), BusinessDayConvention.Following, fixingDays, new List() {0.84}, new List() {0.0}, new List < double? >(), new List < double? >(), @@ -937,7 +937,7 @@ public void testMarketASWSpread() BusinessDayConvention.Unadjusted, BusinessDayConvention.Unadjusted, DateGeneration.Rule.Backward, false); Bond cmsBond1 = new CmsRateBond(settlementDays, vars.faceAmount, cmsBondSchedule1, - vars.swapIndex, new Thirty360(), + vars.swapIndex, new Thirty360(Thirty360.Thirty360Convention.BondBasis), BusinessDayConvention.Following, fixingDays, new List {1.0}, new List {0.0}, new List < double? > {0.055}, new List < double? > {0.025}, @@ -987,7 +987,7 @@ public void testMarketASWSpread() BusinessDayConvention.Unadjusted, BusinessDayConvention.Unadjusted, DateGeneration.Rule.Backward, false); Bond cmsBond2 = new CmsRateBond(settlementDays, vars.faceAmount, cmsBondSchedule2, - vars.swapIndex, new Thirty360(), + vars.swapIndex, new Thirty360(Thirty360.Thirty360Convention.BondBasis), BusinessDayConvention.Following, fixingDays, new List {0.84}, new List {0.0}, new List < double? >(), new List < double? >(), @@ -1273,7 +1273,7 @@ public void testZSpread() BusinessDayConvention.Unadjusted, BusinessDayConvention.Unadjusted, DateGeneration.Rule.Backward, false); Bond cmsBond1 = new CmsRateBond(settlementDays, vars.faceAmount, cmsBondSchedule1, - vars.swapIndex, new Thirty360(), + vars.swapIndex, new Thirty360(Thirty360.Thirty360Convention.BondBasis), BusinessDayConvention.Following, fixingDays, new List {1.0}, new List {0.0}, new List < double? > {0.055}, new List < double? > {0.025}, @@ -1309,7 +1309,7 @@ public void testZSpread() BusinessDayConvention.Unadjusted, BusinessDayConvention.Unadjusted, DateGeneration.Rule.Backward, false); Bond cmsBond2 = new CmsRateBond(settlementDays, vars.faceAmount, cmsBondSchedule2, - vars.swapIndex, new Thirty360(), + vars.swapIndex, new Thirty360(Thirty360.Thirty360Convention.BondBasis), BusinessDayConvention.Following, fixingDays, new List {0.84}, new List {0.0}, new List < double? >(), new List < double? >(), @@ -1602,7 +1602,7 @@ public void testGenericBondImplied() DateGeneration.Rule.Backward, false); List cmsBondLeg1 = new CmsLeg(cmsBondSchedule1, vars.swapIndex) .withFixingDays(fixingDays) - .withPaymentDayCounter(new Thirty360()) + .withPaymentDayCounter(new Thirty360(Thirty360.Thirty360Convention.BondBasis)) .withCaps(0.055) .withFloors(0.025) .inArrears(inArrears) @@ -1648,7 +1648,7 @@ public void testGenericBondImplied() .withFixingDays(fixingDays) .withGearings(0.84) .inArrears(inArrears) - .withPaymentDayCounter(new Thirty360()) + .withPaymentDayCounter(new Thirty360(Thirty360.Thirty360Convention.BondBasis)) .withNotionals(vars.faceAmount); Date cmsbondRedemption2 = bondCalendar.adjust(cmsBondMaturityDate2, BusinessDayConvention.Following); cmsBondLeg2.Add(new SimpleCashFlow(100.0, cmsbondRedemption2)); @@ -1979,7 +1979,7 @@ public void testMASWWithGenericBond() BusinessDayConvention.Unadjusted, BusinessDayConvention.Unadjusted, DateGeneration.Rule.Backward, false); List cmsBondLeg1 = new CmsLeg(cmsBondSchedule1, vars.swapIndex) - .withPaymentDayCounter(new Thirty360()) + .withPaymentDayCounter(new Thirty360(Thirty360.Thirty360Convention.BondBasis)) .withFixingDays(fixingDays) .withCaps(0.055) .withFloors(0.025) @@ -2033,7 +2033,7 @@ public void testMASWWithGenericBond() BusinessDayConvention.Unadjusted, BusinessDayConvention.Unadjusted, DateGeneration.Rule.Backward, false); List cmsBondLeg2 = new CmsLeg(cmsBondSchedule2, vars.swapIndex) - .withPaymentDayCounter(new Thirty360()) + .withPaymentDayCounter(new Thirty360(Thirty360.Thirty360Convention.BondBasis)) .withFixingDays(fixingDays) .withGearings(0.84) .inArrears(inArrears) @@ -2345,7 +2345,7 @@ public void testZSpreadWithGenericBond() BusinessDayConvention.Unadjusted, BusinessDayConvention.Unadjusted, DateGeneration.Rule.Backward, false); List cmsBondLeg1 = new CmsLeg(cmsBondSchedule1, vars.swapIndex) - .withPaymentDayCounter(new Thirty360()) + .withPaymentDayCounter(new Thirty360(Thirty360.Thirty360Convention.BondBasis)) .withFixingDays(fixingDays) .withCaps(0.055) .withFloors(0.025) @@ -2387,7 +2387,7 @@ public void testZSpreadWithGenericBond() BusinessDayConvention.Unadjusted, BusinessDayConvention.Unadjusted, DateGeneration.Rule.Backward, false); List cmsBondLeg2 = new CmsLeg(cmsBondSchedule2, vars.swapIndex) - .withPaymentDayCounter(new Thirty360()) + .withPaymentDayCounter(new Thirty360(Thirty360.Thirty360Convention.BondBasis)) .withFixingDays(fixingDays) .withGearings(0.84) .inArrears(inArrears) @@ -2769,7 +2769,7 @@ public void testSpecializedBondVsGenericBond() BusinessDayConvention.Unadjusted, BusinessDayConvention.Unadjusted, DateGeneration.Rule.Backward, false); List cmsBondLeg1 = new CmsLeg(cmsBondSchedule1, vars.swapIndex) - .withPaymentDayCounter(new Thirty360()) + .withPaymentDayCounter(new Thirty360(Thirty360.Thirty360Convention.BondBasis)) .withFixingDays(fixingDays) .withCaps(0.055) .withFloors(0.025) @@ -2784,7 +2784,7 @@ public void testSpecializedBondVsGenericBond() // equivalent specialized cms bond Bond cmsSpecializedBond1 = new CmsRateBond(settlementDays, vars.faceAmount, cmsBondSchedule1, - vars.swapIndex, new Thirty360(), + vars.swapIndex, new Thirty360(Thirty360.Thirty360Convention.BondBasis), BusinessDayConvention.Following, fixingDays, new List {1.0}, new List {0.0}, new List < double? > {0.055}, new List < double? > {0.025}, @@ -2833,7 +2833,7 @@ public void testSpecializedBondVsGenericBond() BusinessDayConvention.Unadjusted, BusinessDayConvention.Unadjusted, DateGeneration.Rule.Backward, false); List cmsBondLeg2 = new CmsLeg(cmsBondSchedule2, vars.swapIndex) - .withPaymentDayCounter(new Thirty360()) + .withPaymentDayCounter(new Thirty360(Thirty360.Thirty360Convention.BondBasis)) .withFixingDays(fixingDays) .withGearings(0.84) .inArrears(inArrears) @@ -2847,7 +2847,7 @@ public void testSpecializedBondVsGenericBond() // equivalent specialized cms bond Bond cmsSpecializedBond2 = new CmsRateBond(settlementDays, vars.faceAmount, cmsBondSchedule2, - vars.swapIndex, new Thirty360(), + vars.swapIndex, new Thirty360(Thirty360.Thirty360Convention.BondBasis), BusinessDayConvention.Following, fixingDays, new List {0.84}, new List {0.0}, new List < double? >(), new List < double? >(), @@ -3412,7 +3412,7 @@ public void testSpecializedBondVsGenericBondUsingAsw() BusinessDayConvention.Unadjusted, BusinessDayConvention.Unadjusted, DateGeneration.Rule.Backward, false); List cmsBondLeg1 = new CmsLeg(cmsBondSchedule1, vars.swapIndex) - .withPaymentDayCounter(new Thirty360()) + .withPaymentDayCounter(new Thirty360(Thirty360.Thirty360Convention.BondBasis)) .withFixingDays(fixingDays) .withCaps(0.055) .withFloors(0.025) @@ -3427,7 +3427,7 @@ public void testSpecializedBondVsGenericBondUsingAsw() // equivalent specialized cms bond Bond cmsSpecializedBond1 = new CmsRateBond(settlementDays, vars.faceAmount, cmsBondSchedule1, - vars.swapIndex, new Thirty360(), + vars.swapIndex, new Thirty360(Thirty360.Thirty360Convention.BondBasis), BusinessDayConvention.Following, fixingDays, new List {1.0}, new List {0.0}, new List < double? > {0.055}, new List < double? > {0.025}, @@ -3509,7 +3509,7 @@ public void testSpecializedBondVsGenericBondUsingAsw() BusinessDayConvention.Unadjusted, BusinessDayConvention.Unadjusted, DateGeneration.Rule.Backward, false); List cmsBondLeg2 = new CmsLeg(cmsBondSchedule2, vars.swapIndex) - .withPaymentDayCounter(new Thirty360()) + .withPaymentDayCounter(new Thirty360(Thirty360.Thirty360Convention.BondBasis)) .withFixingDays(fixingDays) .withGearings(0.84) .inArrears(inArrears) @@ -3524,7 +3524,7 @@ public void testSpecializedBondVsGenericBondUsingAsw() // equivalent specialized cms bond Bond cmsSpecializedBond2 = new CmsRateBond(settlementDays, vars.faceAmount, cmsBondSchedule2, - vars.swapIndex, new Thirty360(), + vars.swapIndex, new Thirty360(Thirty360.Thirty360Convention.BondBasis), BusinessDayConvention.Following, fixingDays, new List {0.84}, new List {0.0}, new List < double? >(), new List < double? >(), diff --git a/tests/QLNet.Tests/T_Bermudanswaption.cs b/tests/QLNet.Tests/T_Bermudanswaption.cs index 18c5fe41..17b60734 100644 --- a/tests/QLNet.Tests/T_Bermudanswaption.cs +++ b/tests/QLNet.Tests/T_Bermudanswaption.cs @@ -1,5 +1,6 @@ /* Copyright (C) 2010 Philippe Real (ph_real@hotmail.com) + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) This file is part of QLNet Project https://github.com/amaggiulli/qlnet @@ -71,7 +72,7 @@ public CommonVars() floatingConvention = BusinessDayConvention.ModifiedFollowing; fixedFrequency = Frequency.Annual; floatingFrequency = Frequency.Semiannual; - fixedDayCount = new Thirty360(); + fixedDayCount = new Thirty360(Thirty360.Thirty360Convention.BondBasis); termStructure = new RelinkableHandle(); termStructure.linkTo(Utilities.flatRate(new Date(19, Month.February, 2002), 0.04875825, new Actual365Fixed())); diff --git a/tests/QLNet.Tests/T_Bonds.cs b/tests/QLNet.Tests/T_Bonds.cs index b56ba4c5..e5708f02 100644 --- a/tests/QLNet.Tests/T_Bonds.cs +++ b/tests/QLNet.Tests/T_Bonds.cs @@ -1,6 +1,6 @@ /* Copyright (C) 2008 Siarhei Novik (snovik@gmail.com) - Copyright (C) 2008, 2009 , 2010, 2011, 2012 Andrea Maggiulli (a.maggiulli@gmail.com) + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) This file is part of QLNet Project https://github.com/amaggiulli/qlnet @@ -78,7 +78,7 @@ public void testYield() int settlementDays = 3; double[] coupons = new double[] { 0.02, 0.05, 0.08 }; Frequency[] frequencies = new Frequency[] { Frequency.Semiannual, Frequency.Annual }; - DayCounter bondDayCount = new Thirty360(); + DayCounter bondDayCount = new Thirty360(Thirty360.Thirty360Convention.BondBasis); BusinessDayConvention accrualConvention = BusinessDayConvention.Unadjusted; BusinessDayConvention paymentConvention = BusinessDayConvention.ModifiedFollowing; double redemption = 100.0; @@ -752,7 +752,7 @@ public void testBrazilianCached() double tolerance = 1.0e-4; List couponRates = new InitializedList(1); - couponRates[0] = new InterestRate(0.1, new Thirty360(), Compounding.Compounded, Frequency.Annual); + couponRates[0] = new InterestRate(0.1, new Thirty360(Thirty360.Thirty360Convention.BondBasis), Compounding.Compounded, Frequency.Annual); for (int bondIndex = 0; bondIndex < maturityDates.Count; bondIndex++) { @@ -801,11 +801,11 @@ public void testAmortizingFixedBond() Settings.setEvaluationDate(startDate); Period bondLength = new Period(12, TimeUnit.Months); - DayCounter dCounter = new Thirty360(); + DayCounter dCounter = new Thirty360(Thirty360.Thirty360Convention.BondBasis); Frequency payFrequency = Frequency.Monthly; double amount = 400000000; double rate = 0.06; - var discountCurve = new Handle(Utilities.flatRate(startDate, new SimpleQuote(rate), new Thirty360())); + var discountCurve = new Handle(Utilities.flatRate(startDate, new SimpleQuote(rate), new Thirty360(Thirty360.Thirty360Convention.BondBasis))); AmortizingFixedRateBond bond = BondFactory.makeAmortizingFixedBond(startDate, bondLength, dCounter, payFrequency, amount, rate); IPricingEngine bondEngine = new DiscountingBondEngine(discountCurve); @@ -978,14 +978,14 @@ public void testMBSFixedBondCached() Period bondLength = new Period(358, TimeUnit.Months); Period originalLenght = new Period(360, TimeUnit.Months); - DayCounter dCounter = new Thirty360(); + DayCounter dCounter = new Thirty360(Thirty360.Thirty360Convention.BondBasis); Frequency payFrequency = Frequency.Monthly; double amount = 400000000; double WACrate = 0.06; double PassThroughRate = 0.055; PSACurve psa100 = new PSACurve(startDate); - var discountCurve = new Handle(Utilities.flatRate(startDate, new SimpleQuote(WACrate), new Thirty360())); + var discountCurve = new Handle(Utilities.flatRate(startDate, new SimpleQuote(WACrate), new Thirty360(Thirty360.Thirty360Convention.BondBasis))); // 400 Million Pass-Through with a 5.5% Pass-through Rate, a WAC of 6.0%, and a WAM of 358 Months, // Assuming 100% PSA diff --git a/tests/QLNet.Tests/T_CapFlooredCoupon.cs b/tests/QLNet.Tests/T_CapFlooredCoupon.cs index 07d4ab9b..f5cccc3d 100644 --- a/tests/QLNet.Tests/T_CapFlooredCoupon.cs +++ b/tests/QLNet.Tests/T_CapFlooredCoupon.cs @@ -1,4 +1,4 @@ -// Copyright (C) 2008-2016 Andrea Maggiulli (a.maggiulli@gmail.com) +// Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) // // This file is part of QLNet Project https://github.com/amaggiulli/qlnet // QLNet is free software: you can redistribute it and/or modify it @@ -73,7 +73,7 @@ public List makeFixedLeg(Date sDate, int len) convention, convention, DateGeneration.Rule.Forward, false); List coupons = new InitializedList(len, 0.0); return new FixedRateLeg(schedule) - .withCouponRates(coupons, new Thirty360()) + .withCouponRates(coupons, new Thirty360(Thirty360.Thirty360Convention.BondBasis)) .withNotionals(nominals); } diff --git a/tests/QLNet.Tests/T_CreditDefaultSwap.cs b/tests/QLNet.Tests/T_CreditDefaultSwap.cs index 38bb63d9..ab676d8f 100644 --- a/tests/QLNet.Tests/T_CreditDefaultSwap.cs +++ b/tests/QLNet.Tests/T_CreditDefaultSwap.cs @@ -1,5 +1,5 @@ /* - Copyright (C) 2008-2013 Andrea Maggiulli (a.maggiulli@gmail.com) + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) This file is part of QLNet Project https://github.com/amaggiulli/qlnet @@ -184,7 +184,7 @@ public void testCachedMarketValue() RelinkableHandle discountCurve = new RelinkableHandle(); discountCurve.linkTo(new InterpolatedDiscountCurve(discountDates, dfs, curveDayCounter, null, null, null, new LogLinear())); - DayCounter dayCounter = new Thirty360(); + DayCounter dayCounter = new Thirty360(Thirty360.Thirty360Convention.BondBasis); List dates = new List(); dates.Add(evalDate); dates.Add(calendar.advance(evalDate, 6, TimeUnit.Months, BusinessDayConvention.ModifiedFollowing)); @@ -219,7 +219,7 @@ public void testCachedMarketValue() } RelinkableHandle piecewiseFlatHazardRate = new RelinkableHandle(); - piecewiseFlatHazardRate.linkTo(new InterpolatedHazardRateCurve(dates, hazardRates, new Thirty360())); + piecewiseFlatHazardRate.linkTo(new InterpolatedHazardRateCurve(dates, hazardRates, new Thirty360(Thirty360.Thirty360Convention.BondBasis))); // Testing credit default swap @@ -563,7 +563,7 @@ public void testIsdaEngine() for (int i = 0; i < swap_tenors.Length ; i++) { isdaRateHelpers.Add(new SwapRateHelper(swap_quotes[i], new Period(swap_tenors[i], TimeUnit.Years), - new WeekendsOnly(), Frequency.Semiannual, BusinessDayConvention.ModifiedFollowing, new Thirty360(), + new WeekendsOnly(), Frequency.Semiannual, BusinessDayConvention.ModifiedFollowing, new Thirty360(Thirty360.Thirty360Convention.BondBasis), isda_ibor)); } diff --git a/tests/QLNet.Tests/T_DayCounters.cs b/tests/QLNet.Tests/T_DayCounters.cs index 2fd36c25..a363cf19 100644 --- a/tests/QLNet.Tests/T_DayCounters.cs +++ b/tests/QLNet.Tests/T_DayCounters.cs @@ -1,5 +1,5 @@ /* - Copyright (C) 2008-2016 Andrea Maggiulli (a.maggiulli@gmail.com) + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) This file is part of QLNet Project https://github.com/amaggiulli/qlnet @@ -830,5 +830,58 @@ public void testActualActualIsma(bool isEndOfMonth, Frequency frequency, string Assert.Equal(expectedYearFraction, t); } + + [Fact] + public void testAct366() + { + // Testing Act/366 day counter + + Date[] testDates = { + new Date(1, Month.February, 2002), + new Date(4, Month.February, 2002), + new Date(16, Month.May, 2003), + new Date(17, Month.December, 2003), + new Date(17, Month.December, 2004), + new Date(19, Month.December, 2005), + new Date(2, Month.January, 2006), + new Date(13, Month.March, 2006), + new Date(15, Month.May, 2006), + new Date(17, Month.March, 2006), + new Date(15, Month.May, 2006), + new Date(26, Month.July, 2006), + new Date(28, Month.June, 2007), + new Date(16, Month.September, 2009), + new Date(26, Month.July, 2016) + }; + + double[] expected = { + 0.00819672131147541, + 1.27322404371585, + 0.587431693989071, + 1.0000000000000, + 1.00273224043716, + 0.0382513661202186, + 0.191256830601093, + 0.172131147540984, + -0.16120218579235, + 0.16120218579235, + 0.19672131147541, + 0.920765027322404, + 2.21584699453552, + 6.84426229508197 + }; + + DayCounter dayCounter = new Actual366(); + + for (var i=1; i> helpers = makeHelpers(zcData, zcData.Length, ii, @@ -846,7 +846,7 @@ public void testYYTermStructure() }; Period observationLag = new Period(2, TimeUnit.Months); - DayCounter dc = new Thirty360(); + DayCounter dc = new Thirty360(Thirty360.Thirty360Convention.BondBasis); // now build the helpers ... List> helpers = diff --git a/tests/QLNet.Tests/T_InflationCapFloorTest.cs b/tests/QLNet.Tests/T_InflationCapFloorTest.cs index d9d8def7..034b5915 100644 --- a/tests/QLNet.Tests/T_InflationCapFloorTest.cs +++ b/tests/QLNet.Tests/T_InflationCapFloorTest.cs @@ -75,7 +75,7 @@ public CommonVars() settlementDays = 0; fixingDays = 0; settlement = calendar.advance(today, settlementDays, TimeUnit.Days); - dc = new Thirty360(); + dc = new Thirty360(Thirty360.Thirty360Convention.BondBasis); // yoy index // fixing data diff --git a/tests/QLNet.Tests/T_InflationCapFlooredCouponTest.cs b/tests/QLNet.Tests/T_InflationCapFlooredCouponTest.cs index 8e99fec5..b55bc991 100644 --- a/tests/QLNet.Tests/T_InflationCapFlooredCouponTest.cs +++ b/tests/QLNet.Tests/T_InflationCapFlooredCouponTest.cs @@ -1,5 +1,5 @@ /* - Copyright (C) 2008-2016 Andrea Maggiulli (a.maggiulli@gmail.com) + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) This file is part of QLNet Project https://github.com/amaggiulli/qlnet @@ -81,7 +81,7 @@ public CommonVars() fixingDays = 0; settlement = calendar.advance(today, settlementDays, TimeUnit.Days); startDate = settlement; - dc = new Thirty360(); + dc = new Thirty360(Thirty360.Thirty360Convention.BondBasis); // yoy index // fixing data diff --git a/tests/QLNet.Tests/T_OvernightIndexedSwap.cs b/tests/QLNet.Tests/T_OvernightIndexedSwap.cs index e46b8d0a..810148bd 100644 --- a/tests/QLNet.Tests/T_OvernightIndexedSwap.cs +++ b/tests/QLNet.Tests/T_OvernightIndexedSwap.cs @@ -1,6 +1,6 @@ /* - Copyright (C) 2008, 2009 , 2010 Andrea Maggiulli (a.maggiulli@gmail.com) - * + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) + This file is part of QLNet Project https://github.com/amaggiulli/qlnet QLNet is free software: you can redistribute it and/or modify it @@ -209,7 +209,7 @@ public CommonVars() eoniaIndex = new Eonia(eoniaTermStructure); fixedSwapConvention = BusinessDayConvention.ModifiedFollowing; fixedSwapFrequency = Frequency.Annual; - fixedSwapDayCount = new Thirty360(); + fixedSwapDayCount = new Thirty360(Thirty360.Thirty360Convention.BondBasis); swapIndex = (IborIndex) new Euribor3M(swapTermStructure); calendar = eoniaIndex.fixingCalendar(); today = new Date(5, Month.February, 2009); diff --git a/tests/QLNet.Tests/T_ShortRateModels.cs b/tests/QLNet.Tests/T_ShortRateModels.cs index 8c5c35ef..8faefaa9 100644 --- a/tests/QLNet.Tests/T_ShortRateModels.cs +++ b/tests/QLNet.Tests/T_ShortRateModels.cs @@ -1,6 +1,6 @@ /* Copyright (C) 2010 Philippe Real (ph_real@hotmail.com) - Copyright (C) 2008-2014 Andrea Maggiulli (a.maggiulli@gmail.com) + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) This file is part of QLNet Project https://github.com/amaggiulli/qlnet @@ -76,7 +76,7 @@ public void testCachedHullWhite() new Handle(vol), index, new Period(1, TimeUnit.Years), - new Thirty360(), + new Thirty360(Thirty360.Thirty360Convention.BondBasis), new Actual360(), termStructure); helper.setPricingEngine(engine); @@ -222,7 +222,7 @@ public void testSwaps() { VanillaSwap swap = new VanillaSwap(VanillaSwap.Type.Payer, 1000000.0, - fixedSchedule, rates[k], new Thirty360(), + fixedSchedule, rates[k], new Thirty360(Thirty360.Thirty360Convention.BondBasis), floatSchedule, euribor, 0.0, new Actual360()); swap.setPricingEngine(new DiscountingSwapEngine(termStructure)); double expected = swap.NPV(); diff --git a/tests/QLNet.Tests/T_Swaps.cs b/tests/QLNet.Tests/T_Swaps.cs index f334c85d..b91dc50c 100644 --- a/tests/QLNet.Tests/T_Swaps.cs +++ b/tests/QLNet.Tests/T_Swaps.cs @@ -1,5 +1,6 @@ /* Copyright (C) 2008 Siarhei Novik (snovik@gmail.com) + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) This file is part of QLNet Project https://github.com/amaggiulli/qlnet @@ -78,7 +79,7 @@ public CommonVars() floatingConvention = BusinessDayConvention.ModifiedFollowing; fixedFrequency = Frequency.Annual; floatingFrequency = Frequency.Semiannual; - fixedDayCount = new Thirty360(); + fixedDayCount = new Thirty360(Thirty360.Thirty360Convention.BondBasis); index = new Euribor(new Period(floatingFrequency), termStructure); diff --git a/tests/QLNet.Tests/T_Swaption.cs b/tests/QLNet.Tests/T_Swaption.cs index d37ee5d3..3d07613c 100644 --- a/tests/QLNet.Tests/T_Swaption.cs +++ b/tests/QLNet.Tests/T_Swaption.cs @@ -1,6 +1,7 @@ /* Copyright (C) 2009 Philippe Real (ph_real@hotmail.com) Copyright (C) 2019 Jean-Camille Tournier (jean-camille.tournier@avivainvestors.com) + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) This file is part of QLNet Project https://github.com/amaggiulli/qlnet @@ -103,7 +104,7 @@ public CommonVars() fixedConvention = BusinessDayConvention.Unadjusted; fixedFrequency = Frequency.Annual; - fixedDayCount = new Thirty360(); + fixedDayCount = new Thirty360(Thirty360.Thirty360Convention.BondBasis); index = new Euribor6M(termStructure); floatingConvention = index.businessDayConvention(); @@ -517,7 +518,7 @@ public void testCashSettledSwaptions() DateGeneration.Rule.Forward, true); VanillaSwap swap_u360 = new VanillaSwap(type[0], vars.nominal, - fixedSchedule_u, strike, new Thirty360(), + fixedSchedule_u, strike, new Thirty360(Thirty360.Thirty360Convention.BondBasis), floatSchedule, vars.index, 0.0, vars.index.dayCounter()); @@ -536,7 +537,7 @@ public void testCashSettledSwaptions() DateGeneration.Rule.Forward, true); VanillaSwap swap_a360 = new VanillaSwap(type[0], vars.nominal, - fixedSchedule_a, strike, new Thirty360(), + fixedSchedule_a, strike, new Thirty360(Thirty360.Thirty360Convention.BondBasis), floatSchedule, vars.index, 0.0, vars.index.dayCounter()); @@ -564,11 +565,11 @@ public void testCashSettledSwaptions() // FLOATING_POINT_EXCEPTION Handle termStructure_u360 = new Handle( new FlatForward(vars.settlement, swap_u360.fairRate(), - new Thirty360(), Compounding.Compounded, + new Thirty360(Thirty360.Thirty360Convention.BondBasis), Compounding.Compounded, vars.fixedFrequency)); Handle termStructure_a360 = new Handle( new FlatForward(vars.settlement, swap_a360.fairRate(), - new Thirty360(), Compounding.Compounded, + new Thirty360(Thirty360.Thirty360Convention.BondBasis), Compounding.Compounded, vars.fixedFrequency)); Handle termStructure_u365 = new Handle( new FlatForward(vars.settlement, swap_u365.fairRate(), diff --git a/tests/QLNet.Tests/T_TermStructures.cs b/tests/QLNet.Tests/T_TermStructures.cs index e1c27004..bc59bc6e 100644 --- a/tests/QLNet.Tests/T_TermStructures.cs +++ b/tests/QLNet.Tests/T_TermStructures.cs @@ -1,6 +1,6 @@ /* Copyright (C) 2008 Siarhei Novik (snovik@gmail.com) - Copyright (C) 2008, 2009 , 2010 Andrea Maggiulli (a.maggiulli@gmail.com) + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) This file is part of QLNet Project https://github.com/amaggiulli/qlnet @@ -109,7 +109,7 @@ public CommonVars() for (int i = 0; i < swaps; ++i) { instruments.Add(new SwapRateHelper(swapData[i].rate / 100, new Period(swapData[i].n, swapData[i].units), - calendar, Frequency.Annual, BusinessDayConvention.Unadjusted, new Thirty360(), index)); + calendar, Frequency.Annual, BusinessDayConvention.Unadjusted, new Thirty360(Thirty360.Thirty360Convention.BondBasis), index)); } termStructure = new PiecewiseYieldCurve(settlement, instruments, new Actual360()); dummyTermStructure = new PiecewiseYieldCurve(settlement, instruments, new Actual360()); From b7baaedcb099e82d823a04b7a259ca17f230677d Mon Sep 17 00:00:00 2001 From: Andrea Maggiulli Date: Wed, 16 Nov 2022 23:38:34 +0100 Subject: [PATCH 20/35] Added Actual/366 daycounter. --- src/QLNet/Time/DayCounters/Actual366.cs | 53 +++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 src/QLNet/Time/DayCounters/Actual366.cs diff --git a/src/QLNet/Time/DayCounters/Actual366.cs b/src/QLNet/Time/DayCounters/Actual366.cs new file mode 100644 index 00000000..4468e184 --- /dev/null +++ b/src/QLNet/Time/DayCounters/Actual366.cs @@ -0,0 +1,53 @@ +/* + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) + + This file is part of QLNet Project https://github.com/amaggiulli/qlnet + + QLNet is free software: you can redistribute it and/or modify it + under the terms of the QLNet license. You should have received a + copy of the license along with this program; if not, license is + available at . + + QLNet is a based on QuantLib, a free-software/open-source library + for financial quantitative analysts and developers - http://quantlib.org/ + The QuantLib license is available online at http://quantlib.org/license.shtml. + + 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. +*/ +namespace QLNet +{ + /// + /// Actual/366 day count convention + /// + public class Actual366 : DayCounter + { + public Actual366( bool includeLastDay = false) : base(new Impl(includeLastDay)) { } + + private class Impl : DayCounterImpl + { + private readonly bool includeLastDay; + + public Impl(bool includeLastDay) + { + this.includeLastDay = includeLastDay; + } + + public override string name() + { + return includeLastDay ? "Actual/366 (inc)" : "Actual/366"; + } + + public override int dayCount(Date d1, Date d2) + { + return (d2 - d1) + (includeLastDay ? 1 : 0); + } + + public override double yearFraction(Date d1, Date d2, Date refPeriodStart, Date refPeriodEnd) + { + return dayCount(d1,d2)/366.0; + } + } + } +} From b598cd0b9551ca5b1e9402920e3054944ee8f15a Mon Sep 17 00:00:00 2001 From: Andrea Maggiulli Date: Fri, 18 Nov 2022 19:23:00 +0100 Subject: [PATCH 21/35] Added Actual364, Actual36525 and Thirty365 daycounters. Updated daycounters tests suite. --- src/QLNet/Time/DayCounters/Actual364.cs | 39 ++++ src/QLNet/Time/DayCounters/Actual36525.cs | 61 +++++ src/QLNet/Time/DayCounters/Thirty365.cs | 49 ++++ tests/QLNet.Tests/T_DayCounters.cs | 272 +++++++++++++++++++++- 4 files changed, 418 insertions(+), 3 deletions(-) create mode 100644 src/QLNet/Time/DayCounters/Actual364.cs create mode 100644 src/QLNet/Time/DayCounters/Actual36525.cs create mode 100644 src/QLNet/Time/DayCounters/Thirty365.cs diff --git a/src/QLNet/Time/DayCounters/Actual364.cs b/src/QLNet/Time/DayCounters/Actual364.cs new file mode 100644 index 00000000..58f2f42a --- /dev/null +++ b/src/QLNet/Time/DayCounters/Actual364.cs @@ -0,0 +1,39 @@ +/* + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) + + This file is part of QLNet Project https://github.com/amaggiulli/qlnet + + QLNet is free software: you can redistribute it and/or modify it + under the terms of the QLNet license. You should have received a + copy of the license along with this program; if not, license is + available at . + + QLNet is a based on QuantLib, a free-software/open-source library + for financial quantitative analysts and developers - http://quantlib.org/ + The QuantLib license is available online at http://quantlib.org/license.shtml. + + 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. +*/ + +namespace QLNet +{ + /// + /// Actual/364 day count convention + /// + public class Actual364 : DayCounter + { + public Actual364() : base(new Impl()) { } + + private class Impl : DayCounterImpl + { + public override string name() { return "Actual/364"; } + public override double yearFraction(Date d1, Date d2, Date refPeriodStart, Date refPeriodEnd) + { + return dayCount(d1,d2)/364.0; + } + + } + } +} diff --git a/src/QLNet/Time/DayCounters/Actual36525.cs b/src/QLNet/Time/DayCounters/Actual36525.cs new file mode 100644 index 00000000..33470648 --- /dev/null +++ b/src/QLNet/Time/DayCounters/Actual36525.cs @@ -0,0 +1,61 @@ +/* + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) + + This file is part of QLNet Project https://github.com/amaggiulli/qlnet + + QLNet is free software: you can redistribute it and/or modify it + under the terms of the QLNet license. You should have received a + copy of the license along with this program; if not, license is + available at . + + QLNet is a based on QuantLib, a free-software/open-source library + for financial quantitative analysts and developers - http://quantlib.org/ + The QuantLib license is available online at http://quantlib.org/license.shtml. + + 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. +*/ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace QLNet +{ + /// + /// Actual/365.25 day count convention + /// + public class Actual36525 : DayCounter + { + public Actual36525(bool includeLastDay = false) + :base(new Impl(includeLastDay)) + {} + + private class Impl : DayCounterImpl + { + private readonly bool includeLastDay_; + + public Impl(bool includeLastDay) + { + includeLastDay_ = includeLastDay; + } + + public override string name() + { + return includeLastDay_ ? "Actual/365.25 (inc)" : "Actual/365.25"; + } + + public override int dayCount(Date d1, Date d2) + { + return (d2 - d1) + (includeLastDay_ ? 1 : 0); + } + + public override double yearFraction(Date d1, Date d2, Date refPeriodStart, Date refPeriodEnd) + { + return dayCount(d1,d2)/365.25; + } + } + } +} diff --git a/src/QLNet/Time/DayCounters/Thirty365.cs b/src/QLNet/Time/DayCounters/Thirty365.cs new file mode 100644 index 00000000..40b5de57 --- /dev/null +++ b/src/QLNet/Time/DayCounters/Thirty365.cs @@ -0,0 +1,49 @@ +/* + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) + + This file is part of QLNet Project https://github.com/amaggiulli/qlnet + + QLNet is free software: you can redistribute it and/or modify it + under the terms of the QLNet license. You should have received a + copy of the license along with this program; if not, license is + available at . + + QLNet is a based on QuantLib, a free-software/open-source library + for financial quantitative analysts and developers - http://quantlib.org/ + The QuantLib license is available online at http://quantlib.org/license.shtml. + + 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. +*/ +namespace QLNet +{ + public class Thirty365 : DayCounter + { + public Thirty365() + : base(new Impl()) + { } + + private class Impl : DayCounterImpl + { + public override string name() + { + return "30/365"; + } + + public override int dayCount(Date d1, Date d2) + { + int dd1 = d1.Day, dd2 = d2.Day; + int mm1 = d1.month(), mm2 = d2.month(); + int yy1 = d1.year(), yy2 = d2.year(); + + return 360*(yy2-yy1) + 30*(mm2-mm1) + (dd2-dd1); + } + + public override double yearFraction(Date d1, Date d2, Date refPeriodStart, Date refPeriodEnd) + { + return dayCount(d1,d2)/365.0; + } + } + } +} diff --git a/tests/QLNet.Tests/T_DayCounters.cs b/tests/QLNet.Tests/T_DayCounters.cs index a363cf19..697c93b5 100644 --- a/tests/QLNet.Tests/T_DayCounters.cs +++ b/tests/QLNet.Tests/T_DayCounters.cs @@ -20,11 +20,11 @@ under the terms of the QLNet license. You should have received a using System; using System.Collections.Generic; using System.Globalization; +using System.Numerics; +using TestSuite; using Xunit; -using QLNet; -using Calendar = QLNet.Calendar; -namespace TestSuite +namespace QLNet.Tests { [Collection("QLNet CI Tests")] public class T_DayCounters @@ -57,6 +57,19 @@ public SingleCase(ActualActual.Convention convention, Date start, Date end, doub public double _result; } + public struct Thirty360Case + { + public Thirty360Case(Date start, Date end, int expected) + { + _start = start; + _end = end; + _expected = expected; + } + public Date _start; + public Date _end; + public int _expected; + } + private double actualActualDaycountComputation(Schedule schedule, Date start, Date end) { @@ -615,6 +628,32 @@ public void testBusiness252() } } + [Fact] + public void testThirty365() + { + // Testing 30/365 day counter + + Date d1 = new(17,Month.June,2011), d2 = new(30,Month.December,2012); + DayCounter dayCounter = new Thirty365(); + + BigInteger days = dayCounter.dayCount(d1,d2); + if (days != 553) + { + QAssert.Fail("from " + d1 + " to " + d2 + ":\n" + + " calculated: " + days + "\n" + + " expected: " + 553); + } + + var t = dayCounter.yearFraction(d1,d2); + var expected = 553/365.0; + if (Math.Abs(t-expected) > 1.0e-12) + { + QAssert.Fail("from " + d1 + " to " + d2 + ":\n" + + " calculated: " + t + "\n" + + " expected: " + expected); + } + } + [Fact] public void testThirty360_BondBasis() { @@ -754,6 +793,138 @@ public void testThirty360_EurobondBasis() } } + [Fact] + public void testThirty360_ISDA() + { + // Testing 30/360 day counter (ISDA) + // See https://www.isda.org/2008/12/22/30-360-day-count-conventions/ + + Thirty360Case[] data1 = + { + // Example 1: End dates do not involve the last day of February + new Thirty360Case(new Date(20, Month.August, 2006), new Date(20, Month.February, 2007), 180), + new Thirty360Case(new Date(20, Month.February, 2007), new Date(20, Month.August, 2007), 180), + new Thirty360Case(new Date(20, Month.August, 2007), new Date(20, Month.February, 2008), 180), + new Thirty360Case(new Date(20, Month.February, 2008), new Date(20, Month.August, 2008), 180), + new Thirty360Case(new Date(20, Month.August, 2008), new Date(20, Month.February, 2009), 180), + new Thirty360Case(new Date(20, Month.February, 2009), new Date(20, Month.August, 2009), 180) + }; + + var terminationDate = new Date(20, Month.August, 2009); + var dayCounter = new Thirty360(Thirty360.Thirty360Convention.ISDA, terminationDate); + + foreach (var x in data1) + { + var calculated = dayCounter.dayCount(x._start, x._end); + if (calculated != x._expected) + { + QAssert.Fail("from " + x._start + + " to " + x._end + ":\n" + + " calculated: " + calculated + "\n" + + " expected: " + x._expected); + } + } + + Thirty360Case[] data2 = + { + // Example 2: End dates include some end-February dates + new Thirty360Case( new Date(28, Month.February, 2006), new Date(31, Month.August, 2006), 180), + new Thirty360Case( new Date(31, Month.August, 2006), new Date(28, Month.February, 2007), 180), + new Thirty360Case( new Date(28, Month.February, 2007), new Date(31, Month.August, 2007), 180), + new Thirty360Case( new Date(31, Month.August, 2007), new Date(29, Month.February, 2008), 180), + new Thirty360Case( new Date(29, Month.February, 2008), new Date(31, Month.August, 2008), 180), + new Thirty360Case( new Date(31, Month.August, 2008), new Date(28, Month.February, 2009), 180), + new Thirty360Case( new Date(28, Month.February, 2009), new Date(31, Month.August, 2009), 180), + new Thirty360Case( new Date(31, Month.August, 2009), new Date(28, Month.February, 2010), 180), + new Thirty360Case( new Date(28, Month.February, 2010), new Date(31, Month.August, 2010), 180), + new Thirty360Case( new Date(31, Month.August, 2010), new Date(28, Month.February, 2011), 180), + new Thirty360Case( new Date(28, Month.February, 2011), new Date(31, Month.August, 2011), 180), + new Thirty360Case( new Date(31, Month.August, 2011), new Date(29, Month.February, 2012), 179) + }; + + terminationDate = new Date(29, Month.February, 2012); + dayCounter = new Thirty360(Thirty360.Thirty360Convention.ISDA, terminationDate); + + foreach (var x in data2) + { + var calculated = dayCounter.dayCount(x._start, x._end); + if (calculated != x._expected) + { + QAssert.Fail("from " + x._start + + " to " + x._end + ":\n" + + " calculated: " + calculated + "\n" + + " expected: " + x._expected); + } + } + + Thirty360Case[] data3 = + { + // Example 3: Miscellaneous calculations + new Thirty360Case( new Date(31, Month.January, 2006), new Date(28, Month.February, 2006), 30), + new Thirty360Case( new Date(30, Month.January, 2006), new Date(28, Month.February, 2006), 30), + new Thirty360Case( new Date(28, Month.February, 2006), new Date(3, Month.March, 2006), 3), + new Thirty360Case( new Date(14, Month.February, 2006), new Date(28, Month.February, 2006), 16), + new Thirty360Case( new Date(30, Month.September, 2006), new Date(31, Month.October, 2006), 30), + new Thirty360Case( new Date(31, Month.October, 2006), new Date(28, Month.November, 2006), 28), + new Thirty360Case( new Date(31, Month.August, 2007), new Date(28, Month.February, 2008), 178), + new Thirty360Case( new Date(28, Month.February, 2008), new Date(28, Month.August, 2008), 180), + new Thirty360Case( new Date(28, Month.February, 2008), new Date(30, Month.August, 2008), 182), + new Thirty360Case( new Date(28, Month.February, 2008), new Date(31, Month.August, 2008), 182), + new Thirty360Case( new Date(28, Month.February, 2007), new Date(28, Month.February, 2008), 358), + new Thirty360Case( new Date(28, Month.February, 2007), new Date(29, Month.February, 2008), 359), + new Thirty360Case( new Date(29, Month.February, 2008), new Date(28, Month.February, 2009), 360), + new Thirty360Case( new Date(29, Month.February, 2008), new Date(30, Month.March, 2008), 30), + new Thirty360Case( new Date(29, Month.February, 2008), new Date(31, Month.March, 2008), 30) + }; + + terminationDate = new Date(29, Month.February, 2008); + dayCounter = new Thirty360(Thirty360.Thirty360Convention.ISDA, terminationDate); + + foreach (var x in data3) + { + var calculated = dayCounter.dayCount(x._start, x._end); + if (calculated != x._expected) + { + QAssert.Fail("from " + x._start + + " to " + x._end + ":\n" + + " calculated: " + calculated + "\n" + + " expected: " + x._expected); + } + } + } + + [Fact] + public void testActual365_Canadian() + { + // Testing that Actual/365 (Canadian) throws when needed + var dayCounter = new Actual365Fixed(Actual365Fixed.Convention.Canadian); + + try + { + // no reference period + dayCounter.yearFraction( new Date(10, Month.September, 2018), new Date(10, Month.September, 2019)); + QAssert.Fail("Invalid call to yearFraction failed to throw"); + } + catch + { + ; // expected + } + + try + { + // reference period shorter than a month + dayCounter.yearFraction( new Date(10, Month.September, 2018), + new Date(12, Month.September, 2018), + new Date(10, Month.September, 2018), + new Date(15, Month.September, 2018)); + QAssert.Fail("Invalid call to yearFraction failed to throw"); + } + catch + { + ; // expected + } + } + [Fact] public void testIntraday() { @@ -831,6 +1002,44 @@ public void testActualActualIsma(bool isEndOfMonth, Frequency frequency, string Assert.Equal(expectedYearFraction, t); } + [Fact] + public void testActualActualOutOfScheduleRange() + { + var today = new Date(10, Month.November, 2020); + var temp = Settings.evaluationDate(); + Settings.setEvaluationDate(today); + + var effectiveDate = new Date(21, Month.May, 2019); + var terminationDate = new Date(21, Month.May, 2029); + var tenor = new Period(1, TimeUnit.Years); + var calendar = new China(China.Market.IB); + var convention = BusinessDayConvention.Unadjusted; + var terminationDateConvention = convention; + var rule = DateGeneration.Rule.Backward; + var endOfMonth = false; + + var schedule = new Schedule(effectiveDate, terminationDate, tenor, calendar, convention, + terminationDateConvention, rule, endOfMonth); + var dayCounter = new ActualActual(ActualActual.Convention.Bond, schedule); + var raised = false; + + try + { + dayCounter.yearFraction(today, today + new Period(9, TimeUnit.Years)); + } + catch + { + raised = true; + } + + if (!raised) + { + QAssert.Fail("Exception expected but did not happen!"); + } + + Settings.setEvaluationDate(temp); + } + [Fact] public void testAct366() { @@ -883,5 +1092,62 @@ public void testAct366() + " expected: " + expected[i-1]); } } + + [Fact] + public void testAct36525() + { + // Testing Act/365.25 day counter + + Date[] testDates = + { + new Date(1, Month.February, 2002), + new Date(4, Month.February, 2002), + new Date(16, Month.May, 2003), + new Date(17, Month.December, 2003), + new Date(17, Month.December, 2004), + new Date(19, Month.December, 2005), + new Date(2, Month.January, 2006), + new Date(13, Month.March, 2006), + new Date(15, Month.May, 2006), + new Date(17, Month.March, 2006), + new Date(15, Month.May, 2006), + new Date(26, Month.July, 2006), + new Date(28, Month.June, 2007), + new Date(16, Month.September, 2009), + new Date(26, Month.July, 2016) + }; + + double[] expected = + { + 0.0082135523613963, + 1.27583846680356, + 0.588637919233402, + 1.00205338809035, + 1.00479123887748, + 0.0383299110198494, + 0.191649555099247, + 0.172484599589322, + -0.161533196440794, + 0.161533196440794, + 0.197125256673511, + 0.922655715263518, + 2.22039698836413, + 6.85831622176591 + }; + + var dayCounter = new Actual36525(); + + for (var i=1; i 1.0e-12) + { + QAssert.Fail("from " + testDates[i-1] + + " to " + testDates[i] + ":\n" + + " calculated: " + calculated + "\n" + + " expected: " + expected[i-1]); + } + } + } } } From b2fe6f6e909d9b9bfaf7f32fcce74c30ad093b51 Mon Sep 17 00:00:00 2001 From: Andrea Maggiulli Date: Fri, 18 Nov 2022 20:02:30 +0100 Subject: [PATCH 22/35] [skip ci] Update ReadMe --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 401034df..dc2ccbe4 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ QLNet ===== QLNet C# library official repository. -QLNet is a financial library written in C# for the Windows enviroment derived primarily from its C++ counterpart, Quantlib, +QLNet is a financial library written in C# derived primarily from its C++ counterpart, Quantlib, which has been used as a base reference for modelling various financial instruments. QLNet also contains new developments on the bond market like MBS, Amortized Cost, PSA Curve and others. From 49ffacd9515affd037b06815633fc42707f9a0ac Mon Sep 17 00:00:00 2001 From: Andrea Maggiulli Date: Sat, 19 Nov 2022 19:19:19 +0100 Subject: [PATCH 23/35] Refactoring Calendars - Refactored and cleanup Calendar Bridge pattern - Updated all calendars. --- src/QLNet/Time/Calendar.cs | 300 +++++++++++-------- src/QLNet/Time/Calendars/Argentina.cs | 13 +- src/QLNet/Time/Calendars/Australia.cs | 17 +- src/QLNet/Time/Calendars/Austria.cs | 16 +- src/QLNet/Time/Calendars/BespokeCalendar.cs | 41 +-- src/QLNet/Time/Calendars/Botswana.cs | 15 +- src/QLNet/Time/Calendars/Brazil.cs | 125 ++++---- src/QLNet/Time/Calendars/Canada.cs | 109 +++---- src/QLNet/Time/Calendars/Chile.cs | 4 +- src/QLNet/Time/Calendars/China.cs | 33 +- src/QLNet/Time/Calendars/CzechRepublic.cs | 13 +- src/QLNet/Time/Calendars/Denmark.cs | 14 +- src/QLNet/Time/Calendars/Finland.cs | 14 +- src/QLNet/Time/Calendars/France.cs | 12 +- src/QLNet/Time/Calendars/Germany.cs | 75 ++--- src/QLNet/Time/Calendars/HongKong.cs | 14 +- src/QLNet/Time/Calendars/Hungary.cs | 14 +- src/QLNet/Time/Calendars/Iceland.cs | 13 +- src/QLNet/Time/Calendars/India.cs | 14 +- src/QLNet/Time/Calendars/Indonesia.cs | 29 +- src/QLNet/Time/Calendars/Israel.cs | 49 +-- src/QLNet/Time/Calendars/Italy.cs | 40 ++- src/QLNet/Time/Calendars/Japan.cs | 26 +- src/QLNet/Time/Calendars/JointCalendar.cs | 3 +- src/QLNet/Time/Calendars/Mexico.cs | 14 +- src/QLNet/Time/Calendars/NewZealand.cs | 14 +- src/QLNet/Time/Calendars/Norway.cs | 14 +- src/QLNet/Time/Calendars/NullCalendar.cs | 7 +- src/QLNet/Time/Calendars/Poland.cs | 14 +- src/QLNet/Time/Calendars/Romania.cs | 41 +-- src/QLNet/Time/Calendars/Russia.cs | 30 +- src/QLNet/Time/Calendars/SaudiArabia.cs | 14 +- src/QLNet/Time/Calendars/Singapore.cs | 14 +- src/QLNet/Time/Calendars/Slovakia.cs | 13 +- src/QLNet/Time/Calendars/SouthAfrica.cs | 14 +- src/QLNet/Time/Calendars/SouthKorea.cs | 40 +-- src/QLNet/Time/Calendars/Sweden.cs | 12 +- src/QLNet/Time/Calendars/Switzerland.cs | 14 +- src/QLNet/Time/Calendars/TARGET.cs | 15 +- src/QLNet/Time/Calendars/Taiwan.cs | 12 +- src/QLNet/Time/Calendars/Thailand.cs | 45 +-- src/QLNet/Time/Calendars/Turkey.cs | 14 +- src/QLNet/Time/Calendars/Ukraine.cs | 22 +- src/QLNet/Time/Calendars/UnitedKingdom.cs | 40 +-- src/QLNet/Time/Calendars/UnitedStates.cs | 90 +++--- src/QLNet/Time/Calendars/WeekendsOnly.cs | 6 +- tests/QLNet.Tests/T_BusinessDayConvention.cs | 2 +- tests/QLNet.Tests/T_Calendars.cs | 36 +-- 48 files changed, 800 insertions(+), 740 deletions(-) diff --git a/src/QLNet/Time/Calendar.cs b/src/QLNet/Time/Calendar.cs index 06d07e27..6c8af1e4 100644 --- a/src/QLNet/Time/Calendar.cs +++ b/src/QLNet/Time/Calendar.cs @@ -1,6 +1,6 @@ /* Copyright (C) 2008 Siarhei Novik (snovik@gmail.com) - Copyright (C) 2008 Andrea Maggiulli + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) Copyright (C) 2008 Toyin Akin (toyin_akin@hotmail.com) This file is part of QLNet Project https://github.com/amaggiulli/qlnet @@ -23,13 +23,17 @@ under the terms of the QLNet license. You should have received a namespace QLNet { - - /*! - \ingroup datetime - \test the methods for adding and removing holidays are tested - by inspecting the calendar before and after their - invocation. - */ + /// + /// Bridge implementor + /// + public abstract class CalendarImpl + { + public abstract string name(); + public abstract bool isBusinessDay(Date date); + public abstract bool isWeekend(DayOfWeek w); + public SortedSet addedHolidays { get; set; } = new SortedSet(); + public SortedSet removedHolidays { get; set; } = new SortedSet(); + } /// /// This class provides methods for determining whether a date is a @@ -42,85 +46,179 @@ by inspecting the calendar before and after their /// public class Calendar { - protected Calendar calendar_; - public List addedHolidays { get; set; } - public List removedHolidays { get; set; } - - public Calendar calendar - { - get - { - return calendar_; - } - set - { - calendar_ = value; - } - } + protected CalendarImpl _impl; // constructors /*! The default constructor returns a calendar with a null implementation, which is therefore unusable except as a placeholder. */ public Calendar() - { - addedHolidays = new List(); - removedHolidays = new List(); - } + {} - public Calendar(Calendar c) + /// + /// Create a calendar from implementation + /// + /// + protected Calendar(CalendarImpl impl) { - calendar_ = c; - addedHolidays = new List(); - removedHolidays = new List(); + _impl = impl; } - // Wrappers for interface + // other functions // + // Returns whether or not the calendar is initialized + // + public bool empty() { return _impl == null; } + + // + // Returns the name of the calendar. // This method is used for output and comparison between - // calendars. It is not meant to be used for writing + // calendars. It is not> meant to be used for writing // switch-on-type code. // - // + // // The name of the calendar. // - public virtual string name() { return calendar.name(); } - // Date - // Returns true iff the date is a business day for the - // given market. - public virtual bool isBusinessDay(Date d) + public string name() + { + Utils.QL_REQUIRE(_impl!=null,()=> "no calendar implementation provided"); + return _impl.name(); + } + + // Returns the set of added holidays for the given calendar + public SortedSet addedHolidays() + { + Utils.QL_REQUIRE(_impl!=null,()=> "no calendar implementation provided"); + return _impl.addedHolidays; + } + + // Returns the set of removed holidays for the given calendar + public SortedSet removedHolidays() + { + Utils.QL_REQUIRE(_impl!=null,()=> "no calendar implementation provided"); + return _impl.removedHolidays; + } + + // Clear the set of added and removed holidays + public void resetAddedAndRemovedHolidays() + { + Utils.QL_REQUIRE(_impl!=null,()=> "no calendar implementation provided"); + _impl.addedHolidays.Clear(); + _impl.removedHolidays.Clear(); + } + + // Returns true iff the date is a holiday for the given market. + public bool isBusinessDay(Date d) { - if (calendar.addedHolidays.Contains(d)) + Utils.QL_REQUIRE(_impl != null, ()=> "no calendar implementation provided"); + + if (_impl != null && _impl.addedHolidays.Count != 0 && _impl.addedHolidays.Contains(d)) return false; - if (calendar.removedHolidays.Contains(d)) + + if (_impl != null && _impl.removedHolidays.Count != 0 && _impl.removedHolidays.Contains(d)) return true; - return calendar.isBusinessDay(d); + + return _impl!.isBusinessDay(d); } - // - // Returns true iff the weekday is part of the - // weekend for the given market. - // - public virtual bool isWeekend(DayOfWeek w) { return calendar.isWeekend(w); } - // other functions - // - // Returns whether or not the calendar is initialized - // - public bool empty() { return (object)calendar == null; } //! Returns whether or not the calendar is initialized /// - /// Returns true iff the date is a holiday for the given + /// Returns true if the date is a holiday for the given /// market. /// public bool isHoliday(Date d) { return !isBusinessDay(d); } + + /// + /// Returns true if the weekday is part of the + /// weekend for the given market. + /// + public virtual bool isWeekend(DayOfWeek w) + { + Utils.QL_REQUIRE(_impl != null, ()=> "no calendar implementation provided"); + return _impl!.isWeekend(w); + } + /// - /// Returns true iff the date is last business day for the + /// Returns true iff the date is last business day for the /// month in given market. /// - public bool isEndOfMonth(Date d) { return (d.Month != adjust(d + 1).Month); } + public bool isEndOfMonth(Date d) + { + return (d.Month != adjust(d + 1).Month); + } + /// /// last business day of the month to which the given date belongs /// - public Date endOfMonth(Date d) { return adjust(Date.endOfMonth(d), BusinessDayConvention.Preceding); } + public Date endOfMonth(Date d) + { + return adjust(Date.endOfMonth(d), BusinessDayConvention.Preceding); + } + + /// + /// Adds a date to the set of holidays for the given calendar. + /// + public void addHoliday(Date d) + { + Utils.QL_REQUIRE(_impl != null, ()=> "no calendar implementation provided"); + + // if d was a genuine holiday previously removed, revert the change + _impl!.removedHolidays.Remove(d); + // if it's already a holiday, leave the calendar alone. + // Otherwise, add it. + if (_impl.isBusinessDay(d)) + _impl.addedHolidays.Add(d); + } + + /// + /// Removes a date from the set of holidays for the given calendar. + /// + public void removeHoliday(Date d) + { + Utils.QL_REQUIRE(_impl != null, ()=> "no calendar implementation provided"); + + // if d was an artificially-added holiday, revert the change + _impl!.addedHolidays.Remove(d); + // if it's already a business day, leave the calendar alone. + // Otherwise, add it. + if (!_impl.isBusinessDay(d)) + _impl.removedHolidays.Add(d); + } + + /// + /// Returns the holidays between two dates + /// + public List holidayList(Date from, Date to, bool includeWeekEnds = false) + { + Utils.QL_REQUIRE(to >= from, () => "'from' date (" + from + ") must be equal to or earlier than 'to' date (" + to + ")"); + var result = new List(); + + for (var d = from; d <= to; ++d) + { + if (isHoliday(d) + && (includeWeekEnds || !isWeekend(d.DayOfWeek))) + result.Add(d); + } + return result; + } + + /// + /// Returns the business days between two dates. */ + /// + /// + /// + /// + public List businessDayList(Date from, Date to) + { + Utils.QL_REQUIRE(to >= from, () => "'from' date (" + from + ") must be equal to or earlier than 'to' date (" + to + ")"); + var result = new List(); + + for (var d = from; d <= to; ++d) + { + if (isBusinessDay(d)) + result.Add(d); + } + return result; + } /// /// Adjusts a non-business day to the appropriate near business day with respect @@ -128,18 +226,18 @@ public virtual bool isBusinessDay(Date d) /// public Date adjust(Date d, BusinessDayConvention c = BusinessDayConvention.Following) { - if (d == null) - throw new ArgumentException("null date"); + Utils.QL_REQUIRE(d != null,()=> "null date"); if (c == BusinessDayConvention.Unadjusted) return d; - Date d1 = d; - if (c == BusinessDayConvention.Following || c == BusinessDayConvention.ModifiedFollowing || - c == BusinessDayConvention.HalfMonthModifiedFollowing) + var d1 = d; + if (c is BusinessDayConvention.Following or + BusinessDayConvention.ModifiedFollowing or + BusinessDayConvention.HalfMonthModifiedFollowing) { while (isHoliday(d1)) d1++; - if (c == BusinessDayConvention.ModifiedFollowing || c == BusinessDayConvention.HalfMonthModifiedFollowing) + if (c is BusinessDayConvention.ModifiedFollowing or BusinessDayConvention.HalfMonthModifiedFollowing) { if (d1.Month != d.Month) return adjust(d, BusinessDayConvention.Preceding); @@ -152,7 +250,7 @@ public Date adjust(Date d, BusinessDayConvention c = BusinessDayConvention.Follo } } } - else if (c == BusinessDayConvention.Preceding || c == BusinessDayConvention.ModifiedPreceding) + else if (c is BusinessDayConvention.Preceding or BusinessDayConvention.ModifiedPreceding) { while (isHoliday(d1)) d1--; @@ -184,8 +282,7 @@ public Date adjust(Date d, BusinessDayConvention c = BusinessDayConvention.Follo /// The input date is not modified public Date advance(Date d, int n, TimeUnit unit, BusinessDayConvention c = BusinessDayConvention.Following, bool endOfMonth = false) { - if (d == null) - throw new ArgumentException("null date"); + Utils.QL_REQUIRE(d!=null,()=> "null date"); if (n == 0) return adjust(d, c); else if (unit == TimeUnit.Days) @@ -226,6 +323,7 @@ public Date advance(Date d, int n, TimeUnit unit, BusinessDayConvention c = Busi return adjust(d1, c); } } + /// /// Advances the given date as specified by the given period and /// returns the result. @@ -283,59 +381,14 @@ public int businessDaysBetween(Date from, Date to, bool includeFirst = true, boo return wd; } - /// - /// Adds a date to the set of holidays for the given calendar. - /// - public void addHoliday(Date d) - { - // if d was a genuine holiday previously removed, revert the change - calendar.removedHolidays.Remove(d); - // if it's already a holiday, leave the calendar alone. - // Otherwise, add it. - if (isBusinessDay(d)) - calendar.addedHolidays.Add(d); - } - /// - /// Removes a date from the set of holidays for the given calendar. - /// - public void removeHoliday(Date d) - { - // if d was an artificially-added holiday, revert the change - calendar.addedHolidays.Remove(d); - // if it's already a business day, leave the calendar alone. - // Otherwise, add it. - if (!isBusinessDay(d)) - calendar.removedHolidays.Add(d); - } - /// - /// Returns the holidays between two dates - /// - public static List holidayList(Calendar calendar, Date from, Date to, bool includeWeekEnds = false) - { - Utils.QL_REQUIRE(to > from, () => "'from' date (" + from + ") must be earlier than 'to' date (" + to + ")"); - List result = new List(); - - for (Date d = from; d <= to; ++d) - { - if (calendar.isHoliday(d) - && (includeWeekEnds || !calendar.isWeekend(d.DayOfWeek))) - result.Add(d); - } - return result; - } - /// /// This class provides the means of determining the Easter /// Monday for a given year, as well as specifying Saturdays /// and Sundays as weekend days. /// - public class WesternImpl : Calendar + public abstract class WesternImpl : CalendarImpl { - // Western calendars - public WesternImpl() { } - public WesternImpl(Calendar c) : base(c) { } - - int[] EasterMonday = + private static readonly int[] EasterMonday = { 98, 90, 103, 95, 114, 106, 91, 111, 102, // 1901-1909 87, 107, 99, 83, 103, 95, 115, 99, 91, 111, // 1910-1919 @@ -369,7 +422,14 @@ public WesternImpl(Calendar c) : base(c) { } 116, 101, 93, 112, 97, 89, 109, 100, 85, 105 // 2190-2199 }; - public override bool isWeekend(DayOfWeek w) { return w == DayOfWeek.Saturday || w == DayOfWeek.Sunday; } + /// + /// Returns true if the weekday is part of the + /// weekend for the given market. + /// + /// + /// + public override bool isWeekend(DayOfWeek w) { return w is DayOfWeek.Saturday or DayOfWeek.Sunday; } + /// /// Expressed relative to first day of year /// @@ -380,18 +440,15 @@ public int easterMonday(int y) return EasterMonday[y - 1901]; } } + /// /// This class provides the means of determining the Orthodox /// Easter Monday for a given year, as well as specifying /// Saturdays and Sundays as weekend days. /// - public class OrthodoxImpl : Calendar + public abstract class OrthodoxImpl : CalendarImpl { - // Orthodox calendars - public OrthodoxImpl() { } - public OrthodoxImpl(Calendar c) : base(c) { } - - int[] EasterMonday = + private static readonly int[] EasterMonday = { 105, 118, 110, 102, 121, 106, 126, 118, 102, // 1901-1909 122, 114, 99, 118, 110, 95, 115, 106, 126, 111, // 1910-1919 @@ -425,7 +482,10 @@ public OrthodoxImpl(Calendar c) : base(c) { } 116, 108, 128, 119, 104, 124, 116, 100, 120, 112 // 2190-2199 }; - public override bool isWeekend(DayOfWeek w) { return w == DayOfWeek.Saturday || w == DayOfWeek.Sunday; } + /// Returns true if the weekday is part of the + /// weekend for the given market. + public override bool isWeekend(DayOfWeek w) { return w is DayOfWeek.Saturday or DayOfWeek.Sunday; } + /// /// expressed relative to first day of year /// diff --git a/src/QLNet/Time/Calendars/Argentina.cs b/src/QLNet/Time/Calendars/Argentina.cs index 52129b4e..71659ee8 100644 --- a/src/QLNet/Time/Calendars/Argentina.cs +++ b/src/QLNet/Time/Calendars/Argentina.cs @@ -1,6 +1,7 @@ /* Copyright (C) 2008 Alessandro Duci Copyright (C) 2008 Siarhei Novik (snovik@gmail.com) + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) This file is part of QLNet Project https://github.com/amaggiulli/qlnet @@ -49,19 +50,19 @@ public class Argentina : Calendar { public Argentina() : base(Impl.Singleton) { } - class Impl : Calendar.WesternImpl + private class Impl : WesternImpl { - public static readonly Impl Singleton = new Impl(); private Impl() { } + public static readonly Impl Singleton = new(); public override string name() { return "Buenos Aires stock exchange"; } public override bool isBusinessDay(Date date) { - DayOfWeek w = date.DayOfWeek; + var w = date.DayOfWeek; int d = date.Day, dd = date.DayOfYear; - Month m = (Month)date.Month; - int y = date.Year; - int em = easterMonday(y); + var m = (Month)date.Month; + var y = date.Year; + var em = easterMonday(y); if (isWeekend(w) // New Year's Day diff --git a/src/QLNet/Time/Calendars/Australia.cs b/src/QLNet/Time/Calendars/Australia.cs index d5a0a6ae..8ec68563 100644 --- a/src/QLNet/Time/Calendars/Australia.cs +++ b/src/QLNet/Time/Calendars/Australia.cs @@ -1,6 +1,6 @@ /* Copyright (C) 2008 Alessandro Duci - Copyright (C) 2008 Andrea Maggiulli + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) Copyright (C) 2008 Siarhei Novik (snovik@gmail.com) This file is part of QLNet Project https://github.com/amaggiulli/qlnet @@ -45,21 +45,20 @@ namespace QLNet */ public class Australia : Calendar { - public Australia() : base(Impl.Singleton) { } + public Australia() : base(Impl.Singleton) {} - class Impl : Calendar.WesternImpl + private class Impl : WesternImpl { - public static readonly Impl Singleton = new Impl(); private Impl() { } - + public static readonly Impl Singleton = new(); public override string name() { return "Australia"; } public override bool isBusinessDay(Date date) { - DayOfWeek w = date.DayOfWeek; + var w = date.DayOfWeek; int d = date.Day, dd = date.DayOfYear; - Month m = (Month)date.Month; - int y = date.Year; - int em = easterMonday(y); + var m = (Month)date.Month; + var y = date.Year; + var em = easterMonday(y); if (isWeekend(w) // New Year's Day (possibly moved to Monday) diff --git a/src/QLNet/Time/Calendars/Austria.cs b/src/QLNet/Time/Calendars/Austria.cs index 3e69000c..77b78c01 100644 --- a/src/QLNet/Time/Calendars/Austria.cs +++ b/src/QLNet/Time/Calendars/Austria.cs @@ -1,5 +1,5 @@ /* - Copyright (C) 2008-2021 Andrea Maggiulli + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) This file is part of QLNet Project https://github.com/amaggiulli/qlnet @@ -69,7 +69,7 @@ public Austria() : this(Market.Settlement) {} public Austria(Market m) : base() { - calendar_ = m switch + _impl = m switch { Market.Settlement => Settlement.Singleton, Market.Exchange => Exchange.Singleton, @@ -77,10 +77,11 @@ public Austria(Market m) : base() }; } - private class Settlement : Calendar.WesternImpl + private class Settlement : WesternImpl { - public static readonly Settlement Singleton = new Settlement(); private Settlement() { } + public static readonly Settlement Singleton = new(); + public override string name() { return "Austrian settlement"; } public override bool isBusinessDay(Date date) @@ -124,10 +125,11 @@ public override bool isBusinessDay(Date date) } } - private class Exchange : Calendar.WesternImpl + private class Exchange : WesternImpl { - public static readonly Exchange Singleton = new Exchange(); - private Exchange() {} + private Exchange() { } + public static readonly Exchange Singleton = new(); + public override string name() { return "Vienna stock exchange"; } public override bool isBusinessDay(Date date) { diff --git a/src/QLNet/Time/Calendars/BespokeCalendar.cs b/src/QLNet/Time/Calendars/BespokeCalendar.cs index 05b210cd..d15c8da8 100644 --- a/src/QLNet/Time/Calendars/BespokeCalendar.cs +++ b/src/QLNet/Time/Calendars/BespokeCalendar.cs @@ -1,5 +1,5 @@ /* - Copyright (C) 2008 Siarhei Novik (snovik@gmail.com) + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) This file is part of QLNet Project https://github.com/amaggiulli/qlnet @@ -32,35 +32,38 @@ all linked instances. */ public class BespokeCalendar : Calendar { - private string name_; - public override string name() { return name_; } + // here implementation does not follow a singleton pattern + private class Impl : CalendarImpl + { + private readonly string _Name; + private readonly SortedSet _Weekend = new(); + + public Impl(string name) + { + _Name = name; + } + public override string name() { return _Name; } + public override bool isWeekend(DayOfWeek w) { return (_Weekend.Contains(w)); } + public override bool isBusinessDay(Date date) { return !isWeekend(date.DayOfWeek); } + public void addWeekend(DayOfWeek w) { _Weekend.Add(w); } + } + + private Impl _BespokeImpl; /*! \warning different bespoke calendars created with the same name (or different bespoke calendars created with no name) will compare as equal. */ - public BespokeCalendar() : this("") { } - public BespokeCalendar(string name) : base(new Impl()) + public BespokeCalendar(string name = "") : base() { - name_ = name; + _BespokeImpl = new Impl(name); + _impl = _BespokeImpl; } //! marks the passed day as part of the weekend public void addWeekend(DayOfWeek w) { - Impl impl = calendar_ as Impl; - if (impl != null) - impl.addWeekend(w); - } - - // here implementation does not follow a singleton pattern - class Impl : Calendar.WesternImpl - { - public override bool isWeekend(DayOfWeek w) { return (weekend_.Contains(w)); } - public override bool isBusinessDay(Date date) { return !isWeekend(date.DayOfWeek); } - public void addWeekend(DayOfWeek w) { weekend_.Add(w); } - - private List weekend_ = new List(); + _BespokeImpl.addWeekend(w); } } } diff --git a/src/QLNet/Time/Calendars/Botswana.cs b/src/QLNet/Time/Calendars/Botswana.cs index 8d53cd11..8b028b78 100644 --- a/src/QLNet/Time/Calendars/Botswana.cs +++ b/src/QLNet/Time/Calendars/Botswana.cs @@ -1,6 +1,6 @@ /* Copyright (C) 2008 Alessandro Duci - Copyright (C) 2008 Andrea Maggiulli + c Copyright (C) 2008 Siarhei Novik (snovik@gmail.com) Copyright (C) 2017 Francois Botha (igitur@gmail.com) @@ -56,19 +56,18 @@ public class Botswana : Calendar { public Botswana() : base(Impl.Singleton) { } - class Impl : Calendar.WesternImpl + private class Impl : WesternImpl { - public static readonly Impl Singleton = new Impl(); private Impl() { } - + public static readonly Impl Singleton = new(); public override string name() { return "South Africa"; } public override bool isBusinessDay(Date date) { - DayOfWeek w = date.DayOfWeek; + var w = date.DayOfWeek; int d = date.Day, dd = date.DayOfYear; - Month m = (Month)date.Month; - int y = date.Year; - int em = easterMonday(y); + var m = (Month)date.Month; + var y = date.Year; + var em = easterMonday(y); if (isWeekend(w) // New Year's Day (possibly moved to Monday or Tuesday) diff --git a/src/QLNet/Time/Calendars/Brazil.cs b/src/QLNet/Time/Calendars/Brazil.cs index 7c4b8b23..ddfd28c5 100644 --- a/src/QLNet/Time/Calendars/Brazil.cs +++ b/src/QLNet/Time/Calendars/Brazil.cs @@ -1,5 +1,6 @@ /* Copyright (C) 2008 Siarhei Novik (snovik@gmail.com) + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) This file is part of QLNet Project https://github.com/amaggiulli/qlnet @@ -20,57 +21,53 @@ under the terms of the QLNet license. You should have received a namespace QLNet { - //! Brazilian calendar - /*! Banking holidays: -
      -
    • Saturdays
    • -
    • Sundays
    • -
    • New Year's Day, January 1st
    • -
    • Tiradentes's Day, April 21th
    • -
    • Labour Day, May 1st
    • -
    • Independence Day, September 7th
    • -
    • Nossa Sra. Aparecida Day, October 12th
    • -
    • All Souls Day, November 2nd
    • -
    • Republic Day, November 15th
    • -
    • Christmas, December 25th
    • -
    • Passion of Christ
    • -
    • Carnival
    • -
    • Corpus Christi
    • -
    + // Brazilian calendar + /* Banking holidays: + + Saturdays + Sundays + New Year's Day, January 1st + Tiradentes's Day, April 21th + Labour Day, May 1st + Independence Day, September 7th + Nossa Sra. Aparecida Day, October 12th + All Souls Day, November 2nd + Republic Day, November 15th + Christmas, December 25th + Passion of Christ + Carnival + Corpus Christi Holidays for the Bovespa stock exchange -
      -
    • Saturdays
    • -
    • Sundays
    • -
    • New Year's Day, January 1st
    • -
    • Sao Paulo City Day, January 25th
    • -
    • Tiradentes's Day, April 21th
    • -
    • Labour Day, May 1st
    • -
    • Revolution Day, July 9th
    • -
    • Independence Day, September 7th
    • -
    • Nossa Sra. Aparecida Day, October 12th
    • -
    • All Souls Day, November 2nd
    • -
    • Republic Day, November 15th
    • -
    • Black Consciousness Day, November 20th (since 2007)
    • -
    • Christmas Eve, December 24th
    • -
    • Christmas, December 25th
    • -
    • Passion of Christ
    • -
    • Carnival
    • -
    • Corpus Christi
    • -
    • the last business day of the year
    • -
    + + Saturdays + Sundays + New Year's Day, January 1st + Sao Paulo City Day, January 25th + Tiradentes's Day, April 21th + Labour Day, May 1st + Revolution Day, July 9th + Independence Day, September 7th + Nossa Sra. Aparecida Day, October 12th + All Souls Day, November 2nd + Republic Day, November 15th + Black Consciousness Day, November 20th (since 2007) + Christmas Eve, December 24th + Christmas, December 25th + Passion of Christ + Carnival + Corpus Christi + the last business day of the year - \ingroup calendars - - \test the correctness of the returned results is tested - against a list of known holidays. */ public class Brazil : Calendar { - //! Brazilian calendars - public enum Market { Settlement, //!< generic settlement calendar - Exchange //!< BOVESPA calendar - } + // Brazilian calendars + public enum Market + { + Settlement, // generic settlement calendar + Exchange // BOVESPA calendar + } public Brazil() : this(Market.Settlement) { } public Brazil(Market market) @@ -79,10 +76,10 @@ public Brazil(Market market) switch (market) { case Market.Settlement: - calendar_ = SettlementImpl.Singleton; + _impl = SettlementImpl.Singleton; break; case Market.Exchange: - calendar_ = ExchangeImpl.Singleton; + _impl = ExchangeImpl.Singleton; break; default: Utils.QL_FAIL("unknown market"); @@ -91,20 +88,19 @@ public Brazil(Market market) } - private class SettlementImpl : Calendar.WesternImpl + private class SettlementImpl : WesternImpl { - public static readonly SettlementImpl Singleton = new SettlementImpl(); private SettlementImpl() { } - + public static readonly SettlementImpl Singleton = new(); public override string name() { return "Brazil"; } public override bool isBusinessDay(Date date) { - DayOfWeek w = date.DayOfWeek; - int d = date.Day; - Month m = (Month)date.Month; - int y = date.Year; - int dd = date.DayOfYear; - int em = easterMonday(y); + var w = date.DayOfWeek; + var d = date.Day; + var m = (Month)date.Month; + var y = date.Year; + var dd = date.DayOfYear; + var em = easterMonday(y); if (isWeekend(w) // New Year's Day @@ -135,20 +131,19 @@ public override bool isBusinessDay(Date date) } } - private class ExchangeImpl : Calendar.WesternImpl + private class ExchangeImpl : WesternImpl { - public static readonly ExchangeImpl Singleton = new ExchangeImpl(); private ExchangeImpl() { } - + public static readonly ExchangeImpl Singleton = new(); public override string name() { return "BOVESPA"; } public override bool isBusinessDay(Date date) { - DayOfWeek w = date.DayOfWeek; - int d = date.Day; - Month m = (Month)date.Month; - int y = date.Year; - int dd = date.DayOfYear; - int em = easterMonday(y); + var w = date.DayOfWeek; + var d = date.Day; + var m = (Month)date.Month; + var y = date.Year; + var dd = date.DayOfYear; + var em = easterMonday(y); if (isWeekend(w) // New Year's Day diff --git a/src/QLNet/Time/Calendars/Canada.cs b/src/QLNet/Time/Calendars/Canada.cs index f38c34cb..ff58a0fe 100644 --- a/src/QLNet/Time/Calendars/Canada.cs +++ b/src/QLNet/Time/Calendars/Canada.cs @@ -1,6 +1,7 @@ /* Copyright (C) 2008 Alessandro Duci Copyright (C) 2008 Siarhei Novik (snovik@gmail.com) + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) This file is part of QLNet Project https://github.com/amaggiulli/qlnet @@ -21,45 +22,40 @@ under the terms of the QLNet license. You should have received a namespace QLNet { - //! Canadian calendar - /*! Banking holidays: -
      -
    • Saturdays
    • -
    • Sundays
    • -
    • New Year's Day, January 1st (possibly moved to Monday)
    • -
    • Family Day, third Monday of February (since 2008)
    • -
    • Good Friday
    • -
    • Easter Monday
    • -
    • Victoria Day, The Monday on or preceding 24 May
    • -
    • Canada Day, July 1st (possibly moved to Monday)
    • -
    • Provincial Holiday, first Monday of August
    • -
    • Labour Day, first Monday of September
    • -
    • Thanksgiving Day, second Monday of October
    • -
    • Remembrance Day, November 11th (possibly moved to Monday)
    • -
    • Christmas, December 25th (possibly moved to Monday or Tuesday)
    • -
    • Boxing Day, December 26th (possibly moved to Monday or - Tuesday)
    • -
    + // Canadian calendar + /* Banking holidays: + + Saturdays + Sundays + New Year's Day, January 1st (possibly moved to Monday) + Family Day, third Monday of February (since 2008) + Good Friday + Easter Monday + Victoria Day, The Monday on or preceding 24 May + Canada Day, July 1st (possibly moved to Monday) + Provincial Holiday, first Monday of August + Labour Day, first Monday of September + Thanksgiving Day, second Monday of October + Remembrance Day, November 11th (possibly moved to Monday) + Christmas, December 25th (possibly moved to Monday or Tuesday) + Boxing Day, December 26th (possibly moved to Monday or Tuesday) Holidays for the Toronto stock exchange (TSX): -
      -
    • Saturdays
    • -
    • Sundays
    • -
    • New Year's Day, January 1st (possibly moved to Monday)
    • -
    • Family Day, third Monday of February (since 2008)
    • -
    • Good Friday
    • -
    • Easter Monday
    • -
    • Victoria Day, The Monday on or preceding 24 May
    • -
    • Canada Day, July 1st (possibly moved to Monday)
    • -
    • Provincial Holiday, first Monday of August
    • -
    • Labour Day, first Monday of September
    • -
    • Thanksgiving Day, second Monday of October
    • -
    • Christmas, December 25th (possibly moved to Monday or Tuesday)
    • -
    • Boxing Day, December 26th (possibly moved to Monday or - Tuesday)
    • -
    + + Saturdays + Sundays + New Year's Day, January 1st (possibly moved to Monday) + Family Day, third Monday of February (since 2008) + Good Friday + Easter Monday + Victoria Day, The Monday on or preceding 24 May + Canada Day, July 1st (possibly moved to Monday) + Provincial Holiday, first Monday of August + Labour Day, first Monday of September + Thanksgiving Day, second Monday of October + Christmas, December 25th (possibly moved to Monday or Tuesday) + Boxing Day, December 26th (possibly moved to Monday or Tuesday) - \ingroup calendars */ public class Canada : Calendar { @@ -75,32 +71,26 @@ public Canada(Market m) { // all calendar instances on the same market share the same // implementation instance - switch (m) + _impl = m switch { - case Market.Settlement: - calendar_ = Settlement.Singleton; - break; - case Market.TSX: - calendar_ = TSX.Singleton; - break; - default: - throw new ArgumentException("Unknown market: " + m); - } + Market.Settlement => Settlement.Singleton, + Market.TSX => TSX.Singleton, + _ => throw new ArgumentException("Unknown market: " + m) + }; } - class Settlement : Calendar.WesternImpl + private class Settlement : WesternImpl { - public static readonly Settlement Singleton = new Settlement(); private Settlement() { } - + public static readonly Settlement Singleton = new(); public override string name() { return "Canada"; } public override bool isBusinessDay(Date date) { - DayOfWeek w = date.DayOfWeek; + var w = date.DayOfWeek; int d = date.Day, dd = date.DayOfYear; - Month m = (Month)date.Month; - int y = date.Year; - int em = easterMonday(y); + var m = (Month)date.Month; + var y = date.Year; + var em = easterMonday(y); if (isWeekend(w) // New Year's Day (possibly moved to Monday) @@ -140,19 +130,18 @@ public override bool isBusinessDay(Date date) } } - class TSX : Calendar.WesternImpl + private class TSX : WesternImpl { - public static readonly TSX Singleton = new TSX(); private TSX() { } - + public static readonly TSX Singleton = new(); public override string name() { return "TSX"; } public override bool isBusinessDay(Date date) { - DayOfWeek w = date.DayOfWeek; + var w = date.DayOfWeek; int d = date.Day, dd = date.DayOfYear; - Month m = (Month)date.Month; - int y = date.Year; - int em = easterMonday(y); + var m = (Month)date.Month; + var y = date.Year; + var em = easterMonday(y); if (isWeekend(w) // New Year's Day (possibly moved to Monday) diff --git a/src/QLNet/Time/Calendars/Chile.cs b/src/QLNet/Time/Calendars/Chile.cs index 50ad3699..b1d65f0b 100644 --- a/src/QLNet/Time/Calendars/Chile.cs +++ b/src/QLNet/Time/Calendars/Chile.cs @@ -61,7 +61,7 @@ public Chile() : this(Market.SSE) public Chile(Market m) { - calendar_ = m switch + _impl = m switch { Market.SSE => Settlement.Singleton, _ => throw new ArgumentException("Unknown market: " + m) @@ -70,7 +70,7 @@ public Chile(Market m) private class Settlement : WesternImpl { - public static readonly Settlement Singleton = new Settlement(); + public static readonly Settlement Singleton = new(); private Settlement() {} public override string name() { return "Santiago Stock Exchange"; } diff --git a/src/QLNet/Time/Calendars/China.cs b/src/QLNet/Time/Calendars/China.cs index 0aaf177d..93dae28e 100644 --- a/src/QLNet/Time/Calendars/China.cs +++ b/src/QLNet/Time/Calendars/China.cs @@ -1,5 +1,5 @@ /* - Copyright (C) 2008-2013 Andrea Maggiulli (a.maggiulli@gmail.com) + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) Copyright (C) 2008 Alessandro Duci Copyright (C) 2008, 2009 Siarhei Novik (snovik@gmail.com) @@ -65,10 +65,10 @@ public China(Market market = Market.SSE) switch (market) { case Market.SSE: - calendar_ = SseImpl.Singleton; + _impl = SseImpl.Singleton; break; case Market.IB: - calendar_ = IbImpl.Singleton; + _impl = IbImpl.Singleton; break; default: Utils.QL_FAIL("unknown market"); @@ -76,9 +76,9 @@ public China(Market market = Market.SSE) } } - private class SseImpl : Calendar + private class SseImpl : CalendarImpl { - public static readonly SseImpl Singleton = new SseImpl(); + public static readonly SseImpl Singleton = new(); private SseImpl() { } public override string name() { return "Shanghai stock exchange"; } @@ -89,10 +89,10 @@ public override bool isWeekend(DayOfWeek w) public override bool isBusinessDay(Date date) { - DayOfWeek w = date.DayOfWeek; - int d = date.Day; - Month m = (Month)date.Month; - int y = date.Year; + var w = date.DayOfWeek; + var d = date.Day; + var m = (Month)date.Month; + var y = date.Year; if (isWeekend(w) // New Year's Day @@ -222,22 +222,20 @@ public override bool isBusinessDay(Date date) } } - private class IbImpl : Calendar + private class IbImpl : CalendarImpl { - public static readonly IbImpl Singleton = new IbImpl(); - - public IbImpl() + public static readonly IbImpl Singleton = new(); + private IbImpl() { sseImpl = new China(Market.SSE); } - + private readonly Calendar sseImpl; public override string name() { return "China inter bank market"; } - public override bool isWeekend(DayOfWeek w) { return w == DayOfWeek.Saturday || w == DayOfWeek.Sunday; } public override bool isBusinessDay(Date date) { - List working_weekends = new List + var working_weekends = new List { // 2005 new Date(5, Month.February, 2005), @@ -372,9 +370,6 @@ public override bool isBusinessDay(Date date) return sseImpl.isBusinessDay(date) || working_weekends.Contains(date); } - - private Calendar sseImpl; - } } diff --git a/src/QLNet/Time/Calendars/CzechRepublic.cs b/src/QLNet/Time/Calendars/CzechRepublic.cs index ae8a156b..c94d49b7 100644 --- a/src/QLNet/Time/Calendars/CzechRepublic.cs +++ b/src/QLNet/Time/Calendars/CzechRepublic.cs @@ -1,6 +1,7 @@ /* Copyright (C) 2008 Alessandro Duci Copyright (C) 2008 Siarhei Novik (snovik@gmail.com) + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) This file is part of QLNet Project https://github.com/amaggiulli/qlnet @@ -46,19 +47,19 @@ public class CzechRepublic : Calendar { public CzechRepublic() : base(Impl.Singleton) { } - class Impl : Calendar.WesternImpl + private class Impl : WesternImpl { - public static readonly Impl Singleton = new Impl(); + public static readonly Impl Singleton = new(); private Impl() { } public override string name() { return "Prague stock exchange"; } public override bool isBusinessDay(Date date) { - DayOfWeek w = date.DayOfWeek; + var w = date.DayOfWeek; int d = date.Day, dd = date.DayOfYear; - Month m = (Month)date.Month; - int y = date.Year; - int em = easterMonday(y); + var m = (Month)date.Month; + var y = date.Year; + var em = easterMonday(y); if (isWeekend(w) // New Year's Day diff --git a/src/QLNet/Time/Calendars/Denmark.cs b/src/QLNet/Time/Calendars/Denmark.cs index 12f96493..c56af5ee 100644 --- a/src/QLNet/Time/Calendars/Denmark.cs +++ b/src/QLNet/Time/Calendars/Denmark.cs @@ -1,6 +1,6 @@ /* Copyright (C) 2008 Alessandro Duci - Copyright (C) 2008 Andrea Maggiulli + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) Copyright (C) 2008 Siarhei Novik (snovik@gmail.com) This file is part of QLNet Project https://github.com/amaggiulli/qlnet @@ -46,19 +46,19 @@ public class Denmark : Calendar { public Denmark() : base(Impl.Singleton) { } - class Impl : Calendar.WesternImpl + private class Impl : WesternImpl { - public static readonly Impl Singleton = new Impl(); + public static readonly Impl Singleton = new(); private Impl() { } public override string name() { return "Denmark"; } public override bool isBusinessDay(Date date) { - DayOfWeek w = date.DayOfWeek; + var w = date.DayOfWeek; int d = date.Day, dd = date.DayOfYear; - Month m = (Month)date.Month; - int y = date.Year; - int em = easterMonday(y); + var m = (Month)date.Month; + var y = date.Year; + var em = easterMonday(y); if (isWeekend(w) // Maundy Thursday || (dd == em - 4) diff --git a/src/QLNet/Time/Calendars/Finland.cs b/src/QLNet/Time/Calendars/Finland.cs index fd52ed77..f60ab9eb 100644 --- a/src/QLNet/Time/Calendars/Finland.cs +++ b/src/QLNet/Time/Calendars/Finland.cs @@ -1,6 +1,6 @@ /* Copyright (C) 2008 Alessandro Duci - Copyright (C) 2008 Andrea Maggiulli + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) Copyright (C) 2008 Siarhei Novik (snovik@gmail.com) This file is part of QLNet Project https://github.com/amaggiulli/qlnet @@ -47,19 +47,19 @@ public class Finland : Calendar { public Finland() : base(Impl.Singleton) { } - class Impl : Calendar.WesternImpl + private class Impl : WesternImpl { - public static readonly Impl Singleton = new Impl(); + public static readonly Impl Singleton = new(); private Impl() { } public override string name() { return "Finland"; } public override bool isBusinessDay(Date date) { - DayOfWeek w = date.DayOfWeek; + var w = date.DayOfWeek; int d = date.Day, dd = date.DayOfYear; - Month m = (Month)date.Month; - int y = date.Year; - int em = easterMonday(y); + var m = (Month)date.Month; + var y = date.Year; + var em = easterMonday(y); if (isWeekend(w) // New Year's Day diff --git a/src/QLNet/Time/Calendars/France.cs b/src/QLNet/Time/Calendars/France.cs index bc428111..e34894e5 100644 --- a/src/QLNet/Time/Calendars/France.cs +++ b/src/QLNet/Time/Calendars/France.cs @@ -1,5 +1,5 @@ /* - Copyright (C) 2008-2021 Andrea Maggiulli + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) This file is part of QLNet Project https://github.com/amaggiulli/qlnet @@ -68,7 +68,7 @@ public France() : this(Market.Settlement) { } public France(Market m) : base() { - calendar_ = m switch + _impl = m switch { Market.Settlement => Settlement.Singleton, Market.Exchange => Exchange.Singleton, @@ -76,9 +76,9 @@ public France(Market m) : base() }; } - private class Settlement : Calendar.WesternImpl + private class Settlement : WesternImpl { - public static readonly Settlement Singleton = new Settlement(); + public static readonly Settlement Singleton = new(); private Settlement() { } public override string name() { return "French settlement"; } public override bool isBusinessDay(Date date) @@ -116,9 +116,9 @@ public override bool isBusinessDay(Date date) } } - private class Exchange : Calendar.WesternImpl + private class Exchange : WesternImpl { - public static readonly Exchange Singleton = new Exchange(); + public static readonly Exchange Singleton = new(); private Exchange() { } public override string name() { return "Paris stock exchange"; } public override bool isBusinessDay(Date date) diff --git a/src/QLNet/Time/Calendars/Germany.cs b/src/QLNet/Time/Calendars/Germany.cs index b5200098..53600003 100644 --- a/src/QLNet/Time/Calendars/Germany.cs +++ b/src/QLNet/Time/Calendars/Germany.cs @@ -1,6 +1,7 @@ /* Copyright (C) 2008 Alessandro Duci Copyright (C) 2008 Siarhei Novik (snovik@gmail.com) + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) This file is part of QLNet Project https://github.com/amaggiulli/qlnet @@ -127,38 +128,38 @@ public Germany(Market m) switch (m) { case Market.Settlement: - calendar_ = Settlement.Singleton; + _impl = Settlement.Singleton; break; case Market.FrankfurtStockExchange: - calendar_ = FrankfurtStockExchange.Singleton; + _impl = FrankfurtStockExchange.Singleton; break; case Market.Xetra: - calendar_ = Xetra.Singleton; + _impl = Xetra.Singleton; break; case Market.Eurex: - calendar_ = Eurex.Singleton; + _impl = Eurex.Singleton; break; case Market.Euwax: - calendar_ = Euwax.Singleton; + _impl = Euwax.Singleton; break; default: throw new ArgumentException("Unknown market: " + m); } } - class Settlement : Calendar.WesternImpl + private class Settlement : WesternImpl { - public static readonly Settlement Singleton = new Settlement(); + public static readonly Settlement Singleton = new(); private Settlement() { } public override string name() { return "German settlement"; } public override bool isBusinessDay(Date date) { - DayOfWeek w = date.DayOfWeek; + var w = date.DayOfWeek; int d = date.Day, dd = date.DayOfYear; - Month m = (Month)date.Month; - int y = date.Year; - int em = easterMonday(y); + var m = (Month)date.Month; + var y = date.Year; + var em = easterMonday(y); if (isWeekend(w) // New Year's Day @@ -187,19 +188,20 @@ public override bool isBusinessDay(Date date) return true; } } - class FrankfurtStockExchange : Calendar.WesternImpl + + private class FrankfurtStockExchange : WesternImpl { - public static readonly FrankfurtStockExchange Singleton = new FrankfurtStockExchange(); + public static readonly FrankfurtStockExchange Singleton = new(); private FrankfurtStockExchange() { } public override string name() { return "Frankfurt stock exchange"; } public override bool isBusinessDay(Date date) { - DayOfWeek w = date.DayOfWeek; + var w = date.DayOfWeek; int d = date.Day, dd = date.DayOfYear; - Month m = (Month)date.Month; - int y = date.Year; - int em = easterMonday(y); + var m = (Month)date.Month; + var y = date.Year; + var em = easterMonday(y); if (isWeekend(w) // New Year's Day @@ -220,19 +222,20 @@ public override bool isBusinessDay(Date date) return true; } } - class Xetra : Calendar.WesternImpl + + private class Xetra : WesternImpl { - public static readonly Xetra Singleton = new Xetra(); + public static readonly Xetra Singleton = new(); private Xetra() { } public override string name() { return "Xetra"; } public override bool isBusinessDay(Date date) { - DayOfWeek w = date.DayOfWeek; + var w = date.DayOfWeek; int d = date.Day, dd = date.DayOfYear; - Month m = (Month)date.Month; - int y = date.Year; - int em = easterMonday(y); + var m = (Month)date.Month; + var y = date.Year; + var em = easterMonday(y); if (isWeekend(w) // New Year's Day @@ -253,19 +256,20 @@ public override bool isBusinessDay(Date date) return true; } } - class Eurex : Calendar.WesternImpl + + private class Eurex : WesternImpl { - public static readonly Eurex Singleton = new Eurex(); + public static readonly Eurex Singleton = new(); private Eurex() { } public override string name() { return "Eurex"; } public override bool isBusinessDay(Date date) { - DayOfWeek w = date.DayOfWeek; + var w = date.DayOfWeek; int d = date.Day, dd = date.DayOfYear; - Month m = (Month)date.Month; - int y = date.Year; - int em = easterMonday(y); + var m = (Month)date.Month; + var y = date.Year; + var em = easterMonday(y); if (isWeekend(w) // New Year's Day @@ -288,19 +292,20 @@ public override bool isBusinessDay(Date date) return true; } } - class Euwax : Calendar.WesternImpl + + private class Euwax : WesternImpl { - public static readonly Euwax Singleton = new Euwax(); + public static readonly Euwax Singleton = new(); private Euwax() { } public override string name() { return "Euwax"; } public override bool isBusinessDay(Date date) { - DayOfWeek w = date.DayOfWeek; + var w = date.DayOfWeek; int d = date.Day, dd = date.DayOfYear; - Month m = (Month)date.Month; - int y = date.Year; - int em = easterMonday(y); + var m = (Month)date.Month; + var y = date.Year; + var em = easterMonday(y); if ((w == DayOfWeek.Saturday || w == DayOfWeek.Sunday) // New Year's Day diff --git a/src/QLNet/Time/Calendars/HongKong.cs b/src/QLNet/Time/Calendars/HongKong.cs index b140b14e..fe2bb6d8 100644 --- a/src/QLNet/Time/Calendars/HongKong.cs +++ b/src/QLNet/Time/Calendars/HongKong.cs @@ -1,5 +1,5 @@ /* - Copyright (C) 2008-2013 Andrea Maggiulli (a.maggiulli@gmail.com) + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) Copyright (C) 2008 Alessandro Duci Copyright (C) 2008, 2009 Siarhei Novik (snovik@gmail.com) @@ -58,19 +58,19 @@ public class HongKong : Calendar { public HongKong() : base(Impl.Singleton) { } - class Impl : Calendar.WesternImpl + private class Impl : WesternImpl { - public static readonly Impl Singleton = new Impl(); + public static readonly Impl Singleton = new(); private Impl() { } public override string name() { return "Hong Kong stock exchange"; } public override bool isBusinessDay(Date date) { - DayOfWeek w = date.DayOfWeek; + var w = date.DayOfWeek; int d = date.Day, dd = date.DayOfYear; - Month m = (Month)date.Month; - int y = date.Year; - int em = easterMonday(y); + var m = (Month)date.Month; + var y = date.Year; + var em = easterMonday(y); if (isWeekend(w) // New Year's Day diff --git a/src/QLNet/Time/Calendars/Hungary.cs b/src/QLNet/Time/Calendars/Hungary.cs index 44b88fd1..136022aa 100644 --- a/src/QLNet/Time/Calendars/Hungary.cs +++ b/src/QLNet/Time/Calendars/Hungary.cs @@ -1,6 +1,6 @@ /* Copyright (C) 2008 Alessandro Duci - Copyright (C) 2008 Andrea Maggiulli + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) Copyright (C) 2008 Siarhei Novik (snovik@gmail.com) This file is part of QLNet Project https://github.com/amaggiulli/qlnet @@ -46,19 +46,19 @@ public class Hungary : Calendar { public Hungary() : base(Impl.Singleton) { } - class Impl : Calendar.WesternImpl + private class Impl : WesternImpl { - public static readonly Impl Singleton = new Impl(); + public static readonly Impl Singleton = new(); private Impl() { } public override string name() { return "Hungary"; } public override bool isBusinessDay(Date date) { - DayOfWeek w = date.DayOfWeek; + var w = date.DayOfWeek; int d = date.Day, dd = date.DayOfYear; - Month m = (Month)date.Month; - int y = date.Year; - int em = easterMonday(y); + var m = (Month)date.Month; + var y = date.Year; + var em = easterMonday(y); if (isWeekend(w) // Good Friday (since 2017) || (dd == em - 3 && y >= 2017) diff --git a/src/QLNet/Time/Calendars/Iceland.cs b/src/QLNet/Time/Calendars/Iceland.cs index ee0ee3c4..93e24a7e 100644 --- a/src/QLNet/Time/Calendars/Iceland.cs +++ b/src/QLNet/Time/Calendars/Iceland.cs @@ -1,6 +1,7 @@ /* Copyright (C) 2008 Alessandro Duci Copyright (C) 2008 Siarhei Novik (snovik@gmail.com) + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) This file is part of QLNet Project https://github.com/amaggiulli/qlnet @@ -47,19 +48,19 @@ public class Iceland : Calendar { public Iceland() : base(Impl.Singleton) { } - class Impl : Calendar.WesternImpl + private class Impl : WesternImpl { - public static readonly Impl Singleton = new Impl(); + public static readonly Impl Singleton = new(); private Impl() { } public override string name() { return "Iceland stock exchange"; } public override bool isBusinessDay(Date date) { - DayOfWeek w = date.DayOfWeek; + var w = date.DayOfWeek; int d = date.Day, dd = date.DayOfYear; - Month m = (Month)date.Month; - int y = date.Year; - int em = easterMonday(y); + var m = (Month)date.Month; + var y = date.Year; + var em = easterMonday(y); if (isWeekend(w) // New Year's Day (possibly moved to Monday) diff --git a/src/QLNet/Time/Calendars/India.cs b/src/QLNet/Time/Calendars/India.cs index 2ccbede3..ceaf9bf0 100644 --- a/src/QLNet/Time/Calendars/India.cs +++ b/src/QLNet/Time/Calendars/India.cs @@ -1,5 +1,5 @@ /* - Copyright (C) 2008-2013 Andrea Maggiulli (a.maggiulli@gmail.com) + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) Copyright (C) 2008 Alessandro Duci Copyright (C) 2008, 2009 Siarhei Novik (snovik@gmail.com) @@ -64,19 +64,19 @@ public class India : Calendar { public India() : base(Impl.Singleton) { } - class Impl : Calendar.WesternImpl + private class Impl : WesternImpl { - public static readonly Impl Singleton = new Impl(); + public static readonly Impl Singleton = new(); private Impl() { } public override string name() { return "National Stock Exchange of India"; } public override bool isBusinessDay(Date date) { - DayOfWeek w = date.DayOfWeek; + var w = date.DayOfWeek; int d = date.Day, dd = date.DayOfYear; - Month m = (Month)date.Month; - int y = date.Year; - int em = easterMonday(y); + var m = (Month)date.Month; + var y = date.Year; + var em = easterMonday(y); if (isWeekend(w) // Republic Day diff --git a/src/QLNet/Time/Calendars/Indonesia.cs b/src/QLNet/Time/Calendars/Indonesia.cs index 9bb03f0d..77f9a0a4 100644 --- a/src/QLNet/Time/Calendars/Indonesia.cs +++ b/src/QLNet/Time/Calendars/Indonesia.cs @@ -1,5 +1,5 @@ /* - Copyright (C) 2008-2013 Andrea Maggiulli (a.maggiulli@gmail.com) + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) Copyright (C) 2008 Alessandro Duci Copyright (C) 2008, 2009 Siarhei Novik (snovik@gmail.com) @@ -69,31 +69,28 @@ public Indonesia(Market m) { // all calendar instances on the same market share the same // implementation instance - switch (m) + _impl = m switch { - case Market.BEJ: - case Market.JSX: - case Market.IDX: - calendar_ = BEJ.Singleton; - break; - default: - throw new ArgumentException("Unknown market: " + m); - } + Market.BEJ => BEJ.Singleton, + Market.JSX => BEJ.Singleton, + Market.IDX => BEJ.Singleton, + _ => throw new ArgumentException("Unknown market: " + m) + }; } - class BEJ : Calendar.WesternImpl + private class BEJ : WesternImpl { - public static readonly BEJ Singleton = new BEJ(); + public static readonly BEJ Singleton = new(); private BEJ() { } public override string name() { return "Jakarta stock exchange"; } public override bool isBusinessDay(Date date) { - DayOfWeek w = date.DayOfWeek; + var w = date.DayOfWeek; int d = date.Day, dd = date.DayOfYear; - Month m = (Month)date.Month; - int y = date.Year; - int em = easterMonday(y); + var m = (Month)date.Month; + var y = date.Year; + var em = easterMonday(y); if (isWeekend(w) // New Year's Day diff --git a/src/QLNet/Time/Calendars/Israel.cs b/src/QLNet/Time/Calendars/Israel.cs index 9740e28b..7feb4698 100644 --- a/src/QLNet/Time/Calendars/Israel.cs +++ b/src/QLNet/Time/Calendars/Israel.cs @@ -1,18 +1,21 @@ -// Copyright (C) 2008-2016 Andrea Maggiulli (a.maggiulli@gmail.com) -// -// This file is part of QLNet Project https://github.com/amaggiulli/qlnet -// QLNet is free software: you can redistribute it and/or modify it -// under the terms of the QLNet license. You should have received a -// copy of the license along with this program; if not, license is -// available at . -// -// QLNet is a based on QuantLib, a free-software/open-source library -// for financial quantitative analysts and developers - http://quantlib.org/ -// The QuantLib license is available online at http://quantlib.org/license.shtml. -// -// 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. +/* + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) + + This file is part of QLNet Project https://github.com/amaggiulli/qlnet + + QLNet is free software: you can redistribute it and/or modify it + under the terms of the QLNet license. You should have received a + copy of the license along with this program; if not, license is + available at . + + QLNet is a based on QuantLib, a free-software/open-source library + for financial quantitative analysts and developers - http://quantlib.org/ + The QuantLib license is available online at http://quantlib.org/license.shtml. + + 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. +*/ using System; namespace QLNet @@ -62,19 +65,19 @@ public Israel(Market m = Market.Settlement) switch (m) { case Market.Settlement: - calendar_ = TelAvivImpl.Singleton; + _impl = TelAvivImpl.Singleton; break; case Market.TASE: - calendar_ = TelAvivImpl.Singleton; + _impl = TelAvivImpl.Singleton; break; default: throw new ArgumentException("Unknown market: " + m); } } - class TelAvivImpl : Calendar + private class TelAvivImpl : CalendarImpl { - public static readonly TelAvivImpl Singleton = new TelAvivImpl(); + public static readonly TelAvivImpl Singleton = new(); private TelAvivImpl() { } public override string name() { return "Tel Aviv stock exchange"; } @@ -84,10 +87,10 @@ public override bool isWeekend(DayOfWeek w) } public override bool isBusinessDay(Date date) { - DayOfWeek w = date.DayOfWeek; - int d = date.Day; - Month m = (Month)date.Month; - int y = date.Year; + var w = date.DayOfWeek; + var d = date.Day; + var m = (Month)date.Month; + var y = date.Year; if (isWeekend(w) //Purim diff --git a/src/QLNet/Time/Calendars/Italy.cs b/src/QLNet/Time/Calendars/Italy.cs index fa71d96b..83ad0de6 100644 --- a/src/QLNet/Time/Calendars/Italy.cs +++ b/src/QLNet/Time/Calendars/Italy.cs @@ -1,6 +1,7 @@ /* Copyright (C) 2008 Alessandro Duci Copyright (C) 2008 Siarhei Novik (snovik@gmail.com) + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) This file is part of QLNet Project https://github.com/amaggiulli/qlnet @@ -74,33 +75,28 @@ public Italy(Market m) { // all calendar instances on the same market share the same // implementation instance - switch (m) + _impl = m switch { - case Market.Settlement: - calendar_ = Settlement.Singleton; - break; - case Market.Exchange: - calendar_ = Exchange.Singleton; - break; - default: - throw new ArgumentException("Unknown market: " + m); - } + Market.Settlement => Settlement.Singleton, + Market.Exchange => Exchange.Singleton, + _ => throw new ArgumentException("Unknown market: " + m) + }; } - class Settlement : Calendar.WesternImpl + private class Settlement : WesternImpl { - public static readonly Settlement Singleton = new Settlement(); + public static readonly Settlement Singleton = new(); private Settlement() { } public override string name() { return "Italian settlement"; } public override bool isBusinessDay(Date date) { - DayOfWeek w = date.DayOfWeek; + var w = date.DayOfWeek; int d = date.Day, dd = date.DayOfYear; - Month m = (Month)date.Month; - int y = date.Year; - int em = easterMonday(y); + var m = (Month)date.Month; + var y = date.Year; + var em = easterMonday(y); if (isWeekend(w) // New Year's Day @@ -132,19 +128,19 @@ public override bool isBusinessDay(Date date) } } - class Exchange : Calendar.WesternImpl + private class Exchange : WesternImpl { - public static readonly Exchange Singleton = new Exchange(); + public static readonly Exchange Singleton = new(); private Exchange() { } public override string name() { return "Milan stock exchange"; } public override bool isBusinessDay(Date date) { - DayOfWeek w = date.DayOfWeek; + var w = date.DayOfWeek; int d = date.Day, dd = date.DayOfYear; - Month m = (Month)date.Month; - int y = date.Year; - int em = easterMonday(y); + var m = (Month)date.Month; + var y = date.Year; + var em = easterMonday(y); if (isWeekend(w) // New Year's Day diff --git a/src/QLNet/Time/Calendars/Japan.cs b/src/QLNet/Time/Calendars/Japan.cs index e62f683d..3651de29 100644 --- a/src/QLNet/Time/Calendars/Japan.cs +++ b/src/QLNet/Time/Calendars/Japan.cs @@ -1,5 +1,5 @@ /* - Copyright (C) 2008-2013 Andrea Maggiulli (a.maggiulli@gmail.com) + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) Copyright (C) 2008 Alessandro Duci Copyright (C) 2008 Siarhei Novik (snovik@gmail.com) @@ -58,9 +58,9 @@ public class Japan : Calendar { public Japan() : base(Impl.Singleton) { } - class Impl : Calendar + private class Impl : CalendarImpl { - public static readonly Impl Singleton = new Impl(); + public static readonly Impl Singleton = new(); private Impl() { } public override string name() { return "Japan"; } @@ -70,19 +70,19 @@ public override bool isWeekend(DayOfWeek w) } public override bool isBusinessDay(Date date) { - DayOfWeek w = date.DayOfWeek; + var w = date.DayOfWeek; int d = date.Day, dd = date.DayOfYear; - Month m = (Month)date.Month; - int y = date.Year; + var m = (Month)date.Month; + var y = date.Year; // equinox calculation - double exact_vernal_equinox_time = 20.69115; - double exact_autumnal_equinox_time = 23.09; - double diff_per_year = 0.242194; - double moving_amount = (y - 2000) * diff_per_year; - int number_of_leap_years = (y - 2000) / 4 + (y - 2000) / 100 - (y - 2000) / 400; - int ve = (int)(exact_vernal_equinox_time + moving_amount - number_of_leap_years); // vernal equinox day - int ae = (int)(exact_autumnal_equinox_time + moving_amount - number_of_leap_years); // autumnal equinox day + var exact_vernal_equinox_time = 20.69115; + var exact_autumnal_equinox_time = 23.09; + var diff_per_year = 0.242194; + var moving_amount = (y - 2000) * diff_per_year; + var number_of_leap_years = (y - 2000) / 4 + (y - 2000) / 100 - (y - 2000) / 400; + var ve = (int)(exact_vernal_equinox_time + moving_amount - number_of_leap_years); // vernal equinox day + var ae = (int)(exact_autumnal_equinox_time + moving_amount - number_of_leap_years); // autumnal equinox day // checks if (isWeekend(w) // New Year's Day diff --git a/src/QLNet/Time/Calendars/JointCalendar.cs b/src/QLNet/Time/Calendars/JointCalendar.cs index 7771ac10..a7cd559c 100644 --- a/src/QLNet/Time/Calendars/JointCalendar.cs +++ b/src/QLNet/Time/Calendars/JointCalendar.cs @@ -1,5 +1,6 @@ /* Copyright (C) 2008 Siarhei Novik (snovik@gmail.com) + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) This file is part of QLNet Project https://github.com/amaggiulli/qlnet @@ -33,7 +34,7 @@ public enum JointCalendarRule for any of the given calendars */ } - private class Impl : Calendar + private class Impl : CalendarImpl { private JointCalendarRule rule_; private List calendars_ = new List(); diff --git a/src/QLNet/Time/Calendars/Mexico.cs b/src/QLNet/Time/Calendars/Mexico.cs index 95a467f1..70003235 100644 --- a/src/QLNet/Time/Calendars/Mexico.cs +++ b/src/QLNet/Time/Calendars/Mexico.cs @@ -1,5 +1,5 @@ /* - Copyright (C) 2008-2013 Andrea Maggiulli (a.maggiulli@gmail.com) + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) Copyright (C) 2008 Alessandro Duci Copyright (C) 2008 Siarhei Novik (snovik@gmail.com) @@ -47,19 +47,19 @@ public class Mexico : Calendar { public Mexico() : base(Impl.Singleton) { } - class Impl : Calendar.WesternImpl + private class Impl : WesternImpl { - public static readonly Impl Singleton = new Impl(); + public static readonly Impl Singleton = new(); private Impl() { } public override string name() { return "Mexican stock exchange"; } public override bool isBusinessDay(Date date) { - DayOfWeek w = date.DayOfWeek; + var w = date.DayOfWeek; int d = date.Day, dd = date.DayOfYear; - Month m = (Month)date.Month; - int y = date.Year; - int em = easterMonday(y); + var m = (Month)date.Month; + var y = date.Year; + var em = easterMonday(y); if (isWeekend(w) // New Year's Day || (d == 1 && m == Month.January) diff --git a/src/QLNet/Time/Calendars/NewZealand.cs b/src/QLNet/Time/Calendars/NewZealand.cs index 686cafa2..4a65cdaa 100644 --- a/src/QLNet/Time/Calendars/NewZealand.cs +++ b/src/QLNet/Time/Calendars/NewZealand.cs @@ -1,6 +1,6 @@ /* Copyright (C) 2008 Alessandro Duci - Copyright (C) 2008 Andrea Maggiulli + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) Copyright (C) 2008 Siarhei Novik (snovik@gmail.com) This file is part of QLNet Project https://github.com/amaggiulli/qlnet @@ -54,7 +54,7 @@ public class NewZealand : Calendar { public NewZealand() : base(Impl.Singleton) { } - class Impl : WesternImpl + private class Impl : WesternImpl { // https://www.beehive.govt.nz/release/matariki-holiday-dates-next-thirty-years-announced public Date[] MatarikiHolidays = new[] { @@ -91,17 +91,17 @@ class Impl : WesternImpl new Date(21, 6, 2052) }; - public static readonly Impl Singleton = new Impl(); + public static readonly Impl Singleton = new(); private Impl() { } public override string name() { return "New Zealand"; } public override bool isBusinessDay(Date date) { - DayOfWeek w = date.DayOfWeek; + var w = date.DayOfWeek; int d = date.Day, dd = date.DayOfYear; - Month m = (Month)date.Month; - int y = date.Year; - int em = easterMonday(y); + var m = (Month)date.Month; + var y = date.Year; + var em = easterMonday(y); if (isWeekend(w) // New Year's Day (possibly moved to Monday or Tuesday) || ((d == 1 || (d == 3 && (w == DayOfWeek.Monday || w == DayOfWeek.Tuesday))) && diff --git a/src/QLNet/Time/Calendars/Norway.cs b/src/QLNet/Time/Calendars/Norway.cs index e0dd27e9..611c4835 100644 --- a/src/QLNet/Time/Calendars/Norway.cs +++ b/src/QLNet/Time/Calendars/Norway.cs @@ -1,6 +1,6 @@ /* Copyright (C) 2008 Alessandro Duci - Copyright (C) 2008 Andrea Maggiulli + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) Copyright (C) 2008 Siarhei Novik (snovik@gmail.com) This file is part of QLNet Project https://github.com/amaggiulli/qlnet @@ -46,19 +46,19 @@ public class Norway : Calendar { public Norway() : base(Impl.Singleton) { } - class Impl : Calendar.WesternImpl + private class Impl : WesternImpl { - public static readonly Impl Singleton = new Impl(); + public static readonly Impl Singleton = new(); private Impl() { } public override string name() { return "Norway"; } public override bool isBusinessDay(Date date) { - DayOfWeek w = date.DayOfWeek; + var w = date.DayOfWeek; int d = date.Day, dd = date.DayOfYear; - Month m = (Month)date.Month; - int y = date.Year; - int em = easterMonday(y); + var m = (Month)date.Month; + var y = date.Year; + var em = easterMonday(y); if (isWeekend(w) // Holy Thursday diff --git a/src/QLNet/Time/Calendars/NullCalendar.cs b/src/QLNet/Time/Calendars/NullCalendar.cs index 43553bcf..2a6f5163 100644 --- a/src/QLNet/Time/Calendars/NullCalendar.cs +++ b/src/QLNet/Time/Calendars/NullCalendar.cs @@ -1,5 +1,6 @@ /* Copyright (C) 2008 Siarhei Novik (snovik@gmail.com) + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) This file is part of QLNet Project https://github.com/amaggiulli/qlnet @@ -26,9 +27,9 @@ public class NullCalendar : Calendar { public NullCalendar() : base(Impl.Singleton) { } - class Impl : Calendar + private class Impl : CalendarImpl { - public static readonly Impl Singleton = new Impl(); + public static readonly Impl Singleton = new(); private Impl() { } public override string name() { return "Null calendar"; } @@ -36,4 +37,4 @@ private Impl() { } public override bool isBusinessDay(Date d) { return true; } } } -} \ No newline at end of file +} diff --git a/src/QLNet/Time/Calendars/Poland.cs b/src/QLNet/Time/Calendars/Poland.cs index 4458d8ef..b4598984 100644 --- a/src/QLNet/Time/Calendars/Poland.cs +++ b/src/QLNet/Time/Calendars/Poland.cs @@ -1,6 +1,6 @@ /* Copyright (C) 2008 Alessandro Duci - Copyright (C) 2008 Andrea Maggiulli + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) Copyright (C) 2008 Siarhei Novik (snovik@gmail.com) This file is part of QLNet Project https://github.com/amaggiulli/qlnet @@ -47,19 +47,19 @@ public class Poland : Calendar { public Poland() : base(Impl.Singleton) { } - class Impl : Calendar.WesternImpl + private class Impl : WesternImpl { - public static readonly Impl Singleton = new Impl(); + public static readonly Impl Singleton = new(); private Impl() { } public override string name() { return "Poland"; } public override bool isBusinessDay(Date date) { - DayOfWeek w = date.DayOfWeek; + var w = date.DayOfWeek; int d = date.Day, dd = date.DayOfYear; - Month m = (Month)date.Month; - int y = date.Year; - int em = easterMonday(y); + var m = (Month)date.Month; + var y = date.Year; + var em = easterMonday(y); if (isWeekend(w) // Easter Monday diff --git a/src/QLNet/Time/Calendars/Romania.cs b/src/QLNet/Time/Calendars/Romania.cs index af1e69da..b52ea436 100644 --- a/src/QLNet/Time/Calendars/Romania.cs +++ b/src/QLNet/Time/Calendars/Romania.cs @@ -1,18 +1,21 @@ -// Copyright (C) 2008-2016 Andrea Maggiulli (a.maggiulli@gmail.com) -// -// This file is part of QLNet Project https://github.com/amaggiulli/qlnet -// QLNet is free software: you can redistribute it and/or modify it -// under the terms of the QLNet license. You should have received a -// copy of the license along with this program; if not, license is -// available at . -// -// QLNet is a based on QuantLib, a free-software/open-source library -// for financial quantitative analysts and developers - http://quantlib.org/ -// The QuantLib license is available online at http://quantlib.org/license.shtml. -// -// 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. +/* + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) + + This file is part of QLNet Project https://github.com/amaggiulli/qlnet + + QLNet is free software: you can redistribute it and/or modify it + under the terms of the QLNet license. You should have received a + copy of the license along with this program; if not, license is + available at . + + QLNet is a based on QuantLib, a free-software/open-source library + for financial quantitative analysts and developers - http://quantlib.org/ + The QuantLib license is available online at http://quantlib.org/license.shtml. + + 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. +*/ using System; namespace QLNet @@ -52,7 +55,7 @@ public Romania() : this(Market.BVB) { } public Romania(Market m) : base() { - calendar_ = m switch + _impl = m switch { Market.Public => PublicImpl.Singleton, Market.BVB => BVBImpl.Impl, @@ -60,9 +63,9 @@ public Romania(Market m) : base() }; } - private class PublicImpl : Calendar.OrthodoxImpl + private class PublicImpl : OrthodoxImpl { - public static readonly PublicImpl Singleton = new PublicImpl(); + public static readonly PublicImpl Singleton = new(); protected PublicImpl() { } public override string name() { return "Romania"; } @@ -105,7 +108,7 @@ public override bool isBusinessDay(Date date) private class BVBImpl : PublicImpl { - public static readonly BVBImpl Impl = new BVBImpl(); + public static readonly BVBImpl Impl = new(); private BVBImpl() { } public override string name() { return "Romania"; } public override bool isBusinessDay(Date date) diff --git a/src/QLNet/Time/Calendars/Russia.cs b/src/QLNet/Time/Calendars/Russia.cs index fe2414b7..a93e3a5b 100644 --- a/src/QLNet/Time/Calendars/Russia.cs +++ b/src/QLNet/Time/Calendars/Russia.cs @@ -1,5 +1,5 @@ /* - Copyright (C) 2008-2015 Andrea Maggiulli (a.maggiulli@gmail.com) + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) This file is part of QLNet Project https://github.com/amaggiulli/qlnet @@ -61,10 +61,10 @@ public Russia(Market m) switch (m) { case Market.Settlement: - calendar_ = SettlementImpl.Singleton; + _impl = SettlementImpl.Singleton; break; case Market.MOEX: - calendar_ = ExchangeImpl.Singleton; + _impl = ExchangeImpl.Singleton; break; default: throw new ArgumentException("Unknown market: " + m); @@ -72,9 +72,9 @@ public Russia(Market m) } - class SettlementImpl : Calendar.OrthodoxImpl + private class SettlementImpl : OrthodoxImpl { - public static readonly SettlementImpl Singleton = new SettlementImpl(); + public static readonly SettlementImpl Singleton = new(); private SettlementImpl() { } public override string name() { return "Russian settlement"; } @@ -120,11 +120,11 @@ private bool isExtraHolidaySettlementImpl(int d, Month month, int year) } public override bool isBusinessDay(Date date) { - DayOfWeek w = date.DayOfWeek; + var w = date.DayOfWeek; int d = date.Day, dd = date.DayOfYear; - Month m = (Month)date.Month; - int y = date.Year; - int em = easterMonday(y); + var m = (Month)date.Month; + var y = date.Year; + var em = easterMonday(y); if (isWeekend(w) // New Year's holidays @@ -161,9 +161,9 @@ public override bool isBusinessDay(Date date) } } - class ExchangeImpl : Calendar.OrthodoxImpl + private class ExchangeImpl : OrthodoxImpl { - public static readonly ExchangeImpl Singleton = new ExchangeImpl(); + public static readonly ExchangeImpl Singleton = new(); private ExchangeImpl() { } private bool isWorkingWeekend(int d, Month month, int year) @@ -279,10 +279,10 @@ private bool isExtraHolidayExchangeImpl(int d, Month month, int year) public override bool isBusinessDay(Date date) { - DayOfWeek w = date.DayOfWeek; - int d = date.Day ; - Month m = (Month)date.Month; - int y = date.Year; + var w = date.DayOfWeek; + var d = date.Day ; + var m = (Month)date.Month; + var y = date.Year; // the exchange was formally established in 2011, so data are only // available from 2012 to present diff --git a/src/QLNet/Time/Calendars/SaudiArabia.cs b/src/QLNet/Time/Calendars/SaudiArabia.cs index d06b559a..59130c47 100644 --- a/src/QLNet/Time/Calendars/SaudiArabia.cs +++ b/src/QLNet/Time/Calendars/SaudiArabia.cs @@ -1,7 +1,7 @@ /* Copyright (C) 2008 Alessandro Duci Copyright (C) 2008 Siarhei Novik (snovik@gmail.com) - Copyright (C) 2008, 2009 , 2010, 2011 Andrea Maggiulli (a.maggiulli@gmail.com) + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) This file is part of QLNet Project https://github.com/amaggiulli/qlnet @@ -45,9 +45,9 @@ public class SaudiArabia : Calendar { public SaudiArabia() : base(Impl.Singleton) { } - class Impl : Calendar + private class Impl : CalendarImpl { - public static readonly Impl Singleton = new Impl(); + public static readonly Impl Singleton = new(); private Impl() { } public override string name() { return "Tadawul"; } @@ -58,10 +58,10 @@ public override bool isWeekend(DayOfWeek w) public override bool isBusinessDay(Date date) { - DayOfWeek w = date.DayOfWeek; + var w = date.DayOfWeek; int d = date.Day, dd = date.DayOfYear; - Month m = (Month)date.Month; - int y = date.Year; + var m = (Month)date.Month; + var y = date.Year; if (isWeekend(w) // National Day @@ -86,4 +86,4 @@ public override bool isBusinessDay(Date date) } } } -} \ No newline at end of file +} diff --git a/src/QLNet/Time/Calendars/Singapore.cs b/src/QLNet/Time/Calendars/Singapore.cs index d755c957..c7301948 100644 --- a/src/QLNet/Time/Calendars/Singapore.cs +++ b/src/QLNet/Time/Calendars/Singapore.cs @@ -1,5 +1,5 @@ /* - Copyright (C) 2008-2013 Andrea Maggiulli (a.maggiulli@gmail.com) + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) Copyright (C) 2008 Alessandro Duci Copyright (C) 2008, 2009 Siarhei Novik (snovik@gmail.com) @@ -53,19 +53,19 @@ public class Singapore : Calendar { public Singapore() : base(Impl.Singleton) { } - class Impl : Calendar.WesternImpl + private class Impl : WesternImpl { - public static readonly Impl Singleton = new Impl(); + public static readonly Impl Singleton = new(); private Impl() { } public override string name() { return "Singapore exchange"; } public override bool isBusinessDay(Date date) { - DayOfWeek w = date.DayOfWeek; + var w = date.DayOfWeek; int d = date.Day, dd = date.DayOfYear; - Month m = (Month)date.Month; - int y = date.Year; - int em = easterMonday(y); + var m = (Month)date.Month; + var y = date.Year; + var em = easterMonday(y); if (isWeekend(w) // New Year's Day diff --git a/src/QLNet/Time/Calendars/Slovakia.cs b/src/QLNet/Time/Calendars/Slovakia.cs index 457d23f9..abbf289c 100644 --- a/src/QLNet/Time/Calendars/Slovakia.cs +++ b/src/QLNet/Time/Calendars/Slovakia.cs @@ -1,6 +1,7 @@ /* Copyright (C) 2008 Alessandro Duci Copyright (C) 2008 Siarhei Novik (snovik@gmail.com) + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) This file is part of QLNet Project https://github.com/amaggiulli/qlnet @@ -50,19 +51,19 @@ public class Slovakia : Calendar { public Slovakia() : base(Impl.Singleton) { } - class Impl : Calendar.WesternImpl + private class Impl : WesternImpl { - public static readonly Impl Singleton = new Impl(); + public static readonly Impl Singleton = new(); private Impl() { } public override string name() { return "Bratislava stock exchange"; } public override bool isBusinessDay(Date date) { - DayOfWeek w = date.DayOfWeek; + var w = date.DayOfWeek; int d = date.Day, dd = date.DayOfYear; - Month m = (Month)date.Month; - int y = date.Year; - int em = easterMonday(y); + var m = (Month)date.Month; + var y = date.Year; + var em = easterMonday(y); if (isWeekend(w) // New Year's Day || (d == 1 && m == Month.January) diff --git a/src/QLNet/Time/Calendars/SouthAfrica.cs b/src/QLNet/Time/Calendars/SouthAfrica.cs index bc023429..65e382ae 100644 --- a/src/QLNet/Time/Calendars/SouthAfrica.cs +++ b/src/QLNet/Time/Calendars/SouthAfrica.cs @@ -1,6 +1,6 @@ /* Copyright (C) 2008 Alessandro Duci - Copyright (C) 2008 Andrea Maggiulli + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) Copyright (C) 2008 Siarhei Novik (snovik@gmail.com) This file is part of QLNet Project https://github.com/amaggiulli/qlnet @@ -51,19 +51,19 @@ public class SouthAfrica : Calendar { public SouthAfrica() : base(Impl.Singleton) { } - class Impl : Calendar.WesternImpl + private class Impl : WesternImpl { - public static readonly Impl Singleton = new Impl(); + public static readonly Impl Singleton = new(); private Impl() { } public override string name() { return "South Africa"; } public override bool isBusinessDay(Date date) { - DayOfWeek w = date.DayOfWeek; + var w = date.DayOfWeek; int d = date.Day, dd = date.DayOfYear; - Month m = (Month)date.Month; - int y = date.Year; - int em = easterMonday(y); + var m = (Month)date.Month; + var y = date.Year; + var em = easterMonday(y); if (isWeekend(w) // New Year's Day (possibly moved to Monday) diff --git a/src/QLNet/Time/Calendars/SouthKorea.cs b/src/QLNet/Time/Calendars/SouthKorea.cs index 2bd20fb3..bbc9fc59 100644 --- a/src/QLNet/Time/Calendars/SouthKorea.cs +++ b/src/QLNet/Time/Calendars/SouthKorea.cs @@ -1,5 +1,5 @@ /* - Copyright (C) 2008-2016 Andrea Maggiulli (a.maggiulli@gmail.com) + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) Copyright (C) 2008 Alessandro Duci Copyright (C) 2008 Siarhei Novik (snovik@gmail.com) @@ -64,9 +64,11 @@ namespace QLNet */ public class SouthKorea : Calendar { - public enum Market { Settlement, //!< Public holidays - KRX //!< Korea exchange - } + public enum Market + { + Settlement, //!< Public holidays + KRX //!< Korea exchange + } public SouthKorea() : this(Market.KRX) { } public SouthKorea(Market m) @@ -77,20 +79,20 @@ public SouthKorea(Market m) switch (m) { case Market.Settlement: - calendar_ = Settlement.Singleton; + _impl = Settlement.Singleton; break; case Market.KRX: - calendar_ = KRX.Singleton; + _impl = KRX.Singleton; break; default: throw new ArgumentException("Unknown market: " + m); } } - class Settlement : Calendar + private class Settlement : CalendarImpl { - public static readonly Settlement Singleton = new Settlement(); - + public static readonly Settlement Singleton = new(); + protected Settlement(){} public override string name() { return "South-Korean settlement"; } public override bool isWeekend(DayOfWeek w) { @@ -98,10 +100,10 @@ public override bool isWeekend(DayOfWeek w) } public override bool isBusinessDay(Date date) { - DayOfWeek w = date.DayOfWeek; + var w = date.DayOfWeek; int d = date.Day, dd = date.DayOfYear; - Month m = (Month)date.Month; - int y = date.Year; + var m = (Month)date.Month; + var y = date.Year; if (isWeekend(w) // New Year's Day @@ -246,10 +248,10 @@ public override bool isBusinessDay(Date date) } } - class KRX : Settlement + private class KRX : Settlement { - public new static readonly KRX Singleton = new KRX(); - + public new static readonly KRX Singleton = new(); + private KRX(){} public override string name() { return "South-Korea exchange"; } public override bool isBusinessDay(Date date) { @@ -257,10 +259,10 @@ public override bool isBusinessDay(Date date) if (!base.isBusinessDay(date)) return false; - int d = date.Day; - DayOfWeek w = date.DayOfWeek; - Month m = (Month)date.Month; - int y = date.Year; + var d = date.Day; + var w = date.DayOfWeek; + var m = (Month)date.Month; + var y = date.Year; if ( // Year-end closing ((((d == 29 || d == 30) && w == DayOfWeek.Friday) || d == 31) diff --git a/src/QLNet/Time/Calendars/Sweden.cs b/src/QLNet/Time/Calendars/Sweden.cs index 68483487..8d59bcc4 100644 --- a/src/QLNet/Time/Calendars/Sweden.cs +++ b/src/QLNet/Time/Calendars/Sweden.cs @@ -1,6 +1,6 @@ /* Copyright (C) 2008 Alessandro Duci - Copyright (C) 2008 Andrea Maggiulli + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) Copyright (C) 2008 Siarhei Novik (snovik@gmail.com) This file is part of QLNet Project https://github.com/amaggiulli/qlnet @@ -49,18 +49,18 @@ public class Sweden : Calendar { public Sweden() : base(Impl.Singleton) { } - class Impl : Calendar.WesternImpl + private class Impl : WesternImpl { - public static readonly Impl Singleton = new Impl(); + public static readonly Impl Singleton = new(); private Impl() { } public override string name() { return "Sweden"; } public override bool isBusinessDay(Date date) { - DayOfWeek w = date.DayOfWeek; + var w = date.DayOfWeek; int d = date.Day, dd = date.DayOfYear; - Month m = (Month)date.Month; - int y = date.Year; + var m = (Month)date.Month; + var y = date.Year; int em = easterMonday(y); if (isWeekend(w) // Good Friday diff --git a/src/QLNet/Time/Calendars/Switzerland.cs b/src/QLNet/Time/Calendars/Switzerland.cs index fb52227f..a107004f 100644 --- a/src/QLNet/Time/Calendars/Switzerland.cs +++ b/src/QLNet/Time/Calendars/Switzerland.cs @@ -1,6 +1,6 @@ /* Copyright (C) 2008 Alessandro Duci - Copyright (C) 2008 Andrea Maggiulli + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) Copyright (C) 2008 Siarhei Novik (snovik@gmail.com) This file is part of QLNet Project https://github.com/amaggiulli/qlnet @@ -47,19 +47,19 @@ public class Switzerland : Calendar { public Switzerland() : base(Impl.Singleton) { } - class Impl : Calendar.WesternImpl + private class Impl : WesternImpl { - public static readonly Impl Singleton = new Impl(); + public static readonly Impl Singleton = new(); private Impl() { } public override string name() { return "Switzerland"; } public override bool isBusinessDay(Date date) { - DayOfWeek w = date.DayOfWeek; + var w = date.DayOfWeek; int d = date.Day, dd = date.DayOfYear; - Month m = (Month)date.Month; - int y = date.Year; - int em = easterMonday(y); + var m = (Month)date.Month; + var y = date.Year; + var em = easterMonday(y); if (isWeekend(w) // New Year's Day diff --git a/src/QLNet/Time/Calendars/TARGET.cs b/src/QLNet/Time/Calendars/TARGET.cs index fb6fe165..f72bcda0 100644 --- a/src/QLNet/Time/Calendars/TARGET.cs +++ b/src/QLNet/Time/Calendars/TARGET.cs @@ -1,5 +1,6 @@ /* Copyright (C) 2008 Siarhei Novik (snovik@gmail.com) + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) This file is part of QLNet Project https://github.com/amaggiulli/qlnet @@ -38,19 +39,19 @@ public class TARGET : Calendar { public TARGET() : base(Impl.Singleton) {} - class Impl : Calendar.WesternImpl + private class Impl : WesternImpl { - internal static readonly Impl Singleton = new Impl(); + internal static readonly Impl Singleton = new(); private Impl() {} public override string name() { return "TARGET calendar"; } public override bool isBusinessDay(Date date) { - DayOfWeek w = date.DayOfWeek; + var w = date.DayOfWeek; int d = date.Day, dd = date.DayOfYear; - Month m = (Month)date.Month; - int y = date.Year; - int em = easterMonday(y); + var m = (Month)date.Month; + var y = date.Year; + var em = easterMonday(y); if (isWeekend(w) // New Year's Day @@ -73,4 +74,4 @@ public override bool isBusinessDay(Date date) } } -} \ No newline at end of file +} diff --git a/src/QLNet/Time/Calendars/Taiwan.cs b/src/QLNet/Time/Calendars/Taiwan.cs index 6953bd49..5e0fcc55 100644 --- a/src/QLNet/Time/Calendars/Taiwan.cs +++ b/src/QLNet/Time/Calendars/Taiwan.cs @@ -1,5 +1,5 @@ /* - Copyright (C) 2008-2013 Andrea Maggiulli (a.maggiulli@gmail.com) + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) Copyright (C) 2008 Alessandro Duci Copyright (C) 2008, 2009 Siarhei Novik (snovik@gmail.com) @@ -50,9 +50,9 @@ public class Taiwan : Calendar { public Taiwan() : base(Impl.Singleton) { } - class Impl : Calendar + private class Impl : CalendarImpl { - public static readonly Impl Singleton = new Impl(); + public static readonly Impl Singleton = new(); private Impl() { } public override string name() { return "Taiwan stock exchange"; } @@ -62,10 +62,10 @@ public override bool isWeekend(DayOfWeek w) } public override bool isBusinessDay(Date date) { - DayOfWeek w = date.DayOfWeek; + var w = date.DayOfWeek; int d = date.Day, dd = date.DayOfYear; - Month m = (Month)date.Month; - int y = date.Year; + var m = (Month)date.Month; + var y = date.Year; if (isWeekend(w) // New Year's Day diff --git a/src/QLNet/Time/Calendars/Thailand.cs b/src/QLNet/Time/Calendars/Thailand.cs index 58b3afb5..a6c57e80 100644 --- a/src/QLNet/Time/Calendars/Thailand.cs +++ b/src/QLNet/Time/Calendars/Thailand.cs @@ -1,18 +1,21 @@ -// Copyright (C) 2008-2018 Andrea Maggiulli (a.maggiulli@gmail.com) -// -// This file is part of QLNet Project https://github.com/amaggiulli/qlnet -// QLNet is free software: you can redistribute it and/or modify it -// under the terms of the QLNet license. You should have received a -// copy of the license along with this program; if not, license is -// available at . -// -// QLNet is a based on QuantLib, a free-software/open-source library -// for financial quantitative analysts and developers - http://quantlib.org/ -// The QuantLib license is available online at http://quantlib.org/license.shtml. -// -// 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. +/* + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) + + This file is part of QLNet Project https://github.com/amaggiulli/qlnet + + QLNet is free software: you can redistribute it and/or modify it + under the terms of the QLNet license. You should have received a + copy of the license along with this program; if not, license is + available at . + + QLNet is a based on QuantLib, a free-software/open-source library + for financial quantitative analysts and developers - http://quantlib.org/ + The QuantLib license is available online at http://quantlib.org/license.shtml. + + 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. +*/ using System; namespace QLNet @@ -57,18 +60,18 @@ public class Thailand : Calendar { public Thailand() : base(Impl.Singleton) { } - class Impl : Calendar.WesternImpl + private class Impl : WesternImpl { - public static readonly Impl Singleton = new Impl(); + public static readonly Impl Singleton = new(); private Impl() { } public override string name() { return "Thailand stock exchange"; } public override bool isBusinessDay(Date date) { - DayOfWeek w = date.DayOfWeek; - int d = date.Day; - Month m = (Month)date.Month; - int y = date.Year; + var w = date.DayOfWeek; + var d = date.Day; + var m = (Month)date.Month; + var y = date.Year; if (isWeekend(w) // New Year's Day diff --git a/src/QLNet/Time/Calendars/Turkey.cs b/src/QLNet/Time/Calendars/Turkey.cs index fd9038ab..6ed4cd23 100644 --- a/src/QLNet/Time/Calendars/Turkey.cs +++ b/src/QLNet/Time/Calendars/Turkey.cs @@ -1,5 +1,5 @@ /* - Copyright (C) 2008-2013 Andrea Maggiulli (a.maggiulli@gmail.com) + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) Copyright (C) 2008 Alessandro Duci Copyright (C) 2008 Siarhei Novik (snovik@gmail.com) @@ -43,23 +43,23 @@ public class Turkey : Calendar { public Turkey() : base(Impl.Singleton) { } - class Impl : Calendar + private class Impl : CalendarImpl { - public static readonly Impl Singleton = new Impl(); + public static readonly Impl Singleton = new(); private Impl() { } public override string name() { return "Turkey"; } public override bool isWeekend(DayOfWeek w) { - return w == DayOfWeek.Saturday || w == DayOfWeek.Sunday; + return w is DayOfWeek.Saturday or DayOfWeek.Sunday; } public override bool isBusinessDay(Date date) { - DayOfWeek w = date.DayOfWeek; + var w = date.DayOfWeek; int d = date.Day, dd = date.DayOfYear; - Month m = (Month)date.Month; - int y = date.Year; + var m = (Month)date.Month; + var y = date.Year; if (isWeekend(w) // New Year's Day diff --git a/src/QLNet/Time/Calendars/Ukraine.cs b/src/QLNet/Time/Calendars/Ukraine.cs index 03117520..f7fd6e6c 100644 --- a/src/QLNet/Time/Calendars/Ukraine.cs +++ b/src/QLNet/Time/Calendars/Ukraine.cs @@ -1,5 +1,5 @@ /* - Copyright (C) 2008-2015 Andrea Maggiulli (a.maggiulli@gmail.com) + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) This file is part of QLNet Project https://github.com/amaggiulli/qlnet @@ -50,7 +50,7 @@ public Ukraine(Market m = Market.USE) switch (m) { case Market.USE: - calendar_ = Impl.Singleton; + _impl = Impl.Singleton; break; default: Utils.QL_FAIL("unknown market"); @@ -58,22 +58,24 @@ public Ukraine(Market m = Market.USE) } } - public enum Market { USE //!< Ukrainian stock exchange - } + public enum Market + { + USE //!< Ukrainian stock exchange + } - class Impl : Calendar.OrthodoxImpl + private class Impl : OrthodoxImpl { - public static readonly Impl Singleton = new Impl(); + public static readonly Impl Singleton = new(); private Impl() { } public override string name() { return "Ukrainian stock exchange"; } public override bool isBusinessDay(Date date) { - DayOfWeek w = date.DayOfWeek; + var w = date.DayOfWeek; int d = date.Day, dd = date.DayOfYear; - Month m = (Month)date.Month; - int y = date.Year; - int em = easterMonday(y); + var m = (Month)date.Month; + var y = date.Year; + var em = easterMonday(y); if (isWeekend(w) // New Year's Day (possibly moved to Monday) diff --git a/src/QLNet/Time/Calendars/UnitedKingdom.cs b/src/QLNet/Time/Calendars/UnitedKingdom.cs index d9a459f5..8316d618 100644 --- a/src/QLNet/Time/Calendars/UnitedKingdom.cs +++ b/src/QLNet/Time/Calendars/UnitedKingdom.cs @@ -1,6 +1,6 @@ /* Copyright (C) 2008 Siarhei Novik (snovik@gmail.com) - Copyright (C) 2008 Andrea Maggiulli + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) This file is part of QLNet Project https://github.com/amaggiulli/qlnet @@ -84,13 +84,13 @@ public UnitedKingdom(Market m) : base() switch (m) { case Market.Settlement: - calendar_ = Settlement.Singleton; + _impl = Settlement.Singleton; break; case Market.Exchange: - calendar_ = Exchange.Singleton; + _impl = Exchange.Singleton; break; case Market.Metals: - calendar_ = Metals.Singleton; + _impl = Metals.Singleton; break; default: throw new ArgumentException("Unknown market: " + m); @@ -125,11 +125,11 @@ private Settlement() { } public override string name() { return "UK settlement"; } public override bool isBusinessDay(Date date) { - DayOfWeek w = date.DayOfWeek; + var w = date.DayOfWeek; int d = date.Day, dd = date.DayOfYear; - Month m = (Month)date.Month; - int y = date.Year; - int em = easterMonday(y); + var m = (Month)date.Month; + var y = date.Year; + var em = easterMonday(y); if (isWeekend(w) // New Year's Day (possibly moved to Monday) @@ -149,18 +149,19 @@ public override bool isBusinessDay(Date date) return true; } } - private class Exchange : Calendar.WesternImpl + + private class Exchange : WesternImpl { - internal static readonly Exchange Singleton = new Exchange(); + internal static readonly Exchange Singleton = new(); private Exchange() { } public override string name() { return "London stock exchange"; } public override bool isBusinessDay(Date date) { - DayOfWeek w = date.DayOfWeek; + var w = date.DayOfWeek; int d = date.Day, dd = date.DayOfYear; - Month m = (Month)date.Month; - int y = date.Year; + var m = (Month)date.Month; + var y = date.Year; int em = easterMonday(y); if (isWeekend(w) // New Year's Day (possibly moved to Monday) @@ -180,19 +181,20 @@ public override bool isBusinessDay(Date date) return true; } } - private class Metals : Calendar.WesternImpl + + private class Metals : WesternImpl { - internal static readonly Metals Singleton = new Metals(); + internal static readonly Metals Singleton = new(); private Metals() { } public override string name() { return "London metals exchange"; } public override bool isBusinessDay(Date date) { - DayOfWeek w = date.DayOfWeek; + var w = date.DayOfWeek; int d = date.Day, dd = date.DayOfYear; - Month m = (Month)date.Month; - int y = date.Year; - int em = easterMonday(y); + var m = (Month)date.Month; + var y = date.Year; + var em = easterMonday(y); if (isWeekend(w) // New Year's Day (possibly moved to Monday) || ((d == 1 || ((d == 2 || d == 3) && w == DayOfWeek.Monday)) && m == Month.January) diff --git a/src/QLNet/Time/Calendars/UnitedStates.cs b/src/QLNet/Time/Calendars/UnitedStates.cs index 808182cd..da13e8c7 100644 --- a/src/QLNet/Time/Calendars/UnitedStates.cs +++ b/src/QLNet/Time/Calendars/UnitedStates.cs @@ -1,5 +1,6 @@ /* Copyright (C) 2008 Siarhei Novik (snovik@gmail.com) + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) This file is part of QLNet Project https://github.com/amaggiulli/qlnet @@ -207,40 +208,40 @@ public UnitedStates(Market m) : base() switch (m) { case Market.Settlement: - calendar_ = Settlement.Singleton; + _impl = Settlement.Singleton; break; case Market.NYSE: - calendar_ = NYSE.Singleton; + _impl = NYSE.Singleton; break; case Market.GovernmentBond: - calendar_ = GovernmentBond.Singleton; + _impl = GovernmentBond.Singleton; break; case Market.NERC: - calendar_ = NERC.Singleton; + _impl = NERC.Singleton; break; case Market.LiborImpact: - calendar_ = LiborImpact.Singleton; + _impl = LiborImpact.Singleton; break; case Market.FederalReserve: - calendar_ = FederalReserve.Singleton; + _impl = FederalReserve.Singleton; break; default: throw new ArgumentException("Unknown market: " + m); } } - private class Settlement : Calendar.WesternImpl + private class Settlement : WesternImpl { - public static readonly Settlement Singleton = new Settlement(); + public static readonly Settlement Singleton = new(); protected Settlement() { } public override string name() { return "US settlement"; } public override bool isBusinessDay(Date date) { - DayOfWeek w = date.DayOfWeek; - int d = date.Day; - Month m = (Month)date.Month; - int y = date.Year; + var w = date.DayOfWeek; + var d = date.Day; + var m = (Month)date.Month; + var y = date.Year; if (isWeekend(w) // New Year's Day (possibly moved to Monday if on Sunday) || ((d == 1 || (d == 2 && w == DayOfWeek.Monday)) && m == Month.January) @@ -272,19 +273,20 @@ public override bool isBusinessDay(Date date) return true; } } - private class NYSE : Calendar.WesternImpl + + private class NYSE : WesternImpl { - public static readonly NYSE Singleton = new NYSE(); + public static readonly NYSE Singleton = new(); private NYSE() { } public override string name() { return "New York stock exchange"; } public override bool isBusinessDay(Date date) { - DayOfWeek w = date.DayOfWeek; + var w = date.DayOfWeek; int d = date.Day, dd = date.DayOfYear; - Month m = (Month)date.Month; - int y = date.Year; - int em = easterMonday(y); + var m = (Month)date.Month; + var y = date.Year; + var em = easterMonday(y); if (isWeekend(w) // New Year's Day (possibly moved to Monday if on Sunday) || ((d == 1 || (d == 2 && w == DayOfWeek.Monday)) && m == Month.January) @@ -370,19 +372,20 @@ public override bool isBusinessDay(Date date) return true; } } - private class GovernmentBond : Calendar.WesternImpl + + private class GovernmentBond : WesternImpl { - public static readonly GovernmentBond Singleton = new GovernmentBond(); + public static readonly GovernmentBond Singleton = new(); private GovernmentBond() { } public override string name() { return "US government bond market"; } public override bool isBusinessDay(Date date) { - DayOfWeek w = date.DayOfWeek; + var w = date.DayOfWeek; int d = date.Day, dd = date.DayOfYear; - Month m = (Month)date.Month; - int y = date.Year; - int em = easterMonday(y); + var m = (Month)date.Month; + var y = date.Year; + var em = easterMonday(y); if (isWeekend(w) // New Year's Day (possibly moved to Monday if on Sunday) || ((d == 1 || (d == 2 && w == DayOfWeek.Monday)) && m == Month.January) @@ -424,18 +427,19 @@ public override bool isBusinessDay(Date date) return true; } } - private class NERC : Calendar.WesternImpl + + private class NERC : WesternImpl { - public static readonly NERC Singleton = new NERC(); + public static readonly NERC Singleton = new(); private NERC() { } public override string name() { return "North American Energy Reliability Council"; } public override bool isBusinessDay(Date date) { - DayOfWeek w = date.DayOfWeek; - int d = date.Day; - Month m = (Month)date.Month; - int y = date.Year; + var w = date.DayOfWeek; + var d = date.Day; + var m = (Month)date.Month; + var y = date.Year; if (isWeekend(w) // New Year's Day (possibly moved to Monday if on Sunday) || ((d == 1 || (d == 2 && w == DayOfWeek.Monday)) && m == Month.January) @@ -453,9 +457,10 @@ public override bool isBusinessDay(Date date) return true; } } + private class LiborImpact : Settlement { - public new static readonly LiborImpact Singleton = new LiborImpact(); + public new static readonly LiborImpact Singleton = new(); private LiborImpact() { } public override string name() { return "US with Libor impact"; } @@ -463,10 +468,10 @@ public override bool isBusinessDay(Date date) { // Since 2015 Independence Day only impacts Libor if it falls // on a weekday - DayOfWeek w = date.DayOfWeek; - int d = date.Day; - Month m = (Month)date.Month; - int y = date.year(); + var w = date.DayOfWeek; + var d = date.Day; + var m = (Month)date.Month; + var y = date.year(); if (((d == 5 && w == DayOfWeek.Monday) || (d == 3 && w == DayOfWeek.Friday)) && m == Month.July && y >= 2015) return true; @@ -474,21 +479,18 @@ public override bool isBusinessDay(Date date) } } - private class FederalReserve : Calendar.WesternImpl + private class FederalReserve : WesternImpl { - public static readonly FederalReserve Singleton = new FederalReserve(); - + public static readonly FederalReserve Singleton = new(); private FederalReserve() {} - public override string name() { return "Federal Reserve Bankwire System"; } - public override bool isBusinessDay(Date date) { // see https://www.frbservices.org/holidayschedules/ for details - DayOfWeek w = date.DayOfWeek; - int d = date.Day; - Month m = (Month)date.Month; - int y = date.year(); + var w = date.DayOfWeek; + var d = date.Day; + var m = (Month)date.Month; + var y = date.year(); if (isWeekend(w) // New Year's Day (possibly moved to Monday if on Sunday) || ((d == 1 || (d == 2 && w == DayOfWeek.Monday)) && m == Month.January) diff --git a/src/QLNet/Time/Calendars/WeekendsOnly.cs b/src/QLNet/Time/Calendars/WeekendsOnly.cs index 77b2c16e..55cd5b8e 100644 --- a/src/QLNet/Time/Calendars/WeekendsOnly.cs +++ b/src/QLNet/Time/Calendars/WeekendsOnly.cs @@ -1,5 +1,5 @@ /* - Copyright (C) 2008, 2009 , 2010 Andrea Maggiulli (a.maggiulli@gmail.com) + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) This file is part of QLNet Project https://github.com/amaggiulli/qlnet @@ -23,9 +23,9 @@ public class WeekendsOnly : Calendar { public WeekendsOnly() : base(Impl.Singleton) { } - class Impl : Calendar.WesternImpl + private class Impl : WesternImpl { - public static readonly Impl Singleton = new Impl(); + public static readonly Impl Singleton = new(); private Impl() { } public override string name() { return "weekends only"; } diff --git a/tests/QLNet.Tests/T_BusinessDayConvention.cs b/tests/QLNet.Tests/T_BusinessDayConvention.cs index a7877113..4fff9e0d 100644 --- a/tests/QLNet.Tests/T_BusinessDayConvention.cs +++ b/tests/QLNet.Tests/T_BusinessDayConvention.cs @@ -108,7 +108,7 @@ public void testConventions() int n = testCases.Length; for (int i = 0; i < n; i++) { - Calendar calendar = new Calendar(testCases[i].calendar); + Calendar calendar = testCases[i].calendar; Date result = calendar.advance(testCases[i].start, testCases[i].period, testCases[i].convention, testCases[i].endOfMonth); QAssert.IsTrue(result == testCases[i].result, diff --git a/tests/QLNet.Tests/T_Calendars.cs b/tests/QLNet.Tests/T_Calendars.cs index 8a446155..9fc7b780 100644 --- a/tests/QLNet.Tests/T_Calendars.cs +++ b/tests/QLNet.Tests/T_Calendars.cs @@ -163,7 +163,7 @@ public void testUSSettlement() expectedHol.Add(new Date(26, Month.December, 2005)); Calendar c = new UnitedStates(UnitedStates.Market.Settlement); - List hol = Calendar.holidayList(c, new Date(1, Month.January, 2004), + List hol = c.holidayList(new Date(1, Month.January, 2004), new Date(31, Month.December, 2005)); if (hol.Count != expectedHol.Count) QAssert.Fail("there were " + expectedHol.Count @@ -187,7 +187,7 @@ public void testUSSettlement() expectedHol.Add(new Date(23, Month.November, 1961)); expectedHol.Add(new Date(25, Month.December, 1961)); - hol = Calendar.holidayList(c, new Date(1, Month.January, 1961), new Date(31, Month.December, 1961)); + hol = c.holidayList(new Date(1, Month.January, 1961), new Date(31, Month.December, 1961)); if (hol.Count != expectedHol.Count) QAssert.Fail("there were " + expectedHol.Count + @@ -221,7 +221,7 @@ public void testUSGovernmentBondMarket() expectedHol.Add(new Date(24, Month.December, 2004)); Calendar c = new UnitedStates(UnitedStates.Market.GovernmentBond); - List hol = Calendar.holidayList(c, new Date(1, Month.January, 2004), new Date(31, Month.December, 2004)); + List hol = c.holidayList(new Date(1, Month.January, 2004), new Date(31, Month.December, 2004)); for (int i = 0; i < Math.Min(hol.Count, expectedHol.Count); i++) { @@ -270,7 +270,7 @@ public void testUSNewYorkStockExchange() expectedHol.Add(new Date(25, Month.December, 2006)); Calendar c = new UnitedStates(UnitedStates.Market.NYSE); - List hol = Calendar.holidayList(c, new Date(1, Month.January, 2004), new Date(31, Month.December, 2006)); + List hol = c.holidayList(new Date(1, Month.January, 2004), new Date(31, Month.December, 2006)); int i; for (i = 0; i < Math.Min(hol.Count, expectedHol.Count); i++) @@ -385,7 +385,7 @@ public void testTARGET() expectedHol.Add(new Date(26, Month.December, 2006)); Calendar c = new TARGET(); - List hol = Calendar.holidayList(c, new Date(1, Month.January, 1999), new Date(31, Month.December, 2006)); + List hol = c.holidayList(new Date(1, Month.January, 1999), new Date(31, Month.December, 2006)); for (int i = 0; i < Math.Min(hol.Count, expectedHol.Count); i++) { @@ -419,7 +419,7 @@ public void testGermanyFrankfurt() expectedHol.Add(new Date(24, Month.December, 2004)); Calendar c = new Germany(Germany.Market.FrankfurtStockExchange); - List hol = Calendar.holidayList(c, new Date(1, Month.January, 2003), new Date(31, Month.December, 2004)); + List hol = c.holidayList(new Date(1, Month.January, 2003), new Date(31, Month.December, 2004)); for (int i = 0; i < Math.Min(hol.Count, expectedHol.Count); i++) { if (hol[i] != expectedHol[i]) @@ -453,8 +453,7 @@ public void testGermanyEurex() expectedHol.Add(new Date(31, Month.December, 2004)); Calendar c = new Germany(Germany.Market.Eurex); - List hol = Calendar.holidayList(c, new Date(1, Month.January, 2003), - new Date(31, Month.December, 2004)); + List hol = c.holidayList(new Date(1, Month.January, 2003), new Date(31, Month.December, 2004)); for (int i = 0; i < Math.Min(hol.Count, expectedHol.Count); i++) { if (hol[i] != expectedHol[i]) @@ -486,7 +485,7 @@ public void testGermanyXetra() expectedHol.Add(new Date(24, Month.December, 2004)); Calendar c = new Germany(Germany.Market.Xetra); - List hol = Calendar.holidayList(c, new Date(1, Month.January, 2003), new Date(31, Month.December, 2004)); + List hol = c.holidayList(new Date(1, Month.January, 2003), new Date(31, Month.December, 2004)); for (int i = 0; i < Math.Min(hol.Count, expectedHol.Count); i++) { if (hol[i] != expectedHol[i]) @@ -542,7 +541,7 @@ public void testUKSettlement() expectedHol.Add(new Date(26, Month.December, 2007)); Calendar c = new UnitedKingdom(UnitedKingdom.Market.Settlement); - List hol = Calendar.holidayList(c, new Date(1, Month.January, 2004), new Date(31, Month.December, 2007)); + List hol = c.holidayList(new Date(1, Month.January, 2004), new Date(31, Month.December, 2007)); for (int i = 0; i < Math.Min(hol.Count, expectedHol.Count); i++) { if (hol[i] != expectedHol[i]) @@ -599,7 +598,7 @@ public void testUKExchange() expectedHol.Add(new Date(26, Month.December, 2007)); Calendar c = new UnitedKingdom(UnitedKingdom.Market.Exchange); - List hol = Calendar.holidayList(c, new Date(1, Month.January, 2004), new Date(31, Month.December, 2007)); + List hol = c.holidayList(new Date(1, Month.January, 2004), new Date(31, Month.December, 2007)); for (int i = 0; i < Math.Min(hol.Count, expectedHol.Count); i++) { if (hol[i] != expectedHol[i]) @@ -656,7 +655,7 @@ public void testUKMetals() expectedHol.Add(new Date(26, Month.December, 2007)); Calendar c = new UnitedKingdom(UnitedKingdom.Market.Metals); - List hol = Calendar.holidayList(c, new Date(1, Month.January, 2004), new Date(31, Month.December, 2007)); + List hol = c.holidayList(new Date(1, Month.January, 2004), new Date(31, Month.December, 2007)); for (int i = 0; i < Math.Min(hol.Count, expectedHol.Count); i++) { if (hol[i] != expectedHol[i]) @@ -703,7 +702,7 @@ public void testItalyExchange() expectedHol.Add(new Date(31, Month.December, 2004)); Calendar c = new Italy(Italy.Market.Exchange); - List hol = Calendar.holidayList(c, new Date(1, Month.January, 2002), new Date(31, Month.December, 2004)); + List hol = c.holidayList(new Date(1, Month.January, 2002), new Date(31, Month.December, 2004)); for (int i = 0; i < Math.Min(hol.Count, expectedHol.Count); i++) { if (hol[i] != expectedHol[i]) @@ -750,7 +749,7 @@ public void testBrazil() expectedHol.Add(new Date(25, Month.December, 2006)); Calendar c = new Brazil(); - List hol = Calendar.holidayList(c, new Date(1, Month.January, 2005), new Date(31, Month.December, 2006)); + List hol = c.holidayList(new Date(1, Month.January, 2005), new Date(31, Month.December, 2006)); for (int i = 0; i < Math.Min(hol.Count, expectedHol.Count); i++) { if (hol[i] != expectedHol[i]) @@ -841,8 +840,7 @@ public void testSouthKoreanSettlement() expectedHol.Add(new Date(25, Month.December, 2007)); Calendar c = new SouthKorea(SouthKorea.Market.Settlement); - List hol = Calendar.holidayList(c, new Date(1, Month.January, 2004), - new Date(31, Month.December, 2007)); + List hol = c.holidayList(new Date(1, Month.January, 2004), new Date(31, Month.December, 2007)); for (int i = 0; i < Math.Min(hol.Count, expectedHol.Count); i++) { if (hol[i] != expectedHol[i]) @@ -936,8 +934,7 @@ public void testKoreaStockExchange() expectedHol.Add(new Date(31, Month.December, 2007)); Calendar c = new SouthKorea(SouthKorea.Market.KRX); - List hol = Calendar.holidayList(c, new Date(1, Month.January, 2004), - new Date(31, Month.December, 2007)); + List hol = c.holidayList(new Date(1, Month.January, 2004), new Date(31, Month.December, 2007)); for (int i = 0; i < Math.Min(hol.Count, expectedHol.Count); i++) { @@ -1113,8 +1110,7 @@ public void testChinaSSE() expectedHol.Add(new Date(7, Month.October, 2021)); Calendar c = new China(China.Market.SSE); - List hol = Calendar.holidayList(c, new Date(1, Month.January, 2014), - new Date(31, Month.December, 2021)); + List hol = c.holidayList(new Date(1, Month.January, 2014), new Date(31, Month.December, 2021)); for (int i = 0; i < Math.Min(hol.Count, expectedHol.Count); i++) { From e185ddc7a73286d143f197943f2104e8c569064f Mon Sep 17 00:00:00 2001 From: Andrea Maggiulli Date: Sat, 19 Nov 2022 20:13:39 +0100 Subject: [PATCH 24/35] Updated a bunch of calendars up to 2022. --- src/QLNet/Time/Calendars/Australia.cs | 10 ++++++---- src/QLNet/Time/Calendars/China.cs | 17 ++++++++++++++++- src/QLNet/Time/Calendars/CzechRepublic.cs | 2 ++ src/QLNet/Time/Calendars/Iceland.cs | 5 ++--- src/QLNet/Time/Calendars/NewZealand.cs | 3 ++- src/QLNet/Time/Calendars/UnitedKingdom.cs | 6 +++++- 6 files changed, 33 insertions(+), 10 deletions(-) diff --git a/src/QLNet/Time/Calendars/Australia.cs b/src/QLNet/Time/Calendars/Australia.cs index 8ec68563..167ec698 100644 --- a/src/QLNet/Time/Calendars/Australia.cs +++ b/src/QLNet/Time/Calendars/Australia.cs @@ -62,7 +62,7 @@ public override bool isBusinessDay(Date date) if (isWeekend(w) // New Year's Day (possibly moved to Monday) - || (d == 1 && m == Month.January) + || ((d == 1 || ((d == 2 || d == 3) && w == DayOfWeek.Monday)) && m == Month.January) // Australia Day, January 26th (possibly moved to Monday) || ((d == 26 || ((d == 27 || d == 28) && w == DayOfWeek.Monday)) && m == Month.January) @@ -70,8 +70,8 @@ public override bool isBusinessDay(Date date) || (dd == em - 3) // Easter Monday || (dd == em) - // ANZAC Day, April 25th (possibly moved to Monday) - || ((d == 25 || (d == 26 && w == DayOfWeek.Monday)) && m == Month.April) + // ANZAC Day, April 25th + || (d == 25 && m == Month.April) // Queen's Birthday, second Monday in June || ((d > 7 && d <= 14) && w == DayOfWeek.Monday && m == Month.June) // Bank Holiday, first Monday in August @@ -83,7 +83,9 @@ public override bool isBusinessDay(Date date) && m == Month.December) // Boxing Day, December 26th (possibly Monday or Tuesday) || ((d == 26 || (d == 28 && (w == DayOfWeek.Monday || w == DayOfWeek.Tuesday))) - && m == Month.December)) + && m == Month.December) + // National Day of Mourning for Her Majesty, September 22 (only 2022) + || (d == 22 && m == Month.September && y == 2022)) return false; return true; } diff --git a/src/QLNet/Time/Calendars/China.cs b/src/QLNet/Time/Calendars/China.cs index 93dae28e..d37787ed 100644 --- a/src/QLNet/Time/Calendars/China.cs +++ b/src/QLNet/Time/Calendars/China.cs @@ -113,6 +113,7 @@ public override bool isBusinessDay(Date date) || (y == 2019 && d == 1 && m == Month.January) || (y == 2020 && d == 1 && m == Month.January) || (y == 2021 && d == 1 && m == Month.January) + || (y == 2022 && d == 3 && m == Month.January) // Chinese New Year || (y == 2004 && d >= 19 && d <= 28 && m == Month.January) || (y == 2005 && d >= 7 && d <= 15 && m == Month.February) @@ -135,6 +136,7 @@ public override bool isBusinessDay(Date date) || (y == 2019 && d >= 4 && d <= 8 && m == Month.February) || (y == 2020 && (d == 24 || (d >= 27 && d <= 31)) && m == Month.January) || (y == 2021 && (d == 11 || d == 12 || d == 15 || d == 16 || d == 17) && m == Month.February) + || (y == 2022 && ((d == 31 && m == Month.January) || (d <= 4 && m == Month.February))) // Ching Ming Festival || (y <= 2008 && d == 4 && m == Month.April) || (y == 2009 && d == 6 && m == Month.April) @@ -150,6 +152,7 @@ public override bool isBusinessDay(Date date) || (y == 2019 && d == 5 && m == Month.April) || (y == 2020 && d == 6 && m == Month.April) || (y == 2021 && d == 5 && m == Month.April) + || (y == 2022 && d >= 4 && d <= 5 && m == Month.April) // Labor Day || (y <= 2007 && d >= 1 && d <= 7 && m == Month.May) || (y == 2008 && d >= 1 && d <= 2 && m == Month.May) @@ -168,6 +171,7 @@ public override bool isBusinessDay(Date date) || (y == 2019 && d >= 1 && d <= 3 && m == Month.May) || (y == 2020 && (d == 1 || d == 4 || d == 5) && m == Month.May) || (y == 2021 && (d == 3 || d == 4 || d == 5) && m == Month.May) + || (y == 2022 && d >= 2 && d <= 4 && m == Month.May) // Tuen Ng Festival || (y <= 2008 && d == 9 && m == Month.June) || (y == 2009 && (d == 28 || d == 29) && m == Month.May) @@ -183,6 +187,7 @@ public override bool isBusinessDay(Date date) || (y == 2019 && d == 7 && m == Month.June) || (y == 2020 && d >= 25 && d <= 26 && m == Month.June) || (y == 2021 && d == 14 && m == Month.June) + || (y == 2022 && d == 3 && m == Month.June) // Mid-Autumn Festival || (y <= 2008 && d == 15 && m == Month.September) || (y == 2010 && d >= 22 && d <= 24 && m == Month.September) @@ -195,6 +200,7 @@ public override bool isBusinessDay(Date date) || (y == 2018 && d == 24 && m == Month.September) || (y == 2019 && d == 13 && m == Month.September) || (y == 2021 && (d == 20 || d == 21) && m == Month.September) + || (y == 2022 && d == 12 && m == Month.September) // National Day || (y <= 2007 && d >= 1 && d <= 7 && m == Month.October) || (y == 2008 && ((d >= 29 && m == Month.September) || @@ -213,6 +219,7 @@ public override bool isBusinessDay(Date date) || (y == 2020 && d >= 1 && d <= 2 && m == Month.October) || (y == 2020 && d >= 5 && d <= 8 && m == Month.October) || (y == 2021 && (d == 1 || d == 4 || d == 5 || d == 6 || d == 7) && m == Month.October) + || (y == 2022 && d >= 3 && d <= 7 && m == Month.October) // 70th anniversary of the victory of anti-Japaneses war || (y == 2015 && d >= 3 && d <= 4 && m == Month.September) ) @@ -363,7 +370,15 @@ public override bool isBusinessDay(Date date) new Date(8, Month.May, 2021), new Date(18, Month.September, 2021), new Date(26, Month.September, 2021), - new Date(9, Month.October, 2021) + new Date(9, Month.October, 2021), + // 2022 + new Date(29, Month.January, 2022), + new Date(30, Month.January, 2022), + new Date(2, Month.April, 2022), + new Date(24, Month.April, 2022), + new Date(7, Month.May, 2022), + new Date(8, Month.October, 2022), + new Date(9, Month.October, 2022) }; // If it is already a SSE business day, it must be a IB business day diff --git a/src/QLNet/Time/Calendars/CzechRepublic.cs b/src/QLNet/Time/Calendars/CzechRepublic.cs index c94d49b7..8544cc22 100644 --- a/src/QLNet/Time/Calendars/CzechRepublic.cs +++ b/src/QLNet/Time/Calendars/CzechRepublic.cs @@ -64,6 +64,8 @@ public override bool isBusinessDay(Date date) if (isWeekend(w) // New Year's Day || (d == 1 && m == Month.January) + // Good Friday + || (dd == em - 3 && y >= 2016) // Easter Monday || (dd == em) // Labour Day diff --git a/src/QLNet/Time/Calendars/Iceland.cs b/src/QLNet/Time/Calendars/Iceland.cs index 93e24a7e..47ef562b 100644 --- a/src/QLNet/Time/Calendars/Iceland.cs +++ b/src/QLNet/Time/Calendars/Iceland.cs @@ -63,9 +63,8 @@ public override bool isBusinessDay(Date date) var em = easterMonday(y); if (isWeekend(w) - // New Year's Day (possibly moved to Monday) - || ((d == 1 || ((d == 2 || d == 3) && w == DayOfWeek.Monday)) - && m == Month.January) + // New Year's Day + || (d == 1 && m == Month.January) // Holy Thursday || (dd == em - 4) // Good Friday diff --git a/src/QLNet/Time/Calendars/NewZealand.cs b/src/QLNet/Time/Calendars/NewZealand.cs index 4a65cdaa..9882eb67 100644 --- a/src/QLNet/Time/Calendars/NewZealand.cs +++ b/src/QLNet/Time/Calendars/NewZealand.cs @@ -118,8 +118,9 @@ public override bool isBusinessDay(Date date) || (dd == em - 3) // Easter Monday || (dd == em) - // ANZAC Day. April 25th + // ANZAC Day. April 25th ("Mondayised" since 2013) || (d == 25 && m == Month.April) + || ((d == 26 || d == 27) && w == DayOfWeek.Monday && m == Month.April && y > 2013) // Queen's Birthday, first Monday in June || (d <= 7 && w == DayOfWeek.Monday && m == Month.June) // Matariki Holiday diff --git a/src/QLNet/Time/Calendars/UnitedKingdom.cs b/src/QLNet/Time/Calendars/UnitedKingdom.cs index 8316d618..7b7290f8 100644 --- a/src/QLNet/Time/Calendars/UnitedKingdom.cs +++ b/src/QLNet/Time/Calendars/UnitedKingdom.cs @@ -114,7 +114,11 @@ protected static bool isBankHoliday(int d, DayOfWeek w, Month m, int y) // last Monday of August (Summer Bank Holiday) || (d >= 25 && w == DayOfWeek.Monday && m == Month.August) // April 29th, 2011 only (Royal Wedding Bank Holiday) - || (d == 29 && m == Month.April && y == 2011); + || (d == 29 && m == Month.April && y == 2011) + // September 19th, 2022 only (The Queen's Funeral Bank Holiday) + || (d == 19 && m == Month.September && y == 2022) + // May 8th, 2023 (King Charles III Coronation Bank Holiday) + || (d == 8 && m == Month.May && y == 2023); } private class Settlement : Calendar.WesternImpl From e1eee4f71022ff6fc9a285126d45322b1d15c3f0 Mon Sep 17 00:00:00 2001 From: Andrea Maggiulli Date: Tue, 22 Nov 2022 21:56:24 +0100 Subject: [PATCH 25/35] Added new method to Schedule to directly add irregular dates. --- src/QLNet/Time/Schedule.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/QLNet/Time/Schedule.cs b/src/QLNet/Time/Schedule.cs index 51f5ea3f..679116aa 100644 --- a/src/QLNet/Time/Schedule.cs +++ b/src/QLNet/Time/Schedule.cs @@ -592,6 +592,20 @@ public Schedule until(Date truncationDate) } public int Count { get { return dates_.Count; } } + public void addIrregularDates(DateTime[] dates) + { + Utils.QL_REQUIRE(dates is { Length: > 0 }, ()=> "invalid dates"); + foreach (var date1 in dates) + { + if (!dates_.Exists(x => x == (Date)date1) && + date1 < (DateTime)dates_.Max(x=>x)) + { + var index = dates_.FindIndex((x => x > (Date)date1)); + dates_.Insert(index, date1); + isRegular_.Insert(index, false); + } + } + } private Date nextTwentieth(Date d, DateGeneration.Rule rule) { From 78511587c84e13dba99fc23a2bd98340efc35093 Mon Sep 17 00:00:00 2001 From: Andrea Maggiulli Date: Thu, 15 Dec 2022 18:53:13 +0100 Subject: [PATCH 26/35] Fixing WAL calculation. --- .../Pricingengines/Bond/BondFunctions.cs | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/QLNet/Pricingengines/Bond/BondFunctions.cs b/src/QLNet/Pricingengines/Bond/BondFunctions.cs index b3f694a9..68315450 100644 --- a/src/QLNet/Pricingengines/Bond/BondFunctions.cs +++ b/src/QLNet/Pricingengines/Bond/BondFunctions.cs @@ -528,7 +528,7 @@ public static DateTime WeightedAverageLife(DateTime today, List amounts, { Utils.QL_REQUIRE(amounts.Count == schedule.Count, () => "Amount list is incompatible with schedule"); - double totAmount = amounts.Where((t, x) => schedule[x] > today).Sum(); + var totAmount = amounts.Where((t, x) => schedule[x] > today).Sum(); if (totAmount.IsEqual(0)) return today; @@ -536,18 +536,25 @@ public static DateTime WeightedAverageLife(DateTime today, List amounts, double wal = 0; DayCounter dc = new Actual365Fixed(); - for (int x = 0; x < amounts.Count; x++) + for (var x = 0; x < amounts.Count; x++) { if (schedule[x] <= today) continue; - double per = amounts[x] / totAmount; - double years = dc.yearFraction(today, schedule[x]); - double yearw = years * per; + var per = amounts[x] / totAmount; + var years = dc.yearFraction(today, schedule[x]); + var yearw = years * per; wal += yearw; } - return today.AddDays(wal * 365).Date; + var value = wal * 365; + var millisValue = value * MILLIS_PER_DAY + (value >= 0 ? 0.5 : -0.5); + + return today.AddTicks((long)millisValue * TICKS_PER_MILLISECOND).Date; } + + private const double MILLIS_PER_DAY = 86400000; + private const long TICKS_PER_MILLISECOND = 10000; + #endregion #region Get all bonfunctions From 5a215582721379da2d9b6809f1328094033602b6 Mon Sep 17 00:00:00 2001 From: Andrea Maggiulli Date: Thu, 15 Dec 2022 18:57:12 +0100 Subject: [PATCH 27/35] [skip ci] Update ReadMe --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index dc2ccbe4..19a9e3cf 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ QLNet also contains new developments on the bond market like MBS, Amortized Cost [![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=QLNet&metric=sqale_rating)](https://sonarcloud.io/dashboard?id=QLNet) [![Reliability Rating](https://sonarcloud.io/api/project_badges/measure?project=QLNet&metric=reliability_rating)](https://sonarcloud.io/dashboard?id=QLNet) [![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=QLNet&metric=security_rating)](https://sonarcloud.io/dashboard?id=QLNet) -[![Technical Debt](https://sonarcloud.io/api/project_badges/measure?project=QLNet-develop&metric=sqale_index)](https://sonarcloud.io/dashboard?id=QLNet-develop) +[![Technical Debt](https://sonarcloud.io/api/project_badges/measure?project=QLNet-develop&metric=sqale_index)](https://sonarcloud.io/dashboard?id=QLNet) ## Development workflow From e05578f554a625c579637746e45c424c415d887d Mon Sep 17 00:00:00 2001 From: Andrea Maggiulli Date: Thu, 15 Dec 2022 19:06:04 +0100 Subject: [PATCH 28/35] [skip ci] Update ReadMe --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 19a9e3cf..7f2eda8e 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,8 @@ QLNet also contains new developments on the bond market like MBS, Amortized Cost [![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=QLNet&metric=sqale_rating)](https://sonarcloud.io/dashboard?id=QLNet) [![Reliability Rating](https://sonarcloud.io/api/project_badges/measure?project=QLNet&metric=reliability_rating)](https://sonarcloud.io/dashboard?id=QLNet) [![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=QLNet&metric=security_rating)](https://sonarcloud.io/dashboard?id=QLNet) -[![Technical Debt](https://sonarcloud.io/api/project_badges/measure?project=QLNet-develop&metric=sqale_index)](https://sonarcloud.io/dashboard?id=QLNet) +[![Technical Debt](https://sonarcloud.io/api/project_badges/measure?project=QLNet&metric=sqale_index)](https://sonarcloud.io/dashboard?id=QLNet) + ## Development workflow From cb30c0917f3e2e0685906cb131d2bc942be26229 Mon Sep 17 00:00:00 2001 From: pamboscy Date: Tue, 21 Feb 2023 16:57:15 +0200 Subject: [PATCH 29/35] Added Cyprus and Greece calendars (#274) --- src/QLNet/Time/Calendars/Cyprus.cs | 143 +++++++++++++++++++++++++++++ src/QLNet/Time/Calendars/Greece.cs | 136 +++++++++++++++++++++++++++ 2 files changed, 279 insertions(+) create mode 100644 src/QLNet/Time/Calendars/Cyprus.cs create mode 100644 src/QLNet/Time/Calendars/Greece.cs diff --git a/src/QLNet/Time/Calendars/Cyprus.cs b/src/QLNet/Time/Calendars/Cyprus.cs new file mode 100644 index 00000000..a2316b4f --- /dev/null +++ b/src/QLNet/Time/Calendars/Cyprus.cs @@ -0,0 +1,143 @@ +/* + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) + + This file is part of QLNet Project https://github.com/amaggiulli/qlnet + + QLNet is free software: you can redistribute it and/or modify it + under the terms of the QLNet license. You should have received a + copy of the license along with this program; if not, license is + available at . + + QLNet is a based on QuantLib, a free-software/open-source library + for financial quantitative analysts and developers - http://quantlib.org/ + The QuantLib license is available online at http://quantlib.org/license.shtml. + + 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. +*/ + +using System; + +namespace QLNet +{ + //! Cyprus calendar + /*! Public holidays: +
      +
    • Saturdays
    • +
    • Sundays
    • +
    • New Year's Day
    • +
    • Epiphany
    • +
    • Greek Independence Day
    • +
    • Cyprus National Day
    • +
    • Ash Monday / Clean Monday
    • +
    • Good Friday
    • +
    • Orthodox Easter (Sunday)
    • +
    • Orthodox Easter (Monday)
    • +
    • Orthodox Easter (Tuesday)
    • +
    • Labour Day
    • +
    • Holy Spirit Day
    • +
    • Assumption Day
    • +
    • Cyprus Independence Day
    • +
    • Greek National Day
    • +
    • Christmas Eve
    • +
    • Christmas Day
    • +
    • Boxing Day
    • +
    + Holidays for the Cyprus stock exchange + All public holidays plus Catholic Good Friday, Catholic Easter Monday + //https://www.cse.com.cy/en-GB/regulated-market/market-indices/other-information/%CE%9B%CE%B9%CF%83%CF%84%CE%B1-%CE%BC%CE%B5-%CE%B1%CF%81%CE%B3%CE%B9%CE%B5%CF%82-2014/ + */ + public class Cyprus : Calendar + { + public enum Market + { + Public, //!< Public holidays + CSE //!< Cyprus stock-exchange + } + public Cyprus() : this(Market.CSE) { } + + public Cyprus(Market m) : base() + { + _impl = m switch + { + Market.Public => PublicImpl.Singleton, + Market.CSE => CSEImpl.Impl, + _ => throw new ArgumentException("Unknown market: " + m) + }; + } + private class PublicImpl : OrthodoxImpl + { + public static readonly PublicImpl Singleton = new(); + protected PublicImpl() { } + public override string name() { return "Cyprus"; } + + public override bool isBusinessDay(Date date) + { + var w = date.DayOfWeek; + int d = date.Day, dd = date.DayOfYear; + var m = (Month)date.Month; + var y = date.year(); + var em = easterMonday(y); + if (isWeekend(w) + // New Year's Day + || (d == 1 && m == Month.January) + // Epiphany + || (d == 6 && m == Month.January) + //Greek Independence Day + || (d == 25 && m == Month.March) + //Cyprus National Day + || (d == 1 && m == Month.April) + //Ash Monday / Clean Monday + || (dd == em - 49 ) + //Good Friday + || (dd == em-3) + // Orthodox Easter Monday + || (dd == em) + // Orthodox Easter Tuesday + || (dd == em + 1 ) + // Labour Day + || (d == 1 && m == Month.May) + // Holy Spirit Day + || (dd == em + 49) + // Assumption Day + || (d == 15 && m == Month.August) + // Cyprus Independence Day + || (d == 1 && m == Month.October) + // Greek National Day + || (d == 28 && m == Month.October) + // Christmas Eve + || (d == 24 && m == Month.December) + // Christmas + || (d == 25 && m == Month.December) + // 2nd Day of Chritsmas + || (d == 26 && m == Month.December)) + return false; + return true; + } + } + + private class CSEImpl : WesternImpl + { + public static readonly CSEImpl Impl = new(); + private CSEImpl() { } + public override string name() { return "Cyprus"; } + public override bool isBusinessDay(Date date) + { + var publicImpl = new Cyprus(Market.Public); + if (!publicImpl.isBusinessDay(date)) + return false; + + var dd = date.DayOfYear; + var y = date.Year; + var em = easterMonday(y); + // Catholic //Good Friday + if ((dd == em-3) + // Catholic Easter Monday + || (dd == em)) + return false; + return true; + } + } + } +} diff --git a/src/QLNet/Time/Calendars/Greece.cs b/src/QLNet/Time/Calendars/Greece.cs new file mode 100644 index 00000000..7ea1a404 --- /dev/null +++ b/src/QLNet/Time/Calendars/Greece.cs @@ -0,0 +1,136 @@ +/* + Copyright (C) 2008-2022 Andrea Maggiulli (a.maggiulli@gmail.com) + + This file is part of QLNet Project https://github.com/amaggiulli/qlnet + + QLNet is free software: you can redistribute it and/or modify it + under the terms of the QLNet license. You should have received a + copy of the license along with this program; if not, license is + available at . + + QLNet is a based on QuantLib, a free-software/open-source library + for financial quantitative analysts and developers - http://quantlib.org/ + The QuantLib license is available online at http://quantlib.org/license.shtml. + + 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. +*/ + +using System; + +namespace QLNet +{ + //! Cyprus calendar + /*! Public holidays: +
      +
    • Saturdays
    • +
    • Sundays
    • +
    • New Year's Day
    • +
    • Epiphany
    • +
    • Greek Independence Day
    • +
    • Ash Monday / Clean Monday
    • +
    • Good Friday
    • +
    • Orthodox Easter (Sunday)
    • +
    • Orthodox Easter (Monday)
    • +
    • Labour Day
    • +
    • Holy Spirit Day
    • +
    • Assumption Day
    • +
    • Greek National Day
    • +
    • Christmas Eve
    • +
    • Christmas Day
    • +
    • Boxing Day
    • +
    + Holidays for the Cyprus stock exchange + All public holidays plus Catholic Good Friday, Catholic Easter Monday + //https://www.athexgroup.gr/market-alternative-holidays + */ + public class Greece : Calendar + { + public enum Market + { + Public, //!< Public holidays + ASE //!< Athens stock-exchange + } + public Greece() : this(Market.ASE) { } + + public Greece(Market m) : base() + { + _impl = m switch + { + Market.Public => PublicImpl.Singleton, + Market.ASE => ASEImpl.Impl, + _ => throw new ArgumentException("Unknown market: " + m) + }; + } + private class PublicImpl : OrthodoxImpl + { + public static readonly PublicImpl Singleton = new(); + protected PublicImpl() { } + public override string name() { return "Greece"; } + + public override bool isBusinessDay(Date date) + { + var w = date.DayOfWeek; + int d = date.Day, dd = date.DayOfYear; + var m = (Month)date.Month; + var y = date.year(); + var em = easterMonday(y); + if (isWeekend(w) + // New Year's Day + || (d == 1 && m == Month.January) + // Epiphany + || (d == 6 && m == Month.January) + //Greek Independence Day + || (d == 25 && m == Month.March) + //Ash Monday / Clean Monday + || (dd == em - 49) + //Good Friday + || (dd == em-3) + // Orthodox Easter Monday + || (dd == em) + // Orthodox Easter Tuesday + || (dd == em + 1) + // Labour Day + || (d == 1 && m == Month.May) + // Holy Spirit Day + || (dd == em + 49) + // Assumption Day + || (d == 15 && m == Month.August) + // Greek National Day + || (d == 28 && m == Month.October) + // Christmas Eve + || (d == 24 && m == Month.December) + // Christmas + || (d == 25 && m == Month.December) + // 2nd Day of Chritsmas + || (d == 26 && m == Month.December)) + return false; + return true; + } + } + + private class ASEImpl : WesternImpl + { + public static readonly ASEImpl Impl = new(); + private ASEImpl() { } + public override string name() { return "Greece"; } + public override bool isBusinessDay(Date date) + { + var publicImpl = new Greece(Market.Public); + if (!publicImpl.isBusinessDay(date)) + return false; + + var dd = date.DayOfYear; + var y = date.Year; + var em = easterMonday(y); + // Catholic //Good Friday + if ((dd == em-3) + // Catholic Easter Monday + || (dd == em)) + return false; + return true; + } + } + } +} From 18f51e7b13e5217369878f5ac5d24f6cd89908e5 Mon Sep 17 00:00:00 2001 From: Andrea Maggiulli Date: Fri, 7 Apr 2023 00:55:41 +0200 Subject: [PATCH 30/35] Added date comparison safety check --- src/QLNet/Time/Date.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/QLNet/Time/Date.cs b/src/QLNet/Time/Date.cs index 63c0d6c3..e2be3c48 100644 --- a/src/QLNet/Time/Date.cs +++ b/src/QLNet/Time/Date.cs @@ -270,22 +270,22 @@ public static implicit operator Date(DateTime d) public static bool operator <(Date d1, Date d2) { - return d1.date < d2.date; + return d1?.date < d2?.date; } public static bool operator <=(Date d1, Date d2) { - return d1.date <= d2.date; + return d1?.date <= d2?.date; } public static bool operator >(Date d1, Date d2) { - return d1.date > d2.date; + return d1?.date > d2?.date; } public static bool operator >=(Date d1, Date d2) { - return d1.date >= d2.date; + return d1?.date >= d2?.date; } public DateTime ToDateTime() => date; From 7ba4747bf048ba837489c9881d22e6cd11c112ec Mon Sep 17 00:00:00 2001 From: Andrea Maggiulli Date: Fri, 7 Apr 2023 20:01:54 +0200 Subject: [PATCH 31/35] Fixed Schedule DeepCopy --- src/QLNet/Time/Schedule.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/QLNet/Time/Schedule.cs b/src/QLNet/Time/Schedule.cs index 679116aa..b9a37905 100644 --- a/src/QLNet/Time/Schedule.cs +++ b/src/QLNet/Time/Schedule.cs @@ -554,7 +554,9 @@ public bool endOfMonth() //! truncated schedule public Schedule until(Date truncationDate) { - Schedule result = (Schedule)this.MemberwiseClone(); + var result = (Schedule)MemberwiseClone(); + result.dates_ = new List(dates_); + result.isRegular_ = new List(isRegular_); Utils.QL_REQUIRE(truncationDate > result.dates_[0], () => "truncation date " + truncationDate + From fc48993c25f2c9e0afd57a3846962e940a3843d3 Mon Sep 17 00:00:00 2001 From: Andrea Maggiulli Date: Fri, 7 Apr 2023 20:46:26 +0200 Subject: [PATCH 32/35] Fixing Schedule until method. --- src/QLNet/Time/Schedule.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/QLNet/Time/Schedule.cs b/src/QLNet/Time/Schedule.cs index b9a37905..b187e515 100644 --- a/src/QLNet/Time/Schedule.cs +++ b/src/QLNet/Time/Schedule.cs @@ -568,8 +568,8 @@ public Schedule until(Date truncationDate) // remove later dates while (result.dates_.Last() > truncationDate) { - result.dates_.RemoveAt(dates_.Count - 1); - result.isRegular_.RemoveAt(isRegular_.Count - 1); + result.dates_.RemoveAt(result.dates_.Count - 1); + result.isRegular_.RemoveAt(result.isRegular_.Count - 1); } // add truncationDate if missing From d3be509b333a0b6cc62b15912f922b5e3e25d3ca Mon Sep 17 00:00:00 2001 From: Andrea Maggiulli Date: Tue, 18 Apr 2023 15:42:13 +0200 Subject: [PATCH 33/35] Updated calendars for 2023 (#275) --- src/QLNet/Time/Calendars/Australia.cs | 68 ++++++++- src/QLNet/Time/Calendars/China.cs | 18 ++- src/QLNet/Time/Calendars/Denmark.cs | 8 +- src/QLNet/Time/Calendars/NewZealand.cs | 84 +++++++----- src/QLNet/Time/Calendars/SouthAfrica.cs | 6 + src/QLNet/Time/Calendars/Turkey.cs | 168 ++++++++++++++++++++++- src/QLNet/Time/Calendars/UnitedStates.cs | 5 +- tests/QLNet.Tests/T_Calendars.cs | 124 ++++++++++++++++- 8 files changed, 431 insertions(+), 50 deletions(-) diff --git a/src/QLNet/Time/Calendars/Australia.cs b/src/QLNet/Time/Calendars/Australia.cs index 167ec698..64850559 100644 --- a/src/QLNet/Time/Calendars/Australia.cs +++ b/src/QLNet/Time/Calendars/Australia.cs @@ -45,13 +45,30 @@ namespace QLNet */ public class Australia : Calendar { - public Australia() : base(Impl.Singleton) {} + // Australian calendars + public enum Market + { + Settlement, // generic settlement calendar + ASX // Australia ASX calendar + } + + public Australia() : this(Market.Settlement) {} - private class Impl : WesternImpl + public Australia(Market m) : base() { - private Impl() { } - public static readonly Impl Singleton = new(); - public override string name() { return "Australia"; } + _impl = m switch + { + Market.Settlement => Settlement.Singleton, + Market.ASX => Exchange.Singleton, + _ => throw new ArgumentException("Unknown market: " + m) + }; + } + + private class Settlement : WesternImpl + { + private Settlement() { } + public static readonly Settlement Singleton = new(); + public override string name() { return "Australia settlement"; } public override bool isBusinessDay(Date date) { var w = date.DayOfWeek; @@ -90,6 +107,47 @@ public override bool isBusinessDay(Date date) return true; } } + + private class Exchange : WesternImpl + { + private Exchange() { } + public static readonly Exchange Singleton = new(); + public override string name() { return "Australia exchange"; } + public override bool isBusinessDay(Date date) + { + var w = date.DayOfWeek; + int d = date.Day, dd = date.DayOfYear; + var m = (Month)date.Month; + var y = date.Year; + var em = easterMonday(y); + + if (isWeekend(w) + // New Year's Day (possibly moved to Monday) + || ((d == 1 || ((d == 2 || d == 3) && w == DayOfWeek.Monday)) && m == Month.January) + // Australia Day, January 26th (possibly moved to Monday) + || ((d == 26 || ((d == 27 || d == 28) && w == DayOfWeek.Monday)) && + m == Month.January) + // Good Friday + || (dd == em-3) + // Easter Monday + || (dd == em) + // ANZAC Day, April 25th + || (d == 25 && m == Month.April) + // Queen's Birthday, second Monday in June + || ((d > 7 && d <= 14) && w == DayOfWeek.Monday && m == Month.June) + // Christmas, December 25th (possibly Monday or Tuesday) + || ((d == 25 || (d == 27 && (w == DayOfWeek.Monday || w == DayOfWeek.Tuesday))) + && m == Month.December) + // Boxing Day, December 26th (possibly Monday or Tuesday) + || ((d == 26 || (d == 28 && (w == DayOfWeek.Monday || w == DayOfWeek.Tuesday))) + && m == Month.December) + // National Day of Mourning for Her Majesty, September 22 (only 2022) + || (d == 22 && m == Month.September && y == 2022)) + return false; + return true; + } + } + } } diff --git a/src/QLNet/Time/Calendars/China.cs b/src/QLNet/Time/Calendars/China.cs index d37787ed..e7a23fe4 100644 --- a/src/QLNet/Time/Calendars/China.cs +++ b/src/QLNet/Time/Calendars/China.cs @@ -114,6 +114,7 @@ public override bool isBusinessDay(Date date) || (y == 2020 && d == 1 && m == Month.January) || (y == 2021 && d == 1 && m == Month.January) || (y == 2022 && d == 3 && m == Month.January) + || (y == 2023 && d == 2 && m == Month.January) // Chinese New Year || (y == 2004 && d >= 19 && d <= 28 && m == Month.January) || (y == 2005 && d >= 7 && d <= 15 && m == Month.February) @@ -137,6 +138,7 @@ public override bool isBusinessDay(Date date) || (y == 2020 && (d == 24 || (d >= 27 && d <= 31)) && m == Month.January) || (y == 2021 && (d == 11 || d == 12 || d == 15 || d == 16 || d == 17) && m == Month.February) || (y == 2022 && ((d == 31 && m == Month.January) || (d <= 4 && m == Month.February))) + || (y == 2023 && d >= 23 && d <= 27 && m == Month.January) // Ching Ming Festival || (y <= 2008 && d == 4 && m == Month.April) || (y == 2009 && d == 6 && m == Month.April) @@ -153,6 +155,7 @@ public override bool isBusinessDay(Date date) || (y == 2020 && d == 6 && m == Month.April) || (y == 2021 && d == 5 && m == Month.April) || (y == 2022 && d >= 4 && d <= 5 && m == Month.April) + || (y == 2023 && d == 5 && m == Month.April) // Labor Day || (y <= 2007 && d >= 1 && d <= 7 && m == Month.May) || (y == 2008 && d >= 1 && d <= 2 && m == Month.May) @@ -172,6 +175,7 @@ public override bool isBusinessDay(Date date) || (y == 2020 && (d == 1 || d == 4 || d == 5) && m == Month.May) || (y == 2021 && (d == 3 || d == 4 || d == 5) && m == Month.May) || (y == 2022 && d >= 2 && d <= 4 && m == Month.May) + || (y == 2023 && d >= 1 && d <= 3 && m == Month.May) // Tuen Ng Festival || (y <= 2008 && d == 9 && m == Month.June) || (y == 2009 && (d == 28 || d == 29) && m == Month.May) @@ -188,6 +192,7 @@ public override bool isBusinessDay(Date date) || (y == 2020 && d >= 25 && d <= 26 && m == Month.June) || (y == 2021 && d == 14 && m == Month.June) || (y == 2022 && d == 3 && m == Month.June) + || (y == 2023 && d >= 22 && d <= 23 && m == Month.June) // Mid-Autumn Festival || (y <= 2008 && d == 15 && m == Month.September) || (y == 2010 && d >= 22 && d <= 24 && m == Month.September) @@ -201,6 +206,7 @@ public override bool isBusinessDay(Date date) || (y == 2019 && d == 13 && m == Month.September) || (y == 2021 && (d == 20 || d == 21) && m == Month.September) || (y == 2022 && d == 12 && m == Month.September) + || (y == 2023 && d == 29 && m == Month.September) // National Day || (y <= 2007 && d >= 1 && d <= 7 && m == Month.October) || (y == 2008 && ((d >= 29 && m == Month.September) || @@ -220,6 +226,7 @@ public override bool isBusinessDay(Date date) || (y == 2020 && d >= 5 && d <= 8 && m == Month.October) || (y == 2021 && (d == 1 || d == 4 || d == 5 || d == 6 || d == 7) && m == Month.October) || (y == 2022 && d >= 3 && d <= 7 && m == Month.October) + || (y == 2023 && d >= 2 && d <= 6 && m == Month.October) // 70th anniversary of the victory of anti-Japaneses war || (y == 2015 && d >= 3 && d <= 4 && m == Month.September) ) @@ -378,12 +385,19 @@ public override bool isBusinessDay(Date date) new Date(24, Month.April, 2022), new Date(7, Month.May, 2022), new Date(8, Month.October, 2022), - new Date(9, Month.October, 2022) + new Date(9, Month.October, 2022), + // 2023 + new Date(28, Month.January, 2023), + new Date(29, Month.January, 2023), + new Date(23, Month.April, 2023), + new Date(6, Month.May, 2023), + new Date(25, Month.June, 2023), + new Date(7, Month.October, 2023), + new Date(8, Month.October, 2023) }; // If it is already a SSE business day, it must be a IB business day return sseImpl.isBusinessDay(date) || working_weekends.Contains(date); - } } diff --git a/src/QLNet/Time/Calendars/Denmark.cs b/src/QLNet/Time/Calendars/Denmark.cs index c56af5ee..435a9a7f 100644 --- a/src/QLNet/Time/Calendars/Denmark.cs +++ b/src/QLNet/Time/Calendars/Denmark.cs @@ -70,8 +70,8 @@ public override bool isBusinessDay(Date date) || (dd == em + 25) // Ascension || (dd == em + 38) - // Day after Ascension (bank holiday after year 2008) - || (dd == em + 39 && date.Year > 2008) + // Day after Ascension + || (dd == em + 39 && date.Year > 2009) // Whit Monday || (dd == em + 49) // New Year's Day @@ -84,8 +84,8 @@ public override bool isBusinessDay(Date date) || (d == 25 && m == Month.December) // Boxing Day || (d == 26 && m == Month.December) - // New Year's Eve (bank holiday from 2003) - || (d == 31 && m == Month.December && date.Year >= 2003)) + // New Year's Eve + || (d == 31 && m == Month.December)) return false; return true; } diff --git a/src/QLNet/Time/Calendars/NewZealand.cs b/src/QLNet/Time/Calendars/NewZealand.cs index 9882eb67..15c5dcb9 100644 --- a/src/QLNet/Time/Calendars/NewZealand.cs +++ b/src/QLNet/Time/Calendars/NewZealand.cs @@ -47,7 +47,8 @@ namespace QLNet \note The holiday rules for New Zealand were documented by David Gilbert for IDB (http://www.jrefinery.com/ibd/) - + The Matariki holiday calendar has been released by the NZ Government + (https://www.legislation.govt.nz/act/public/2022/0014/latest/LMS557893.html) \ingroup calendars */ public class NewZealand : Calendar @@ -103,37 +104,58 @@ public override bool isBusinessDay(Date date) var y = date.Year; var em = easterMonday(y); if (isWeekend(w) - // New Year's Day (possibly moved to Monday or Tuesday) - || ((d == 1 || (d == 3 && (w == DayOfWeek.Monday || w == DayOfWeek.Tuesday))) && - m == Month.January) - // Day after New Year's Day (possibly moved to Mon or Tuesday) - || ((d == 2 || (d == 4 && (w == DayOfWeek.Monday || w == DayOfWeek.Tuesday))) && - m == Month.January) - // Anniversary Day, Monday nearest January 22nd - || ((d >= 19 && d <= 25) && w == DayOfWeek.Monday && m == Month.January) - // Waitangi Day. February 6th ("Mondayised" since 2013) - || (d == 6 && m == Month.February) - || ((d == 7 || d == 8) && w == DayOfWeek.Monday && m == Month.February && y > 2013) - // Good Friday - || (dd == em - 3) - // Easter Monday - || (dd == em) - // ANZAC Day. April 25th ("Mondayised" since 2013) - || (d == 25 && m == Month.April) - || ((d == 26 || d == 27) && w == DayOfWeek.Monday && m == Month.April && y > 2013) - // Queen's Birthday, first Monday in June - || (d <= 7 && w == DayOfWeek.Monday && m == Month.June) - // Matariki Holiday - || MatarikiHolidays.Any(x => x == date) - // Labour Day, fourth Monday in October - || ((d >= 22 && d <= 28) && w == DayOfWeek.Monday && m == Month.October) - // Christmas, December 25th (possibly Monday or Tuesday) - || ((d == 25 || (d == 27 && (w == DayOfWeek.Monday || w == DayOfWeek.Tuesday))) - && m == Month.December) - // Boxing Day, December 26th (possibly Monday or Tuesday) - || ((d == 26 || (d == 28 && (w == DayOfWeek.Monday || w == DayOfWeek.Tuesday))) - && m == Month.December)) + // New Year's Day (possibly moved to Monday or Tuesday) + || ((d == 1 || (d == 3 && (w == DayOfWeek.Monday || w == DayOfWeek.Tuesday))) && + m == Month.January) + // Day after New Year's Day (possibly moved to Mon or Tuesday) + || ((d == 2 || (d == 4 && (w == DayOfWeek.Monday || w == DayOfWeek.Tuesday))) && + m == Month.January) + // Anniversary Day, Monday nearest January 22nd + || ((d >= 19 && d <= 25) && w == DayOfWeek.Monday && m == Month.January) + // Waitangi Day. February 6th ("Mondayised" since 2013) + || (d == 6 && m == Month.February) + || ((d == 7 || d == 8) && w == DayOfWeek.Monday && m == Month.February && y > 2013) + // Good Friday + || (dd == em - 3) + // Easter Monday + || (dd == em) + // ANZAC Day. April 25th ("Mondayised" since 2013) + || (d == 25 && m == Month.April) + || ((d == 26 || d == 27) && w == DayOfWeek.Monday && m == Month.April && y > 2013) + // Queen's Birthday, first Monday in June + || (d <= 7 && w == DayOfWeek.Monday && m == Month.June) + // Matariki Holiday + || MatarikiHolidays.Any(x => x == date) + // Labour Day, fourth Monday in October + || ((d >= 22 && d <= 28) && w == DayOfWeek.Monday && m == Month.October) + // Christmas, December 25th (possibly Monday or Tuesday) + || ((d == 25 || (d == 27 && (w == DayOfWeek.Monday || w == DayOfWeek.Tuesday))) + && m == Month.December) + // Boxing Day, December 26th (possibly Monday or Tuesday) + || ((d == 26 || (d == 28 && (w == DayOfWeek.Monday || w == DayOfWeek.Tuesday))) + && m == Month.December) + // Matariki, it happens on Friday in June or July + // official calendar released by the NZ government for the + // next 30 years + || (d == 20 && m == Month.June && y == 2025) + || (d == 21 && m == Month.June && (y == 2030 || y == 2052)) + || (d == 24 && m == Month.June && (y == 2022 || y == 2033 || y == 2044)) + || (d == 25 && m == Month.June && (y == 2027 || y == 2038 || y == 2049)) + || (d == 28 && m == Month.June && y == 2024) + || (d == 29 && m == Month.June && (y == 2035 || y == 2046)) + || (d == 30 && m == Month.June && y == 2051) + || (d == 2 && m == Month.July && y == 2032) + || (d == 3 && m == Month.July && (y == 2043 || y == 2048)) + || (d == 6 && m == Month.July && (y == 2029 || y == 2040)) + || (d == 7 && m == Month.July && (y == 2034 || y == 2045)) + || (d == 10 && m == Month.July && (y == 2026 || y == 2037)) + || (d == 11 && m == Month.July && (y == 2031 || y == 2042)) + || (d == 14 && m == Month.July && (y == 2023 || y == 2028)) + || (d == 15 && m == Month.July && (y == 2039 || y == 2050)) + || (d == 18 && m == Month.July && y == 2036) + || (d == 19 && m == Month.July && (y == 2041 || y == 2047))) return false; + return true; } } diff --git a/src/QLNet/Time/Calendars/SouthAfrica.cs b/src/QLNet/Time/Calendars/SouthAfrica.cs index 65e382ae..0054cb14 100644 --- a/src/QLNet/Time/Calendars/SouthAfrica.cs +++ b/src/QLNet/Time/Calendars/SouthAfrica.cs @@ -101,6 +101,12 @@ public override bool isBusinessDay(Date date) // Day of Goodwill (possibly moved to Monday) || ((d == 26 || (d == 27 && w == DayOfWeek.Monday)) && m == Month.December) + // one-shot: Election day 2009 + || (d == 22 && m == Month.April && y == 2009) + // one-shot: Election day 2016 + || (d == 3 && m == Month.August && y == 2016) + // one-shot: In lieu of Christmas falling on Sunday in 2022 + || (d == 27 && m == Month.December && y == 2022) ) return false; return true; diff --git a/src/QLNet/Time/Calendars/Turkey.cs b/src/QLNet/Time/Calendars/Turkey.cs index 6ed4cd23..22000e56 100644 --- a/src/QLNet/Time/Calendars/Turkey.cs +++ b/src/QLNet/Time/Calendars/Turkey.cs @@ -25,7 +25,8 @@ namespace QLNet { //! Turkish calendar /*! Holidays for the Istanbul Stock Exchange: - (data from ): + (data from ) and +
    • Saturdays
    • Sundays
    • @@ -34,7 +35,7 @@ namespace QLNet
    • Youth and Sports Day, May 19th
    • Victory Day, August 30th
    • Republic Day, October 29th
    • -
    • Local Holidays (Kurban, Ramadan; 2004 to 2013 only)
    • +
    • Local Holidays (Kurban, Ramadan - dates need further validation for >= 2024)
    \ingroup calendars @@ -173,6 +174,169 @@ public override bool isBusinessDay(Date date) || (m == Month.October && d == 29)) return false; } + else if (y == 2015) + { + // Ramadan + if ((m == Month.July && d >= 17 && d <= 19) + // Kurban + || (m == Month.October && d >= 24 && d <= 27)) + return false; + } + else if (y == 2016) + { + // Ramadan + if ((m == Month.July && d >= 5 && d <= 7) + // Kurban + || (m == Month.September && d >= 12 && d <= 15)) + return false; + } + else if (y == 2017) + { + // Ramadan + if ((m == Month.June && d >= 25 && d <= 27) + // Kurban + || (m == Month.September && d >= 1 && d <= 4)) + return false; + } + else if (y == 2018) + { + // Ramadan + if ((m == Month.June && d >= 15 && d <= 17) + // Kurban + || (m == Month.August && d >= 21 && d <= 24)) + return false; + } + else if (y == 2019) + { + // Ramadan + if ((m == Month.June && d >= 4 && d <= 6) + // Kurban + || (m == Month.August && d >= 11 && d <= 14)) + return false; + } + else if (y == 2020) + { + // Ramadan + if ((m == Month.May && d >= 24 && d <= 26) + // Kurban + || (m == Month.July && d == 31) || (m == Month.August && d >= 1 && d <= 3)) + return false; + } + else if (y == 2021) + { + // Ramadan + if ((m == Month.May && d >= 13 && d <= 15) + // Kurban + || (m == Month.July && d >= 20 && d <= 23)) + return false; + } + else if (y == 2022) + { + // Ramadan + if ((m == Month.May && d >= 2 && d <= 4) + // Kurban + || (m == Month.July && d >= 9 && d <= 12)) + return false; + } + else if (y == 2023) + { + // Ramadan + if ((m == Month.April && d >= 21 && d <= 23) + // Kurban + // July 1 is also a holiday but falls on a Saturday which is already flagged + || (m == Month.June && d >= 28 && d <= 30)) + return false; + } + else if (y == 2024) + { + // Note: Holidays >= 2024 are not yet officially anounced by borsaistanbul.com + // and need further validation + // Ramadan + if ((m == Month.April && d >= 10 && d <= 12) + // Kurban + || (m == Month.June && d >= 17 && d <= 19)) + return false; + } + else if (y == 2025) + { + // Ramadan + if ((m == Month.March && d == 31) || (m == Month.April && d >= 1 && d <= 2) + // Kurban + || (m == Month.June && d >= 6 && d <= 9)) + return false; + } + else if (y == 2026) + { + // Ramadan + if ((m == Month.March && d >= 20 && d <= 22) + // Kurban + || (m == Month.May && d >= 26 && d <= 29)) + return false; + } + else if (y == 2027) + { + // Ramadan + if ((m == Month.March && d >= 10 && d <= 12) + // Kurban + || (m == Month.May && d >= 16 && d <= 19)) + return false; + } + else if (y == 2028) + { + // Ramadan + if ((m == Month.February && d >= 27 && d <= 29) + // Kurban + || (m == Month.May && d >= 4 && d <= 7)) + return false; + } + else if (y == 2029) + { + // Ramadan + if ((m == Month.February && d >= 15 && d <= 17) + // Kurban + || (m == Month.April && d >= 23 && d <= 26)) + return false; + } + else if (y == 2030) + { + // Ramadan + if ((m == Month.February && d >= 5 && d <= 7) + // Kurban + || (m == Month.April && d >= 13 && d <= 16)) + return false; + } + else if (y == 2031) + { + // Ramadan + if ((m == Month.January && d >= 25 && d <= 27) + // Kurban + || (m == Month.April && d >= 2 && d <= 5)) + return false; + } + else if (y == 2032) + { + // Ramadan + if ((m == Month.January && d >= 14 && d <= 16) + // Kurban + || (m == Month.March && d >= 21 && d <= 24)) + return false; + } + else if (y == 2033) + { + // Ramadan + if ((m == Month.January && d >= 3 && d <= 5) || (m == Month.December && d == 23) + // Kurban + || (m == Month.March && d >= 11 && d <= 14)) + return false; + } + else if (y == 2034) + { + // Ramadan + if ((m == Month.December && d >= 12 && d <= 14) + // Kurban + || (m == Month.February && d == 28) || (m == Month.March && d >= 1 && d <= 3)) + return false; + } return true; } } diff --git a/src/QLNet/Time/Calendars/UnitedStates.cs b/src/QLNet/Time/Calendars/UnitedStates.cs index da13e8c7..d424f49a 100644 --- a/src/QLNet/Time/Calendars/UnitedStates.cs +++ b/src/QLNet/Time/Calendars/UnitedStates.cs @@ -393,8 +393,9 @@ public override bool isBusinessDay(Date date) || ((d >= 15 && d <= 21) && w == DayOfWeek.Monday && m == Month.January && y >= 1983) // Washington's birthday (third Monday in February) || isWashingtonBirthday(d, m, y, w) - // Good Friday (2015 was half day due to NFP report) - || (dd == em - 3 && y != 2015) + // Good Friday (2015, 2021, 2023 are half day due to NFP/SIFMA; + // see ) + || (dd == em-3 && y != 2015 && y != 2021 && y != 2023) // Memorial Day (last Monday in May) || isMemorialDay(d, m, y, w) // Juneteenth (Monday if Sunday or Friday if Saturday) diff --git a/tests/QLNet.Tests/T_Calendars.cs b/tests/QLNet.Tests/T_Calendars.cs index 9fc7b780..9a12cd25 100644 --- a/tests/QLNet.Tests/T_Calendars.cs +++ b/tests/QLNet.Tests/T_Calendars.cs @@ -1,5 +1,5 @@ /* - Copyright (C) 2008 Andrea Maggiulli + Copyright (C) 2008-2023 Andrea Maggiulli (a.maggiulli@gmail.com) Copyright (C) 2008 Siarhei Novik (snovik@gmail.com) This file is part of QLNet Project https://github.com/amaggiulli/qlnet @@ -22,7 +22,6 @@ under the terms of the QLNet license. You should have received a using System.Collections.Generic; using Xunit; using QLNet; -using System.Diagnostics; namespace TestSuite { @@ -762,6 +761,64 @@ public void testBrazil() + " calculated holidays"); } + [Fact] + public void testDenmark() + { + // Testing Denmark holiday list + var expectedHol = new List(); + expectedHol.Add(new Date(1, Month.January, 2020)); + expectedHol.Add(new Date(9, Month.April, 2020)); + expectedHol.Add(new Date(10, Month.April, 2020)); + expectedHol.Add(new Date(13, Month.April, 2020)); + expectedHol.Add(new Date(8, Month.May, 2020)); + expectedHol.Add(new Date(21, Month.May, 2020)); + expectedHol.Add(new Date(22, Month.May, 2020)); + expectedHol.Add(new Date(1, Month.June, 2020)); + expectedHol.Add(new Date(5, Month.June, 2020)); + expectedHol.Add(new Date(24, Month.December, 2020)); + expectedHol.Add(new Date(25, Month.December, 2020)); + // Saturday: expectedHol.emplace_back(26, December, 2020); + expectedHol.Add(new Date(31, Month.December, 2020)); + expectedHol.Add(new Date(1, Month.January, 2021)); + expectedHol.Add(new Date(1, Month.April, 2021)); + expectedHol.Add(new Date(2, Month.April, 2021)); + expectedHol.Add(new Date(5, Month.April, 2021)); + expectedHol.Add(new Date(30, Month.April, 2021)); + expectedHol.Add(new Date(13, Month.May, 2021)); + expectedHol.Add(new Date(14, Month.May, 2021)); + expectedHol.Add(new Date(24, Month.May, 2021)); + // Saturday: expectedHol.emplace_back(5, June, 2021); + expectedHol.Add(new Date(24, Month.December, 2021)); + // Saturday: expectedHol.emplace_back(25, December, 2021); + // Sunday: expectedHol.emplace_back(26, December, 2021); + expectedHol.Add(new Date(31, Month.December, 2021)); + // Saturday: expectedHol.emplace_back(1, January, 2022); + expectedHol.Add(new Date(14, Month.April, 2022)); + expectedHol.Add(new Date(15, Month.April, 2022)); + expectedHol.Add(new Date(18, Month.April, 2022)); + expectedHol.Add(new Date(13, Month.May, 2022)); + expectedHol.Add(new Date(26, Month.May, 2022)); + expectedHol.Add(new Date(27, Month.May, 2022)); + // Sunday: expectedHol.emplace_back(5, June, 2022); + expectedHol.Add(new Date(6, Month.June, 2022)); + // Saturday: expectedHol.emplace_back(24, December, 2022); + // Sunday: expectedHol.emplace_back(25, December, 2022); + expectedHol.Add(new Date(26, Month.December, 2022)); + // Saturday: expectedHol.emplace_back(31, December, 2022); + + Calendar c = new Denmark(); + var hol = c.holidayList(new Date(1, Month.January, 2020), new Date(31, Month.December, 2022)); + + for (var i = 0; i < Math.Min(hol.Count, expectedHol.Count); i++) + { + if (hol[i] != expectedHol[i]) + QAssert.Fail("expected holiday was " + expectedHol[i] + " while calculated holiday is " + hol[i]); + } + if (hol.Count != expectedHol.Count) + QAssert.Fail("there were " + expectedHol.Count + " expected holidays, while there are " + + hol.Count + " calculated holidays"); + } + [Fact] public void testSouthKoreanSettlement() { @@ -1109,8 +1166,49 @@ public void testChinaSSE() expectedHol.Add(new Date(6, Month.October, 2021)); expectedHol.Add(new Date(7, Month.October, 2021)); + // China Shanghai Securities Exchange holiday list in the year 2022 + expectedHol.Add(new Date(3, Month.Jan, 2022)); + expectedHol.Add(new Date(31, Month.Jan, 2022)); + expectedHol.Add(new Date(1, Month.Feb, 2022)); + expectedHol.Add(new Date(2, Month.Feb, 2022)); + expectedHol.Add(new Date(3, Month.Feb, 2022)); + expectedHol.Add(new Date(4, Month.Feb, 2022)); + expectedHol.Add(new Date(4, Month.April, 2022)); + expectedHol.Add(new Date(5, Month.April, 2022)); + expectedHol.Add(new Date(2, Month.May, 2022)); + expectedHol.Add(new Date(3, Month.May, 2022)); + expectedHol.Add(new Date(4, Month.May, 2022)); + expectedHol.Add(new Date(3, Month.June, 2022)); + expectedHol.Add(new Date(12, Month.September, 2022)); + expectedHol.Add(new Date(3, Month.October, 2022)); + expectedHol.Add(new Date(4, Month.October, 2022)); + expectedHol.Add(new Date(5, Month.October, 2022)); + expectedHol.Add(new Date(6, Month.October, 2022)); + expectedHol.Add(new Date(7, Month.October, 2022)); + + // China Shanghai Securities Exchange holiday list in the year 2023 + expectedHol.Add(new Date(2, Month.Jan, 2023)); + expectedHol.Add(new Date(23, Month.Jan, 2023)); + expectedHol.Add(new Date(24, Month.Jan, 2023)); + expectedHol.Add(new Date(25, Month.Jan, 2023)); + expectedHol.Add(new Date(26, Month.Jan, 2023)); + expectedHol.Add(new Date(27, Month.Jan, 2023)); + expectedHol.Add(new Date(5, Month.April, 2023)); + expectedHol.Add(new Date(1, Month.May, 2023)); + expectedHol.Add(new Date(2, Month.May, 2023)); + expectedHol.Add(new Date(3, Month.May, 2023)); + expectedHol.Add(new Date(22, Month.June, 2023)); + expectedHol.Add(new Date(23, Month.June, 2023)); + expectedHol.Add(new Date(29, Month.September, 2023)); + expectedHol.Add(new Date(2, Month.October, 2023)); + expectedHol.Add(new Date(3, Month.October, 2023)); + expectedHol.Add(new Date(4, Month.October, 2023)); + expectedHol.Add(new Date(5, Month.October, 2023)); + expectedHol.Add(new Date(6, Month.October, 2023)); + + Calendar c = new China(China.Market.SSE); - List hol = c.holidayList(new Date(1, Month.January, 2014), new Date(31, Month.December, 2021)); + List hol = c.holidayList(new Date(1, Month.January, 2014), new Date(31, Month.December, 2023)); for (int i = 0; i < Math.Min(hol.Count, expectedHol.Count); i++) { @@ -1193,9 +1291,27 @@ public void testChinaIB() expectedWorkingWeekEnds.Add(new Date(26, Month.September, 2021)); expectedWorkingWeekEnds.Add(new Date(9, Month.October, 2021)); + // China Inter Bank working weekends list in the year 2022 + expectedWorkingWeekEnds.Add(new Date(29, Month.Jan, 2022)); + expectedWorkingWeekEnds.Add(new Date(30, Month.Jan, 2022)); + expectedWorkingWeekEnds.Add(new Date(2, Month.April, 2022)); + expectedWorkingWeekEnds.Add(new Date(24, Month.April, 2022)); + expectedWorkingWeekEnds.Add(new Date(7, Month.May, 2022)); + expectedWorkingWeekEnds.Add(new Date(8, Month.October, 2022)); + expectedWorkingWeekEnds.Add(new Date(9, Month.October, 2022)); + + // China Inter Bank working weekends list in the year 2023 + expectedWorkingWeekEnds.Add(new Date(28, Month.Jan, 2023)); + expectedWorkingWeekEnds.Add(new Date(29, Month.Jan, 2023)); + expectedWorkingWeekEnds.Add(new Date(23, Month.April, 2023)); + expectedWorkingWeekEnds.Add(new Date(6, Month.May, 2023)); + expectedWorkingWeekEnds.Add(new Date(25, Month.June, 2023)); + expectedWorkingWeekEnds.Add(new Date(7, Month.October, 2023)); + expectedWorkingWeekEnds.Add(new Date(8, Month.October, 2023)); + Calendar c = new China(China.Market.IB); Date start = new Date(1, Month.Jan, 2014); - Date end = new Date(31, Month.Dec, 2021); + Date end = new Date(31, Month.Dec, 2023); int k = 0; From 6e4f632f35ea14539c5aeea22491e6b79d4fcd67 Mon Sep 17 00:00:00 2001 From: Andrea Maggiulli Date: Tue, 18 Apr 2023 16:02:52 +0200 Subject: [PATCH 34/35] Updated to .NET 7.0 --- src/QLNet/QLNet.csproj | 2 +- tests/QLNet.Tests/QLNet.Tests.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/QLNet/QLNet.csproj b/src/QLNet/QLNet.csproj index faccdce0..beeffb27 100644 --- a/src/QLNet/QLNet.csproj +++ b/src/QLNet/QLNet.csproj @@ -2,7 +2,7 @@ 1.12.0 - net6.0;netstandard2.0 + net7.0;netstandard2.0 latest $(DefineConstants);QL_NEGATIVE_RATES QLNet diff --git a/tests/QLNet.Tests/QLNet.Tests.csproj b/tests/QLNet.Tests/QLNet.Tests.csproj index 0cc5a0fc..c02987f0 100644 --- a/tests/QLNet.Tests/QLNet.Tests.csproj +++ b/tests/QLNet.Tests/QLNet.Tests.csproj @@ -1,7 +1,7 @@  - net6.0 + net7.0 latest false From 081f3c1ddc3b037fca00294ce15cc128fc9bda0d Mon Sep 17 00:00:00 2001 From: Andrea Maggiulli Date: Tue, 18 Apr 2023 16:57:43 +0200 Subject: [PATCH 35/35] Updated News. --- ChangeLog.txt | 749 ++++++++++++++++++++++++-------------------------- News.txt | 45 +-- appveyor.yml | 14 +- 3 files changed, 393 insertions(+), 415 deletions(-) diff --git a/ChangeLog.txt b/ChangeLog.txt index b8b470ab..28358430 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -1,484 +1,455 @@ -commit 21e7100473af85d45dbd635fdaf9c844f409efd1 +commit 6e4f632f35ea14539c5aeea22491e6b79d4fcd67 Author: Andrea Maggiulli -Date: Tue Nov 23 20:00:02 2021 +0100 +Date: Tue Apr 18 16:02:52 2023 +0200 - Updated to netstandard2.1. + Updated to .NET 7.0 - src/QLNet/QLNet.csproj | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) + src/QLNet/QLNet.csproj | 2 +- + tests/QLNet.Tests/QLNet.Tests.csproj | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) -commit cc11a60d51efc6a25eed61739999bb263c7377de -Author: Andrea Maggiulli -Date: Tue Nov 23 19:57:11 2021 +0100 +commit d3be509b333a0b6cc62b15912f922b5e3e25d3ca +Author: Andrea Maggiulli +Date: Tue Apr 18 15:42:13 2023 +0200 - Updated calendars tests. + Updated calendars for 2023 (#275) - tests/QLNet.Tests/T_Calendars.cs | 74 +++++++++++++++++++++++++++++++++++----- - 1 file changed, 66 insertions(+), 8 deletions(-) + src/QLNet/Time/Calendars/Australia.cs | 68 +++++++++++++++++++++++++++++++++++++++++---- + src/QLNet/Time/Calendars/China.cs | 18 ++++++++++-- + src/QLNet/Time/Calendars/Denmark.cs | 8 +++--- + src/QLNet/Time/Calendars/NewZealand.cs | 84 +++++++++++++++++++++++++++++++++++--------------------- + src/QLNet/Time/Calendars/SouthAfrica.cs | 6 ++++ + src/QLNet/Time/Calendars/Turkey.cs | 168 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- + src/QLNet/Time/Calendars/UnitedStates.cs | 5 ++-- + tests/QLNet.Tests/T_Calendars.cs | 124 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- + 8 files changed, 431 insertions(+), 50 deletions(-) -commit 10eb3c3133a66b0caf093014a206fa8a81e9b871 +commit fc48993c25f2c9e0afd57a3846962e940a3843d3 Author: Andrea Maggiulli -Date: Tue Nov 23 19:56:46 2021 +0100 - - Updated all calendars. - - src/QLNet/Time/Calendars/France.cs | 1 - - src/QLNet/Time/Calendars/Germany.cs | 18 +- - src/QLNet/Time/Calendars/HongKong.cs | 352 ++++++++++++++++++------------ - src/QLNet/Time/Calendars/Hungary.cs | 4 +- - src/QLNet/Time/Calendars/India.cs | 64 ++++++ - src/QLNet/Time/Calendars/Japan.cs | 62 ++++-- - src/QLNet/Time/Calendars/Mexico.cs | 4 +- - src/QLNet/Time/Calendars/NewZealand.cs | 5 +- - src/QLNet/Time/Calendars/Norway.cs | 2 + - src/QLNet/Time/Calendars/Romania.cs | 59 ++++- - src/QLNet/Time/Calendars/Russia.cs | 108 ++++++++- - src/QLNet/Time/Calendars/SouthKorea.cs | 22 +- - src/QLNet/Time/Calendars/Sweden.cs | 6 +- - src/QLNet/Time/Calendars/Taiwan.cs | 107 ++++++++- - src/QLNet/Time/Calendars/Thailand.cs | 96 ++++---- - src/QLNet/Time/Calendars/Turkey.cs | 4 +- - src/QLNet/Time/Calendars/UnitedKingdom.cs | 52 ++--- - src/QLNet/Time/Calendars/UnitedStates.cs | 122 ++++++++++- - 18 files changed, 811 insertions(+), 277 deletions(-) - -commit 5d06ff463ed22e7d0131bc047e35ef9eb9cd0e05 -Author: Andrea Maggiulli -Date: Mon Nov 22 20:23:43 2021 +0100 +Date: Fri Apr 7 20:46:26 2023 +0200 - Updated Canadian calendar. Close #266 + Fixing Schedule until method. - src/QLNet/Time/Calendars/Canada.cs | 9 ++++++--- - 1 file changed, 6 insertions(+), 3 deletions(-) + src/QLNet/Time/Schedule.cs | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) -commit a8d3dd34792341d3f78a326592905bd63f49df34 +commit 7ba4747bf048ba837489c9881d22e6cd11c112ec Author: Andrea Maggiulli -Date: Mon Nov 22 20:22:23 2021 +0100 +Date: Fri Apr 7 20:01:54 2023 +0200 - Added Chilean calendars. + Fixed Schedule DeepCopy - src/QLNet/Time/Calendars/Chile.cs | 133 ++++++++++++++++++++++++++++++++++++++ - 1 file changed, 133 insertions(+) + src/QLNet/Time/Schedule.cs | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) -commit 8e685b4cd0e7cff512dad76ba6d281fd76f028fe +commit 18f51e7b13e5217369878f5ac5d24f6cd89908e5 Author: Andrea Maggiulli -Date: Mon Nov 22 20:21:30 2021 +0100 +Date: Fri Apr 7 00:55:41 2023 +0200 - Added French calendars. + Added date comparison safety check - src/QLNet/Time/Calendars/France.cs | 154 +++++++++++++++++++++++++++++++++++++ - 1 file changed, 154 insertions(+) + src/QLNet/Time/Date.cs | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) -commit 20a5cadcc0d514ce126f6ced93e1d02ffa3d65d2 -Author: Andrea Maggiulli -Date: Mon Nov 22 19:47:08 2021 +0100 +commit cb30c0917f3e2e0685906cb131d2bc942be26229 +Author: pamboscy +Date: Tue Feb 21 16:57:15 2023 +0200 - Updated China calendar up to 2021. + Added Cyprus and Greece calendars (#274) - src/QLNet/Time/Calendars/China.cs | 37 ++++++++++++++++++++++++++++++++++--- - 1 file changed, 34 insertions(+), 3 deletions(-) + src/QLNet/Time/Calendars/Cyprus.cs | 143 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + src/QLNet/Time/Calendars/Greece.cs | 136 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 279 insertions(+) -commit b272d031e02cccee37d1b2d5a35cab9782bc2fb7 -Author: Andrea Maggiulli -Date: Mon Nov 22 18:49:16 2021 +0100 +commit e05578f554a625c579637746e45c424c415d887d +Author: Andrea Maggiulli +Date: Thu Dec 15 19:06:04 2022 +0100 - Added Austrian calendar. + [skip ci] Update ReadMe - src/QLNet/Time/Calendars/Austria.cs | 167 ++++++++++++++++++++++++++++++++++++ - 1 file changed, 167 insertions(+) + README.md | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) -commit d97bfab91e19d92faf3df7aeebf2eafc47a3a723 -Author: Olivier Milla -Date: Thu Sep 30 18:57:47 2021 +0200 +commit 5a215582721379da2d9b6809f1328094033602b6 +Author: Andrea Maggiulli +Date: Thu Dec 15 18:57:12 2022 +0100 - Target Net5.0 Framework in samples too. (#267) + [skip ci] Update ReadMe - samples/BermudanSwaption/BermudanSwaption.csproj | 2 +- - samples/Bonds/Bonds.csproj | 2 +- - samples/CVAIRS/CVAIRS.csproj | 2 +- - samples/CallableBonds/CallableBonds.csproj | 2 +- - samples/ConvertibleBonds/ConvertibleBonds.csproj | 2 +- - samples/EquityOption/EquityOption.csproj | 2 +- - samples/FRA/FRA.csproj | 2 +- - samples/FittedBondCurve/FittedBondCurve.csproj | 2 +- - samples/Repo/Repo.csproj | 2 +- - samples/Swap/Swap.csproj | 2 +- - 10 files changed, 10 insertions(+), 10 deletions(-) + README.md | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) -commit a9c2fc211788e3ff90705aa8bebf6043f98fc38e +commit 78511587c84e13dba99fc23a2bd98340efc35093 Author: Andrea Maggiulli -Date: Tue Mar 2 19:39:41 2021 +0100 +Date: Thu Dec 15 18:53:13 2022 +0100 - Updated tests suite to .Net Core 5. + Fixing WAL calculation. - tests/QLNet.Tests/QLNet.Tests.csproj | 2 +- - tests/QLNet.Tests/T_Inflation.cs | 2 +- - 2 files changed, 2 insertions(+), 2 deletions(-) + src/QLNet/Pricingengines/Bond/BondFunctions.cs | 19 +++++++++++++------ + 1 file changed, 13 insertions(+), 6 deletions(-) -commit f0d80d3b82de1effee85ad3686d8d9006e721c40 +commit e1eee4f71022ff6fc9a285126d45322b1d15c3f0 Author: Andrea Maggiulli -Date: Tue Mar 2 19:12:22 2021 +0100 +Date: Tue Nov 22 21:56:24 2022 +0100 - Fixed ActualActualISMA daycounter calculation for long/short final periods, thanks @kristofferpagels. - This close #265 + Added new method to Schedule to directly add irregular dates. - src/QLNet/Time/DayCounters/ActualActual.cs | 39 +++++++++++++++++++---- - tests/QLNet.Tests/T_DayCounters.cs | 51 ++++++++++++++++++++++++++++++ - 2 files changed, 83 insertions(+), 7 deletions(-) + src/QLNet/Time/Schedule.cs | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) -commit 452499ef5a73174e4f82ad12c1b1af38e7433601 -Author: Francois Botha -Date: Wed Feb 24 02:56:46 2021 +0200 +commit e185ddc7a73286d143f197943f2104e8c569064f +Author: Andrea Maggiulli +Date: Sat Nov 19 20:13:39 2022 +0100 - Add utility Date.ToDateTime() method (#263) + Updated a bunch of calendars up to 2022. - src/QLNet/Time/Date.cs | 2 ++ - 1 file changed, 2 insertions(+) + src/QLNet/Time/Calendars/Australia.cs | 10 ++++++---- + src/QLNet/Time/Calendars/China.cs | 17 ++++++++++++++++- + src/QLNet/Time/Calendars/CzechRepublic.cs | 2 ++ + src/QLNet/Time/Calendars/Iceland.cs | 5 ++--- + src/QLNet/Time/Calendars/NewZealand.cs | 3 ++- + src/QLNet/Time/Calendars/UnitedKingdom.cs | 6 +++++- + 6 files changed, 33 insertions(+), 10 deletions(-) -commit f883f6ba4b8c315714d778f47e7c57d15670aba5 +commit 3c45dc68fb959955348933941d06414f4c3ece5e +Merge: 49ffacd b2fe6f6 Author: Andrea Maggiulli -Date: Wed Feb 24 01:12:18 2021 +0100 +Date: Sat Nov 19 19:19:33 2022 +0100 - Fixing Nuget deployement. + Merge branch 'develop' of github.com:amaggiulli/QLNet into develop - appveyor.yml | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -commit 340bb979be30b6f027042aa4620ab8ea01f5006f +commit 49ffacd9515affd037b06815633fc42707f9a0ac Author: Andrea Maggiulli -Date: Wed Feb 24 00:52:24 2021 +0100 +Date: Sat Nov 19 19:19:19 2022 +0100 + + Refactoring Calendars + - Refactored and cleanup Calendar Bridge pattern + - Updated all calendars. + + src/QLNet/Time/Calendar.cs | 300 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------------------------- + src/QLNet/Time/Calendars/Argentina.cs | 13 ++--- + src/QLNet/Time/Calendars/Australia.cs | 17 +++---- + src/QLNet/Time/Calendars/Austria.cs | 16 +++--- + src/QLNet/Time/Calendars/BespokeCalendar.cs | 41 ++++++++------- + src/QLNet/Time/Calendars/Botswana.cs | 15 +++--- + src/QLNet/Time/Calendars/Brazil.cs | 125 ++++++++++++++++++++++----------------------- + src/QLNet/Time/Calendars/Canada.cs | 109 ++++++++++++++++++--------------------- + src/QLNet/Time/Calendars/Chile.cs | 4 +- + src/QLNet/Time/Calendars/China.cs | 33 +++++------- + src/QLNet/Time/Calendars/CzechRepublic.cs | 13 ++--- + src/QLNet/Time/Calendars/Denmark.cs | 14 ++--- + src/QLNet/Time/Calendars/Finland.cs | 14 ++--- + src/QLNet/Time/Calendars/France.cs | 12 ++--- + src/QLNet/Time/Calendars/Germany.cs | 75 ++++++++++++++------------- + src/QLNet/Time/Calendars/HongKong.cs | 14 ++--- + src/QLNet/Time/Calendars/Hungary.cs | 14 ++--- + src/QLNet/Time/Calendars/Iceland.cs | 13 ++--- + src/QLNet/Time/Calendars/India.cs | 14 ++--- + src/QLNet/Time/Calendars/Indonesia.cs | 29 +++++------ + src/QLNet/Time/Calendars/Israel.cs | 49 +++++++++--------- + src/QLNet/Time/Calendars/Italy.cs | 40 +++++++-------- + src/QLNet/Time/Calendars/Japan.cs | 26 +++++----- + src/QLNet/Time/Calendars/JointCalendar.cs | 3 +- + src/QLNet/Time/Calendars/Mexico.cs | 14 ++--- + src/QLNet/Time/Calendars/NewZealand.cs | 14 ++--- + src/QLNet/Time/Calendars/Norway.cs | 14 ++--- + src/QLNet/Time/Calendars/NullCalendar.cs | 7 +-- + src/QLNet/Time/Calendars/Poland.cs | 14 ++--- + src/QLNet/Time/Calendars/Romania.cs | 41 ++++++++------- + src/QLNet/Time/Calendars/Russia.cs | 30 +++++------ + src/QLNet/Time/Calendars/SaudiArabia.cs | 14 ++--- + src/QLNet/Time/Calendars/Singapore.cs | 14 ++--- + src/QLNet/Time/Calendars/Slovakia.cs | 13 ++--- + src/QLNet/Time/Calendars/SouthAfrica.cs | 14 ++--- + src/QLNet/Time/Calendars/SouthKorea.cs | 40 ++++++++------- + src/QLNet/Time/Calendars/Sweden.cs | 12 ++--- + src/QLNet/Time/Calendars/Switzerland.cs | 14 ++--- + src/QLNet/Time/Calendars/TARGET.cs | 15 +++--- + src/QLNet/Time/Calendars/Taiwan.cs | 12 ++--- + src/QLNet/Time/Calendars/Thailand.cs | 45 ++++++++-------- + src/QLNet/Time/Calendars/Turkey.cs | 14 ++--- + src/QLNet/Time/Calendars/Ukraine.cs | 22 ++++---- + src/QLNet/Time/Calendars/UnitedKingdom.cs | 40 ++++++++------- + src/QLNet/Time/Calendars/UnitedStates.cs | 90 ++++++++++++++++---------------- + src/QLNet/Time/Calendars/WeekendsOnly.cs | 6 +-- + tests/QLNet.Tests/T_BusinessDayConvention.cs | 2 +- + tests/QLNet.Tests/T_Calendars.cs | 36 ++++++------- + 48 files changed, 800 insertions(+), 740 deletions(-) + +commit b2fe6f6e909d9b9bfaf7f32fcce74c30ad093b51 +Author: Andrea Maggiulli +Date: Fri Nov 18 20:02:30 2022 +0100 - Disabling failing test. + [skip ci] Update ReadMe - tests/QLNet.Tests/T_Piecewiseyieldcurve.cs | 2 +- + README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) -commit e9d4f6d661b9d515858de77970943e30533ce9df +commit b598cd0b9551ca5b1e9402920e3054944ee8f15a Author: Andrea Maggiulli -Date: Wed Feb 24 00:28:48 2021 +0100 +Date: Fri Nov 18 19:23:00 2022 +0100 - Fixing Nuget deployement. + Added Actual364, Actual36525 and Thirty365 daycounters. + Updated daycounters tests suite. - appveyor.yml | 7 +++---- - 1 file changed, 3 insertions(+), 4 deletions(-) + src/QLNet/Time/DayCounters/Actual364.cs | 39 ++++++++++++++++ + src/QLNet/Time/DayCounters/Actual36525.cs | 61 +++++++++++++++++++++++++ + src/QLNet/Time/DayCounters/Thirty365.cs | 49 ++++++++++++++++++++ + tests/QLNet.Tests/T_DayCounters.cs | 272 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- + 4 files changed, 418 insertions(+), 3 deletions(-) -commit db83a242cdc5c09a944994d02ec6ef8f2935fb00 +commit b7baaedcb099e82d823a04b7a259ca17f230677d Author: Andrea Maggiulli -Date: Tue Feb 23 22:34:02 2021 +0100 +Date: Wed Nov 16 23:38:34 2022 +0100 - Fixing the build + Added Actual/366 daycounter. - appveyor.yml | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) + src/QLNet/Time/DayCounters/Actual366.cs | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 53 insertions(+) -commit 33eba0af05268777c4a22371ccf8a304f10f6897 +commit c523a9d2026604cc219661ca0d7797eddc0c3524 +Author: Andrea Maggiulli +Date: Wed Nov 16 23:28:16 2022 +0100 + + Refactoring DayCounters : + - Refactored and cleanup DayCounter Bridge pattern + - Updated all daycounters. + + src/QLNet/Cashflows/CPICoupon.cs | 4 +- + src/QLNet/Instruments/Bonds/PSACurve.cs | 4 +- + src/QLNet/Time/DayCounter.cs | 91 +++++++++++++++++++++++++--------------------- + src/QLNet/Time/DayCounters/Actual360.cs | 44 ++++++---------------- + src/QLNet/Time/DayCounters/Actual365Fixed.cs | 85 +++++++++++++++++++++++++++++++++++++++---- + src/QLNet/Time/DayCounters/Actual365NoLeap.cs | 67 ---------------------------------- + src/QLNet/Time/DayCounters/ActualActual.cs | 89 +++++++++++++++++---------------------------- + src/QLNet/Time/DayCounters/Business252.cs | 147 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----- + src/QLNet/Time/DayCounters/OneDayCounter.cs | 10 ++--- + src/QLNet/Time/DayCounters/SimpleDayCounter.cs | 34 ++++++++++------- + src/QLNet/Time/DayCounters/Thirty360.cs | 197 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------- + tests/QLNet.Tests/T_AssetSwap.cs | 42 ++++++++++----------- + tests/QLNet.Tests/T_Bermudanswaption.cs | 3 +- + tests/QLNet.Tests/T_Bonds.cs | 14 +++---- + tests/QLNet.Tests/T_CapFlooredCoupon.cs | 4 +- + tests/QLNet.Tests/T_CreditDefaultSwap.cs | 8 ++-- + tests/QLNet.Tests/T_DayCounters.cs | 55 +++++++++++++++++++++++++++- + tests/QLNet.Tests/T_Inflation.cs | 6 +-- + tests/QLNet.Tests/T_InflationCapFloorTest.cs | 2 +- + tests/QLNet.Tests/T_InflationCapFlooredCouponTest.cs | 4 +- + tests/QLNet.Tests/T_OvernightIndexedSwap.cs | 6 +-- + tests/QLNet.Tests/T_ShortRateModels.cs | 6 +-- + tests/QLNet.Tests/T_Swaps.cs | 3 +- + tests/QLNet.Tests/T_Swaption.cs | 11 +++--- + tests/QLNet.Tests/T_TermStructures.cs | 4 +- + 25 files changed, 598 insertions(+), 342 deletions(-) + +commit e2ec6a666b0e54bcf2ab6b0caaf5309aecc48d7f Author: Andrea Maggiulli -Date: Tue Feb 23 21:30:45 2021 +0100 +Date: Thu Sep 22 20:11:25 2022 +0200 - Fixing the build + Added back multi-target with netstandard 2.0 for .NET framework users. Should close #273. - appveyor.yml | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) + src/QLNet/QLNet.csproj | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) -commit d315456be91f161123899325095ed0ae90923de1 +commit cfc7964ef5e90dcb6b6d6a53310ed8362861858d Author: Andrea Maggiulli -Date: Tue Feb 23 21:00:42 2021 +0100 +Date: Mon Sep 12 16:04:50 2022 +0200 - Fixing the build + Added batch Bond Functions calculation with benchmark test. - appveyor.yml | 5 +++-- - 1 file changed, 3 insertions(+), 2 deletions(-) + src/QLNet/Pricingengines/Bond/BondFunctions.cs | 44 ++++++++++++++++++++++++++++++++++++++++++++ + src/QLNet/Requests/BondFunctionsRequest.cs | 38 ++++++++++++++++++++++++++++++++++++++ + src/QLNet/Responses/BondFunctionsResponse.cs | 32 ++++++++++++++++++++++++++++++++ + tests/QLNet.Tests/Fakers/BondFunctionRequestFaker.cs | 33 +++++++++++++++++++++++++++++++++ + tests/QLNet.Tests/T_Batch.cs | 14 ++++++++++++++ + 5 files changed, 161 insertions(+) -commit b4568f366e95dc47d9b966836be5805a37e13fcc -Author: Andrea Maggiulli -Date: Tue Feb 23 19:49:42 2021 +0100 +commit 66d3bac82dcc5be296e61cb551a55ec879b7e1b1 +Author: Andrea Maggiulli +Date: Fri Aug 26 20:27:30 2022 +0200 - Fixing the build + [skip ci] Updated status badge - appveyor.yml | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) + README.md | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) -commit 66d282df47d00af25478de9e7f192390c368a47d +commit cd4d7fbbc06134ce6265ae0d09917079013c6fff +Author: Andrea Maggiulli +Date: Fri Aug 26 20:02:00 2022 +0200 + + Added batch calculations (#272) + + * Added batch yield calculation with benchmark tests. + + * Added batch Weighted Average Life calculation with benchmark test. + + * Added batch Duration and Accrued calculation with benchmark test. + + src/QLNet/Pricingengines/Bond/BondFunctions.cs | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- + src/QLNet/Requests/AccruedRequest.cs | 27 +++++++++++++++++++++++++++ + src/QLNet/Requests/DurationRequest.cs | 32 ++++++++++++++++++++++++++++++++ + src/QLNet/Requests/WalRequest.cs | 31 +++++++++++++++++++++++++++++++ + src/QLNet/Requests/YieldRequest.cs | 34 ++++++++++++++++++++++++++++++++++ + src/QLNet/Responses/AccruedResponse.cs | 27 +++++++++++++++++++++++++++ + src/QLNet/Responses/DurationResponse.cs | 26 ++++++++++++++++++++++++++ + src/QLNet/Responses/WalResponse.cs | 28 ++++++++++++++++++++++++++++ + src/QLNet/Responses/YieldResponse.cs | 26 ++++++++++++++++++++++++++ + tests/QLNet.Tests/Fakers/AccruedRequestFaker.cs | 42 ++++++++++++++++++++++++++++++++++++++++++ + tests/QLNet.Tests/Fakers/DurationRequestFaker.cs | 47 +++++++++++++++++++++++++++++++++++++++++++++++ + tests/QLNet.Tests/Fakers/FixedRateBondFaker.cs | 34 ++++++++++++++++++++++++++++++++++ + tests/QLNet.Tests/Fakers/ScheduleFaker.cs | 38 ++++++++++++++++++++++++++++++++++++++ + tests/QLNet.Tests/Fakers/WalRequestFaker.cs | 44 ++++++++++++++++++++++++++++++++++++++++++++ + tests/QLNet.Tests/Fakers/YieldRequestFaker.cs | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ + tests/QLNet.Tests/QLNet.Tests.csproj | 1 + + tests/QLNet.Tests/T_Batch.cs | 99 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + 17 files changed, 646 insertions(+), 2 deletions(-) + +commit dbe969b82ba11d13bdf325e3d7259786bf4bb252 Author: Andrea Maggiulli -Date: Tue Feb 23 19:12:29 2021 +0100 +Date: Tue Jul 12 18:45:39 2022 +0200 - Fixing the build + Fixing AppVeyor Java Home - appveyor.yml | 2 ++ - 1 file changed, 2 insertions(+) + appveyor.yml | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) -commit 82cf2d16be6e24244a39c45cc0764cc280657450 +commit dcb271097e5cc3ad5674791a1772f45aef22d103 Author: Andrea Maggiulli -Date: Mon Sep 14 18:23:46 2020 +0200 +Date: Tue Jul 12 18:19:09 2022 +0200 - Fixed helpers sort for Piecewise Curves. This should fix #260 + Fixing AppVeyor Java Home - src/QLNet/Termstructures/Inflation/PiecewiseYoYInflationCurve.cs | 3 +++ - src/QLNet/Termstructures/Inflation/PiecewiseZeroInflationCurve.cs | 3 +++ - src/QLNet/Termstructures/Iterativebootstrap.cs | 3 --- - src/QLNet/Termstructures/Yield/PiecewiseYieldCurve.cs | 6 ++++++ - 4 files changed, 12 insertions(+), 3 deletions(-) + appveyor.yml | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) -commit 549a72d527d986426e01668e03a3ad3817f23cdd +commit cc974824a0e73df122c04403e6c9d2945e975df1 Author: Andrea Maggiulli -Date: Wed May 20 16:32:36 2020 +0200 +Date: Tue Jul 12 17:51:49 2022 +0200 - #256 - Fixed Denmark holidays, thx @hhaldn + Updated appveyor.yml - src/QLNet/Time/Calendars/Denmark.cs | 12 +++++++++--- - 1 file changed, 9 insertions(+), 3 deletions(-) + appveyor.yml | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) -commit d837fec600e517c1c4590600eccb504535df9b8f -Author: Andrea Maggiulli -Date: Mon May 11 17:49:50 2020 +0200 +commit 29c5ef1225a157859091262963c065da232989c6 +Author: Andrea Maggiulli +Date: Tue Jul 12 17:46:53 2022 +0200 - Updated Sonar badges [skip ci] + Updated to .NET 6.0 - README.md | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) + src/QLNet/Cashflows/OvernightIndexedCoupon.cs | 2 +- + src/QLNet/QLNet.csproj | 6 ++---- + tests/QLNet.Tests/QLNet.Tests.csproj | 2 +- + 3 files changed, 4 insertions(+), 6 deletions(-) -commit c397fb4da2e0d02b58dc9146d0d888caf677e39c -Author: Andrea Maggiulli -Date: Mon May 11 17:48:39 2020 +0200 +commit bcb57944fe3aeb77ae24336eed5f3e8cd8c5aed0 +Author: Andrea Maggiulli +Date: Tue Jul 12 17:10:20 2022 +0200 - Updated Sonar badges [skip ci] + Fixing tests. - README.md | 13 ++++++++----- - 1 file changed, 8 insertions(+), 5 deletions(-) + tests/QLNet.Tests/T_OvernightIndexedCoupon.cs | 22 +++++++++++++++++----- + tests/QLNet.Tests/T_SofrFutures.cs | 4 ++-- + 2 files changed, 19 insertions(+), 7 deletions(-) -commit b19ac52d07a295ec4a53986dcb1e6653462cb8b3 -Author: Andrea Maggiulli -Date: Mon May 11 17:02:07 2020 +0200 +commit 1cf3e8eed251f4910cef1e0a48d5e2760ada41a8 +Merge: b057161 5ad17b8 +Author: Andrea Maggiulli +Date: Tue Jul 12 16:38:54 2022 +0200 - Feature #253 (#254) - - * #253 Moved example projects into samples folder. - - * #253 Set feature's build to Release, enabled tests. - - * #253 Set feature's build to Release, enabled tests. - - * #253 Removed MSTest framework, updated test project to netcore. - - * #253 Updated test script for features. - - * #253 Fixing tests. - - * #253 Fixing tests. - - * #253 Fixing tests. - - * #253 Removed old projects files. - - * #253 Fixing build. - - * Revert "#253 Fixing tests." - - * #253 Fixed compilation warnings. - - * #253 Fixing appveyor script [skip ci] - - * #253 Fixing appveyor script. - - * #253 Moved to the new SonarCloud site. - - * #253 Fixing appveyor script. + Merge branch 'develop' of github.com:amaggiulli/QLNet into develop + +commit b057161a5dab98360602fd54a3c61012824024ff +Author: Andrea Maggiulli +Date: Sun Jul 10 13:35:46 2022 +0200 + + Added SOFR Index and OvernightIndexFuture instrument with helpers and tests. + + src/QLNet/Cashflows/IborCoupon.cs | 6 ++- + src/QLNet/Cashflows/OvernightIndexedCoupon.cs | 112 ++++++++++++++++++++++++++++++++++++++- + src/QLNet/Cashflows/RateAveragingType.cs | 31 +++++++++++ + src/QLNet/Indexes/Ibor/Sofr.cs | 26 +++++++++ + src/QLNet/Instruments/OvernightIndexFuture.cs | 145 ++++++++++++++++++++++++++++++++++++++++++++++++++ + src/QLNet/Termstructures/Yield/BasisSwapRateHelpers.cs | 213 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + src/QLNet/Termstructures/Yield/OvernightIndexFutureRateHelper.cs | 137 ++++++++++++++++++++++++++++++++++++++++++++++++ + tests/QLNet.Tests/T_BasisSwapRateHelpers.cs | 241 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + tests/QLNet.Tests/T_OvernightIndexedCoupon.cs | 250 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + tests/QLNet.Tests/T_SofrFutures.cs | 104 ++++++++++++++++++++++++++++++++++++ + 10 files changed, 1263 insertions(+), 2 deletions(-) + +commit 5ad17b80d26c6da44a8878fb52e5c3b53d9fd9fb +Author: ninetiger +Date: Sun Jul 10 22:38:43 2022 +1200 + + Support New Zealand's new publish holiday: Matariki holiday. (#271) - * #253 Fixing appveyor script. + * features/supportNzMatarikiHolidays - * #253 Adding coverage for Sonar. + * Update unit test only - * #253 Fixing tests. - - QLNet.sln | 74 +- - QLNetOld.sln | 38 - - appveyor.yml | 66 +- - {src => samples}/BermudanSwaption/.editorconfig | 0 - .../BermudanSwaption/BermudanSwaption.cs | 0 - .../BermudanSwaption/BermudanSwaption.csproj | 2 +- - {src => samples}/Bonds/.editorconfig | 0 - {src => samples}/Bonds/Bonds.cs | 0 - {src => samples}/Bonds/Bonds.csproj | 2 +- - {src => samples}/CVAIRS/CVAIRS.cs | 0 - {src => samples}/CVAIRS/CVAIRS.csproj | 2 +- - {src => samples}/CallableBonds/.editorconfig | 0 - {src => samples}/CallableBonds/CallableBonds.cs | 8 +- - .../CallableBonds/CallableBonds.csproj | 2 +- - {src => samples}/ConvertibleBonds/.editorconfig | 0 - .../ConvertibleBonds/ConvertibleBonds.cs | 6 +- - .../ConvertibleBonds/ConvertibleBonds.csproj | 2 +- - {src => samples}/EquityOption/.editorconfig | 0 - {src => samples}/EquityOption/EquityOption.cs | 0 - {src => samples}/EquityOption/EquityOption.csproj | 2 +- - {src => samples}/FRA/.editorconfig | 0 - {src => samples}/FRA/FRA.cs | 0 - {src => samples}/FRA/FRA.csproj | 2 +- - {src => samples}/FittedBondCurve/.editorconfig | 0 - .../FittedBondCurve/FittedBondCurve.cs | 0 - .../FittedBondCurve/FittedBondCurve.csproj | 2 +- - samples/QLNetSamples.sln | 79 + - {src => samples}/Repo/.editorconfig | 0 - {src => samples}/Repo/Repo.cs | 0 - {src => samples}/Repo/Repo.csproj | 2 +- - {src => samples}/Swap/.editorconfig | 0 - {src => samples}/Swap/Swap.csproj | 2 +- - {src => samples}/Swap/swapvaluation.cs | 0 - {src => samples}/VB/Swap/Swapvaluation.vb | 0 - src/QLNet.Old/AssemblyInfo.cs | 34 - - src/QLNet.Old/Cashflows/.gitkeep | 0 - src/QLNet.Old/Currencies/.gitkeep | 0 - src/QLNet.Old/Extensions/.gitkeep | 0 - src/QLNet.Old/Indexes/Ibor/.gitkeep | 0 - src/QLNet.Old/Indexes/Inflation/.gitkeep | 0 - src/QLNet.Old/Indexes/Swap/.gitkeep | 0 - src/QLNet.Old/Instruments/Bonds/.gitkeep | 0 - src/QLNet.Old/Math/Distributions/.gitkeep | 0 - src/QLNet.Old/Math/Interpolations/.gitkeep | 0 - src/QLNet.Old/Math/Optimization/.gitkeep | 0 - src/QLNet.Old/Math/Solvers1d/.gitkeep | 0 - src/QLNet.Old/Math/integrals/.gitkeep | 0 - src/QLNet.Old/Math/matrixutilities/.gitkeep | 0 - src/QLNet.Old/Math/randomnumbers/.gitkeep | 0 - src/QLNet.Old/Math/statistics/.gitkeep | 0 - src/QLNet.Old/Methods/Finitedifferences/.gitkeep | 0 - src/QLNet.Old/Methods/lattices/.gitkeep | 0 - src/QLNet.Old/Methods/montecarlo/.gitkeep | 0 - src/QLNet.Old/Models/Equity/.gitkeep | 0 - .../MarketModels/BrownianGenerators/.gitkeep | 0 - .../Models/Shortrate/Onefactormodels/.gitkeep | 0 - .../Models/Shortrate/Twofactorsmodels/.gitkeep | 0 - .../Models/Shortrate/calibrationhelpers/.gitkeep | 0 - src/QLNet.Old/Patterns/.gitkeep | 0 - src/QLNet.Old/Pricingengines/Basket/.gitkeep | 0 - src/QLNet.Old/Pricingengines/Bond/.gitkeep | 0 - src/QLNet.Old/Pricingengines/CapFloor/.gitkeep | 0 - src/QLNet.Old/Pricingengines/Cliquet/.gitkeep | 0 - src/QLNet.Old/Pricingengines/Forward/.gitkeep | 0 - src/QLNet.Old/Pricingengines/Loan/.gitkeep | 0 - src/QLNet.Old/Pricingengines/Lookback/.gitkeep | 0 - src/QLNet.Old/Pricingengines/Swap/.gitkeep | 0 - src/QLNet.Old/Pricingengines/asian/.gitkeep | 0 - src/QLNet.Old/Pricingengines/barrier/.gitkeep | 0 - src/QLNet.Old/Pricingengines/credit/.gitkeep | 0 - src/QLNet.Old/Pricingengines/inflation/.gitkeep | 0 - src/QLNet.Old/Pricingengines/swaption/.gitkeep | 0 - src/QLNet.Old/Pricingengines/vanilla/.gitkeep | 0 - src/QLNet.Old/QLNet.Old.csproj | 2154 ------------- - src/QLNet.Old/Quotes/.gitkeep | 0 - src/QLNet.Old/Termstructures/Credit/.gitkeep | 0 - src/QLNet.Old/Termstructures/Inflation/.gitkeep | 0 - .../Termstructures/Volatility/Bond/.gitkeep | 0 - .../Termstructures/Volatility/CapFloor/.gitkeep | 0 - .../Termstructures/Volatility/Inflation/.gitkeep | 0 - .../Termstructures/Volatility/Optionlet/.gitkeep | 0 - .../Termstructures/Volatility/equityfx/.gitkeep | 0 - .../Termstructures/Volatility/swaption/.gitkeep | 0 - src/QLNet.Old/Termstructures/Yield/.gitkeep | 0 - src/QLNet.Old/Time/Calendars/.gitkeep | 0 - src/QLNet.Old/Time/DayCounters/.gitkeep | 0 - src/QLNet.Old/legacy/libormarketmodels/.gitkeep | 0 - src/QLNet.Old/processes/.gitkeep | 0 - src/QLNet/QLNet.csproj | 2 +- - tests/QLNet.Tests.Old/AssemblyInfo.cs | 39 - - tests/QLNet.Tests.Old/QLNet.Tests.Old.csproj | 212 -- - tests/QLNet.Tests/QLNet.Tests.csproj | 24 +- - tests/QLNet.Tests/T_AmericanOption.cs | 44 +- - tests/QLNet.Tests/T_AsianOptions.cs | 34 +- - tests/QLNet.Tests/T_AssetSwap.cs | 60 +- - tests/QLNet.Tests/T_BarrierOption.cs | 28 +- - tests/QLNet.Tests/T_BasketOption.cs | 15 +- - tests/QLNet.Tests/T_Bermudanswaption.cs | 27 +- - tests/QLNet.Tests/T_BinaryOption.cs | 16 +- - tests/QLNet.Tests/T_BlackDeltaCalculator.cs | 24 +- - tests/QLNet.Tests/T_BlackFormula.cs | 16 +- - tests/QLNet.Tests/T_Bonds.cs | 174 +- - tests/QLNet.Tests/T_BusinessDayConvention.cs | 12 +- - tests/QLNet.Tests/T_CPISwap.cs | 20 +- - tests/QLNet.Tests/T_Calendars.cs | 93 +- - tests/QLNet.Tests/T_CapFloor.cs | 51 +- - tests/QLNet.Tests/T_CapFlooredCoupon.cs | 16 +- - tests/QLNet.Tests/T_CashFlows.cs | 24 +- - tests/QLNet.Tests/T_CatBonds.cs | 37 +- - tests/QLNet.Tests/T_CliquetOption.cs | 21 +- - tests/QLNet.Tests/T_Cms.cs | 20 +- - tests/QLNet.Tests/T_ConvertibleBond.cs | 20 +- - tests/QLNet.Tests/T_CreditDefaultSwap.cs | 32 +- - tests/QLNet.Tests/T_Dates.cs | 28 +- - tests/QLNet.Tests/T_DayCounters.cs | 48 +- - tests/QLNet.Tests/T_DefaultProbabilityCurves.cs | 20 +- - tests/QLNet.Tests/T_DigitalCoupon.cs | 40 +- - tests/QLNet.Tests/T_DigitalOption.cs | 40 +- - tests/QLNet.Tests/T_DividendOption.cs | 42 +- - tests/QLNet.Tests/T_DoubleBarrierOption.cs | 21 +- - tests/QLNet.Tests/T_DoubleBinaryOption.cs | 12 +- - tests/QLNet.Tests/T_EuropeanOption.cs | 55 +- - tests/QLNet.Tests/T_ExchangeRate.cs | 28 +- - tests/QLNet.Tests/T_FastFourierTransform.cs | 17 +- - tests/QLNet.Tests/T_FdmLinearOp.cs | 48 +- - tests/QLNet.Tests/T_ForwardOption.cs | 28 +- - tests/QLNet.Tests/T_Functions.cs | 28 +- - tests/QLNet.Tests/T_HestonModel.cs | 78 +- - .../QLNet.Tests/T_HybridHestonHullWhiteProcess.cs | 60 +- - tests/QLNet.Tests/T_Inflation.cs | 24 +- - tests/QLNet.Tests/T_InflationCPICapFloor.cs | 16 +- - tests/QLNet.Tests/T_InflationCapFloorTest.cs | 35 +- - .../QLNet.Tests/T_InflationCapFlooredCouponTest.cs | 31 +- - tests/QLNet.Tests/T_Instruments.cs | 17 +- - tests/QLNet.Tests/T_InterestRate.cs | 12 +- - tests/QLNet.Tests/T_Interpolations.cs | 80 +- - tests/QLNet.Tests/T_LiborMarketModel.cs | 39 +- - tests/QLNet.Tests/T_LiborMarketModelProcess.cs | 36 +- - .../QLNet.Tests/T_LinearLeastSquaresRegression.cs | 36 +- - tests/QLNet.Tests/T_LookbackOption.cs | 24 +- - tests/QLNet.Tests/T_LowDiscrepancySequences.cs | 79 +- - tests/QLNet.Tests/T_Matrices.cs | 36 +- - tests/QLNet.Tests/T_Money.cs | 20 +- - tests/QLNet.Tests/T_Operators.cs | 16 +- - tests/QLNet.Tests/T_Optimizers.cs | 24 +- - tests/QLNet.Tests/T_OptionletStripper.cs | 40 +- - tests/QLNet.Tests/T_OvernightIndexedSwap.cs | 40 +- - tests/QLNet.Tests/T_PSACurve.cs | 12 +- - tests/QLNet.Tests/T_PathGenerator.cs | 35 +- - .../T_PiecewiseZeroSpreadedTermStructure.cs | 48 +- - tests/QLNet.Tests/T_Piecewiseyieldcurve.cs | 168 +- - tests/QLNet.Tests/T_Quotes.cs | 25 +- - tests/QLNet.Tests/T_RNGTraits.cs | 20 +- - tests/QLNet.Tests/T_RangeAccrual.cs | 3150 ++++++++++---------- - tests/QLNet.Tests/T_RiskStats.cs | 12 +- - tests/QLNet.Tests/T_Rounding.cs | 28 +- - tests/QLNet.Tests/T_SVI.cs | 35 +- - tests/QLNet.Tests/T_SampledCurve.cs | 12 +- - tests/QLNet.Tests/T_Schedule.cs | 44 +- - tests/QLNet.Tests/T_ShortRateModels.cs | 20 +- - tests/QLNet.Tests/T_Solvers.cs | 44 +- - tests/QLNet.Tests/T_SpreadOption.cs | 12 +- - tests/QLNet.Tests/T_Stats.cs | 26 +- - tests/QLNet.Tests/T_Swaps.cs | 51 +- - tests/QLNet.Tests/T_Swaption.cs | 51 +- - tests/QLNet.Tests/T_SwaptionVolatilityCube.cs | 822 +++-- - tests/QLNet.Tests/T_SwaptionVolatilitymatrix.cs | 31 +- - tests/QLNet.Tests/T_TermStructures.cs | 73 +- - tests/QLNet.Tests/T_TimeGrid.cs | 40 +- - tests/QLNet.Tests/T_Vector.cs | 24 +- - tests/QLNet.Tests/Utilities.cs | 66 - - tests/QLNet.Tests/xunit.runner.json | 6 + - 172 files changed, 2355 insertions(+), 7238 deletions(-) - -commit b5a730b3518b0b6b5889ec2397fa2f42a37de179 + Co-authored-by: Xiao Gong + + src/QLNet/Time/Calendars/NewZealand.cs | 41 ++++++++++++++++++++++++++++++++++++++++- + tests/QLNet.Tests/T_Calendars.cs | 20 ++++++++++++++++++++ + 2 files changed, 60 insertions(+), 1 deletion(-) + +commit 762fa9e556689a05f585bda12d2bc76483262a8c +Author: Andrea Maggiulli +Date: Mon May 23 19:15:09 2022 +0200 + + Removed local test example. + + appveyor.yml | 4 ++-- + tests/QLNet.Tests/T_Bonds.cs | 14 -------------- + 2 files changed, 2 insertions(+), 16 deletions(-) + +commit 3ab894402b6e076c21676ed1b7a7042923201b33 Author: Andrea Maggiulli -Date: Thu May 7 16:45:13 2020 +0200 +Date: Mon May 23 18:47:21 2022 +0200 - #252 Fixed LineSearchBasedMethod.minimize, thx @hhaldn for spotting it. + Added new CashFlows method to return both accrued days and accrued amount. - .../Math/Optimization/LineSearchBasedMethod.cs | 4 +-- - tests/QLNet.Tests/T_Optimizers.cs | 40 ++++++++++++++++++++++ - 2 files changed, 42 insertions(+), 2 deletions(-) + src/QLNet/Cashflows/CashFlows.cs | 20 ++++++++++++++++++++ + tests/QLNet.Tests/T_Bonds.cs | 48 +++++++++++++++++++++++++++++++++++++----------- + 2 files changed, 57 insertions(+), 11 deletions(-) -commit 9da55ba9830963b3a3aac59182054e44be57cfbd +commit 8bfee0f3ec000e17ab0fdbdcfdf67087e2edcf25 Author: Andrea Maggiulli -Date: Thu May 7 16:10:41 2020 +0200 +Date: Tue Dec 7 19:04:21 2021 +0100 + + Removed AStyle formatting [skip ci] - Bumping to version 1.12.0 for next release [skip ci] + format_code.bat | 2 -- + qlnet.astyle | 28 ---------------------------- + tools/AStyle.exe | Bin 595456 -> 0 bytes + 3 files changed, 30 deletions(-) + +commit 6b1872e8b41c7e720ba833594bcfffc1eaf89a2b +Merge: 413ab5d 6ee6eb2 +Author: Andrea Maggiulli +Date: Tue Dec 7 18:53:54 2021 +0100 + + Merge branch 'develop' of github.com:amaggiulli/QLNet into develop + +commit 413ab5dd285942876eb10c06395ac691b24ac14f +Author: Andrea Maggiulli +Date: Tue Dec 7 18:53:41 2021 +0100 + + Updated editorconfig settings. Close #262 [skip ci] + + .editorconfig | 249 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----- + 1 file changed, 241 insertions(+), 8 deletions(-) + +commit 6ee6eb248b98edde0ab06f18233ef018a7f95444 +Author: Mogens Heller Grabe +Date: Mon Dec 6 13:39:47 2021 +0100 + + Fixed MCDiscreteAveragingAsianEngine timeGrid and ArithmeticAPOPathPricer path value retrieval (#269) + + * don't add to InitializedList, put :) + + * actually use the 'value' function - appveyor.yml | 14 +++++++------- - 1 file changed, 7 insertions(+), 7 deletions(-) \ No newline at end of file + src/QLNet/Pricingengines/asian/McDiscreteAsianEngine.cs | 16 ++++++++-------- + src/QLNet/Pricingengines/asian/Mc_Discr_Arith_Av_Price.cs | 3 +-- + 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/News.txt b/News.txt index 0daeb041..b9b86898 100644 --- a/News.txt +++ b/News.txt @@ -1,35 +1,42 @@ -QLNet 1.12.0 +QLNet 1.13.0 ========================= -QLNet 1.12.0 +QLNet 1.13.0 +Mayor changes . A detailed list of changes is available in ChangeLog.txt. FRAMEWORK -+ Removed QLNet old framework solution -+ Updated tests to use only xunit -+ Updated library to netstandard 2.1 -+ Updated test suite to .Net Core 5 -+ Updated samples to .Net Core 5 ++ Updated to .NET 7.0 / netstandard 2.0 ++ Removed AStyle formatting for more standard ediconfig setting -MATH +PRICING ENGINES -+ Fixed LineSearchBasedMethod.minimize, thx @hhaldn for spotting it. ++ Fixed MCDiscreteAveragingAsianEngine timeGrid and ArithmeticAPOPathPricer path value retrieval thanks @mookid8000 -CALENDARS +CASHFLOWS -+ Fixed Denmark holidays, thx @hhaldn -+ Added Austrian, French and Chilean calendars -+ Updated all existing calendars up to 2021 ++ Added new CashFlows method to return both accrued days and accrued amount. -TIME +INDEXES -+ Added utility Date.ToDateTime() method -+ Fixed ActualActualISMA daycounter calculation for long/short final periods, thanks @kristofferpagels. - ++ Added SOFR Index + +CALENDARS + ++ Support New Zealand's new publish holiday: Matariki holiday, thanks @ninetiger -TERMSTRUCTURES +MISC -+ Fixed helpers sort for Piecewise Curves ++ Added batch calculations + +TIME ++ Refactoring DayCounters ++ Added Actual/366 daycounter. ++ Added Actual364, Actual36525 and Thirty365 daycounters. ++ Refactoring Calendars ++ Added Cyprus and Greece calendars, thanks @pamboscy ++ Fixed Schedule until method. ++ Updated several caledars up to 2023 diff --git a/appveyor.yml b/appveyor.yml index 9c7dae78..9cb94c2d 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -6,7 +6,7 @@ branches: only: - master - version: 1.12.1 + version: 1.13.0 configuration: Release platform: Any CPU image: Visual Studio 2022 @@ -21,18 +21,18 @@ after_build: - cmd: dotnet pack src/qlnet/qlnet.csproj -c Release -o ./ artifacts: - - path: src\QLNet\bin\Release\net6.0\QLNet.dll + - path: src\QLNet\bin\Release\net7.0\QLNet.dll name: Windows - path: .\*.nupkg name: ng deploy: - provider: GitHub - tag: QLNet-v1.12.1 - release: QLNet Version 1.12.1 - description: QLNet 1.12.1 + tag: QLNet-v1.13.0 + release: QLNet Version 1.13.0 + description: QLNet 1.13.0 auth_token: - secure: yPDUXqBIfD2IB3l3KMeERPZjvD7jFC7spFIkbE92e1uWcJ4j3XSei/a966C5Aa+M - artifact: src\QLNet\bin\Release\net6.0\QLNet.dll + secure: 4cCBgQRwNh1N6VbvMJTCUMoq7BVD0Cmj04AjQa8L3k5k6MirTm8dz5TFUuqRDbHhj5x2QJS4l2EfoTsAMgc8ZhWYVJ2CV1deITs7sHYG98BrXl8NGKdPivoOZZdZT9oo + artifact: src\QLNet\bin\Release\net7.0\QLNet.dll draft: false force_update: false - provider: NuGet
  • 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 a(r%d Date: Mon, 23 May 2022 18:47:21 +0200 Subject: [PATCH 06/35] Added new CashFlows method to return both accrued days and accrued amount. --- src/QLNet/Cashflows/CashFlows.cs | 20 +++++++++++++ tests/QLNet.Tests/T_Bonds.cs | 48 ++++++++++++++++++++++++-------- 2 files changed, 57 insertions(+), 11 deletions(-) diff --git a/src/QLNet/Cashflows/CashFlows.cs b/src/QLNet/Cashflows/CashFlows.cs index 8d74f4e5..5d107086 100644 --- a/src/QLNet/Cashflows/CashFlows.cs +++ b/src/QLNet/Cashflows/CashFlows.cs @@ -629,6 +629,26 @@ public static double accruedPeriod(Leg leg, bool includeSettlementDateFlows, Dat } return 0; } + public static (int accruedDays, double accruedAmount) accruedDaysAndAmount(Leg leg, bool includeSettlementDateFlows, Date settlementDate = null) + { + if (settlementDate == null) + settlementDate = Settings.evaluationDate(); + + CashFlow cf = nextCashFlow(leg, includeSettlementDateFlows, settlementDate); + if (cf == null) + return (0,0); + Date paymentDate = cf.date(); + + foreach (CashFlow x in leg.Where(x => x.date() == paymentDate)) + { + Coupon cp = x as Coupon; + if (cp != null) + { + return (cp.accruedDays(settlementDate), cp.accruedAmount(settlementDate)); + } + } + return (0,0); + } public static int accruedDays(Leg leg, bool includeSettlementDateFlows, Date settlementDate = null) { if (settlementDate == null) diff --git a/tests/QLNet.Tests/T_Bonds.cs b/tests/QLNet.Tests/T_Bonds.cs index 53f5565f..51ddbb4d 100644 --- a/tests/QLNet.Tests/T_Bonds.cs +++ b/tests/QLNet.Tests/T_Bonds.cs @@ -1298,18 +1298,18 @@ public enum CouponType } [Theory] - [InlineData(5.25, "2/13/2018", "12/01/2032", "3/23/2018", "", 5.833)] - [InlineData( 0, "3/15/2018", "1/1/2054", "3/26/2018", "", 0.00)] - [InlineData(2.2, "3/1/2018", "3/1/2021", "3/26/2018", "", 1.53)] - [InlineData(2.25, "3/1/2018", "3/1/2021", "3/26/2018", "", 1.56)] - [InlineData(3, "2/15/2018", "2/15/2031", "3/26/2018", "", 3.42)] - [InlineData(4, "2/1/2018", "2/15/2027", "3/26/2018", "08/15/2018", 6.11)] - [InlineData(4, "2/20/2018", "10/1/2036", "3/26/2018", "", 4.00)] - [InlineData(1.85, "2/1/2018", "2/1/2021", "3/26/2018", "", 2.83)] - [InlineData(2.85, "2/15/2018", "2/15/2031", "3/26/2018", "", 3.25)] - [InlineData(5.375, "08/26/2010", "03/01/2023", "7/16/2018", "", 20.156)] + [InlineData(5.25, "2/13/2018", "12/01/2032", "3/23/2018", "", 5.833, 40)] + [InlineData(0, "3/15/2018", "1/1/2054", "3/26/2018", "", 0.00, 0)] + [InlineData(2.2, "3/1/2018", "3/1/2021", "3/26/2018", "", 1.53, 25)] + [InlineData(2.25, "3/1/2018", "3/1/2021", "3/26/2018", "", 1.56, 25)] + [InlineData(3, "2/15/2018", "2/15/2031", "3/26/2018", "", 3.42, 41)] + [InlineData(4, "2/1/2018", "2/15/2027", "3/26/2018", "08/15/2018", 6.11, 55)] + [InlineData(4, "2/20/2018", "10/1/2036", "3/26/2018", "", 4.00, 36)] + [InlineData(1.85, "2/1/2018", "2/1/2021", "3/26/2018", "", 2.83, 55)] + [InlineData(2.85, "2/15/2018", "2/15/2031", "3/26/2018", "", 3.25, 41)] + [InlineData(5.375, "08/26/2010", "03/01/2023", "7/16/2018", "", 20.156, 135)] public void testAccruedInterest(double Coupon, string AccrualDate, string MaturityDate, - string SettlementDate, string FirstCouponDate, double expectedAccruedInterest) + string SettlementDate, string FirstCouponDate, double expectedAccruedInterest, int expectedAccruedDays) { // Convert dates Date maturityDate = Convert.ToDateTime(MaturityDate, new CultureInfo("en-US")); @@ -1341,6 +1341,18 @@ public void testAccruedInterest(double Coupon, string AccrualDate, string Maturi QAssert.Fail("Failed to reproduce accrual interest at " + settlementDate + "\n calculated: " + accruedInterest + "\n expected: " + expectedAccruedInterest); + + var accruedDaysAndAmount = CashFlows.accruedDaysAndAmount(bond.cashflows(), false, settlementDate); + if (Math.Abs(accruedDaysAndAmount.accruedAmount - expectedAccruedInterest) > 1e-2) + QAssert.Fail("Failed to reproduce accrual interest at " + settlementDate + + "\n calculated: " + accruedInterest + + "\n expected: " + expectedAccruedInterest); + + if (accruedDaysAndAmount.accruedDays != expectedAccruedDays) + QAssert.Fail("Failed to reproduce accrual days at " + settlementDate + + "\n calculated: " + accruedDaysAndAmount.accruedDays + + "\n expected: " + expectedAccruedDays); + } public struct test_case @@ -1945,5 +1957,19 @@ public void testQLNetExceptions(double Coupon, QAssert.Fail("Failed to capture QLNet exception"); } + + + [Fact] + public void testFixedBuild() + { + var dates = new List() { new Date(01, 01, 2022), new Date(01, 06, 2022) }; + var schedule = new Schedule(dates, new TARGET(),BusinessDayConvention.Unadjusted, BusinessDayConvention.Unadjusted, + new Period(Frequency.Semiannual),null,null, new List(){true}); + var coupons = new List() { 0.02875 }; + + var fixed1 = new FixedRateBond(0, 100, schedule, coupons, new Actual360()); + + var yield = fixed1.yield(100, fixed1.dayCounter(), Compounding.Compounded, Frequency.Semiannual); + } } } From 762fa9e556689a05f585bda12d2bc76483262a8c Mon Sep 17 00:00:00 2001 From: Andrea Maggiulli Date: Mon, 23 May 2022 19:15:09 +0200 Subject: [PATCH 07/35] Removed local test example. --- appveyor.yml | 4 ++-- tests/QLNet.Tests/T_Bonds.cs | 14 -------------- 2 files changed, 2 insertions(+), 16 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index a0dc1c88..942bd139 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -37,7 +37,7 @@ force_update: false - provider: NuGet api_key: - secure: f2WH9fKVEnIKoXOYcDXDkR8C35WgQFvLD4xQ/SzfhkZAnXhdjyUU/3MD+OQAqNDZ + secure: B/mINFQqQ7mV7FjaOEErtgDWsvkgcnajcscRwCm6tTVFnu8+fc1Xti79M9CAZcgd skip_symbols: true artifact: ng @@ -86,7 +86,7 @@ deploy: - provider: NuGet api_key: - secure: PfSWPcbGqOhlEhGG74+J7tmJMa5RU/N19DEyjVP2GUXGQG4MWBeTmrKE/W4A70dh + secure: B/mINFQqQ7mV7FjaOEErtgDWsvkgcnajcscRwCm6tTVFnu8+fc1Xti79M9CAZcgd skip_symbols: false artifact: ng diff --git a/tests/QLNet.Tests/T_Bonds.cs b/tests/QLNet.Tests/T_Bonds.cs index 51ddbb4d..b56ba4c5 100644 --- a/tests/QLNet.Tests/T_Bonds.cs +++ b/tests/QLNet.Tests/T_Bonds.cs @@ -1957,19 +1957,5 @@ public void testQLNetExceptions(double Coupon, QAssert.Fail("Failed to capture QLNet exception"); } - - - [Fact] - public void testFixedBuild() - { - var dates = new List() { new Date(01, 01, 2022), new Date(01, 06, 2022) }; - var schedule = new Schedule(dates, new TARGET(),BusinessDayConvention.Unadjusted, BusinessDayConvention.Unadjusted, - new Period(Frequency.Semiannual),null,null, new List(){true}); - var coupons = new List() { 0.02875 }; - - var fixed1 = new FixedRateBond(0, 100, schedule, coupons, new Actual360()); - - var yield = fixed1.yield(100, fixed1.dayCounter(), Compounding.Compounded, Frequency.Semiannual); - } } } From 5ad17b80d26c6da44a8878fb52e5c3b53d9fd9fb Mon Sep 17 00:00:00 2001 From: ninetiger Date: Sun, 10 Jul 2022 22:38:43 +1200 Subject: [PATCH 08/35] Support New Zealand's new publish holiday: Matariki holiday. (#271) * features/supportNzMatarikiHolidays * Update unit test only Co-authored-by: Xiao Gong --- src/QLNet/Time/Calendars/NewZealand.cs | 41 +++++++++++++++++++++++++- tests/QLNet.Tests/T_Calendars.cs | 20 +++++++++++++ 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/src/QLNet/Time/Calendars/NewZealand.cs b/src/QLNet/Time/Calendars/NewZealand.cs index b857f503..686cafa2 100644 --- a/src/QLNet/Time/Calendars/NewZealand.cs +++ b/src/QLNet/Time/Calendars/NewZealand.cs @@ -20,6 +20,7 @@ under the terms of the QLNet license. You should have received a */ using System; +using System.Linq; namespace QLNet { @@ -38,6 +39,7 @@ namespace QLNet