コード例 #1
0
        public void BarrierFacts()
        {
            var t   = 1.0;
            var k   = 100;
            var b   = 0.0;
            var f   = 100;
            var vol = 0.32;
            var rf  = 0.05;
            var cp  = OptionType.C;

            //zero barrier knock-in down is worthless
            var PV = BlackFunctions.BarrierOptionPV(f, k, rf, t, vol, cp, b, BarrierType.KI, BarrierSide.Down);

            Assert.Equal(0.0, PV, 10);

            //zero barrier knock-in up is worth vanilla
            var vanillaPV = BlackFunctions.BlackPV(f, k, rf, t, vol, cp);

            PV = BlackFunctions.BarrierOptionPV(f, k, rf, t, vol, cp, b, BarrierType.KI, BarrierSide.Up);
            Assert.Equal(vanillaPV, PV, 10);

            //ki forward is worth same as fwd
            b = 100;
            k = 110;
            var PVc   = BlackFunctions.BarrierOptionPV(f, k, rf, t, vol, OptionType.C, b, BarrierType.KI, BarrierSide.Down);
            var PVp   = BlackFunctions.BarrierOptionPV(f, k, rf, t, vol, OptionType.P, b, BarrierType.KI, BarrierSide.Down);
            var df    = Exp(-rf * t);
            var fwdPV = (f - k) * df;

            Assert.Equal(fwdPV, PVc - PVp, 10);
        }
コード例 #2
0
        public void PathsGenerated()
        {
            var vol = 0.32;

            using var engine = new PathEngine(IsCoverageOnly ? 2.IntPow(6) : 2 << 10);
            engine.AddPathProcess(new Random.MersenneTwister.MersenneTwister64()
            {
                UseNormalInverse = true
            });
            var asset2 = new ConstantVolSingleAsset
                         (
                startDate: DateTime.Now.Date,
                expiry: DateTime.Now.Date.AddYears(1),
                vol: vol,
                spot: 500,
                drift: 0.00,
                numberOfSteps: IsCoverageOnly ? 1 : 365,
                name: "TestAsset2"
                         );

            engine.AddPathProcess(asset2);
            var payoff = new EuropeanPut("TestAsset2", 500, DateTime.Now.Date.AddYears(1));

            engine.AddPathProcess(payoff);
            engine.SetupFeatures();
            engine.RunProcess();

            var pv      = payoff.AverageResult;
            var blackPv = BlackFunctions.BlackPV(500, 500, 0, 1, vol, OptionType.P);

            if (!IsCoverageOnly)
            {
                Assert.Equal(blackPv, pv, 0);
            }
        }
コード例 #3
0
        public void ThetaFacts()
        {
            var t   = 1.0;
            var k   = 100;
            var f   = 100;
            var vol = 0.32;
            var rf  = 0.05;
            var cp  = OptionType.P;


            //theta closely matches numerical estimate
            var bumpT    = 1e-10;
            var PV1      = BlackFunctions.BlackPV(f, k, rf, t, vol, cp);
            var PV2      = BlackFunctions.BlackPV(f, k, rf, t - bumpT, vol, cp);
            var thetaEst = (PV2 - PV1) / bumpT;
            var theta    = BlackFunctions.BlackTheta(f, k, rf, t, vol, cp);

            Assert.Equal(thetaEst, theta, 3);


            //theta closely matches numerical estimate
            cp       = OptionType.C;
            PV1      = BlackFunctions.BlackPV(f, k, rf, t, vol, cp);
            PV2      = BlackFunctions.BlackPV(f, k, rf, t - bumpT, vol, cp);
            thetaEst = (PV2 - PV1) / bumpT;
            theta    = BlackFunctions.BlackTheta(f, k, rf, t, vol, cp);
            Assert.Equal(thetaEst, theta, 3);
        }
コード例 #4
0
        public void VegaFacts()
        {
            var t   = 1.0;
            var k   = 100;
            var f   = 100;
            var vol = 0.32;
            var rf  = 0.05;
            var cp  = OptionType.P;

            //vega closely matches numerical estimate
            var PV1     = BlackFunctions.BlackPV(f, k, rf, t, vol - 0.00005, cp);
            var PV2     = BlackFunctions.BlackPV(f, k, rf, t, vol + 0.00005, cp);
            var vegaEst = (PV2 - PV1) / 0.0001 * 0.01;
            var vega    = BlackFunctions.BlackVega(f, k, rf, t, vol);

            Assert.Equal(vegaEst, vega, 6);

            //all else the same, more time==more vega
            var vegaNear = BlackFunctions.BlackVega(f, k, rf, t, vol);
            var vegaFar  = BlackFunctions.BlackVega(f, k, rf, t * 2, vol);

            Assert.True(vegaFar > vegaNear);

            //cases for zero vega
            vega = BlackFunctions.BlackVega(f, 0, rf, t, vol);
            Assert.Equal(0, vega, 8);

            vega = BlackFunctions.BlackVega(f, 1e6, rf, t, vol);
            Assert.Equal(0, vega, 8);
        }
コード例 #5
0
        public void LVMC_PathsGenerated()
        {
            var origin = DateTime.Now.Date;

            using var engine = new PathEngine(2.IntPow(IsCoverageOnly ? 6 : 12))
                  {
                      Parallelize = false
                  };

            engine.AddPathProcess(new Random.MersenneTwister.MersenneTwister64()
            {
                UseNormalInverse = true,
                UseAnthithetic   = true
            });

            var tenorsStr  = new[] { "1m", "2m", "3m", "6m" };
            var tenors     = tenorsStr.Select(x => new Frequency(x));
            var expiries   = tenors.Select(t => origin.AddPeriod(RollType.F, new Calendar(), t)).ToArray();
            var deltaKs    = new[] { 0.1, 0.25, 0.5, 0.75, 0.9 };
            var smileVols  = new[] { 0.32, 0.3, 0.29, 0.3, 0.32 };
            var vols       = Enumerable.Repeat(smileVols, expiries.Length).ToArray();
            var tExp       = (origin.AddMonths(6) - origin).TotalDays / 365.0;
            var volSurface = new GridVolSurface(origin, deltaKs, expiries, vols,
                                                StrikeType.ForwardDelta, Interpolator1DType.GaussianKernel,
                                                Interpolator1DType.LinearInVariance, DayCountBasis.Act365F);

            var fwdCurve = new Func <double, double>(t => { return(900 + 100 * t / tExp); });
            var asset    = new LVSingleAsset
                           (
                startDate: origin,
                expiryDate: origin.AddMonths(6),
                volSurface: volSurface,
                forwardCurve: fwdCurve,
                nTimeSteps: IsCoverageOnly ? 3 : 100,
                name: "TestAsset"
                           );

            engine.AddPathProcess(asset);
            var payoff  = new EuropeanPut("TestAsset", 900, origin.AddMonths(6));
            var payoff2 = new EuropeanCall("TestAsset", 0, origin.AddMonths(6));

            engine.AddPathProcess(payoff);
            engine.AddPathProcess(payoff2);
            engine.SetupFeatures();
            engine.RunProcess();

            var pv       = payoff.AverageResult;
            var blackVol = volSurface.GetVolForAbsoluteStrike(900, origin.AddMonths(6), fwdCurve(tExp));
            var blackPv  = BlackFunctions.BlackPV(fwdCurve(tExp), 900, 0, tExp, blackVol, OptionType.P);

            if (!IsCoverageOnly)
            {
                Assert.True(System.Math.Abs(blackPv / pv - 1.0) < 0.02);
                var fwd = payoff2.AverageResult;
                Assert.True(System.Math.Abs(fwdCurve(tExp) / fwd - 1.0) < 0.005);
            }
        }
