Files
quantconnect--lean/Tests/Common/Securities/FutureOptionMarginBuyingPowerModelTests.cs
2026-07-13 13:02:50 +08:00

440 lines
24 KiB
C#

/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using NUnit.Framework;
using QuantConnect.Data;
using QuantConnect.Orders;
using QuantConnect.Logging;
using QuantConnect.Securities;
using QuantConnect.Data.Market;
using QuantConnect.Tests.Engine;
using QuantConnect.Securities.Future;
using QuantConnect.Securities.Option;
using QuantConnect.Tests.Engine.DataFeeds;
using QuantConnect.Brokerages.Backtesting;
using QuantConnect.Lean.Engine.TransactionHandlers;
namespace QuantConnect.Tests.Common.Securities
{
[TestFixture, Parallelizable(ParallelScope.All)]
public class FutureOptionMarginBuyingPowerModelTests
{
[Test]
public void MarginWithNoFutureOptionHoldings()
{
const decimal price = 2300m;
var time = new DateTime(2020, 10, 14);
var expDate = new DateTime(2021, 3, 19);
var tz = TimeZones.NewYork;
// For this symbol we dont have any history, but only one date and margins line
var ticker = QuantConnect.Securities.Futures.Indices.SP500EMini;
var future = Symbol.CreateFuture(ticker, Market.CME, expDate);
var symbol = Symbol.CreateOption(future, Market.CME, OptionStyle.American, OptionRight.Call, 2550m, new DateTime(2021, 3, 19));
var optionSecurity = new Option(
SecurityExchangeHours.AlwaysOpen(tz),
new SubscriptionDataConfig(typeof(TradeBar), symbol, Resolution.Minute, tz, tz, true, false, false),
new Cash(Currencies.USD, 0, 1m),
new OptionSymbolProperties(SymbolProperties.GetDefault(Currencies.USD)),
ErrorCurrencyConverter.Instance,
RegisteredSecurityDataTypesProvider.Null
);
optionSecurity.Underlying = new Future(
SecurityExchangeHours.AlwaysOpen(tz),
new SubscriptionDataConfig(typeof(TradeBar), future, Resolution.Minute, tz, tz, true, false, false),
new Cash(Currencies.USD, 0, 1m),
new OptionSymbolProperties(SymbolProperties.GetDefault(Currencies.USD)),
ErrorCurrencyConverter.Instance,
RegisteredSecurityDataTypesProvider.Null
);
optionSecurity.Underlying.SetMarketPrice(new Tick { Value = price, Time = time });
optionSecurity.Underlying.Holdings.SetHoldings(1.5m, 1);
var futureBuyingPowerModel = new FutureMarginModel(security: optionSecurity.Underlying);
var futureOptionBuyingPowerModel = new FuturesOptionsMarginModel(futureOption: optionSecurity);
// we don't hold FOPs!
Assert.AreEqual(0m, futureOptionBuyingPowerModel.GetMaintenanceMargin(optionSecurity));
Assert.AreNotEqual(0m, futureBuyingPowerModel.GetMaintenanceMargin(optionSecurity.Underlying));
Assert.AreNotEqual(0m, futureOptionBuyingPowerModel.GetInitialMarginRequirement(optionSecurity, 10));
}
[Test]
public void MarginWithFutureAndFutureOptionHoldings()
{
const decimal price = 2300m;
var time = new DateTime(2020, 10, 14);
var expDate = new DateTime(2021, 3, 19);
var tz = TimeZones.NewYork;
// For this symbol we dont have any history, but only one date and margins line
var ticker = QuantConnect.Securities.Futures.Indices.SP500EMini;
var future = Symbol.CreateFuture(ticker, Market.CME, expDate);
var symbol = Symbol.CreateOption(future, Market.CME, OptionStyle.American, OptionRight.Call, 2550m,
new DateTime(2021, 3, 19));
var optionSecurity = new Option(
SecurityExchangeHours.AlwaysOpen(tz),
new SubscriptionDataConfig(typeof(TradeBar), symbol, Resolution.Minute, tz, tz, true, false, false),
new Cash(Currencies.USD, 0, 1m),
new OptionSymbolProperties(SymbolProperties.GetDefault(Currencies.USD)),
ErrorCurrencyConverter.Instance,
RegisteredSecurityDataTypesProvider.Null
);
optionSecurity.Underlying = new Future(
SecurityExchangeHours.AlwaysOpen(tz),
new SubscriptionDataConfig(typeof(TradeBar), future, Resolution.Minute, tz, tz, true, false, false),
new Cash(Currencies.USD, 0, 1m),
new OptionSymbolProperties(SymbolProperties.GetDefault(Currencies.USD)),
ErrorCurrencyConverter.Instance,
RegisteredSecurityDataTypesProvider.Null
);
optionSecurity.Underlying.SetMarketPrice(new Tick {Value = price, Time = time});
optionSecurity.Holdings.SetHoldings(1.5m, 1);
optionSecurity.Underlying.Holdings.SetHoldings(1.5m, 1);
var futureOptionBuyingPowerModel = new FuturesOptionsMarginModel(futureOption: optionSecurity);
Assert.AreNotEqual(0m, futureOptionBuyingPowerModel.GetMaintenanceMargin(optionSecurity));
}
[Test]
public void MarginWithFutureOptionHoldings()
{
const decimal price = 2300m;
var time = new DateTime(2020, 10, 14);
var expDate = new DateTime(2021, 3, 19);
var tz = TimeZones.NewYork;
// For this symbol we dont have any history, but only one date and margins line
var ticker = QuantConnect.Securities.Futures.Indices.SP500EMini;
var future = Symbol.CreateFuture(ticker, Market.CME, expDate);
var symbol = Symbol.CreateOption(future, Market.CME, OptionStyle.American, OptionRight.Call, 2550m,
new DateTime(2021, 3, 19));
var optionSecurity = new Option(
SecurityExchangeHours.AlwaysOpen(tz),
new SubscriptionDataConfig(typeof(TradeBar), symbol, Resolution.Minute, tz, tz, true, false, false),
new Cash(Currencies.USD, 0, 1m),
new OptionSymbolProperties(SymbolProperties.GetDefault(Currencies.USD)),
ErrorCurrencyConverter.Instance,
RegisteredSecurityDataTypesProvider.Null
);
optionSecurity.Underlying = new Future(
SecurityExchangeHours.AlwaysOpen(tz),
new SubscriptionDataConfig(typeof(TradeBar), future, Resolution.Minute, tz, tz, true, false, false),
new Cash(Currencies.USD, 0, 1m),
new OptionSymbolProperties(SymbolProperties.GetDefault(Currencies.USD)),
ErrorCurrencyConverter.Instance,
RegisteredSecurityDataTypesProvider.Null
);
optionSecurity.Underlying.SetMarketPrice(new Tick { Value = price, Time = time });
optionSecurity.Holdings.SetHoldings(1.5m, 1);
var futureBuyingPowerModel = new FutureMarginModel(security: optionSecurity.Underlying);
var futureOptionBuyingPowerModel = new FuturesOptionsMarginModel(futureOption: optionSecurity);
Assert.AreNotEqual(0m, futureOptionBuyingPowerModel.GetMaintenanceMargin(optionSecurity));
Assert.AreEqual(0, futureBuyingPowerModel.GetMaintenanceMargin(optionSecurity.Underlying));
}
[Test]
public void OptionExerciseWhenFullyInvested()
{
var algorithm = new AlgorithmStub();
algorithm.SetFinishedWarmingUp();
var backtestingTransactionHandler = new BacktestingTransactionHandler();
using var brokerage = new BacktestingBrokerage(algorithm);
algorithm.Transactions.SetOrderProcessor(backtestingTransactionHandler);
backtestingTransactionHandler.Initialize(algorithm, brokerage, new TestResultHandler());
try
{
const decimal price = 2600m;
var time = new DateTime(2020, 10, 14);
var expDate = new DateTime(2021, 3, 19);
// For this symbol we dont have any history, but only one date and margins line
var ticker = QuantConnect.Securities.Futures.Indices.SP500EMini;
var future = Symbol.CreateFuture(ticker, Market.CME, expDate);
var symbol = Symbol.CreateOption(future, Market.CME, OptionStyle.American, OptionRight.Call, 2550m,
new DateTime(2021, 3, 19));
var optionSecurity = algorithm.AddOptionContract(symbol);
optionSecurity.Underlying = algorithm.AddFutureContract(future);
optionSecurity.Underlying.SetMarketPrice(new Tick { Value = price, Time = time });
optionSecurity.SetMarketPrice(new Tick { Value = 150, Time = time });
optionSecurity.Holdings.SetHoldings(1.5m, 10);
algorithm.SetDateTime(time.AddHours(14)); // 10am
var ticket = algorithm.ExerciseOption(optionSecurity.Symbol, 10, true);
// Process orders
backtestingTransactionHandler.ProcessSynchronousEvents();
Assert.AreEqual(OrderStatus.Filled, ticket.Status);
}
finally
{
backtestingTransactionHandler.Exit();
}
}
[Test]
public void MarginRequirementsAreSetCorrectly()
{
var expDate = new DateTime(2021, 3, 19);
var tz = TimeZones.NewYork;
// For this symbol we dont have any history, but only one date and margins line
var ticker = QuantConnect.Securities.Futures.Indices.SP500EMini;
var future = Symbol.CreateFuture(ticker, Market.CME, expDate);
var symbol = Symbol.CreateOption(future, Market.CME, OptionStyle.American, OptionRight.Call, 2550m,
new DateTime(2021, 3, 19));
var futureSecurity = new Future(
SecurityExchangeHours.AlwaysOpen(tz),
new SubscriptionDataConfig(typeof(TradeBar), future, Resolution.Minute, tz, tz, true, false, false),
new Cash(Currencies.USD, 0, 1m),
new OptionSymbolProperties(SymbolProperties.GetDefault(Currencies.USD)),
ErrorCurrencyConverter.Instance,
RegisteredSecurityDataTypesProvider.Null
);
var optionSecurity = new QuantConnect.Securities.FutureOption.FutureOption(symbol,
SecurityExchangeHours.AlwaysOpen(tz),
new Cash(Currencies.USD, 0, 1m),
new OptionSymbolProperties(SymbolProperties.GetDefault(Currencies.USD)),
ErrorCurrencyConverter.Instance,
RegisteredSecurityDataTypesProvider.Null,
new SecurityCache(),
futureSecurity
);
var futureMarginModel = new FuturesOptionsMarginModel(futureOption: optionSecurity);
optionSecurity.Underlying.SetMarketPrice(new Tick { Value = 1500, Time = new DateTime(2001, 01, 07) });
var initialIntradayMarginRequirement = futureMarginModel.InitialIntradayMarginRequirement;
var maintenanceIntradayMarginRequirement = futureMarginModel.MaintenanceIntradayMarginRequirement;
var initialOvernightMarginRequirement = futureMarginModel.MaintenanceOvernightMarginRequirement;
var maintenanceOvernightMarginRequirement = futureMarginModel.InitialOvernightMarginRequirement;
Assert.AreNotEqual(0, initialIntradayMarginRequirement);
Assert.AreNotEqual(0, maintenanceIntradayMarginRequirement);
Assert.AreNotEqual(0, initialOvernightMarginRequirement);
Assert.AreNotEqual(0, maintenanceOvernightMarginRequirement);
}
// Long Call initial
[TestCase(10, 70000, OptionRight.Call, PositionSide.Long, 59375)]
[TestCase(23.5, 69000, OptionRight.Call, PositionSide.Long, 59375)]
[TestCase(30.5, 68000, OptionRight.Call, PositionSide.Long, 59375)]
[TestCase(55, 50000, OptionRight.Call, PositionSide.Long, 59375)]
[TestCase(66, 30000, OptionRight.Call, PositionSide.Long, 59375)]
[TestCase(72, 17000, OptionRight.Call, PositionSide.Long, 59375)]
[TestCase(87, 3700, OptionRight.Call, PositionSide.Long, 59375)]
[TestCase(108.5, 1000, OptionRight.Call, PositionSide.Long, 59375)]
[TestCase(125, 570, OptionRight.Call, PositionSide.Long, 59375)]
[TestCase(1000, 0, OptionRight.Call, PositionSide.Long, 59375)]
// Long Call maintenance
[TestCase(10, 56000, OptionRight.Call, PositionSide.Long, 47500)]
[TestCase(23.5, 55000, OptionRight.Call, PositionSide.Long, 47500)]
[TestCase(30.5, 54000, OptionRight.Call, PositionSide.Long, 47500)]
[TestCase(55, 40000, OptionRight.Call, PositionSide.Long, 47500)]
[TestCase(66, 24000, OptionRight.Call, PositionSide.Long, 47500)]
[TestCase(72, 14000, OptionRight.Call, PositionSide.Long, 47500)]
[TestCase(87, 3600, OptionRight.Call, PositionSide.Long, 47500)]
[TestCase(108.5, 1000, OptionRight.Call, PositionSide.Long, 47500)]
[TestCase(125, 540, OptionRight.Call, PositionSide.Long, 47500)]
[TestCase(1000, 0, OptionRight.Call, PositionSide.Long, 47500)]
// Short Call initial
[TestCase(10, 59400, OptionRight.Call, PositionSide.Short, 59375)]
[TestCase(23.5, 59680, OptionRight.Call, PositionSide.Short, 59375)]
[TestCase(30.5, 59750, OptionRight.Call, PositionSide.Short, 59375)]
[TestCase(55, 56712, OptionRight.Call, PositionSide.Short, 59375)]
[TestCase(66, 48134, OptionRight.Call, PositionSide.Short, 59375)]
[TestCase(72, 43492, OptionRight.Call, PositionSide.Short, 59375)]
[TestCase(87, 28960, OptionRight.Call, PositionSide.Short, 59375)]
[TestCase(108.5, 11373, OptionRight.Call, PositionSide.Short, 59375)]
[TestCase(125, 3900, OptionRight.Call, PositionSide.Short, 59375)]
[TestCase(1000, 0, OptionRight.Call, PositionSide.Short, 59375)]
// Long Put initial
[TestCase(10, 45, OptionRight.Put, PositionSide.Long, 59375)]
[TestCase(18, 171, OptionRight.Put, PositionSide.Long, 59375)]
[TestCase(26.5, 537, OptionRight.Put, PositionSide.Long, 59375)]
[TestCase(37.5, 1920, OptionRight.Put, PositionSide.Long, 59375)]
[TestCase(47.5, 6653, OptionRight.Put, PositionSide.Long, 59375)]
[TestCase(69.5, 48637, OptionRight.Put, PositionSide.Long, 59375)]
[TestCase(83, 59201, OptionRight.Put, PositionSide.Long, 59375)]
[TestCase(108, 60000, OptionRight.Put, PositionSide.Long, 59375)]
[TestCase(152, 59475, OptionRight.Put, PositionSide.Long, 59375)]
// Long Put maintenance
[TestCase(10, 45, OptionRight.Put, PositionSide.Long, 47500)]
[TestCase(18, 171, OptionRight.Put, PositionSide.Long, 47500)]
[TestCase(26.5, 537, OptionRight.Put, PositionSide.Long, 47500)]
[TestCase(37.5, 1920, OptionRight.Put, PositionSide.Long, 47500)]
[TestCase(47.5, 6653, OptionRight.Put, PositionSide.Long, 47500)]
[TestCase(69.5, 38910, OptionRight.Put, PositionSide.Long, 47500)]
[TestCase(83, 47361, OptionRight.Put, PositionSide.Long, 47500)]
[TestCase(108, 48000, OptionRight.Put, PositionSide.Long, 47500)]
[TestCase(152, 47580, OptionRight.Put, PositionSide.Long, 47500)]
// Short Put initial
[TestCase(10, 23729, OptionRight.Put, PositionSide.Short, 59375)]
[TestCase(18, 33859, OptionRight.Put, PositionSide.Short, 59375)]
[TestCase(26.5, 40000, OptionRight.Put, PositionSide.Short, 59375)]
[TestCase(37.5, 52714, OptionRight.Put, PositionSide.Short, 59375)]
[TestCase(47.5, 58414, OptionRight.Put, PositionSide.Short, 59375)]
[TestCase(69.5, 72647, OptionRight.Put, PositionSide.Short, 59375)]
[TestCase(83, 73160, OptionRight.Put, PositionSide.Short, 59375)]
[TestCase(108, 71782, OptionRight.Put, PositionSide.Short, 59375)]
[TestCase(152, 70637, OptionRight.Put, PositionSide.Short, 59375)]
public void MarginRequirementCrudeOil(decimal strike, double expected, OptionRight optionRight, PositionSide positionSide, decimal underlyingRequirement)
{
var tz = TimeZones.NewYork;
var expDate = new DateTime(2021, 3, 19);
// For this symbol we dont have any history, but only one date and margins line
var ticker = QuantConnect.Securities.Futures.Energy.CrudeOilWTI;
var future = Symbol.CreateFuture(ticker, Market.NYMEX, expDate);
var symbol = Symbol.CreateOption(future, Market.NYMEX, OptionStyle.American, optionRight, strike,
new DateTime(2021, 3, 19));
var futureSecurity = new Future(
SecurityExchangeHours.AlwaysOpen(tz),
new SubscriptionDataConfig(typeof(TradeBar), future, Resolution.Minute, tz, tz, true, false, false),
new Cash(Currencies.USD, 0, 1m),
new OptionSymbolProperties(SymbolProperties.GetDefault(Currencies.USD)),
ErrorCurrencyConverter.Instance,
RegisteredSecurityDataTypesProvider.Null
);
var optionSecurity = new QuantConnect.Securities.FutureOption.FutureOption(symbol,
SecurityExchangeHours.AlwaysOpen(tz),
new Cash(Currencies.USD, 0, 1m),
new OptionSymbolProperties(SymbolProperties.GetDefault(Currencies.USD)),
ErrorCurrencyConverter.Instance,
RegisteredSecurityDataTypesProvider.Null,
new SecurityCache(),
futureSecurity
);
optionSecurity.Underlying.SetMarketPrice(new Tick { Value = 60, Time = new DateTime(2001, 01, 07) });
var marginRequirement = FuturesOptionsMarginModel.GetMarginRequirement(optionSecurity, underlyingRequirement, positionSide);
Log.Debug($"Side {positionSide}. Right {optionRight}. Strike {strike}. Margin: {marginRequirement}");
Assert.AreEqual(expected, marginRequirement, (double)underlyingRequirement * 0.30d);
}
// Long Call initial
[TestCase(1300, 154000, OptionRight.Call, PositionSide.Long, 112729)]
[TestCase(1755, 97000, OptionRight.Call, PositionSide.Long, 112729)]
[TestCase(1805, 84000, OptionRight.Call, PositionSide.Long, 112729)]
[TestCase(1900, 55000, OptionRight.Call, PositionSide.Long, 112729)]
[TestCase(2040, 24000, OptionRight.Call, PositionSide.Long, 112729)]
[TestCase(2100, 16000, OptionRight.Call, PositionSide.Long, 112729)]
[TestCase(2295, 5000, OptionRight.Call, PositionSide.Long, 112729)]
[TestCase(3000, 740, OptionRight.Call, PositionSide.Long, 112729)]
[TestCase(4000, 180, OptionRight.Call, PositionSide.Long, 112729)]
public void MarginRequirementGold(decimal strike, double expected, OptionRight optionRight, PositionSide positionSide, decimal underlyingRequirement)
{
var tz = TimeZones.NewYork;
var expDate = new DateTime(2021, 3, 19);
// For this symbol we dont have any history, but only one date and margins line
var ticker = QuantConnect.Securities.Futures.Metals.Gold;
var future = Symbol.CreateFuture(ticker, Market.COMEX, expDate);
var symbol = Symbol.CreateOption(future, Market.COMEX, OptionStyle.American, optionRight, strike,
new DateTime(2021, 3, 19));
var futureSecurity = new Future(
SecurityExchangeHours.AlwaysOpen(tz),
new SubscriptionDataConfig(typeof(TradeBar), future, Resolution.Minute, tz, tz, true, false, false),
new Cash(Currencies.USD, 0, 1m),
new OptionSymbolProperties(SymbolProperties.GetDefault(Currencies.USD)),
ErrorCurrencyConverter.Instance,
RegisteredSecurityDataTypesProvider.Null
);
var optionSecurity = new QuantConnect.Securities.FutureOption.FutureOption(symbol,
SecurityExchangeHours.AlwaysOpen(tz),
new Cash(Currencies.USD, 0, 1m),
new OptionSymbolProperties(SymbolProperties.GetDefault(Currencies.USD)),
ErrorCurrencyConverter.Instance,
RegisteredSecurityDataTypesProvider.Null,
new SecurityCache(),
futureSecurity
);
optionSecurity.Underlying.SetMarketPrice(new Tick { Value = 1887, Time = new DateTime(2001, 01, 07) });
var marginRequirement = FuturesOptionsMarginModel.GetMarginRequirement(optionSecurity, underlyingRequirement, positionSide);
Log.Debug($"Side {positionSide}. Right {optionRight}. Strike {strike}. Margin: {marginRequirement}");
Assert.AreEqual(expected, marginRequirement, (double)underlyingRequirement * 0.30d);
}
// Long Call initial
[TestCase(2200, 16456, OptionRight.Call, PositionSide.Long, 15632)]
[TestCase(3200, 15582, OptionRight.Call, PositionSide.Long, 15632)]
[TestCase(3500, 14775, OptionRight.Call, PositionSide.Long, 15632)]
[TestCase(3570, 14310, OptionRight.Call, PositionSide.Long, 15632)]
[TestCase(4190, 7128, OptionRight.Call, PositionSide.Long, 15632)]
[TestCase(4370, 4089, OptionRight.Call, PositionSide.Long, 15632)]
[TestCase(4900, 233, OptionRight.Call, PositionSide.Long, 15632)]
// Short Call initial
[TestCase(2200, 17069, OptionRight.Call, PositionSide.Short, 15632)]
[TestCase(3200, 16716, OptionRight.Call, PositionSide.Short, 15632)]
[TestCase(3500, 16409, OptionRight.Call, PositionSide.Short, 15632)]
[TestCase(3570, 16222, OptionRight.Call, PositionSide.Short, 15632)]
[TestCase(4190, 14429, OptionRight.Call, PositionSide.Short, 15632)]
[TestCase(4370, 13003, OptionRight.Call, PositionSide.Short, 15632)]
[TestCase(4900, 6528, OptionRight.Call, PositionSide.Short, 15632)]
public void MarginRequirementEs(decimal strike, double expected, OptionRight optionRight, PositionSide positionSide, decimal underlyingRequirement)
{
var tz = TimeZones.NewYork;
var expDate = new DateTime(2021, 3, 19);
// For this symbol we dont have any history, but only one date and margins line
var ticker = QuantConnect.Securities.Futures.Indices.SP500EMini;
var future = Symbol.CreateFuture(ticker, Market.Globex, expDate);
var symbol = Symbol.CreateOption(future, Market.Globex, OptionStyle.American, optionRight, strike,
new DateTime(2021, 3, 19));
var futureSecurity = new Future(
SecurityExchangeHours.AlwaysOpen(tz),
new SubscriptionDataConfig(typeof(TradeBar), future, Resolution.Minute, tz, tz, true, false, false),
new Cash(Currencies.USD, 0, 1m),
new OptionSymbolProperties(SymbolProperties.GetDefault(Currencies.USD)),
ErrorCurrencyConverter.Instance,
RegisteredSecurityDataTypesProvider.Null
);
var optionSecurity = new QuantConnect.Securities.FutureOption.FutureOption(symbol,
SecurityExchangeHours.AlwaysOpen(tz),
new Cash(Currencies.USD, 0, 1m),
new OptionSymbolProperties(SymbolProperties.GetDefault(Currencies.USD)),
ErrorCurrencyConverter.Instance,
RegisteredSecurityDataTypesProvider.Null,
new SecurityCache(),
futureSecurity
);
optionSecurity.Underlying.SetMarketPrice(new Tick { Value = 4172, Time = new DateTime(2001, 01, 07) });
var marginRequirement = FuturesOptionsMarginModel.GetMarginRequirement(optionSecurity, underlyingRequirement, positionSide);
Log.Debug($"Side {positionSide}. Right {optionRight}. Strike {strike}. Margin: {marginRequirement}");
Assert.AreEqual(expected, marginRequirement, (double)underlyingRequirement * 0.30d);
}
}
}