Files
zszq-trs/YLErpDAL/Modules/TradeModule/OrderModule/OtcTradeSaveService.cs
T
2024-05-09 14:06:26 +08:00

1636 lines
62 KiB
C#

using FluentFTP.Helpers;
using Microsoft.EntityFrameworkCore.Storage;
using NPOI.SS.Formula.Functions;
using Org.BouncyCastle.Ocsp;
using Qdp.Pricing.Base.Utilities;
using YLErp.BLL.Calculation;
using YLErp.Configuration.Enums;
using YLErp.Helpers;
using YLErp.Model.Enum;
using YLErp.Modules.CalculationModule;
using YLErp.Modules.SystemModule;
using YLErp.Modules.TagModule;
using static iTextSharp.text.pdf.AcroFields;
namespace YLErp.Modules.TradeModule.OrderModule
{
/// <summary>
/// 衍生品交易录入服务
/// </summary>
public class OtcTradeSaveService : YLBaseService
{
public OtcTradeSaveService(OptUserInfo userInfo) : base(userInfo)
{
}
public OtcTradeSaveService(YLBaseService baseService) : base(baseService)
{
}
/// <summary>
/// 保存来自API或导入的交易
/// </summary>
public trade SaveOptionTradeFromApiOrImport(OtcOptionTradeFullEx tdReq, TradeSourceEnum dataSource, IDbContextTransaction trans = null)
{
if (tdReq is null)
{
throw new ArgumentNullException(nameof(tdReq));
}
if (dataSource != TradeSourceEnum.WebApiV2 && dataSource != TradeSourceEnum.导入交易)
{
throw new NotSupportedException("不支持:" + dataSource.ToString());
}
if (tdReq._Update)
{
if (string.IsNullOrWhiteSpace(tdReq.TradeNumber))
{
throw new ServiceException("交易编号 必须填写");
}
var td = DbContext.trade.Where(n => n.TradeNumber == tdReq.TradeNumber)
.Select(n => new
{
n.id,
n.TradeType,
n.IsGroup,
n.TradeDate,
n.ClientId,
n.SpotPrice,
n.TradePrice,
n.ParentTradeId,
n.StructureType,
n.TradeStatus
}).FirstOrDefault();
if (td == null)
{
throw new ServiceException("未找到对应的交易数据:" + tdReq.TradeNumber);
}
//国君需求,如果有更多update需求,更改tdReq._Update处理
if (td.TradeStatus != ConsTrade.新增待确认)
{
throw new ServiceException("交易状态必须是'新增待确认'状态才允许修改");
}
tdReq.id = td.id;
//扩展支持组合交易主交易的修改
if (td.IsGroup == 1 && td.TradeType == "结构化交易")
{
tdReq.IsGroup = 1;
tdReq.TradeType = "结构化交易";
tdReq.ClientId = td.ClientId;
tdReq.TradeDate = td.TradeDate;
if (!tdReq.SpotPrice.HasValue)
{
tdReq.SpotPrice = td.SpotPrice;
}
tdReq.TradePrice = td.TradePrice;
CheckApiOrImportSave(tdReq, dataSource);
return new TradeSaveService(this).SaveGroupTrade(tdReq, null);
}
else if (td.IsGroup == 2 && td.ParentTradeId > 0)
{
tdReq.IsGroup = 2;
tdReq.ParentTradeId = td.ParentTradeId;
tdReq.StructureType = td.StructureType;
}
}
else
{
tdReq.id = 0;
}
var saveExt = CheckApiOrImportSave(tdReq, dataSource);
var dbtrade = TradeConverter.ConvertOptionTrade(tdReq);
dbtrade.SaveExt = saveExt;
return new TradeSaveService(this).SaveTrade(dbtrade, dataSource, trans);
}
/// <summary>
/// 保存来自API或导入的结构化组合交易
/// </summary>
public trade SaveStructureOptionTradeFromApiOrImport(StructureOrderRequest request, TradeSourceEnum dataSource, IDbContextTransaction trans = null)
{
if (request is null)
{
throw new ArgumentNullException(nameof(request));
}
if (dataSource != TradeSourceEnum.WebApiV2 && dataSource != TradeSourceEnum.导入交易)
{
throw new NotSupportedException("不支持:" + dataSource.ToString());
}
switch (request.StructureType)
{
case "牛市价差":
case "熊市价差":
case "亚式熊市价差":
case "跨式组合":
case "宽跨式组合":
case "复制标的资产":
case "蝶式组合":
case "飞鹰式组合":
case "比例价差":
case "日历价差":
case "箱式价差":
case "风险逆转":
case "三领口组合":
case "结构化交易":
break;
default:
if (string.IsNullOrWhiteSpace(request.StructureType))
{
throw new ServiceException("结构化类型 必须填写");
}
throw new ServiceException("结构化类型 不可辨识:" + request.StructureType);
}
if (request.Trades == null || request.Trades.Count() < 2)
{
throw new ServiceException("结构化交易组合必须有两条腿及以上");
}
var dbtrades = request.Trades.Select(td =>
{
td.id = 0;
td.StructureType = request.StructureType;
var saveExt = CheckApiOrImportSave(td, dataSource);
var trad = TradeConverter.ConvertOptionTrade(td);
trad.SaveExt = saveExt;
return trad;
}).ToArray();
var struTrade = dbtrades[0].Clone();
struTrade.TradeType = "结构化交易";
struTrade.SubTrades = dbtrades;
return new TradeSaveService(OptUser).SaveTrade(struTrade, dataSource, trans);
}
/// <summary>
/// 保存来自API或导入的组合交易
/// </summary>
public trade SaveGroupOptionTradeFromApiOrImport(StructureOrderRequest request, TradeSourceEnum dataSource, IDbContextTransaction trans = null)
{
if (request is null)
{
throw new ArgumentNullException(nameof(request));
}
if (dataSource != TradeSourceEnum.WebApiV2 && dataSource != TradeSourceEnum.导入交易)
{
throw new NotSupportedException("不支持:" + dataSource.ToString());
}
if (request.Trades == null || request.Trades.Count() <= 2)
{
throw new ServiceException("组合交易必须有两条腿及以上");
}
var dbtrades = request.Trades.Select(td =>
{
td.id = 0;
if (td.IsGroup == 1)
{
td.StructureType = td.TradeType;
td.TradeType = "结构化交易";
}
var saveExt = CheckApiOrImportSave(td, dataSource);
var trad = TradeConverter.ConvertOptionTrade(td);
trad.SaveExt = saveExt;
return trad;
}).ToArray();
var struTrade = dbtrades.FirstOrDefault(x => x.IsGroup == 1);
if (struTrade == null)
{
throw new Exception("未找到组合主交易,组合子交易必须和主交易配对出现");
}
struTrade.SubTrades = dbtrades.Where(x => x.IsGroup != 1);
return new TradeSaveService(OptUser).SaveTrade(struTrade, dataSource, trans);
}
/// <summary>
/// 保存组合报价的交易
/// </summary>
public List<trade> SaveOptionTradesFromPricing(IEnumerable<OtcOptionTradeFull> trades)
{
var tdGroups = trades.GroupBy(n => n.CalcId.Split('-')[0]).ToArray();
var dbTrades = new List<trade>();
var saveChecker = new OtcOptionSaveChecker(this);
var tags = (trades != null && trades.Any()) ? trades.First().Tags : null;
try
{
foreach (var tdGroup in tdGroups)
{
var tdList = tdGroup.Select(td =>
{
saveChecker.CheckBasic(td).CheckAnnualizeFactorForEditSave(td);
saveChecker.CheckPrincipalRate(td).CheckParticipationRate(td);
saveChecker.CheckExoticFields(td);
return TradeConverter.ConvertOptionTrade(td);
}).ToArray();
var tdConv = tdList.Where(O => O.TradeType != "现金流交易").FirstOrDefault() ?? tdList[0];
if (tdList.Length > 1)
{
tdConv = tdConv.Clone();
tdConv.TradeType = "结构化交易";
tdConv.SubTrades = tdList;
}
if (PS.Config.Is浙期 && IsCodesExistsCommoditySpot(new string[] { tdConv.UnderlyingCode }))
{
tdConv.MetaDic["ActualExerciseDate"] = tdConv.ExerciseDate.Value.ToString("yyyy-MM-dd"); //没有录入
}
var dbTrade = new TradeSaveService(this).SaveTrade(tdConv, TradeSourceEnum.系统交易);
using (var tagService = new TagService(OptUser))
{
tagService.SetTradeTagForTradeEdit(new TagModule.Dto.SetTradeTagForTradeEditRequest
{
TradeId = dbTrade.id,
Tags = tags
});
if ("结构化交易".Equals(dbTrade.TradeType))
{
if (dbTrade.SubTrades != null && dbTrade.SubTrades.Any())
{
dbTrade.SubTrades.ToList().ForEach(p =>
{
if (p.id > 0)
{
tagService.SetTradeTagForTradeEdit(new TagModule.Dto.SetTradeTagForTradeEditRequest
{
TradeId = p.id,
Tags = tags
});
}
});
}
}
}
var variety = UnderlyingDataProvider.GetVariety(dbTrade.UnderlyingCode);
if (variety != null)
{
dbTrade.QuoteUnit = variety.QuoteUnit;
dbTrade.QuoteUnitSingle = variety.QuoteUnitSingle;
}
dbTrades.Add(dbTrade);
}
}
catch
{
if (dbTrades.Count > 0)
{
var ids = dbTrades.Select(n => n.id.ToString()).ToArray();
var sql = "UPDATE trade SET ValidState='InValid', TradeNumber='XX', ClientId=0, ChangeMsg='因组合报价录入交易部分失败所以无效' WHERE id in ({0}) OR ParentTradeId in ({0});";
sql = string.Format(sql, string.Join(",", ids));
var changes = DbContext.Database.ExecuteSqlRaw(sql);
}
throw;
}
return dbTrades;
}
public trade SaveOptionForwardTradeFromApi(OtcTradeForward req, TradeSourceEnum dataSource)
{
req.TradeType = "远期";
var otcOptionTradeFullEx = new OtcOptionTradeFullEx { ClientName = req.ClientName };
new OtcOptionSaveChecker(this).CheckAssetBook(req).CheckTrader(req).CheckClient(otcOptionTradeFullEx);
req.ClientId = otcOptionTradeFullEx.ClientId;
var um = DataCacheProvider.GetUnderlyingDataSource().GetData(req.UnderlyingCode);
if (um == null)
{
throw new ServiceException("系统中不存在,传入的标的1代码");
}
else if (!string.IsNullOrEmpty(req.BasisUnderlyingCode))
{
if (DataCacheProvider.GetUnderlyingDataSource().GetData(req.BasisUnderlyingCode) == null)
{
throw new ServiceException("系统中不存在,传入的标的2代码");
}
}
if (req.IsSupplyForwardValue == 0)
{
req.MetaDic["IsSupplyForwardValue"] = "0";
}
else if (req.IsSupplyForwardValue == -1)
{
req.MetaDic["IsSupplyForwardValue"] = "-1";
}
else if (req.IsSupplyForwardValue == 1)
{
req.MetaDic["IsSupplyForwardValue"] = "1";
}
else
{
throw new ServiceException("是否补偿远期价值,传入值错误,只能为0,1,-1;");
}
if (req.PriceModel == 0)
{
if ((PS.Config.ErpElement.ForwardTradePriceModel == ForwardTradePriceModel.STANDARD || PS.Config.ErpElement.ForwardTradePriceModel == ForwardTradePriceModel.HOOK_PRICE))
{
if (PS.Config.ErpElement.ForwardTradePriceModel == ForwardTradePriceModel.STANDARD)
{
var spList = new List<string>
{
req.SpotPrice.OtcFormatUmPrice()
};
if (!string.IsNullOrEmpty(req.BasisUnderlyingCode))
{
if (req.SpotPrice2 == null)
{
throw new ServiceException("填写标的2代码,则必须填写标的2期初价格");
}
else
{
spList.Add((req.SpotPrice2 ?? 0).OtcFormatUmPrice());
}
}
else
{
throw new ServiceException("请填写标的2代码");
}
req.SpotPrice = (double)((decimal)req.SpotPrice - (decimal)(req.SpotPrice2 ?? 0));
req.MetaDic["期初信息"] = spList.ToJson();
}
new ForwardModule.ForwardTradeImportService(UserInfo).GetSpotPrice(req);
}
}
else
{
var spList = new List<string>
{
req.SpotPrice.OtcFormatUmPrice()
};
req.MetaDic["期初信息"] = spList.ToJson();
}
new ForwardModule.ForwardTradeImportService(UserInfo).GetCountRatio(req);
new ForwardModule.ForwardTradeImportService(UserInfo).GetNotional_TradeAmount_Lots(req, um);
new ForwardModule.ForwardTradeImportService(UserInfo).GetForwardValue(req);
new ForwardModule.ForwardTradeImportService(UserInfo).GetOpenCommission_TradePrice(req, null, req.OpenCommission, req.TradePrice);
var td = TradeConverter.ConvertForward(req);
td = new TradeSaveService(this).SaveTrade(td, dataSource);
var variety = UnderlyingDataProvider.GetVariety(td.UnderlyingCode);
if (variety != null)
{
td.QuoteUnit = variety.QuoteUnit;
td.QuoteUnitSingle = variety.QuoteUnitSingle;
}
return td;
}
/// <summary>
/// 保存分组交易
/// </summary>
public trade SaveGroupTradeFromPricing(trade trade, IEnumerable<OtcOptionTradeFull> subTrades)
{
var saveChecker = new OtcOptionSaveChecker(this);
subTrades.ToList().ForEach(x =>
{
saveChecker.CheckBasic(x).CheckAnnualizeFactorForEditSave(x);
saveChecker.CheckPrincipalRate(x).CheckParticipationRate(x);
saveChecker.CheckExoticFields(x);
});
trade.TradeType = "结构化交易";
trade.IsGroup = 1;
trade.SubTrades = subTrades.Select(x => TradeConverter.ConvertOptionTrade(x)).ToArray();
trade.UnderlyingCode = trade.SubTrades.FirstOrDefault(x => !string.IsNullOrEmpty(x.UnderlyingCode)).UnderlyingCode;
var dbTrade = new TradeSaveService(this).SaveTrade(trade, TradeSourceEnum.系统交易);
using (var tagService = new TagService(OptUser))
{
tagService.SetTradeTagForTradeEdit(new TagModule.Dto.SetTradeTagForTradeEditRequest
{
TradeId = dbTrade.id,
Tags = trade.Tags
});
}
return dbTrade;
}
/// <summary>
/// 保存交易编辑的交易
/// </summary>
public trade SaveOptionTradesFromTradeEdit(OtcOptionTradeFull td)
{
var saveChecker = new OtcOptionSaveChecker(this);
saveChecker.CheckBasic(td).CheckAnnualizeFactorForEditSave(td);
saveChecker.CheckPrincipalRate(td).CheckParticipationRate(td);
saveChecker.CheckExoticFields(td);
var tdConv = TradeConverter.ConvertOptionTrade(td);
var dbTrade = new TradeSaveService(this).SaveTrade(tdConv, TradeSourceEnum.系统交易);
var variety = UnderlyingDataProvider.GetVariety(dbTrade.UnderlyingCode);
if (variety != null)
{
dbTrade.QuoteUnit = variety.QuoteUnit;
dbTrade.QuoteUnitSingle = variety.QuoteUnitSingle;
}
using (var tagService = new TagService(OptUser))
{
tagService.SetTradeTagForTradeEdit(new TagModule.Dto.SetTradeTagForTradeEditRequest
{
TradeId = dbTrade.id,
Tags = td.Tags
});
}
return dbTrade;
}
/// <summary>
/// 判断标的是否为现货
/// </summary>
/// <param name="codes"></param>
/// <returns></returns>
public bool IsCodesExistsCommoditySpot(string[] codes)
{
return UndelyingHelper.IsCodesExistsCommoditySpot(codes, (um) =>
{
return um.UnderlyingInstrumentType == "CommoditySpot" ? true : false;
});
}
//检查导入和API数据
private TradeSaveExt CheckApiOrImportSave(OtcOptionTradeFullEx td, TradeSourceEnum dataSource)
{
if (dataSource != TradeSourceEnum.WebApiV2 && dataSource != TradeSourceEnum.导入交易)
{
throw new NotSupportedException("不支持:" + dataSource.ToString());
}
var saveChecker = new OtcOptionSaveChecker(this).CheckBasic(td);
//簿记、交易员、交易对手
saveChecker.CheckAssetBook(td).CheckTrader(td).CheckClient(td);
//年化系数
saveChecker.CheckAnnualizeFactorForApiOrImport(td);
//保底收益率、参与率
saveChecker.CheckPrincipalRate(td).CheckParticipationRate(td);
TradingPremiumFlag premiumFlag;
var notionalFlag = TradingNotionalFlag.None;
// 权利金单价/期权费率/权利金总额
// 20200622: 权利金总额/单价/费率、名义本金/份额导入时可能存在为负值的情况
// 20210425: 国海凤凰雪球期权资金记录方向问题,需要权利金可以为负
if (dataSource == TradeSourceEnum.导入交易)
{
if ((td.HasFlag & OtcOptionTradeFullHasFlag.TradeSinglePrice) > 0)
{
premiumFlag = TradingPremiumFlag.TradeSinglePrice;
td.TradeSinglePrice = td.TradeSinglePrice.Value;
}
else if ((td.HasFlag & OtcOptionTradeFullHasFlag.TradePrice) > 0)
{
premiumFlag = TradingPremiumFlag.TradePrice;
td.TradePrice = td.TradePrice.Value;
}
else if ((td.HasFlag & OtcOptionTradeFullHasFlag.PremiumRate) > 0)
{
premiumFlag = TradingPremiumFlag.PremiumRate;
td.PremiumRate = td.PremiumRate.Value;
}
else
{
//暂时不支持小于0
td.TradeSinglePrice = td.PremiumRate = td.TradePrice = 0;
premiumFlag = TradingPremiumFlag.AllZero;
}
}
else
{
if (td.TradeSinglePrice.HasValue)
{
premiumFlag = TradingPremiumFlag.TradeSinglePrice;
}
else if (td.PremiumRate.HasValue)
{
premiumFlag = TradingPremiumFlag.PremiumRate;
}
else
{
//TradePrice在20200803被修改后一直有值,已经不具备优先使用的条件
premiumFlag = TradingPremiumFlag.TradePrice;
}
}
// 有效名义本金/名义本金/交易份额/交易数量
if (notionalFlag == TradingNotionalFlag.None)
{
if (Math.Abs(td.Notional) > 0)
{
notionalFlag = TradingNotionalFlag.Notional;
td.Notional = Math.Abs(td.Notional);
}
else if (Math.Abs(td.TradeAmount) > 0)
{
notionalFlag = TradingNotionalFlag.TradeAmount;
td.TradeAmount = Math.Abs(td.TradeAmount);
}
else if (td.TradeType == "累计期权")
{
throw new ServiceException("有效交易份额/有效交易数量 两个字段中必须有一个值大于0");
}
else if (Math.Abs(td.StockEqvNotional) > 0)
{
notionalFlag = TradingNotionalFlag.StockEqvNotional;
td.StockEqvNotional = Math.Abs(td.StockEqvNotional);
}
else if (Math.Abs(td.StockEqvNotionalReal) > 0)
{
notionalFlag = TradingNotionalFlag.StockEqvNotionalReal;
td.StockEqvNotionalReal = Math.Abs(td.StockEqvNotionalReal);
}
else
{
//交易数量
throw new ServiceException("有效交易份额/有效交易数量/有效名义本金/名义本金 四个字段中必须有一个值大于0");
}
}
if (!(td.TradeType == "结构化交易" && td.IsGroup == 1))
{
td.IsUsePremiumRate = premiumFlag == TradingPremiumFlag.PremiumRate
|| premiumFlag == TradingPremiumFlag.TradePrice && (notionalFlag == TradingNotionalFlag.StockEqvNotionalReal || notionalFlag == TradingNotionalFlag.StockEqvNotional);
}
// 组合标的(使用最大名义本金)
if (saveChecker.CheckSyntheticUnderlying(td, td.SyntheticPrices, out var maxSpotPrice) && maxSpotPrice > 0)
{
//暂时不再支持最大名义本金方式导入处理
//var spotPrice = Math.Abs(td.SpotPrice.Value);
//var spotPriceRate = spotPrice / maxSpotPrice;
//switch (notionalFlag)
//{
// case TradingNotionalFlag.StockEqvNotionalReal:
// td.StockEqvNotionalMax = TradeHelper.GetStockEqvNotional(td.StockEqvNotionalReal, td.ParticipationRate, td.AnnualizeFactor);
// td.StockEqvNotionalReal *= spotPriceRate;
// break;
// case TradingNotionalFlag.StockEqvNotional:
// td.StockEqvNotionalMax = td.StockEqvNotional;
// td.StockEqvNotional *= spotPriceRate;
// break;
// case TradingNotionalFlag.Notional:
// td.StockEqvNotionalMax = td.Notional * maxSpotPrice;
// break;
// case TradingNotionalFlag.TradeAmount:
// //组合标的没有数量和份额的乘积因子
// td.StockEqvNotionalMax = td.TradeAmount * maxSpotPrice;
// break;
// default:
// throw new SystemException("[NotionalFlag]系统程序错误");
//}
}
if (td.Comments?.Length > 100)
{
throw new ServiceException("交易备注 最多100个字");
}
// 奇异期权检查
saveChecker.CheckExoticFields(td);
return new TradeSaveExt
{
PremiumFlag = premiumFlag,
NotionalFlag = notionalFlag
};
}
}
class OtcOptionSaveChecker : YLBaseService
{
public OtcOptionSaveChecker(YLBaseService baseService) : base(baseService)
{
}
/// <summary>
/// 检查基础信息(结构类型/标的代码/初始预付金)
/// </summary>
public OtcOptionSaveChecker CheckBasic(OtcTradeBase td)
{
if (!string.IsNullOrEmpty(td.TradeNumber))
{
var exclueStr = new List<string> { "+","/","?","%","#","&","=" };
exclueStr.ForEach(x =>
{
if (td.TradeNumber.Contains(x))
{
throw new ServiceException("交易编号不合法,不能包含以下字符:"+ exclueStr.Join(","));
}
});
}
// 结构类型
if (string.IsNullOrWhiteSpace(td.TradeType))
{
throw new ServiceException("结构类型 必须填写");
}
if (td.TradeType == "现金流交易" || td.TradeType == "结构化交易" && td.IsGroup == 1)
{
return this;
}
// 标的代码
if (string.IsNullOrWhiteSpace(td.UnderlyingCode))
{
throw new ServiceException("标的代码 必须填写");
}
var um = DataCacheProvider.GetUnderlyingDataSource().GetData(td.UnderlyingCode);
// 标的代码
if (um == null)
{
throw new ServiceException("标的代码 必须在系统中存在:" + td.UnderlyingCode);
}
if (um.UnderlyingInstrumentType == "Stock")
{
var limitStatus = new UnderlyingModule.StockBlackWhiteService(UserInfo).GetStockBlackWhiteList(Configuration.Enums.LimitRangeEnum.Option, out var Codes);
if (Codes != null)
{
if (limitStatus == Codes.Contains(um.UnderlyingCode))
{
if (limitStatus)
{
throw new ServiceException("标的代码 填写错误,存在于黑名单中,请求值:" + td.UnderlyingCode);
}
else
{
throw new ServiceException("标的代码 填写错误,不存在于白名单中,请求值:" + td.UnderlyingCode);
}
}
}
}
return this;
}
/// <summary>
/// 簿记账户
/// </summary>
public OtcOptionSaveChecker CheckAssetBook(OtcTradeBase td)
{
if (td.AssetId > 0)
{
return this;
}
if (string.IsNullOrWhiteSpace(td.AssetBookName))
{
throw new ServiceException("簿记账户 必须填写");
}
var assetUnit = DbContext.assetunit.Where(au => au.Name == td.AssetBookName).Select(n => new { n.id }).FirstOrDefault();
if (assetUnit == null)
{
throw new ServiceException("簿记账户不存在:" + td.AssetBookName);
}
td.AssetId = assetUnit.id;
return this;
}
/// <summary>
/// 交易员赋值及验证逻辑
/// </summary>
public OtcOptionSaveChecker CheckTrader(OtcTradeBase td)
{
if (td.TraderId > 0)
{
return this;
}
if (string.IsNullOrWhiteSpace(td.TraderName))
{
throw new ServiceException("交易员名称 必须填写");
}
using (var sysdb = DbContextFactory.GetErpBaseContext())
{
var systemUser = sysdb.SystemUsers.Where(su => su.LoginName == td.TraderName)
.Select(n => new { n.Id, n.Name }).FirstOrDefault()
?? sysdb.SystemUsers.Where(su => su.Name == td.TraderName)
.Select(n => new { n.Id, n.Name }).FirstOrDefault();
if (systemUser == null)
{
throw new ServiceException("交易员不存在:" + td.TraderName);
}
else
{
td.TraderId = systemUser.Id;
td.TraderName = systemUser.Name;
}
}
return this;
}
/// <summary>
/// 交易对手方
/// </summary>
public OtcOptionSaveChecker CheckClient(OtcOptionTradeFullEx td)
{
if (td.ClientId > 0)
{
return this;
}
var clientQuery = DbContextFactory.GetClientDbContext(OptUser).client.AsQueryable();
if (!string.IsNullOrWhiteSpace(td.ClientNumber))
{
clientQuery = clientQuery.Where(c => c.Number == td.ClientNumber);
}
else if (!string.IsNullOrWhiteSpace(td.ClientName))
{
clientQuery = clientQuery.Where(c => c.Name == td.ClientName);
}
else
{
throw new ServiceException("交易对手方名称或编号 必须填写");
}
var client = clientQuery.Select(n => new { n.id }).FirstOrDefault();
if (client == null)
{
throw new ServiceException($"交易对手方不存在,客户名称:[{td.ClientName}],交易对手方编号:[{td.ClientNumber}]");
}
td.ClientId = client.id;
if (!td.MetaDic.ContainsKey("主协议编号"))
{
var mainProtocal = ClientDataModelV1.getMainProtocols(td.ClientId).FirstOrDefault();
if (mainProtocal != null)
{
td.MetaDic.Add("主协议编号", mainProtocal.Value);
}
}
if (!td.MetaDic.ContainsKey("补充协议编号") && td.MetaDic.ContainsKey("主协议编号"))
{
var sideProtocal = ClientDataModelV1.getSideProtocols(td.MetaDic["主协议编号"]).FirstOrDefault();
if (sideProtocal != null)
{
td.MetaDic.Add("补充协议编号", sideProtocal);
}
}
return this;
}
/// <summary>
/// 年化系数(API或导入的时候使用的是trade的字段所以雪球和凤凰需要转换为自己的字段)
/// </summary>
public OtcOptionSaveChecker CheckAnnualizeFactorForApiOrImport(OtcOptionTradeFull td)
{
//非期权年化
if (!td.IsAnnualized)
{
td.AnnualizeFactor = 1;
if (td.TradeType == "雪球期权" || td.TradeType == "凤凰期权")
{
td.IsAnnualized2 = false;
td.AnnualizeFactor2 = 1;
}
td.MetaDic[nameof(td.AnnualizeFactor)] = null;
td.MetaDic[nameof(td.AnnualizeFactor2)] = null;
return this;
}
//期权年化
if (!td.MetaDic.TryGetValue(nameof(td.AnnualizeFactor), out var str) || string.IsNullOrWhiteSpace(str))
{
if (td.AnnualizeFactor > 0)
{
var daysInYear = CalculatorHelper.GetTradeDayCount().ToDayCountImpl().DaysInYear();
var ttmdays = td.AnnualizeFactor.Value * daysInYear;
td.MetaDic[nameof(td.AnnualizeFactor)] = $"{ttmdays}/{daysInYear}";
}
else
{
var un = UnderlyingDataProvider.GetUnderlying(td.UnderlyingCode);
var ttmdays = TradeCalcHelper.CalculateTTMDays(td.TradeDate.Value, td.SettlementDate ?? td.ExerciseDate.Value, un?.UnderlyingTypeId ?? 0, false);
var daysInYear = CalculatorHelper.GetTradeDayCount().ToDayCountImpl().DaysInYear();
td.AnnualizeFactor = ttmdays / daysInYear;
td.MetaDic[nameof(td.AnnualizeFactor)] = $"{ttmdays}/{daysInYear}";
}
}
//雪球和凤凰使用自己的年化处理方式
if (td.TradeType == "雪球期权" || td.TradeType == "凤凰期权")
{
td.IsAnnualized2 = true;
td.AnnualizeFactor2 = td.AnnualizeFactor;
td.MetaDic[nameof(td.AnnualizeFactor2)] = td.MetaDic[nameof(td.AnnualizeFactor)];
td.IsAnnualized = false;
td.AnnualizeFactor = 1;
td.MetaDic[nameof(td.AnnualizeFactor)] = null;
}
return this;
}
/// <summary>
/// 检查年化系数(组合报价或编辑保存时使用)
/// </summary>
public OtcOptionSaveChecker CheckAnnualizeFactorForEditSave(OtcOptionTradeFull td)
{
var IsAnnualized2 = td.TradeType == "雪球期权" || td.TradeType == "凤凰期权";
if (IsAnnualized2)
{
td.IsAnnualized = false;
td.AnnualizeFactor = 1;
td.MetaDic[nameof(td.AnnualizeFactor)] = null;
if (!td.IsAnnualized2)
{
td.AnnualizeFactor2 = 1;
td.MetaDic[nameof(td.AnnualizeFactor2)] = null;
return this;
}
}
else
{
td.IsAnnualized2 = false;
td.AnnualizeFactor2 = 1;
td.MetaDic[nameof(td.AnnualizeFactor2)] = null;
if (!td.IsAnnualized)
{
td.AnnualizeFactor = 1;
td.MetaDic[nameof(td.AnnualizeFactor)] = null;
return this;
}
}
var AnnualizeFactorField = IsAnnualized2 ? nameof(td.AnnualizeFactor2) : nameof(td.AnnualizeFactor);
if (!td.MetaDic.TryGetValue(AnnualizeFactorField, out var str) || string.IsNullOrWhiteSpace(str))
{
throw new ServiceException("年化系数 必须填写");
}
var index = str.IndexOf('/');
if (index < 0)
{
throw new ServiceException("年化系数 填写错误,格式必须为:天数/年天数,填写值为:" + str);
}
if (!double.TryParse(str.Substring(0, index).Trim(), out var ttmDays))
{
throw new ServiceException("年化系数 填写错误,天数解析失败,填写值为:" + str);
}
if (ttmDays < 0)
{
throw new ServiceException("年化系数 填写错误,格式必须为:天数/年天数,天数必须大于等于0,填写值为:" + str);
}
if (!double.TryParse(str.Substring(index + 1).Trim(), out var daysOfYear))
{
throw new ServiceException("年化系数 填写错误,格式必须为:天数/年天数,年天数解析失败,填写值为:" + str);
}
if (daysOfYear < 1)
{
throw new ServiceException("年化系数 填写错误,格式必须为:天数/年天数,年天数必须大于0,填写值为:" + str);
}
if (IsAnnualized2)
{
td.AnnualizeFactor2 = ttmDays / daysOfYear;
}
else
{
td.AnnualizeFactor = ttmDays / daysOfYear;
}
return this;
}
/// <summary>
/// 保底收益率
/// </summary>
public OtcOptionSaveChecker CheckPrincipalRate(OtcTradeBase td)
{
if (!td.PrincipalRate.HasValue)
{
td.PrincipalRate = 0;
}
else if (td.PrincipalRate.Value < 0)
{
throw new ServiceException("保底收益率 填写错误,必须大于等于0,请求值:" + td.PrincipalRate.Value);
}
return this;
}
/// <summary>
/// 参与率
/// </summary>
public OtcOptionSaveChecker CheckParticipationRate(OtcTradeBase td)
{
if (!td.ParticipationRate.HasValue)
{
td.ParticipationRate = 1;
}
else if (td.ParticipationRate.Value < 0)
{
throw new ServiceException("参与率 填写错误,必须大于等于0,请求值:" + td.ParticipationRate.Value);
}
return this;
}
//组合标的
public bool CheckSyntheticUnderlying(OtcTradeBase td, IEnumerable<UnderlyingPriceModel> syntheticPrices, out double maxSpotPrice)
{
maxSpotPrice = 0;
var synthetic = DataCacheProvider.GetUnderlyingDataSource().GetSyntheticUnderlying(td.UnderlyingCode);
if (synthetic == null)
{
return false;
}
if (syntheticPrices == null)
{
throw new ServiceException("[组合标的]必须填写每个组成标的的期初价格");
}
var model = synthetic.GetSyntheticPriceModel();
var codeSet = model.SuList.Select(n => n.UnderlyingCode).ToHashSet(StringComparer.OrdinalIgnoreCase);
foreach (var item in syntheticPrices)
{
if (string.IsNullOrWhiteSpace(item.UnderlyingCode))
{
continue;
}
if (!codeSet.Remove(item.UnderlyingCode))
{
throw new ServiceException($"[组合标的]填写错误,组合标的中不存在此标的:{item.UnderlyingCode}");
}
model.SuList.First(n => n.UnderlyingCode.Equals(item.UnderlyingCode, StringComparison.OrdinalIgnoreCase)).Price = item.Price;
}
if (codeSet.Any())
{
throw new ServiceException("[组合标的]标的价格未填写完整,缺少:" + string.Join(",", codeSet));
}
//组合标的有独特的名义本金处理方式
maxSpotPrice = model.SuList.Max(n => Math.Abs(n.Coefficient * n.Price));
if (maxSpotPrice < 1e-6)
{
throw new ServiceException("[组合标的]标的价格 填写错误");
}
td.SpotPrice = model.Price = model.SuList.Sum(n => n.Coefficient * n.Price) + model.Constant;
td.MetaDic["组合标的"] = JsonHelper.ToJson(model);
return true;
}
/// <summary>
/// 奇异期权字段检查
/// </summary>
public OtcOptionSaveChecker CheckExoticFields(OtcOptionTradeFull td)
{
switch (td.TradeType)
{
case "香草期权":
case "自定义交易":
case "结构化产品":
case "合成价差期权": break;
case "障碍期权":
CheckBarrierTrade(td); break;
case "Risky期权":
CheckRiskyTrade(td); break;
case "二元期权":
CheckBinaryTrade(td); break;
case "亚式期权":
CheckAisanTrade(td); break;
case "双鲨期权":
CheckDoubleSharkTrade(td); break;
case "区间累积期权":
CheckRangeAccTrade(td); break;
case "凤凰期权":
CheckAutoCallTrade(td); break;
case "雪球期权":
CheckSnowballTrade(td); break;
case "气囊结构":
CheckAirbagTrade(td); break;
case "收益增强结构":
CheckUnderlyigEnhanceTrade(td); break;
case "结构化交易":
case "现金流交易": break;
case "累计期权":
CheckAccumulatorTrade(td); break;
case "彩虹期权":
case "价差期权":
throw new ServiceException("暂不支持");
default:
throw new ServiceException("不可辨识的结构类型:" + td.TradeType);
}
return this;
}
//障碍期权
private void CheckBarrierTrade(OtcOptionTradeFull td)
{
//---------------------------------------------
// 障碍类型
//---------------------------------------------
if (string.IsNullOrWhiteSpace(td.BarrierType))
{
throw new ServiceException("[障碍期权]障碍类型 必须填写");
}
switch (td.BarrierType)
{
default:
throw new ServiceException("[障碍期权]障碍类型 不支持:" + td.BarrierType);
case "上升敲入":
case "上升敲出":
case "下降敲入":
case "下降敲出":
case "双障碍敲入":
case "双障碍敲出":
break;
}
//障碍价格BarrierLow 高障碍价格BarrierHigh
if (td.BarrierType.Contains("双障碍"))
{
if (!td.BarrierHigh.HasValue)
{
throw new ServiceException($"[障碍期权][障碍类型为{td.BarrierType}时]高障碍价格 必须填写");
}
if (td.BarrierHigh < td.BarrierLow)
{
throw new ServiceException("[障碍期权]高障碍价格 必须大于障碍价格");
}
}
//---------------------------------------------
// 观察方式
//---------------------------------------------
if (string.IsNullOrWhiteSpace(td.MonitorType))
{
td.MonitorType = "离散";
}
else if (td.MonitorType != "离散" && td.MonitorType != "连续")
{
throw new ServiceException("[障碍期权]观察方式 填写错误,当前支持的类型为:离散、连续,请求值为:" + td.MonitorType);
}
//---------------------------------------------
// 补偿支付方式
//---------------------------------------------
if (string.IsNullOrWhiteSpace(td.RebateType))
{
td.RebateType = "AtHit";
}
else if (td.RebateType != "AtHit" && td.RebateType != "AtEnd")
{
throw new ServiceException("[障碍期权]补偿支付方式 填写错误,当前支持的类型为:AtHit、AtEnd,请求值为:" + td.RebateType);
}
//补偿金额(根据权利金数据格式进行处理)
if (td.IsUsePremiumRate == true)
{
td.Rebate = 0;
}
else
{
td.RebateRate = 0;
}
//高障碍补偿金额(根据权利金数据格式进行处理)
if (td.IsUsePremiumRate == true)
{
td.RebateHigh = 0;
}
else
{
td.RebateHighRate = 0;
}
//补偿金额是否年化选否的时候,清空DayCount
if (!td.RebateAnnualizedAtKO)
{
td.RebateDayCount = "";
}
//障碍偏移
if (!td.BarrierShift.HasValue)
{
td.BarrierShift = 0;
}
}
//二元期权
private void CheckBinaryTrade(OtcOptionTradeFull td)
{
//---------------------------------------------
// 二元类型
//---------------------------------------------
if (string.IsNullOrWhiteSpace(td.PayoffType))
{
throw new ServiceException("[二元期权]二元类型 必须填写");
}
switch (td.PayoffType)
{
default:
throw new ServiceException("[二元期权]二元类型 不支持:" + td.PayoffType);
case "CashOrNothing":
case "AssetOrNothing":
if (td.ExerciseMode != "European")
{
throw new ServiceException("[二元期权]二元类型 填写错误,所选行权方式不支持:" + td.PayoffType);
}
break;
case "UpOneTouch":
case "DownOneTouch":
case "UpNoTouch":
case "DownNoTouch":
case "DoubleOneTouch":
case "DoubleNoTouch":
if (td.ExerciseMode != "American")
{
throw new ServiceException("[二元期权]二元类型 填写错误,所选行权方式不支持:" + td.PayoffType);
}
break;
}
//补偿金额,高障碍补偿金额(根据权利金数据格式进行处理)
if (td.IsUsePremiumRate == true)
{
td.CashOrNothingAmount = 0;
td.CashOrNothingAmountHigh = 0;
}
else
{
td.CashOrNothingAmountRate = 0;
td.CashOrNothingAmountHighRate = 0;
}
//补偿金额是否年化选否的时候,清空DayCount
if (!td.RebateAnnualizedAtKO)
{
td.RebateDayCount = "";
}
if (td.ExerciseMode == "American")
{
if (td.PayoffType.Contains("Double"))
{
if (!td.BarrierHigh.HasValue)
{
throw new ServiceException($"[二元期权][美式行权,二元类型为{td.PayoffType}时]高障碍价格 必须填写");
}
//高障碍价格BarrierHigh
}
//---------------------------------------------
// 观察方式
//---------------------------------------------
if (string.IsNullOrWhiteSpace(td.MonitorType))
{
td.MonitorType = "离散";
}
else if (td.MonitorType != "离散" && td.MonitorType != "连续")
{
throw new ServiceException("[二元期权]观察方式 填写错误,当前支持的类型为:离散、连续,请求值为:" + td.MonitorType);
}
//---------------------------------------------
// 补偿支付方式
//---------------------------------------------
if (string.IsNullOrWhiteSpace(td.RebateType))
{
td.RebateType = "AtHit";
}
else if (td.RebateType != "AtHit" && td.RebateType != "AtEnd")
{
throw new ServiceException("[二元期权]补偿支付方式 填写错误,当前支持的类型为:AtHit、AtEnd,请求值为:" + td.RebateType);
}
}
else
{
td.MonitorType = "";
td.RebateType = "";
}
}
//亚式期权
private void CheckAisanTrade(OtcOptionTradeFull td)
{
if (!td.AveragingPeriodStartDate.HasValue)
{
td.AveragingPeriodStartDate = td.TradeDate.Value;
}
else if (td.AveragingPeriodStartDate.Value < td.TradeDate.Value || td.AveragingPeriodStartDate >= td.ExerciseDate.Value)
{
throw new ServiceException("[亚式期权]均价起算日 必须在交易开始日和到期日之间");
}
switch (td.PayoffType)
{
default:
if (!string.IsNullOrWhiteSpace(td.PayoffType))
{
throw new ServiceException("[亚式期权]均价计算方式 不支持:" + td.PayoffType);
}
td.PayoffType = td.StrikeType == "Segmented" ? "EnhancedArithmeticAverage" : "ArithmeticAverage";
break;
case "ArithmeticAverage":
case "GeometricAverage":
case "DiscreteArithmeticAverage":
case "EnhancedArithmeticAverage":
break;
}
switch (td.StrikeType)
{
default:
if (!string.IsNullOrWhiteSpace(td.StrikeType))
{
throw new ServiceException("[亚式期权]行权价类型 不支持:" + td.StrikeType);
}
td.StrikeType = "Fixed";
break;
case "Fixed":
break;
case "Segmented":
if (td.PayoffType != "EnhancedArithmeticAverage")
{
throw new ServiceException("[亚式期权]行权价类型为'分段式'时,均价计算类型只支持'增强算术平均'");
}
break;
case "Floating":
td.Strike = null;
break;
}
if (!td.StrikeGearingFactor.HasValue)
{
td.StrikeGearingFactor = 1;
}
else if (td.StrikeGearingFactor < 0)
{
throw new ServiceException("[亚式期权]杠杆率 必须大于等于0");
}
}
//双鲨期权
private void CheckDoubleSharkTrade(OtcOptionTradeFull td)
{
//BarrierLow 障碍价格,BarrierHigh 高障碍价格
if (!td.BarrierHigh.HasValue)
{
throw new ServiceException("[双鲨期权]高障碍价格 必须填写");
}
if (td.BarrierHigh <= td.BarrierLow)
{
throw new ServiceException("[双鲨期权]高障碍价格 必须大于障碍价格");
}
//补偿金额+高障碍补偿金额(根据权利金数据格式进行处理)
if (td.IsUsePremiumRate == true)
{
td.Rebate = 0;
td.RebateHigh = 0;
}
else
{
td.RebateRate = 0;
td.RebateHighRate = 0;
}
//---------------------------------------------
// 观察方式
//---------------------------------------------
if (string.IsNullOrWhiteSpace(td.MonitorType))
{
td.MonitorType = "离散";
}
else if (td.MonitorType != "离散" && td.MonitorType != "连续")
{
throw new ServiceException("[双鲨期权]观察方式 填写错误,当前支持的类型为:离散、连续,请求值为:" + td.MonitorType);
}
//---------------------------------------------
// 补偿支付方式
//---------------------------------------------
if (string.IsNullOrWhiteSpace(td.RebateType))
{
td.RebateType = "AtHit";
}
else if (td.RebateType != "AtHit" && td.RebateType != "AtEnd")
{
throw new ServiceException("[双鲨期权]补偿支付方式 填写错误,当前支持的类型为:AtHit、AtEnd,请求值为:" + td.RebateType);
}
}
//区间累积期权
private void CheckRangeAccTrade(OtcOptionTradeFull td)
{
//区间下限 LowerRange,区间上限 UpperRange
if (td.UpperRange < td.LowerRange)
{
throw new ServiceException("[区间累积期权]区间下限 必须小于 区间上限");
}
if (td.BonusRate < 0)
{
throw new ServiceException("[区间累积期权]区间收益 必须大于等于0");
}
}
//凤凰期权
private void CheckAutoCallTrade(OtcOptionTradeFull td)
{
//敲出障碍价格 KOBarrier,敲入障碍价格:KIBarrier,派息障碍价格 CouponBarrier
//敲入期权行权价2 SpreadStrike,敲入期权行权价1 SpreadStrike1
if (td.Coupon < 0)
{
throw new ServiceException("[凤凰期权]票息/敲出补偿 必须大于等于0");
}
}
//雪球期权
private void CheckSnowballTrade(OtcOptionTradeFull td)
{
if (td.KORebate < 0)
{
throw new ServiceException("[雪球期权]票息/敲出补偿 必须大于等于0");
}
if (td.Coupon < 0)
{
throw new ServiceException("[雪球期权]未敲出补偿 必须大于等于0");
}
if (!string.IsNullOrWhiteSpace(td.KOObservationDates) && !td.KOObservationDates.All(n => n == ';') && !td.KOObservationDates.Contains(td.ExerciseDate.Value.ToString("yyyy-MM-dd")))
{
//throw new ServiceException($"[雪球期权]敲出观察频率 必须包含到期日({td.ExerciseDate.Value:yyyy-MM-dd})");
throw new ServiceException($"[雪球期权]到期日和最后一个敲出观察日不一致,请修改敲出观察日列表");
}
}
//气囊结构
private void CheckAirbagTrade(OtcOptionTradeFull td)
{
//敲入参与率
if (td.KIParticipationRate < 0)
{
throw new ServiceException("[气囊结构]敲入参与率 必须大于等于0");
}
//障碍价格 BarrierLow,收益封顶价格 HighStrike
}
//收益增强结构
private void CheckUnderlyigEnhanceTrade(OtcOptionTradeFull td)
{
if (td.AnnualizedEnhanceRate < 0)
{
throw new ServiceException("[收益增强结构]年化增强收益 必须大于等于0");
}
}
//累计期权
private void CheckAccumulatorTrade(OtcOptionTradeFull td)
{
switch (td.PayoffType)
{
default:
if (!string.IsNullOrWhiteSpace(td.PayoffType))
{
throw new ServiceException("[累计期权]上端收益类型 不支持:" + td.PayoffType);
}
td.PayoffType = "浮动";
break;
case "固定":
case "浮动": break;
}
switch (td.SettlementMode)
{
default:
if (!string.IsNullOrWhiteSpace(td.SettlementMode))
{
throw new ServiceException("[累计期权]结算方式 不支持:" + td.SettlementMode);
}
td.SettlementMode = "现金当日";
break;
case "现金当日":
case "现金期末":
case "实物交割":
case "现金实物合并结算":
break;
case "现金结算(当日)":
td.SettlementMode = "现金当日"; break;
case "现金结算(期末)":
td.SettlementMode = "现金期末"; break;
}
if (string.IsNullOrWhiteSpace(td.CouponDayCount))
{
td.CouponDayCount = "Act365";
}
else
{
var daycounts = Enum.GetNames(typeof(Qdp.Pricing.Base.Enums.DayCount));
if (!daycounts.Any(n => n.Equals(td.CouponDayCount, StringComparison.OrdinalIgnoreCase)))
{
throw new ServiceException("[累计期权]票息日历规则 不支持:" + td.CouponDayCount);
}
}
if (td.AccumulatorStructureType== AccumulatorStructureTypeEnum.Segmented)
{
switch (td.SettlementMode2)
{
default:
if (!string.IsNullOrWhiteSpace(td.SettlementMode2))
{
throw new ServiceException("[累计期权]结算方式 不支持:" + td.SettlementMode2);
}
td.SettlementMode2 = "现金当日";
break;
case "现金当日":
case "现金期末":
case "实物交割": break;
case "现金实物合并结算":
break;
case "现金结算(当日)":
td.SettlementMode2 = "现金当日"; break;
}
switch (td.SettlementMode3)
{
default:
if (!string.IsNullOrWhiteSpace(td.SettlementMode3))
{
throw new ServiceException("[累计期权]结算方式 不支持:" + td.SettlementMode3);
}
td.SettlementMode3 = "现金当日";
break;
case "现金当日":
case "现金期末":
case "实物交割": break;
case "现金实物合并结算":
break;
case "现金结算(当日)":
td.SettlementMode3 = "现金当日"; break;
}
}
}
//Risky期权
private void CheckRiskyTrade(OtcOptionTradeFull td)
{
if (td.Strike1 == null)
{
throw new ServiceException("[Risky期权]执行价格1 必须填写");
}
if (td.Strike2 == null)
{
throw new ServiceException("[Risky期权]执行价格2 必须填写");
}
if (td.Strike3 == null)
{
throw new ServiceException("[Risky期权]执行价格3 必须填写");
}
if (td.ParticipationRate1 == null)
{
throw new ServiceException("[Risky期权]参与率1 必须填写");
}
if (td.ParticipationRate2 == null)
{
throw new ServiceException("[Risky期权]参与率2 必须填写");
}
if (td.ParticipationRate3 == null)
{
throw new ServiceException("[Risky期权]参与率3 必须填写");
}
if (td.Strike1 > td.Strike2)
{
throw new ServiceException("[Risky期权]执行价格1 必须小于等于执行价格2");
}
if (td.Strike2 > td.Strike3)
{
throw new ServiceException("[Risky期权]执行价格2 必须小于等于执行价格3");
}
}
}
/// <summary>
/// 为了解决导入时某些可用null类型判断的属性被人为设定成必然不为null,比如TradePrice
/// </summary>
enum OtcOptionTradeFullHasFlag
{
None = 0,
PremiumRate = 1 << 0,
TradeSinglePrice = 1 << 1,
TradePrice = 1 << 2
}
/// <summary>
/// 场外期权全量(API和导入用到)
/// </summary>
public class OtcOptionTradeFullEx : OtcOptionTradeFull
{
/// <summary>
/// 是否更新已有数据
/// </summary>
public bool _Update { get; set; }
/// <summary>
/// 了结信息
/// </summary>
public TradeCloseRequestModel CloseRequest { get; set; }
/// <summary>
/// 组合标的价格列表
/// </summary>
public IEnumerable<UnderlyingPriceModel> SyntheticPrices { get; set; }
/// <summary>
/// HasFlag
/// </summary>
internal OtcOptionTradeFullHasFlag HasFlag { get; set; }
}
/// <summary>
/// 组合交易下单请求
/// </summary>
public class StructureOrderRequest
{
public string StructureType { get; set; }
public IEnumerable<OtcOptionTradeFullEx> Trades { get; set; }
}
}
//if (td.IsUsePremiumRate == true)
//{
// td.StockEqvNotionalMax = TradeHelper.GetStockEqvNotionalReal(td.StockEqvNotional, td.ParticipationRate, td.AnnualizeFactor);
//}
//else
//{
// td.StockEqvNotionalReal = spotPrice * td.Notional;
// td.StockEqvNotional = TradeHelper.GetStockEqvNotional(td.StockEqvNotionalReal, td.ParticipationRate, td.AnnualizeFactor);
//}