finmath / finmath-lib

Mathematical Finance Library: Algorithms and methodologies related to mathematical finance.
Apache License 2.0
495 stars 169 forks source link

How to calcualte black-Scholes option value for put options? #12

Closed mrt181 closed 8 years ago

mrt181 commented 8 years ago

In net.finmath.functions.AnalyticFormulas I can find functions to calculate different option values for call options using black-scholes.

But there is no function to calculate the option value of a put option? Am I missing something?

cfries commented 8 years ago

I assume the reason is that the valuation for a put can be done via the call using put-call-parity: valuePut = valueCall - (forward-strike)*payoffUnit

I consider adding a flag to value a put, if it is convenient.

mrt181 commented 8 years ago

ok, using that formula and these inputs I get a value which looks wrong.

I have some error but where?

double marketprice = 12.35d;
Double interestRate = 0.02d;
Double volatility = 0.02d;
double runtime = 7.466681599999999d; // hours
double strikeprice = 12.5d;

protected double getOptionValue(double marketprice, Double interestRate, Double volatility, double runtime, double strikeprice) {
    double forward = strikeprice * Math.exp(interestRate * runtime); // 14.513253643305523
    double payoff = Math.exp(-interestRate * runtime); // 0.8612817158174474
    double callValue = blackScholesOptionValue(marketprice, interestRate, volatility, runtime, strikeprice); // 1.585196389882374
    double putValue = callValue - forward * payoff; // -10.914803610117627
    return putValue;
  }
cfries commented 8 years ago

I just had a quick look.

First, the formula is double putValue = callValue - (forward-strikeprice) * payoff; but your implementation is double putValue = callValue - forward * payoff;

Second, I believe there is an error in the calculation of the forward. The forward is double forward = marketprice * Math.exp(interestRate * runtime); but your implementation is double forward = strikeprice * Math.exp(interestRate * runtime);

Also, the comment after runtime (maturity) is "hours", but note that interest rates and volatility are often annualized, in which case runtime is a year fraction (years). Of course, if you have another definition of interest rates and volatility, then the definition of time may be different (but 2% per hour looks like a high rate). I assume 7.4667 are approximately 7 and a half year.

mrt181 commented 8 years ago

OK, i miss read (forward-strike) as a variable instead of an operation and fixed the error. I got the hour part from the project owner.

I have changed the method - I have two implementations, one for put and one for call values

  /**
   * @return the put option value
   */
  @Override
  protected double getOptionValue(double marketprice, double interestRate, double volatility, double runtime, double strikeprice) {
    double callMarketprice = strikeprice;
    double forward = marketprice * Math.exp(interestRate * runtime);
    double callStrikeprice = marketprice;
    double payoff = Math.exp(-interestRate * runtime);
    double callValue = blackScholesOptionValue(callMarketprice, interestRate, volatility, runtime, callStrikeprice);
    double putValue = callValue - (forward - strikeprice) * payoff;
    LOGGER.info("bid price (marketprice): {}", marketprice);
    LOGGER.info("ask price              : {}", callMarketprice);
    LOGGER.info("forward = {} * {}", marketprice, Math.exp(interestRate * runtime));
    LOGGER.info("forward                : {}", forward);
    LOGGER.info("strike                 : {}", strikeprice);
    LOGGER.info("payoff = Math.exp({} * {})", -interestRate, runtime);
    LOGGER.info("payoff                 : {}", payoff);
    LOGGER.info("callValue = blackScholesOptionValue({}, {}, {}, {}, {})", callMarketprice, interestRate, volatility, runtime, callStrikeprice);
    LOGGER.info("callValue              : {}", callValue);
    LOGGER.info("putValue = {} - ({} - {}) * {}", callValue, forward, strikeprice, payoff);
    LOGGER.info("putValue               : {}", putValue);
    return putValue;
  }

  /**
   * @return the call option value
   */
  @Override
  protected double getOptionValue(double marketprice, double interestRate, double volatility, double runtime, double strikeprice) {
    double callValue = blackScholesOptionValue(marketprice, interestRate, volatility, runtime, strikeprice);
    LOGGER.info("ask price (marketprice): {}", marketprice);
    LOGGER.info("bid price              : {}", strikeprice);
    LOGGER.info("strike                 : {}", strikeprice);
    LOGGER.info("callValue = blackScholesOptionValue({}, {}, {}, {}, {})", marketprice, interestRate, volatility, runtime, strikeprice);
    LOGGER.info("callValue              : {}", callValue);
    return callValue;
  }

This yields these results.

// putValue
bid price (marketprice): 9.0
ask price              : 13.775
forward = 9.0 * 1.4115203078039358
forward                : 12.703682770235423
strike                 : 13.775
payoff = Math.exp(-0.02 * 17.2333678)
payoff                 : 0.7084559779064141
callValue = blackScholesOptionValue(13.775, 0.02, 0.02, 17.2333678, 9.0)
callValue              : 7.398896198842273
putValue = 7.398896198842273 - (12.703682770235423 - 13.775) * 0.7084559779064141
putValue               : 8.157877294503127

// callValue
ask price (marketprice): 13.775
bid price              : 9.0
strike                 : 9.0
callValue = blackScholesOptionValue(13.775, 0.02, 0.02, 17.2333678, 9.0)
callValue              : 7.398896198842273

I hope I am doing it correctly now.

cfries commented 8 years ago

It is unclear to me why you swap "spot" and "strike" (you call it marketprice and strikeprice) for the call. Maybe I miss s.th. here. The best way to get confidence on your result is

mrt181 commented 8 years ago

I was simply doing it all wrong but i got it now. I get the maturity in minutes but convert it to years before passing it to this method.

  /**
   * @return the put option value
   */
  @Overrid
  protected double getOptionValue(double spot, double rate, double volatility, double maturity, double strike) {
    double payoff = Math.exp(-rate * maturity);
    double callValue = blackScholesOptionValue(spot, rate, volatility, maturity, strike);
    double putValue = strike * payoff - spot + callValue;
    return putValue;
  }
cfries commented 8 years ago

This looks better. I have added a method which features a flag "isCall" https://github.com/finmath/finmath-lib/blob/master/src/main/java/net/finmath/functions/AnalyticFormulas.java#L139