700 lines
29 KiB
C#
700 lines
29 KiB
C#
using YLErp.Commons;
|
||
using YLErp.Models;
|
||
using YLErp.Modules.DataCacheModule;
|
||
using YLErp.Office.ExcelModule;
|
||
using YLErp.QdpModule.Constants;
|
||
|
||
namespace YLErp.Modules.VolatilityModule
|
||
{
|
||
/// <summary>
|
||
/// 波动率导入读取服务
|
||
/// </summary>
|
||
public class VolatilityImportReadService : YLBaseService
|
||
{
|
||
VolatilityImportReadModel _model;
|
||
|
||
//三种空格\u0020:32--半角空格(英文符号),\u3000:全角空格(中文符号),\u00a0:160--不间断空格(office常用)
|
||
readonly static char[] TrimChars;
|
||
readonly static char[] TrimStrikeChars;
|
||
|
||
static VolatilityImportReadService()
|
||
{
|
||
TrimChars = new[] { '"', '\t', ' ', '\u00a0', '\u3000' };
|
||
TrimStrikeChars = new[] { '"', '\t', ' ', '\u00a0', '\u3000', '%' };
|
||
}
|
||
|
||
public VolatilityImportReadService(OptUserInfo userInfo) : base(userInfo)
|
||
{
|
||
}
|
||
|
||
|
||
class SheetHandleModel
|
||
{
|
||
public string sheetName;
|
||
public string volType;
|
||
public string volSurfaceMode;
|
||
public double reviewDownLimit;
|
||
public double reviewUpLimit;
|
||
public string userGroup;
|
||
|
||
public SheetHandleModel(string sheetName, string volType, VolatilityImportReadModel baseModel)
|
||
{
|
||
this.sheetName = sheetName;
|
||
this.volType = volType;
|
||
volSurfaceMode = baseModel.volSurfaceMode;
|
||
reviewDownLimit = baseModel.ReviewDownLimit;
|
||
reviewUpLimit = baseModel.ReviewUpLimit;
|
||
userGroup = baseModel.UserGroup;
|
||
}
|
||
}
|
||
|
||
public List<volatility> ImportFile(Stream file, VolatilityImportReadModel model)
|
||
{
|
||
_model = model ?? new VolatilityImportReadModel();
|
||
|
||
var reader = new NpoiExcelReader(file);
|
||
|
||
if (model.volType == "BidAsk")
|
||
{
|
||
return ImportBidAskVols(reader, model);
|
||
}
|
||
|
||
if (model.volType == "All")
|
||
{
|
||
return ImportAllVols(model, reader);
|
||
}
|
||
|
||
if (model.volType != "BidAsk" && reader.NumberOfSheets() == 2)
|
||
{
|
||
throw new ServiceException($"文件选择有误!当前文件仅支持BidAsk波动率类型导入,请重新选择!");
|
||
}
|
||
else
|
||
{
|
||
if (!reader.TrySetSheet(0, out var sheet) || sheet.LastRowNum < 0)
|
||
{
|
||
throw new ServiceException("导入数据空,请重新导入!");
|
||
}
|
||
var result = SheetHandle(reader, new SheetHandleModel(sheet.SheetName, model.volType, model), false);
|
||
|
||
if (!result.Any())
|
||
{
|
||
throw new ServiceException($"导入名为{sheet.SheetName}的Sheet中数据为空,请重新导入!");
|
||
}
|
||
var codes = result.GroupBy(x => $"[{x.VolType}]{x.ContractCode}").Select(g => (new { name = g.Key, count = g.Count() }))
|
||
.Where(x => x.count > 1).Select(O => O.name);
|
||
if (codes.Any())
|
||
{
|
||
throw new ServiceException($"导入名为{sheet.SheetName}的Sheet中数据有重复合约,请重新导入!\r\n重复合约:\r\n" + string.Join("\r\n", codes));
|
||
}
|
||
return result;
|
||
}
|
||
}
|
||
private List<volatility> SheetHandle(NpoiExcelReader reader, SheetHandleModel model, bool needSetSheet = true)
|
||
{
|
||
if (model is null)
|
||
{
|
||
throw new ArgumentNullException(nameof(model));
|
||
}
|
||
|
||
ReadProcessBase proc = null;
|
||
|
||
if (needSetSheet && (!reader.TrySetSheet(model.sheetName, out var lastRowNum) || lastRowNum < 0))
|
||
{
|
||
throw new ServiceException($"【Sheet: {model.sheetName} 】导入数据空,请重新导入!");
|
||
}
|
||
|
||
try
|
||
{
|
||
for (var i = 0; i <= 100000; i++)
|
||
{
|
||
if (reader.ReadRow(out var rowNum, out var lineValues))
|
||
{
|
||
if (i == 0)
|
||
{
|
||
var isMultiMode = false;
|
||
//0voltype,1ms,2expire
|
||
var volinfo = lineValues[0].Split('_').ToList();
|
||
if (volinfo.Count > 1)
|
||
{
|
||
isMultiMode = volinfo[1].IndexOf("Moneyness") >= 0 || volinfo[1].IndexOf("Strike") >= 0;
|
||
}
|
||
if (isMultiMode && volinfo.Count < 3)
|
||
{
|
||
throw new ServiceException("请输入格式如:bid_Moneyless_1M");
|
||
}
|
||
//判断第一行第一列为MoneynessVol/StrikeVol_期限 时为申万导入相对行权价简易模式
|
||
proc = isMultiMode
|
||
? (ReadProcessBase)new ShenWanProcess(this, model.reviewDownLimit, model.reviewUpLimit, model.userGroup)
|
||
: new StandardProcess(this, model.volType, model.volSurfaceMode, model.reviewDownLimit, model.reviewUpLimit, model.userGroup);
|
||
}
|
||
|
||
proc.ProcessLine(lineValues, rowNum + 1);
|
||
}
|
||
}
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
throw new Exception($"【Sheet:{model.sheetName}】导入出错。{e.Message}");
|
||
}
|
||
|
||
proc.ProcessEnd();
|
||
|
||
return proc.GetResults();
|
||
}
|
||
|
||
//导入bidask类型的波动率
|
||
private List<volatility> ImportBidAskVols(NpoiExcelReader reader, VolatilityImportReadModel model)
|
||
{
|
||
if (!reader.TryGetSheet("BID", out var sheet) || sheet.LastRowNum < 0)
|
||
{
|
||
throw new ServiceException("导入名为BID的Sheet中数据为空,请重新导入!");
|
||
}
|
||
if (!reader.TryGetSheet("ASK", out sheet) || sheet.LastRowNum < 0)
|
||
{
|
||
throw new ServiceException("导入名为ASK的Sheet中数据为空,请重新导入!");
|
||
}
|
||
var bidList = SheetHandle(reader, new SheetHandleModel("BID", "报价Bid", model));
|
||
if (bidList.Any())
|
||
{
|
||
var codes = bidList.GroupBy(x => x.ContractCode).Select(g => (new { name = g.Key, count = g.Count() })).Where(x => x.count > 1).Select(O => O.name);
|
||
if (codes.Any())
|
||
{
|
||
throw new ServiceException("导入名为BID的Sheet中数据有重复合约,请重新导入!\r\n重复合约:\r\n" + string.Join("\r\n", codes));
|
||
}
|
||
var askList = SheetHandle(reader, new SheetHandleModel("ASK", "报价Ask", model));
|
||
if (askList.Any())
|
||
{
|
||
codes = askList.GroupBy(x => x.ContractCode).Select(g => (new { name = g.Key, count = g.Count() })).Where(x => x.count > 1).Select(O => O.name);
|
||
if (codes.Any())
|
||
{
|
||
throw new ServiceException("导入名为ASK的Sheet中数据有重复合约,请重新导入!\r\n重复合约:\r\n" + string.Join("\r\n", codes));
|
||
}
|
||
|
||
#region 检查数据匹配并顺便计算mid
|
||
var midList = new List<volatility>();
|
||
foreach (var itema in askList)
|
||
{
|
||
try
|
||
{
|
||
var itemb = bidList.Single(x => x.ContractCode == itema.ContractCode);
|
||
if (itema.VolTable.Count != itemb.VolTable.Count)
|
||
{
|
||
throw new Exception();
|
||
}
|
||
|
||
var midVolTable = new List<SingleVol>();
|
||
foreach (var avol in itema.VolTable)
|
||
{
|
||
var bvol = itemb.VolTable.Single(x => x.Expire == avol.Expire && x.Strike == avol.Strike);
|
||
// mid曲面的 strike 和 期限一致, 波动率值为 (bid+ask)/2.
|
||
midVolTable.Add(new SingleVol() { Expire = avol.Expire, Strike = avol.Strike, Vol = (avol.Vol + bvol.Vol) / 2 });
|
||
}
|
||
|
||
var mid = ConstructVolSurfaceWithDeviationCheck(itema.UnderlyingId ?? 0, itema.ContractCode
|
||
, itema.QuotationDate, "", itema.VolSurfaceMode, midVolTable);
|
||
mid.VolType = "交易";//计算Deviation?
|
||
mid.ReviewDownLimit = _model.ReviewDownLimit;
|
||
mid.ReviewUpLimit = _model.ReviewUpLimit;
|
||
mid.UserGroup = _model.UserGroup;
|
||
midList.Add(mid);
|
||
}
|
||
catch
|
||
{
|
||
throw new ServiceException($"匹配合约{itema.ContractCode}出错,请检查Bid/Ask合约-期限-执行价是否匹配!");
|
||
}
|
||
}
|
||
#endregion
|
||
|
||
bidList.AddRange(askList);
|
||
bidList.AddRange(midList);
|
||
return bidList;
|
||
}
|
||
|
||
throw new ServiceException("导入ASK的Sheet数据空,请重新导入!");
|
||
}
|
||
|
||
throw new ServiceException("导入BID的Sheet数据空,请重新导入!");
|
||
}
|
||
|
||
//全量导入
|
||
private List<volatility> ImportAllVols(VolatilityImportReadModel model, NpoiExcelReader reader)
|
||
{
|
||
var volSurfaceMode = model.volSurfaceMode;
|
||
if (!reader.TryGetSheet("BID", out var sheetBid) && sheetBid.LastRowNum < 0)
|
||
{
|
||
throw new ServiceException("导入名为BID的Sheet中数据为空,请重新导入!");
|
||
}
|
||
|
||
if (!reader.TryGetSheet("ASK", out var sheetAsk) && sheetAsk.LastRowNum < 0)
|
||
{
|
||
throw new ServiceException("导入名为ASK的Sheet中数据为空,请重新导入!");
|
||
}
|
||
|
||
if (!reader.TryGetSheet("MID", out var sheetMid) && sheetAsk.LastRowNum < 0)
|
||
{
|
||
throw new ServiceException("导入名为MID的Sheet中数据为空,请重新导入!");
|
||
}
|
||
|
||
var bidList = SheetHandle(reader, new SheetHandleModel("BID", "报价Bid", model));
|
||
if (!bidList.Any())
|
||
{
|
||
throw new ServiceException("导入名为BID的Sheet中数据为空,请重新导入!");
|
||
}
|
||
var codes = bidList.GroupBy(x => x.ContractCode).Select(g => (new { name = g.Key, count = g.Count() })).Where(x => x.count > 1).Select(O => O.name);
|
||
if (codes.Any())
|
||
{
|
||
throw new ServiceException("导入名为BID的Sheet中数据有重复合约,请重新导入!\r\n重复合约:\r\n" + string.Join("\r\n", codes));
|
||
}
|
||
|
||
var askList = SheetHandle(reader, new SheetHandleModel("ASK", "报价Ask", model));
|
||
if (!askList.Any())
|
||
{
|
||
throw new ServiceException("导入名为ASK的Sheet中数据为空,请重新导入!");
|
||
}
|
||
codes = askList.GroupBy(x => x.ContractCode).Select(g => (new { name = g.Key, count = g.Count() })).Where(x => x.count > 1).Select(O => O.name);
|
||
if (codes.Any())
|
||
{
|
||
throw new ServiceException("导入名为ASK的Sheet中数据有重复合约,请重新导入!\r\n重复合约:\r\n" + string.Join("\r\n", codes));
|
||
}
|
||
|
||
var midList = SheetHandle(reader, new SheetHandleModel("MID", "交易", model));
|
||
if (!midList.Any())
|
||
{
|
||
throw new ServiceException("导入名为MID的Sheet中数据为空,请重新导入!");
|
||
}
|
||
codes = midList.GroupBy(x => x.ContractCode).Select(g => (new { name = g.Key, count = g.Count() })).Where(x => x.count > 1).Select(O => O.name);
|
||
if (codes.Any())
|
||
{
|
||
throw new ServiceException("导入名为MID的Sheet中数据有重复合约,请重新导入!\r\n重复合约:\r\n" + string.Join("\r\n", codes));
|
||
}
|
||
|
||
#region 检查数据匹配并顺便计算mid
|
||
foreach (var itemm in midList)
|
||
{
|
||
try
|
||
{
|
||
var itemb = bidList.Single(x => x.ContractCode == itemm.ContractCode);
|
||
if (itemm.VolTable.Count != itemb.VolTable.Count)
|
||
{
|
||
throw new Exception();
|
||
}
|
||
var itema = askList.Single(x => x.ContractCode == itemm.ContractCode);
|
||
if (itemm.VolTable.Count != itema.VolTable.Count)
|
||
{
|
||
throw new Exception();
|
||
}
|
||
}
|
||
catch
|
||
{
|
||
throw new ServiceException($"匹配合约{itemm.ContractCode}出错,请检查Bid/Ask/Mid合约-期限-执行价是否匹配!");
|
||
}
|
||
}
|
||
#endregion
|
||
|
||
bidList.AddRange(askList);
|
||
bidList.AddRange(midList);
|
||
return bidList;
|
||
}
|
||
|
||
public DateTime quotationDate
|
||
{
|
||
get { return _model.quotationDate; }
|
||
}
|
||
|
||
/// <summary>
|
||
/// 构造波动率曲面,并检查是否需要从交易波动率调整出Bid和Ask
|
||
/// </summary>
|
||
private volatility ConstructVolSurfaceWithDeviationCheck(int underlyingId, string contractCode,
|
||
DateTime quotationDate, string volType, string volSurfaceMode, List<SingleVol> singleVolList)
|
||
{
|
||
var newVol = new volatility
|
||
{
|
||
UnderlyingId = underlyingId,
|
||
ContractCode = contractCode,
|
||
OptId = UserId,
|
||
OptName = UserName,
|
||
QuotationDate = quotationDate,
|
||
VolType = volType,
|
||
VolSurfaceMode = volSurfaceMode
|
||
};
|
||
|
||
newVol.Data = singleVolList.ToJson();
|
||
newVol.InterpolationMethod = ConsVolMethod.Default;
|
||
|
||
if (newVol.VolType == "交易")
|
||
{
|
||
newVol.Ask_Deviation = _model.Ask_Deviation;
|
||
newVol.Bid_Deviation = _model.Bid_Deviation;
|
||
}
|
||
|
||
return newVol;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 申万excel波动率类型匹配,目前只有ask,bid和交易
|
||
/// </summary>
|
||
public static string GetVolTypeViaPre(string pre)
|
||
{
|
||
switch (pre)
|
||
{
|
||
case "ask":
|
||
return ConsVolInfos.报价Ask;
|
||
case "bid":
|
||
return ConsVolInfos.报价Bid;
|
||
case "trade":
|
||
return "交易";
|
||
}
|
||
return "交易";
|
||
}
|
||
|
||
public static string GetVolModeViaPre(string mode)
|
||
{
|
||
if (mode.IndexOf("Moneyness") >= 0)
|
||
{
|
||
return "MoneynessVol";
|
||
}
|
||
|
||
if (mode.IndexOf("Strike") >= 0)
|
||
{
|
||
return "StrikeVol";
|
||
}
|
||
|
||
return "MoneynessVol";
|
||
}
|
||
|
||
|
||
abstract class ReadProcessBase
|
||
{
|
||
protected readonly List<volatility> _volList = new List<volatility>();
|
||
|
||
public abstract void ProcessLine(string[] lineValues, int lineNumber);
|
||
|
||
public abstract void ProcessEnd();
|
||
|
||
public List<volatility> GetResults()
|
||
{
|
||
return _volList;
|
||
}
|
||
}
|
||
|
||
class StandardProcess : ReadProcessBase
|
||
{
|
||
string contractCode = null;
|
||
underlying_manager _underlying;
|
||
List<double> strikeList = null;
|
||
readonly List<SingleVol> singleVolList = new List<SingleVol>();
|
||
bool nextFlag = true;
|
||
|
||
readonly string _volType, _volSurfaceMode, _userGroup;
|
||
readonly double _reviewDownLimit, _reviewUpLimit;
|
||
readonly VolatilityImportReadService _service;
|
||
|
||
public StandardProcess(VolatilityImportReadService service, string volType, string volSurfaceMode,
|
||
double reviewDownLimit, double reviewUpLimit, string userGroup)
|
||
{
|
||
_service = service;
|
||
_volType = volType;
|
||
_volSurfaceMode = volSurfaceMode;
|
||
_reviewDownLimit = reviewDownLimit;
|
||
_reviewUpLimit = reviewUpLimit;
|
||
_userGroup = userGroup;
|
||
}
|
||
|
||
public override void ProcessLine(string[] lineValues, int lineNumber)
|
||
{
|
||
if (!lineValues.Any(O => !string.IsNullOrWhiteSpace(O)))
|
||
{
|
||
nextFlag = true;
|
||
|
||
if (_underlying == null)
|
||
{
|
||
throw new ServiceException($"第{lineNumber}行处理失败:不能确认标的");
|
||
}
|
||
|
||
if (singleVolList.Count > 0)
|
||
{
|
||
var newVol = _service.ConstructVolSurfaceWithDeviationCheck(
|
||
_underlying.id, contractCode,
|
||
_service.quotationDate, _volType, _volSurfaceMode, singleVolList);
|
||
newVol.ReviewDownLimit = _reviewDownLimit;
|
||
newVol.ReviewUpLimit = _reviewUpLimit;
|
||
newVol.UserGroup = _userGroup;
|
||
_volList.Add(newVol);
|
||
singleVolList.Clear();
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
if (nextFlag)
|
||
{
|
||
contractCode = lineValues[0].Trim(TrimChars);
|
||
_underlying = DataCacheManager.GetUnderlyingDataSource().GetData(contractCode);
|
||
if (_underlying == null)
|
||
{
|
||
throw new ServiceException($"第{lineNumber}行{contractCode}不存在\r\n");
|
||
}
|
||
|
||
nextFlag = false;
|
||
strikeList = new List<double>();
|
||
foreach (var x in lineValues.Skip(1))
|
||
{
|
||
if (x.Contains("%"))
|
||
{
|
||
var value = double.Parse(x.Trim(TrimStrikeChars));
|
||
if (value <= 0)
|
||
{
|
||
throw new ServiceException($"导入文件中存在非正数行权价! 请检查第{lineNumber}行");
|
||
}
|
||
if (strikeList.Any(s => s == value / 100))
|
||
{
|
||
throw new ServiceException("行权价重复:" + x);
|
||
}
|
||
strikeList.Add(value / 100);
|
||
}
|
||
else
|
||
{
|
||
strikeList.Add(double.Parse(x.Trim(TrimChars)));
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
var expire = lineValues[0].Trim(TrimChars).ToUpper();
|
||
//检查导入文件中到期日格式是否正确
|
||
if (!new System.Text.RegularExpressions.Regex(@"^\d+(W|Y|M|D)$").IsMatch(expire))
|
||
{
|
||
throw new ServiceException($"导入文件中到期日格式错误! 请检查第{lineNumber}行");
|
||
}
|
||
if (lineValues.Length < (strikeList.Count + 1))
|
||
{
|
||
throw new ServiceException($"请检查导入文件内容,内容有误! 请检查第{lineNumber}行");
|
||
}
|
||
double vol = 0;
|
||
var oneRowVol = new List<SingleVol>();
|
||
for (var x = 1; x < lineValues.Length; x++)
|
||
{
|
||
if (double.TryParse(lineValues[x].Trim(TrimChars), out var value))
|
||
{
|
||
if (PS.Config.ErpElement.VolMoreAccurate)
|
||
{
|
||
vol = OtcFormatHelper.FormatValue(value, 6);
|
||
}
|
||
else
|
||
{
|
||
vol = OtcFormatHelper.FormatValue(value, 4);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
throw new ServiceException($"非法的波动率数值! 请检查第{lineNumber}行");
|
||
}
|
||
var singleVol = new SingleVol { Strike = strikeList[x - 1], Expire = expire, Vol = vol };
|
||
oneRowVol.Add(singleVol);
|
||
}
|
||
if (singleVolList.Any(s => s.Expire == expire))
|
||
{
|
||
throw new ServiceException($"重复的到期日{expire}");
|
||
}
|
||
singleVolList.AddRange(oneRowVol);
|
||
}
|
||
}
|
||
|
||
public override void ProcessEnd()
|
||
{
|
||
//当文件最后面有空行时,文件中的最后一个波动率曲面已经在上面的循环中保存了
|
||
//当文件最后面没有空行时,需要在这里保存最后一个波动率曲面
|
||
if (singleVolList.Count > 0)
|
||
{
|
||
if (_underlying == null)
|
||
{
|
||
throw new ServiceException($"尾行处理失败:不能确认标的");
|
||
}
|
||
var newVol = _service.ConstructVolSurfaceWithDeviationCheck(
|
||
_underlying.id, contractCode, _service.quotationDate, _volType, _volSurfaceMode, singleVolList);
|
||
newVol.ReviewDownLimit = _reviewDownLimit;
|
||
newVol.ReviewUpLimit = _reviewUpLimit;
|
||
newVol.UserGroup = _userGroup;
|
||
_volList.Add(newVol);
|
||
}
|
||
}
|
||
}
|
||
|
||
class ShenWanProcess : ReadProcessBase
|
||
{
|
||
readonly VolatilityImportReadService _service;
|
||
readonly List<double> _strikes;
|
||
|
||
private string _simpleExpire;
|
||
private string _volType;
|
||
private string _volSurfaceMode;
|
||
private readonly string _userGroup;
|
||
readonly double _reviewDownLimit, _reviewUpLimit;
|
||
|
||
public ShenWanProcess(VolatilityImportReadService service, double reviewDownLimit, double reviewUpLimit, string userGroup)
|
||
{
|
||
_service = service;
|
||
_reviewDownLimit = reviewDownLimit;
|
||
_reviewUpLimit = reviewUpLimit;
|
||
_userGroup = userGroup;
|
||
_strikes = new List<double>();
|
||
}
|
||
|
||
public override void ProcessLine(string[] lineValues, int lineNumber)
|
||
{
|
||
//0voltype,1ms,2expire
|
||
if (lineValues.Length == 0)
|
||
{
|
||
return;
|
||
}
|
||
|
||
var volheadinfo = lineValues[0].Split('_').ToList();
|
||
if (volheadinfo.Count == 3)
|
||
{
|
||
_strikes.Clear();
|
||
|
||
_simpleExpire = volheadinfo[2];
|
||
_volType = GetVolTypeViaPre(volheadinfo[0]);
|
||
_volSurfaceMode = GetVolModeViaPre(volheadinfo[1]);
|
||
|
||
//当前Strike信息
|
||
foreach (var s in lineValues.Skip(1).ToList())
|
||
{
|
||
var isPercent = s.IndexOf("%") >= 0;
|
||
var strike = Convert.ToDouble(s.Trim(TrimStrikeChars));
|
||
if (isPercent)
|
||
{
|
||
strike = strike / 100;
|
||
}
|
||
|
||
if (_strikes.Any(sk => sk == strike))
|
||
{
|
||
throw new ServiceException($"{strike}行权价重复");
|
||
}
|
||
_strikes.Add(strike);
|
||
}
|
||
}
|
||
else if (lineValues.Length > 0 && !string.IsNullOrWhiteSpace(lineValues[0]))
|
||
{
|
||
//有数据
|
||
var voldata = lineValues.Skip(1).ToList();
|
||
var contractCode = lineValues[0].Split('.')[0];
|
||
var underlying = DataCacheManager.GetUnderlyingDataSource().GetData(contractCode);
|
||
if (underlying == null)
|
||
{
|
||
throw new ServiceException($"标的代码[{contractCode}]在系统中不存在!");
|
||
}
|
||
|
||
double vol = 0;
|
||
var singleVolList = new List<SingleVol>();
|
||
|
||
for (var i = 0; i < voldata.Count; i++)
|
||
{
|
||
if (double.TryParse(voldata[i].Trim(TrimChars), out var value))
|
||
{
|
||
if (PS.Config.ErpElement.VolMoreAccurate)
|
||
{
|
||
vol = OtcFormatHelper.FormatValue(value, 6);
|
||
}
|
||
else
|
||
{
|
||
vol = OtcFormatHelper.FormatValue(value, 4);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
throw new ServiceException($"非法的波动率数值! 请检查第{lineNumber}行");
|
||
}
|
||
var singleVol = new SingleVol { Strike = _strikes[i], Expire = _simpleExpire, Vol = vol };
|
||
singleVolList.Add(singleVol);
|
||
}
|
||
|
||
var newVol = _service.ConstructVolSurfaceWithDeviationCheck(
|
||
underlying.id, contractCode, _service.quotationDate, _volType, _volSurfaceMode, singleVolList);
|
||
newVol.ReviewDownLimit = _reviewDownLimit;
|
||
newVol.ReviewUpLimit = _reviewUpLimit;
|
||
newVol.UserGroup = _userGroup;
|
||
_volList.Add(newVol);
|
||
}
|
||
}
|
||
|
||
public override void ProcessEnd()
|
||
{
|
||
//申万模式特殊处理,交易不要放在第一个,否则导入时会以为是交易波动率,按照之前api规则替换ask,bid
|
||
var vgroup = _volList.GroupBy(v => v.ContractCode).ToArray();
|
||
|
||
//检查 是否存在相同标的,不同波动率模式的数据,存在则不让导入提示错误
|
||
var invalidGroup = vgroup.FirstOrDefault(n => n.GroupBy(v => v.VolSurfaceMode).Count() > 1);
|
||
if (invalidGroup != null)
|
||
{
|
||
throw new ServiceException($"导入失败,{invalidGroup.Key}有多种模式波动率,请只输入一种!");
|
||
}
|
||
|
||
_volList.Clear();
|
||
|
||
foreach (var single in vgroup)
|
||
{
|
||
var singlevols = single.ToList();
|
||
var singlevoltypes = singlevols.GroupBy(s => s.VolType).ToArray();
|
||
|
||
foreach (var singlevoltype in singlevoltypes)
|
||
{
|
||
var first = singlevoltype.First();
|
||
var importvol = new volatility
|
||
{
|
||
Ask_Deviation = 0,
|
||
Bid_Deviation = 0,
|
||
VolSurfaceMode = first.VolSurfaceMode,
|
||
UnderlyingId = first.UnderlyingId,
|
||
ContractCode = first.ContractCode,
|
||
VolType = singlevoltype.Key,
|
||
QuotationDate = _service.quotationDate,
|
||
OptDate = DateTime.Now,
|
||
OptId = _service.UserId,
|
||
OptName = _service.UserName,
|
||
ReviewDownLimit = _reviewDownLimit,
|
||
ReviewUpLimit = _reviewUpLimit,
|
||
UserGroup = _userGroup
|
||
};
|
||
var ssv = new List<SingleVol>();
|
||
foreach (var sv in singlevoltype)
|
||
{
|
||
ssv.AddRange(sv.VolTable);
|
||
}
|
||
importvol.Data = ssv.ToJson();
|
||
_volList.Add(importvol);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
public class VolatilityImportReadModel
|
||
{
|
||
public DateTime quotationDate { get; set; }
|
||
|
||
public string volType { get; set; }
|
||
|
||
public string volSurfaceMode { get; set; }
|
||
|
||
public double Bid_Deviation { get; set; }
|
||
|
||
public double Ask_Deviation { get; set; }
|
||
|
||
/// <summary>
|
||
/// 审核波动率下限
|
||
/// </summary>
|
||
public double ReviewDownLimit { get; set; }
|
||
|
||
/// <summary>
|
||
/// 审核波动率上限
|
||
/// </summary>
|
||
public double ReviewUpLimit { get; set; }
|
||
|
||
/// <summary>
|
||
/// 用户组
|
||
/// </summary>
|
||
public string UserGroup { get; set; }
|
||
}
|
||
}
|