从山证v2.3.0拷贝
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
using System.Data;
|
||||
|
||||
namespace YLErp.Modules.SkewMapVolModule
|
||||
{
|
||||
public interface IVolSkewMapInitializer
|
||||
{
|
||||
DataTable GetSkewMapData(bool isBuy);
|
||||
|
||||
bool SetSkewMapData(string table, bool isBuy);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
using BaseOUDAL;
|
||||
using System.Text.RegularExpressions;
|
||||
using YLErp.Modules.CalculationModule;
|
||||
|
||||
namespace YLErp.Modules.VolatilityModule.SkewMapVolModule
|
||||
{
|
||||
/// <summary>
|
||||
/// 从数据库中查询SkewVol
|
||||
/// </summary>
|
||||
public class SkewVolQueryService
|
||||
{
|
||||
public static volatility GetVol(int userId, SkewVolRequest req)
|
||||
{
|
||||
if (req is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(req));
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(req.VolType))
|
||||
{
|
||||
throw new Exception("波动率类型不能为空");
|
||||
}
|
||||
|
||||
var userGroup = UserBLL.GetUserGroup(userId);
|
||||
|
||||
var vols = VolatilityHelper.GetVol(req.valueDate, "交易", req.UnderlyingCode, userGroup);
|
||||
|
||||
if (vols == null || string.IsNullOrEmpty(vols.VolSurfaceMode))
|
||||
{
|
||||
throw new InvalidOperationException($"找不到波动率曲面{req.UnderlyingCode}");
|
||||
}
|
||||
|
||||
var singleVols = vols.VolTable;
|
||||
|
||||
for (var i = 0; i < singleVols.Count; i++)
|
||||
{
|
||||
if (!Regex.IsMatch(singleVols[i].Expire, @"\d")) { continue; }
|
||||
|
||||
var ExerciseDate = GetExerciseDate(req.valueDate, singleVols[i].Expire);
|
||||
|
||||
singleVols[i].Vol = SkewMapVolHelper.GetInterpolatedVol(
|
||||
volSurface: vols,
|
||||
valueDate: req.valueDate,
|
||||
underlyingCode: req.UnderlyingCode,
|
||||
exerciseDate: ExerciseDate,
|
||||
strikePrice: req.Strike,
|
||||
isBuy: req.VolType == "报价Bid",
|
||||
isCall: false,
|
||||
spotPrice: req.Strike,
|
||||
skewMapVolVar: (int)(req.VolType == "报价Bid" ? vols.GetBidVar() : vols.GetAskVar())
|
||||
);
|
||||
}
|
||||
vols.Data = singleVols.ToJson();
|
||||
return vols;
|
||||
}
|
||||
|
||||
private static DateTime GetExerciseDate(DateTime valueDate, string term)
|
||||
{
|
||||
var result = valueDate;
|
||||
var m = Regex.Match(term, @"^(?<num>\d+)(?<unit>[D|W|M|Y])$");
|
||||
if (!m.Success) { return result; }
|
||||
var number = int.Parse(m.Groups["num"].Value);
|
||||
switch (m.Groups["unit"].Value)
|
||||
{
|
||||
case "D":
|
||||
result = result.AddDays(number);
|
||||
break;
|
||||
case "W":
|
||||
result = result.AddDays(number * 7);
|
||||
break;
|
||||
case "M":
|
||||
result = result.AddMonths(number).AddDays(-1);
|
||||
break;
|
||||
case "Y":
|
||||
result = result.AddYears(number).AddDays(-1);
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public class SkewVolRequest
|
||||
{
|
||||
public DateTime valueDate { get; set; }
|
||||
|
||||
public string VolType { get; set; }
|
||||
|
||||
public string UnderlyingCode { get; set; }
|
||||
|
||||
public double Strike { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
using System.Data;
|
||||
using YLErp.BLL;
|
||||
|
||||
namespace YLErp.Modules.SkewMapVolModule
|
||||
{
|
||||
public class VolSkewMapDbInitializer : IVolSkewMapInitializer
|
||||
{
|
||||
/// <summary>
|
||||
/// 从数据库表读取SkewMapData
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public DataTable GetSkewMapData(bool isBuy)
|
||||
{
|
||||
if (isBuy)
|
||||
{
|
||||
if (_bidTable == null)
|
||||
{
|
||||
var rawTable = ReadDataFromDb(isBuy);
|
||||
if (rawTable == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
_bidTable = ExpandSkewMap(rawTable);
|
||||
}
|
||||
|
||||
return _bidTable;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_askTable == null)
|
||||
{
|
||||
var rawTable = ReadDataFromDb(isBuy);
|
||||
if (rawTable == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
_askTable = ExpandSkewMap(rawTable);
|
||||
}
|
||||
|
||||
return _askTable;
|
||||
}
|
||||
}
|
||||
|
||||
public bool SetSkewMapData(string table, bool isBuy)
|
||||
{
|
||||
if (SaveDataToDb(table, isBuy))
|
||||
{
|
||||
var rawTable = ReadDataFromDb(isBuy);
|
||||
if (isBuy)
|
||||
{
|
||||
_bidTable = ExpandSkewMap(rawTable);
|
||||
}
|
||||
else
|
||||
{
|
||||
_askTable = ExpandSkewMap(rawTable);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static DataTable ExpandSkewMap(DataTable base_map)
|
||||
{
|
||||
int[] base_var = { 1, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50 };
|
||||
var target_var = new int[50];
|
||||
for (int i = 0; i < 50; i++)
|
||||
{
|
||||
target_var[i] = i + 1;
|
||||
}
|
||||
|
||||
var tb = new DataTable();
|
||||
tb.Columns.Add("term", System.Type.GetType("System.Double"));
|
||||
tb.Columns.Add("moneyness", System.Type.GetType("System.Double"));
|
||||
tb.Columns.Add("basevol", System.Type.GetType("System.Double"));
|
||||
foreach (int i in target_var)
|
||||
{
|
||||
tb.Columns.Add(i.ToString(), System.Type.GetType("System.Double"));
|
||||
}
|
||||
|
||||
foreach (DataRow dr in base_map.Rows)
|
||||
{
|
||||
var r = tb.NewRow();
|
||||
r[0] = Convert.ToSingle(dr["term"]);
|
||||
r[1] = Convert.ToSingle(dr["moneyness"]);
|
||||
r[2] = Convert.ToSingle(dr["basevol"]);
|
||||
int loc = 3;
|
||||
|
||||
foreach (int v in target_var)
|
||||
{
|
||||
var var_col = v.ToString();
|
||||
if (base_var.Contains(v))
|
||||
{
|
||||
r[loc] = Convert.ToSingle(dr[var_col]);
|
||||
}
|
||||
else
|
||||
{
|
||||
int l_var = base_var.Where(b => b < v).Last();
|
||||
int h_var = base_var.Where(b => b > v).First();
|
||||
float l_v = Convert.ToSingle(dr[l_var.ToString()]);
|
||||
float h_v = Convert.ToSingle(dr[h_var.ToString()]);
|
||||
float n_value = l_v + (v - l_var) * (h_v - l_v) / (h_var - l_var);
|
||||
r[loc] = n_value;
|
||||
}
|
||||
loc++;
|
||||
}
|
||||
tb.Rows.Add(r);
|
||||
}
|
||||
return tb;
|
||||
|
||||
}
|
||||
|
||||
private static DataTable ReadDataFromDb(bool isBuy)
|
||||
{
|
||||
using (var db = new YLContext())
|
||||
{
|
||||
var skewMapType = ConvertToSkewMapType(isBuy);
|
||||
var skewMap = db.globalSkewMap.FirstOrDefault(x => x.SkewMapType == skewMapType);
|
||||
if (skewMap == null || string.IsNullOrWhiteSpace(skewMap.SkewMapTableData))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var tb = new DataTable();
|
||||
var rows = skewMap.SkewMapTableData.Split('\n');
|
||||
for (int i = 0; i < rows.Count() - 1; i++)
|
||||
{
|
||||
var rowValues = rows[i].Split(',');
|
||||
if (i == 0)
|
||||
{
|
||||
//tb.Columns.Add(rowValues[0].Trim());
|
||||
for (int j = 0; j < rowValues.Count(); j++)
|
||||
{
|
||||
tb.Columns.Add(rowValues[j].Trim());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var dr = tb.NewRow();
|
||||
for (int k = 0; k < rowValues.Count(); k++)
|
||||
{
|
||||
dr[k] = Convert.ToSingle(rowValues[k]);
|
||||
}
|
||||
tb.Rows.Add(dr);
|
||||
}
|
||||
}
|
||||
return tb;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool SaveDataToDb(string tableStr, bool isBuy)
|
||||
{
|
||||
try
|
||||
{
|
||||
var skewMapType = ConvertToSkewMapType(isBuy);
|
||||
using (var db = new YLContext())
|
||||
{
|
||||
var existingRecord = db.globalSkewMap.FirstOrDefault(x => x.SkewMapType == skewMapType);
|
||||
if (existingRecord == null)
|
||||
{
|
||||
existingRecord = new Model.GlobalSkewMap
|
||||
{
|
||||
SkewMapTableData = tableStr,
|
||||
SkewMapType = skewMapType,
|
||||
UpdateTime = DateTime.Now
|
||||
};
|
||||
db.globalSkewMap.Add(existingRecord);
|
||||
}
|
||||
else
|
||||
{
|
||||
existingRecord.SkewMapTableData = tableStr;
|
||||
existingRecord.UpdateTime = DateTime.Now;
|
||||
}
|
||||
db.SaveChanges();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static string ConvertToSkewMapType(bool isBuy)
|
||||
{
|
||||
return isBuy ? "Bid" : "Ask";
|
||||
}
|
||||
|
||||
private DataTable _bidTable = null;
|
||||
private DataTable _askTable = null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
namespace YLErp.Modules.SkewMapVolModule
|
||||
{
|
||||
public class VolSkewMapInitializerSingleton
|
||||
{
|
||||
public static IVolSkewMapInitializer Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_instance == null)
|
||||
{
|
||||
_instance = new VolSkewMapDbInitializer();
|
||||
}
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
|
||||
private static IVolSkewMapInitializer _instance;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user