コード例 #6
0
        public void PVFacts()
        {
            var t   = 1.0;
            var t2  = 2.0;
            var k   = 0;
            var f   = 100;
            var vol = 0.32;
            var rf  = 0.05;
            var cp  = OptionType.P;

            //expired is worthless
            var PV = TurnbullWakeman.PV(f, 0, vol, 0, t, 0, rf, OptionType.P);

            Assert.Equal(0, PV, 10);
            PV = TurnbullWakeman.PV(f, 0, vol, 0, t, 0, rf, OptionType.C);
            Assert.Equal(0, PV, 10);

            //zero strike put is worthless
            PV = TurnbullWakeman.PV(f, 0, vol, 0, t, t2, rf, cp);
            Assert.Equal(0, PV, 10);
            PV = TurnbullWakeman.PV(f, 50, vol, 0, -0.1, t2, rf, cp);
            Assert.Equal(0, PV, 10);

            //zero strike call is worth discounted fwd
            cp = OptionType.C;
            PV = TurnbullWakeman.PV(f, 0, vol, 0, t, t2, rf, cp);
            Assert.Equal(System.Math.Exp(-rf * t2) * f, PV, 2);

            //OTM option with zero vol is worthless
            vol = 0.0;
            k   = f + 1;
            PV  = TurnbullWakeman.PV(f, k, vol, 0, t, t2, rf, cp);
            Assert.Equal(0, PV, 10);

            //put-call parity at f==k
            k   = f;
            vol = 0.32;
            var PVcall = TurnbullWakeman.PV(f, 0, vol, k, t, t2, rf, OptionType.C);
            var PVput  = TurnbullWakeman.PV(f, 0, vol, k, t, t2, rf, OptionType.P);

            Assert.Equal(PVcall, PVput, 2);

            //independent fwds version
            var valDate         = new DateTime(2019, 10, 24);
            var fixingStartDate = new DateTime(2019, 10, 01);
            var fixingEndDate   = new DateTime(2019, 10, 25);
            var fixingDates     = DateExtensions.BusinessDaysInPeriod(fixingStartDate, fixingEndDate, TestProviderHelper.CalendarProvider.Collection["NYC"]).ToArray();
            var fwds            = fixingDates.Select(x => 100.0).ToArray();
            var sigmas          = fixingDates.Select(x => 0.32).ToArray();

            PV = TurnbullWakeman.PV(fwds, fixingDates, valDate, fixingEndDate, sigmas, 1, 0.0, OptionType.C, true);
            var blackPV = BlackFunctions.BlackPV(100.0, 1, 0.0, 1 / 365.0, 0.32, OptionType.C);

            Assert.Equal(blackPV, PV, 4);
        }
コード例 #7
0
ファイル: MCLocalVolFacts.cs プロジェクト: mpvyard/qwack
        public void LVMC_PathsGenerated()
        {
            var origin = DateTime.Now.Date;
            var engine = new PathEngine(2.IntPow(17));

            //engine.AddPathProcess(new Random.MersenneTwister.MersenneTwister64()
            //{
            //    UseNormalInverse = true,
            //    UseAnthithetic = false
            //});
            engine.AddPathProcess(new Random.Sobol.SobolShiftedPathGenerator(new Random.Sobol.SobolDirectionNumbers(s_directionNumbers), 0)
            {
                UseNormalInverse = true
            });
            var tenorsStr = new[] { "1m", "2m", "3m", "6m", "9m", "1y" };
            var tenors    = tenorsStr.Select(x => new Frequency(x));
            var expiries  = tenors.Select(t => origin.AddPeriod(RollType.F, new Calendar(), t)).ToArray();
            var deltaKs   = new[] { -0.1, -0.25, -0.5, -0.75, -0.9 };
            var smileVols = new[] { 0.32, 0.3, 0.29, 0.3, 0.32 };
            var vols      = Enumerable.Repeat(smileVols, expiries.Length).ToArray();

            var volSurface = new GridVolSurface(origin, deltaKs, expiries, vols,
                                                Core.Basic.StrikeType.ForwardDelta, Interpolator1DType.LinearFlatExtrap,
                                                Interpolator1DType.LinearInVariance, DayCountBasis.Act365F);

            var fwdCurve = new Func <double, double>(t => { return(900 + 100 * t); });
            var asset    = new LVSingleAsset
                           (
                startDate: origin,
                expiryDate: origin.AddYears(1),
                volSurface: volSurface,
                forwardCurve: fwdCurve,
                nTimeSteps: 365,
                name: "TestAsset"
                           );

            engine.AddPathProcess(asset);
            var payoff  = new EuropeanPut("TestAsset", 900, origin.AddYears(1));
            var payoff2 = new EuropeanCall("TestAsset", 0, origin.AddYears(1));

            engine.AddPathProcess(payoff);
            engine.AddPathProcess(payoff2);
            engine.SetupFeatures();
            engine.RunProcess();
            var pv       = payoff.AverageResult;
            var blackVol = volSurface.GetVolForAbsoluteStrike(900, origin.AddYears(1), fwdCurve(1.0));
            var blackPv  = BlackFunctions.BlackPV(1000, 900, 0, 1, blackVol, OptionType.P);

            Assert.Equal(blackPv, pv, 0);
            var fwd = payoff2.AverageResult;

            Assert.True(System.Math.Abs(fwdCurve(1) / fwd - 1.0) < 0.001);
            //var output = new OutputPathsToImage(engine,2000,1000);
        }
