using Newtonsoft.Json.Linq; using YLErp.Core.Helpers; using YLErp.DBModels; using YLErp.DBModels.Consts; using YLErp.DBModels.Enums; using YLErp.Modules.UnderlyingModule; using YLErp.Plugins.TradeDocGenerator; using YLErp.Plugins.TradeDocGenerator.Abstracts; 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; } /// /// 获取收益互换模板路径 /// 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.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}"; dic["主协议编号"] = client.MainProtocolCode ?? ""; dic["名义本金"] = trade.OriginalStockEqvNotional?.ToString("N2") ?? "0.00"; // 银行账户信息 dic["户名"] = bank?.ClientName ?? ""; dic["银行账号"] = bank?.Card ?? ""; dic["支付系统号"] = ""; 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["票面利率"] = (bond.CouponRate ?? 0).ToString("N4"); 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("N4"); dic["参考标的期初净价"] = ((double)(swapPosition.PosiNetNoFeePrice ?? 0m)).ToString("N4"); dic["参考标的期初全价%"] = ((double)swapPosition.PosiGrossPrice * 100).ToString("N4"); dic["参考标的期初净价%"] = ((double)(swapPosition.PosiNetNoFeePrice ?? 0m) * 100).ToString("N4"); // 固定收益率(年化)- 债券期初到期收益率 //dic["固定收益率(年化)"] = swapPosition.InitYtm.HasValue // ? ((double)swapPosition.InitYtm.Value * 100).ToString("N4") // : "0.0000"; dic["固定收益率(年化)"] = "0.0000"; //需求说直接都是0 // 获取客户适用的保证金率 var clientMarginRate = UnderlyingHelper.GetApplicableMarginRate( client.id, swapPosition.UnderlyingCode, trade.TradeDate ?? DateTime.Now); if (clientMarginRate != null) { dic["初始保障金率"] = ((double)clientMarginRate.init_rate * 100).ToString("N4"); dic["维持保障金率"] = ((double)clientMarginRate.maintain_rate * 100).ToString("N4"); // 期初预付比例和金额 dic["期初预付比例"] = ((double)clientMarginRate.init_rate * 100).ToString("N4"); dic["期初预付金额"] = ((trade.OriginalStockEqvNotional ?? 0) * (double)clientMarginRate.init_rate).ToString("N2"); } else { dic["初始保障金率"] = "0.0000"; dic["维持保障金率"] = "0.0000"; dic["期初预付比例"] = "0.0000"; dic["期初预付金额"] = "0.00"; } bool posiLong = IsCustomerLong(swapPosition); // 计算平仓线、预警线、档位值(利率债TRS,固定4档) var maintainRatePercent = (double)(clientMarginRate?.maintain_rate ?? 0) * 100; // 维持保证金率 A(%) if (maintainRatePercent > 0) { if (posiLong) { // 客户看多:档位n = 100 - n * A dic["平仓线"] = (100 - 4 * maintainRatePercent).ToString("0.##"); dic["预警线"] = (100 - 3 * maintainRatePercent).ToString("0.##"); dic["档位1"] = (100 - 1 * maintainRatePercent).ToString("0.##"); dic["档位2"] = (100 - 2 * maintainRatePercent).ToString("0.##"); dic["档位3"] = (100 - 3 * maintainRatePercent).ToString("0.##"); dic["档位4"] = (100 - 4 * maintainRatePercent).ToString("0.##"); } else { // 客户看空:档位n = 100 + n * A dic["平仓线"] = (100 + 4 * maintainRatePercent).ToString("0.##"); dic["预警线"] = (100 + 3 * maintainRatePercent).ToString("0.##"); dic["档位1"] = (100 + 1 * maintainRatePercent).ToString("0.##"); dic["档位2"] = (100 + 2 * maintainRatePercent).ToString("0.##"); dic["档位3"] = (100 + 3 * maintainRatePercent).ToString("0.##"); dic["档位4"] = (100 + 4 * maintainRatePercent).ToString("0.##"); } } else { dic["平仓线"] = ""; dic["预警线"] = ""; dic["档位1"] = ""; dic["档位2"] = ""; dic["档位3"] = ""; dic["档位4"] = ""; } // 期初预付金利率(InterestMode == 初始预付金) var initialMarginPosition = swapPositions .Where(x => x.InterestMode == (int)InterestModeEnum.初始预付金) .FirstOrDefault(); dic["期初预付金利率"] = initialMarginPosition != null ? ((double)initialMarginPosition.InterestRateDefault * 100).ToString("N4") : "0.0000"; // 利率类型判断(固定/浮动) var 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"] = "□"; if (interestMargin != null) { if (string.IsNullOrEmpty(interestMargin.FloatRateUnderlyingCode)) { dic["利率类型"] = "固定利率"; dic["IsFixed"] = "☑"; //☑ dic["固定利率"] = ((double)interestMargin.InterestRateDefault * 100).ToString("N4"); dic["利差"] = ""; } else { dic["利率类型"] = "浮动利率"; dic["IsFloat"] = "☑"; dic["固定利率"] = ""; dic["利差"] = ((double)interestMargin.InterestRateDefault * 10000).ToString("N2"); dic["重置频率"] = (interestMargin.interest_rest_days ?? 0) + "天"; } } else { dic["利率类型"] = "固定利率"; dic["固定利率"] = "0.0000"; dic["利差"] = ""; } // 基本费率 = PosiTradingFeePending / 名义本金 * 100 var notional = trade.OriginalStockEqvNotional ?? 0; var tradingFee = (double)swapPosition.PosiTradingFeePending; var basicFeeRate = notional == 0 ? 0 : tradingFee / notional * 100; dic["基本费率"] = basicFeeRate.ToString("N4"); // 期初现金交换比例和金额(使用初始预付金数据) dic["期初现金交换比例"] = initialMarginPosition != null ? ((double)initialMarginPosition.InterestRateDefault * 100).ToString("N4") : "0.0000"; dic["期初现金交换金额"] = initialMarginPosition != null ? ((double)initialMarginPosition.InterestPrincipalFix).ToString("N2") : "0.00"; } else { dic["参考标的期初全价"] = "0.0000"; dic["参考标的期初净价"] = "0.0000"; dic["初始保障金率"] = "0.0000"; dic["维持保障金率"] = "0.0000"; dic["期初预付比例"] = "0.0000"; dic["期初预付金额"] = "0.00"; dic["期初预付金利率"] = "0.0000"; dic["利率类型"] = "固定利率"; dic["固定利率"] = "0.0000"; dic["利差"] = ""; dic["基本费率"] = "0.0000"; dic["期初现金交换比例"] = "0.0000"; dic["期初现金交换金额"] = "0.00"; } // 参考标的券面总额(名义本金) dic["参考标的券面总额"] = trade.OriginalStockEqvNotional?.ToString("N2") ?? "0.00"; // 参考标的证券全称和参考标的名义份额(复用上方已声明的bond) dic["参考标的证券全称"] = underlying != null ? (JsonHelper.Deserialize(underlying.ExJson)?.UnderlyingFullName ?? underlying.UnderlyingName) : ""; dic["参考标的名义份额"] = swapPosition != null ? ((double)swapPosition.PosiQuantity).ToString("N2") : "0.00"; dic["参考标的基金管理人"] = ""; // 乙方联系人信息 var clientDuties = Context.GetClientDuties(); var contact = clientDuties.FirstOrDefault(); dic["联系人"] = contact?.ContactName ?? ""; dic["电子邮件"] = contact?.Email ?? ""; dic["电话"] = contact?.PhoneNumber ?? ""; // 表格数据示例 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"; } } }