using Newtonsoft.Json.Linq; using YLErp.BLL; using YLErp.Core.Helpers; using YLErp.DBModels; using YLErp.DBModels.Consts; using YLErp.DBModels.Enums; using YLErp.Modules.MarginModule; using YLErp.Modules.UnderlyingModule; using YLErp.Plugins.TradeDocGenerator; using YLErp.Plugins.TradeDocGenerator.Abstracts; using YieldChain.Helpers; namespace YLErp.Plugins.GuoLian.DocumentGenerator { /// /// 国联证券交易确认书生成器(单个生成) /// internal class TradeConfirmationGenerator : BaseConfirmationGenerator, ITradeConfirmationGenerator { public TradeConfirmationGenerator() : base(ContractTypeEnum.Trade) { } /// /// 获取模板文件路径 /// 收益互换模板选择逻辑: /// 1. 客户看多/看空:根据 swap_position 的 PosiDirection(收支方向)和 PositionType(多空方向) /// 组合出我方方向,再取反得到客户方向。 /// 2. 标的类型: /// - 债券ETF:.SH后缀且511开头,或.SZ后缀且59开头 /// - 现券:其他 /// protected override string GetTemplateFilePath() { var trade = Context.Trade; if (trade.TradeType == "收益互换") { return GetSwapTemplatePath(); } else if (trade.TradeType.Contains("期权")) { // 期权使用期权模板 return Path.Combine(GlobalConfig.PluginFolder, "App_Docs\\contract_template\\option_01.docx"); } else { throw new Exception($"不支持的贸易类型: {trade.TradeType}"); } } /// /// 判断客户是否为看多方向 /// 规则:根据 PosiDirection(收支方向)和 PositionType(多空方向)组合出我方方向,再取反得到客户方向 /// private bool IsCustomerLong(swap_position position) { if (position == null) return false; bool isOurLong = position.PosiDirection == (int)SwapDirectionEnum.支付 ? position.PositionType == (int)PositionTypeFlag.Short // 支付端:我方方向与 PositionType 相反 : position.PositionType == (int)PositionTypeFlag.Long; // 收取端:我方方向与 PositionType 相同 return !isOurLong; } /// /// 阶段三 §3.3 决策①:方案B手工录入结构直接赋值(不再用 100∓n×(x−y) 推导)。 /// 模板追保表 4 行 ↔ SpanConfig 4 层:多头 第1层=[Lower,+∞)(模板行0只用下限)、第n层=[Lower,Upper); /// 空头 第1层=(−∞,Upper](模板行0只用上限)、第n层=(Lower,Upper]; /// 追保金额比例 = AmountRate×100(累计到该层的追保比例,直接取值),金额文字口径 n%×期初全价×券面总额 由模板静态文字承载; /// 预警线/平仓线直接取录入值,未录时按 docx 口径取边界(平仓线=最深层边界、预警线=次深层边界); /// 配置层不足 4 层时余下行置空;返还追保事件表与追保事件同值(模板静态文字区分方向措辞)。 /// private static void FillSpanConfigLines(Dictionary dic, SpanConfig spanCfg, bool posiLong) { var tiers = (posiLong ? spanCfg.LongSpans : spanCfg.ShortSpans) ?? new List(); var valid = tiers.Where(t => t != null && (t.Lower.HasValue || t.Upper.HasValue)).ToList(); var last = valid.LastOrDefault(); var warnLine = spanCfg.WarnLine; var closeLine = spanCfg.CloseLine; if (last != null) { closeLine ??= posiLong ? last.Lower : last.Upper; warnLine ??= posiLong ? last.Upper : last.Lower; } dic["预警线"] = warnLine.HasValue ? (warnLine.Value * 100).ToString("0.##") : ""; dic["平仓线"] = closeLine.HasValue ? (closeLine.Value * 100).ToString("0.##") : ""; //档位n = 第n层触发线(多头取各层 Lower、空头取各层 Upper),现用模板无档位占位符,保留赋值兼容旧模板 for (int n = 1; n <= 4; n++) { var tier = n <= valid.Count ? valid[n - 1] : null; var line = tier == null ? null : (posiLong ? tier.Lower : tier.Upper); dic[$"档位{n}"] = line.HasValue ? (line.Value * 100).ToString("0.##") : ""; } //追保事件/返还追保事件区间表:行0=第1层,行1..3=第2..4层; //与上方档位循环同用过滤后的 valid 列表(BUG-26:配置中间夹空层时两类占位符不错位) for (int i = 0; i <= 3; i++) { var tier = i < valid.Count ? valid[i] : null; var hasTier = tier != null && (tier.Lower.HasValue || tier.Upper.HasValue); dic[$"追保区间{i}下限"] = hasTier && tier.Lower.HasValue ? (tier.Lower.Value * 100).ToString("0.##") : ""; dic[$"追保区间{i}上限"] = hasTier && tier.Upper.HasValue ? (tier.Upper.Value * 100).ToString("0.##") : ""; dic[$"追保金额比例{i}"] = hasTier && tier.AmountRate.HasValue ? (tier.AmountRate.Value * 100).ToString("0.##") : ""; dic[$"返还区间{i}下限"] = dic[$"追保区间{i}下限"]; dic[$"返还区间{i}上限"] = dic[$"追保区间{i}上限"]; dic[$"返还金额比例{i}"] = dic[$"追保金额比例{i}"]; } } /// /// 获取收益互换模板路径 /// private string GetSwapTemplatePath() { var trade = Context.Trade; var underlying = Context.GetTradeUnderlying(); // 判断客户看多/看空方向 var swapPosition = Context.GetSwapPositions(trade.id, true) .Where(x => x.PositionType == (int)PositionTypeFlag.Long || x.PositionType == (int)PositionTypeFlag.Short) .FirstOrDefault(); bool isCustomerLong = IsCustomerLong(swapPosition); // 判断标的类型(债券ETF vs 现券) bool isEtf = IsBondEtf(underlying?.UnderlyingCode ?? string.Empty); // 选择对应模板(使用客户提供的原始文件名) string templateName; if (isCustomerLong && !isEtf) templateName = "国联民生-收益互换交易确认书-境内模板-【客户看多】-【现券】-清洁版.docx"; else if (isCustomerLong && isEtf) templateName = "国联民生-收益互换交易确认书-境内模板-【客户看多】-【债券ETF】-清洁版.docx"; else if (!isCustomerLong && !isEtf) templateName = "国联民生-收益互换交易确认书-境内模板-【客户看空】-【现券】-清洁版.docx"; else templateName = "国联民生-收益互换交易确认书-境内模板-【客户看空】-【债券ETF】-清洁版.docx"; return Path.Combine(GlobalConfig.PluginFolder, "App_Docs/contract_template", templateName); } /// /// 判断是否为债券ETF /// - .SH后缀且511开头 -> 债券ETF /// - .SZ后缀且159开头 -> 债券ETF /// - 其他 -> 现券 /// private bool IsBondEtf(string underlyingCode) { if (string.IsNullOrWhiteSpace(underlyingCode)) return false; underlyingCode = underlyingCode.Trim().ToUpper(); // 上交所债券ETF:511开头.SH后缀 if (underlyingCode.EndsWith(".SH") && underlyingCode.StartsWith("511")) return true; // 深交所债券ETF:59开头.SZ后缀 if (underlyingCode.EndsWith(".SZ") && underlyingCode.StartsWith("159")) return true; return false; } /// /// 获取输出文件名 /// protected override string GetOutputFileName(string contractNo, string contractIndex) { var trade = Context.Trade; var rule = $"【待用印】{trade.TradeType}交易确认书_{contractNo}"; return $"{rule}.{DocType.ToLower()}"; } /// /// 获取合同编号 /// protected override string GetContractNo(out string contractIndex) { var contractNo = Context.GenerateContractNo(out contractIndex); return contractNo; } /// /// 准备视图数据(Word模板数据) /// protected override void PrepareViewData(Dictionary dic) { var trade = Context.Trade; var client = Context.GetClient(); // 基础信息 // 注意:合同编号由基类统一设置,此处不再重复生成 dic["交易编号"] = trade.TradeNumber; dic["客户名称"] = client.Name; dic["乙方名称"] = client.Name; // 交易日期相关 dic["成交日期"] = trade.TradeDate?.ToString("【yyyy】年【M】月【d】日"); dic["成交日期/"] = trade.TradeDate?.ToString("yyyy/M/d"); dic["开始日期"] = trade.StartDate?.ToString("【yyyy】年【M】月【d】日"); dic["到期日期"] = trade.ExerciseDate?.ToString("【yyyy】年【M】月【d】日"); // 根据交易类型填充不同数据 switch (trade.TradeType) { case "收益互换": SetSwapData(dic); break; case "香草期权": case "亚式期权": case "障碍期权": SetOptionData(dic); break; default: // 默认基础数据 SetBaseData(dic); break; } } /// /// 设置收益互换数据 /// private void SetSwapData(Dictionary dic) { var trade = Context.Trade; var client = Context.GetClient(); var bank = Context.GetClientBankCard(false); dic["交易对手方全称"] = client.ClientType != "产品" ? client.Name : $"{client.Manager}作为管理人代表{client.Name}"; // 主协议编号:优先取 client 表字段,为空时从 client_meta 表兜底 var mainProtocolCode = client.MainProtocolCode; if (string.IsNullOrWhiteSpace(mainProtocolCode)) { mainProtocolCode = Context.GetClientMeta(client.id, ConsTradeMetaKey.MainProtocolCode)?.MetaValue; } dic["主协议编号"] = mainProtocolCode ?? ""; // 主协议类型取客户开户维护的下拉选择(client_meta.MainProtocolType):1=NAFMII,其余按 SAC。 var mainProtocolType = Context.GetClientMeta(client.id, "MainProtocolType")?.MetaValue; var isNafmii = mainProtocolType == "1"; if (string.IsNullOrWhiteSpace(mainProtocolType)) { LogFactory.GetLogger("确认书生成").Info($"客户 {client.Number} {client.Name} 未维护主协议类型,确认书协议段按 SAC 勾选展示"); } // 协议段勾选框对应模板占位符,按客户签署的主协议类型动态勾选,替代模板中硬编码的 Wingdings 2 复选框 static string Check(bool on) => on ? "☑" : "□"; dic["主协议SAC勾选"] = Check(!isNafmii); dic["主协议NAFMII勾选"] = Check(isNafmii); dic["补充协议SAC勾选"] = Check(!isNafmii); dic["补充协议NAFMII勾选"] = Check(isNafmii); // 定义文件签署维度暂无客户协议数据,统一展示空框,待客户资料补齐后接入 dic["协会证券业勾选"] = "□"; dic["协会交易商勾选"] = "□"; dic["定义文件商品勾选"] = "□"; dic["定义文件利率勾选"] = "□"; dic["定义文件债券勾选"] = "□"; // 补充协议编号:优先取 client 表字段,为空时从 client_meta 表兜底 var supProtocolCode = client.SupProtocolCode; if (string.IsNullOrWhiteSpace(supProtocolCode)) { supProtocolCode = Context.GetClientMeta(client.id, ConsTradeMetaKey.SupProtocolCode)?.MetaValue; } dic["补充协议编号"] = supProtocolCode ?? ""; dic["名义本金"] = trade.OriginalStockEqvNotional?.ToString("N2") ?? "0.00"; // 银行账户信息 dic["户名"] = bank?.ClientName ?? ""; dic["银行账号"] = bank?.Card ?? ""; dic["支付系统号"] = bank?.Payment ?? ""; dic["开户行"] = bank?.Bank ?? ""; dic["大额行号"] = bank?.Payment ?? ""; // 标的相关的数据可以通过 Context 获取 var underlying = Context.GetTradeUnderlying(); if (underlying != null) { dic["标的代码"] = underlying.UnderlyingCode; dic["标的名称"] = underlying.UnderlyingName; // 标的发行人(从债券信息中获取) var bond = JsonHelper.Deserialize(underlying.ExJson) ?? new UnderlyingBond(); dic["参考标的发行人"] = bond.UnderlyingIssuer ?? ""; dic["参考标的担保人"] = ""; dic["票面利率"] = ((double)(bond.CouponRate ?? 0) * 100).ToString("0.00"); dic["参考标的到期日"] = underlying.MaturityDate?.ToString("【yyyy】年【M】月【d】日") ?? ""; } // 从swap_position获取期初价格、保证金率等信息 var swapPositions = Context.GetSwapPositions(trade.id, true); var swapPosition = swapPositions .Where(x => x.PositionType == (int)PositionTypeFlag.Long || x.PositionType == (int)PositionTypeFlag.Short) .FirstOrDefault(); if (swapPosition != null) { // 期初全价和净价(转换为百分比格式) dic["参考标的期初全价"] = ((double)swapPosition.PosiGrossPrice).ToString("0.000"); dic["参考标的期初净价"] = ((double)(swapPosition.PosiNetNoFeePrice ?? 0m)).ToString("N4"); dic["参考标的期初全价%"] = ((double)swapPosition.PosiGrossPrice * 100).ToString("N4"); dic["参考标的期初净价%"] = ((double)(swapPosition.PosiNetNoFeePrice ?? 0m) * 100).ToString("N4"); // 固定收益率(年化)仅对应增强收益腿;互换利率腿在模板中应留空。 bool isEtf = IsBondEtf(underlying?.UnderlyingCode ?? string.Empty); var enhancePosition = swapPositions .FirstOrDefault(x => ConsTrade.InterestModels.Contains(x.InterestMode) && x.category_tag == "增强收益"); var swapRatePosition = swapPositions .FirstOrDefault(x => ConsTrade.InterestModels.Contains(x.InterestMode) && x.category_tag == "互换利率"); dic["固定收益率"] = enhancePosition != null ? ((double)enhancePosition.InterestRateDefault * 100).ToString("N4") : swapRatePosition != null ? "" : "0.0000"; // 获取客户适用的保证金率 //阶段三 §3.3 决策①:档位/预警线/平仓线/追保区间统一按交易绑定的预付金模板V2参数赋值—— //方案B手工录入结构(SpanConfig.WarnLine/CloseLine/LongSpans/ShortSpans)直接取录入值,不再用 100∓n×(x−y) 推导; //旧互换预付金率维护(client_margin_config / UnderlyingHelper.GetApplicableMarginRate)回退链路已移除 //(本注释保留一个观察周期后删除):取不到模板参数时率显示 0、档位与区间置空。 var templateRate = MarginTemplateV2RateHelper.GetTradeMarginRate( trade.id, swapPosition.UnderlyingCode, swapPosition.UnderlyingInstrumentType, trade.TradeDate ?? DateTime.Now); var initRate = templateRate?.InitRate ?? 0m; var maintainRate = templateRate?.MaintainRate ?? 0m; var spanCfg = templateRate?.Detail?.SpanConfig; dic["初始保障金率"] = ((double)initRate * 100).ToString("N4"); dic["维持保障金率"] = ((double)maintainRate * 100).ToString("N4"); // 期初预付/期初现金交换:预付=初始预付金腿合计(模板口径:可全部或部分授信替代), // 现金=资金记录实付现金(ClientCashInCashOut:Action=应付预付金且 Money<0 即客户期初实付; // 授信部分不产生资金流水,平仓返还为正数、追加保证金是独立Action,均不落入该口径) var notional = trade.OriginalStockEqvNotional ?? 0; var initialLegs = swapPositions.Where(x => x.InterestMode == (int)InterestModeEnum.初始预付金).ToList(); var initialLegIds = initialLegs.Select(x => x.id).ToList(); double cashPaid; using (var db = new YLContext()) { cashPaid = db.ClientCashInCashOut .Where(x => x.TradeId == trade.id && x.Action == ClientCashInCashOut.系统操作_应付预付金 && x.ValidState != ConsGlobal.InValid && x.Money < 0 && (x.Deal == 0 || initialLegIds.Contains(x.Deal))) .Sum(x => x.Money) ?? 0; } var cashRatio = notional == 0 ? 0 : -cashPaid / notional * 100; dic["期初现金交换比例"] = cashRatio.ToString("0.##"); dic["期初现金交换金额"] = (-cashPaid).ToString("N2"); // 期初预付比例和金额:有应付腿取腿合计(合约录入值),无应付腿(无预付金模板/合约维度盯市)走模板率兜底 var totalPayable = initialLegs .Select(x => x.InterestPrincipalFix * (x.InterestDirection == 1 ? 1m : -1m)) .Where(x => x > 0) .Sum(); if (totalPayable > 0) { dic["期初预付比例"] = (notional == 0 ? 0 : (double)totalPayable / notional * 100).ToString("0.##"); dic["期初预付金额"] = ((double)totalPayable).ToString("N2"); } else { dic["期初预付比例"] = ((double)initRate * 100).ToString("0.##"); dic["期初预付金额"] = (notional * (double)initRate).ToString("N2"); } bool posiLong = IsCustomerLong(swapPosition); var maintainRatePercent = (double)maintainRate * 100; // A(%) if (YLErp.Modules.SwapModule.Margin.SwapSpanMarginCalc.HasSpanConfig(spanCfg)) { //方案B:直接取录入的预警/平仓线与多空区间(未录线时按 docx 口径取最深/次深档位边界) FillSpanConfigLines(dic, spanCfg, posiLong); } else { //存量 x/y 配置:维持 100∓n×(B−A) 推导(B=初始率,A=维持率) var initRatePercent = (double)initRate * 100; // B(%) var diff = initRatePercent - maintainRatePercent; // B - A if (maintainRatePercent > 0) { if (posiLong) { // 客户看多:档位n = 100 - n * (B-A) dic["档位1"] = (100 - 1 * diff).ToString("0.##"); dic["档位2"] = (100 - 2 * diff).ToString("0.##"); dic["档位3"] = (100 - 3 * diff).ToString("0.##"); dic["档位4"] = (100 - 4 * diff).ToString("0.##"); dic["预警线"] = dic["档位4"]; // 预警线 = 最高档位 dic["平仓线"] = (100 - 5 * diff).ToString("0.##"); // 平仓线 = 最高档位 - (B-A) } else { // 客户看空:档位n = 100 + n * (B-A) dic["档位1"] = (100 + 1 * diff).ToString("0.##"); dic["档位2"] = (100 + 2 * diff).ToString("0.##"); dic["档位3"] = (100 + 3 * diff).ToString("0.##"); dic["档位4"] = (100 + 4 * diff).ToString("0.##"); dic["预警线"] = dic["档位4"]; // 预警线 = 最高档位 dic["平仓线"] = (100 + 5 * diff).ToString("0.##"); // 平仓线 = 最高档位 + (B-A) } } else { dic["平仓线"] = ""; dic["预警线"] = ""; dic["档位1"] = ""; dic["档位2"] = ""; dic["档位3"] = ""; dic["档位4"] = ""; } if (maintainRatePercent > 0) { if (posiLong) { // === 客户看多 追保事件 === // 区间0:当前净价 ≥ {100-(B-A)}% × 期初净价,追保0% dic["追保区间0下限"] = (100 - 1 * diff).ToString("0.##"); dic["追保金额比例0"] = "0"; // 区间1:{100-2(B-A)}% ≤ 当前净价 < {100-(B-A)}%,追保{B-A}% dic["追保区间1下限"] = (100 - 2 * diff).ToString("0.##"); dic["追保区间1上限"] = (100 - 1 * diff).ToString("0.##"); dic["追保金额比例1"] = (1 * diff).ToString("0.##"); // 区间2:{100-3(B-A)}% ≤ 当前净价 < {100-2(B-A)}%,追保{2(B-A)}% dic["追保区间2下限"] = (100 - 3 * diff).ToString("0.##"); dic["追保区间2上限"] = (100 - 2 * diff).ToString("0.##"); dic["追保金额比例2"] = (2 * diff).ToString("0.##"); // 区间3:{100-4(B-A)}% ≤ 当前净价 < {100-3(B-A)}%,追保{3(B-A)}% dic["追保区间3下限"] = (100 - 4 * diff).ToString("0.##"); dic["追保区间3上限"] = (100 - 3 * diff).ToString("0.##"); dic["追保金额比例3"] = (3 * diff).ToString("0.##"); // === 客户看多 返还追保事件 === for (int n = 1; n <= 3; n++) { dic[$"返还区间{n}下限"] = (100 - (n + 1) * diff).ToString("0.##"); dic[$"返还区间{n}上限"] = (100 - n * diff).ToString("0.##"); dic[$"返还金额比例{n}"] = (n * diff).ToString("0.##"); } dic["返还区间0下限"] = (100 - 1 * diff).ToString("0.##"); dic["返还金额比例0"] = "0"; } else { // === 客户看空 追保事件 === // 区间0:当前净价 ≤ {100+(B-A)}% × 期初净价,追保0% dic["追保区间0上限"] = (100 + 1 * diff).ToString("0.##"); dic["追保金额比例0"] = "0"; // 区间1:{100+(B-A)}% < 当前净价 ≤ {100+2(B-A)}%,追保{B-A}% dic["追保区间1下限"] = (100 + 1 * diff).ToString("0.##"); dic["追保区间1上限"] = (100 + 2 * diff).ToString("0.##"); dic["追保金额比例1"] = (1 * diff).ToString("0.##"); // 区间2:{100+2(B-A)}% < 当前净价 ≤ {100+3(B-A)}%,追保{2(B-A)}% dic["追保区间2下限"] = (100 + 2 * diff).ToString("0.##"); dic["追保区间2上限"] = (100 + 3 * diff).ToString("0.##"); dic["追保金额比例2"] = (2 * diff).ToString("0.##"); // 区间3:{100+3(B-A)}% < 当前净价 ≤ {100+4(B-A)}%,追保{3(B-A)}% dic["追保区间3下限"] = (100 + 3 * diff).ToString("0.##"); dic["追保区间3上限"] = (100 + 4 * diff).ToString("0.##"); dic["追保金额比例3"] = (3 * diff).ToString("0.##"); // === 客户看空 返还追保事件 === for (int n = 1; n <= 3; n++) { dic[$"返还区间{n}下限"] = (100 + n * diff).ToString("0.##"); dic[$"返还区间{n}上限"] = (100 + (n + 1) * diff).ToString("0.##"); dic[$"返还金额比例{n}"] = (n * diff).ToString("0.##"); } dic["返还区间0上限"] = (100 + 1 * diff).ToString("0.##"); dic["返还金额比例0"] = "0"; } } else { // 清空追保/返还相关字典值 for (int i = 0; i <= 3; i++) { dic[$"追保区间{i}下限"] = ""; dic[$"追保区间{i}上限"] = ""; dic[$"追保金额比例{i}"] = ""; dic[$"返还区间{i}下限"] = ""; dic[$"返还区间{i}上限"] = ""; dic[$"返还金额比例{i}"] = ""; } } } // 期初预付金利率(InterestMode == 初始预付金) var initialMarginPosition = initialLegs.FirstOrDefault(); dic["期初预付金利率"] = initialMarginPosition != null ? ((double)initialMarginPosition.InterestRateDefault * 100).ToString("N4") : "0.0000"; // 利率类型判断(固定/浮动) swap_position interestMargin = null; // 优先取互换利率腿,避免同时存在增强收益腿时受集合顺序影响。 interestMargin = swapPositions .Where(x => ConsTrade.InterestModels.Contains(x.InterestMode) && x.category_tag == "互换利率") .FirstOrDefault(); if (interestMargin == null) interestMargin = swapPositions .Where(x => ConsTrade.InterestModels.Contains(x.InterestMode) && x.interest_rest_days != null) .FirstOrDefault(); if (interestMargin == null) interestMargin = swapPositions .Where(x => ConsTrade.InterestModels.Contains(x.InterestMode) && string.IsNullOrWhiteSpace(x.FloatRateUnderlyingCode)) .FirstOrDefault(); if (interestMargin == null) interestMargin = swapPositions .Where(x => ConsTrade.InterestModels.Contains(x.InterestMode)) .FirstOrDefault(); dic["IsFixed"] = "□"; dic["IsFloat"] = "□"; dic["期初观察日"] = trade.StartDate?.ToString("【yyyy】年【M】月【d】日") ?? ""; dic["期末观察日"] = trade.ExerciseDate?.ToString("【yyyy】年【M】月【d】日") ?? ""; if (interestMargin != null) { // 增强收益腿在确认书中固定展示为固定利率,不展示其浮动利率标的或利差。 if (interestMargin.category_tag == "增强收益" || string.IsNullOrEmpty(interestMargin.FloatRateUnderlyingCode)) { dic["利率类型"] = "固定利率"; dic["IsFixed"] = "☑"; //☑ dic["固定利率"] = interestMargin.category_tag == "增强收益" ? "0.0000" : ((double)interestMargin.InterestRateDefault * 100).ToString("N4"); dic["利差"] = ""; } else { dic["利率类型"] = "浮动利率"; dic["IsFloat"] = "☑"; dic["固定利率"] = ""; dic["利差"] = ((double)interestMargin.InterestRateDefault * 10000).ToString("N0"); dic["重置频率"] = (interestMargin.interest_rest_days ?? 0) + "天"; } if (interestMargin.SwapIntervalList.Count()>1) { var firstInterval = interestMargin.SwapIntervalList.OrderBy(o=>o.Date).First(); var lastInterval = interestMargin.SwapIntervalList.OrderBy(o => o.Date).Last(); dic["期初观察日"] = firstInterval.Date.ToString("【yyyy】年【M】月【d】日") ?? ""; dic["期末观察日"] = lastInterval.Date.ToString("【yyyy】年【M】月【d】日") ?? ""; } } else { dic["利率类型"] = "固定利率"; dic["固定利率"] = "0.0000"; dic["利差"] = ""; } // 基本费率 = PosiTradingFeePending / 名义本金 * 100 // var tradingFee = (double)swapPosition.PosiTradingFeePending; // var basicFeeRate = notional == 0 ? 0 : tradingFee / notional * 100; // dic["基本费率"] = basicFeeRate.ToString("0.####"); // @张名锐: 直接取值PosiTradingFeeUnit(基础费率) dic["基本费率"] = swapPosition.PosiTradingFeeUnit.ToString("0.####"); } else { dic["参考标的期初全价"] = "0.000"; dic["参考标的期初净价"] = "0.0000"; dic["初始保障金率"] = "0.0000"; dic["维持保障金率"] = "0.0000"; dic["期初预付比例"] = "0"; dic["期初预付金额"] = "0.00"; dic["期初预付金利率"] = "0.0000"; dic["利率类型"] = "固定利率"; dic["固定利率"] = "0.0000"; dic["利差"] = ""; dic["基本费率"] = "0"; dic["期初现金交换比例"] = "0.0000"; dic["期初现金交换金额"] = "0.00"; } // 参考标的券面总额(名义本金) dic["参考标的券面总额"] = swapPosition != null ? ((double)swapPosition.PosiQuantity).ToString("N2") : "0"; // 参考标的证券全称和参考标的名义份额(复用上方已声明的bond) dic["参考标的证券全称"] = underlying != null // ? (JsonHelper.Deserialize(underlying.ExJson)?.UnderlyingFullName ?? underlying.UnderlyingName) ? (underlying.UnderlyingName ?? "") : ""; dic["参考标的名义份额"] = swapPosition != null ? ((double)swapPosition.PosiQuantity).ToString("0.##") : "0"; dic["参考标的基金管理人"] = underlying?.InvestAdvisorName ?? ""; var contractTypeId = (Context.GetContractTypes().FirstOrDefault(O => O.ContactType == "交易确认书接收")?.id) ?? 0; // 乙方联系人信息 var clientDuties = Context.GetClientDuties().Where(O => O.ContactTypeIdsInt.Contains(contractTypeId)).ToList(); dic["联系人"] = string.Join(",", clientDuties.Select(O => O.ContactName)).Trim(','); dic["电子邮件"] = string.Join(";", clientDuties.Select(O => O.Email)).Trim(';'); dic["电话"] = string.Join(";", clientDuties.Select(O => O.PhoneNumber)).Trim(';'); // 表格数据示例 var table1 = new JArray(); var row = new JObject { ["序号"] = 1, ["标的代码"] = underlying?.UnderlyingCode ?? "", ["标的名称"] = underlying?.UnderlyingName ?? "", ["名义本金"] = trade.OriginalStockEqvNotional?.ToString("N2") ?? "0.00", ["成交日期"] = trade.TradeDate?.ToString("【yyyy】年【M】月【d】日"), ["到期日期"] = trade.ExerciseDate?.ToString("【yyyy】年【M】月【d】日") }; table1.Add(row); dic["table1"] = table1; } /// /// 设置期权数据 /// private void SetOptionData(Dictionary dic) { var trade = Context.Trade; var client = Context.GetClient(); var underlying = Context.GetTradeUnderlying(); dic["期权类型"] = trade.OptionType; dic["买卖方向"] = trade.BuySell; dic["行权价格"] = trade.Strike?.ToString("N4") ?? "0.0000"; dic["名义本金"] = trade.StockEqvNotional.ToString("N2"); dic["期权费"] = trade.TradePrice?.ToString("N2") ?? "0.00"; dic["期初价格"] = trade.SpotPrice?.ToString("N4") ?? "0.0000"; if (underlying != null) { dic["标的代码"] = underlying.UnderlyingCode; dic["标的名称"] = underlying.UnderlyingName; dic["报价单位"] = underlying.QuoteUnitString ?? "元"; } // 美式/欧式 dic["行权方式"] = trade.ExerciseMode == "American" ? "美式" : "欧式"; } /// /// 设置基础数据 /// private void SetBaseData(Dictionary dic) { var trade = Context.Trade; var client = Context.GetClient(); dic["交易类型"] = trade.TradeType; dic["客户编号"] = client.Number; dic["名义本金"] = trade.OriginalStockEqvNotional?.ToString("N2") ?? "0.00"; } } }