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";
}
}
}