コード例 #8
0
        public static double InverseCDFex(this IVolSurface surface, double t, double fwd, double p)
        {
            var deltaK    = fwd * 1e-10;
            var lowGuess  = fwd / 2;
            var highGuess = fwd * 2;

            var targetFunc = new Func <double, double>(k =>
            {
                var volM = surface.GetVolForAbsoluteStrike(k - deltaK, t, fwd);
                var volP = surface.GetVolForAbsoluteStrike(k + deltaK, t, fwd);
                var pvM  = BlackFunctions.BlackPV(fwd, k - deltaK, 0.0, t, volM, OptionType.P);
                var pvP  = BlackFunctions.BlackPV(fwd, k + deltaK, 0.0, t, volP, OptionType.P);
                var digi = (pvP - pvM) / (2 * deltaK);
                //var digi = BlackFunctions.BlackDigitalPV(fwd, k, 0, t, surface.GetVolForAbsoluteStrike(k, t, fwd), OptionType.P);
                return(p - digi);
            });

            var breakCount = 0;

            while (targetFunc(lowGuess) < 0)
            {
                // highGuess = lowGuess*2.0;
                lowGuess /= 2.0;
                breakCount++;
                if (breakCount == 10)
                {
                    return(lowGuess);
                }
            }
            breakCount = 0;
            while (targetFunc(highGuess) > 0)
            {
                //lowGuess = highGuess/2.0;
                highGuess *= 2.0;
                breakCount++;
                if (breakCount == 10)
                {
                    return(highGuess);
                }
            }

            var b = Math.Solvers.Brent.BrentsMethodSolve(targetFunc, lowGuess, highGuess, 1e-8);

            //var b = Math.Solvers.Newton1D.MethodSolve2(targetFunc, fwd, 1e-6, 1000, fwd * 0.00001);
            if (double.IsInfinity(b) || double.IsNaN(b))
            {
                throw new Exception("Invalid strike found");
            }
            //if (b==lowGuess || b==highGuess)
            //    throw new Exception("Strike outside of bounds");

            return(b);
        }
コード例 #9
0
        public static IInterpolator1D GenerateCDF(this IVolSurface surface, int numSamples, DateTime expiry, double fwd, bool returnInverse = false)
        {
            var deltaKLow  = 0.0000001;
            var deltaKHi   = 0.9999999;
            var kStepD     = (deltaKHi - deltaKLow) / (numSamples + 3);
            var deltaKBump = deltaKLow / 10;

            var t = surface.OriginDate.CalculateYearFraction(expiry, DayCountBasis.Act365F);

            var x = new double[numSamples + 2];
            var y = new double[numSamples + 2];


            for (var i = 0; i < x.Length; i++)
            {
                var deltaKNew = deltaKLow + i * kStepD;
                var mStrike   = deltaKNew - deltaKBump / 2;
                var pStrike   = deltaKNew + deltaKBump / 2;

                var mStrikeVol = surface.GetVolForDeltaStrike(mStrike, t, fwd);
                var mk         = BlackFunctions.AbsoluteStrikefromDeltaKAnalytic(fwd, -mStrike, 0, t, mStrikeVol);
                var pStrikeVol = surface.GetVolForDeltaStrike(pStrike, t, fwd);
                var pk         = BlackFunctions.AbsoluteStrikefromDeltaKAnalytic(fwd, -pStrike, 0, t, pStrikeVol);

                if (i == 0)
                {
                    x[0] = mk / 2.0;
                    y[0] = 0;
                    continue;
                }
                if (i == x.Length - 1)
                {
                    x[i] = pk * 2;
                    y[i] = 1;
                    continue;
                }

                var dkAbs = (pk - mk);

                var pPut = BlackFunctions.BlackPV(fwd, pk, 0, t, pStrikeVol, OptionType.P);
                var mPut = BlackFunctions.BlackPV(fwd, mk, 0, t, mStrikeVol, OptionType.P);


                var digital = (pPut - mPut) / dkAbs;
                y[i] = digital;
                x[i] = (mk + pk) / 2.0;
            }

            return(returnInverse ?
                   InterpolatorFactory.GetInterpolator(y, x, Interpolator1DType.LinearFlatExtrap) :
                   InterpolatorFactory.GetInterpolator(x, y, Interpolator1DType.LinearFlatExtrap));
        }
コード例 #10
0
        public static IInterpolator1D GeneratePDF(this IVolSurface surface, int numSamples, DateTime expiry, double fwd)
        {
            var deltaK = fwd * 0.0001;

            var t            = surface.OriginDate.CalculateYearFraction(expiry, DayCountBasis.Act365F);
            var lowStrikeVol = surface.GetVolForDeltaStrike(0.0001, t, fwd);
            var lowStrike    = BlackFunctions.AbsoluteStrikefromDeltaKAnalytic(fwd, -0.0001, 0, t, lowStrikeVol);
            var hiStrikeVol  = surface.GetVolForDeltaStrike(0.9999, t, fwd);
            var hiStrike     = BlackFunctions.AbsoluteStrikefromDeltaKAnalytic(fwd, -0.9999, 0, t, hiStrikeVol);

            var x = new double[numSamples + 2];
            var y = new double[numSamples + 2];

            var k = lowStrike;

            var kStep = (hiStrike - lowStrike) / numSamples;

            for (var i = 0; i < x.Length; i++)
            {
                if (i == 0)
                {
                    x[0] = lowStrike / 2.0;
                    y[0] = 0;
                    continue;
                }
                if (i == x.Length - 1)
                {
                    x[i] = k * 2;
                    y[i] = 0;
                    continue;
                }

                var volLow    = surface.GetVolForAbsoluteStrike(k - deltaK, t, fwd);
                var callLow   = BlackFunctions.BlackPV(fwd, k - deltaK, 0, t, volLow, OptionType.C);
                var volMid    = surface.GetVolForAbsoluteStrike(k, t, fwd);
                var callMid   = BlackFunctions.BlackPV(fwd, k, 0, t, volMid, OptionType.C);
                var volHi     = surface.GetVolForAbsoluteStrike(k + deltaK, t, fwd);
                var callHi    = BlackFunctions.BlackPV(fwd, k + deltaK, 0, t, volHi, OptionType.C);
                var digitalLo = (callLow - callMid) / deltaK;
                var digitalHi = (callMid - callHi) / deltaK;
                y[i] = (digitalLo - digitalHi) / deltaK;
                x[i] = k;
                k   += kStep;
            }

            var firstPass = InterpolatorFactory.GetInterpolator(x, y, Interpolator1DType.LinearFlatExtrap);
            var totalY    = ((IIntegrableInterpolator)firstPass).DefiniteIntegral(x.First(), x.Last());

            y = y.Select(v => v / totalY).ToArray();
            return(InterpolatorFactory.GetInterpolator(x, y, Interpolator1DType.LinearFlatExtrap));
        }
