Skip to content

Commit

Permalink
Fix case where implied volatility is outside the initial range (#2)
Browse files Browse the repository at this point in the history
Co-authored-by: Zois Pagoulatos <[email protected]>
  • Loading branch information
SpellChucker and zoispag authored Feb 2, 2024
1 parent cfa12d9 commit 9657399
Show file tree
Hide file tree
Showing 3 changed files with 25 additions and 7 deletions.
5 changes: 4 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,10 @@
"format": "vendor/bin/php-cs-fixer fix --config .php_cs.dist.php --allow-risky=yes"
},
"config": {
"sort-packages": true
"sort-packages": true,
"allow-plugins": {
"pestphp/pest-plugin": true
}
},
"minimum-stability": "dev",
"prefer-stable": true
Expand Down
17 changes: 14 additions & 3 deletions src/Black76.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Kyos\OptionsCalculator;

use Exception;
use RuntimeException;

class Black76
{
Expand All @@ -15,7 +16,7 @@ class Black76
private float $interestRate;

/**
* @param float $interestRate Annual risk-free nterest rate, defaults to 0.01 (1%)
* @param float $interestRate Annual risk-free interest rate, defaults to 0.01 (1%)
*/
public function __construct(float $interestRate = 0.01)
{
Expand Down Expand Up @@ -99,6 +100,8 @@ public function getValues(
* @param float $marketPrice Option price
*
* @return float
*
* @throws RuntimeException
*/
private function impliedVolaUsingBisection(
string $type,
Expand All @@ -113,8 +116,16 @@ private function impliedVolaUsingBisection(

$volMin = 0.00001;
$volMax = 5;
$volGuess = $volMin;

$valueMin = $this->getValues($type, $underlyingPrice, $strikePrice, $timeToMaturity, $volMin)['value'];
$valueMax = $this->getValues($type, $underlyingPrice, $strikePrice, $timeToMaturity, $volMax)['value'];

do {
if ($marketPrice < $valueMin || $marketPrice > $valueMax) {
throw new RuntimeException("Implied volatility could not be found in the range {$volMin} - {$volMax}.");
}

while (++$i < $maxIterations && ($volMax - $volMin > $epsilon || $valueMin != $valueMax)) {
$valueMin = $this->getValues($type, $underlyingPrice, $strikePrice, $timeToMaturity, $volMin)['value'];
$valueMax = $this->getValues($type, $underlyingPrice, $strikePrice, $timeToMaturity, $volMax)['value'];

Expand All @@ -126,7 +137,7 @@ private function impliedVolaUsingBisection(
} else {
$volMax = $volGuess;
}
} while (++$i <= $maxIterations && $volMax - $volMin > $epsilon);
}

return $volGuess;
}
Expand Down
10 changes: 7 additions & 3 deletions tests/Black76Test.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,15 @@
$this->expect((new Black76())->getImpliedVolatility(Black76::PUT, 10.5, 12, 30 / 365.25, 1.7398186455107651))->toEqualWithDelta(0.60, 0.00000000000001);
});

// Expections
it('throws expection if we try to derive implied volatility using newton rapshon', function () {
it('throws exception if implied volatility is outside the initial range', function () {
(new Black76())->getImpliedVolatility(Black76::PUT, 10.5, 12, 30 / 365.25, 1);
})->throws(RuntimeException::class);

// Exceptions
it('throws exception if we try to derive implied volatility using newton rapshon', function () {
(new Black76())->getImpliedVolatility(Black76::PUT, 10.5, 12, 30 / 365.25, 0.240, Black76::METHOD_NEWTON_RAPHSON);
})->throws('Not implemented');

it('throws expection if we try to derive implied volatility using wrong method', function () {
it('throws exception if we try to derive implied volatility using wrong method', function () {
(new Black76())->getImpliedVolatility(Black76::PUT, 10.5, 12, 30 / 365.25, 0.240, 99);
})->throws('Wrong method 99 or not supported');

0 comments on commit 9657399

Please sign in to comment.