-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
0fe3cdf
commit 43ca1d7
Showing
3 changed files
with
429 additions
and
0 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,169 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Threading.Tasks; | ||
using ExcelDna.Integration; | ||
using Qwack.Options; | ||
using Qwack.Excel.Services; | ||
using Microsoft.Extensions.Logging; | ||
using Microsoft.Extensions.DependencyInjection; | ||
using Qwack.Options.VolSurfaces; | ||
using Qwack.Excel.Utils; | ||
using Qwack.Dates; | ||
using Qwack.Core.Basic; | ||
|
||
namespace Qwack.Excel.Options | ||
{ | ||
public class AsianFunctions | ||
{ | ||
private static readonly ILogger _logger = ContainerStores.GlobalContainer.GetService<ILoggerFactory>()?.CreateLogger<BlackFunctions>(); | ||
|
||
[ExcelFunction(Description = "Returns asian option PV using the Turnbull-Wakeman formula", Category = CategoryNames.Options, Name = CategoryNames.Options + "_" + nameof(TurnbullWakemanPV))] | ||
public static object TurnbullWakemanPV( | ||
[ExcelArgument(Description = "Time-to-expiry")] double T, | ||
[ExcelArgument(Description = "Time-to-average start")] double TavgStart, | ||
[ExcelArgument(Description = "Average-to-date")] double KnownAverage, | ||
[ExcelArgument(Description = "Strike")] double K, | ||
[ExcelArgument(Description = "Forward")] double F, | ||
[ExcelArgument(Description = "Discounting rate")] double R, | ||
[ExcelArgument(Description = "Volatility")] double V, | ||
[ExcelArgument(Description = "Call or Put")] string CP) | ||
{ | ||
return ExcelHelper.Execute(_logger, () => | ||
{ | ||
if (!Enum.TryParse(CP, out OptionType optType)) | ||
{ | ||
return $"Could not parse call or put flag - {CP}"; | ||
} | ||
return Qwack.Options.Asians.TurnbullWakeman.PV(F, KnownAverage, V, K, TavgStart, T, R, optType); | ||
}); | ||
} | ||
|
||
[ExcelFunction(Description = "Returns asian option PV using the Turnbull-Wakeman formula", Category = CategoryNames.Options, Name = CategoryNames.Options + "_" + nameof(TurnbullWakemanPV2))] | ||
public static object TurnbullWakemanPV2( | ||
[ExcelArgument(Description = "Evaluation Date")] DateTime EvalDate, | ||
[ExcelArgument(Description = "Average Start Date")] DateTime AverageStartDate, | ||
[ExcelArgument(Description = "Average End Date")] DateTime AverageEndDate, | ||
[ExcelArgument(Description = "Average-to-date")] double KnownAverage, | ||
[ExcelArgument(Description = "Strike")] double K, | ||
[ExcelArgument(Description = "Forward")] double F, | ||
[ExcelArgument(Description = "Discounting rate")] double R, | ||
[ExcelArgument(Description = "Volatility")] double V, | ||
[ExcelArgument(Description = "Call or Put")] string CP) | ||
{ | ||
return ExcelHelper.Execute(_logger, () => | ||
{ | ||
if (!Enum.TryParse(CP, out OptionType optType)) | ||
{ | ||
return $"Could not parse call or put flag - {CP}"; | ||
} | ||
return Qwack.Options.Asians.TurnbullWakeman.PV(F, KnownAverage, V, K, EvalDate, AverageStartDate, AverageEndDate, R, optType); | ||
}); | ||
} | ||
|
||
[ExcelFunction(Description = "Returns asian option delta using the Turnbull-Wakeman formula", Category = CategoryNames.Options, Name = CategoryNames.Options + "_" + nameof(TurnbullWakemanDelta))] | ||
public static object TurnbullWakemanDelta( | ||
[ExcelArgument(Description = "Evaluation Date")] DateTime EvalDate, | ||
[ExcelArgument(Description = "Average Start Date")] DateTime AverageStartDate, | ||
[ExcelArgument(Description = "Average End Date")] DateTime AverageEndDate, | ||
[ExcelArgument(Description = "Average-to-date")] double KnownAverage, | ||
[ExcelArgument(Description = "Strike")] double K, | ||
[ExcelArgument(Description = "Forward")] double F, | ||
[ExcelArgument(Description = "Discounting rate")] double R, | ||
[ExcelArgument(Description = "Volatility")] double V, | ||
[ExcelArgument(Description = "Call or Put")] string CP) | ||
{ | ||
return ExcelHelper.Execute(_logger, () => | ||
{ | ||
if (!Enum.TryParse(CP, out OptionType optType)) | ||
{ | ||
return $"Could not parse call or put flag - {CP}"; | ||
} | ||
return Qwack.Options.Asians.TurnbullWakeman.Delta(F, KnownAverage, V, K, EvalDate, AverageStartDate, AverageEndDate, R, optType); | ||
}); | ||
} | ||
|
||
[ExcelFunction(Description = "Returns strike for asian option with specified PV, using the Turnbull-Wakeman formula", Category = CategoryNames.Options, Name = CategoryNames.Options + "_" + nameof(TurnbullWakemanStrikeForPV))] | ||
public static object TurnbullWakemanStrikeForPV( | ||
[ExcelArgument(Description = "Evaluation Date")] DateTime EvalDate, | ||
[ExcelArgument(Description = "Average Start Date")] DateTime AverageStartDate, | ||
[ExcelArgument(Description = "Average End Date")] DateTime AverageEndDate, | ||
[ExcelArgument(Description = "Average-to-date")] double KnownAverage, | ||
[ExcelArgument(Description = "Target PV")] double PV, | ||
[ExcelArgument(Description = "Forward")] double F, | ||
[ExcelArgument(Description = "Discounting rate")] double R, | ||
[ExcelArgument(Description = "Volatility Surface")] string VolSurface, | ||
[ExcelArgument(Description = "Call or Put")] string CP) | ||
{ | ||
return ExcelHelper.Execute(_logger, () => | ||
{ | ||
if (!ContainerStores.GetObjectCache<IVolSurface>().TryGetObject(VolSurface, out var surface)) | ||
{ | ||
return $"Could not parse find vol surface {VolSurface} in the cache"; | ||
} | ||
if (!Enum.TryParse(CP, out OptionType optType)) | ||
{ | ||
return $"Could not parse call or put flag - {CP}"; | ||
} | ||
return Qwack.Options.Asians.TurnbullWakeman.StrikeForPV(PV, F, KnownAverage, surface.Value, EvalDate, AverageStartDate, AverageEndDate, R, optType); | ||
}); | ||
} | ||
|
||
[ExcelFunction(Description = "Returns asian option PV using the Clewlow/LME formula", Category = CategoryNames.Options, Name = CategoryNames.Options + "_" + nameof(ClewlowPV))] | ||
public static object ClewlowPV( | ||
[ExcelArgument(Description = "Evaluation Date")] DateTime EvalDate, | ||
[ExcelArgument(Description = "Average Start Date")] DateTime AverageStartDate, | ||
[ExcelArgument(Description = "Average End Date")] DateTime AverageEndDate, | ||
[ExcelArgument(Description = "Average-to-date")] double KnownAverage, | ||
[ExcelArgument(Description = "Strike")] double K, | ||
[ExcelArgument(Description = "Forward")] double F, | ||
[ExcelArgument(Description = "Discounting rate")] double R, | ||
[ExcelArgument(Description = "Volatility")] double V, | ||
[ExcelArgument(Description = "Call or Put")] string CP, | ||
[ExcelArgument(Description = "Fixing Calendar")] object FixingCalendar) | ||
{ | ||
|
||
return ExcelHelper.Execute(_logger, () => | ||
{ | ||
var fixCal = FixingCalendar.OptionalExcel<string>("Weekends"); | ||
if (!ContainerStores.SessionContainer.GetService<ICalendarProvider>().Collection.TryGetCalendar(fixCal, out var cal)) | ||
return $"Calendar {FixingCalendar} not found in cache"; | ||
if (!Enum.TryParse(CP, out OptionType optType)) | ||
{ | ||
return $"Could not parse call or put flag - {CP}"; | ||
} | ||
return Qwack.Options.Asians.LME_Clewlow.PV(F, KnownAverage, V, K, EvalDate, AverageStartDate, AverageEndDate, R, optType, cal); | ||
}); | ||
} | ||
|
||
[ExcelFunction(Description = "Returns asian option delta using the Clewlow/LME formula", Category = CategoryNames.Options, Name = CategoryNames.Options + "_" + nameof(ClewlowDelta))] | ||
public static object ClewlowDelta( | ||
[ExcelArgument(Description = "Evaluation Date")] DateTime EvalDate, | ||
[ExcelArgument(Description = "Average Start Date")] DateTime AverageStartDate, | ||
[ExcelArgument(Description = "Average End Date")] DateTime AverageEndDate, | ||
[ExcelArgument(Description = "Average-to-date")] double KnownAverage, | ||
[ExcelArgument(Description = "Strike")] double K, | ||
[ExcelArgument(Description = "Forward")] double F, | ||
[ExcelArgument(Description = "Discounting rate")] double R, | ||
[ExcelArgument(Description = "Volatility")] double V, | ||
[ExcelArgument(Description = "Call or Put")] string CP, | ||
[ExcelArgument(Description = "Fixing Calendar")] object FixingCalendar) | ||
{ | ||
|
||
return ExcelHelper.Execute(_logger, () => | ||
{ | ||
var fixCal = FixingCalendar.OptionalExcel<string>("Weekends"); | ||
if (!ContainerStores.SessionContainer.GetService<ICalendarProvider>().Collection.TryGetCalendar(fixCal, out var cal)) | ||
return $"Calendar {FixingCalendar} not found in cache"; | ||
if (!Enum.TryParse(CP, out OptionType optType)) | ||
{ | ||
return $"Could not parse call or put flag - {CP}"; | ||
} | ||
return Qwack.Options.Asians.LME_Clewlow.Delta(F, KnownAverage, V, K, EvalDate, AverageStartDate, AverageEndDate, R, optType, cal); | ||
}); | ||
} | ||
} | ||
} |
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,159 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Text; | ||
using Qwack.Dates; | ||
using Qwack.Core.Basic; | ||
using System.Linq; | ||
|
||
namespace Qwack.Options.Asians | ||
{ | ||
public static class LME_Clewlow | ||
{ | ||
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 System.Math.Max(knownAverage - K, 0); | ||
else | ||
return System.Math.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 RT = resetDates.Count; | ||
|
||
//Initialise Variables | ||
double E1 = 0.0, E2 = 0.0, E3 = 0.0, E4 = 0.0, E5 = 0.0; | ||
double DeltaTPrime = 0.0, DeltaT = 0.0; | ||
|
||
var FBar = forward; | ||
var tvs = resetDates.Count(x => x < evalDate); | ||
|
||
if (tvs > 0) | ||
DeltaTPrime = (resetDates[tvs + 1] - resetDates[tvs]).TotalDays / 365.0; | ||
else | ||
DeltaTPrime = ((resetDates[0] - evalDate).TotalDays) / 365.0; | ||
|
||
if (tvs + 2 >= RT) | ||
DeltaT = DeltaTPrime; | ||
else | ||
DeltaT = (avgEndDate - resetDates[tvs+1]).TotalDays / 365.0 / (RT - tvs); | ||
|
||
var Ak = knownAverage * tvs / RT; | ||
E5 = 2 * Ak * FBar * (RT - tvs) / RT + (Ak * Ak); | ||
|
||
if (tvs < RT) | ||
{ | ||
E4 = (1 + System.Math.Exp(sigma * sigma * DeltaT)) * (System.Math.Exp((RT - tvs) * sigma * sigma * DeltaT) - 1); | ||
E4 += 2 * (RT - tvs) * (1 - System.Math.Exp(sigma * sigma * DeltaT)); | ||
E4 /= (System.Math.Pow(System.Math.Exp(sigma * sigma * DeltaT) - 1, 2)); | ||
} | ||
else | ||
E4 = 1; | ||
|
||
|
||
E3 = System.Math.Pow(FBar / RT, 2) * System.Math.Exp(sigma * sigma * DeltaTPrime); | ||
E2 = E3 * E4 + E5; | ||
E1 = FBar * (RT - tvs) / RT + Ak; | ||
|
||
var EA = Ak + FBar * (RT - tvs) / RT; | ||
|
||
var b = System.Math.Log(E2) - 2 * System.Math.Log(E1); | ||
var d1 = (System.Math.Log(EA / K) + 0.5 * b) / System.Math.Sqrt(b); | ||
var d2 = d1 - System.Math.Sqrt(b); | ||
|
||
|
||
var df = System.Math.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; | ||
} | ||
|
||
} | ||
|
||
public static double Delta(double forward, double knownAverage, double sigma, double K, DateTime evalDate, DateTime avgStartDate, DateTime avgEndDate, double riskFree, OptionType callPut, Calendar fixingCalendar) | ||
{ | ||
//Build Vector of Observation Dates | ||
var resetDates = avgStartDate.BusinessDaysInPeriod(avgEndDate, fixingCalendar); | ||
var RT = resetDates.Count; | ||
|
||
if (avgEndDate == evalDate) | ||
if (callPut == OptionType.C) | ||
return knownAverage > K ? 1.0 / RT : 0; | ||
else | ||
return knownAverage < K ? -1.0 / RT : 0; | ||
else if (avgEndDate < evalDate) | ||
return 0; | ||
else if (avgStartDate == avgEndDate) | ||
{ | ||
var t = (avgEndDate - evalDate).TotalDays / 365.0; | ||
return BlackFunctions.BlackDelta(forward, K, riskFree, t, sigma, callPut); | ||
} | ||
|
||
|
||
//Initialise Variables | ||
double E1 = 0.0, E2 = 0.0, E3 = 0.0, E4 = 0.0, E5 = 0.0; | ||
double DeltaTPrime = 0.0, DeltaT = 0.0; | ||
|
||
var FBar = forward; | ||
var tvs = resetDates.Count(x => x < evalDate); | ||
|
||
if (tvs > 0) | ||
DeltaTPrime = (resetDates[tvs + 1] - resetDates[tvs]).TotalDays / 365.0; | ||
else | ||
DeltaTPrime = ((resetDates[0] - evalDate).TotalDays) / 365.0; | ||
|
||
if (tvs + 2 >= RT) | ||
DeltaT = DeltaTPrime; | ||
else | ||
DeltaT = (avgEndDate - resetDates[tvs + 1]).TotalDays / 365.0 / (RT - tvs); | ||
|
||
var Ak = knownAverage * tvs / RT; | ||
E5 = 2 * Ak * FBar * (RT - tvs) / RT + (Ak * Ak); | ||
|
||
if (tvs < RT) | ||
{ | ||
E4 = (1 + System.Math.Exp(sigma * sigma * DeltaT)) * (System.Math.Exp((RT - tvs) * sigma * sigma * DeltaT) - 1); | ||
E4 += 2 * (RT - tvs) * (1 - System.Math.Exp(sigma * sigma * DeltaT)); | ||
E4 /= (System.Math.Pow(System.Math.Exp(sigma * sigma * DeltaT) - 1, 2)); | ||
} | ||
else | ||
E4 = 1; | ||
|
||
|
||
E3 = System.Math.Pow(FBar / RT, 2) * System.Math.Exp(sigma * sigma * DeltaTPrime); | ||
E2 = E3 * E4 + E5; | ||
E1 = FBar * (RT - tvs) / RT + Ak; | ||
|
||
var EA = Ak + FBar * (RT - tvs) / RT; | ||
|
||
var b = System.Math.Log(E2) - 2 * System.Math.Log(E1); | ||
var d1 = (System.Math.Log(EA / K) + 0.5 * b) / System.Math.Sqrt(b); | ||
|
||
var df = System.Math.Exp(-riskFree * (avgEndDate - evalDate).TotalDays / 365.0); | ||
|
||
d1 = Math.Statistics.NormSDist(d1 * | ||
(callPut == OptionType.Call ? 1.0 : -1.0)); | ||
|
||
var delta = d1 * df; | ||
return delta; | ||
} | ||
} | ||
} |
Oops, something went wrong.