コード例 #11
0
ファイル: ClewlowFacts.cs プロジェクト: biqueta/qwack
        public void PVFacts()
        {
            var evalDate = DateTime.Today;
            var avgStart = evalDate.AddDays(365);
            var avgEnd   = avgStart.AddDays(32);
            var t        = (avgEnd - evalDate).TotalDays / 365.0;

            var fixCal = new Calendar();

            var k   = 0;
            var f   = 100;
            var vol = 0.32;
            var rf  = 0.05;

            //zero strike put is worthless
            var PV = LME_Clewlow.PV(f, 0, vol, 0.0, evalDate, avgStart, avgEnd, rf, OptionType.P, fixCal);

            Assert.Equal(0, PV, 10);

            //zero strike call is worth discounted fwd
            PV = LME_Clewlow.PV(f, 0, vol, 0.0, evalDate, avgStart, avgEnd, rf, OptionType.C, fixCal);
            Assert.Equal(System.Math.Exp(-rf * t) * f, PV, 2);

            //OTM option with zero vol is worthless
            vol = 0.0;
            k   = f + 1;
            PV  = LME_Clewlow.PV(f, 0, vol, 0.0, evalDate, avgStart, avgEnd, rf, OptionType.C, fixCal);
            Assert.Equal(0, PV, 10);

            //put-call parity at f==k
            k   = f;
            vol = 0.32;
            var PVcall = LME_Clewlow.PV(f, 0, vol, k, evalDate, avgStart, avgEnd, rf, OptionType.C, fixCal);
            var PVput  = LME_Clewlow.PV(f, 0, vol, k, evalDate, avgStart, avgEnd, rf, OptionType.P, fixCal);

            Assert.Equal(PVcall, PVput, 2);

            //bullet defaults to european
            avgStart = avgEnd;
            PV       = LME_Clewlow.PV(f, 0.0, vol, k, evalDate, avgStart, avgEnd, rf, OptionType.P, fixCal);
            var blackPV = BlackFunctions.BlackPV(f, k, rf, t, vol, OptionType.P);

            Assert.Equal(blackPV, PV, 10);

            //on expiry its intrinsic
            evalDate = avgEnd;
            PV       = LME_Clewlow.PV(f, f, vol, f + 10, evalDate, avgStart, avgEnd, rf, OptionType.P, fixCal);
            Assert.Equal(10.0, PV, 10);
            PV = LME_Clewlow.PV(f, f, vol, f - 10, evalDate, avgStart, avgEnd, rf, OptionType.C, fixCal);
            Assert.Equal(10.0, PV, 10);
        }
コード例 #12
0
        public void ImpliedVolFacts()
        {
            var t   = 1.0;
            var k   = 120;
            var f   = 100;
            var vol = 0.32;
            var rf  = 0.05;
            var cp  = OptionType.P;

            var PV         = BlackFunctions.BlackPV(f, k, rf, t, vol, cp);
            var impliedVol = BlackFunctions.BlackImpliedVol(f, k, rf, t, PV, cp);

            Assert.Equal(vol, impliedVol, 10);
        }
コード例 #13
0
        public void DeltaGammaFacts()
        {
            var t   = 1.0;
            var k   = 100;
            var f   = 100;
            var vol = 0.32;
            var rf  = 0.05;
            var cp  = OptionType.P;

            //delta closely matches numerical estimate
            var PV1      = BlackFunctions.BlackPV(f + 0.000005, k, rf, t, vol, cp);
            var PV2      = BlackFunctions.BlackPV(f - 0.000005, k, rf, t, vol, cp);
            var deltaEst = (PV1 - PV2) / 0.00001;
            var delta    = BlackFunctions.BlackDelta(f, k, rf, t, vol, cp);

            Assert.Equal(deltaEst, delta, 6);

            //all else the same, more time for OTM option == more delta
            k = 150;
            var deltaNear = BlackFunctions.BlackDelta(f, k, rf, t, vol, cp);
            var deltaFar  = BlackFunctions.BlackDelta(f, k, rf, t * 2, vol, cp);

            Assert.True(deltaFar > deltaNear);

            //put-call parity
            var deltaCall = BlackFunctions.BlackDelta(f, k, rf, t, vol, OptionType.C);
            var deltaPut  = BlackFunctions.BlackDelta(f, k, rf, t, vol, OptionType.P);

            var syntheticFwdDelta = deltaCall - deltaPut;

            Assert.Equal(System.Math.Exp(-rf * t), syntheticFwdDelta, 10);

            //gamma closely matches numerical estimate
            var delta1   = BlackFunctions.BlackDelta(f + 0.000005, k, rf, t, vol, cp);
            var delta2   = BlackFunctions.BlackDelta(f - 0.000005, k, rf, t, vol, cp);
            var gammaEst = (delta1 - delta2) / 0.00001;
            var gamma    = BlackFunctions.BlackGamma(f, k, rf, t, vol);

            Assert.Equal(gammaEst, gamma, 6);

            //cases for zero delta / gamma
            delta = BlackFunctions.BlackDelta(f, 0, rf, t, vol, OptionType.P);
            Assert.Equal(0, delta, 8);
            delta = BlackFunctions.BlackDelta(f, 1e6, rf, t, vol, OptionType.C);
            Assert.Equal(0, delta, 8);
            gamma = BlackFunctions.BlackGamma(f, 0, rf, t, vol);
            Assert.Equal(0, gamma, 8);
            gamma = BlackFunctions.BlackGamma(f, 1e6, rf, t, vol);
            Assert.Equal(0, gamma, 8);
        }
