using System.Data; namespace YLErp.Modules.SkewMapVolModule { public struct SkewMapVolSurface { public SkewMapVolSurface(Dictionary 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(); var weight = new Dictionary(); 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 _baseVol; } }