-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* partial securitydata * Security data cache done. need data cache general still * Pass the option collection up * start data cache * shell methods * progress * nullability * more * delegate cleanup * Allow supplemental data update logic * gets done * builds. time to test data structures * composite sample app * covariance * create new ones * internal data cache implementation * internalize internal sets * Allow 2 configs at the same time. Giggity. * overflow bug * optional candle * checkpoint * partial cleanup * Compile but need values filled from api * greek supplementary data fetch * greek sample app * tweak dividend call * nullability * hookups * working * perf improvements * more hooks * back to default sample app * braces formatting * more formatting * unused var * integrate the cache * No async (#45) * WIP on SMS/RealtimeGreeks * composite sample app * unused field * stack only greek struct * nullability * Plugin for consolidation of hookups * add doc comments * Startup load, and offload the followup event without using closure * troubleshoot httpclient interferring with websocket * cleanup and doc part 1 * more doc updates * start on readme * readme * detail
- Loading branch information
1 parent
ac56c1a
commit 94f5221
Showing
36 changed files
with
3,270 additions
and
289 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
209 changes: 209 additions & 0 deletions
209
Intrinio.Realtime/Composite/BlackScholesGreekCalculator.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,209 @@ | ||
using System; | ||
using System.Runtime.CompilerServices; | ||
|
||
namespace Intrinio.Realtime.Composite; | ||
|
||
public static class BlackScholesGreekCalculator | ||
{ | ||
private const double LOW_VOL = 0.0D; | ||
private const double HIGH_VOL = 5.0D; | ||
private const double VOL_TOLERANCE = 0.0001D; | ||
private const double MIN_Z_SCORE = -8.0D; | ||
private const double MAX_Z_SCORE = 8.0D; | ||
|
||
|
||
public static Greek Calculate( double riskFreeInterestRate, | ||
double dividendYield, | ||
Intrinio.Realtime.Equities.Trade underlyingTrade, | ||
Intrinio.Realtime.Options.Trade latestOptionTrade, | ||
Intrinio.Realtime.Options.Quote latestOptionQuote) | ||
{ | ||
if (latestOptionQuote.AskPrice <= 0.0D || latestOptionQuote.BidPrice <= 0.0D) | ||
return new Greek(0.0D, 0.0D, 0.0D, 0.0D, 0.0D, false); | ||
if (riskFreeInterestRate <= 0.0D) | ||
return new Greek(0.0D, 0.0D, 0.0D, 0.0D, 0.0D, false);; | ||
|
||
bool isPut = latestOptionTrade.IsPut(); | ||
double underlyingPrice = underlyingTrade.Price; | ||
double strike = latestOptionTrade.GetStrikePrice(); | ||
double daysToExpiration = GetDaysToExpiration(latestOptionTrade, latestOptionQuote); | ||
double marketPrice = (latestOptionQuote.AskPrice + latestOptionQuote.BidPrice) / 2.0D; | ||
double impliedVolatility = CalcImpliedVolatility(isPut, underlyingPrice, strike, daysToExpiration, riskFreeInterestRate, dividendYield, marketPrice); | ||
double sigma = impliedVolatility; | ||
double delta = CalcDelta(isPut, underlyingPrice, strike, daysToExpiration, riskFreeInterestRate, dividendYield, marketPrice, sigma); | ||
double gamma = CalcGamma(underlyingPrice, strike, daysToExpiration, riskFreeInterestRate, dividendYield, marketPrice, sigma); | ||
double theta = CalcTheta(isPut, underlyingPrice, strike, daysToExpiration, riskFreeInterestRate, dividendYield, marketPrice, sigma); | ||
double vega = CalcVega(underlyingPrice, strike, daysToExpiration, riskFreeInterestRate, dividendYield, marketPrice, sigma); | ||
|
||
return new Greek(impliedVolatility, delta, gamma, theta, vega, true); | ||
} | ||
|
||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
private static double CalcImpliedVolatilityCall(double underlyingPrice, double strike, double daysToExpiration, double riskFreeInterestRate, double dividendYield, double marketPrice) | ||
{ | ||
double low = LOW_VOL, high = HIGH_VOL; | ||
while ((high - low) > VOL_TOLERANCE) | ||
{ | ||
if (CalcPriceCall(underlyingPrice, strike, daysToExpiration, riskFreeInterestRate, (high + low) / 2.0D, dividendYield) > marketPrice) | ||
high = (high + low) / 2.0D; | ||
else | ||
low = (high + low) / 2.0D; | ||
} | ||
|
||
return (high + low) / 2.0D; | ||
} | ||
|
||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
private static double CalcImpliedVolatilityPut(double underlyingPrice, double strike, double daysToExpiration, double riskFreeInterestRate, double dividendYield, double marketPrice) | ||
{ | ||
double low = LOW_VOL, high = HIGH_VOL; | ||
while ((high - low) > VOL_TOLERANCE) | ||
{ | ||
if (CalcPricePut(underlyingPrice, strike, daysToExpiration, riskFreeInterestRate, (high + low) / 2.0D, dividendYield) > marketPrice) | ||
high = (high + low) / 2.0D; | ||
else | ||
low = (high + low) / 2.0D; | ||
} | ||
|
||
return (high + low) / 2.0D; | ||
} | ||
|
||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
private static double CalcImpliedVolatility(bool isPut, double underlyingPrice, double strike, double daysToExpiration, double riskFreeInterestRate, double dividendYield, double marketPrice) | ||
{ | ||
return isPut | ||
? CalcImpliedVolatilityPut(underlyingPrice, strike, daysToExpiration, riskFreeInterestRate, dividendYield, marketPrice) | ||
: CalcImpliedVolatilityCall(underlyingPrice, strike, daysToExpiration, riskFreeInterestRate, dividendYield, marketPrice); | ||
} | ||
|
||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
private static double CalcDeltaCall(double underlyingPrice, double strike, double daysToExpiration, double riskFreeInterestRate, double dividendYield, double marketPrice, double sigma) | ||
{ | ||
return NormalSDist( D1( underlyingPrice, strike, daysToExpiration, riskFreeInterestRate, sigma, dividendYield ) ); | ||
} | ||
|
||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
private static double CalcDeltaPut(double underlyingPrice, double strike, double daysToExpiration, double riskFreeInterestRate, double dividendYield, double marketPrice, double sigma) | ||
{ | ||
return CalcDeltaCall( underlyingPrice, strike, daysToExpiration, riskFreeInterestRate, dividendYield, marketPrice, sigma) - 1; | ||
} | ||
|
||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
private static double CalcDelta(bool isPut, double underlyingPrice, double strike, double daysToExpiration, double riskFreeInterestRate, double dividendYield, double marketPrice, double sigma) | ||
{ | ||
return isPut | ||
? CalcDeltaPut(underlyingPrice, strike, daysToExpiration, riskFreeInterestRate, dividendYield, marketPrice, sigma) | ||
: CalcDeltaCall(underlyingPrice, strike, daysToExpiration, riskFreeInterestRate, dividendYield, marketPrice, sigma); | ||
} | ||
|
||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
private static double CalcGamma(double underlyingPrice, double strike, double daysToExpiration, double riskFreeInterestRate, double dividendYield, double marketPrice, double sigma) | ||
{ | ||
return Phi( D1( underlyingPrice, strike, daysToExpiration, riskFreeInterestRate, sigma, dividendYield ) ) / ( underlyingPrice * sigma * System.Math.Sqrt(daysToExpiration) ); | ||
} | ||
|
||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
private static double CalcThetaCall(double underlyingPrice, double strike, double daysToExpiration, double riskFreeInterestRate, double dividendYield, double marketPrice, double sigma) | ||
{ | ||
double term1 = underlyingPrice * Phi( D1( underlyingPrice, strike, daysToExpiration, riskFreeInterestRate, sigma, dividendYield ) ) * sigma / ( 2.0D * System.Math.Sqrt(daysToExpiration) ); | ||
double term2 = riskFreeInterestRate * strike * System.Math.Exp(-1.0D * riskFreeInterestRate * daysToExpiration) * NormalSDist( D2( underlyingPrice, strike, daysToExpiration, riskFreeInterestRate, sigma, dividendYield ) ); | ||
return ( -term1 - term2 ) / 365.25D; | ||
} | ||
|
||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
private static double CalcThetaPut(double underlyingPrice, double strike, double daysToExpiration, double riskFreeInterestRate, double dividendYield, double marketPrice, double sigma) | ||
{ | ||
double term1 = underlyingPrice * Phi( D1( underlyingPrice, strike, daysToExpiration, riskFreeInterestRate, sigma, dividendYield ) ) * sigma / ( 2.0D * System.Math.Sqrt(daysToExpiration) ); | ||
double term2 = riskFreeInterestRate * strike * System.Math.Exp(-1.0D * riskFreeInterestRate * daysToExpiration) * NormalSDist( - D2( underlyingPrice, strike, daysToExpiration, riskFreeInterestRate, sigma, dividendYield ) ); | ||
return ( -term1 + term2 ) / 365.25D; | ||
} | ||
|
||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
private static double CalcTheta(bool isPut, double underlyingPrice, double strike, double daysToExpiration, double riskFreeInterestRate, double dividendYield, double marketPrice, double sigma) | ||
{ | ||
return isPut | ||
? CalcThetaPut(underlyingPrice, strike, daysToExpiration, riskFreeInterestRate, dividendYield, marketPrice, sigma) | ||
: CalcThetaCall(underlyingPrice, strike, daysToExpiration, riskFreeInterestRate, dividendYield, marketPrice, sigma); | ||
} | ||
|
||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
private static double CalcVega(double underlyingPrice, double strike, double daysToExpiration, double riskFreeInterestRate, double dividendYield, double marketPrice, double sigma) | ||
{ | ||
return 0.01D * underlyingPrice * System.Math.Sqrt(daysToExpiration) * Phi(D1(underlyingPrice, strike, daysToExpiration, riskFreeInterestRate, sigma, dividendYield)); | ||
} | ||
|
||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
private static double D1(double underylyingPrice, double strike, double daysToExpiration, double riskFreeInterestRate, double sigma, double dividendYield) | ||
{ | ||
double numerator = ( System.Math.Log(underylyingPrice / strike) + (riskFreeInterestRate - dividendYield + 0.5D * System.Math.Pow(sigma, 2.0D) ) * daysToExpiration); | ||
double denominator = ( sigma * System.Math.Sqrt(daysToExpiration)); | ||
return numerator / denominator; | ||
} | ||
|
||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
private static double D2(double underylyingPrice, double strike, double daysToExpiration, double riskFreeInterestRate, double sigma, double dividendYield) | ||
{ | ||
return D1( underylyingPrice, strike, daysToExpiration, riskFreeInterestRate, sigma, dividendYield ) - ( sigma * System.Math.Sqrt(daysToExpiration) ); | ||
} | ||
|
||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
private static double NormalSDist(double z) | ||
{ | ||
if (z < MIN_Z_SCORE) | ||
return 0.0D; | ||
if (z > MAX_Z_SCORE) | ||
return 1.0D; | ||
double i = 3.0D, sum = 0.0D, term = z; | ||
while ((sum + term) != sum) | ||
{ | ||
sum += term; | ||
term = term * z * z / i; | ||
i += 2.0D; | ||
} | ||
return 0.5D + sum * Phi(z); | ||
} | ||
|
||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
private static double Phi(double x) | ||
{ | ||
double numerator = System.Math.Exp(-1.0D * x*x / 2.0D); | ||
double denominator = System.Math.Sqrt(2.0D * System.Math.PI); | ||
return numerator / denominator; | ||
} | ||
|
||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
private static double CalcPriceCall(double underylyingPrice, double strike, double daysToExpiration, double riskFreeInterestRate, double sigma, double dividendYield) | ||
{ | ||
double d1 = D1( underylyingPrice, strike, daysToExpiration, riskFreeInterestRate, sigma, dividendYield ); | ||
double discountedUnderlying = System.Math.Exp(-1.0D * dividendYield * daysToExpiration) * underylyingPrice; | ||
double probabilityWeightedValueOfBeingExercised = discountedUnderlying * NormalSDist( d1 ); | ||
|
||
double d2 = d1 - ( sigma * System.Math.Sqrt(daysToExpiration) ); | ||
double discountedStrike = System.Math.Exp(-1.0D * riskFreeInterestRate * daysToExpiration) * strike; | ||
double probabilityWeightedValueOfDiscountedStrike = discountedStrike * NormalSDist( d2 ); | ||
|
||
return probabilityWeightedValueOfBeingExercised - probabilityWeightedValueOfDiscountedStrike; | ||
} | ||
|
||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
private static double CalcPricePut(double underylyingPrice, double strike, double daysToExpiration, double riskFreeInterestRate, double sigma, double dividendYield) | ||
{ | ||
double d2 = D2( underylyingPrice, strike, daysToExpiration, riskFreeInterestRate, sigma, dividendYield ); | ||
double discountedStrike = strike * System.Math.Exp(-1.0D * riskFreeInterestRate * daysToExpiration); | ||
double probabiltityWeightedValueOfDiscountedStrike = discountedStrike * NormalSDist( -1.0D * d2 ); | ||
|
||
double d1 = d2 + ( sigma * System.Math.Sqrt(daysToExpiration) ); | ||
double discountedUnderlying = underylyingPrice * System.Math.Exp(-1.0D * dividendYield * daysToExpiration); | ||
double probabilityWeightedValueOfBeingExercised = discountedUnderlying * NormalSDist( -1.0D * d1 ); | ||
|
||
return probabiltityWeightedValueOfDiscountedStrike - probabilityWeightedValueOfBeingExercised; | ||
} | ||
|
||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | ||
private static double GetDaysToExpiration(Intrinio.Realtime.Options.Trade latestOptionTrade, Intrinio.Realtime.Options.Quote latestOptionQuote) | ||
{ | ||
double latestActivity = System.Math.Max(latestOptionTrade.Timestamp, latestOptionQuote.Timestamp); | ||
double expiration = (latestOptionTrade.GetExpirationDate() - DateTime.UnixEpoch.ToUniversalTime()).TotalSeconds; | ||
return (expiration - latestActivity) / 86400.0D; //86400 is seconds in a day | ||
} | ||
} |
Oops, something went wrong.