コード例 #14
0
        public void PVFacts()
        {
            var t   = 1.0;
            var k   = 0;
            var f   = 100;
            var vol = 0.32;
            var rf  = 0.05;
            var cp  = OptionType.P;
            var df  = System.Math.Exp(-rf * t);

            //zero strike put is worthless
            var PV = BinomialTree.AmericanFutureOptionPV(f, k, rf, t, vol, cp);

            Assert.Equal(0, PV, 10);
            PV = TrinomialTree.AmericanFutureOptionPV(f, k, rf, t, vol, cp);
            Assert.Equal(0, PV, 10);

            //zero strike call is worth fwd with no discounting
            cp = OptionType.C;
            PV = BinomialTree.AmericanFutureOptionPV(f, k, rf, t, vol, cp);
            Assert.Equal(f, PV, 10);
            PV = TrinomialTree.AmericanFutureOptionPV(f, k, rf, t, vol, cp);
            Assert.Equal(f, PV, 10);

            //and has delta = 1.0
            var greeks = TrinomialTree.AmericanFutureOption(f, k, rf, t, vol, cp);

            Assert.Equal(1.0, (double)greeks[1, 0], 10);
            greeks = BinomialTree.AmericanFutureOption(f, k, rf, t, vol, cp);
            Assert.Equal(1.0, (double)greeks[1, 0], 10);

            //OTM option with zero vol is worthless
            vol = 0.0;
            k   = f + 1;
            PV  = BinomialTree.AmericanFutureOptionPV(f, k, rf, t, vol, cp);
            Assert.Equal(0, PV, 10);
            PV = TrinomialTree.AmericanFutureOptionPV(f, k, rf, t, vol, cp);
            Assert.Equal(0, PV, 10);

            //option worth >= black in all cases for same inputs
            vol = 0.32;
            k   = f + 20;
            var PVBlack = BlackFunctions.BlackPV(f, k, rf, t, vol, cp);

            PV = BinomialTree.AmericanFutureOptionPV(f, k, rf, t, vol, cp);
            Assert.True(PV >= PVBlack);
            PV = TrinomialTree.AmericanFutureOptionPV(f, k, rf, t, vol, cp);
            Assert.True(PV >= PVBlack);
        }
コード例 #15
0
        public void PremiumInterpolatorFacts()
        {
            var origin       = new DateTime(2017, 02, 07);
            var expiry       = origin.AddYears(1);
            var t            = (expiry - origin).TotalDays / 365.0;
            var volAsset     = 0.32;
            var fwd          = 100.0;
            var surfaceAsset = new ConstantVolSurface(origin, volAsset);

            var premInterp = surfaceAsset.GeneratePremiumInterpolator(100, expiry, fwd, OptionType.P);

            var strike = fwd * 0.8;

            Assert.Equal(BlackFunctions.BlackPV(fwd, strike, 0.0, t, volAsset, OptionType.P), premInterp.Interpolate(strike), 2);
        }
コード例 #16
0
        public void PVFacts()
        {
            var t   = 1.0;
            var k   = 0.0;
            var f   = 100.0;
            var vol = 0.32;
            var rf  = 0.05;
            var cp  = OptionType.P;

            //zero strike put is worthless
            var PV = TrinomialTree.AmericanAssetOptionPV(f, k, rf, f, t, vol, cp);

            Assert.Equal(0, PV, 10);
            PV = BinomialTree.AmericanPV(t, f, k, rf, vol, cp, rf, 100);
            Assert.Equal(0, PV, 10);
            var flatInterp    = new DummyPointInterpolator(new[] { 1.0 }, new[] { rf });
            var flatFwdInterp = new DummyPointInterpolator(new[] { 1.0 }, new[] { f });

            PV = TrinomialTree.VanillaPV(t, f, k, flatInterp, vol, cp, flatFwdInterp, 100, true);
            Assert.Equal(0, PV, 10);

            //zero strike call is worth fwd with no discounting
            cp = OptionType.C;
            PV = TrinomialTree.AmericanAssetOptionPV(f, k, rf, f, t, vol, cp);
            Assert.Equal(f, PV, 10);
            PV = BinomialTree.AmericanPV(t, f, k, rf, vol, cp, rf, 100);
            Assert.Equal(f, PV, 10);

            //OTM option with zero vol is worthless
            vol = 0.0;
            k   = f + 1.0;
            PV  = BinomialTree.AmericanPV(t, f, k, rf, vol, cp, rf, 100);
            Assert.Equal(0, PV, 10);
            PV = TrinomialTree.AmericanAssetOptionPV(f, k, rf, f, t, vol, cp);
            Assert.Equal(0, PV, 10);

            //option worth >= black in all cases for same inputs
            vol = 0.32;
            k   = f + 20;
            var PVBlack = BlackFunctions.BlackPV(f, k, rf, t, vol, cp);

            PV = BinomialTree.AmericanPV(t, f, k, rf, vol, cp, rf, 100);
            Assert.True(PV >= PVBlack);
            PV = TrinomialTree.AmericanAssetOptionPV(f, k, rf, f, t, vol, cp);
            Assert.True(PV >= PVBlack);
        }
コード例 #17
0
        public void PVFacts()
        {
            var t   = 1.0;
            var k   = 0;
            var f   = 100;
            var vol = 0.32;
            var rf  = 0.05;
            var cp  = OptionType.P;

            //zero strike put is worthless
            var PV = BlackFunctions.BlackPV(f, k, rf, t, vol, cp);

            Assert.Equal(0, PV, 10);

            //zero strike call is worth discounted fwd
            cp = OptionType.C;
            PV = BlackFunctions.BlackPV(f, k, rf, t, vol, cp);
            Assert.Equal(Exp(-rf * t) * f, PV, 10);

            //OTM option with zero vol is worthless
            vol = 0.0;
            k   = f + 1;
            PV  = BlackFunctions.BlackPV(f, k, rf, t, vol, cp);
            Assert.Equal(0, PV, 10);

            //put-call parity at f==k
            k   = f;
            vol = 0.32;
            var PVcall = BlackFunctions.BlackPV(f, k, rf, t, vol, OptionType.C);
            var PVput  = BlackFunctions.BlackPV(f, k, rf, t, vol, OptionType.P);

            Assert.Equal(PVcall, PVput, 10);

            //forward price constant wrt total variance
            rf = 0;
            var variance = vol * vol * t;
            var t2       = t * 2.0;
            var vol2     = Sqrt(variance / t2);

            var PVnear = BlackFunctions.BlackPV(f, k, rf, t, vol, OptionType.C);
            var PVfar  = BlackFunctions.BlackPV(f, k, rf, t2, vol2, OptionType.C);

            Assert.Equal(PVnear, PVfar, 10);
        }
