private void SpreadOptionBjerksundCalc(string spreadType = "TwoAssetsSpread", Boolean isCall = true, string ValuationDate = "2017-12-26", double vol1 = 0.28, double spot1 = 1.0, double vol2 = 0.30, double spot2 = 2.0,
                                               double vol3       = 0.28, double spot3 = 1.0, double vol4 = 0.28, double spot4 = 1.0, double strike = 1.03, double tol = 1e-8)
        {
            var valuationDate = DateFromStr(ValuationDate);
            var maturityDate  = new Term("176D").Next(valuationDate);
            var calendar      = CalendarImpl.Get("chn");

            var asset1 = "asset1";
            var asset2 = "asset2";
            var asset3 = "asset3";
            var asset4 = "asset4";


            var option = new SpreadOption(
                startDate: valuationDate,
                maturityDate: maturityDate,
                exercise: OptionExercise.European,
                optionType: (isCall) ? OptionType.Call : OptionType.Put,
                spreadType: ToSpreadType(spreadType),
                strike: strike,
                weights: new double[] { 1.0, 1.0, 1.0, 1.0 },
                underlyingInstrumentType: InstrumentType.EquityIndex,
                calendar: calendar,
                dayCount: new Act365(),
                payoffCcy: CurrencyCode.CNY,
                settlementCcy: CurrencyCode.CNY,
                exerciseDates: new[] { maturityDate },
                observationDates: new[] { maturityDate },
                underlyingTickers: new[] { asset1, asset2, asset3, asset4 }
                )
            {
                UnderlyingTickers = new string[] { asset1, asset2, asset3, asset4 }
            };


            var market = TestMarket(referenceDate: ValuationDate, vol1: vol1, vol2: vol2, vol3: vol3, vol4: vol4, spot1: spot1, spot2: spot2, spot3: spot3, spot4: spot4,
                                    asset1: asset1, asset2: asset2, asset3: asset3, asset4: asset4);

            var analyticalEngine = new AnalyticalSpreadOptionBjerksundEngine();
            var analyticalResult = analyticalEngine.Calculate(option, market, PricingRequest.All);

            var Engine = new AnalyticalSpreadOptionKirkEngine();
            var Result = Engine.Calculate(option, market, PricingRequest.All);

            Assert.AreEqual(analyticalResult.Pv, Result.Pv, tol);
        }
        private void SpreadOptionParity(string ValuationDate = "2017-12-26", double vol1 = 0.28, double spot1 = 1.0, double vol2 = 0.30, double spot2 = 2.0, double vol3 = 0.30, double spot3 = 2.0, double strike = 1.03)
        {
            var valuationDate = DateFromStr(ValuationDate);
            var maturityDate  = new Term("176D").Next(valuationDate);
            var calendar      = CalendarImpl.Get("chn");

            var asset1 = "asset1";
            var asset2 = "asset2";
            var asset3 = "asset3";
            var asset4 = "asset4";

            var option1 = new SpreadOption(
                startDate: valuationDate,
                maturityDate: maturityDate,
                exercise: OptionExercise.European,
                optionType: OptionType.Call,
                spreadType: SpreadType.ThreeAssetsSpread,
                strike: strike,
                weights: new double[] { 1.0, 1.0, 1.0, 1.0 },
                underlyingInstrumentType: InstrumentType.EquityIndex,
                calendar: calendar,
                dayCount: new Act365(),
                payoffCcy: CurrencyCode.CNY,
                settlementCcy: CurrencyCode.CNY,
                exerciseDates: new[] { maturityDate },
                observationDates: new[] { maturityDate },
                underlyingTickers: new[] { asset1, asset2, asset3, asset4 }
                )
            {
                UnderlyingTickers = new string[] { asset1, asset2, asset3, asset4 }
            };


            var option2 = new SpreadOption(
                startDate: valuationDate,
                maturityDate: maturityDate,
                exercise: OptionExercise.European,
                optionType: OptionType.Call,
                spreadType: SpreadType.FourAssetsSpread,
                strike: strike,
                weights: new double[] { 1.0, 1.0, 1.0, 1.0 },
                underlyingInstrumentType: InstrumentType.EquityIndex,
                calendar: calendar,
                dayCount: new Act365(),
                payoffCcy: CurrencyCode.CNY,
                settlementCcy: CurrencyCode.CNY,
                exerciseDates: new[] { maturityDate },
                observationDates: new[] { maturityDate },
                underlyingTickers: new[] { asset1, asset2, asset3, asset4 }
                )
            {
                UnderlyingTickers = new string[] { asset1, asset2, asset3, asset4 }
            };

            var option3 = new SpreadOption(
                startDate: valuationDate,
                maturityDate: maturityDate,
                exercise: OptionExercise.European,
                optionType: OptionType.Call,
                spreadType: SpreadType.ThreeAssetsSpreadBasket,
                strike: strike,
                weights: new double[] { 1.0, 1.0, 1.0, 1.0 },
                underlyingInstrumentType: InstrumentType.EquityIndex,
                calendar: calendar,
                dayCount: new Act365(),
                payoffCcy: CurrencyCode.CNY,
                settlementCcy: CurrencyCode.CNY,
                exerciseDates: new[] { maturityDate },
                observationDates: new[] { maturityDate },
                underlyingTickers: new[] { asset1, asset2, asset3, asset4 }
                )
            {
                UnderlyingTickers = new string[] { asset1, asset2, asset3, asset4 }
            };


            var option4 = new SpreadOption(
                startDate: valuationDate,
                maturityDate: maturityDate,
                exercise: OptionExercise.European,
                optionType: OptionType.Call,
                spreadType: SpreadType.FourAssetsSpreadBasketType1,
                strike: strike,
                weights: new double[] { 1.0, 1.0, 1.0, 1.0 },
                underlyingInstrumentType: InstrumentType.EquityIndex,
                calendar: calendar,
                dayCount: new Act365(),
                payoffCcy: CurrencyCode.CNY,
                settlementCcy: CurrencyCode.CNY,
                exerciseDates: new[] { maturityDate },
                observationDates: new[] { maturityDate },
                underlyingTickers: new[] { asset1, asset2, asset3, asset4 }
                )
            {
                UnderlyingTickers = new string[] { asset1, asset2, asset3, asset4 }
            };

            var market = TestMarket(referenceDate: ValuationDate, vol1: vol1, vol2: vol2, vol3: vol3, spot1: spot1, spot2: spot2, spot3: spot3,
                                    asset1: asset1, asset2: asset2, asset3: asset3, asset4: asset4);

            var analyticalEngine = new AnalyticalSpreadOptionKirkEngine();
            var result1          = analyticalEngine.Calculate(option1, market, PricingRequest.All);
            var result2          = analyticalEngine.Calculate(option2, market, PricingRequest.All);
            var result3          = analyticalEngine.Calculate(option3, market, PricingRequest.All);
            var result4          = analyticalEngine.Calculate(option4, market, PricingRequest.All);

            var Engine  = new AnalyticalSpreadOptionBjerksundEngine();
            var result5 = Engine.Calculate(option1, market, PricingRequest.All);
            var result6 = Engine.Calculate(option2, market, PricingRequest.All);
            var result7 = Engine.Calculate(option3, market, PricingRequest.All);
            var result8 = Engine.Calculate(option4, market, PricingRequest.All);

            Assert.AreEqual(result1.Pv, result2.Pv, 1.0e-8);
            Assert.AreEqual(result3.Pv, result4.Pv, 1.0e-8);
            //Assert.AreEqual(result5.Pv, result6.Pv, 1.0e-8);
            Assert.AreEqual(result7.Pv, result8.Pv, 1.0e-8);
        }