Files
zszq-trs/YLErpDAL/Modules/VolatilityModule/SkewMapVolModule/SkewMapVolSurface.cs
T
2024-05-09 14:06:26 +08:00

148 lines
4.9 KiB
C#

using System.Data;
namespace YLErp.Modules.SkewMapVolModule
{
public struct SkewMapVolSurface
{
public SkewMapVolSurface(Dictionary<string, double> baseVol)
{
_baseVol = baseVol;
}
public double GetVol(double t, double k, double spot, bool isCall, bool isBuy, int var)
{
var basevol = GetBaseVol(t: t);
return GetVolWithBaseVol(baseVol: basevol, t: t, k: k, spot: spot, isCall: isCall, isBuy: isBuy, var: var);
}
public double GetVolWithBaseVol(double baseVol, double t, double k, double spot, bool isCall, bool isBuy, int var)
{
var moneyness = k / spot - 1;
if (isCall)
{
moneyness *= -1;
}
return InterpolateVol(term: t, moneyness: moneyness, basevol: baseVol, isBuy: isBuy, var: var);
}
public double GetBaseVol(double t)
{
var m = 21;
double vol;
if (t <= 21)
{
vol = _baseVol["1M"];
}
else if (t > m && t < 3 * m)
{
vol = ((3 * m - t) * _baseVol["1M"] + (t - m) * _baseVol["3M"]) / (2 * m);
}
else if (t > 3 * m)
{
vol = ((6 * m - t) * _baseVol["3M"] + (t - 3 * m) * _baseVol["6M"]) / (3 * m);
}
else
{
vol = _baseVol["6M"];
}
return vol;
}
private double InterpolateVol(double term, double moneyness, double basevol, bool isBuy, int var)
{
var index = new Dictionary<int, double>();
var weight = new Dictionary<int, double>();
if (term <= 10)
{
index[1] = index[2] = 10;
weight[1] = weight[2] = 0.5;
}
else if (term >= 80)
{
index[1] = index[2] = 80;
weight[1] = weight[2] = 0.5;
}
else
{
index[1] = Math.Max(10, Math.Floor(term / 20) * 20);
index[2] = (Math.Floor(term / 20) + 1) * 20;
weight[1] = (index[2] - term) / (index[2] - index[1]);
weight[2] = (term - index[1]) / (index[2] - index[1]);
}
if (moneyness <= -0.1)
{
index[3] = index[4] = -0.1;
weight[3] = weight[4] = 0.5;
}
else if (moneyness >= 0.1)
{
index[3] = index[4] = 0.1;
weight[3] = weight[4] = 0.5;
}
else
{
index[3] = Math.Floor(moneyness / 0.01) * 0.01;
index[4] = (Math.Floor(moneyness / 0.01) + 1) * 0.01;
weight[3] = (index[4] - moneyness) / (index[4] - index[3]);
weight[4] = (moneyness - index[3]) / (index[4] - index[3]);
}
if (basevol <= 0.1)
{
index[5] = index[6] = 0.1;
weight[5] = weight[6] = 0.5 * basevol / 0.1;
}
else if (basevol >= 0.4)
{
index[5] = index[6] = 0.4;
weight[5] = weight[6] = 0.5 * basevol / 0.4;
}
else
{
index[5] = Math.Floor(basevol / 0.05) * 0.05;
index[6] = (Math.Floor(basevol / 0.05) + 1) * 0.05;
weight[5] = (index[6] - basevol) / (index[6] - index[5]);
weight[6] = (basevol - index[5]) / (index[6] - index[5]);
}
double vol = 0;
for (var i = 1; i <= 2; i++)
{
for (var j = 3; j <= 4; j++)
{
for (var k = 5; k <= 6; k++)
{
var current_weight = weight[i] * weight[j] * weight[k];
var vol_to_add = FindSkew(term: index[i], moneyness: index[j], atm: index[k], isBuy: isBuy, var: var);
vol += vol_to_add * current_weight;
}
}
}
return vol;
}
private double FindSkew(double term, double moneyness, double atm, bool isBuy, int var)
{
var skewMap = VolSkewMapInitializerSingleton.Instance.GetSkewMapData(isBuy);
if (skewMap == null)
{
throw new Exception("无法获取全局SkewMap数据.");
}
var all = from DataRow row in skewMap.Rows
where Math.Abs((double)row["term"] - term) < 1e-7 &&
Math.Abs((double)row["moneyness"] - moneyness) < 1e-7 &&
Math.Abs((double)row["basevol"] - atm) < 1e-7
select row;
var res = all.First();
return (double)res[var.ToString()];
}
private readonly Dictionary<string, double> _baseVol;
}
}