コード例 #18
0
        public void DigitalFacts()
        {
            var t   = 1.0;
            var f   = 100;
            var vol = 0.32;
            var rf  = 0.0;
            var cp  = OptionType.C;
            var k   = 110;

            var digiPV   = BlackFunctions.BlackDigitalPV(f, k, rf, t, vol, cp);
            var spread   = 0.0001;
            var expected = (BlackFunctions.BlackPV(f, k, rf, t, vol, cp) - BlackFunctions.BlackPV(f, k + spread, rf, t, vol, cp)) / spread;

            Assert.Equal(expected, digiPV, 6);

            var iv = BlackFunctions.BlackDigitalImpliedVol(f, k, rf, t, digiPV, cp);

            Assert.Equal(vol, iv, 6);
        }
コード例 #19
0
        public static IInterpolator1D GenerateCDF(this IInterpolator1D smile, int numSamples, double t, double fwd)
        {
            var deltaK = fwd * 0.0001;

            var atmVol    = smile.Interpolate(fwd);
            var lowStrike = BlackFunctions.AbsoluteStrikefromDeltaKAnalytic(fwd, -0.0001, 0, t, atmVol);
            var hiStrike  = BlackFunctions.AbsoluteStrikefromDeltaKAnalytic(fwd, -0.9999, 0, t, atmVol);

            var x = new double[numSamples + 2];
            var y = new double[numSamples + 2];

            var k = lowStrike;

            var kStep = (hiStrike - lowStrike) / numSamples;

            for (var i = 0; i < x.Length; i++)
            {
                if (i == 0)
                {
                    x[0] = k / 2.0;
                    y[0] = 0;
                    continue;
                }
                if (i == x.Length - 1)
                {
                    x[i] = k * 2;
                    y[i] = 1;
                    continue;
                }
                var volLow  = smile.Interpolate(k - deltaK / 2.0);
                var putLow  = BlackFunctions.BlackPV(fwd, k - deltaK / 2.0, 0, t, volLow, OptionType.P);
                var volHi   = smile.Interpolate(k + deltaK / 2.0);
                var putHi   = BlackFunctions.BlackPV(fwd, k + deltaK / 2.0, 0, t, volHi, OptionType.P);
                var digital = (putHi - putLow) / deltaK;
                y[i] = digital;
                x[i] = k;
                k   += kStep;
            }

            return(InterpolatorFactory.GetInterpolator(x, y, Interpolator1DType.Linear));
        }
コード例 #20
0
ファイル: TurnbullWakeman.cs プロジェクト: zhangz/qwack
        public static double PV(double forward, double knownAverage, double sigma, double K, double tAvgStart, double tExpiry, double riskFree, OptionType callPut)
        {
            if (tExpiry <= 0) //work out intrinsic
            {
                return(callPut == OptionType.Call ? Max(0, knownAverage - K) : Max(0, K - knownAverage));
            }

            var tau = Max(0, tAvgStart);
            var M   = 2 * (Exp(sigma * sigma * tExpiry) - Exp(sigma * sigma * tau) * (1 + sigma * sigma * (tExpiry - tau)));

            M /= Pow(sigma, 4.0) * (tExpiry - tau) * (tExpiry - tau);

            //hack to fix deep ITM options have imaginary vol


            var sigma_a = tExpiry == 0 ? 0.0 : Sqrt(Log(M) / tExpiry);

            K = AsianUtils.AdjustedStrike(K, knownAverage, tExpiry, tAvgStart);

            if (K <= 0 && tAvgStart < 0)
            {
                if (callPut == OptionType.P)
                {
                    return(0);
                }
                var t2     = tExpiry - tAvgStart;
                var expAvg = knownAverage * (t2 - tExpiry) / t2 + forward * tExpiry / t2;
                var df     = Exp(-riskFree * tExpiry);
                return(df * expAvg);
            }


            var pv = BlackFunctions.BlackPV(forward, K, riskFree, tExpiry, sigma_a, callPut);

            if (tAvgStart < 0)
            {
                pv *= tExpiry / (tExpiry - tAvgStart);
            }
            return(pv);
        }
コード例 #21
0
ファイル: MCBlackVolFacts.cs プロジェクト: wy6688/qwack
        public void BlackMC_PathsGenerated()
        {
            var origin = DateTime.Now.Date;

            using var engine = new PathEngine(2.IntPow(IsCoverageOnly ? 6 : 15));
            engine.AddPathProcess(new Random.MersenneTwister.MersenneTwister64()
            {
                UseNormalInverse = true,
                UseAnthithetic   = false
            });
            var volSurface = new ConstantVolSurface(origin, 0.32);
            var fwdCurve   = new Func <double, double>(t => { return(900 + 100 * t); });
            var asset      = new BlackSingleAsset
                             (
                startDate: origin,
                expiryDate: origin.AddYears(1),
                volSurface: volSurface,
                forwardCurve: fwdCurve,
                nTimeSteps: IsCoverageOnly ? 1 : 10,
                name: "TestAsset"
                             );

            engine.AddPathProcess(asset);
            var payoff  = new EuropeanPut("TestAsset", 900, origin.AddYears(1));
            var payoff2 = new EuropeanCall("TestAsset", 0, origin.AddYears(1));

            engine.AddPathProcess(payoff);
            engine.AddPathProcess(payoff2);
            engine.SetupFeatures();
            engine.RunProcess();
            var pv      = payoff.AverageResult;
            var blackPv = BlackFunctions.BlackPV(1000, 900, 0, 1, 0.32, OptionType.P);

            if (!IsCoverageOnly)
            {
                Assert.True(System.Math.Abs(blackPv - pv) < 1.0);
                var fwd = payoff2.AverageResult;
                Assert.True(System.Math.Abs(fwdCurve(1) / fwd - 1.0) < 0.001);
            }
        }
コード例 #22
0
        public void EuropeanOnGridFacts()
        {
            var t   = 1.0;
            var k   = 120;
            var f   = 100;
            var vol = 0.32;
            var rf  = 0.05;
            var cp  = OptionType.P;

            var PVBlack = BlackFunctions.BlackPV(f, k, rf, t, vol, cp);
            var PVbi    = BinomialTree.EuropeanPV(t, f, k, rf, vol, cp, rf, 100);
            var PVtri   = TrinomialTree.EuropeanPV(t, f, k, rf, vol, cp, rf, 100);

            Assert.Equal(PVBlack, PVbi, 1);
            Assert.Equal(PVBlack, PVtri, 1);

            PVbi  = BinomialTree.EuropeanFuturePV(t, f, k, rf, vol, cp, 100);
            PVtri = TrinomialTree.EuropeanFuturePV(t, f, k, rf, vol, cp, 100);

            Assert.Equal(PVBlack, PVbi, 1);
            Assert.Equal(PVBlack, PVtri, 1);
        }
