/** * Calculates the delta of a call option under a Black-Scholes model * * <p>The method also handles cases where the forward and/or option strike is negative and some * limit cases where the forward or the option strike is zero. In the case forward = option strike * = 0 the method returns 1.0. * * @param initialStockValue The initial value of the underlying, i.e., the spot. * @param riskFreeRate The risk free rate of the bank account numerarie. * @param volatility The Black-Scholes volatility. * @param optionMaturity The option maturity T. * @param optionStrike The option strike. * @return The delta of the option */ public static RandomVariableInterface blackScholesOptionDelta( RandomVariableInterface initialStockValue, RandomVariableInterface riskFreeRate, RandomVariableInterface volatility, double optionMaturity, RandomVariableInterface optionStrike) { if (optionMaturity < 0) { return initialStockValue.mult(0.0); } else { // Calculate delta RandomVariableInterface dPlus = initialStockValue .div(optionStrike) .log() .add(volatility.squared().mult(0.5).add(riskFreeRate).mult(optionMaturity)) .div(volatility) .div(Math.sqrt(optionMaturity)); UnivariateFunction cummulativeNormal = new UnivariateFunction() { public double value(double x) { return NormalDistribution.cumulativeDistribution(x); } }; RandomVariableInterface delta = dPlus.apply(cummulativeNormal); return delta; } }
/** * Calculates the Black-Scholes option value of a call, i.e., the payoff max(S(T)-K,0) P, where S * follows a log-normal process with constant log-volatility. * * <p>The model specific quantities are considered to be random variable, i.e., the function may * calculate an per-path valuation in a single call. * * @param forward The forward of the underlying. * @param volatility The Black-Scholes volatility. * @param optionMaturity The option maturity T. * @param optionStrike The option strike. If the option strike is ≤ 0.0 the method returns the * value of the forward contract paying S(T)-K in T. * @param payoffUnit The payoff unit (e.g., the discount factor) * @return Returns the value of a European call option under the Black-Scholes model. */ public static RandomVariableInterface blackScholesGeneralizedOptionValue( RandomVariableInterface forward, RandomVariableInterface volatility, double optionMaturity, double optionStrike, RandomVariableInterface payoffUnit) { if (optionMaturity < 0) { return forward.mult(0.0); } else { RandomVariableInterface dPlus = forward .div(optionStrike) .log() .add(volatility.squared().mult(0.5 * optionMaturity)) .div(volatility) .div(Math.sqrt(optionMaturity)); RandomVariableInterface dMinus = dPlus.sub(volatility.mult(Math.sqrt(optionMaturity))); UnivariateFunction cumulativeNormal = new UnivariateFunction() { public double value(double x) { return NormalDistribution.cumulativeDistribution(x); } }; RandomVariableInterface valueAnalytic = dPlus .apply(cumulativeNormal) .mult(forward) .sub(dMinus.apply(cumulativeNormal).mult(optionStrike)) .mult(payoffUnit); return valueAnalytic; } }
/* (non-Javadoc) * @see net.finmath.stochastic.RandomVariableInterface#equals(net.finmath.montecarlo.RandomVariable) */ @Override public boolean equals(RandomVariableInterface randomVariable) { if (this.time != randomVariable.getFiltrationTime()) return false; if (this.isDeterministic() && randomVariable.isDeterministic()) { return this.valueIfNonStochastic == randomVariable.get(0); } if (this.isDeterministic() != randomVariable.isDeterministic()) return false; for (int i = 0; i < realizations.length; i++) if (realizations[i] != randomVariable.get(i)) return false; return true; }
/** * Calculates the option value of a call, i.e., the payoff max(S(T)-K,0) P, where S follows a * normal process with constant volatility, i.e., a Bachelier model. * * @param forward The forward of the underlying. * @param volatility The Bachelier volatility. * @param optionMaturity The option maturity T. * @param optionStrike The option strike. * @param payoffUnit The payoff unit (e.g., the discount factor) * @return Returns the value of a European call option under the Bachelier model. */ public static RandomVariableInterface bachelierOptionValue( RandomVariableInterface forward, RandomVariableInterface volatility, double optionMaturity, double optionStrike, RandomVariableInterface payoffUnit) { if (optionMaturity < 0) { return forward.mult(0.0); } else { RandomVariableInterface integratedVolatility = volatility.mult(Math.sqrt(optionMaturity)); RandomVariableInterface dPlus = forward.sub(optionStrike).div(integratedVolatility); UnivariateFunction cummulativeNormal = new UnivariateFunction() { public double value(double x) { return NormalDistribution.cumulativeDistribution(x); } }; UnivariateFunction densityNormal = new UnivariateFunction() { public double value(double x) { return NormalDistribution.density(x); } }; RandomVariableInterface valueAnalytic = dPlus .apply(cummulativeNormal) .mult(forward.sub(optionStrike)) .add(dPlus.apply(densityNormal).mult(integratedVolatility)) .mult(payoffUnit); return valueAnalytic; } }
public RandomVariableInterface barrier( RandomVariableInterface trigger, RandomVariableInterface valueIfTriggerNonNegative, double valueIfTriggerNegative) { return this.barrier( trigger, valueIfTriggerNonNegative, new RandomVariable(valueIfTriggerNonNegative.getFiltrationTime(), valueIfTriggerNegative)); }
/* (non-Javadoc) * @see net.finmath.stochastic.RandomVariableInterface#subRatio(net.finmath.stochastic.RandomVariableInterface, net.finmath.stochastic.RandomVariableInterface) */ public RandomVariableInterface subRatio( RandomVariableInterface numerator, RandomVariableInterface denominator) { // Set time of this random variable to maximum of time with respect to which measurability is // known. double newTime = Math.max(Math.max(time, numerator.getFiltrationTime()), denominator.getFiltrationTime()); if (isDeterministic() && numerator.isDeterministic() && denominator.isDeterministic()) { double newValueIfNonStochastic = valueIfNonStochastic - (numerator.get(0) / denominator.get(0)); return new RandomVariable(newTime, newValueIfNonStochastic); } else { double[] newRealizations = new double[Math.max(Math.max(size(), numerator.size()), denominator.size())]; for (int i = 0; i < newRealizations.length; i++) newRealizations[i] = get(i) - numerator.get(i) / denominator.get(i); return new RandomVariable(newTime, newRealizations); } }
/** * This static method calculated the gamma of a call option under a Black-Scholes model * * @param initialStockValue The initial value of the underlying, i.e., the spot. * @param riskFreeRate The risk free rate of the bank account numerarie. * @param volatility The Black-Scholes volatility. * @param optionMaturity The option maturity T. * @param optionStrike The option strike. * @return The gamma of the option */ public static RandomVariableInterface blackScholesOptionGamma( RandomVariableInterface initialStockValue, RandomVariableInterface riskFreeRate, RandomVariableInterface volatility, double optionMaturity, double optionStrike) { if (optionStrike <= 0.0 || optionMaturity <= 0.0) { // The Black-Scholes model does not consider it being an option return initialStockValue.mult(0.0); } else { // Calculate gamma RandomVariableInterface dPlus = initialStockValue .div(optionStrike) .log() .add(volatility.squared().mult(0.5).add(riskFreeRate).mult(optionMaturity)) .div(volatility) .div(Math.sqrt(optionMaturity)); RandomVariableInterface gamma = dPlus .squared() .mult(-0.5) .exp() .div( initialStockValue .mult(volatility) .mult(Math.sqrt(2.0 * Math.PI * optionMaturity))); return gamma; } }
@Override public double getAverage(RandomVariableInterface probabilities) { if (isDeterministic()) return valueIfNonStochastic; if (size() == 0) return Double.NaN; /* * Kahan summation on (realizations[i] * probabilities.get(i)) */ double sum = 0.0; double error = 0.0; // Running error compensation for (int i = 0; i < realizations.length; i++) { double value = realizations[i] * probabilities.get(i) - error; // Error corrected value double newSum = sum + value; // New sum error = (newSum - sum) - value; // New numerical error sum = newSum; } return sum / realizations.length; }
/* (non-Javadoc) * @see net.finmath.stochastic.RandomVariableInterface#floor(net.finmath.stochastic.RandomVariableInterface) */ public RandomVariableInterface floor(RandomVariableInterface randomVariable) { // Set time of this random variable to maximum of time with respect to which measurability is // known. double newTime = Math.max(time, randomVariable.getFiltrationTime()); if (isDeterministic() && randomVariable.isDeterministic()) { double newValueIfNonStochastic = FastMath.max(valueIfNonStochastic, randomVariable.get(0)); return new RandomVariable(newTime, newValueIfNonStochastic); } else if (isDeterministic()) return randomVariable.floor(this); else { double[] newRealizations = new double[Math.max(size(), randomVariable.size())]; for (int i = 0; i < newRealizations.length; i++) newRealizations[i] = FastMath.max(realizations[i], randomVariable.get(i)); return new RandomVariable(newTime, newRealizations); } }
@Override public double getVariance(RandomVariableInterface probabilities) { if (isDeterministic()) return 0.0; if (size() == 0) return Double.NaN; double average = getAverage(probabilities); /* * Kahan summation on (realizations[i] - average)^2 * probabilities.get(i) */ double sum = 0.0; double errorOfSum = 0.0; for (int i = 0; i < realizations.length; i++) { double value = (realizations[i] - average) * (realizations[i] - average) * probabilities.get(i) - errorOfSum; double newSum = sum + value; errorOfSum = (newSum - sum) - value; sum = newSum; } return sum; }
/* (non-Javadoc) * @see net.finmath.stochastic.RandomVariableInterface#addProduct(net.finmath.stochastic.RandomVariableInterface, double) */ public RandomVariableInterface addProduct(RandomVariableInterface factor1, double factor2) { // Set time of this random variable to maximum of time with respect to which measurability is // known. double newTime = Math.max(time, factor1.getFiltrationTime()); if (isDeterministic() && factor1.isDeterministic()) { double newValueIfNonStochastic = valueIfNonStochastic + (factor1.get(0) * factor2); return new RandomVariable(newTime, newValueIfNonStochastic); } else if (isDeterministic() && !factor1.isDeterministic()) { double[] factor1Realizations = factor1.getRealizations(); double[] newRealizations = new double[Math.max(size(), factor1.size())]; for (int i = 0; i < newRealizations.length; i++) newRealizations[i] = valueIfNonStochastic + factor1Realizations[i] * factor2; return new RandomVariable(newTime, newRealizations); } else if (!isDeterministic() && factor1.isDeterministic()) { double factor1Value = factor1.get(0); double[] newRealizations = new double[Math.max(size(), factor1.size())]; for (int i = 0; i < newRealizations.length; i++) newRealizations[i] = realizations[i] + factor1Value * factor2; return new RandomVariable(newTime, newRealizations); } else { double[] factor1Realizations = factor1.getRealizations(); double[] newRealizations = new double[Math.max(size(), factor1.size())]; for (int i = 0; i < newRealizations.length; i++) newRealizations[i] = realizations[i] + factor1Realizations[i] * factor2; return new RandomVariable(newTime, newRealizations); } }
/* (non-Javadoc) * @see net.finmath.stochastic.RandomVariableInterface#barrier(net.finmath.stochastic.RandomVariableInterface, net.finmath.stochastic.RandomVariableInterface, net.finmath.stochastic.RandomVariableInterface) */ public RandomVariableInterface barrier( RandomVariableInterface trigger, RandomVariableInterface valueIfTriggerNonNegative, RandomVariableInterface valueIfTriggerNegative) { // Set time of this random variable to maximum of time with respect to which measurability is // known. double newTime = Math.max(time, trigger.getFiltrationTime()); newTime = Math.max(newTime, valueIfTriggerNonNegative.getFiltrationTime()); newTime = Math.max(newTime, valueIfTriggerNegative.getFiltrationTime()); if (isDeterministic() && trigger.isDeterministic() && valueIfTriggerNonNegative.isDeterministic() && valueIfTriggerNegative.isDeterministic()) { double newValueIfNonStochastic = trigger.get(0) >= 0 ? valueIfTriggerNonNegative.get(0) : valueIfTriggerNegative.get(0); return new RandomVariable(newTime, newValueIfNonStochastic); } else { int numberOfPaths = Math.max( Math.max(trigger.size(), valueIfTriggerNonNegative.size()), valueIfTriggerNegative.size()); double[] newRealizations = new double[numberOfPaths]; for (int i = 0; i < newRealizations.length; i++) { newRealizations[i] = trigger.get(i) >= 0.0 ? valueIfTriggerNonNegative.get(i) : valueIfTriggerNegative.get(i); } return new RandomVariable(newTime, newRealizations); } }
/* (non-Javadoc) * @see net.finmath.stochastic.RandomVariableInterface#discount(net.finmath.stochastic.RandomVariableInterface, double) */ public RandomVariableInterface discount(RandomVariableInterface rate, double periodLength) { // Set time of this random variable to maximum of time with respect to which measurability is // known. double newTime = Math.max(time, rate.getFiltrationTime()); if (isDeterministic() && rate.isDeterministic()) { double newValueIfNonStochastic = valueIfNonStochastic / (1 + rate.get(0) * periodLength); return new RandomVariable(newTime, newValueIfNonStochastic); } else if (isDeterministic() && !rate.isDeterministic()) { double[] rateRealizations = rate.getRealizations(); double[] newRealizations = new double[Math.max(size(), rate.size())]; for (int i = 0; i < newRealizations.length; i++) newRealizations[i] = valueIfNonStochastic / (1.0 + rateRealizations[i] * periodLength); return new RandomVariable(newTime, newRealizations); } else if (!isDeterministic() && rate.isDeterministic()) { double rateValue = rate.get(0); double[] newRealizations = new double[Math.max(size(), rate.size())]; for (int i = 0; i < newRealizations.length; i++) newRealizations[i] = realizations[i] / (1.0 + rateValue * periodLength); return new RandomVariable(newTime, newRealizations); } else { double[] rateRealizations = rate.getRealizations(); double[] newRealizations = new double[Math.max(size(), rate.size())]; for (int i = 0; i < newRealizations.length; i++) newRealizations[i] = realizations[i] / (1.0 + rateRealizations[i] * periodLength); return new RandomVariable(newTime, newRealizations); } }
/** * Create a random variable from a given other implementation of <code>RandomVariableInterface * </code>. * * @param value Object implementing <code>RandomVariableInterface</code>. */ public RandomVariable(RandomVariableInterface value) { super(); this.time = value.getFiltrationTime(); this.realizations = value.isDeterministic() ? null : value.getRealizations(); this.valueIfNonStochastic = value.isDeterministic() ? value.get(0) : Double.NaN; }
@Override public RandomVariableInterface applyStateSpaceTransform( int componentIndex, RandomVariableInterface randomVariable) { return randomVariable.exp(); }