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 SpreadOptionGreekCalc(double ExpectedPv, string spreadType = "TwoAssetsSpread", string ValuationDate = "2017-12-26")
        {
            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: OptionType.Call,
                spreadType: ToSpreadType(spreadType),
                strike: 1.03,
                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, asset1: asset1, asset2: asset2, asset3: asset3, asset4: asset4);

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

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

            //Assert.AreEqual(ExpectedPv, analyticalResult.Pv, 1e-8);
            Assert.AreEqual(ExpectedPv, Result.Pv, 1e-8);
        }
        private void SpreadOptionGreekTest(double vol1       = 0.28, double vol2 = 0.30, double vol3 = 0.40, double spot1 = 4900, double spot2 = 1500, double spot3 = 1500,
                                           string spreadType = "TwoAssetsSpread", string t0 = "2017-12-26", string t1 = "2017-12-27", double volMove = 0.10, double mktMove = 1e-4, double toleranceInPct = 2)
        {
            var valuationDate = DateFromStr(t0);
            var maturityDate  = new Term("176D").Next(valuationDate);
            var calendar      = CalendarImpl.Get("chn");
            var spot1New      = spot1 + spot1 * mktMove;
            var spot2New      = spot2 + spot2 * mktMove;
            var spot3New      = spot3 + spot3 * mktMove;
            var vol1New       = vol1 + volMove;
            var vol2New       = vol2 + volMove;
            var vol3New       = vol3 + volMove;
            var T0            = DateFromStr(t0);
            var T1            = DateFromStr(t1);


            var valuationDay    = t0;
            var valuationDayNew = t1;

            var asset1 = "asset1";
            var asset2 = "asset2";
            var asset3 = "asset3";
            var asset4 = "asset4";
            var option = new SpreadOption(
                startDate: valuationDate,
                maturityDate: maturityDate,
                exercise: OptionExercise.European,
                optionType: OptionType.Call,
                spreadType: ToSpreadType(spreadType),
                strike: 1000,
                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: t0, vol1: vol1, vol2: vol2, vol3: vol3, spot1: spot1, spot2: spot2, spot3: spot3,
                                    asset1: asset1, asset2: asset2, asset3: asset3, asset4: asset4);
            var marketNew = TestMarket(referenceDate: t1, vol1: vol1New, vol2: vol2New, vol3: vol3New, spot1: spot1New, spot2: spot2New, spot3: spot3New,
                                       asset1: asset1, asset2: asset2, asset3: asset3, asset4: asset4);
            var marketPI = TestMarket(referenceDate: t0, vol1: vol1, vol2: vol2, vol3: vol3, spot1: spot1New, spot2: spot2New, spot3: spot3New,
                                      asset1: asset1, asset2: asset2, asset3: asset3, asset4: asset4);
            var marketVI = TestMarket(referenceDate: t0, vol1: vol1New, vol2: vol2New, vol3: vol3New, spot1: spot1, spot2: spot2, spot3: spot3,
                                      asset1: asset1, asset2: asset2, asset3: asset3, asset4: asset4);
            var marketPVC = TestMarket(referenceDate: t0, vol1: vol1New, vol2: vol2New, vol3: vol3New, spot1: spot1New, spot2: spot2New, spot3: spot3New,
                                       asset1: asset1, asset2: asset2, asset3: asset3, asset4: asset4);

            var engine = new AnalyticalSpreadOptionKirkEngine();

            var result    = engine.Calculate(option, market, PricingRequest.All);
            var resultNew = engine.Calculate(option, marketNew, PricingRequest.All);
            var resultPI  = engine.Calculate(option, marketPI, PricingRequest.All);
            var resultVI  = engine.Calculate(option, marketVI, PricingRequest.All);
            var resultPVC = engine.Calculate(option, marketPVC, PricingRequest.All);

            var actualPL = resultNew.Pv - result.Pv;

            //price Impact
            //PI = PV(t-1, priceNew) - Pv(t-1)
            var basePv  = result.Pv;
            var PI      = resultPI.Pv - basePv;
            var thetapl = result.Theta * (T1 - T0);

            //vol impact
            //VI = PV(t-1. volNew) - Pv (t-1)
            var VI = resultVI.Pv - basePv;

            //price vol cross impact
            //PVC = PV(t-1. volNew, PriceNew) - Pv (t-1) - (PI+VI)
            var PVC = resultPVC.Pv - basePv - PI - VI;

            var newEstimate    = PI + VI + PVC + thetapl;
            var newUnexplained = actualPL - newEstimate;

            //Time impact
            //TI = PV(t, all OldInfo) - Pv(t-1)

            //TODO:
            //Time/ price cross Impact
            //TPC = PV(t, priceNew) - pv(t-1) - (TI +PI)

            //Time/vol cross impact
            //TVC = PV(t, volNew) - pv(t-1) -(TI+VI)


            //TODO:
            //in case of big move ( vol and spot), we need high order risk to explain pnl
            //var diff = actualPL - esimstatedPL;
            //Assert.AreEqual(true, Math.Abs(diff / actualPL) * 100.0 < toleranceInPct); //pnl well explained in not too extreme moves
            Assert.AreEqual(true, Math.Abs(newUnexplained / actualPL) * 100.0 < toleranceInPct);
        }
        private void SpreadVanillaCalc(string spreadType = "TwoAssetsSpread", string ValuationDate = "2017-12-26",
                                       double spot1      = 4500, double spot2 = 2500, double strike = 2000, double vol1 = 0.5, double vol2 = 0.5, double rho12 = 0.5)
        {
            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: OptionType.Call,
                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 option1 = new VanillaOption(
                valuationDate,
                maturityDate,
                OptionExercise.European,
                OptionType.Call,
                2.0 * strike,
                InstrumentType.EquityIndex,
                calendar,
                new Act365(),
                CurrencyCode.CNY,
                CurrencyCode.CNY,
                new[] { maturityDate },
                new[] { maturityDate }
                );

            var option2 = new VanillaOption(
                valuationDate,
                maturityDate,
                OptionExercise.European,
                OptionType.Put,
                strike,
                InstrumentType.EquityIndex,
                calendar,
                new Act365(),
                CurrencyCode.CNY,
                CurrencyCode.CNY,
                new[] { maturityDate },
                new[] { maturityDate }
                );

            var market = TestMarket(referenceDate: ValuationDate, vol1: vol1, vol2: vol2, vol3: 0.0, spot1: spot1, spot2: spot2, spot3: 0.0,
                                    asset1: asset1, asset2: asset2, asset3: asset3, asset4: asset4, rho12: rho12, rho23: 0.0, rho13: 0.0);
            var market1 = GetMarket(referenceDate: ValuationDate, vol: vol1, spot: spot1);
            var market2 = GetMarket(referenceDate: ValuationDate, vol: vol2, spot: spot2);

            var analyticalEngine  = new AnalyticalSpreadOptionKirkEngine();
            var analyticalEngine2 = new AnalyticalVanillaEuropeanOptionEngine();
            var result            = analyticalEngine.Calculate(option, market, PricingRequest.All);
            var result1           = analyticalEngine2.Calculate(option1, market1, PricingRequest.All);
            var result2           = analyticalEngine2.Calculate(option2, market2, PricingRequest.All);

            Assert.AreEqual(true, result1.Pv + result2.Pv - result.Pv > 0);
        }
        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);
        }