从山证v2.3.0拷贝

This commit is contained in:
吴方海
2024-05-09 14:06:26 +08:00
parent 566ff33259
commit f9d8a256a6
4471 changed files with 1203456 additions and 9 deletions
@@ -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;
}
}