541 lines
23 KiB
C#
541 lines
23 KiB
C#
using BaseOUDAL;
|
|
using System.Text.RegularExpressions;
|
|
using YLErp.Abstract.DataProviders;
|
|
using YLErp.Commons;
|
|
using YLErp.DBModels.Consts;
|
|
using YLErp.Model;
|
|
using YLErp.Modules.DataProviderModule;
|
|
using YLErp.Modules.SuperviseReportModule.CFMMC.Model;
|
|
|
|
namespace YLErp.Modules.SuperviseReportModule.CFMMC.Service
|
|
{
|
|
public abstract class SuperviseReportBaseService : YLBaseService
|
|
{
|
|
protected SuperviseReportReq _req { get; set; }
|
|
|
|
/// <summary>
|
|
/// 是否是权益报告
|
|
/// </summary>
|
|
protected bool _isEquity => _req.DataSource == "权益";
|
|
/// <summary>
|
|
/// 分隔符
|
|
/// <para>权益:分号</para>
|
|
/// <para>商品:逗号</para>
|
|
/// </summary>
|
|
protected string _separator => _isEquity ? ";" : ",";
|
|
|
|
protected IEodPriceProviderWrap _priceProvider;
|
|
|
|
protected SuperviseReportBaseService(SuperviseReportReq req, OptUserInfo userInfo) : base(userInfo)
|
|
{
|
|
_req = req ?? throw new ArgumentNullException(nameof(req));
|
|
|
|
_priceProvider = new EodPriceProvider(_req.ValueDate).GetPriceProvider(SettlementTypeEnum.ClosePrice);
|
|
}
|
|
|
|
private readonly List<string> ignoreTradeTypes = new List<string>() { "凤凰期权", "雪球期权", "区间累积期权" };
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <param name="model"></param>
|
|
/// <param name="varietyTypes">标的资产类型</param>
|
|
/// <param name="assetTypes">资产类型</param>
|
|
/// <param name="varietySuffixs">标的资产品种后缀和标的资产对应合约后缀</param>
|
|
/// <param name="syntheticPrice">组合标的价格</param>
|
|
/// <exception cref="ServiceException">EodTrade不存在时抛出</exception>
|
|
protected virtual void FormatModel(SuperviseReportBaseModel model, List<DictionaryItem> varietyTypes, List<DictionaryItem> assetTypes, List<DictionaryItem> varietySuffixs, List<DictionaryItem> varietyCodeList, out SyntheticPriceModel syntheticPrice)
|
|
{
|
|
if (model.trade == null)
|
|
{
|
|
throw new ServiceException($"日终Trade不存在,交易编号:{model.OriginalTradeNumber}");
|
|
}
|
|
|
|
model.BuySell = model.trade.BuySell == "买入" ? "B" : "S";
|
|
if (model.trade.TradeType == "远期")
|
|
{
|
|
if (model.trade.OptionType == "看跌")
|
|
{
|
|
model.BuySell = model.trade.BuySell == "卖出" ? "B" : "S";
|
|
}
|
|
|
|
}
|
|
model.ToolType = formatToolType(model.trade.TradeType, model.StructureType);
|
|
model.ExerciseMode = /*ignoreTradeTypes.Contains(model.trade.TradeType) ? "O" :*/ IsOption(model.trade) ? (model.trade.ExerciseMode == "European" ? "EU" : "US") : "";
|
|
//国投特殊处理
|
|
model.ExerciseMode = PS.Config.Company == Configuration.CompanyEnum.国投 && model.trade.TradeType == "雪球期权" ? "O" : model.ExerciseMode;
|
|
model.OptionType = ignoreTradeTypes.Contains(model.trade.TradeType) ? "9" : IsOption(model.trade) ? (model.trade.OptionType == "看涨" ? "2" : "1") : "";
|
|
model.Strike = (model.trade.IsMoneynessOptionData ? model.trade.SpotPrice * model.trade.Strike : model.trade.Strike) ?? 0;
|
|
model.Strike = model.TradeType == "收益互换" ? Convert.ToDouble((model.trade.SpotPrice)) : model.Strike;
|
|
model.ParticipateRate = model.trade.ParticipationRate ?? 1;
|
|
if (model.trade.TradeType == "雪球期权")
|
|
{
|
|
model.IsAnnualTrade = (model.trade.trade_snowball?.IsAnnualized2).GetValueOrDefault() ? "Y" : "N";
|
|
}
|
|
else if (model.trade.TradeType == "凤凰期权")
|
|
{
|
|
model.IsAnnualTrade = (model.trade.trade_autocall?.IsAnnualized2).GetValueOrDefault() ? "Y" : "N";
|
|
}
|
|
else
|
|
{
|
|
model.IsAnnualTrade = model.trade.IsAnnualized ? "Y" : "N";
|
|
}
|
|
//国投特殊处理
|
|
model.IsAnnualTrade = PS.Config.Company == Configuration.CompanyEnum.国投 && model.TradeType == "收益互换" ? "" : model.IsAnnualTrade;
|
|
model.TradeNumber = formatTradeNumber(model);
|
|
model.TradeTypeEn = IsOption(model.trade) ? formatTradeTypeEn(model) : (model.TradeType == "收益互换" && PS.Config.Company == Configuration.CompanyEnum.华安) ? "VE" : "";
|
|
|
|
var um = DataCacheProvider.GetUnderlyingDataSource().GetData(model.trade.UnderlyingCode);
|
|
if (um.IsSynthetic() || !string.IsNullOrWhiteSpace(model.trade.BasisUnderlyingCode))
|
|
{
|
|
Dictionary<string, string> marketNoDict = new Dictionary<string, string>();
|
|
var query = DbContext.TradeMeta.Where(O => O.TradeId == model.trade.id && O.MetaValue.Length > 5);
|
|
if (um.IsSynthetic())
|
|
{
|
|
query = query.Where(O => O.MetaKey.StartsWith(ConsTradeMetaKey.SyntheticUnderlying));
|
|
var tradeMeta = query.FirstOrDefault();
|
|
if (string.IsNullOrWhiteSpace(tradeMeta?.MetaValue))
|
|
{
|
|
throw new ServiceException($"组合标的期初价格信息不完整,交易编号:{model.OriginalTradeNumber}");
|
|
}
|
|
syntheticPrice = JsonHelper.Deserialize<SyntheticPriceModel>(tradeMeta.MetaValue);
|
|
if (syntheticPrice.SuList == null)
|
|
{
|
|
throw new ServiceException($"组合标的价格缺失,交易编号:{model.trade.TradeNumber}");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
query = query.Where(O => O.MetaKey == ConsTradeMetaKey.SpotInfos);
|
|
var tradeMeta = query.FirstOrDefault();
|
|
string[] arrPrice = null;
|
|
if (string.IsNullOrWhiteSpace(tradeMeta?.MetaValue))
|
|
{
|
|
arrPrice = new[] { model.trade.SpotPrice?.ToString() };
|
|
}
|
|
else
|
|
{
|
|
arrPrice = JsonHelper.Deserialize<string[]>(tradeMeta.MetaValue);
|
|
if (arrPrice == null || arrPrice.Length == 0)
|
|
{
|
|
throw new ServiceException($"期初价格缺失,交易编号:{model.trade.TradeNumber}");
|
|
}
|
|
}
|
|
var prides = new UnderlyingPriceModel[arrPrice.Length];
|
|
for (int i = 0; i < arrPrice.Length; i++)
|
|
{
|
|
prides[i] = new UnderlyingPriceModel();
|
|
prides[i].Price = double.TryParse(arrPrice[i], out var p) ? p : 0;
|
|
prides[i].Coefficient = i == 0 ? 1 : -1;
|
|
prides[i].UnderlyingCode = i == 0 ? model.trade.UnderlyingCode : model.trade.BasisUnderlyingCode;
|
|
}
|
|
syntheticPrice = new SyntheticPriceModel();
|
|
syntheticPrice.SuList = prides;
|
|
}
|
|
UnderlyingPriceModel[] underlyingCodes = syntheticPrice.SuList.Select(O => O).ToArray();
|
|
string[] arrCode = new string[underlyingCodes.Length];
|
|
string[] arrUnitZh = new string[underlyingCodes.Length];
|
|
string[] arrUnitEn = new string[underlyingCodes.Length];
|
|
string[] arrAssetTypeEn = new string[underlyingCodes.Length];
|
|
string[] arrVarietyType = new string[underlyingCodes.Length];
|
|
for (int i = 0; i < underlyingCodes.Length; i++)
|
|
{
|
|
if (underlyingCodes[i] == null || string.IsNullOrWhiteSpace(underlyingCodes[i].UnderlyingCode))
|
|
{
|
|
arrCode[i] = "";
|
|
arrUnitZh[i] = "";
|
|
arrUnitEn[i] = "";
|
|
arrAssetTypeEn[i] = "";
|
|
arrVarietyType[i] = "";
|
|
continue;
|
|
}
|
|
var variety = UnderlyingDataProvider.GetVariety(underlyingCodes[i].UnderlyingCode);
|
|
if (variety == null) { throw new ServiceException($"未找到品种数据: {underlyingCodes[i].UnderlyingCode}"); }
|
|
var um1 = UnderlyingDataProvider.GetUnderlying(underlyingCodes[i].UnderlyingCode);
|
|
string varietySuffix = formatDictItem(varietySuffixs, um1?.MarketCode, um1?.MarketCode);
|
|
marketNoDict[underlyingCodes[i].UnderlyingCode] = varietySuffix;
|
|
arrCode[i] = formatVarietyCode(underlyingCodes[i].UnderlyingCode, variety, varietyCodeList, varietySuffix);
|
|
arrUnitZh[i] = variety.QuoteUnitSingleOriginal;
|
|
arrUnitEn[i] = formatQuoteUnit(arrUnitZh[i]);
|
|
arrAssetTypeEn[i] = formatDictItem(assetTypes, variety.AssetType);
|
|
arrVarietyType[i] = formatDictItem(varietyTypes, variety.AssetType, "O");
|
|
}
|
|
model.UnderlyingCode = formatSyntheticUnderlying(syntheticPrice, marketNoDict);
|
|
//model.TradeAmount = string.Join(",", arrAmount);
|
|
model.VarietyCode = JoinArr(arrCode);
|
|
model.QuoteUnit = JoinArr(arrUnitZh);
|
|
model.QuoteUnitEn = JoinArr(arrUnitEn);
|
|
model.AssetTypeEn = JoinArr(arrAssetTypeEn);
|
|
model.VarietyType = JoinArr(arrVarietyType);
|
|
if (arrUnitEn.All(o => o == "bp"))
|
|
{
|
|
model.PriceSymbol = 3;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
var variety = UnderlyingDataProvider.GetVariety(model.trade.UnderlyingCode);
|
|
if (variety == null) { throw new ServiceException($"未找到品种数据: {model.trade.UnderlyingCode}"); }
|
|
string varietySuffix = formatDictItem(varietySuffixs, um?.MarketCode, um?.MarketCode);
|
|
|
|
//model.TradeAmount = formatTradeAmount(model.trade.OriginalNotional);
|
|
model.UnderlyingCode = formatUnderlyingCode(model.trade.UnderlyingCode, varietySuffix);
|
|
model.VarietyCode = formatVarietyCode(model.trade.UnderlyingCode, variety, varietyCodeList, varietySuffix);
|
|
model.QuoteUnit = (variety?.QuoteUnitSingleOriginal) ?? "";
|
|
model.QuoteUnitEn = formatQuoteUnit(variety?.QuoteUnitSingleOriginal);
|
|
model.AssetTypeEn = formatDictItem(assetTypes, variety.AssetType);
|
|
model.VarietyType = formatDictItem(varietyTypes, variety.AssetType, "O");
|
|
if (model.QuoteUnitEn == "bp")
|
|
{
|
|
model.PriceSymbol = 3;
|
|
}
|
|
syntheticPrice = null;
|
|
}
|
|
}
|
|
|
|
protected double formatInitCastClientPayable(trade t)
|
|
{
|
|
double result = 0;
|
|
if (t != null)
|
|
{
|
|
result = (t.InitialMargin * (t.Notional / t.OriginalNotional)) ?? 0;
|
|
}
|
|
return Math.Max(0, result);
|
|
}
|
|
|
|
protected string formatTradeAmount(double? notional, double coefficient = 1)
|
|
{
|
|
double? value = notional * coefficient;
|
|
return OtcFormatHelper.FormatValue(value, 4).ToString();
|
|
}
|
|
|
|
private string formatToolType(string tradeType, string structureType)
|
|
{
|
|
string toolType = "O";
|
|
switch (tradeType)
|
|
{
|
|
case "收益互换":
|
|
toolType = "SW";
|
|
break;
|
|
case "远期":
|
|
toolType = "FW";
|
|
if (structureType == "掉期")
|
|
{
|
|
toolType = "SW";
|
|
}
|
|
break;
|
|
default:
|
|
toolType = "OP";
|
|
break;
|
|
}
|
|
|
|
return toolType;
|
|
}
|
|
|
|
protected string formatDictItem(List<DictionaryItem> dictItemMap, string name, string defaultValue = "")
|
|
{
|
|
if (name == null || dictItemMap == null) { return defaultValue; }
|
|
var obj = dictItemMap.FirstOrDefault(O => O.Name == name);
|
|
return obj == null ? defaultValue : obj.ShortName;
|
|
}
|
|
|
|
private string formatSyntheticUnderlying(SyntheticPriceModel underlying, Dictionary<string, string> exchangeNo)
|
|
{
|
|
Func<double, string, string> formatCode = (_coefficient, _underlyingCode) =>
|
|
{
|
|
string code = formatUnderlyingCode(_underlyingCode, exchangeNo[_underlyingCode]);
|
|
string coef = Math.Abs(_coefficient).ToString();
|
|
return $"{(coef == "1" ? "" : coef)}{code}";
|
|
};
|
|
|
|
var tipsInfo = "";
|
|
foreach (var item in underlying.SuList)
|
|
{
|
|
if (!string.IsNullOrEmpty(tipsInfo) || item.Coefficient < 0)
|
|
{
|
|
tipsInfo += (item.Coefficient >= 0 ? "+" : "-");
|
|
}
|
|
tipsInfo += formatCode(item.Coefficient, item.UnderlyingCode);
|
|
}
|
|
|
|
return tipsInfo;
|
|
}
|
|
|
|
private string formatUnderlyingCode(string underlyingCode, string exchangeNo)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(underlyingCode))
|
|
{
|
|
throw new ArgumentException("标的代码不存在", nameof(underlyingCode));
|
|
}
|
|
var underlying = DataCacheProvider.GetUnderlyingDataSource().GetData(underlyingCode);
|
|
if (underlying == null)
|
|
{
|
|
throw new ArgumentException("标的信息不存在:" + underlyingCode);
|
|
}
|
|
if (underlying.UnderlyingInstrumentType == ConsGlobal.InstrumentType.CommoditySpot)
|
|
{
|
|
return underlying.UnderlyingName;
|
|
}
|
|
else if (underlyingCode.Contains("."))
|
|
{
|
|
return underlyingCode;
|
|
}
|
|
return $"{underlyingCode}.{exchangeNo}";
|
|
}
|
|
|
|
private string formatTradeNumber(SuperviseReportBaseModel model)
|
|
{
|
|
if ((model.trade.ParentTradeId > 0))
|
|
{
|
|
var legNumber = 0;
|
|
if (PS.Config.Company == Configuration.CompanyEnum.浙期)
|
|
{
|
|
if (!int.TryParse(Regex.Match(model.OriginalTradeNumber ?? "", @"(?<=X)\d+$").Value, out legNumber))
|
|
{
|
|
return "";
|
|
}
|
|
}
|
|
else if (!int.TryParse(Regex.Match(model.trade.TradeNumber ?? "", @"(?<=-)\d+$").Value, out legNumber))
|
|
{
|
|
return "";
|
|
}
|
|
return legNumber.ToString();
|
|
}
|
|
else
|
|
{
|
|
return "";
|
|
}
|
|
}
|
|
|
|
private string formatTradeTypeEn(SuperviseReportBaseModel model)
|
|
{
|
|
string tradeTypeEn = "O";
|
|
switch (model.TradeType)
|
|
{
|
|
case "香草期权":
|
|
tradeTypeEn = "VA";
|
|
break;
|
|
case "结构化交易":
|
|
switch (model.StructureType)
|
|
{
|
|
case "风险逆转":
|
|
tradeTypeEn = "RE";
|
|
break;
|
|
case "三领口":
|
|
case "三领口组合":
|
|
tradeTypeEn = "CO";
|
|
break;
|
|
case "跨式组合":
|
|
tradeTypeEn = "STR";
|
|
break;
|
|
case "宽跨式":
|
|
case "宽跨式组合":
|
|
tradeTypeEn = "STA";
|
|
break;
|
|
case "牛市价差":
|
|
case "熊市价差":
|
|
tradeTypeEn = "VE";
|
|
break;
|
|
case "结构化交易":
|
|
//自由组合
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case "合成价差期权":
|
|
tradeTypeEn = "SP";
|
|
break;
|
|
case "凤凰期权":
|
|
tradeTypeEn = "PA";
|
|
break;
|
|
case "雪球期权":
|
|
tradeTypeEn = "SA";
|
|
break;
|
|
case "障碍期权":
|
|
tradeTypeEn = "SF";
|
|
break;
|
|
case "二元期权":
|
|
switch (model.ExerciseMode)
|
|
{
|
|
case "US":
|
|
case "American":
|
|
switch (model.PayoffType)
|
|
{
|
|
case "UpOneTouch":
|
|
case "DownOneTouch":
|
|
case "DoubleOneTouch":
|
|
tradeTypeEn = "OT";
|
|
break;
|
|
case "DoubleNoTouch":
|
|
tradeTypeEn = "DNT";
|
|
break;
|
|
case "UpNoTouch":
|
|
case "DownNoTouch":
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case "EU":
|
|
case "European":
|
|
tradeTypeEn = "DI";
|
|
switch (model.PayoffType)
|
|
{
|
|
case "AssetOrNothing":
|
|
break;
|
|
case "CashOrNothing":
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case "亚式期权":
|
|
tradeTypeEn = "AS";
|
|
break;
|
|
case "双鲨期权":
|
|
tradeTypeEn = "DSF";
|
|
break;
|
|
case "区间累计期权":
|
|
case "区间累积期权":
|
|
tradeTypeEn = "RA";
|
|
break;
|
|
default:
|
|
tradeTypeEn = "O";
|
|
break;
|
|
}
|
|
return tradeTypeEn;
|
|
}
|
|
|
|
private string formatVarietyCode(string underlyingCode, Variety variety, List<DictionaryItem> varietyCodeList, string marketExchangeNo)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(underlyingCode))
|
|
{
|
|
throw new ArgumentException("标的代码不存在", nameof(underlyingCode));
|
|
}
|
|
var code = varietyCodeList.Where(O => O.Name.ToLower() == underlyingCode.ToLower()).Select(O => O.ShortName).FirstOrDefault();
|
|
if (!string.IsNullOrEmpty(code))
|
|
{
|
|
return code;
|
|
}
|
|
var underlying = DataCacheProvider.GetUnderlyingDataSource().GetData(underlyingCode);
|
|
if (underlying == null)
|
|
{
|
|
throw new ArgumentException("标的信息不存在:" + underlyingCode);
|
|
}
|
|
string assetVariety = $"{{0}}.{marketExchangeNo}";
|
|
switch (underlying.UnderlyingInstrumentType)
|
|
{
|
|
case ConsGlobal.InstrumentType.StockIndex:
|
|
case ConsGlobal.InstrumentType.StockIF:
|
|
assetVariety = variety.VarietyName;
|
|
break;
|
|
case ConsGlobal.InstrumentType.CommodityFutures:
|
|
assetVariety = string.Format(assetVariety, variety.VarietyCode);
|
|
break;
|
|
case ConsGlobal.InstrumentType.CommoditySpot:
|
|
if (marketExchangeNo == "SGE")
|
|
{
|
|
assetVariety = string.Format(assetVariety, underlyingCode);
|
|
}
|
|
else
|
|
{
|
|
assetVariety = variety.VarietyName;
|
|
}
|
|
break;
|
|
case ConsGlobal.InstrumentType.Stock:
|
|
assetVariety = string.Format(assetVariety, getStockVarietyCode(underlyingCode));
|
|
break;
|
|
default:
|
|
assetVariety = underlyingCode;
|
|
break;
|
|
}
|
|
return assetVariety;
|
|
}
|
|
|
|
private string getStockVarietyCode(string code)
|
|
{
|
|
string assetVariety = "";
|
|
if (code.StartsWith("60") || code.StartsWith("000"))
|
|
{ //主板
|
|
assetVariety = "ZB";
|
|
}
|
|
else if (code.StartsWith("002"))
|
|
{//中小板
|
|
assetVariety = "ZXB";
|
|
}
|
|
else if (code.StartsWith("300"))
|
|
{//创业板
|
|
assetVariety = "CYB";
|
|
}
|
|
else if (code.StartsWith("688"))
|
|
{//科创版
|
|
assetVariety = "KCB";
|
|
}
|
|
else
|
|
{
|
|
assetVariety = "JW";
|
|
}
|
|
return assetVariety;
|
|
}
|
|
|
|
private string formatQuoteUnit(string quoteUnit)
|
|
{
|
|
switch (quoteUnit)
|
|
{
|
|
case "吨":
|
|
return "t";
|
|
case "桶":
|
|
return "barrel";
|
|
case "克":
|
|
return "g";
|
|
case "千克":
|
|
return "kg";
|
|
case "500千克":
|
|
return "500kg";
|
|
case "股":
|
|
return "share";
|
|
case "点":
|
|
if (PS.Config.Company == Configuration.CompanyEnum.国投)
|
|
{
|
|
return "bp";
|
|
}
|
|
return "point";
|
|
case "基点":
|
|
return "bp";
|
|
case "units":
|
|
return "units";
|
|
default:
|
|
return "o";
|
|
}
|
|
//return quoteUnit;
|
|
}
|
|
|
|
protected double formatEndStockEqvNotionalReal(double? stockEqvNotional, double percentRate)
|
|
{
|
|
double? result = stockEqvNotional * percentRate;
|
|
return Math.Abs(OtcFormatHelper.FormatValue((result ?? 0), 4));
|
|
}
|
|
|
|
protected string JoinArr(string[] arr)
|
|
{
|
|
HashSet<string> singleArr = new HashSet<string>(arr);
|
|
singleArr.Remove("");
|
|
return string.Join(",", singleArr);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 是否为期权
|
|
/// </summary>
|
|
/// <param name="t"></param>
|
|
/// <returns></returns>
|
|
private bool IsOption(trade t)
|
|
{
|
|
return t.IsOption() || t.TradeType == "自定义交易";
|
|
}
|
|
}
|
|
}
|