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