コード例 #23
0
        public static IInterpolator1D GeneratePremiumInterpolator(this IVolSurface surface, int numSamples, double t, double fwd, OptionType cp)
        {
            var lowStrikeVol = surface.GetVolForDeltaStrike(0.001, t, fwd);
            var lowStrike    = BlackFunctions.AbsoluteStrikefromDeltaKAnalytic(fwd, -0.001, 0, t, lowStrikeVol);
            var hiStrikeVol  = surface.GetVolForDeltaStrike(0.999, t, fwd);
            var hiStrike     = BlackFunctions.AbsoluteStrikefromDeltaKAnalytic(fwd, -0.999, 0, t, hiStrikeVol);

            var x = new double[numSamples + 2];
            var y = new double[numSamples + 2];

            var k = lowStrike;

            var kStep = (hiStrike - lowStrike) / (numSamples + 1.0);

            var vol  = surface.GetVolForAbsoluteStrike(k / 10, t, fwd);
            var call = BlackFunctions.BlackPV(fwd, k / 10, 0, t, vol, cp);

            y[0] = call;
            x[0] = k / 10;

            for (var i = 0; i < x.Length - 1; i++)
            {
                vol      = surface.GetVolForAbsoluteStrike(k, t, fwd);
                call     = BlackFunctions.BlackPV(fwd, k, 0, t, vol, cp);
                y[i + 1] = call;
                x[i + 1] = k;
                k       += kStep;
            }

            vol             = surface.GetVolForAbsoluteStrike(k * 10, t, fwd);
            call            = BlackFunctions.BlackPV(fwd, k * 10, 0, t, vol, cp);
            y[x.Length - 1] = call;
            x[x.Length - 1] = k * 10;

            return(InterpolatorFactory.GetInterpolator(x, y, Interpolator1DType.MonotoneCubicSpline));
        }
コード例 #24
0
        public static double PV(double forward, double knownAverage, double sigma, double K, DateTime evalDate, DateTime avgStartDate, DateTime avgEndDate, double riskFree, OptionType callPut, Calendar fixingCalendar)
        {
            if (avgEndDate == evalDate)
            {
                if (callPut == OptionType.C)
                {
                    return(Max(knownAverage - K, 0));
                }
                else
                {
                    return(Max(K - knownAverage, 0));
                }
            }

            else if (avgEndDate < evalDate)
            {
                return(0);
            }
            else if (avgStartDate == avgEndDate)
            {
                var t = (avgEndDate - evalDate).TotalDays / 365.0;
                return(BlackFunctions.BlackPV(forward, K, riskFree, t, sigma, callPut));
            }

            //Build Vector of Observation Dates
            var resetDates = avgStartDate.BusinessDaysInPeriod(avgEndDate, fixingCalendar);
            var nResets    = resetDates.Count;
            var nFixed     = resetDates.Count(x => x < evalDate);
            var nFloat     = nResets - nFixed;

            var DeltaTPrime = (nFixed > 0) ?
                              (resetDates[nFixed + 1] - resetDates[nFixed]).TotalDays / 365.0 :
                              (resetDates[0] - evalDate).TotalDays / 365.0;

            var DeltaT = (nFixed + 2 >= nResets) ?
                         DeltaTPrime :
                         (avgEndDate - resetDates[nFixed + 1]).TotalDays / 365.0 / nFloat;

            var Ak = knownAverage * nFixed / nResets;
            var E5 = 2 * Ak * forward * nFloat / nResets + (Ak * Ak);

            var E4 = 1.0;

            if (nFixed < nResets)
            {
                E4  = (1 + Exp(sigma * sigma * DeltaT)) * (Exp(nFloat * sigma * sigma * DeltaT) - 1);
                E4 += 2 * (nResets - nFixed) * (1 - Exp(sigma * sigma * DeltaT));
                E4 /= Pow(Exp(sigma * sigma * DeltaT) - 1, 2);
            }

            var E3 = Pow(forward / nResets, 2) * Exp(sigma * sigma * DeltaTPrime);
            var E2 = E3 * E4 + E5;
            var E1 = forward * (nResets - nFixed) / nResets + Ak;

            var EA = Ak + forward * nFloat / nResets;

            var b  = Log(E2) - 2 * Log(E1);
            var d1 = (Log(EA / K) + 0.5 * b) / Sqrt(b);
            var d2 = d1 - Sqrt(b);

            var df = Exp(-riskFree * (avgEndDate - evalDate).TotalDays / 365.0);

            //Main Option valuation
            if (callPut == OptionType.Call)
            {
                d1 = Math.Statistics.NormSDist(d1);
                d2 = Math.Statistics.NormSDist(d2);
                return((EA * d1 - K * d2) * df);
            }
            else
            {
                d1 = Math.Statistics.NormSDist(-d1);
                d2 = Math.Statistics.NormSDist(-d2);
                return((K * d2 - EA * d1) * df);
            }
        }
