From 470ffe5a5f167d89ab93ad21655b75ea30318eaf Mon Sep 17 00:00:00 2001 From: ninetiger Date: Wed, 17 Apr 2024 03:57:14 +1200 Subject: [PATCH] Added Futures Type - Custom; (#288) * Added Futures Type - Custom; This is trnasfered from the C++ code: https://github.com/lballabio/QuantLib/pull/1915 This allows passing custom future dates. Closes #1855: https://github.com/lballabio/QuantLib/issues/1855 * Update the default case message only --------- Co-authored-by: Xiao Gong --- src/QLNet/Instruments/Futures.cs | 8 ++--- src/QLNet/Termstructures/Yield/Ratehelpers.cs | 35 ++++++++++++------- 2 files changed, 25 insertions(+), 18 deletions(-) diff --git a/src/QLNet/Instruments/Futures.cs b/src/QLNet/Instruments/Futures.cs index cdcff5515..94c54528f 100644 --- a/src/QLNet/Instruments/Futures.cs +++ b/src/QLNet/Instruments/Futures.cs @@ -13,11 +13,6 @@ // 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; - namespace QLNet { public struct Futures @@ -28,8 +23,9 @@ public enum Type { IMM, /*!< Chicago Mercantile Internation Money Market, i.e. third Wednesday of March, June, September, December */ - ASX /*!< Australian Security Exchange, i.e. second Friday + ASX, /*!< Australian Security Exchange, i.e. second Friday of March, June, September, December */ + Custom /*!< Other rules */ } } } diff --git a/src/QLNet/Termstructures/Yield/Ratehelpers.cs b/src/QLNet/Termstructures/Yield/Ratehelpers.cs index af565db71..70acacb95 100644 --- a/src/QLNet/Termstructures/Yield/Ratehelpers.cs +++ b/src/QLNet/Termstructures/Yield/Ratehelpers.cs @@ -39,16 +39,18 @@ public FuturesRateHelper(Handle price, switch (type) { - case QLNet.Futures.Type.IMM: - Utils.QL_REQUIRE(QLNet.IMM.isIMMdate(iborStartDate, false), () => + case Futures.Type.IMM: + Utils.QL_REQUIRE(IMM.isIMMdate(iborStartDate, false), () => iborStartDate + " is not a valid IMM date"); break; - case QLNet.Futures.Type.ASX: + case Futures.Type.ASX: Utils.QL_REQUIRE(ASX.isASXdate(iborStartDate, false), () => iborStartDate + " is not a valid ASX date"); break; + case Futures.Type.Custom: + break; default: - Utils.QL_FAIL("unknown futures type (" + type + ")"); + Utils.QL_FAIL("unsupported futures type (" + type + ")"); break; } earliestDate_ = iborStartDate; @@ -83,8 +85,10 @@ public FuturesRateHelper(double price, Utils.QL_REQUIRE(ASX.isASXdate(iborStartDate, false), () => iborStartDate + " is not a valid ASX date"); break; + case Futures.Type.Custom: + break; default: - Utils.QL_FAIL("unknown futures type (" + type + ")"); + Utils.QL_FAIL("unsupported futures type (" + type + ")"); break; } earliestDate_ = iborStartDate; @@ -124,7 +128,7 @@ public FuturesRateHelper(Handle price, maturityDate_ = iborEndDate; } break; - case QLNet.Futures.Type.ASX: + case Futures.Type.ASX: Utils.QL_REQUIRE(ASX.isASXdate(iborStartDate, false), () => iborStartDate + " is not a valid ASX date"); if (iborEndDate == null) @@ -143,8 +147,10 @@ public FuturesRateHelper(Handle price, maturityDate_ = iborEndDate; } break; + case Futures.Type.Custom: + break; default: - Utils.QL_FAIL("unknown futures type (" + type + ")"); + Utils.QL_FAIL("unsupported futures type (" + type + ")"); break; } earliestDate_ = iborStartDate; @@ -204,8 +210,10 @@ public FuturesRateHelper(double price, maturityDate_ = iborEndDate; } break; + case Futures.Type.Custom: + break; default: - Utils.QL_FAIL("unknown futures type (" + type + ")"); + Utils.QL_FAIL("unsupported futures type (" + type + ")"); break; } earliestDate_ = iborStartDate; @@ -232,8 +240,10 @@ public FuturesRateHelper(Handle price, Utils.QL_REQUIRE(ASX.isASXdate(iborStartDate, false), () => iborStartDate + " is not a valid ASX date"); break; + case Futures.Type.Custom: + break; default: - Utils.QL_FAIL("unknown futures type (" + type + ")"); + Utils.QL_FAIL("unsupported futures type (" + type + ")"); break; } earliestDate_ = iborStartDate; @@ -263,8 +273,10 @@ public FuturesRateHelper(double price, Utils.QL_REQUIRE(ASX.isASXdate(iborStartDate, false), () => iborStartDate + " is not a valid ASX date"); break; + case Futures.Type.Custom: + break; default: - Utils.QL_FAIL("unknown futures type (" + type + ")"); + Utils.QL_FAIL("unsupported futures type (" + type + ")"); break; } earliestDate_ = iborStartDate; @@ -281,11 +293,10 @@ public override double impliedQuote() double forwardRate = (termStructure_.discount(earliestDate_) / termStructure_.discount(maturityDate_) - 1) / yearFraction_; - double convAdj = convAdj_.empty() ? 0 : convAdj_.link.value(); // Convexity, as FRA/futures adjustment, has been used in the // past to take into account futures margining vs FRA. // Therefore, there's no requirement for it to be non-negative. - double futureRate = forwardRate + convAdj; + double futureRate = forwardRate + convexityAdjustment(); return 100.0 * (1.0 - futureRate); }