2585 lines
103 KiB
C#
2585 lines
103 KiB
C#
using BaseOUDAL;
|
||
using Qdp.Pricing.Base.Enums;
|
||
using Qdp.Pricing.Base.Implementations;
|
||
using System.Data;
|
||
using System.Globalization;
|
||
using System.Text.RegularExpressions;
|
||
using YLErp.Commons;
|
||
using YLErp.DBModels.Consts;
|
||
using YLErp.DBModels.Enums;
|
||
using YLErp.DBModels.Helpers;
|
||
using YLErp.Enums;
|
||
using YLErp.Helpers;
|
||
using YLErp.Model.Enum;
|
||
using YLErp.Models;
|
||
using YLErp.Modules.DictionaryModule;
|
||
using YLErp.Modules.SalesModule;
|
||
using YLErp.Modules.StructureModule;
|
||
using YLErp.Modules.TradeModule.DealModule;
|
||
using YLErp.QdpModule;
|
||
|
||
namespace YLErp.Modules.TradeModule.OrderModule
|
||
{
|
||
/// <summary>
|
||
/// 场外交易导入服务
|
||
/// </summary>
|
||
public class OtcTradeImportService : YLBaseService
|
||
{
|
||
public OtcTradeImportService(YLBaseService baseService) : base(baseService)
|
||
{
|
||
}
|
||
|
||
public OtcTradeImportService(OptUserInfo userInfo) : base(userInfo)
|
||
{
|
||
}
|
||
|
||
/// <summary>
|
||
/// 从excel文件中导入场外期权交易
|
||
/// </summary>
|
||
public void ImportOptionTradesFromExcel(Stream streamIn, out int totalNum, out int successNum)
|
||
{
|
||
new OtcOptionTradeImportService(this).ImportFromExcel(streamIn, out totalNum, out successNum);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 从excel文件中读取场外期权交易导入
|
||
/// </summary>
|
||
public List<OtcOptionTradeFullEx> ReadOptionTradesFromExcel(Stream streamIn)
|
||
{
|
||
return new OtcOptionTradeImportService(this).ReadFromExcel(streamIn);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 场外期权交易导入服务
|
||
/// </summary>
|
||
class OtcOptionTradeImportService : OtcTradeSaveService
|
||
{
|
||
public OtcOptionTradeImportService(OptUserInfo userInfo) : base(userInfo)
|
||
{
|
||
|
||
}
|
||
|
||
public OtcOptionTradeImportService(YLBaseService baseService) : base(baseService)
|
||
{
|
||
|
||
}
|
||
|
||
private readonly ErpBaseContext basedb = new ErpBaseContext();
|
||
|
||
/// <summary>
|
||
/// 导入交易
|
||
/// </summary>
|
||
/// <param name="streamIn"></param>
|
||
/// <param name="totalNum">当前文件中的目标期权总条数</param>
|
||
/// <param name="successNum">成功入库的数量</param>
|
||
public void ImportFromExcel(Stream streamIn, out int totalNum, out int successNum)
|
||
{
|
||
totalNum = 0;
|
||
successNum = 0;
|
||
|
||
var rowIndex = 0;
|
||
var groupTradeList = new List<OtcOptionTradeFullEx>();
|
||
var structureTradeList = new List<OtcOptionTradeFullEx>();
|
||
|
||
try
|
||
{
|
||
var ds = Office.ExcelHelper.ReadExcelAsDataSet(stream: streamIn, includeSheetIndexs: new[] { 0 }, headerRowIndex: 0);
|
||
|
||
if (ds.Tables.Count < 1 || ds.Tables[0].Rows.Count < 3)
|
||
{
|
||
throw new ServiceException("读取导入数据失败:数据为空") { Tag = "111" };
|
||
}
|
||
|
||
if (ds.Tables[0].TableName != "交易导入模板V3")
|
||
{
|
||
throw new ServiceException("请使用最新交易导入模板V3") { Tag = "111" };
|
||
}
|
||
|
||
var table = ds.Tables[0];
|
||
var reader = new DataRowReader(table);
|
||
|
||
rowIndex = 2;
|
||
totalNum = table.Rows.Count - rowIndex;
|
||
|
||
foreach (var row in table.Rows.Cast<DataRow>().Skip(2))
|
||
{
|
||
rowIndex++;
|
||
|
||
if (row.ItemArray.All(n => string.IsNullOrWhiteSpace(n?.ToString())))
|
||
{
|
||
totalNum--;
|
||
continue;
|
||
}
|
||
|
||
reader.SetDataRow(row);
|
||
|
||
//映射导入数据到交易对象
|
||
var importTrade = MapTrade(reader);
|
||
|
||
if (groupTradeList.Count > 0 && importTrade.IsGroup != 2)
|
||
{
|
||
rowIndex -= 1;
|
||
//save 方法里会清空列表
|
||
successNum += InnerSaveGroupTrade(groupTradeList);
|
||
rowIndex += 1;
|
||
}
|
||
|
||
//保存结构化交易
|
||
if (structureTradeList.Count > 0 &&
|
||
(structureTradeList[0].StructureType != importTrade.StructureType || structureTradeList[0].CalcId != importTrade.CalcId))
|
||
{
|
||
rowIndex -= 1;
|
||
successNum += InnerSaveStructureTrade(structureTradeList);
|
||
rowIndex += 1;
|
||
}
|
||
|
||
//当上一个组合保存完毕,接下来又遇到组合主交易时
|
||
if (importTrade.IsGroup == 1 || importTrade.IsGroup == 2)
|
||
{
|
||
groupTradeList.Add(importTrade);
|
||
}
|
||
//当上一个组合保存完毕,接下来遇到非组合主交易时
|
||
else
|
||
{
|
||
//结构化交易
|
||
if (!string.IsNullOrWhiteSpace(importTrade.StructureType) && importTrade.TradeType != "自定义交易")
|
||
{
|
||
structureTradeList.Add(importTrade);
|
||
}
|
||
//单笔交易
|
||
else
|
||
{
|
||
InnerSaveTrade(importTrade);
|
||
successNum++;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
catch (ServiceException se)
|
||
{
|
||
if (se.Tag != null)
|
||
{
|
||
throw;
|
||
}
|
||
|
||
throw new ServiceException($"已成功导入{successNum}条;\n第{rowIndex}行,{se.Message}");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogFactory.GetLogger("导入期权交易").Error(ex);
|
||
throw new ServiceException($"已成功导入{successNum}条,\n第{rowIndex}行,发生错误:{ex.Message}", ex);
|
||
}
|
||
|
||
try
|
||
{
|
||
//保存组合交易
|
||
if (groupTradeList.Count > 0)
|
||
{
|
||
rowIndex -= (groupTradeList.Count - 1);
|
||
successNum += InnerSaveGroupTrade(groupTradeList);
|
||
}
|
||
|
||
//保存结构化
|
||
if (structureTradeList.Count > 0)
|
||
{
|
||
rowIndex -= (structureTradeList.Count - 1);
|
||
successNum += InnerSaveStructureTrade(structureTradeList);
|
||
}
|
||
}
|
||
catch (ServiceException se)
|
||
{
|
||
if (se.Tag != null)
|
||
{
|
||
throw;
|
||
}
|
||
|
||
throw new ServiceException($"已成功导入{successNum}条;\n第{rowIndex}行开始的结构化/组合交易中,{se.Message}");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LogFactory.GetLogger("导入期权交易").Error(ex);
|
||
throw new ServiceException($"已成功导入{successNum}条,\n第{rowIndex}行开始的结构化/组合交易中,发生错误:{ex.Message}", ex);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 导入交易
|
||
/// </summary>
|
||
public List<OtcOptionTradeFullEx> ReadFromExcel(Stream streamIn)
|
||
{
|
||
var rowIndex = 0;
|
||
|
||
var ds = Office.ExcelHelper.ReadExcelAsDataSet(streamIn, new[] { 0 }, 0);
|
||
|
||
if (ds.Tables.Count < 1 || ds.Tables[0].Rows.Count < 3)
|
||
{
|
||
throw new ServiceException("读取导入数据失败:数据为空") { Tag = "111" };
|
||
}
|
||
|
||
rowIndex = 2;
|
||
|
||
var table = ds.Tables[0];
|
||
var reader = new DataRowReader(table);
|
||
|
||
var list = new List<OtcOptionTradeFullEx>(table.Rows.Count);
|
||
|
||
foreach (var row in table.Rows.Cast<DataRow>().Skip(2))
|
||
{
|
||
rowIndex++;
|
||
|
||
if (row.ItemArray.All(n => string.IsNullOrWhiteSpace(n?.ToString())))
|
||
{
|
||
continue;
|
||
}
|
||
|
||
reader.SetDataRow(row);
|
||
|
||
//映射导入数据到交易对象
|
||
var importTrade = MapTrade(reader);
|
||
|
||
list.Add(importTrade);
|
||
}
|
||
|
||
return list;
|
||
}
|
||
|
||
//保存交易
|
||
private void InnerSaveTrade(OtcOptionTradeFullEx importTrade)
|
||
{
|
||
using (var trans = DbContext.Database.BeginTransaction())
|
||
{
|
||
var dbTrade = SaveOptionTradeFromApiOrImport(importTrade, TradeSourceEnum.导入交易, trans);
|
||
if (importTrade.CloseRequest != null)
|
||
{
|
||
ExecuteClose(dbTrade, importTrade.CloseRequest);
|
||
}
|
||
trans.Commit();
|
||
}
|
||
}
|
||
|
||
//保存结构化交易
|
||
private int InnerSaveStructureTrade(List<OtcOptionTradeFullEx> structureTradeList)
|
||
{
|
||
var count = structureTradeList.Count;
|
||
|
||
if (count < 2)
|
||
{
|
||
throw new ServiceException("结构化交易组合必须有两条腿及以上");
|
||
}
|
||
|
||
if (structureTradeList.Select(n => n.TradeNumber).Distinct().Count() > 1)
|
||
{
|
||
throw new ServiceException("结构化交易组合必须有相同的交易编号");
|
||
}
|
||
|
||
//在底层服务已经做了验证:结构化交易组合必须有相同的交易对手方
|
||
|
||
using (var trans = DbContext.Database.BeginTransaction())
|
||
{
|
||
var dbTrade = SaveStructureOptionTradeFromApiOrImport(new StructureOrderRequest
|
||
{
|
||
Trades = structureTradeList,
|
||
StructureType = structureTradeList[0].StructureType
|
||
}, TradeSourceEnum.导入交易, trans);
|
||
|
||
var subTrades = dbTrade.SubTrades.ToArray();
|
||
|
||
DbContext.trade.Attach(dbTrade);
|
||
|
||
for (var i = 0; i < count; i++)
|
||
{
|
||
DbContext.trade.Attach(subTrades[i]);
|
||
ExecuteClose(subTrades[i], structureTradeList[i].CloseRequest);
|
||
}
|
||
|
||
structureTradeList.Clear();
|
||
|
||
trans.Commit();
|
||
}
|
||
|
||
return count;
|
||
}
|
||
|
||
private int InnerSaveGroupTrade(List<OtcOptionTradeFullEx> structureTradeList)
|
||
{
|
||
var count = structureTradeList.Count;
|
||
|
||
if (count <= 2)
|
||
{
|
||
throw new ServiceException("组合交易必须有两条子交易及以上");
|
||
}
|
||
|
||
//在底层服务已经做了验证:结构化交易组合必须有相同的交易对手方
|
||
|
||
using (var trans = DbContext.Database.BeginTransaction())
|
||
{
|
||
var dbTrade = SaveGroupOptionTradeFromApiOrImport(new StructureOrderRequest
|
||
{
|
||
Trades = structureTradeList
|
||
}, TradeSourceEnum.导入交易, trans);
|
||
|
||
structureTradeList.Clear();
|
||
|
||
trans.Commit();
|
||
}
|
||
|
||
return count;
|
||
}
|
||
|
||
private OtcOptionTradeFullEx MapTrade(DataRowReader reader)
|
||
{
|
||
var td = new OtcOptionTradeFullEx
|
||
{
|
||
//基本要素
|
||
TradeNumber = reader.GetString("交易编号"),
|
||
AssetBookName = reader.GetString("簿记账户名称", true),
|
||
TraderName = reader.GetString("交易员名称", true),
|
||
TradeType = reader.GetString("结构类型", true),
|
||
IsGroup = reader.GetString("组合交易类型") == "组合主交易" ? 1 : (reader.GetString("组合交易类型") == "组合子交易" ? 2 : 0),
|
||
ClientNumber = reader.GetString("交易对手方编号"),
|
||
ClientName = reader.GetString("交易对手方名称")
|
||
};
|
||
if (string.IsNullOrWhiteSpace(td.ClientNumber) && string.IsNullOrWhiteSpace(td.ClientName))
|
||
{
|
||
throw new ServiceException("交易对手方编号 或 交易对手方名称 必须填写其中一个");
|
||
}
|
||
|
||
var tradeTypes = new StructureService(OptUser).QueryStructureInfo(td.IsGroup == 1 ? StructureRangeEnum.BALCK_TRADE : StructureRangeEnum.CUSTOM_TRADE).Select(O => O.StructureName);
|
||
if (!ConsTrade.AllTradeTypes.Contains(td.TradeType) && td.TradeType != "累计期权三段式")
|
||
{
|
||
if (!tradeTypes.Contains(td.TradeType))
|
||
{
|
||
throw new ServiceException($"交易类型{td.TradeType}不合法");
|
||
}
|
||
if (td.IsGroup == 0)
|
||
{
|
||
td.StructureType = td.TradeType;
|
||
td.TradeType = "自定义交易";
|
||
}
|
||
}
|
||
|
||
var required = td.TradeType != "自定义交易" && td.TradeType != "现金流交易" && td.IsGroup != 1;
|
||
|
||
//交易方向
|
||
|
||
td.BuySell = reader.GetString("交易方向", true);
|
||
|
||
switch (td.BuySell)
|
||
{
|
||
case "Buy":
|
||
td.BuySell = "买入";
|
||
break;
|
||
case "Sell":
|
||
td.BuySell = "卖出";
|
||
break;
|
||
case "买入":
|
||
case "卖出":
|
||
break;
|
||
default:
|
||
throw new ServiceException("交易方向 填写错误:" + td.BuySell);
|
||
}
|
||
|
||
//看涨看跌
|
||
|
||
td.OptionType = reader.GetString("看涨看跌", required);
|
||
|
||
if (td.TradeType == "Risky期权" && td.OptionType != "看涨" && td.OptionType != "Call")
|
||
{
|
||
throw new ServiceException("Risky期权看涨看跌 必须为看涨");
|
||
}
|
||
|
||
switch (td.OptionType)
|
||
{
|
||
case "看涨":
|
||
case "看跌":
|
||
break;
|
||
case "Call":
|
||
td.OptionType = "看涨";
|
||
break;
|
||
case "Put":
|
||
td.OptionType = "看跌";
|
||
break;
|
||
default:
|
||
if (!string.IsNullOrWhiteSpace(td.OptionType))
|
||
{
|
||
throw new ServiceException("看涨看跌 填写错误:" + td.OptionType);
|
||
}
|
||
if (required)
|
||
{
|
||
throw new ServiceException("看涨看跌 必须填写");
|
||
}
|
||
break;
|
||
}
|
||
|
||
//行权方式(需要转换预校验会用到)
|
||
|
||
td.ExerciseMode = reader.GetString("行权方式", required);
|
||
|
||
switch (td.ExerciseMode)
|
||
{
|
||
case "欧式":
|
||
td.ExerciseMode = "European";
|
||
break;
|
||
case "美式":
|
||
td.ExerciseMode = "American";
|
||
break;
|
||
case "European":
|
||
case "American":
|
||
break;
|
||
default:
|
||
if (!string.IsNullOrWhiteSpace(td.ExerciseMode))
|
||
{
|
||
throw new ServiceException("行权方式 填写错误:" + td.ExerciseMode);
|
||
}
|
||
if (required)
|
||
{
|
||
throw new ServiceException("行权方式 必须填写");
|
||
}
|
||
break;
|
||
}
|
||
|
||
//交易日期
|
||
td.TradeDate = reader.GetDate("交易日期", true);
|
||
|
||
//标的代码
|
||
td.UnderlyingCode = reader.GetString("标的代码", td.TradeType != "现金流交易");
|
||
|
||
td.ExerciseDate = reader.GetDate("到期日期", true);
|
||
|
||
//结算日期
|
||
td.SettlementDate = reader.GetDate("结算日期");
|
||
|
||
if (PS.Config.Is浙期)
|
||
{
|
||
var flag = false;
|
||
|
||
var codes = new List<string>();
|
||
|
||
codes.Add(td.UnderlyingCode);
|
||
|
||
//判断标的是否为现货
|
||
flag = UndelyingHelper.IsCodesExistsCommoditySpot(codes, (um) =>
|
||
{
|
||
return um.UnderlyingInstrumentType == "CommoditySpot" ? true : false;
|
||
});
|
||
|
||
if (flag)
|
||
{
|
||
var dateStr = reader.GetDate("实际到期日期", false) ?? td.ExerciseDate;
|
||
|
||
|
||
td.MetaDic["ActualExerciseDate"] = dateStr.Value.ToString("yyyy-MM-dd");
|
||
}
|
||
|
||
}
|
||
|
||
//判断是否组合标的
|
||
var synthetic = DataCacheProvider.GetUnderlyingDataSource().GetSyntheticUnderlying(td.UnderlyingCode);
|
||
|
||
//标的价格(组合标的时忽略并由组合标的数据算出)
|
||
if (synthetic == null)
|
||
{
|
||
td.SpotPrice = reader.GetDouble("标的价格", td.TradeType != "现金流交易");
|
||
}
|
||
|
||
//相对行权价
|
||
td.IsMoneynessOption = reader.GetString("相对行权价");
|
||
|
||
//执行价格(亚式浮动行权价类型时非必填)
|
||
td.Strike = reader.GetDoubleOrPercent("执行价格", false, td.IsMoneynessOption == "是");
|
||
|
||
double? dtemp;
|
||
|
||
//权利金单价
|
||
td.TradeSinglePrice = dtemp = reader.GetDouble("权利金单价");
|
||
if (dtemp.HasValue)
|
||
{
|
||
td.HasFlag |= OtcOptionTradeFullHasFlag.TradeSinglePrice;
|
||
}
|
||
else
|
||
{
|
||
//权利金总额
|
||
td.TradePrice = dtemp = reader.GetDouble("权利金总额");
|
||
|
||
if (dtemp.HasValue)
|
||
{
|
||
td.HasFlag |= OtcOptionTradeFullHasFlag.TradePrice;
|
||
}
|
||
else
|
||
{
|
||
//期权费率
|
||
td.PremiumRate = dtemp = reader.GetPercent("期权费率");
|
||
if (dtemp.HasValue)
|
||
{
|
||
td.HasFlag |= OtcOptionTradeFullHasFlag.PremiumRate;
|
||
}
|
||
}
|
||
}
|
||
var underlying = DataCacheProvider.GetUnderlyingDataSource().GetData(td.UnderlyingCode);
|
||
if (underlying == null)
|
||
{
|
||
throw new ServiceException($"标的{td.UnderlyingCode}信息不存在");
|
||
}
|
||
var variety = DataCacheProvider.GetVarietyDataSource().GetData(underlying.UnderlyingTypeId);
|
||
//币种
|
||
td.QuoteCurrency = variety?.QuoteCurrency;
|
||
|
||
//汇率
|
||
var ExchangeRate = reader.GetDouble("汇率", false);
|
||
if (ExchangeRate != null)
|
||
{
|
||
if (!string.IsNullOrEmpty(td.QuoteCurrency))
|
||
{
|
||
td.MetaDic.Add("ExchangeRate", ExchangeRate.ToString());
|
||
}
|
||
}
|
||
|
||
//参与率(百分比格式)
|
||
td.ParticipationRate = reader.GetPercent("参与率", td.TradeType == "Risky期权") ?? 1;
|
||
if(td.ParticipationRate.HasValue && td.ParticipationRate.Value < 0)
|
||
{
|
||
throw new ServiceException("[基本要素]参与率 不能小于0,当前填写值:" + td.ParticipationRate);
|
||
}
|
||
|
||
//有效交易份额
|
||
td.Notional = reader.GetDouble("有效交易份额") ?? 0;
|
||
|
||
//交易份额(虚拟)
|
||
var notionalV = reader.GetDouble("交易份额");
|
||
if (notionalV.HasValue && notionalV.Value > 0)
|
||
{
|
||
var ttypes = new[] { "凤凰期权", "雪球期权" };
|
||
td.Notional = notionalV.Value * (ttypes.Contains(td.TradeType) ? 1 : td.AnnualizeFactor ?? 1) * (td.ParticipationRate ?? 1);
|
||
}
|
||
|
||
if (td.TradeType != "累计期权" && td.TradeType != "累计期权三段式")
|
||
{
|
||
//名义本金
|
||
td.StockEqvNotional = reader.GetDouble("名义本金") ?? 0;
|
||
|
||
//有效名义本金
|
||
td.StockEqvNotionalReal = reader.GetDouble("有效名义本金") ?? reader.GetDouble("实际名义本金") ?? 0;
|
||
}
|
||
|
||
//期权年化
|
||
td.IsAnnualized = reader.GetString("期权年化") == "是";
|
||
if (td.IsAnnualized)
|
||
{
|
||
var annualizeFactor = reader.GetString("年化系数");
|
||
if (!string.IsNullOrEmpty(annualizeFactor))
|
||
{
|
||
var index = annualizeFactor.IndexOf('/');
|
||
if (index < 0)
|
||
{
|
||
throw new ServiceException("[基本要素]年化系数 不是正确格式(正确格式样例:25/244),当前填写值:" + annualizeFactor);
|
||
}
|
||
if (!int.TryParse(annualizeFactor.Substring(0, index).TrimEnd(), out var ttmDays) || ttmDays < 0)
|
||
{
|
||
throw new ServiceException("[基本要素]年化系数 '/'左边不是正确的天数:" + annualizeFactor);
|
||
}
|
||
if (!int.TryParse(annualizeFactor.Substring(index + 1).TrimStart(), out var daysOfYear) || daysOfYear < 1)
|
||
{
|
||
throw new ServiceException("[基本要素]年化系数 '/'右边不是正确的年天数:" + annualizeFactor);
|
||
}
|
||
td.AnnualizeFactor = (double)ttmDays / daysOfYear;
|
||
td.MetaDic[nameof(td.AnnualizeFactor)] = annualizeFactor;
|
||
}
|
||
}
|
||
|
||
bool IsUsePremiumRate = false;
|
||
if (td.HasFlag == OtcOptionTradeFullHasFlag.PremiumRate)
|
||
{
|
||
IsUsePremiumRate = true;
|
||
}
|
||
else if (td.HasFlag == OtcOptionTradeFullHasFlag.TradePrice)
|
||
{
|
||
IsUsePremiumRate = !(td.Notional > 0);
|
||
}
|
||
|
||
//保底收益
|
||
td.OriginalPrincipalSum = reader.GetDouble("保底收益总额") ?? 0;
|
||
double stockEqvNotional = td.StockEqvNotional > 0 ? td.StockEqvNotional : td.StockEqvNotionalReal;
|
||
if (IsUsePremiumRate)
|
||
{
|
||
td.PrincipalRateWrite = reader.GetPercent("保底收益") ?? 0;
|
||
td.SinglePrincipalWrite = TradeHelper.GetSinglePrincipal(stockEqvNotional, td.PrincipalRateWrite, td.Notional, td.AnnualizeFactor);
|
||
if (reader.GetDouble("保底收益总额") == null && reader.GetPercent("保底收益") != null)
|
||
{
|
||
td.OriginalPrincipalSum = TradeHelper.GetRateToPrincipalSum(stockEqvNotional, td.PrincipalRateWrite, td.AnnualizeFactor);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
td.SinglePrincipalWrite = reader.GetDouble("保底收益") ?? 0;
|
||
td.PrincipalRateWrite = TradeHelper.GetPrincipalRate(stockEqvNotional, td.SinglePrincipalWrite, td.Notional, td.AnnualizeFactor);
|
||
if (reader.GetDouble("保底收益总额") == null && reader.GetPercent("保底收益") != null)
|
||
{
|
||
td.OriginalPrincipalSum = TradeHelper.GetSingleToPrincipalSum(td.Notional, td.SinglePrincipalWrite);
|
||
}
|
||
}
|
||
|
||
//无风险利率(百分比格式)
|
||
td.NoRiskRate = reader.GetPercent("无风险利率");
|
||
|
||
//分红率(百分比格式)
|
||
td.DividendRate = reader.GetPercent("分红率");
|
||
|
||
//销售员 提成方式 提成金额
|
||
var salesmen = reader.GetString("销售员");
|
||
|
||
if (PS.Config.ErpElement.SaleMode == YLErp.Configuration.Enums.SaleMode.CustomerManager)
|
||
{
|
||
//客户经理 提成方式 提成金额
|
||
salesmen = reader.GetString("客户经理");
|
||
}
|
||
if (!string.IsNullOrWhiteSpace(salesmen))
|
||
{
|
||
var salesCommission = new TradeSalesCommissionInfo
|
||
{
|
||
CommissionFixed = reader.GetString("提成方式") == "绝对值"
|
||
};
|
||
var commission = reader.GetPercent("提成金额", false);
|
||
if (commission < 0)
|
||
{
|
||
throw new ServiceException("销售佣金金额 不能小于0");
|
||
}
|
||
//todo 多人
|
||
salesCommission.SalesIds = GetSalesIds(salesmen);
|
||
salesCommission.Commission = commission;
|
||
salesCommission.ClientId = GetClientId(td.ClientName);
|
||
salesCommission.SaleMode = (int)PS.Config.ErpElement.SaleMode;
|
||
td.SalesCommission = salesCommission;
|
||
}
|
||
|
||
//备注
|
||
td.Comments = reader.GetString("备注");
|
||
|
||
//成交波动率(必需&百分比格式)
|
||
td.TradeOpenVolatility = reader.GetPercent("成交波动率", required);
|
||
|
||
//目标波动率(必需&百分比格式)
|
||
td.TradeCloseVolatility = reader.GetPercent("目标波动率", required);
|
||
|
||
//平滑过渡天数
|
||
td.NumOfSmoothingDays = reader.GetInt32("平滑过渡天数");
|
||
|
||
//存续天数
|
||
td.DurationDays = (short?)reader.GetInt32("存续天数");
|
||
|
||
//期末费率
|
||
td.MetaDic["期末费率"] = reader.GetString("期末费率");
|
||
|
||
//初始预付金
|
||
td.InitialMargin = reader.GetDouble("初始预付金");
|
||
|
||
//计算标识
|
||
td.CalcFlag = reader.GetInt32("计算标识") ?? 0;
|
||
|
||
//收益结算
|
||
var SettlementTypeStr = reader.GetString("收益结算");
|
||
switch (SettlementTypeStr)
|
||
{
|
||
case "收盘价":
|
||
td.SettlementType = SettlementTypeEnum.ClosePrice;
|
||
break;
|
||
case "结算价":
|
||
td.SettlementType = SettlementTypeEnum.SettlePrice;
|
||
break;
|
||
case "参考价":
|
||
td.SettlementType = SettlementTypeEnum.ReferencePrice;
|
||
break;
|
||
default:
|
||
if (!string.IsNullOrWhiteSpace(SettlementTypeStr))
|
||
{
|
||
throw new ServiceException("收益结算 填写错误:" + SettlementTypeStr);
|
||
}
|
||
break;
|
||
}
|
||
|
||
//结构说明
|
||
td.StructureIntroduction = reader.GetString("结构说明");
|
||
|
||
//自定义保存
|
||
if (td.TradeType == "自定义交易" || (td.IsGroup == 1 && td.TradeType != "气囊结构"))
|
||
{
|
||
reader.SetTopType("自定义属性");
|
||
ReadCustomizedOption(reader, td);
|
||
}
|
||
else
|
||
{
|
||
if (reader.CustomField.Length > 0)
|
||
{
|
||
reader.SetTopType("自定义属性");
|
||
var propertys = new List<ExtendInfoModel>();
|
||
foreach (var item in reader.CustomField)
|
||
{
|
||
var cusofield = reader.GetString(item);
|
||
if (!string.IsNullOrWhiteSpace(cusofield))
|
||
{
|
||
propertys.Add(new ExtendInfoModel()
|
||
{
|
||
name = item,
|
||
value = cusofield
|
||
});
|
||
}
|
||
}
|
||
td.MetaDic[ConsTradeMetaKey.tradeExtendInfo] = "自定义";
|
||
td.ExtendInfo = JsonHelper.Serialize(propertys);
|
||
}
|
||
|
||
|
||
reader.SetTopType(td.TradeType);
|
||
|
||
switch (td.TradeType)
|
||
{
|
||
case "香草期权":
|
||
case "合成价差期权":
|
||
break;
|
||
case "亚式期权":
|
||
ReadAsiaOption(reader, td);
|
||
break;
|
||
case "障碍期权":
|
||
ReadBarrierOption(reader, td);
|
||
break;
|
||
case "双鲨期权":
|
||
ReadDoubleSharkOption(reader, td);
|
||
break;
|
||
case "二元期权":
|
||
ReadBinaryOption(reader, td);
|
||
break;
|
||
case "区间累积期权":
|
||
ReadRangeAccOption(reader, td);
|
||
break;
|
||
case "气囊结构":
|
||
ReadAirBagOption(reader, td);
|
||
break;
|
||
case "收益增强结构":
|
||
//年化增强收益(必需&百分比格式)
|
||
td.AnnualizedEnhanceRate = reader.GetPercent("年化增强收益", true).Value;
|
||
break;
|
||
case "凤凰期权":
|
||
ReadAutoCallOption(reader, td);
|
||
break;
|
||
case "雪球期权":
|
||
ReadSnowBallOption(reader, td);
|
||
break;
|
||
case "累计期权":
|
||
ReadAccumulatorOption(reader, td);
|
||
break;
|
||
case "累计期权三段式":
|
||
ReadAccumulatorOptionSegmented(reader, td);
|
||
break;
|
||
case "现金流交易":
|
||
ReadCashFlow(reader, td);
|
||
break;
|
||
case "Risky期权":
|
||
ReadRisky(reader, td);
|
||
break;
|
||
default:
|
||
throw new ServiceException("交易类型 填写错误,不支持:" + td.TradeType);
|
||
}
|
||
}
|
||
|
||
if (!td.Strike.HasValue && TradeHelper.IsStrikeRequired(td))
|
||
{
|
||
throw new ServiceException("[基本要素]执行价格 必须填写");
|
||
}
|
||
//了结信息
|
||
td.CloseRequest = ReadCloseData(reader, td);
|
||
|
||
if (td.CloseRequest != null && td.SalesCommission != null)
|
||
{
|
||
td.SalesCommission.IsValid = true;
|
||
}
|
||
|
||
if (td.TradeType != "自定义交易")
|
||
{
|
||
//结构化交易
|
||
reader.SetTopType("结构化交易");
|
||
|
||
td.StructureType = reader.GetString("结构化类型");
|
||
if (!string.IsNullOrWhiteSpace(td.StructureType))
|
||
{
|
||
td.CalcId = reader.GetString("组合标识");
|
||
td.MetaDic["合约编号"] = reader.GetString("合约编号");
|
||
if (string.IsNullOrEmpty(td.CalcId))
|
||
{
|
||
td.CalcId = td.TradeNumber.TrimToNull() ?? td.MetaDic["合约编号"];
|
||
}
|
||
if (string.IsNullOrEmpty(td.CalcId))
|
||
{
|
||
throw new ServiceException($"[结构化交易]组合标识 必须填写");
|
||
}
|
||
}
|
||
}
|
||
|
||
//读取组合标的
|
||
if (synthetic != null)
|
||
{
|
||
reader.SetTopType("组合标的");
|
||
|
||
var sulist = new List<UnderlyingPriceModel>(4);
|
||
var codeSet = synthetic.GetUnderlyingCodes().ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||
|
||
for (var i = 1; i <= 4; i++)
|
||
{
|
||
var code = reader.GetString("标的" + i + "_代码");
|
||
|
||
if (string.IsNullOrWhiteSpace(code))
|
||
{
|
||
continue;
|
||
}
|
||
|
||
if (!codeSet.Remove(code))
|
||
{
|
||
throw new ServiceException($"[组合标的]标的{i}_代码 填写错误,组合标的中不存在此标的:{code}");
|
||
}
|
||
|
||
sulist.Add(new UnderlyingPriceModel { UnderlyingCode = code, Price = reader.GetDouble("标的" + i + "_价格", true).Value });
|
||
}
|
||
|
||
if (codeSet.Any())
|
||
{
|
||
throw new ServiceException("[组合标的]未填写完整");
|
||
}
|
||
|
||
td.SyntheticPrices = sulist;
|
||
}
|
||
|
||
return td;
|
||
}
|
||
|
||
#region---读取奇异期权字段----
|
||
|
||
//亚式期权
|
||
private void ReadAsiaOption(DataRowReader reader, OtcOptionTradeFull td)
|
||
{
|
||
//均价起算日
|
||
td.AveragingPeriodStartDate = reader.GetDate("均价起算日");
|
||
//均价计算
|
||
td.PayoffType = reader.GetString("均价计算");
|
||
|
||
switch (td.PayoffType)
|
||
{
|
||
case "算术平均":
|
||
td.PayoffType = "ArithmeticAverage";
|
||
break;
|
||
case "几何平均":
|
||
td.PayoffType = "GeometricAverage";
|
||
break;
|
||
case "算术平均(离散)":
|
||
case "算术平均(离散)":
|
||
td.PayoffType = "DiscreteArithmeticAverage";
|
||
break;
|
||
case "增强算术平均":
|
||
td.PayoffType = "EnhancedArithmeticAverage";
|
||
break;
|
||
case "ArithmeticAverage":
|
||
case "GeometricAverage":
|
||
case "DiscreteArithmeticAverage":
|
||
case "EnhancedArithmeticAverage":
|
||
default:
|
||
td.PayoffType = string.IsNullOrWhiteSpace(td.PayoffType)
|
||
? "ArithmeticAverage"
|
||
: throw new ServiceException("[亚式期权]均价计算 填写错误,不支持:" + td.PayoffType);
|
||
break;
|
||
}
|
||
|
||
if (td.PayoffType == "EnhancedArithmeticAverage")
|
||
{
|
||
td.SettleMode = reader.GetString("结算类型");
|
||
switch (td.SettleMode)
|
||
{
|
||
case "递延":
|
||
case "AtEnd":
|
||
td.SettleMode = "AtEnd";
|
||
break;
|
||
case "立即":
|
||
case "AtHit":
|
||
default:
|
||
td.SettleMode = "AtHit";
|
||
break;
|
||
}
|
||
}
|
||
|
||
//行权价类型
|
||
td.StrikeType = reader.GetString("行权价类型");
|
||
switch (td.StrikeType)
|
||
{
|
||
case "固定行权价":
|
||
td.StrikeType = "Fixed";
|
||
break;
|
||
case "浮动行权价":
|
||
td.StrikeType = "Floating";
|
||
td.Strike = null;
|
||
break;
|
||
case "分段式":
|
||
td.StrikeType = "Segmented";
|
||
break;
|
||
case "Fixed":
|
||
case "Segmented":
|
||
break;
|
||
case "Floating":
|
||
td.Strike = null;
|
||
break;
|
||
default:
|
||
td.StrikeType = string.IsNullOrWhiteSpace(td.StrikeType)
|
||
? "Fixed" : throw new ServiceException("[亚式期权]行权价类型 填写错误,不支持:" + td.StrikeType);
|
||
break;
|
||
}
|
||
bool enhancedPriceRequired = td.PayoffType == "EnhancedArithmeticAverage" && td.StrikeType != "Floating";
|
||
td.EnhancedPrice = reader.GetDoubleOrPercent("增强价格", enhancedPriceRequired, td.IsMoneynessOptionData) ?? 0;
|
||
//杠杆率(百分比格式)
|
||
td.StrikeGearingFactor = reader.GetPercent("杠杆率");
|
||
}
|
||
|
||
//障碍期权
|
||
private void ReadBarrierOption(DataRowReader reader, OtcOptionTradeFull td)
|
||
{
|
||
var ismoneyness = td.IsMoneynessOption == "是";
|
||
|
||
//障碍类型
|
||
td.BarrierType = reader.GetString("障碍类型", true);
|
||
|
||
//障碍价格
|
||
td.BarrierLow = reader.GetDoubleOrPercent("障碍价格", true, ismoneyness).Value;
|
||
|
||
//高障碍价格
|
||
td.BarrierHigh = reader.GetDoubleOrPercent("高障碍价格", false, ismoneyness) ?? 0;
|
||
|
||
//补偿金额
|
||
td.Rebate = td.RebateRate = reader.GetDoubleOrPercent("补偿金额", false, td.IsUsePremiumRate == true);
|
||
|
||
//高障碍补偿金额
|
||
td.RebateHigh = td.RebateHighRate = reader.GetDoubleOrPercent("高障碍补偿金额", false, td.IsUsePremiumRate == true);
|
||
|
||
//障碍偏移
|
||
td.BarrierShift = reader.GetDouble("障碍偏移");
|
||
|
||
//观察方式
|
||
td.MonitorType = reader.GetString("观察方式");
|
||
|
||
//补偿按敲出日年化
|
||
td.RebateAnnualizedAtKO = reader.GetString("补偿按敲出日年化") == "是";
|
||
|
||
//补偿计息规则
|
||
td.RebateDayCount = reader.GetString("补偿计息规则") ?? "";
|
||
|
||
//补偿支付
|
||
ReadRebateType(reader, td);
|
||
|
||
//观察频率
|
||
ReadObservationDates(reader, td);
|
||
}
|
||
|
||
//双鲨期权
|
||
private void ReadDoubleSharkOption(DataRowReader reader, OtcOptionTradeFull td)
|
||
{
|
||
var ismoneyness = td.IsMoneynessOption == "是";
|
||
var isPremiumRate = td.IsUsePremiumRate == true;
|
||
|
||
//障碍价格
|
||
td.BarrierLow = reader.GetDoubleOrPercent("低障碍价格", true, ismoneyness).Value;
|
||
|
||
//高障碍价格
|
||
td.BarrierHigh = reader.GetDoubleOrPercent("高障碍价格", false, ismoneyness) ?? 0;
|
||
|
||
//高行权价
|
||
td.StrikeHigh = reader.GetDoubleOrPercent("高行权价", false, ismoneyness);
|
||
|
||
//补偿金额
|
||
td.Rebate = td.RebateRate = reader.GetDoubleOrPercent("补偿金额", false, isPremiumRate);
|
||
|
||
//高障碍补偿金额
|
||
td.RebateHigh = td.RebateHighRate = reader.GetDoubleOrPercent("高障碍补偿金额", false, isPremiumRate);
|
||
|
||
//观察方式
|
||
td.MonitorType = reader.GetString("观察方式");
|
||
|
||
//高参与率(百分比格式)
|
||
td.CallParticipationRate = reader.GetPercent("高参与率") ?? 0;
|
||
if (td.CallParticipationRate.HasValue && td.CallParticipationRate.Value < 0)
|
||
{
|
||
throw new ServiceException("[双鲨期权]高参与率 不能小于0,当前填写值:" + td.CallParticipationRate);
|
||
}
|
||
|
||
//低参与率(百分比格式)
|
||
td.PutParticipationRate = reader.GetPercent("低参与率") ?? 0;
|
||
if (td.PutParticipationRate.HasValue && td.PutParticipationRate.Value < 0)
|
||
{
|
||
throw new ServiceException("[双鲨期权]低参与率 不能小于0,当前填写值:" + td.PutParticipationRate);
|
||
}
|
||
|
||
//补偿支付
|
||
ReadRebateType(reader, td);
|
||
|
||
//观察频率
|
||
ReadObservationDates(reader, td);
|
||
}
|
||
|
||
//二元期权
|
||
private void ReadBinaryOption(DataRowReader reader, OtcOptionTradeFull td)
|
||
{
|
||
var isMoneyness = td.IsMoneynessOption == "是";
|
||
var isPremiumRate = td.IsUsePremiumRate == true;
|
||
|
||
//二元类型
|
||
td.PayoffType = reader.GetString("二元类型", true);
|
||
|
||
//补偿金额
|
||
td.CashOrNothingAmount = td.CashOrNothingAmountRate = reader.GetDoubleOrPercent("补偿金额", false, isPremiumRate);
|
||
|
||
//高障碍价格
|
||
td.BarrierHigh = reader.GetDoubleOrPercent("高障碍价格", false, isMoneyness) ?? 0;
|
||
|
||
//高障碍补偿金额
|
||
td.CashOrNothingAmountHigh = td.CashOrNothingAmountHighRate = reader.GetDoubleOrPercent("高障碍补偿金额", false, isPremiumRate);
|
||
|
||
td.MonitorType = reader.GetString("观察方式");
|
||
|
||
//补偿按敲出日年化
|
||
td.RebateAnnualizedAtKO = reader.GetString("补偿按敲出日年化") == "是";
|
||
|
||
//补偿计息规则
|
||
td.RebateDayCount = reader.GetString("补偿计息规则") ?? "";
|
||
|
||
//补偿支付
|
||
ReadRebateType(reader, td);
|
||
}
|
||
|
||
//区间累积期权
|
||
private void ReadRangeAccOption(DataRowReader reader, OtcOptionTradeFull td)
|
||
{
|
||
var isMoneyness = td.IsMoneynessOption == "是";
|
||
|
||
//区间下限
|
||
td.LowerRange = reader.GetDoubleOrPercent("区间下限", true, isMoneyness).Value;
|
||
|
||
//区间上限
|
||
td.UpperRange = reader.GetDoubleOrPercent("区间上限", true, isMoneyness).Value;
|
||
|
||
//区间收益
|
||
td.BonusRate = reader.GetPercent("区间收益") ?? 0;
|
||
|
||
//观察频率
|
||
ReadObservationDates(reader, td);
|
||
}
|
||
|
||
//气囊结构
|
||
private void ReadAirBagOption(DataRowReader reader, OtcOptionTradeFull td)
|
||
{
|
||
var isMoneyness = td.IsMoneynessOption == "是";
|
||
|
||
//障碍价格
|
||
td.BarrierLow = reader.GetDoubleOrPercent("障碍价格", true, isMoneyness).Value;
|
||
|
||
//是否离散观察
|
||
td.IsDiscreteMonitored = reader.GetString("是否离散观察") == "是";
|
||
|
||
//敲入参与率(百分比格式)
|
||
td.KIParticipationRate = reader.GetPercent("敲入参与率") ?? 0;
|
||
if (td.KIParticipationRate < 0)
|
||
{
|
||
throw new ServiceException("[气囊结构]敲入参与率 不能小于0,当前填写值:" + td.KIParticipationRate);
|
||
}
|
||
|
||
//非敲入参与率
|
||
td.NotKIParticipationRate = reader.GetPercent("未敲入参与率") ?? 0;
|
||
if (td.NotKIParticipationRate.HasValue && td.NotKIParticipationRate.Value < 0)
|
||
{
|
||
throw new ServiceException("[气囊结构]非敲入参与率 不能小于0,当前填写值:" + td.NotKIParticipationRate);
|
||
}
|
||
|
||
//收益封顶
|
||
td.HasPayoffLimit = reader.GetString("收益封顶") == "是";
|
||
|
||
if (td.HasPayoffLimit)
|
||
{
|
||
//收益封顶价格
|
||
td.HighStrike = reader.GetDoubleOrPercent("收益封顶价格", false, isMoneyness) ?? 0;
|
||
}
|
||
}
|
||
|
||
//凤凰期权
|
||
private void ReadAutoCallOption(DataRowReader reader, OtcOptionTradeFull td)
|
||
{
|
||
var isMoneyness = td.IsMoneynessOption == "是";
|
||
|
||
//票息是否年化
|
||
td.IsFixedCoupon = reader.GetString("票息年化") != "是";
|
||
|
||
//票息率
|
||
td.Coupon = reader.GetPercent("票息率") ?? 0;
|
||
|
||
//票息障碍价格
|
||
td.CouponBarrier = reader.GetDoubleOrPercent("票息障碍价格", false, isMoneyness) ?? 0;
|
||
|
||
//票息日历规则
|
||
td.CouponDayCount = reader.GetString("票息日历规则");
|
||
|
||
//票息结算方式
|
||
var couponPayType = reader.GetString("票息结算方式");
|
||
switch (couponPayType)
|
||
{
|
||
case "产生时支付":
|
||
td.CouponPayType = CouponPayTypeEnum.AtCreated;
|
||
break;
|
||
case "敲出时支付":
|
||
td.CouponPayType = CouponPayTypeEnum.AtKnockout;
|
||
break;
|
||
case "期末支付":
|
||
td.CouponPayType = CouponPayTypeEnum.AtMaturity;
|
||
break;
|
||
default:
|
||
if (!string.IsNullOrEmpty(couponPayType))
|
||
{
|
||
throw new ServiceException("[凤凰期权]票息结算方式 填写错误:" + couponPayType);
|
||
}
|
||
break;
|
||
}
|
||
|
||
td.CouponIncludeStartDate = reader.GetString("票息包含首日") == "是";
|
||
td.CouponUsePaymentDate = reader.GetString("使用支付日计息") == "是";
|
||
|
||
//敲出障碍价格
|
||
td.KOBarrier = reader.GetDoubleOrPercent("敲出障碍价格", true, isMoneyness) ?? 0;
|
||
|
||
//敲入障碍价格
|
||
td.KIBarrier = reader.GetDoubleOrPercent("敲入障碍价格", false, isMoneyness) ?? 0;
|
||
|
||
if (td.CouponPayType != CouponPayTypeEnum.AtMaturity && reader.GetString("敲入到期是否支付票息") == "否")
|
||
{
|
||
throw new ServiceException("只有期末支付时,【敲入到期是否支付票息】可以填否");
|
||
}
|
||
//敲入到期是否支付票息(期末支付时可以选择否)
|
||
td.IncludeCouponAfterKI = td.CouponPayType != CouponPayTypeEnum.AtMaturity || reader.GetString("敲入到期是否支付票息") == "是";
|
||
|
||
//敲入到期支付类别
|
||
var kiPayoffType = reader.GetString("敲入到期支付类别");
|
||
switch (kiPayoffType)
|
||
{
|
||
case "敲入转看跌":
|
||
td.KIPayoffType = KIPayoffTypeEnum.ToPutOption;
|
||
break;
|
||
case "敲入转熊市价差":
|
||
td.KIPayoffType = KIPayoffTypeEnum.ToPutSpreadOption;
|
||
break;
|
||
case "敲入转看涨":
|
||
td.KIPayoffType = KIPayoffTypeEnum.ToCallOption;
|
||
break;
|
||
case "敲入转牛市价差":
|
||
td.KIPayoffType = KIPayoffTypeEnum.ToCallSpreadOption;
|
||
break;
|
||
default:
|
||
if (!string.IsNullOrEmpty(kiPayoffType))
|
||
{
|
||
throw new ServiceException("[凤凰期权]敲入到期支付类别 填写错误:" + kiPayoffType);
|
||
}
|
||
break;
|
||
}
|
||
|
||
//敲入行权价
|
||
td.Strike = td.SpreadStrike1 = reader.GetDoubleOrPercent("敲入行权价", false, isMoneyness) ?? 0;
|
||
|
||
//封顶/封底行权价
|
||
if (td.KIPayoffType == KIPayoffTypeEnum.ToPutSpreadOption || td.KIPayoffType == KIPayoffTypeEnum.ToCallSpreadOption)
|
||
{
|
||
td.SpreadStrike = reader.GetDoubleOrPercent("封顶/封底行权价", false, isMoneyness) ?? 0;
|
||
}
|
||
|
||
//敲出观察频率
|
||
ReadKOObservationDates(reader, td);
|
||
|
||
//敲入观察频率
|
||
ReadObservationDates(reader, td);
|
||
|
||
//年化系数在保存类中检查转换
|
||
}
|
||
|
||
//雪球期权
|
||
private void ReadSnowBallOption(DataRowReader reader, OtcOptionTradeFull td)
|
||
{
|
||
var isMoneyness = td.IsMoneynessOption == "是";
|
||
|
||
//敲出障碍价格
|
||
td.KOBarrier = reader.GetDoubleOrPercent("敲出障碍价格", true, isMoneyness) ?? 0;
|
||
|
||
//敲出赔付类别
|
||
var koPayoffType = reader.GetString("敲出赔付类别");
|
||
switch (koPayoffType)
|
||
{
|
||
case "票息补偿":
|
||
td.KOPayoffType = KOPayoffTypeEnum.Rebate;
|
||
td.IsFixedCoupon = reader.GetString("票息年化") != "是";
|
||
td.KORebate = reader.GetPercent("票息率") ?? 0;
|
||
td.CouponDayCount = reader.GetString("票息日历规则");
|
||
td.AnnualizedPremiumRate = reader.GetPercent("年化期权费率");
|
||
td.CouponIncludeStartDate = reader.GetString("票息包含首日") == "是";
|
||
td.CouponUsePaymentDate = reader.GetString("使用支付日计息") == "是";
|
||
break;
|
||
case "敲出转期权":
|
||
td.KOPayoffType = KOPayoffTypeEnum.ToOption;
|
||
td.SpreadStrikeAtKO1 = reader.GetDoubleOrPercent("敲出行权价1", true, isMoneyness) ?? 0;
|
||
break;
|
||
case "敲出转看涨":
|
||
if (!ConsGlobal.CallPut.IsCall(td.CallPut))
|
||
{
|
||
throw new ServiceException("[雪球期权]敲出赔付类别 看跌时不支持:敲出转看涨");
|
||
}
|
||
td.KOPayoffType = KOPayoffTypeEnum.ToOption;
|
||
td.SpreadStrikeAtKO1 = reader.GetDoubleOrPercent("敲出行权价1", true, isMoneyness) ?? 0;
|
||
break;
|
||
case "敲出转看跌":
|
||
if (ConsGlobal.CallPut.IsCall(td.CallPut))
|
||
{
|
||
throw new ServiceException("[雪球期权]敲出赔付类别 看涨时不支持:敲出转看跌");
|
||
}
|
||
td.KOPayoffType = KOPayoffTypeEnum.ToOption;
|
||
td.SpreadStrikeAtKO1 = reader.GetDoubleOrPercent("敲出行权价1", true, isMoneyness) ?? 0;
|
||
break;
|
||
case "敲出转价差期权":
|
||
td.KOPayoffType = KOPayoffTypeEnum.ToSpreadOption;
|
||
td.SpreadStrikeAtKO1 = reader.GetDoubleOrPercent("敲出行权价1", true, isMoneyness) ?? 0;
|
||
td.SpreadStrikeAtKO = reader.GetDoubleOrPercent("敲出行权价2", true, isMoneyness) ?? 0;
|
||
break;
|
||
case "敲出转牛市价差":
|
||
if (!ConsGlobal.CallPut.IsCall(td.CallPut))
|
||
{
|
||
throw new ServiceException("[雪球期权]敲出赔付类别 看跌时不支持:敲出转牛市价差");
|
||
}
|
||
td.KOPayoffType = KOPayoffTypeEnum.ToSpreadOption;
|
||
td.SpreadStrikeAtKO1 = reader.GetDoubleOrPercent("敲出行权价1", true, isMoneyness) ?? 0;
|
||
td.SpreadStrikeAtKO = reader.GetDoubleOrPercent("敲出行权价2", true, isMoneyness) ?? 0;
|
||
break;
|
||
case "敲出转熊市价差":
|
||
if (ConsGlobal.CallPut.IsCall(td.CallPut))
|
||
{
|
||
throw new ServiceException("[雪球期权]敲出赔付类别 看涨时不支持:敲出转熊市价差");
|
||
}
|
||
td.KOPayoffType = KOPayoffTypeEnum.ToSpreadOption;
|
||
td.SpreadStrikeAtKO1 = reader.GetDoubleOrPercent("敲出行权价1", true, isMoneyness) ?? 0;
|
||
td.SpreadStrikeAtKO = reader.GetDoubleOrPercent("敲出行权价2", true, isMoneyness) ?? 0;
|
||
break;
|
||
default:
|
||
if (!string.IsNullOrEmpty(koPayoffType))
|
||
{
|
||
throw new ServiceException("[雪球期权]票息结算方式 填写错误:" + koPayoffType);
|
||
}
|
||
break;
|
||
}
|
||
|
||
//敲出支付方式
|
||
var koRebateType = reader.GetString("敲出支付方式");
|
||
switch (koRebateType)
|
||
{
|
||
case "立即支付":
|
||
td.KORebateType = RebateTypeEnum.AtHit;
|
||
break;
|
||
case "递延至期末支付":
|
||
td.KORebateType = RebateTypeEnum.AtEnd;
|
||
break;
|
||
default:
|
||
if (!string.IsNullOrEmpty(koRebateType))
|
||
{
|
||
throw new ServiceException("[雪球期权]敲出支付方式 填写错误:" + koRebateType);
|
||
}
|
||
break;
|
||
}
|
||
|
||
//票息支付日期
|
||
//td.KOObservationSettleDates = reader.GetString("票息支付日期");
|
||
//if (!string.IsNullOrWhiteSpace(td.KOObservationSettleDates))
|
||
//{
|
||
// td.KOObservationSettleDates = Regex.Replace(td.KOObservationSettleDates, "\\s0+:00:00", "");
|
||
//}
|
||
|
||
//初始敲入状态
|
||
td.IsInitialKnockedIn = reader.GetString("初始敲入") == "是";
|
||
//敲入障碍价格
|
||
td.KIBarrier = reader.GetDoubleOrPercent("敲入障碍价格", false, isMoneyness) ?? 0;
|
||
|
||
//敲入到期支付类别
|
||
var kiPayoffType = reader.GetString("敲入到期支付类别");
|
||
switch (kiPayoffType)
|
||
{
|
||
case "无":
|
||
if (td.IsInitialKnockedIn)
|
||
{
|
||
throw new ServiceException("[雪球期权]敲入到期支付类别 初始敲入为是时不支持:无");
|
||
}
|
||
td.KIPayoffType = KIPayoffTypeEnum.None;
|
||
break;
|
||
case "敲入转看跌":
|
||
if (!ConsGlobal.CallPut.IsCall(td.CallPut))
|
||
{
|
||
throw new ServiceException("[雪球期权]敲入到期支付类别 看跌时不支持:敲入转看跌");
|
||
}
|
||
td.KIPayoffType = KIPayoffTypeEnum.ToPutOption;
|
||
td.Strike = td.SpreadStrike1 = reader.GetDoubleOrPercent("敲入行权价", false, isMoneyness) ?? 0;
|
||
break;
|
||
case "敲入转熊市价差":
|
||
if (!ConsGlobal.CallPut.IsCall(td.CallPut))
|
||
{
|
||
throw new ServiceException("[雪球期权]敲入到期支付类别 看跌时不支持:敲入转熊市价差");
|
||
}
|
||
td.KIPayoffType = KIPayoffTypeEnum.ToPutSpreadOption;
|
||
td.Strike = td.SpreadStrike1 = reader.GetDoubleOrPercent("敲入行权价", false, isMoneyness) ?? 0;
|
||
td.SpreadStrike = reader.GetDoubleOrPercent("封顶/封底行权价", false, isMoneyness) ?? 0;
|
||
break;
|
||
case "敲入转看涨":
|
||
if (ConsGlobal.CallPut.IsCall(td.CallPut))
|
||
{
|
||
throw new ServiceException("[雪球期权]敲入到期支付类别 看涨时不支持:敲入转看涨");
|
||
}
|
||
td.KIPayoffType = KIPayoffTypeEnum.ToCallOption;
|
||
td.Strike = td.SpreadStrike1 = reader.GetDoubleOrPercent("敲入行权价", false, isMoneyness) ?? 0;
|
||
break;
|
||
case "敲入转牛市价差":
|
||
if (ConsGlobal.CallPut.IsCall(td.CallPut))
|
||
{
|
||
throw new ServiceException("[雪球期权]敲入到期支付类别 看涨时不支持:敲入转牛市价差");
|
||
}
|
||
td.KIPayoffType = KIPayoffTypeEnum.ToCallSpreadOption;
|
||
td.Strike = td.SpreadStrike1 = reader.GetDoubleOrPercent("敲入行权价", false, isMoneyness) ?? 0;
|
||
td.SpreadStrike = reader.GetDoubleOrPercent("封顶/封底行权价", false, isMoneyness) ?? 0;
|
||
break;
|
||
default:
|
||
if (!string.IsNullOrEmpty(kiPayoffType))
|
||
{
|
||
throw new ServiceException("[雪球期权]敲入到期支付类别 填写错误:" + kiPayoffType);
|
||
}
|
||
break;
|
||
}
|
||
|
||
//红利票息
|
||
td.Coupon = reader.GetDoubleOrPercent("红利票息", false, isMoneyness) ?? 0;
|
||
|
||
//敲出观察频率
|
||
ReadKOObservationDates(reader, td);
|
||
|
||
//敲入观察频率
|
||
ReadObservationDates(reader, td);
|
||
|
||
//年化系数在保存类中检查转换
|
||
}
|
||
//累计期权 三段式
|
||
private void ReadAccumulatorOptionSegmented(DataRowReader reader, OtcOptionTradeFull td)
|
||
{
|
||
var isMoneyness = td.IsMoneynessOption == "是";
|
||
td.AccumulatorStructureType = AccumulatorStructureTypeEnum.Segmented;
|
||
td.AccumuTradeAmount = reader.GetDouble("沽购数量") ?? 0;
|
||
//执行价2
|
||
td.Strike2 = reader.GetDoubleOrPercent("执行价2", true, isMoneyness) ?? 0;
|
||
//执行价3
|
||
td.Strike3 = reader.GetDoubleOrPercent("执行价3", true, isMoneyness) ?? 0;
|
||
//敲出障碍价格
|
||
td.KOBarrier = reader.GetDoubleOrPercent("敲出障碍价格", true, isMoneyness) ?? 0;
|
||
|
||
//上端收益类型
|
||
var payType = reader.GetString("上端收益类型");
|
||
|
||
switch (payType)
|
||
{
|
||
case "浮动":
|
||
td.PayoffType = "浮动";
|
||
break;
|
||
case "固定(票息)":
|
||
td.PayoffType = "固定";
|
||
break;
|
||
default:
|
||
if (!string.IsNullOrEmpty(payType))
|
||
{
|
||
throw new ServiceException("[累计期权三段式]上端收益类型 填写错误:" + payType);
|
||
}
|
||
td.PayoffType = "浮动";
|
||
break;
|
||
}
|
||
|
||
if (td.PayoffType == "固定")
|
||
{
|
||
//票息率
|
||
td.Coupon = reader.TryGetDoubleOrPercent("票息1", true, out var percent) ?? 0;
|
||
td.Coupon2 = reader.TryGetDoubleOrPercent("票息2", true, out var percent1) ?? 0;
|
||
td.CouponPercent = percent;
|
||
|
||
//票息是否年化
|
||
td.IsFixedCoupon = reader.GetString("票息年化") != "是";
|
||
|
||
//票息日历规则
|
||
td.CouponDayCount = reader.GetString("票息日历规则");
|
||
|
||
//乘数1
|
||
td.Multiplier = reader.GetDouble("乘数1", false) ?? 0;
|
||
//乘数2
|
||
td.Multiplier2 = reader.GetDouble("乘数2", false) ?? 0;
|
||
//乘数3
|
||
td.Multiplier3 = reader.GetDouble("乘数3", false) ?? 0;
|
||
}
|
||
else
|
||
{
|
||
//乘数1
|
||
td.Multiplier = reader.GetDouble("乘数1", false) ?? 1;
|
||
if (td.Multiplier <= 0)
|
||
{
|
||
throw new ServiceException("[累计期权三段式]乘数1不能小于等于0 填写错误:" + td.Multiplier);
|
||
}
|
||
//乘数2
|
||
td.Multiplier2 = reader.GetDouble("乘数2", false) ?? 1;
|
||
if (td.Multiplier2 <= 0)
|
||
{
|
||
throw new ServiceException("[累计期权三段式]乘数2不能小于等于0 填写错误:" + td.Multiplier2);
|
||
}
|
||
//乘数3
|
||
td.Multiplier3 = reader.GetDouble("乘数3", false) ?? 1;
|
||
if (td.Multiplier3 <= 0)
|
||
{
|
||
throw new ServiceException("[累计期权三段式]乘数3不能小于等于0 填写错误:" + td.Multiplier3);
|
||
}
|
||
}
|
||
//敲出是否终止
|
||
td.EarlyTerminate = reader.GetString("敲出是否终止") == "是";
|
||
|
||
//结算方式
|
||
var settlementMode = reader.GetString("结算方式1");
|
||
switch (settlementMode)
|
||
{
|
||
case "现金当日":
|
||
case "现金结算(当日)":
|
||
td.SettlementMode = "现金当日";
|
||
break;
|
||
case "实物交割":
|
||
td.SettlementMode = "实物交割";
|
||
break;
|
||
case "现金实物合并结算":
|
||
td.SettlementMode = "现金实物合并结算";
|
||
break;
|
||
default:
|
||
if (!string.IsNullOrEmpty(settlementMode))
|
||
{
|
||
throw new ServiceException("[累计期权三段式]结算方式1 填写错误:" + settlementMode);
|
||
}
|
||
td.SettlementMode = "现金当日";
|
||
break;
|
||
}
|
||
DictionarySerivice dictionarySerivice = new DictionarySerivice();
|
||
var dic = dictionarySerivice.GetForwardDateDic();
|
||
if (td.SettlementMode == "实物交割" || td.SettlementMode == "现金实物合并结算")
|
||
{
|
||
td.ForwardPriceType = reader.GetString("远期期初价类型1");
|
||
td.ForwardDateType = reader.GetString("远期到期日类型1");
|
||
if (td.ForwardDateType != "标的到期日" && td.ForwardDateType != "期权到期日")
|
||
{
|
||
var dicModel = dic.FirstOrDefault(l => l.Value == td.ForwardDateType);
|
||
if (dicModel.Value == null || dicModel.Value == "") { throw new ServiceException("[累计期权三段式]远期到期日类型1 系统中未找到:" + td.ForwardDateType); }
|
||
}
|
||
}
|
||
var settlementMode2 = reader.GetString("结算方式2");
|
||
switch (settlementMode2)
|
||
{
|
||
case "现金当日":
|
||
case "现金结算(当日)":
|
||
td.SettlementMode2 = "现金当日";
|
||
break;
|
||
case "实物交割":
|
||
td.SettlementMode2 = "实物交割";
|
||
break;
|
||
case "现金实物合并结算":
|
||
td.SettlementMode2 = "现金实物合并结算";
|
||
break;
|
||
default:
|
||
if (!string.IsNullOrEmpty(settlementMode2))
|
||
{
|
||
throw new ServiceException("[累计期权三段式]结算方式2 填写错误:" + settlementMode2);
|
||
}
|
||
td.SettlementMode2 = "现金当日";
|
||
break;
|
||
}
|
||
td.ForwardPriceType2 = reader.GetString("远期期初价类型2");
|
||
td.ForwardDateType2 = reader.GetString("远期到期日类型2");
|
||
if (td.SettlementMode2 == "实物交割" || td.SettlementMode2 == "现金实物合并结算")
|
||
{
|
||
if (td.ForwardDateType2 != "标的到期日" && td.ForwardDateType2 != "期权到期日")
|
||
{
|
||
var dicModel = dic.FirstOrDefault(l => l.Value == td.ForwardDateType2);
|
||
if (dicModel.Value == null || dicModel.Value == "") { throw new ServiceException("[累计期权三段式]远期到期日类型2 系统中未找到:" + td.ForwardDateType2); }
|
||
}
|
||
}
|
||
//结算方式
|
||
var settlementMode3 = reader.GetString("结算方式3");
|
||
switch (settlementMode3)
|
||
{
|
||
case "现金当日":
|
||
case "现金结算(当日)":
|
||
td.SettlementMode3 = "现金当日";
|
||
break;
|
||
case "实物交割":
|
||
td.SettlementMode3 = "实物交割";
|
||
break;
|
||
case "现金实物合并结算":
|
||
td.SettlementMode3 = "现金实物合并结算";
|
||
break;
|
||
default:
|
||
if (!string.IsNullOrEmpty(settlementMode3))
|
||
{
|
||
throw new ServiceException("[累计期权三段式]结算方式3 填写错误:" + settlementMode3);
|
||
}
|
||
td.SettlementMode3 = "现金当日";
|
||
break;
|
||
}
|
||
if (td.SettlementMode3 == "实物交割" || td.SettlementMode3 == "现金实物合并结算")
|
||
{
|
||
td.ForwardPriceType3 = reader.GetString("远期期初价类型3");
|
||
td.ForwardDateType3 = reader.GetString("远期到期日类型3");
|
||
if (td.ForwardDateType3 != "标的到期日" && td.ForwardDateType3 != "期权到期日")
|
||
{
|
||
var dicModel = dic.FirstOrDefault(l => l.Value == td.ForwardDateType3);
|
||
if (dicModel.Value == null || dicModel.Value == "") { throw new ServiceException("[累计期权三段式]远期到期日类型3 系统中未找到:" + td.ForwardDateType3); }
|
||
}
|
||
}
|
||
|
||
|
||
//敲出观察频率
|
||
ReadKOObservationDates(reader, td);
|
||
//保存 还是 用累计期权 保存 只不过 导入时取对应三段式列时需要判断
|
||
td.TradeType = "累计期权";
|
||
}
|
||
|
||
//累计期权
|
||
private void ReadAccumulatorOption(DataRowReader reader, OtcOptionTradeFull td)
|
||
{
|
||
var isMoneyness = td.IsMoneynessOption == "是";
|
||
td.AccumulatorStructureType = AccumulatorStructureTypeEnum.Standard;
|
||
td.AccumuTradeAmount = reader.GetDouble("沽购数量") ?? 0;
|
||
|
||
//敲出障碍价格
|
||
td.KOBarrier = reader.GetDoubleOrPercent("敲出障碍价格", true, isMoneyness) ?? 0;
|
||
|
||
//上端收益类型
|
||
var payType = reader.GetString("上端收益类型");
|
||
|
||
switch (payType)
|
||
{
|
||
case "浮动":
|
||
td.PayoffType = "浮动";
|
||
break;
|
||
case "固定(票息)":
|
||
td.PayoffType = "固定";
|
||
break;
|
||
default:
|
||
if (!string.IsNullOrEmpty(payType))
|
||
{
|
||
throw new ServiceException("[累计期权]上端收益类型 填写错误:" + payType);
|
||
}
|
||
td.PayoffType = "浮动";
|
||
break;
|
||
}
|
||
|
||
if (td.PayoffType == "固定")
|
||
{
|
||
//票息率
|
||
td.Coupon = reader.TryGetDoubleOrPercent("票息", true, out var percent) ?? 0;
|
||
td.CouponPercent = percent;
|
||
|
||
//票息是否年化
|
||
td.IsFixedCoupon = reader.GetString("票息年化") != "是";
|
||
|
||
//票息日历规则
|
||
td.CouponDayCount = reader.GetString("票息日历规则");
|
||
}
|
||
|
||
//杠杆倍数
|
||
if (PS.Config.ErpElement.AccumulatorShowMultiplier1)
|
||
{
|
||
td.CallMultiplier = td.PutMultiplier = 1;
|
||
var multiplier = reader.GetDouble("杠杆倍数", false) ?? 1;
|
||
if (multiplier <= 0)
|
||
{
|
||
throw new ServiceException("[累计期权]杠杆倍数不能小于等于0 填写错误:" + multiplier);
|
||
}
|
||
if (ConsGlobal.CallPut.IsCall(td.CallPut))
|
||
{
|
||
td.PutMultiplier = multiplier;
|
||
}
|
||
else
|
||
{
|
||
td.CallMultiplier = multiplier;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
//看涨乘数
|
||
td.CallMultiplier = reader.GetDouble("看涨乘数", false) ?? 1;
|
||
if (td.CallMultiplier <= 0)
|
||
{
|
||
throw new ServiceException("[累计期权]看涨乘数不能小于等于0 填写错误:" + td.CallMultiplier);
|
||
}
|
||
//看跌乘数
|
||
td.PutMultiplier = reader.GetDouble("看跌乘数", false) ?? 1;
|
||
if (td.PutMultiplier <= 0)
|
||
{
|
||
throw new ServiceException("[累计期权]看跌乘数不能小于等于0 填写错误:" + td.PutMultiplier);
|
||
}
|
||
}
|
||
//结算方式
|
||
var settlementMode = reader.GetString("结算方式");
|
||
|
||
switch (settlementMode)
|
||
{
|
||
case "现金当日":
|
||
case "现金结算(当日)":
|
||
td.SettlementMode = "现金当日";
|
||
break;
|
||
case "现金期末":
|
||
case "现金结算(期末)":
|
||
td.SettlementMode = "现金期末";
|
||
td.AccumuType = reader.GetString("累计类型") ?? "";
|
||
if (!string.IsNullOrWhiteSpace(td.AccumuType) && td.AccumuType != "子弹")
|
||
{
|
||
throw new ServiceException("[累计期权]结算方式 填写错误:" + td.AccumuType);
|
||
}
|
||
break;
|
||
case "实物交割":
|
||
td.SettlementMode = "实物交割";
|
||
|
||
td.AccumuType = reader.GetString("累计类型") ?? "";
|
||
if (!string.IsNullOrWhiteSpace(td.AccumuType) && td.AccumuType != "区间现金结算")
|
||
{
|
||
throw new ServiceException("[累计期权]结算方式 填写错误:" + td.AccumuType);
|
||
}
|
||
|
||
break;
|
||
case "现金实物合并结算":
|
||
td.SettlementMode = "现金实物合并结算";
|
||
td.AccumuType = reader.GetString("累计类型") ?? "";
|
||
if (!string.IsNullOrWhiteSpace(td.AccumuType) && td.AccumuType != "区间现金结算")
|
||
{
|
||
throw new ServiceException("[累计期权]结算方式 填写错误:" + td.AccumuType);
|
||
}
|
||
break;
|
||
default:
|
||
if (!string.IsNullOrEmpty(settlementMode))
|
||
{
|
||
throw new ServiceException("[累计期权]结算方式 填写错误:" + settlementMode);
|
||
}
|
||
td.SettlementMode = "现金当日";
|
||
break;
|
||
}
|
||
if (td.SettlementMode == "实物交割" || td.SettlementMode == "现金实物合并结算")
|
||
{
|
||
DictionarySerivice dictionarySerivice = new DictionarySerivice();
|
||
var dic = dictionarySerivice.GetForwardDateDic();
|
||
td.ForwardPriceType = reader.GetString("远期期初价类型");
|
||
td.ForwardDateType = reader.GetString("远期到期日类型");
|
||
if (td.ForwardDateType != "标的到期日" && td.ForwardDateType != "期权到期日")
|
||
{
|
||
var dicModel = dic.FirstOrDefault(l => l.Value == td.ForwardDateType);
|
||
if (dicModel.Value == null || dicModel.Value == "") { throw new ServiceException("[累计期权]远期到期日类型 系统中未找到:" + td.ForwardDateType); }
|
||
}
|
||
}
|
||
|
||
|
||
//敲出是否终止
|
||
td.EarlyTerminate = reader.GetString("敲出是否终止") == "是";
|
||
|
||
|
||
//敲出观察频率
|
||
ReadKOObservationDates(reader, td);
|
||
}
|
||
|
||
//现金流交易
|
||
private void ReadCashFlow(DataRowReader reader, OtcOptionTradeFull td)
|
||
{
|
||
td.CouponDayCount = reader.GetString("计息日历规则");
|
||
td.ProfitRate = reader.GetPercent("利率") ?? 0;
|
||
var rateType = reader.GetString("利率类型");
|
||
switch (rateType)
|
||
{
|
||
case "实际利率":
|
||
td.RateType = CashFlowRateTypeEnum.实际利率;
|
||
break;
|
||
case "年化利率":
|
||
td.RateType = CashFlowRateTypeEnum.年化利率;
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
td.PrepayRatio = reader.GetPercent("返还比例") ?? 0;
|
||
var depositType = reader.GetString("资金类型");
|
||
switch (depositType)
|
||
{
|
||
case "成本摊还":
|
||
td.DepositType = CashflowDepositTypeEnum.成本摊还;
|
||
break;
|
||
case "资金收益":
|
||
td.DepositType = CashflowDepositTypeEnum.资金收益;
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
|
||
//自定义交易
|
||
private void ReadCustomizedOption(DataRowReader reader, OtcOptionTradeFull td)
|
||
{
|
||
var dicInfo = new StructureService(OptUser).QueryStructureMap(td.IsGroup > 0 ? StructureRangeEnum.BALCK_TRADE : StructureRangeEnum.CUSTOM_TRADE);
|
||
if (!dicInfo.TryGetValue(td.StructureType ?? td.TradeType, out var dictionaryNames))
|
||
{
|
||
return;
|
||
}
|
||
var propertys = new List<ExtendInfoModel>();
|
||
dictionaryNames.ForEach(x =>
|
||
{
|
||
string value = null;
|
||
switch (x.ColumnType)
|
||
{
|
||
case StructureColumnTypeEnum.NUMBER:
|
||
value = reader.GetDouble(x.ColumnName)?.ToString("0.########");
|
||
break;
|
||
case StructureColumnTypeEnum.DATE:
|
||
value = reader.GetDate(x.ColumnName)?.ToString("yyyy-MM-dd");
|
||
break;
|
||
case StructureColumnTypeEnum.COMBO_BOX_SINGLE:
|
||
value = reader.GetString(x.ColumnName);
|
||
break;
|
||
case StructureColumnTypeEnum.TEXT:
|
||
default:
|
||
value = reader.GetString(x.ColumnName);
|
||
break;
|
||
}
|
||
if (!string.IsNullOrWhiteSpace(value))
|
||
{
|
||
propertys.Add(new ExtendInfoModel()
|
||
{
|
||
name = x.ColumnName,
|
||
value = value
|
||
});
|
||
}
|
||
});
|
||
td.ExtendInfo = JsonHelper.Serialize(propertys);
|
||
}
|
||
|
||
//Risky
|
||
private void ReadRisky(DataRowReader reader, OtcOptionTradeFull td)
|
||
{
|
||
//执行价格(亚式浮动行权价类型时非必填)
|
||
td.Strike1 = td.Strike;
|
||
td.Strike2 = reader.GetDoubleOrPercent("执行价格2", true, td.IsMoneynessOption == "是");
|
||
td.Strike3 = reader.GetDoubleOrPercent("执行价格3", true, td.IsMoneynessOption == "是");
|
||
|
||
td.ParticipationRate1 = td.ParticipationRate.Value;
|
||
td.ParticipationRate2 = reader.GetPercent("参与率2", true) ?? 0;
|
||
td.ParticipationRate3 = reader.GetPercent("参与率3", true) ?? 0;
|
||
td.ParticipationRate = 1;
|
||
|
||
td.PremiumPayDate = td.ExerciseDate;
|
||
}
|
||
|
||
|
||
//读取补偿支付
|
||
private void ReadRebateType(DataRowReader reader, OtcOptionTradeFull td)
|
||
{
|
||
td.RebateType = reader.GetString("补偿支付");
|
||
switch (td.RebateType)
|
||
{
|
||
case "立即":
|
||
td.RebateType = "AtHit";
|
||
break;
|
||
case "递延":
|
||
td.RebateType = "AtEnd";
|
||
break;
|
||
case "AtHit":
|
||
case "AtEnd":
|
||
break;
|
||
default:
|
||
td.RebateType = string.IsNullOrWhiteSpace(td.RebateType)
|
||
? "AtHit" : throw new ServiceException($"[{td.TradeType}]补偿支付 填写错误,不支持:{td.RebateType}");
|
||
break;
|
||
}
|
||
}
|
||
|
||
//读取敲入观察频率
|
||
private void ReadObservationDates(DataRowReader reader, OtcOptionTradeFull td)
|
||
{
|
||
var topType = reader.TopType;
|
||
|
||
reader.SetTopType("敲入观察日设置");
|
||
|
||
td.ObservationDates = reader.GetString("自定义观察日");
|
||
|
||
if (string.IsNullOrWhiteSpace(td.ObservationDates))
|
||
{
|
||
var ruleStr = reader.GetString("使用规则生成观察日");
|
||
td.ObservationDates = GenObservationDatesByRule(td, ruleStr);
|
||
}
|
||
else
|
||
{
|
||
var observationDates = td.ObservationDates.Replace('/', '-').Split(',').Select(O => O.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)[0]);
|
||
td.ObservationDates = string.Join(",", observationDates);
|
||
if (td.ObservationDates.Split(',').Any(n => !DateTime.TryParse(n, out _)))
|
||
{
|
||
throw new ServiceException($"[{td.TradeType}][敲入观察日设置]自定义观察日 填写错误:不符合要求的格式");
|
||
}
|
||
if (td.ObservationDates.Split(',').Any(n =>
|
||
{
|
||
DateTime.TryParse(n, out var date);
|
||
return QdpCalendarHelper.IsHoliday(date);
|
||
}))
|
||
{
|
||
throw new ServiceException($"[{td.TradeType}][敲入观察日设置]自定义观察日 填写错误:观察日存在非交易日,导入失败");
|
||
}
|
||
|
||
#region 导入时,写入敲入观察频率
|
||
string termStr = null;
|
||
var ruleStr = reader.GetString("使用规则生成观察日");
|
||
if (!string.IsNullOrWhiteSpace(ruleStr))
|
||
{
|
||
|
||
switch (ruleStr)
|
||
{
|
||
case "每日":
|
||
termStr = "1D";
|
||
break;
|
||
case "每周":
|
||
termStr = "1W";
|
||
break;
|
||
case "每月":
|
||
termStr = "1M";
|
||
break;
|
||
case "每年":
|
||
termStr = "1Y";
|
||
break;
|
||
}
|
||
if (termStr == null)
|
||
{
|
||
var strs = ruleStr.Split(new[] { ',' });
|
||
termStr = strs[0].Trim();
|
||
}
|
||
//敲入观察周期写入metadic中
|
||
td.MetaDic["敲入观察周期"] = termStr;
|
||
}
|
||
|
||
#endregion
|
||
}
|
||
|
||
reader.SetTopType(topType);
|
||
}
|
||
|
||
//读取观察日设置并生成观察日列表
|
||
private static string GenObservationDatesByRule(OtcOptionTradeFull td, string ruleStr, bool isKO = false)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(ruleStr))
|
||
{
|
||
return null;
|
||
}
|
||
|
||
string termStr = null;
|
||
|
||
var dayConvention = BusinessDayConvention.Following; //假日调整
|
||
|
||
var alignEnd = true; //是否向到期日对齐
|
||
|
||
switch (ruleStr)
|
||
{
|
||
case "每日":
|
||
termStr = "1D";
|
||
break;
|
||
case "每周":
|
||
termStr = "1W";
|
||
break;
|
||
case "每月":
|
||
termStr = "1M";
|
||
break;
|
||
case "每年":
|
||
termStr = "1Y";
|
||
break;
|
||
}
|
||
|
||
var kstr = isKO ? "敲出" : "敲入";
|
||
|
||
if (termStr == null)
|
||
{
|
||
var strs = ruleStr.Split(new[] { ',' });
|
||
|
||
termStr = strs[0].Trim();
|
||
|
||
if (!Term.IsTerm(termStr))
|
||
{
|
||
throw new ServiceException($"[{td.TradeType}][{kstr}观察日设置]使用规则生成观察日 观察日间隔填写错误");
|
||
}
|
||
|
||
//假日调整
|
||
if (strs.Length > 1)
|
||
{
|
||
var observationHolidayType = strs[1];
|
||
switch (observationHolidayType)
|
||
{
|
||
case "向后调整":
|
||
dayConvention = BusinessDayConvention.Following;
|
||
break;
|
||
case "向前调整":
|
||
dayConvention = BusinessDayConvention.Previous;
|
||
break;
|
||
case "不调整":
|
||
dayConvention = BusinessDayConvention.None;
|
||
break;
|
||
default:
|
||
dayConvention = string.IsNullOrWhiteSpace(observationHolidayType)
|
||
? BusinessDayConvention.Following
|
||
: throw new ServiceException($"[{td.TradeType}][{kstr}观察日设置]使用规则生成观察日 假日调整填写错误,不支持:" + observationHolidayType);
|
||
break;
|
||
}
|
||
}
|
||
|
||
//对齐规则
|
||
if (strs.Length > 2)
|
||
{
|
||
var alignEndStr = strs[2];
|
||
switch (alignEndStr)
|
||
{
|
||
case "向开始日对齐":
|
||
case "向到期日对齐":
|
||
break;
|
||
default:
|
||
alignEndStr = string.IsNullOrWhiteSpace(alignEndStr) ? "向开始日对齐"
|
||
: throw new ServiceException($"[{td.TradeType}][{kstr}观察日设置]使用规则生成观察日 对齐规则填写错误,不支持:" + alignEndStr);
|
||
break;
|
||
}
|
||
alignEnd = alignEndStr == "向到期日对齐";
|
||
}
|
||
}
|
||
|
||
//敲入观察周期写入metadic中
|
||
if (!isKO)
|
||
{
|
||
td.MetaDic["敲入观察周期"] = termStr;
|
||
}
|
||
else if (isKO)
|
||
{
|
||
td.MetaDic["敲出观察周期"] = termStr;
|
||
}
|
||
|
||
return QdpObservationHelper.GetObservationDateString(td.TradeDate.Value, td.ExerciseDate.Value, termStr, dayConvention, alignEnd);
|
||
}
|
||
|
||
//读取敲出观察频率
|
||
private void ReadKOObservationDates(DataRowReader reader, OtcOptionTradeFull td)
|
||
{
|
||
var topType = reader.TopType;
|
||
|
||
reader.SetTopType("敲出观察日设置");
|
||
|
||
var separators = new[] { ',', ';', ',', ';' };
|
||
|
||
List<string> obDateList, stDateList;
|
||
|
||
string[] barriePrices, coupons;
|
||
|
||
//观察日
|
||
var obDatesStr = reader.GetString("观察日");
|
||
|
||
if (string.IsNullOrWhiteSpace(obDatesStr))
|
||
{
|
||
var ruleStr = reader.GetString("敲出观察频率");
|
||
td.KOObservationDates = GenObservationDatesByRule(td, ruleStr, true);
|
||
//结算日期
|
||
var stDatesStr = reader.GetString("结算日期");
|
||
if (string.IsNullOrWhiteSpace(stDatesStr))
|
||
{
|
||
if (!string.IsNullOrEmpty(td.KOObservationDates))
|
||
{
|
||
td.KOObservationSettleDates = td.KOObservationDates;
|
||
}
|
||
}
|
||
if (!string.IsNullOrEmpty(td.KOObservationDates))
|
||
{
|
||
var odNum = td.KOObservationDates.Split(",").Count();
|
||
var koBarrierStr = "";
|
||
var koKORebate = "";
|
||
for (var i = 0; i < odNum; i++)
|
||
{
|
||
koBarrierStr += td.KOBarrier + ",";
|
||
koKORebate += "0,";
|
||
}
|
||
if (!string.IsNullOrEmpty(koBarrierStr))
|
||
{
|
||
koBarrierStr = koBarrierStr.Substring(0, koBarrierStr.Length - 1);
|
||
}
|
||
if (!string.IsNullOrEmpty(koKORebate) && !td.TradeType.Contains("累计"))
|
||
{
|
||
koKORebate = koKORebate.Substring(0, koKORebate.Length - 1);
|
||
}
|
||
td.KOObservationDates += ";" + koBarrierStr + (!td.TradeType.Contains("累计")?(";" + koKORebate):"");
|
||
}
|
||
}
|
||
else
|
||
{
|
||
td.MetaDic["敲出观察周期"] = "1D";
|
||
|
||
var strArr = obDatesStr.Split(separators, StringSplitOptions.RemoveEmptyEntries);
|
||
|
||
obDateList = new List<string>(strArr.Length);
|
||
|
||
foreach (var str in strArr)
|
||
{
|
||
if (DateTime.TryParse(str, out var dt))
|
||
{
|
||
obDateList.Add(dt.ToString("yyyy-MM-dd"));
|
||
|
||
if (dt < td.TradeDate.Value)
|
||
{
|
||
throw new ServiceException($"[{td.TradeType}][敲出观察日设置]观察日期 填写错误,观察日期'{str}'不能小于开始日期'{td.TradeDate.Value:yyyy-MM-dd}'");
|
||
}
|
||
|
||
if (dt > td.ExerciseDate.Value)
|
||
{
|
||
throw new ServiceException($"[{td.TradeType}][敲出观察日设置]观察日期 填写错误,观察日期'{str}'不能大于到期日期'{td.ExerciseDate.Value:yyyy-MM-dd}'");
|
||
}
|
||
|
||
if (QdpCalendarHelper.IsHoliday(dt))
|
||
{
|
||
throw new ServiceException($"[{td.TradeType}][敲出观察日设置]观察日期 填写错误,存在非交易日'{str}'");
|
||
}
|
||
}
|
||
else
|
||
{
|
||
throw new ServiceException($"[{td.TradeType}][敲出观察日设置]观察日期 填写错误,日期'{str}'未能解析成功");
|
||
}
|
||
}
|
||
|
||
//结算日期
|
||
|
||
var stDatesStr = reader.GetString("结算日期");
|
||
|
||
if (string.IsNullOrWhiteSpace(stDatesStr))
|
||
{
|
||
stDateList = obDateList;
|
||
}
|
||
else
|
||
{
|
||
strArr = stDatesStr.Split(separators, StringSplitOptions.RemoveEmptyEntries);
|
||
|
||
stDateList = new List<string>(strArr.Length);
|
||
|
||
foreach (var str in strArr)
|
||
{
|
||
if (DateTime.TryParse(str, out var dt))
|
||
{
|
||
stDateList.Add(dt.ToString("yyyy-MM-dd"));
|
||
|
||
if (dt < td.TradeDate.Value)
|
||
{
|
||
throw new ServiceException($"[{td.TradeType}][敲出观察日设置]结算日期 填写错误,日期'{str}'不能小于开始日期'{td.TradeDate.Value:yyyy-MM-dd}'");
|
||
}
|
||
}
|
||
else
|
||
{
|
||
throw new ServiceException($"[{td.TradeType}][敲出观察日设置]结算日期 填写错误,日期'{str}'未能解析成功");
|
||
}
|
||
}
|
||
|
||
if (stDateList.Count != obDateList.Count)
|
||
{
|
||
throw new ServiceException($"[{td.TradeType}][敲出观察日设置]结算日期 列表数量应和观察日一致");
|
||
}
|
||
}
|
||
|
||
//障碍价格
|
||
|
||
var barriePricesStr = reader.GetString("障碍价格");
|
||
if (string.IsNullOrWhiteSpace(barriePricesStr))
|
||
{
|
||
barriePrices = obDateList.Select(n => td.KOBarrier.HasValue ? td.KOBarrier.Value.ToString("0.######") : string.Empty).ToArray();
|
||
}
|
||
else
|
||
{
|
||
if (td.TradeType == "凤凰期权")
|
||
{
|
||
barriePrices = barriePricesStr.Split(separators);
|
||
}
|
||
else
|
||
{
|
||
barriePrices = barriePricesStr.Split(separators, StringSplitOptions.RemoveEmptyEntries);
|
||
}
|
||
if (barriePrices.Length != obDateList.Count)
|
||
{
|
||
throw new ServiceException($"[{td.TradeType}][敲出观察日设置]障碍价格 列表数量应和观察日一致");
|
||
}
|
||
var converts = barriePrices.Select(str =>
|
||
{
|
||
var percent = str.EndsWith("%");
|
||
if (percent)
|
||
{
|
||
str = str.TrimEnd('%');
|
||
}
|
||
if (td.TradeType == "凤凰期权")
|
||
{
|
||
return double.TryParse(str, out var num) ? (percent ? num / 100 : num).ToString() : "";
|
||
}
|
||
else
|
||
{
|
||
return double.TryParse(str, out var num) ? (percent ? num / 100 : num).ToString() :
|
||
throw new ServiceException($"[{td.TradeType}][敲出观察日设置]障碍价格 数字填写错误");
|
||
}
|
||
}).ToArray();
|
||
|
||
|
||
|
||
barriePrices = converts.Select(n => string.IsNullOrEmpty(n) ? "" : Convert.ToDouble(n).ToString("0.######")).ToArray();
|
||
}
|
||
|
||
//票息率
|
||
|
||
var couponsStr = reader.GetString("票息率");
|
||
if (string.IsNullOrWhiteSpace(couponsStr))
|
||
{
|
||
var couponStr = (td.TradeType == "雪球期权" ? td.KORebate : td.Coupon).ToString("0.########");
|
||
if (couponStr.Length < 1)
|
||
{
|
||
couponStr = "0";
|
||
}
|
||
coupons = obDateList.Select(n => couponStr).ToArray();
|
||
}
|
||
else
|
||
{
|
||
coupons = couponsStr.Split(separators, StringSplitOptions.RemoveEmptyEntries);
|
||
if (coupons.Length != obDateList.Count)
|
||
{
|
||
throw new ServiceException($"[{td.TradeType}][敲出观察日设置]票息率 列表数量应和观察日一致");
|
||
}
|
||
var converts = coupons.Select(str =>
|
||
{
|
||
var percent = str.EndsWith("%");
|
||
if (percent)
|
||
{
|
||
str = str.TrimEnd('%');
|
||
}
|
||
return double.TryParse(str, out var num) ? (percent ? num / 100 : num) :
|
||
throw new ServiceException($"[{td.TradeType}][敲出观察日设置]票息率 数字填写错误");
|
||
}).ToArray();
|
||
coupons = converts.Select(n => n.ToString("0.########")).ToArray();
|
||
}
|
||
|
||
td.KOObservationDates = string.Join(";", new[] {
|
||
string.Join(",",obDateList),
|
||
string.Join(",",barriePrices),
|
||
string.Join(",",coupons)
|
||
});
|
||
|
||
td.KOObservationSettleDates = string.Join(",", stDateList);
|
||
}
|
||
|
||
reader.SetTopType(topType);
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region---读取交易了结----
|
||
|
||
//了结信息
|
||
private TradeCloseRequestModel ReadCloseData(DataRowReader reader, OtcOptionTradeFull td)
|
||
{
|
||
reader.SetTopType("了结信息");
|
||
|
||
//了结方式
|
||
var closeType = reader.GetString("了结方式");
|
||
|
||
switch (closeType)
|
||
{
|
||
case "平仓":
|
||
break;
|
||
case "行权":
|
||
case "到期":
|
||
case "提前行权":
|
||
if (td.TradeType == "自定义交易")
|
||
{
|
||
throw new ServiceException("[了结信息]了结方式 填写错误,结构类型为'自定义交易'时仅支持'平仓',填写值为:" + closeType);
|
||
}
|
||
break;
|
||
default:
|
||
if (!string.IsNullOrWhiteSpace(closeType))
|
||
{
|
||
throw new ServiceException("[了结信息]了结方式 填写错误,不支持:" + closeType);
|
||
}
|
||
return null;
|
||
}
|
||
|
||
var model = new TradeCloseRequestModel
|
||
{
|
||
CloseType = closeType
|
||
};
|
||
|
||
//了结日期
|
||
model.CloseDate = model.CloseType == "到期" ? td.ExerciseDate.Value : reader.GetDate("了结日期") ?? td.ExerciseDate.Value;
|
||
|
||
//了结标的价格
|
||
model.UnderlyingPrice = reader.GetDouble("了结标的价格", "了结时标的价格", true).Value;
|
||
|
||
//行权标的均价
|
||
model.UnderlyingAvgPrice = reader.GetDouble("行权标的均价");
|
||
|
||
if (closeType == "平仓")
|
||
{
|
||
//平仓总额
|
||
model.UnwindTotalFee = reader.GetDouble("平仓总额");
|
||
|
||
if (!model.UnwindTotalFee.HasValue)
|
||
{
|
||
//平仓单价
|
||
model.UnwindPrice = reader.GetDouble("平仓单价");
|
||
|
||
//平仓单价比例
|
||
model.UnwindPriceRate = reader.GetDouble("平仓单价比例");
|
||
}
|
||
|
||
//平仓波动率
|
||
model.UnwindVolatility = reader.GetPercent("平仓波动率");
|
||
}
|
||
//导入了结方式为到期并且平仓总额不为空
|
||
if (closeType == "到期"&& reader.GetDouble("平仓总额")!=null)
|
||
{
|
||
//平仓总额
|
||
model.UnwindTotalFee = reader.GetDouble("平仓总额");
|
||
}
|
||
return model;
|
||
}
|
||
|
||
// 执行了结
|
||
private void ExecuteClose(trade dbTrade, TradeCloseRequestModel req)
|
||
{
|
||
if (req == null)
|
||
{
|
||
return;
|
||
}
|
||
|
||
dbTrade.TradeStatus = "确认成交";
|
||
|
||
var dbTradeCashList = DbContext.trade_cash.Where(t => t.TradeId == dbTrade.id && !t.IsDeleted).ToList();
|
||
var dbClientCashInOutList = DbContext.ClientCashInCashOut.Where(t => dbTrade.id == t.TradeId.Value).ToList();
|
||
DbContext.trade_cash.RemoveRange(dbTradeCashList);
|
||
var tradeCashIds = dbTradeCashList.Select(x => x.id);
|
||
var tradeCashDetials = DbContext.trade_cash_detail.Where(x => tradeCashIds.Contains(x.TradeCashId));
|
||
DbContext.trade_cash_detail.RemoveRange(tradeCashDetials);
|
||
DbContext.ClientCashInCashOut.RemoveRange(dbClientCashInOutList);
|
||
|
||
var newTC = new trade_cash
|
||
{
|
||
ValidState = "Valid",
|
||
OptId = UserId,
|
||
OptName = UserName,
|
||
OptDate = DateTime.Now,
|
||
Action = ClientCashInCashOut.系统操作_期权费,
|
||
ExceciseType = "现金",
|
||
TradeId = dbTrade.id,
|
||
ValueDate = dbTrade.TradeDate.Value,
|
||
Strike = dbTrade.IsMoneynessOptionData ? dbTrade.Strike * (dbTrade.SpotPrice ?? 0.0) : dbTrade.Strike,
|
||
Notional = dbTrade.Notional
|
||
};
|
||
|
||
var CountRatio = UnderlyingDataProvider.GetCountRatio(dbTrade.UnderlyingCode);
|
||
newTC.TradeAmount = newTC.Notional / CountRatio;
|
||
|
||
newTC.Amount = ((dbTrade.BuySell == "买入" ? -1 : 1) * dbTrade.TradePrice) ?? 0;
|
||
newTC.TradeType = dbTrade.BuySell;
|
||
newTC.Comments = "导入交易了结添加期权费";
|
||
DbContext.trade_cash.Add(newTC);
|
||
DbContext.SaveChanges();
|
||
|
||
if (dbTrade.PremiumPayDate.HasValue && dbTrade.PremiumPayDate > dbTrade.TradeDate)
|
||
{
|
||
newTC.Status = TradeCashStatusEnum.冻结;
|
||
}
|
||
else
|
||
{
|
||
var client = DataCacheProvider.GetClientDataSource().GetData(dbTrade.ClientId);
|
||
var newCC = new ClientCashInCashOut
|
||
{
|
||
Number = UniqueTimeId.GetStr(),
|
||
HappenDate = dbTrade.PremiumPayDate ?? dbTrade.TradeDate,
|
||
Action = ClientCashInCashOut.系统操作_期权费,
|
||
Direction = ClientCashInCashOut.应收,
|
||
OptDate = DateTime.Now,
|
||
OptId = UserId,
|
||
OptName = UserName,
|
||
CreateDate = DateTime.Now,
|
||
CreatorId = UserId,
|
||
CreatorName = UserName,
|
||
ClientId = dbTrade.ClientId,
|
||
ClientName = dbTrade.ClientName,
|
||
ClientNumber = client.Number,
|
||
TradeId = dbTrade.id,
|
||
TradeCashId = newTC.id,
|
||
Money = (dbTrade.BuySell == "买入" ? 1 : -1) * dbTrade.TradePrice,
|
||
ValidState = "Valid",
|
||
//权利金默认为应收 已执行
|
||
TradeNumber = dbTrade.TradeNumber,
|
||
IsGroup = dbTrade.IsGroup
|
||
};
|
||
|
||
//根据是否需要结算设置资金状态
|
||
newTC.Status = TradeCashStatusEnum.已执行;
|
||
newTC.SettleDate = newTC.ValueDate;
|
||
|
||
newCC.State = ClientCashInCashOut.已确认;
|
||
newCC.SettleDate = newTC.ValueDate;
|
||
|
||
DbContext.ClientCashInCashOut.Add(newCC);
|
||
}
|
||
DbContext.SaveChanges();
|
||
|
||
new TradeCashService(this).SaveTradeCashDetail(newTC);
|
||
|
||
req.ImportFlag = "交易导入";
|
||
req.SkipWorkflow = true;
|
||
req.TradeNumber = dbTrade.TradeNumber;
|
||
req.CloseTradeAmountRate = 1;
|
||
req.CloseTradeAmount = dbTrade.TradeAmount;
|
||
new OtcTradeCloseService(this).ExecuteClose(req, true);
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region---内部业务类----
|
||
|
||
class DataRowReader
|
||
{
|
||
DataRow _row;
|
||
|
||
readonly Dictionary<string, int> _colMap;
|
||
|
||
public DataRowReader(DataTable table)
|
||
{
|
||
var colCount = table.Columns.Count;
|
||
|
||
_colMap = new Dictionary<string, int>(colCount, StringComparer.OrdinalIgnoreCase);
|
||
|
||
var row1 = table.Rows[0];
|
||
var row2 = table.Rows[1];
|
||
var preCol1 = string.Empty;
|
||
var customField = new List<string>();
|
||
|
||
for (var index = 0; index < colCount; index++)
|
||
{
|
||
var col1 = row1[index]?.ToString()?.Trim();
|
||
var col2 = row2[index]?.ToString();
|
||
if (string.IsNullOrWhiteSpace(col2))
|
||
{
|
||
continue;
|
||
}
|
||
if (!string.IsNullOrWhiteSpace(col1))
|
||
{
|
||
preCol1 = col1;
|
||
}
|
||
col2 = col2.Replace("%", "").Trim();
|
||
if (preCol1 == "自定义属性")
|
||
{
|
||
customField.Add(col2);
|
||
}
|
||
_colMap[preCol1 + col2] = index;
|
||
}
|
||
CustomField = customField.ToArray();
|
||
}
|
||
|
||
public string TopType { get; private set; }
|
||
|
||
/// <summary>
|
||
/// 自定义字段名
|
||
/// </summary>
|
||
public string[] CustomField { get; private set; }
|
||
|
||
/// <summary>
|
||
/// 设置datarow
|
||
/// </summary>
|
||
public void SetDataRow(DataRow row)
|
||
{
|
||
_row = row;
|
||
TopType = "基本要素";
|
||
}
|
||
|
||
/// <summary>
|
||
/// 设置顶部类型
|
||
/// </summary>
|
||
public void SetTopType(string type)
|
||
{
|
||
TopType = type;
|
||
}
|
||
|
||
public string GetString(string fieldName, bool required = false)
|
||
{
|
||
var str = _colMap.TryGetValue(TopType + fieldName, out var colIndex) ? _row[colIndex]?.ToString()?.Trim() : null;
|
||
|
||
if (required && string.IsNullOrWhiteSpace(str))
|
||
{
|
||
throw new ServiceException($"[{TopType}]{fieldName} 必须填写");
|
||
}
|
||
|
||
return str;
|
||
}
|
||
|
||
public double? GetDoubleOrPercent(string fieldName, bool required, bool percent)
|
||
{
|
||
var str = GetString(fieldName, required);
|
||
|
||
if (!required && string.IsNullOrWhiteSpace(str))
|
||
{
|
||
return null;
|
||
}
|
||
|
||
if (percent && (percent = str.EndsWith("%")))
|
||
{
|
||
str = str.TrimEnd('%');
|
||
}
|
||
|
||
return double.TryParse(str, out var num) ? (percent ? num / 100 : num) : throw new ServiceException($"[{TopType}]{fieldName} 填写错误:{str}");
|
||
}
|
||
|
||
public double? TryGetDoubleOrPercent(string fieldName, bool required, out bool percent)
|
||
{
|
||
var str = GetString(fieldName, required);
|
||
|
||
if (!required && string.IsNullOrWhiteSpace(str))
|
||
{
|
||
percent = false;
|
||
return null;
|
||
}
|
||
|
||
percent = str.EndsWith("%");
|
||
|
||
if (percent)
|
||
{
|
||
str = str.TrimEnd('%');
|
||
}
|
||
|
||
return double.TryParse(str, out var num) ? (percent ? num / 100 : num) : throw new ServiceException($"[{TopType}]{fieldName} 填写错误:{str}");
|
||
}
|
||
|
||
public double? GetDouble(string fieldName, bool required = false)
|
||
{
|
||
var str = GetString(fieldName, required);
|
||
|
||
if (!required && string.IsNullOrWhiteSpace(str))
|
||
{
|
||
return null;
|
||
}
|
||
|
||
return double.TryParse(str, out var num) ? num : throw new ServiceException($"[{TopType}]{fieldName} 填写错误:{str}");
|
||
}
|
||
|
||
//为了兼容模板修改导致的字段名称改变问题
|
||
public double? GetDouble(string fieldName, string fieldName2, bool required = false)
|
||
{
|
||
var str = GetString(fieldName, false) ?? GetString(fieldName2, false);
|
||
|
||
if (string.IsNullOrWhiteSpace(str))
|
||
{
|
||
return required ? throw new ServiceException($"[{TopType}]{fieldName} 必须填写") : (double?)null;
|
||
}
|
||
|
||
return double.TryParse(str, out var num) ? num : throw new ServiceException($"[{TopType}]{fieldName} 填写错误:{str}");
|
||
}
|
||
|
||
public double? GetPercent(string fieldName, bool required = false)
|
||
{
|
||
var str = GetString(fieldName, required);
|
||
|
||
if (!required && string.IsNullOrWhiteSpace(str))
|
||
{
|
||
return null;
|
||
}
|
||
|
||
var percent = str.EndsWith("%");
|
||
|
||
if (percent)
|
||
{
|
||
str = str.TrimEnd('%');
|
||
}
|
||
|
||
return double.TryParse(str, out var num) ? (percent ? num / 100 : num) : throw new ServiceException($"[{TopType}]{fieldName} 填写错误:{str}");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取日期(不包括时间)
|
||
/// </summary>
|
||
public DateTime? GetDate(string fieldName, bool required = false)
|
||
{
|
||
var str = GetString(fieldName, required);
|
||
|
||
if (!required && string.IsNullOrWhiteSpace(str))
|
||
{
|
||
return null;
|
||
}
|
||
|
||
if (str.Length == 8 && Regex.IsMatch(str, @"^\d+$"))
|
||
{
|
||
return DateTime.TryParseExact(str, "yyyyMMdd", CultureInfo.InvariantCulture, DateTimeStyles.None, out var dt2) ? dt2 : throw new ServiceException($"{fieldName} 填写错误:{str}");
|
||
}
|
||
|
||
return DateTime.TryParse(str, out var dt) ? dt.Date : throw new ServiceException($"[{TopType}]{fieldName} 填写错误:{str}");
|
||
}
|
||
|
||
/// <summary>
|
||
///
|
||
/// </summary>
|
||
public int? GetInt32(string fieldName, bool required = false)
|
||
{
|
||
var str = GetString(fieldName, required);
|
||
|
||
if (!required && string.IsNullOrWhiteSpace(str))
|
||
{
|
||
return null;
|
||
}
|
||
|
||
return int.TryParse(str, out var num) ? num : throw new ServiceException($"[{TopType}]{fieldName} 填写错误:{str}");
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
|
||
/// <summary>
|
||
/// 组合销售提成信息
|
||
/// </summary>
|
||
private List<int> GetSalesIds(string salesmenNames)
|
||
{
|
||
var salesNameArr = salesmenNames.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
|
||
|
||
if (!salesNameArr.Any())
|
||
{
|
||
return null;
|
||
}
|
||
|
||
var salesDic = new SalesmenDataService(OptUser).GetSalesIdByNames(salesNameArr)
|
||
.ToDictionary(n => n.Name, n => n.id);
|
||
|
||
var results = new List<int>(salesNameArr.Length);
|
||
|
||
foreach (var name in salesNameArr)
|
||
{
|
||
if (!salesDic.TryGetValue(name, out var salesId))
|
||
{
|
||
throw new ServiceException(PS.Config.ErpElement.SaleMode == YLErp.Configuration.Enums.SaleMode.CustomerManager ? $"客户经理'{name}'未找到" : $"销售员'{name}'未找到");
|
||
}
|
||
results.Add(salesId);
|
||
}
|
||
|
||
return results;
|
||
}
|
||
|
||
private int GetClientId(string clientName)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(clientName))
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
var client = DataCacheProvider.GetClientDataSource().AsQueryable().FirstOrDefault(p => p.Name == clientName);
|
||
|
||
if (client != null)
|
||
{
|
||
return client.id;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
}
|
||
}
|