从山证v2.3.0拷贝
This commit is contained in:
@@ -0,0 +1,699 @@
|
||||
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; }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user