コード例 #25
0
        public void SolveSmileMarket()
        {
            var valDate = new DateTime(2018, 07, 28);
            var expDate = valDate.AddDays(365);
            var tExp    = (expDate - valDate).TotalDays / 365.0;
            var fwd     = 1000;

            double[] strikes = { 0.25, 0.5, 0.75 };

            var atmConstraint = new ATMStraddleConstraint
            {
                ATMVolType = AtmVolType.ZeroDeltaStraddle,
                MarketVol  = 0.32
            };

            var smile25d = new RRBFConstraint
            {
                Delta         = 0.25,
                FlyVol        = 0.01,
                RisykVol      = 0.02,
                WingQuoteType = WingQuoteType.Market
            };

            var s = new AssetSmileSolver();

            if (IsCoverageOnly)
            {
                s.Tollerance = 1;
            }

            var smile = s.Solve(atmConstraint, new[] { smile25d }, valDate, expDate, fwd, strikes, Interpolator1DType.Linear);

            if (!IsCoverageOnly)
            {
                Assert.Equal(atmConstraint.MarketVol, smile[1], 8);
            }

            var surface = new GridVolSurface(valDate, strikes, new[] { expDate }, new[] { smile }, StrikeType.ForwardDelta, Interpolator1DType.Linear, Interpolator1DType.Linear, DayCountBasis.Act365F);

            //reprice market RR structrure off smile, premium must match
            var marketVolC  = atmConstraint.MarketVol + smile25d.FlyVol + 0.5 * smile25d.RisykVol;
            var marketVolP  = atmConstraint.MarketVol + smile25d.FlyVol - 0.5 * smile25d.RisykVol;
            var marketKC25  = BlackFunctions.AbsoluteStrikefromDeltaKAnalytic(fwd, 0.25, 0, tExp, marketVolC);
            var marketKP25  = BlackFunctions.AbsoluteStrikefromDeltaKAnalytic(fwd, -0.25, 0, tExp, marketVolP);
            var marketC25FV = BlackFunctions.BlackPV(fwd, marketKC25, 0, tExp, marketVolC, OptionType.C);
            var marketP25FV = BlackFunctions.BlackPV(fwd, marketKP25, 0, tExp, marketVolP, OptionType.P);
            var marketRR    = marketC25FV - marketP25FV;

            var volC25d  = surface.GetVolForAbsoluteStrike(marketKC25, expDate, fwd);
            var volP25d  = surface.GetVolForAbsoluteStrike(marketKP25, expDate, fwd);
            var call25FV = BlackFunctions.BlackPV(fwd, marketKC25, 0, tExp, volC25d, OptionType.C);
            var put25FV  = BlackFunctions.BlackPV(fwd, marketKP25, 0, tExp, volP25d, OptionType.P);
            var smileRR  = call25FV - put25FV;

            if (!IsCoverageOnly)
            {
                Assert.Equal(marketRR, smileRR, 8);
            }

            //reprice market BF structrure off smile, premium must match
            var marketVolBF   = atmConstraint.MarketVol + smile25d.FlyVol;
            var marketKBFC25  = BlackFunctions.AbsoluteStrikefromDeltaKAnalytic(fwd, 0.25, 0, tExp, marketVolBF);
            var marketKBFP25  = BlackFunctions.AbsoluteStrikefromDeltaKAnalytic(fwd, -0.25, 0, tExp, marketVolBF);
            var marketBFC25FV = BlackFunctions.BlackPV(fwd, marketKBFC25, 0, tExp, marketVolBF, OptionType.C);
            var marketBFP25FV = BlackFunctions.BlackPV(fwd, marketKBFP25, 0, tExp, marketVolBF, OptionType.P);
            var marketBF      = marketBFC25FV + marketBFP25FV;

            var volCBF25d  = surface.GetVolForAbsoluteStrike(marketKBFC25, expDate, fwd);
            var volPBF25d  = surface.GetVolForAbsoluteStrike(marketKBFP25, expDate, fwd);
            var callBF25FV = BlackFunctions.BlackPV(fwd, marketKBFC25, 0, tExp, volCBF25d, OptionType.C);
            var putBF25FV  = BlackFunctions.BlackPV(fwd, marketKBFP25, 0, tExp, volPBF25d, OptionType.P);
            var smileBF    = callBF25FV + putBF25FV;

            if (!IsCoverageOnly)
            {
                Assert.Equal(marketBF, smileBF, 8);
            }
        }
コード例 #26
0
ファイル: TurnbullWakeman.cs プロジェクト: zhangz/qwack
        public static double PV(double[] forwards, DateTime[] fixingDates, DateTime evalDate, DateTime payDate, double[] sigmas, double K, double riskFree, OptionType callPut, bool todayFixed = false)
        {
            if (payDate < evalDate)
            {
                return(0.0);
            }

            if (forwards.Length != fixingDates.Length || fixingDates.Length != sigmas.Length)
            {
                throw new DataMisalignedException();
            }



            var nFixed = evalDate < fixingDates.First() ? 0 :
                         fixingDates.Where(x => (todayFixed ? x <= evalDate : x < evalDate)).Count();
            var nFloat = fixingDates.Length - nFixed;

            var m1           = forwards.Skip(nFixed).Average();
            var wholeAverage = forwards.Average();

            var tExpiry = evalDate.CalculateYearFraction(fixingDates.Last(), DayCountBasis.Act365F, false);
            var tPay    = evalDate.CalculateYearFraction(payDate, DayCountBasis.Act365F, false);
            var df      = Exp(-riskFree * tPay);

            if (tExpiry <= 0) //work out intrinsic
            {
                return(df * (callPut == OptionType.Call ? Max(0, wholeAverage - K) : Max(0, K - wholeAverage)));
            }

            var m2 = 0.0;
            var ts = fixingDates.Select(x => Max(0, evalDate.CalculateYearFraction(x, DayCountBasis.Act365F, false))).ToArray();

            for (var i = nFixed; i < fixingDates.Length; i++)
            {
                for (var j = nFixed; j < fixingDates.Length; j++)
                {
                    m2 += forwards[i] * forwards[j] * Exp(sigmas[i] * sigmas[j] * ts[Min(i, j)]);
                }
            }

            m2 /= nFloat * nFloat;
            var sigma_a = Sqrt(1 / tExpiry * Log(m2 / (m1 * m1)));

            var tAvgStart    = evalDate.CalculateYearFraction(fixingDates.First(), DayCountBasis.Act365F, false);
            var knownAverage = nFixed == 0 ? 0.0 : forwards.Take(nFixed).Average();

            var k0 = K;

            K = AsianUtils.AdjustedStrike(K, knownAverage, tExpiry, tAvgStart);

            if (K <= 0)
            {
                return((callPut == OptionType.P) ? 0.0 : df *Max(wholeAverage - k0, 0));
            }

            var pv = BlackFunctions.BlackPV(m1, K, 0.0, tExpiry, sigma_a, callPut);

            if (tAvgStart < 0)
            {
                pv *= tExpiry / (tExpiry - tAvgStart);
            }
            return(df * pv);
        }
コード例 #27
0
ファイル: BlackFunctionsFacts.cs プロジェクト: biqueta/qwack
 public void BlackPV_Facts()
 {
     Assert.Equal("Could not parse call or put flag - blah", BlackFunctions.BlackPV(1.0, 1.0, 1, 1, 1, "blah"));
     Assert.Equal(0.0, BlackFunctions.BlackPV(0.1, 1.0, 0.5, 0.0, 0.0, "C"));
 }