private void ResetStrikeOptionParityCalc(String ValuationDate = "2017-12-20", Double vol = 0.28, Double spot = 2.0, Double strike = 1.03, Boolean isCall = true, Boolean isPercentage = true)
        {
            var valuationDate    = DateFromStr(ValuationDate);
            var strikefixingDate = valuationDate;
            var exerciseDate     = new Term("176D").Next(valuationDate);
            var calendar         = CalendarImpl.Get("chn");


            var option1 = new ResetStrikeOption(
                valuationDate,
                exerciseDate,
                OptionExercise.European,
                isCall ? OptionType.Call : OptionType.Put,
                isPercentage ? ResetStrikeType.PercentagePayoff : ResetStrikeType.NormalPayoff,
                strike,
                InstrumentType.EquityIndex,
                calendar,
                new Act365(),
                CurrencyCode.CNY,
                CurrencyCode.CNY,
                new[] { exerciseDate },
                new[] { exerciseDate },
                strikefixingDate
                );

            var option2 = new VanillaOption(
                valuationDate,
                exerciseDate,
                OptionExercise.European,
                isCall ? OptionType.Call : OptionType.Put,
                strike,
                InstrumentType.EquityIndex,
                calendar,
                new Act365(),
                CurrencyCode.CNY,
                CurrencyCode.CNY,
                new[] { exerciseDate },
                new[] { exerciseDate }
                );
            var market = TestMarket(referenceDate: ValuationDate, vol: vol, spot: spot);

            var engine        = new AnalyticalResetStrikeOptionEngine();
            var result        = engine.Calculate(option1, market, PricingRequest.All);
            var engineVanilla = new AnalyticalVanillaEuropeanOptionEngine();
            var resultVanilla = engineVanilla.Calculate(option2, market, PricingRequest.All);

            var result1 = (isPercentage) ? result.Pv * strike : result.Pv;

            Assert.AreEqual(result1, resultVanilla.Pv, 1e-8);
        }
        private void ResetStrikeOptionGreekCalc(double expectedPv, double expectedDelta, double expectedGamma, double expectedVega, double expectedTheta, double expectedRho,
                                                String ValuationDate = "2017-12-20", Double vol = 0.28, Double spot = 1.0, Double strike = 1.03, Boolean isCall = true, Boolean isPercentage = true)
        {
            var valuationDate    = DateFromStr(ValuationDate);
            var strikefixingDate = new Term("50D").Next(valuationDate);
            var exerciseDate     = new Term("176D").Next(valuationDate);
            var calendar         = CalendarImpl.Get("chn");


            var option = new ResetStrikeOption(
                valuationDate,
                exerciseDate,
                OptionExercise.European,
                isCall ? OptionType.Call : OptionType.Put,
                isPercentage ? ResetStrikeType.PercentagePayoff : ResetStrikeType.NormalPayoff,
                strike,
                InstrumentType.EquityIndex,
                calendar,
                new Act365(),
                CurrencyCode.CNY,
                CurrencyCode.CNY,
                new[] { exerciseDate },
                new[] { exerciseDate },
                strikefixingDate
                );
            var market = TestMarket(referenceDate: ValuationDate, vol: vol, spot: spot);

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

            Assert.AreEqual(expectedPv, analyticalResult.Pv, 1e-8);
            Assert.AreEqual(expectedDelta, analyticalResult.Delta, 1e-8);
            Assert.AreEqual(expectedGamma, analyticalResult.Gamma, 1e-8);
            Assert.AreEqual(expectedVega, analyticalResult.Vega, 1e-8);
            Assert.AreEqual(expectedTheta, analyticalResult.Theta, 1e-8);
            Assert.AreEqual(expectedRho, analyticalResult.Rho, 1e-8);
        }
        private void ResetStrikeOptionPnLCalc(double vol = 0.28, double spot       = 1.0, double strike = 1.03, Boolean isCall = true, Boolean isPercentage = true,
                                              string t0  = "2017-12-20", string t1 = "2017-12-21", double volMove = 0.10, double mktMove = 1e-4, double toleranceInPct = 2)
        {
            var T0      = DateFromStr(t0);
            var T1      = DateFromStr(t1);
            var spotNew = spot + spot * mktMove;
            var volNew  = vol + volMove;

            var strikefixingDate = new Term("50D").Next(T0);
            var exerciseDate     = new Term("176D").Next(T0);
            var calendar         = CalendarImpl.Get("chn");

            var valuationDay    = t0;
            var valuationDayNew = t1;

            var option = new ResetStrikeOption(
                T0,
                exerciseDate,
                OptionExercise.European,
                isCall ? OptionType.Call : OptionType.Put,
                isPercentage ? ResetStrikeType.PercentagePayoff : ResetStrikeType.NormalPayoff,
                strike,
                InstrumentType.EquityIndex,
                calendar,
                new Act365(),
                CurrencyCode.CNY,
                CurrencyCode.CNY,
                new[] { exerciseDate },
                new[] { exerciseDate },
                strikefixingDate
                );
            var market    = TestMarket(referenceDate: t0, vol: vol, spot: spot);
            var marketNew = TestMarket(referenceDate: t1, vol: volNew, spot: spotNew);
            var marketPI  = TestMarket(referenceDate: t0, vol: vol, spot: spotNew);
            var marketVI  = TestMarket(referenceDate: t0, vol: volNew, spot: spot);
            var marketPVC = TestMarket(referenceDate: t0, vol: volNew, spot: spotNew);

            var engine = new AnalyticalResetStrikeOptionEngine();

            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);
        }