148 lines
4.9 KiB
C#
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;
|
|
}
|
|
}
|