Merge branch 'glms/feature/valuation_0709_zmr' into glms/feature/1.4.2
This commit is contained in:
@@ -52,19 +52,21 @@ namespace YLErp.DBModels
|
||||
[DataChange]
|
||||
public string StructureType { get; set; }
|
||||
/// <summary>
|
||||
/// 合约名义本金
|
||||
/// 合约名义本金。取交易原始等价名义本金,表示合约约定规模;
|
||||
/// 不等于多头与空头日终腿的代数和。
|
||||
/// </summary>
|
||||
[DisplayName("合约名义本金")]
|
||||
[DataChange]
|
||||
public decimal NotionalValue { get; set; }
|
||||
/// <summary>
|
||||
/// 合约多头名义本金
|
||||
/// 合约多头名义本金。框架合约展示口径中多头始终为正数。
|
||||
/// </summary>
|
||||
[DisplayName("合约多头名义本金")]
|
||||
[DataChange]
|
||||
public decimal NotionalValueLong { get; set; }
|
||||
/// <summary>
|
||||
/// 合约空头名义本金
|
||||
/// 合约空头名义本金。框架合约展示口径中空头始终为负数,
|
||||
/// 以便与多头直接相加得到净方向。
|
||||
/// </summary>
|
||||
[DisplayName("合约空头名义本金")]
|
||||
[DataChange]
|
||||
@@ -174,5 +176,46 @@ namespace YLErp.DBModels
|
||||
public int ClientId { get; set; }
|
||||
|
||||
public string SwapTradeTypeStr { get; set; }
|
||||
|
||||
public string UnderlyingType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 合约期内已实现加待实现的付息/分红金额。
|
||||
/// 该字段用于框架合约风险展示,不按每日估值报告的“期间付息/期间分红”列拆分。
|
||||
/// </summary>
|
||||
public decimal PeriodAmount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 合约浮动端待实现收益,仅取浮动腿盯市收益,
|
||||
/// 不包含期间付息/分红,避免与 <see cref="PeriodAmount"/> 重复。
|
||||
/// </summary>
|
||||
public decimal FloatingUnrealizedPnl { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 付息/分红支付方式:到期轧差时计入到期轧差估值,派息日支付时在期间支付口径展示。
|
||||
/// </summary>
|
||||
public string InterestPaymentMethod { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 合约估值(到期轧差口径)= 浮动端待实现收益 + 利率端待实现收益 + 期间付息/分红。
|
||||
/// 仅当支付方式为到期轧差时赋值。
|
||||
/// </summary>
|
||||
public decimal? MaturityNettingValuation { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 合约估值(派息日支付口径)= 浮动端待实现收益 + 利率端待实现收益。
|
||||
/// 派息/分红在支付日独立结算,因此不计入该估值。
|
||||
/// </summary>
|
||||
public decimal? PeriodPaymentValuation { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 我方收取的保证金利息累计额,按保证金腿方向归集并取绝对值展示。
|
||||
/// </summary>
|
||||
public decimal MarginInterestGain { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 我方支付的保证金利息累计额,按保证金腿方向归集并取绝对值展示。
|
||||
/// </summary>
|
||||
public decimal MarginInterestLoss { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,8 +20,7 @@ namespace YLErp.DBModels
|
||||
/// </summary>
|
||||
public string StructureType { get; set; }
|
||||
/// <summary>
|
||||
/// <summary>
|
||||
/// 交易对手方名称
|
||||
/// 交易对手方名称。每日估值报告页面当前不展示该列,但发送报告与其他调用方仍可使用。
|
||||
/// </summary>
|
||||
public string ClientName { get; set; }
|
||||
/// <summary>
|
||||
@@ -33,44 +32,65 @@ namespace YLErp.DBModels
|
||||
/// </summary>
|
||||
public string TradeNumber { get; set; }
|
||||
/// <summary>
|
||||
/// 期间付息
|
||||
/// 期间付息。仅现券标的赋值;ETF、指数及其他标的返回 <c>null</c>,由前端和 Excel 显示为空白。
|
||||
/// </summary>
|
||||
public decimal PeriodAmount { get; set; }
|
||||
public decimal? PeriodAmount { get; set; }
|
||||
/// <summary>
|
||||
/// 到期结算日,直接取日终浮动腿的到期日期,不叠加结算规则或节假日顺延。
|
||||
/// </summary>
|
||||
public DateTime? MaturitySettlementDate { get; set; }
|
||||
/// <summary>
|
||||
/// 期间分红。仅 ETF 标的赋值;现券、指数及其他标的返回 <c>null</c>,避免同一金额在不适用列展示。
|
||||
/// </summary>
|
||||
public decimal? DividendAmount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 期初标的成交收益率。仅现券标的直接取交易录入的 <c>trade.InitYtm</c>;其他标的返回 <c>null</c>。
|
||||
/// </summary>
|
||||
public decimal? InitYtm { get; set; }
|
||||
/// <summary>
|
||||
/// 期限
|
||||
/// 实际期限,按估值日与起始日的自然日差加一计算,包含起始日。
|
||||
/// </summary>
|
||||
public int DayCount { get; set; }
|
||||
/// <summary>
|
||||
/// 期初预付金-不包含追加预付金 取轧差
|
||||
/// 期初预付金本金,仅汇总初始预付金交易腿;收取为正、支付为负。
|
||||
/// </summary>
|
||||
public decimal OpenMarginAmount { get; set; }
|
||||
/// <summary>
|
||||
/// 期初预付金利率-不包含追加预付金 取轧差
|
||||
/// 预付金利率,初始和追加预付金腿按本金规模加权平均
|
||||
/// </summary>
|
||||
public decimal OpenMarginRate { get; set; }
|
||||
/// <summary>
|
||||
/// 预付金利息 取轧差
|
||||
/// 预付金利息,初始和追加预付金腿按本金规模加权平均
|
||||
/// </summary>
|
||||
public decimal MarginInterestAmount { get; set; }
|
||||
/// <summary>
|
||||
/// 浮动利率(绝对)利率端待实现收益/(标的名义金额/期初标的交割价格全价)
|
||||
/// 追加预付金本金,仅汇总估值日前已生效的追加预付金交易腿;收取为正、支付为负。
|
||||
/// </summary>
|
||||
public decimal AdditionalMarginAmount { get; set; }
|
||||
/// <summary>
|
||||
/// 浮动利率(绝对)= 利率收益金额 / 标的名义金额。
|
||||
/// 该字段是展示型比例,不参与净额结算金额计算。
|
||||
/// </summary>
|
||||
public decimal FloatRateAbs { get; set; }
|
||||
/// <summary>
|
||||
/// 利差
|
||||
/// 利差,汇总非预付金利息腿的约定利率。
|
||||
/// </summary>
|
||||
public decimal InterestRate { get; set; }
|
||||
/// <summary>
|
||||
/// 利率收益金额 利率端待实现收益
|
||||
/// 利率收益金额,汇总非预付金利息腿的 <c>InterestIncomeSum</c>,并转换为我方视角。
|
||||
/// </summary>
|
||||
public decimal InterestAmount { get; set; }
|
||||
/// <summary>
|
||||
/// 净额结算金额 互换持仓价值+待返还的预付金本金
|
||||
/// 净额结算金额 = 利率收益金额 + 浮动收益金额 + 开平仓交易费用 + 预付金利息
|
||||
/// + 到期轧差方式下应计入的期间付息/分红;不包含两类预付金本金。
|
||||
/// </summary>
|
||||
public decimal NetSettmentAmount { get; set; }
|
||||
/// <summary>
|
||||
/// TRS估值 = 净额结算金额 + 期初预付金 + 追加预付金。
|
||||
/// </summary>
|
||||
public decimal TrsValue { get; set; }
|
||||
/// <summary>
|
||||
/// 交易费用
|
||||
/// </summary>
|
||||
public decimal TradingFee { get; set; }
|
||||
|
||||
@@ -15,12 +15,27 @@ namespace YLErp.Model
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// 估值日。每日估值报告的互换估值查询当前按该日期精确筛选,
|
||||
/// 交易日期同时不得晚于该日期。
|
||||
/// </summary>
|
||||
public DateTime? ValueDate { get; set; }
|
||||
/// <summary>
|
||||
/// 请求携带的估值日下界。互换持仓明细、交易流水等调用方可使用该字段;
|
||||
/// 当前 <c>GetSearchEodPositionList</c> 未启用该下界,仍是单日估值查询。
|
||||
/// </summary>
|
||||
public DateTime? ValueDateFrom { get; set; }
|
||||
/// <summary>
|
||||
/// 对手方筛选条件。为空时不按对手方收窄结果。
|
||||
/// </summary>
|
||||
public int? ClientId { get; set; }
|
||||
/// <summary>
|
||||
/// 簿记账户筛选条件,对应 <c>trade.AssetId</c>;为空时包含该对手方下全部簿记账户。
|
||||
/// </summary>
|
||||
public int? BookId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 调用方传入的结构类型。互换估值查询当前固定同时覆盖普通债券类收益互换和普通收益互换。
|
||||
/// </summary>
|
||||
public string StructureType { get; set; }
|
||||
}
|
||||
/// <summary>
|
||||
|
||||
@@ -156,6 +156,7 @@ namespace YLErp.Model
|
||||
public const string 交易预付金配置 = "tradeMarginTemplateList";
|
||||
|
||||
public const string 互换估值 = "SettmentEodSwapPositionList";
|
||||
public const string 互换估值V1 = "SettmentEodSwapPositionListV1";
|
||||
|
||||
public const string 衡泰对账 = "compare_heitai_data";
|
||||
}
|
||||
|
||||
@@ -28,6 +28,13 @@
|
||||
/// </summary>
|
||||
public string MarginDetail { get; set; }
|
||||
public int ClientId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 每日估值报告页面选择的簿记账户。为空时按客户维度生成全量报告;
|
||||
/// 有值时仅筛选互换估值页的交易所属账户。
|
||||
/// </summary>
|
||||
public int? BookId { get; set; }
|
||||
|
||||
public DateTime From { get; set; }
|
||||
public DateTime To { get; set; }
|
||||
public double PayableMargin { get; set; }
|
||||
|
||||
+74
-5
@@ -4,6 +4,7 @@ using OfficeOpenXml.Style;
|
||||
using Org.BouncyCastle.Ocsp;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
@@ -14,6 +15,7 @@ using YLErp.BLL;
|
||||
using YLErp.BLL.EodSettlement;
|
||||
using YLErp.Configuration;
|
||||
using YLErp.Core.Helpers;
|
||||
using YLErp.DBModels;
|
||||
using YLErp.Enums;
|
||||
using YLErp.Helpers;
|
||||
using YLErp.Model;
|
||||
@@ -60,7 +62,16 @@ namespace YLErp.Modules.ReportModule.SettlementReportModule
|
||||
#endregion
|
||||
if (emailData.SendContent.Contains("互换估值"))
|
||||
{
|
||||
var eodReq = new ClientSwapPositionRequest { ClientId = emailData.ClientId, ValueDate = emailData.To, ValueDateFrom = emailData.From,page=1, rows=10000,StructureType= "普通债券类收益互换" };
|
||||
var eodReq = new ClientSwapPositionRequest
|
||||
{
|
||||
ClientId = emailData.ClientId,
|
||||
BookId = emailData.BookId,
|
||||
ValueDate = emailData.To,
|
||||
ValueDateFrom = emailData.From,
|
||||
page = 1,
|
||||
rows = 10000,
|
||||
StructureType = "普通债券类收益互换"
|
||||
};
|
||||
report.EodSwapPositions = swapEodPositionService.SearchEodPositionList(eodReq).rows.ToList();
|
||||
}
|
||||
if (emailData.SendContent.Contains("互换持仓明细"))
|
||||
@@ -208,7 +219,7 @@ namespace YLErp.Modules.ReportModule.SettlementReportModule
|
||||
Directory.CreateDirectory(targetPath);
|
||||
}
|
||||
var clientName = report.client.Name;
|
||||
var fileName = report.ReportFrom == DateTime.MinValue ? $"证券_估值表_{report.ReportEnd:yyyyMMdd}_{clientName}" : $"证券_估值表_{report.ReportFrom:yyyyMMdd}_{report.ReportEnd:yyyyMMdd}_{clientName}";
|
||||
var fileName = $"{clientName}_每日估值报告_{report.ReportEnd:yyyyMMdd}";
|
||||
var targetFileName = Path.Combine(targetPath, $"{fileName}.xlsx");
|
||||
|
||||
var excelDeclareModel = new ExcelDeclareModel()
|
||||
@@ -238,14 +249,22 @@ namespace YLErp.Modules.ReportModule.SettlementReportModule
|
||||
{
|
||||
modelDict.Add("互换估值", new
|
||||
{
|
||||
// 明细列表供模板渲染;以下 *Sum 字段用于“互换估值”页签合计行。
|
||||
EodSwapPositions = report.EodSwapPositions,
|
||||
// 持仓规模、期间收益和利息/分红类金额合计。
|
||||
PosiNotionalValueSum= report.EodSwapPositions.Sum(x => x.position.PosiNotionalValue),
|
||||
PosiQuantitySum= report.EodSwapPositions.Sum(x => x.position.PosiQuantity),
|
||||
PeriodAmountSum= report.EodSwapPositions.Sum(x => x.PeriodAmount),
|
||||
PeriodAmountSum= report.EodSwapPositions.Sum(x => x.PeriodAmount) ?? 0m,
|
||||
DividendAmountSum = report.EodSwapPositions.Sum(x => x.DividendAmount) ?? 0m,
|
||||
InterestAmountSum= report.EodSwapPositions.Sum(x => x.InterestAmount),
|
||||
PosiFeePendingSum = report.EodSwapPositions.Sum(x => x.position.PosiFeePending),
|
||||
PosiProfitSum= report.EodSwapPositions.Sum(x => x.position.PosiProfitSum),
|
||||
// 保证金相关收益和保证金占用金额合计。
|
||||
MarginInterestAmountSum = report.EodSwapPositions.Sum(x => x.MarginInterestAmount),
|
||||
OpenMarginAmountSum = report.EodSwapPositions.Sum(x => x.OpenMarginAmount),
|
||||
AdditionalMarginAmountSum = report.EodSwapPositions.Sum(x => x.AdditionalMarginAmount),
|
||||
// 净结算金额为日终估值口径;TRS价值在净结算金额基础上叠加期初/追加保证金。
|
||||
NetSettmentAmountSum= report.EodSwapPositions.Sum(x => x.NetSettmentAmount),
|
||||
TrsValueSum = report.EodSwapPositions.Sum(x => x.TrsValue),
|
||||
});
|
||||
}
|
||||
if (report.SwapPositions != null)
|
||||
@@ -287,7 +306,10 @@ namespace YLErp.Modules.ReportModule.SettlementReportModule
|
||||
string sourceFileName = Path.Combine(sourcePath, $"结算报告模板.xlsx");
|
||||
ExcelPackage.LicenseContext = LicenseContext.NonCommercial;
|
||||
var pdffile = ExcelTemplate.GeneratePDFFromExeclTemplate(sourcePath, sourceFileName, modelDict, targetPath, targetFileName
|
||||
, shouldDeleteSheet: true, needToPdf: false);
|
||||
, shouldDeleteSheet: true, needToPdf: false, callback: sheets =>
|
||||
{
|
||||
FormatSwapValuationDisplayCells(sheets, report.EodSwapPositions);
|
||||
});
|
||||
if (needToPdf)
|
||||
{
|
||||
var targetPdfFileName = FileHelper.ReplaceExtension(targetFileName, ".pdf");
|
||||
@@ -297,6 +319,53 @@ namespace YLErp.Modules.ReportModule.SettlementReportModule
|
||||
return Path.Combine(targetPath, targetFileName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 部分 Office 版本会将可选小数格式(例如 <c>#,##0.##</c>)错误显示为 <c>20,000.</c>。
|
||||
/// 互换估值中的这些列是展示字段,不参与 Excel 公式计算,因此在模板替换完成后写为已格式化文本,
|
||||
/// 既保留去尾零口径,也避免留下孤立的小数点。
|
||||
/// </summary>
|
||||
private static void FormatSwapValuationDisplayCells(IEnumerable<ExcelWorksheet> sheets, IEnumerable<EodSwapPositionResponse> positions)
|
||||
{
|
||||
var worksheet = sheets.FirstOrDefault(x => x.Name == "互换估值");
|
||||
var positionList = positions?.ToList() ?? new List<EodSwapPositionResponse>();
|
||||
if (worksheet == null || !positionList.Any())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const int dataStartRow = 2;
|
||||
for (var index = 0; index < positionList.Count; index++)
|
||||
{
|
||||
var row = dataStartRow + index;
|
||||
var item = positionList[index];
|
||||
SetTrimmedExcelText(worksheet.Cells[row, 8], item.position.PosiNotionalValue, 2);
|
||||
SetTrimmedExcelText(worksheet.Cells[row, 9], item.position.PosiQuantity, 9);
|
||||
SetTrimmedExcelText(worksheet.Cells[row, 10], item.PeriodAmount, 2);
|
||||
SetTrimmedExcelText(worksheet.Cells[row, 11], item.DividendAmount, 2);
|
||||
SetTrimmedExcelText(worksheet.Cells[row, 13], item.InitYtm, 4, percent: true);
|
||||
}
|
||||
|
||||
var totalRow = dataStartRow + positionList.Count;
|
||||
SetTrimmedExcelText(worksheet.Cells[totalRow, 8], positionList.Sum(x => x.position.PosiNotionalValue), 2);
|
||||
SetTrimmedExcelText(worksheet.Cells[totalRow, 10], positionList.Sum(x => x.PeriodAmount) ?? 0m, 2);
|
||||
SetTrimmedExcelText(worksheet.Cells[totalRow, 11], positionList.Sum(x => x.DividendAmount) ?? 0m, 2);
|
||||
}
|
||||
|
||||
private static void SetTrimmedExcelText(ExcelRange cell, decimal? value, int decimalPlaces, bool percent = false)
|
||||
{
|
||||
if (!value.HasValue)
|
||||
{
|
||||
cell.Value = null;
|
||||
return;
|
||||
}
|
||||
|
||||
var displayValue = percent ? value.Value * 100m : value.Value;
|
||||
var format = "#,##0." + new string('#', decimalPlaces);
|
||||
var text = displayValue.ToString(format, CultureInfo.InvariantCulture).TrimEnd('.');
|
||||
cell.Value = percent ? text + "%" : text;
|
||||
cell.Style.Numberformat.Format = "@";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 财务状况
|
||||
/// </summary>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using BaseOUDAL;
|
||||
using Newtonsoft.Json;
|
||||
using NPOI.POIFS.Properties;
|
||||
using System;
|
||||
using System.Linq.Expressions;
|
||||
using YLErp.DBModels;
|
||||
using YLErp.DBModels.Consts;
|
||||
@@ -155,6 +156,36 @@ namespace YLErp.Modules.SwapModule
|
||||
return UnderlyingCodePrice(code, settleDate, out vobp);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取用于互换浮动腿盯市的标的价格。
|
||||
///
|
||||
/// 普通债券类收益互换的新录入页面将全价按小数保存,例如页面录入 20% 后
|
||||
/// PosiGrossPrice 为 0.2;而历史交易中仍可能存在直接保存为 20 的展示态价格。
|
||||
/// 中债估值正常经 EodPriceQueryService 转换后应为小数价格,但手工维护的历史
|
||||
/// 行情可能仍以展示态进入该服务,例如 2000 经一次转换后得到 20。若将 20
|
||||
/// 与 0.2 直接相减,会把 20% 的价格差误算成 1,980,000 的浮动损益。
|
||||
///
|
||||
/// 因此仅当交易期初价已经是小数口径、且当前债券价明显仍处于展示态时,再做
|
||||
/// 一次展示态到存储态转换。期初价本身是历史展示态口径的存量交易保持原价格,
|
||||
/// 避免修改日终估值链路后改变其既有损益。
|
||||
/// </summary>
|
||||
private decimal GetSwapValuationPrice(string code, decimal posiGrossPrice, DateTime settleDate, out decimal vobp)
|
||||
{
|
||||
var price = GetUnderlyingPrice(code, settleDate, out vobp);
|
||||
var underlying = GetUnderlyingData(code);
|
||||
var usesStoragePrice = Math.Abs(posiGrossPrice) < 2m;
|
||||
var usesDisplayPrice = Math.Abs(price) >= 10m;
|
||||
|
||||
if (underlying?.IsBond() == true && usesStoragePrice && usesDisplayPrice)
|
||||
{
|
||||
var normalizedPrice = BondPriceConverter.ToStorage(price);
|
||||
Log.Error($"互换债券日终价格按展示态返回,已转换为存储态: UnderlyingCode={code}, ValueDate={settleDate:yyyy-MM-dd}, PosiGrossPrice={posiGrossPrice}, SourcePrice={price}, NormalizedPrice={normalizedPrice}");
|
||||
return normalizedPrice;
|
||||
}
|
||||
|
||||
return price;
|
||||
}
|
||||
|
||||
/// <summary>获取标的缓存数据(生产: DataCacheProvider;测试: 返回内存对象)</summary>
|
||||
protected virtual underlying_manager GetUnderlyingData(string underlyingCode)
|
||||
{
|
||||
@@ -1158,7 +1189,8 @@ namespace YLErp.Modules.SwapModule
|
||||
{
|
||||
ratio = -ratio;
|
||||
}
|
||||
var lastInterestIncomeSum = eodPayPosition.InterestIncomeSum;
|
||||
// 首次日终结算可能包含当日收盘,因此尚无先前的日终利息持仓。
|
||||
var lastInterestIncomeSum = eodPayPosition?.InterestIncomeSum ?? 0m;
|
||||
eodPayPosition = new eod_swap_position();
|
||||
eodPayPosition.ClientId = td.ClientId;
|
||||
eodPayPosition.SwapTradeId = td.id;
|
||||
@@ -1524,7 +1556,7 @@ namespace YLErp.Modules.SwapModule
|
||||
int shortRatio = eod.PositionType == (int)PositionTypeFlag.Long ? 1 : -1;
|
||||
int directionRatio = eod.PosiDirection == (int)SwapDirectionEnum.收取 ? 1 : -1;
|
||||
curretEod.PosiStatus = curretEod.PosiQuantity == 0 ? 1 : 0;
|
||||
var price = GetUnderlyingPrice(eod.UnderlyingCode, dealDate, out decimal vobp);
|
||||
var price = GetSwapValuationPrice(eod.UnderlyingCode, eod.PosiGrossPrice, dealDate, out decimal vobp);
|
||||
curretEod.dv01 = Dv01Helper.CalcDv01(eod.UnderlyingCode, curretEod.PosiQuantity, eod.PosiDirection, eod.PositionType, vobp);
|
||||
decimal tax = um.ValueAddedTax ?? 0;
|
||||
if (valueDate > td.StartDate.Value && curretEod.PosiQuantity > 0)
|
||||
@@ -1604,7 +1636,7 @@ namespace YLErp.Modules.SwapModule
|
||||
var dealDate = curretEod.ValueDate;
|
||||
int shortRatio = eod.PositionType == (int)PositionTypeFlag.Long ? 1 : -1;
|
||||
int directionRatio = eod.PosiDirection == (int)SwapDirectionEnum.收取 ? 1 : -1;
|
||||
var price = GetUnderlyingPrice(eod.UnderlyingCode, dealDate, out decimal vobp);
|
||||
var price = GetSwapValuationPrice(eod.UnderlyingCode, eod.PosiGrossPrice, dealDate, out decimal vobp);
|
||||
var todayConsumedDividend = CalcConsumedDividend(curretEod, unwindEvents);
|
||||
var originNotional = (decimal)td.OriginalStockEqvNotional / swapPosition.PosiNetPrice;
|
||||
decimal totalPayment = CalcBondPayment(curretEod.UnderlyingCode, td.StartDate.Value, valueDate, (decimal)originNotional, shortRatio, directionRatio);
|
||||
@@ -1791,7 +1823,7 @@ namespace YLErp.Modules.SwapModule
|
||||
curretEod.ContractSize = position.ContractSize;
|
||||
curretEod.CountRatio = position.CountRatio;
|
||||
curretEod.PosiTradingFee = position.PosiTradingFee;
|
||||
curretEod.UnderlyingPrice = GetUnderlyingPrice(position.UnderlyingCode, dealDate, out decimal vobp);
|
||||
curretEod.UnderlyingPrice = GetSwapValuationPrice(position.UnderlyingCode, position.PosiGrossPrice, dealDate, out decimal vobp);
|
||||
SetPriceInfoByFlowEvent(eod, curretEod, unwindEvents, position);
|
||||
curretEod.dv01 = Dv01Helper.CalcDv01(curretEod.UnderlyingCode, curretEod.PosiQuantity, curretEod.PosiDirection, curretEod.PositionType, vobp);
|
||||
//if (settleDate == td.TradeDate)
|
||||
@@ -1898,12 +1930,16 @@ namespace YLErp.Modules.SwapModule
|
||||
eod_Swap = new eod_swap();
|
||||
}
|
||||
var tradeSpan = DbContext.trade_span.FirstOrDefault(x => x.TradeId == td.id && x.ValueDate == settleDate);
|
||||
// eod_swap 是交易级汇总;eod_swap_position 是浮动腿、利息腿和保证金腿的明细。
|
||||
// 以下先按日终明细拆腿,再按框架合约展示口径汇总。
|
||||
var eodSwapPositions = DbContext.eod_swap_position.Where(x => x.SwapTradeId == td.id && x.ValueDate == settleDate && !x.Invalid).ToList();
|
||||
var interestPositions = eodSwapPositions.Where(x => string.IsNullOrEmpty(x.UnderlyingCode)).ToList();//利息腿
|
||||
var positions = eodSwapPositions.Where(x => !string.IsNullOrEmpty(x.UnderlyingCode)).ToList();//持仓腿
|
||||
// 框架合约的方向约定:多头为正、空头为负;总名义本金取交易原始规模,
|
||||
// 不能直接用多空腿相加,否则会把对冲方向误当成合约规模变化。
|
||||
eod_Swap.NotionalValueLong = positions.Where(x => x.PositionType == (int)PositionTypeFlag.Long).Sum(s => s.PosiNotionalValue);
|
||||
eod_Swap.NotionalValueShort = positions.Where(x => x.PositionType == (int)PositionTypeFlag.Short).Sum(s => s.PosiNotionalValue);
|
||||
eod_Swap.NotionalValue = eod_Swap.NotionalValueLong + eod_Swap.NotionalValueShort;
|
||||
eod_Swap.NotionalValueShort = -Math.Abs(positions.Where(x => x.PositionType == (int)PositionTypeFlag.Short).Sum(s => s.PosiNotionalValue));
|
||||
eod_Swap.NotionalValue = Convert.ToDecimal(td.OriginalStockEqvNotional ?? td.StockEqvNotional);
|
||||
eod_Swap.SwapTradeId = td.id;
|
||||
eod_Swap.SwapTradeNo = td.TradeNumber;
|
||||
eod_Swap.ClientId = td.ClientId;
|
||||
@@ -1915,6 +1951,8 @@ namespace YLErp.Modules.SwapModule
|
||||
eod_Swap.FloatingPnL = positions.Sum(s => s.PosiProfitSum);
|
||||
eod_Swap.dv01 = positions.Sum(s => s.dv01 ?? 0);
|
||||
decimal interestPnL = 0;
|
||||
// 利息腿按我方视角归集。保证金腿的利息现金流方向与普通利息腿相反,
|
||||
// 因此保证金腿需要额外反转符号,确保 InterestPnL 表示我方的合约利率端收益。
|
||||
interestPositions.ForEach(x =>
|
||||
{
|
||||
decimal ratio = x.InterestDirection == (int)SwapDirectionEnum.收取 ? 1 : -1;//收取为正,支付为负
|
||||
@@ -1969,12 +2007,13 @@ namespace YLErp.Modules.SwapModule
|
||||
eod_Swap.ValueDate = settleDate;
|
||||
DbContext.eod_swap.Add(eod_Swap);
|
||||
}
|
||||
// 单标的调整与首次归档使用同一套框架合约汇总口径,避免重算后多空和名义本金展示不一致。
|
||||
var eodSwapPositions = DbContext.eod_swap_position.Where(x => x.SwapTradeId == td.id && x.ValueDate == settleDate && !x.Invalid).ToList();
|
||||
var interestPositions = eodSwapPositions.Where(x => string.IsNullOrEmpty(x.UnderlyingCode)).ToList();//利息腿
|
||||
var positions = eodSwapPositions.Where(x => !string.IsNullOrEmpty(x.UnderlyingCode)).ToList();//持仓腿
|
||||
eod_Swap.NotionalValue = Convert.ToDecimal(td.StockEqvNotional);
|
||||
eod_Swap.NotionalValue = Convert.ToDecimal(td.OriginalStockEqvNotional ?? td.StockEqvNotional);
|
||||
eod_Swap.NotionalValueLong = positions.Where(x => x.PositionType == (int)PositionTypeFlag.Long).Sum(s => s.PosiNotionalValue);
|
||||
eod_Swap.NotionalValueShort = positions.Where(x => x.PositionType == (int)PositionTypeFlag.Short).Sum(s => s.PosiNotionalValue);
|
||||
eod_Swap.NotionalValueShort = -Math.Abs(positions.Where(x => x.PositionType == (int)PositionTypeFlag.Short).Sum(s => s.PosiNotionalValue));
|
||||
eod_Swap.MarketValueLong = positions.Where(x => x.PositionType == (int)PositionTypeFlag.Long).Sum(s => s.UnderlyingMarketValue);
|
||||
eod_Swap.MarketValueShort = positions.Where(x => x.PositionType == (int)PositionTypeFlag.Short).Sum(s => s.UnderlyingMarketValue);
|
||||
eod_Swap.FloatingPnL = positions.Sum(s => s.PosiProfitSum);
|
||||
@@ -2162,10 +2201,62 @@ namespace YLErp.Modules.SwapModule
|
||||
}
|
||||
DbContext.SetDebugLog();
|
||||
var retListResult = query.ToSearchList(req);
|
||||
var tradeIds = retListResult.rows.Select(x => x.position.SwapTradeId).Distinct().ToList();
|
||||
var valueDates = retListResult.rows.Select(x => x.position.ValueDate).Distinct().ToList();
|
||||
var tradeNotionals = DbContext.trade
|
||||
.Where(x => tradeIds.Contains(x.id))
|
||||
.Select(x => new { x.id, x.OriginalStockEqvNotional, x.StockEqvNotional })
|
||||
.ToDictionary(x => x.id);
|
||||
var eodPositionDetails = DbContext.eod_swap_position
|
||||
.Where(x => tradeIds.Contains(x.SwapTradeId) && valueDates.Contains(x.ValueDate) && !x.Invalid)
|
||||
.ToList();
|
||||
var tradeExtends = DbContext.trade_extend.Where(x => tradeIds.Contains(x.TradeId)).ToList();
|
||||
var underlyingDataSource = DataCacheProvider.GetUnderlyingDataSource();
|
||||
var varietyDataSource = DataCacheProvider.GetVarietyDataSource();
|
||||
foreach (var item in retListResult.rows)
|
||||
{
|
||||
item.position.NotionalValueShort = -Math.Abs(item.position.NotionalValueShort);
|
||||
if (tradeNotionals.TryGetValue(item.position.SwapTradeId, out var tradeNotional))
|
||||
{
|
||||
item.position.NotionalValue = Convert.ToDecimal(tradeNotional.OriginalStockEqvNotional ?? tradeNotional.StockEqvNotional);
|
||||
}
|
||||
var client = DataCacheProvider.GetClientDataSource().GetData(item.ClientId);
|
||||
item.SwapTradeTypeStr = client?.SwapTradeTypeStr;
|
||||
var details = eodPositionDetails
|
||||
.Where(x => x.SwapTradeId == item.position.SwapTradeId && x.ValueDate == item.position.ValueDate)
|
||||
.ToList();
|
||||
var floatingLegs = details.Where(x => !string.IsNullOrEmpty(x.UnderlyingCode)).ToList();
|
||||
var marginLegs = details.Where(x => marginTypes.Contains(x.InterestMode)).ToList();
|
||||
var tradeExtend = tradeExtends.FirstOrDefault(x => x.TradeId == item.position.SwapTradeId);
|
||||
var dividendPayDate = tradeExtend?.ExtendObj?.DividendPayDate ?? 1;
|
||||
|
||||
item.UnderlyingType = string.Join(",", floatingLegs
|
||||
.Select(x =>
|
||||
{
|
||||
var underlying = underlyingDataSource.GetData(x.UnderlyingCode);
|
||||
return varietyDataSource.GetData(underlying?.UnderlyingTypeId ?? 0)?.AssetType
|
||||
?? underlying?.UnderlyingInstrumentTypeCn
|
||||
?? underlying?.UnderlyingType;
|
||||
})
|
||||
.Where(x => !string.IsNullOrWhiteSpace(x))
|
||||
.Distinct());
|
||||
item.PeriodAmount = floatingLegs.Sum(x => x.RealizedDividend + x.PosiDividendSum);
|
||||
item.FloatingUnrealizedPnl = floatingLegs.Sum(x => x.PosiMtmPnL);
|
||||
item.InterestPaymentMethod = dividendPayDate == 0 ? "到期轧差" : "派息日支付";
|
||||
if (dividendPayDate == 0)
|
||||
{
|
||||
item.MaturityNettingValuation = item.FloatingUnrealizedPnl + item.position.InterestPnL + item.PeriodAmount;
|
||||
}
|
||||
else
|
||||
{
|
||||
item.PeriodPaymentValuation = item.FloatingUnrealizedPnl + item.position.InterestPnL;
|
||||
}
|
||||
item.MarginInterestGain = marginLegs
|
||||
.Where(x => x.InterestDirection == (int)SwapDirectionEnum.收取)
|
||||
.Sum(x => Math.Abs(x.InterestIncomeSum));
|
||||
item.MarginInterestLoss = marginLegs
|
||||
.Where(x => x.InterestDirection == (int)SwapDirectionEnum.支付)
|
||||
.Sum(x => Math.Abs(x.InterestIncomeSum));
|
||||
}
|
||||
|
||||
var dv01 = query.Sum(O => O.position.dv01??0);
|
||||
@@ -2300,6 +2391,7 @@ namespace YLErp.Modules.SwapModule
|
||||
|
||||
private SearchListResult<EodSwapPositionResponse> GetSearchEodPositionList(ClientSwapPositionRequest req)
|
||||
{
|
||||
// 每日估值报告以有数量的浮动腿为主记录;利息腿和保证金腿仅作为同交易、同估值日的辅助数据参与汇总。
|
||||
var predicate = PredicateBuilder.Create<eod_swap_position>(n => !n.Invalid && n.PosiQuantity > 0);
|
||||
var interestPredicate = PredicateBuilder.Create<eod_swap_position>(n => !n.Invalid && n.InterestDirection > 0);
|
||||
var tradePredicate = PredicateBuilder.Create<trade>(n => n.ValidState != "InValid");
|
||||
@@ -2310,10 +2402,15 @@ namespace YLErp.Modules.SwapModule
|
||||
{
|
||||
predicate = predicate.And(x => x.ClientId == req.ClientId);
|
||||
}
|
||||
if (req.ValueDateFrom != null)
|
||||
if (req.BookId > 0)
|
||||
{
|
||||
predicate = predicate.And(x => x.ValueDate >= req.ValueDateFrom);
|
||||
tradePredicate = tradePredicate.And(x => x.AssetId == req.BookId.Value);
|
||||
}
|
||||
// ValueDateFrom 保留在请求模型中,但当前互换估值查询按 ValueDate 单日取数。
|
||||
// if (req.ValueDateFrom != null)
|
||||
// {
|
||||
// predicate = predicate.And(x => x.ValueDate >= req.ValueDateFrom);
|
||||
// }
|
||||
if (req.ValueDate != null)
|
||||
{
|
||||
predicate = predicate.And(x => x.ValueDate == req.ValueDate);
|
||||
@@ -2343,53 +2440,132 @@ namespace YLErp.Modules.SwapModule
|
||||
}
|
||||
var retListResult = query.ToSearchList(req);
|
||||
var tradeIds = retListResult.rows.Select(s => s.position.SwapTradeId).ToList();
|
||||
if (!tradeIds.Any())
|
||||
{
|
||||
return retListResult;
|
||||
}
|
||||
interestPredicate = interestPredicate.And(x => tradeIds.Contains(x.SwapTradeId));
|
||||
var valueDates = retListResult.rows.Select(s => s.position.ValueDate).Distinct().ToList();
|
||||
interestPredicate = interestPredicate.And(x => valueDates.Contains(x.ValueDate));
|
||||
// 主查询分页后再取同交易、同估值日的全部辅助腿,避免利息/保证金归集跨估值日串数据。
|
||||
var eodPositions = DbContext.eod_swap_position.Where(interestPredicate).ToList();
|
||||
var positions = DbContext.swap_position.Where(x => tradeIds.Contains(x.SwapTradeId) && x.InterestMode == (int)InterestModeEnum.初始预付金 && x.IsInitial && !x.Invalid).ToList();
|
||||
var marginPositions = DbContext.swap_position
|
||||
.Where(x => tradeIds.Contains(x.SwapTradeId) && marginTypes.Contains(x.InterestMode) && x.IsInitial && !x.Invalid)
|
||||
.ToList();
|
||||
var tradeExtends = DbContext.trade_extend.Where(x => tradeIds.Contains(x.TradeId)).ToList();
|
||||
Dictionary<string, bool> tradeDic = new Dictionary<string, bool>();
|
||||
foreach (var item in retListResult.rows)
|
||||
{
|
||||
var tradeExtend = tradeExtends.FirstOrDefault(x => x.TradeId == item.position.SwapTradeId);
|
||||
var eventDate = item.position.ValueDate;
|
||||
if (tradeExtend != null)
|
||||
{
|
||||
eventDate = QdpCalendarHelper.GetNonHoliday(eventDate.AddDays(tradeExtend.ExtendObj.SettlementRules));
|
||||
}
|
||||
item.DayCount = Math.Max(0, (eventDate - item.position.PosiStartDate).Days + 1);
|
||||
// 到期结算日按合同到期日展示;实际期限按自然日且包含起始日,二者均不使用结算规则偏移。
|
||||
item.MaturitySettlementDate = item.position.PosiMatuirityDate;
|
||||
item.DayCount = Math.Max(0, (item.position.ValueDate - item.position.PosiStartDate).Days + 1);
|
||||
//item.position.PosiProfitSum += item.position.VTradingFee-item.position.PosiFeePending;
|
||||
SetClientEodPosition(item.position);
|
||||
//item.position.PosiProfitSum += item.TradingFee;
|
||||
var posiProfitSum = item.position.PosiProfitSum;
|
||||
//item.position.PosiProfitSum 不需要加交易费用
|
||||
item.position.PosiProfitSum = item.position.PosiProfitSum - item.position.PosiFeePending - item.position.PosiDividendSum;
|
||||
item.NetSettmentAmount = item.position.PosiProfitSum + item.position.PosiDividendSum + item.position.PosiFeePending;
|
||||
item.PeriodAmount = item.position.PosiDividendSum;
|
||||
var margins = positions.Where(x => x.SwapTradeId == item.position.SwapTradeId);
|
||||
// PosiProfitSum 原值包含交易费用和期间付息/分红。先拆出这两部分,
|
||||
// 使“浮动收益金额”仅反映标的盯市收益,后续净额公式再按支付方式决定是否加回期间金额。
|
||||
var pendingDividend = item.position.PosiDividendSum;
|
||||
item.position.PosiProfitSum = item.position.PosiProfitSum - item.position.PosiFeePending - pendingDividend;
|
||||
// 现券仅展示期间付息和期初成交收益率;ETF(标的主数据类型 Fund)仅展示期间分红。
|
||||
// 其余标的的三列均不适用,返回 null 使页面和 Excel 模板保持空白,而不是展示 0。
|
||||
var isCashBond = ConsGlobal.InstrumentType.IsBond(item.position.UnderlyingInstrumentType);
|
||||
var isEtf = ConsGlobal.InstrumentType.Fund.Equals(
|
||||
item.position.UnderlyingInstrumentType,
|
||||
StringComparison.OrdinalIgnoreCase);
|
||||
if (isCashBond)
|
||||
{
|
||||
item.PeriodAmount = pendingDividend;
|
||||
item.DividendAmount = null;
|
||||
// 期初标的成交收益率是债券现券成交口径,非现券不展示该交易录入值。
|
||||
}
|
||||
else if (isEtf)
|
||||
{
|
||||
item.PeriodAmount = null;
|
||||
item.DividendAmount = pendingDividend;
|
||||
}
|
||||
else
|
||||
{
|
||||
item.PeriodAmount = null;
|
||||
item.DividendAmount = null;
|
||||
}
|
||||
if (!isCashBond)
|
||||
{
|
||||
item.InitYtm = null;
|
||||
}
|
||||
// 预付金本金和利率来自交易腿,并以发生日判断在估值日是否已生效;
|
||||
// 预付金利息则来自当日日终腿,以获得截至估值日的 InterestIncomeSum。
|
||||
var tradeMargins = marginPositions
|
||||
.Where(x => x.SwapTradeId == item.position.SwapTradeId
|
||||
&& (!x.HappenDate.HasValue || x.HappenDate.Value <= item.position.ValueDate))
|
||||
.ToList();
|
||||
var interests = eodPositions.Where(x => x.SwapTradeId == item.position.SwapTradeId && x.ValueDate == item.position.ValueDate);
|
||||
var eodMargins = interests.Where(x => marginTypes.Contains(x.InterestMode));
|
||||
var eodInterests = interests.Where(x => !marginTypes.Contains(x.InterestMode));
|
||||
var eodMargins = interests.Where(x => marginTypes.Contains(x.InterestMode)).ToList();
|
||||
var eodInterests = interests.Where(x => !marginTypes.Contains(x.InterestMode)).ToList();
|
||||
var initialMargins = tradeMargins.Where(x => x.InterestMode == (int)InterestModeEnum.初始预付金).ToList();
|
||||
var additionalMargins = tradeMargins.Where(x => x.InterestMode == (int)InterestModeEnum.追加预付金).ToList();
|
||||
var floatRateInterest = eodInterests.Where(x => !string.IsNullOrEmpty(x.FloatRateUnderlyingCode)).FirstOrDefault();
|
||||
item.position.FloatRateUnderlyingCode = floatRateInterest?.FloatRateUnderlyingCode;
|
||||
item.position.FloatRate = floatRateInterest?.FloatRate ?? 0;
|
||||
item.OpenMarginAmount = margins.Sum(s => s.InterestPrincipalFix * (s.InterestDirection == (int)SwapDirectionEnum.收取 ? 1 : -1));
|
||||
item.OpenMarginRate = margins.Sum(s => s.InterestRateDefault * (s.InterestDirection == (int)SwapDirectionEnum.收取 ? 1 : -1));
|
||||
item.MarginInterestAmount = eodMargins.Sum(s => s.InterestIncomeSum * (s.InterestDirection == (int)SwapDirectionEnum.收取 ? 1 : -1));
|
||||
item.OpenMarginAmount = initialMargins.Sum(s => s.InterestPrincipalFix * (s.InterestDirection == (int)SwapDirectionEnum.收取 ? 1 : -1));
|
||||
item.OpenMarginRate = CalculateWeightedMarginRate(tradeMargins);
|
||||
item.AdditionalMarginAmount = additionalMargins.Sum(s => s.InterestPrincipalFix * (s.InterestDirection == (int)SwapDirectionEnum.收取 ? 1 : -1));
|
||||
item.MarginInterestAmount = CalculateWeightedMarginInterest(eodMargins);
|
||||
item.InterestAmount = eodInterests.Sum(s => s.InterestIncomeSum * (s.InterestDirection == (int)SwapDirectionEnum.收取 ? -1 : 1));
|
||||
item.InterestRate = eodInterests.Sum(s => s.InterestRateDefault);
|
||||
item.NetSettmentAmount += item.InterestAmount + item.MarginInterestAmount + eodMargins.Sum(s => s.InterestPrincipalFix * (s.InterestDirection == (int)SwapDirectionEnum.收取 ? 1 : -1));
|
||||
// 到期轧差才把期间付息/分红并入净额结算;派息日支付已在现金流层独立结算,不能重复计入估值。
|
||||
var nettingDividend = (tradeExtend?.ExtendObj?.DividendPayDate ?? 1) == 0 ? pendingDividend : 0m;
|
||||
item.NetSettmentAmount = item.InterestAmount
|
||||
+ item.position.PosiProfitSum
|
||||
+ item.position.PosiFeePending
|
||||
+ item.MarginInterestAmount
|
||||
+ nettingDividend;
|
||||
item.NetSettmentAmount = Math.Round(item.NetSettmentAmount, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero);
|
||||
item.TrsValue = Math.Round(item.NetSettmentAmount + item.OpenMarginAmount + item.AdditionalMarginAmount, ConsGlobal.MoneyRound, MidpointRounding.AwayFromZero);
|
||||
if (item.position.PosiNotionalValue != 0 && item.position.PosiNetPrice != 0)
|
||||
{
|
||||
item.FloatRateAbs = item.position.PosiNotionalValue == 0 ? 0 : item.InterestAmount / item.position.PosiNotionalValue;
|
||||
}
|
||||
SetPosiPrice(item.position);
|
||||
// 交易录入的债券类收益互换价格以小数保存,展示时转为百分比价格;
|
||||
// 普通收益互换录入的是数量/原始数值,不做乘 100 转换。
|
||||
SetPosiPrice(item.position, item.StructureType == "普通债券类收益互换");
|
||||
}
|
||||
return retListResult;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置客户视角
|
||||
/// 计算预付金利率。多条初始/追加预付金腿按本金绝对值加权,
|
||||
/// 不按收付方向轧差,避免相反方向本金抵消后放大利率。
|
||||
/// </summary>
|
||||
private static decimal CalculateWeightedMarginRate(IEnumerable<swap_position> margins)
|
||||
{
|
||||
var marginList = margins.ToList();
|
||||
var totalWeight = marginList.Sum(x => Math.Abs(x.InterestPrincipalFix));
|
||||
return totalWeight == 0
|
||||
? 0
|
||||
: marginList.Sum(x => x.InterestRateDefault * Math.Abs(x.InterestPrincipalFix)) / totalWeight;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算预付金利息。先按收取为正、支付为负转换为我方视角,
|
||||
/// 再按日终本金绝对值加权平均;本金合计为零时返回零。
|
||||
/// </summary>
|
||||
private static decimal CalculateWeightedMarginInterest(IEnumerable<eod_swap_position> margins)
|
||||
{
|
||||
var marginList = margins.ToList();
|
||||
var totalWeight = marginList.Sum(x => Math.Abs(x.InterestPrincipalFix));
|
||||
return totalWeight == 0
|
||||
? 0
|
||||
: marginList.Sum(x => x.InterestIncomeSum
|
||||
* (x.InterestDirection == (int)SwapDirectionEnum.收取 ? 1 : -1)
|
||||
* Math.Abs(x.InterestPrincipalFix)) / totalWeight;
|
||||
}
|
||||
/// <summary>
|
||||
/// 将数据库中以公司/交易簿记方向保存的日终字段转换为客户视角。
|
||||
/// 该转换必须在拆分浮动收益、费用和期间付息/分红之前完成,
|
||||
/// 否则页面、Excel 和净额结算金额会出现相反符号。
|
||||
/// </summary>
|
||||
/// <param name="position"></param>
|
||||
private void SetClientEodPosition(eod_swap_position position)
|
||||
@@ -2412,10 +2588,10 @@ namespace YLErp.Modules.SwapModule
|
||||
position.SwapPositionValue = -position.SwapPositionValue;
|
||||
position.PosiDividendSum = -position.PosiDividendSum;
|
||||
}
|
||||
private void SetPosiPrice(eod_swap_position position)
|
||||
private void SetPosiPrice(eod_swap_position position, bool? useBondPriceScale = null)
|
||||
{
|
||||
var um = DataCacheProvider.GetUnderlyingDataSource().GetData(position.UnderlyingCode);
|
||||
if (um != null && um.IsBond())
|
||||
if (useBondPriceScale ?? (um != null && um.IsBond()))
|
||||
{
|
||||
position.PosiNetPrice *= 100;
|
||||
position.UnderlyingPrice *= 100;
|
||||
|
||||
Binary file not shown.
@@ -6,9 +6,8 @@
|
||||
var pageObj = new
|
||||
{
|
||||
EndTime = ViewBag.EndTime?.ToString(),
|
||||
StartTime = ViewBag.StartTime?.ToString(),
|
||||
valueDate = valuedateBLL.ValueDate.ToString("yyyy-MM-dd"),
|
||||
configcolumn = configcolumn_data.互换估值,
|
||||
configcolumn = configcolumn_data.互换估值V1,
|
||||
StructureType = ViewBag.StructureType
|
||||
};
|
||||
}
|
||||
@@ -23,7 +22,7 @@
|
||||
width: 250px !important;
|
||||
}
|
||||
|
||||
#ValueDateFrom, #ValueDate {
|
||||
#ValueDate {
|
||||
width: 152px !important;
|
||||
font-size: 1rem !important;
|
||||
}
|
||||
@@ -51,9 +50,8 @@
|
||||
|
||||
<div class="searchdiv">
|
||||
@Html.MyAceDropdownInput("ClientId", "客户名称", ClientDataModel.GetAllClient(), false, true, null, false)
|
||||
@Html.MyAceDropdownInput("BookId", "簿记账户", AssetunitController.GetClientassetunit(), false, true, null, false)
|
||||
@Html.CheckBox("ParentFlag", false) @Html.Label(null,"包含子级", new { @style = "font-size:12px;" })
|
||||
@Html.ShortInput("ValueDateFrom", "起始日期:")
|
||||
@Html.MyAceDropdownInput("BookId", "簿记账户", AssetunitController.GetClientassetunit(), false, true, null, false)
|
||||
@Html.ShortInput("ValueDate", "结束日期:")
|
||||
@MyControls.SearchBtn()
|
||||
@if (CurUser.结算管理_每日估值报告邮件发送)
|
||||
@@ -61,7 +59,6 @@
|
||||
@MyControls.Btn("配置报告", "PopDesc()")
|
||||
@MyControls.Btn("发送报告", "SendReport()")
|
||||
}
|
||||
<button onclick="exportSwapValuationVisibleColumns()" class="btn btn-primary">导出互换估值</button>
|
||||
</div>
|
||||
<br />
|
||||
|
||||
|
||||
@@ -456,10 +456,6 @@ function colModelGridEodPosition() {
|
||||
return colModelGrid;
|
||||
}
|
||||
//框架合约table
|
||||
//TODO(估值模块V1-缺失字段): 以下需求字段本轮未实现,待后端确认数据来源后补充:
|
||||
// - 期间付息/分红(浮动端分组)
|
||||
// - 付息方式、合约估值(到期轧差口径/期间支付派息口径)(估值与实现收益分组)
|
||||
// - 标的类型(基本信息分组)
|
||||
function colModelGridEodSwap() {
|
||||
//按需求《估值模块V1》2.2 分组定义排列列顺序,确保组内列连续(setGroupHeaders 要求)
|
||||
var colModelGrid = [{
|
||||
@@ -526,6 +522,13 @@ function colModelGridEodSwap() {
|
||||
width: 100,
|
||||
align: 'center',
|
||||
sortable: false
|
||||
}, {
|
||||
name: 'UnderlyingType',
|
||||
label: '标的类型',
|
||||
index: 'UnderlyingType',
|
||||
width: 100,
|
||||
align: 'center',
|
||||
sortable: false
|
||||
},
|
||||
//=== 名义本金 ===
|
||||
{
|
||||
@@ -568,12 +571,22 @@ function colModelGridEodSwap() {
|
||||
},
|
||||
//=== 浮动端 ===
|
||||
{
|
||||
name: 'position.FloatingPnL',
|
||||
name: 'FloatingUnrealizedPnl',
|
||||
label: '合约浮动端待实现收益',
|
||||
index: 'position.FloatingPnL',
|
||||
index: 'FloatingUnrealizedPnl',
|
||||
width: 150,
|
||||
align: 'center',
|
||||
formatter: StockEqvNotionalFormat,
|
||||
}, {
|
||||
name: 'PeriodAmount',
|
||||
label: '期间付息/分红',
|
||||
index: 'PeriodAmount',
|
||||
width: 150,
|
||||
align: 'center',
|
||||
formatter: StockEqvNotionalFormat,
|
||||
cellattr: function () {
|
||||
return ' title="合约期间内的期间付息金额(无关乎派息支付日)"';
|
||||
},
|
||||
},
|
||||
//=== 利息端 ===
|
||||
{
|
||||
@@ -613,30 +626,23 @@ function colModelGridEodSwap() {
|
||||
width: 150,
|
||||
align: 'center',
|
||||
formatter: StockEqvNotionalFormat,
|
||||
},
|
||||
//=== 估值与实现收益 ===
|
||||
{
|
||||
name: 'position.PostionValue',
|
||||
label: '合约持仓价值',
|
||||
index: 'position.PostionValue',
|
||||
width: 150,
|
||||
align: 'center',
|
||||
formatter: StockEqvNotionalFormat
|
||||
}, {
|
||||
name: 'position.TdRealizedPnL',
|
||||
label: '合约当日实现收益',
|
||||
index: 'position.TdRealizedPnL',
|
||||
width: 150,
|
||||
align: 'center',
|
||||
formatter: StockEqvNotionalFormat
|
||||
}, {
|
||||
name: 'position.RealizedPnL',
|
||||
label: '合约已实现收益',
|
||||
index: 'position.RealizedPnL',
|
||||
width: 150,
|
||||
name: 'MarginInterestGain',
|
||||
label: '收取对手方保证金利息',
|
||||
index: 'MarginInterestGain',
|
||||
width: 170,
|
||||
align: 'center',
|
||||
formatter: StockEqvNotionalFormat,
|
||||
}, {
|
||||
name: 'MarginInterestLoss',
|
||||
label: '支付对手方保证金利息',
|
||||
index: 'MarginInterestLoss',
|
||||
width: 170,
|
||||
align: 'center',
|
||||
formatter: StockEqvNotionalFormat,
|
||||
},
|
||||
//=== 估值与实现收益 ===
|
||||
{
|
||||
name: 'position.dv01',
|
||||
label: 'DV',
|
||||
index: 'position.dv01',
|
||||
@@ -648,6 +654,33 @@ function colModelGridEodSwap() {
|
||||
if (abs < 0.0001) return "0";
|
||||
return cellvalue.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 4 });
|
||||
},
|
||||
}, {
|
||||
name: 'InterestPaymentMethod',
|
||||
label: '付息方式',
|
||||
index: 'InterestPaymentMethod',
|
||||
width: 120,
|
||||
align: 'center',
|
||||
}, {
|
||||
name: 'MaturityNettingValuation',
|
||||
label: '合约估值(到期轧差口径)',
|
||||
index: 'MaturityNettingValuation',
|
||||
width: 190,
|
||||
align: 'center',
|
||||
formatter: NullableStockEqvNotionalFormat,
|
||||
}, {
|
||||
name: 'PeriodPaymentValuation',
|
||||
label: '合约估值(期间支付派息口径)',
|
||||
index: 'PeriodPaymentValuation',
|
||||
width: 210,
|
||||
align: 'center',
|
||||
formatter: NullableStockEqvNotionalFormat,
|
||||
}, {
|
||||
name: 'position.RealizedPnL',
|
||||
label: '合约已实现收益',
|
||||
index: 'position.RealizedPnL',
|
||||
width: 150,
|
||||
align: 'center',
|
||||
formatter: StockEqvNotionalFormat,
|
||||
}
|
||||
|
||||
];
|
||||
@@ -657,13 +690,13 @@ function colModelGridEodSwap() {
|
||||
//框架合约分组配置(对应需求《估值模块V1》2.2 字段定义)
|
||||
//columns 使用 colModel.name;组内列在 colModel 中必须连续
|
||||
var eodSwapGroupConfig = [
|
||||
{ title: '基本信息', columns: ['position.ValueDate', 'AssetBookName', 'ClientName', 'SwapTradeNo', 'StructureType', 'SwapTradeTypeStr'] },
|
||||
{ title: '基本信息', columns: ['position.ValueDate', 'AssetBookName', 'ClientName', 'SwapTradeNo', 'StructureType', 'SwapTradeTypeStr', 'UnderlyingType'] },
|
||||
{ title: '名义本金', columns: ['position.NotionalValue', 'position.NotionalValueLong', 'position.NotionalValueShort'] },
|
||||
{ title: '标的市值', columns: ['position.MarketValueLong', 'position.MarketValueShort'] },
|
||||
{ title: '浮动端', columns: ['position.FloatingPnL'] },
|
||||
{ title: '浮动端', columns: ['FloatingUnrealizedPnl', 'PeriodAmount'] },
|
||||
{ title: '利息端', columns: ['position.InterestPnL'] },
|
||||
{ title: '保证金', columns: ['position.InitMarginGain', 'position.PostionMarginGain', 'position.InitMarginLoss', 'position.PostionMarginLoss'] },
|
||||
{ title: '估值与实现收益', columns: ['position.PostionValue', 'position.TdRealizedPnL', 'position.RealizedPnL', 'position.dv01'] }
|
||||
{ title: '保证金', columns: ['position.InitMarginGain', 'position.PostionMarginGain', 'position.InitMarginLoss', 'position.PostionMarginLoss', 'MarginInterestGain', 'MarginInterestLoss'] },
|
||||
{ title: '估值与实现收益', columns: ['position.dv01', 'InterestPaymentMethod', 'MaturityNettingValuation', 'PeriodPaymentValuation', 'position.RealizedPnL'] }
|
||||
];
|
||||
|
||||
|
||||
@@ -677,7 +710,11 @@ function gridComplete() {
|
||||
if (page.tabIndex == 2) {
|
||||
var defer = main.setcolumnChooser(jgrid, page.configcolumn_data);
|
||||
$.when(defer).done(function () {
|
||||
normalizeEodSwapColumnsForGroupHeaders(jgrid, eodSwapGroupConfig);
|
||||
main.initCollapsibleGroupHeaders(jgrid, eodSwapGroupConfig);
|
||||
jgrid.jqGrid('setLabel', 'PeriodAmount', null, null, {
|
||||
title: '合约期间内的期间付息金额(无关乎派息支付日)'
|
||||
});
|
||||
});
|
||||
} else {
|
||||
main.setcolumnChooser(jgrid, page.configcolumn_data);
|
||||
@@ -704,7 +741,69 @@ function exportVisibleColumns() {
|
||||
var dateStr = $("#DateValueDate").val() || '';
|
||||
var tabName = page.tabIndex == 2 ? '框架合约' : '日终持仓';
|
||||
var fileName = '日终持仓风险_互换_' + tabName + (dateStr ? '_' + dateStr : '');
|
||||
main.exportVisibleColumnsToExcel(jgrid, fileName);
|
||||
var groupConfig = page.tabIndex == 2 ? eodSwapGroupConfig : null;
|
||||
if (page.tabIndex != 2) {
|
||||
main.exportVisibleColumnsToExcel(jgrid, fileName, groupConfig);
|
||||
return;
|
||||
}
|
||||
|
||||
var exportPostData = $.extend({}, GetPostData(), {
|
||||
page: 1,
|
||||
rows: 0,
|
||||
sidx: jgrid.jqGrid('getGridParam', 'sortname'),
|
||||
sord: jgrid.jqGrid('getGridParam', 'sortorder')
|
||||
});
|
||||
$.ajax({
|
||||
url: queryurl,
|
||||
type: 'POST',
|
||||
dataType: 'json',
|
||||
traditional: true,
|
||||
data: exportPostData
|
||||
}).done(function (result) {
|
||||
main.exportVisibleColumnsToExcel(jgrid, fileName, groupConfig, result && result.rows ? result.rows : []);
|
||||
}).fail(function () {
|
||||
main.message && main.message('导出失败,无法获取筛选后的全部数据');
|
||||
});
|
||||
}
|
||||
|
||||
function normalizeEodSwapColumnsForGroupHeaders(jgrid, groupConfig) {
|
||||
var colModel = jgrid.jqGrid('getGridParam', 'colModel') || [];
|
||||
var currentNames = colModel.map(function (c) { return c.name; });
|
||||
var groupedNames = [];
|
||||
var groupedNameMap = {};
|
||||
groupConfig.forEach(function (group) {
|
||||
(group.columns || []).forEach(function (name) {
|
||||
if (!groupedNameMap[name] && currentNames.indexOf(name) >= 0) {
|
||||
groupedNameMap[name] = true;
|
||||
groupedNames.push(name);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
var firstGroupedIndex = currentNames.findIndex(function (name) { return groupedNameMap[name]; });
|
||||
if (firstGroupedIndex < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
var prefixNames = currentNames.slice(0, firstGroupedIndex).filter(function (name) {
|
||||
return !groupedNameMap[name];
|
||||
});
|
||||
var suffixNames = currentNames.slice(firstGroupedIndex).filter(function (name) {
|
||||
return !groupedNameMap[name];
|
||||
});
|
||||
var desiredNames = prefixNames.concat(groupedNames).concat(suffixNames);
|
||||
if (desiredNames.join('|') === currentNames.join('|')) {
|
||||
return;
|
||||
}
|
||||
|
||||
var perm = desiredNames.map(function (name) {
|
||||
return currentNames.indexOf(name);
|
||||
}).filter(function (idx) {
|
||||
return idx >= 0;
|
||||
});
|
||||
if (perm.length === currentNames.length) {
|
||||
jgrid.jqGrid("remapColumns", perm, true);
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------Formatter---------------------------------
|
||||
@@ -719,6 +818,12 @@ function RealizedPnlFormat(cellValue, options, rowObject) {
|
||||
function StockEqvNotionalFormat(cellValue, options, rowObject) {
|
||||
return otcformat.trading.StockEqvNotional(cellValue);
|
||||
}
|
||||
function NullableStockEqvNotionalFormat(cellValue, options, rowObject) {
|
||||
if (cellValue === null || cellValue === undefined || cellValue === '') {
|
||||
return '';
|
||||
}
|
||||
return StockEqvNotionalFormat(cellValue, options, rowObject);
|
||||
}
|
||||
function PosiStatusFormat(cellValue, options, rowObject) {
|
||||
return cellValue == 1 ? "已平" : "正常";
|
||||
}
|
||||
@@ -817,4 +922,4 @@ function SearchClick(isSearchclick) {
|
||||
function showcolumnChooser() {
|
||||
var jgrid = jQuery('#listGrid');
|
||||
main.showcolumnChooser(jgrid, cloumnTargetName, page.configcolumn_data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,32 +2,16 @@
|
||||
$(function () {
|
||||
var PostData = {};
|
||||
|
||||
//控件选择时的触发事件
|
||||
main.setTradeDatePicker("", "#ValueDate", page.calcDate, function (selectedDate) {
|
||||
if (selectedDate) {
|
||||
$("#ValueDateFrom").datepicker("option", "maxDate", selectedDate);
|
||||
}
|
||||
});
|
||||
main.setTradeDatePicker("", "#ValueDateFrom", page.calcDate, function (selectedDate) {
|
||||
if (selectedDate) {
|
||||
$("#ValueDate").datepicker("option", "minDate", selectedDate);
|
||||
}
|
||||
});
|
||||
//手动修改时的触发事件
|
||||
$("#ValueDate").change(function () {
|
||||
$("#ValueDateFrom").datepicker("option", "maxDate", $("#ValueDate").val());
|
||||
});
|
||||
$("#ValueDateFrom").change(function () {
|
||||
$("#ValueDate").datepicker("option", "minDate", $("#ValueDateFrom").val());
|
||||
});
|
||||
main.setTradeDatePicker("", "#ValueDate", page.calcDate);
|
||||
|
||||
//默认初始值赋值逻辑
|
||||
// 互换估值页仅暴露一个估值日。当前后端按 ValueDate 单日查询,
|
||||
// 因此 ValueDateFrom 传同日仅用于保持请求对象和报告参数的日期语义一致。
|
||||
$("#ValueDate").val(page.EndTime || page.calcDate);
|
||||
$("#ValueDateFrom").val(page.StartTime && page.EndTime ? page.StartTime : '');
|
||||
PostData.StructureType = page.StructureType;
|
||||
PostData.ValueDateFrom = $("#ValueDateFrom").val();
|
||||
PostData.ValueDateFrom = $("#ValueDate").val();
|
||||
PostData.ValueDate = $("#ValueDate").val();
|
||||
PostData.ClientId = $("#ClientId").val();
|
||||
PostData.BookId = $("#BookId").val();
|
||||
|
||||
var grid = jQuery('#listGrid').jqGrid({
|
||||
url: '/swaptrade2/clientEodSwapPositionQuery',
|
||||
@@ -50,8 +34,7 @@ $(function () {
|
||||
pagerpos: 'left',
|
||||
rowNum: 100,
|
||||
rowList: [100, 1000],
|
||||
loadComplete: gridComplete,
|
||||
grouping: true
|
||||
loadComplete: gridComplete
|
||||
});
|
||||
g_grid = jQuery('#listGrid');
|
||||
|
||||
@@ -110,11 +93,7 @@ function searchPositionDetials(isSearchclick) {
|
||||
// main.message("结束日期不能大于当前系统日期!");
|
||||
// return;
|
||||
//}
|
||||
if ($("#ValueDate").val() < $("#ValueDateFrom").val()) {
|
||||
main.message("起始日期不能大于结束日期!");
|
||||
return;
|
||||
}
|
||||
listGrid.appendPostData({ ValueDateFrom: $("#ValueDateFrom").val() });
|
||||
listGrid.appendPostData({ ValueDateFrom: $("#ValueDate").val() });
|
||||
listGrid.appendPostData({ ValueDate: $("#ValueDate").val() });
|
||||
listGrid.appendPostData({ StructureType: page.StructureType });
|
||||
if (typeof (isSearchclick) != "undefined" && isSearchclick) {
|
||||
@@ -134,7 +113,7 @@ function onSortCol(index, icol, sortorder) {
|
||||
|
||||
var i = 0;
|
||||
var colModelGrid = [
|
||||
//隐藏列(不参与分组)
|
||||
//隐藏列
|
||||
{
|
||||
name: 'position.EncryptId',
|
||||
label: 'EncryptId',
|
||||
@@ -156,14 +135,6 @@ var colModelGrid = [
|
||||
index: 'UnwindDate',
|
||||
hidden: true,
|
||||
optionHide: true
|
||||
}, {
|
||||
name: 'TradeNumber',
|
||||
label: '交易编号',
|
||||
index: 'TradeNumber',
|
||||
sortIndex: i++,
|
||||
width: 180,
|
||||
align: 'center',
|
||||
sortable: false
|
||||
}, {
|
||||
name: 'ConfrimNo',
|
||||
label: '确认书编号',
|
||||
@@ -173,11 +144,11 @@ var colModelGrid = [
|
||||
align: 'center',
|
||||
sortable: false
|
||||
}, {
|
||||
name: 'ClientName',
|
||||
label: '交易对手',
|
||||
index: 'ClientName',
|
||||
name: 'TradeNumber',
|
||||
label: '交易编号',
|
||||
index: 'TradeNumber',
|
||||
sortIndex: i++,
|
||||
width: 120,
|
||||
width: 180,
|
||||
align: 'center',
|
||||
sortable: false
|
||||
}, {
|
||||
@@ -189,6 +160,15 @@ var colModelGrid = [
|
||||
align: 'center',
|
||||
sortable: false,
|
||||
formatter:'date',
|
||||
}, {
|
||||
name: 'MaturitySettlementDate',
|
||||
label: '到期结算日',
|
||||
index: 'MaturitySettlementDate',
|
||||
sortIndex: i++,
|
||||
width: 100,
|
||||
align: 'center',
|
||||
sortable: false,
|
||||
formatter: 'date',
|
||||
}, {
|
||||
name: 'position.ValueDate',
|
||||
label: '估值日',
|
||||
@@ -212,22 +192,7 @@ var colModelGrid = [
|
||||
width: 120,
|
||||
align: 'center',
|
||||
sortable: false,
|
||||
formatter: RateFormat
|
||||
}, {
|
||||
name: 'position.FloatRateUnderlyingCode',
|
||||
label: '基准利率',
|
||||
index: 'position.FloatRateUnderlyingCode',
|
||||
width: 120,
|
||||
align: 'center',
|
||||
sortable: false
|
||||
}, {
|
||||
name: 'position.FloatRate',
|
||||
label: '当日适用基准利率',
|
||||
index: 'position.FloatRate',
|
||||
width: 120,
|
||||
align: 'center',
|
||||
sortable: false,
|
||||
formatter: RateFormat
|
||||
formatter: SpreadRateFormat
|
||||
}, {
|
||||
name: 'position.PosiNotionalValue',
|
||||
label: '标的名义金额',
|
||||
@@ -235,7 +200,7 @@ var colModelGrid = [
|
||||
width: 150,
|
||||
align: 'center',
|
||||
sortable: false,
|
||||
formatter: StockEqvNotionalFormat,
|
||||
formatter: AmountFormat,
|
||||
}, {
|
||||
name: 'position.PosiQuantity',
|
||||
label: '标的数量',
|
||||
@@ -250,6 +215,15 @@ var colModelGrid = [
|
||||
index: 'PeriodAmount',
|
||||
width: 100,
|
||||
align: 'center',
|
||||
formatter: NullableAmountFormat,
|
||||
sortable: false
|
||||
}, {
|
||||
name: 'DividendAmount',
|
||||
label: '期间分红',
|
||||
index: 'DividendAmount',
|
||||
width: 100,
|
||||
align: 'center',
|
||||
formatter: NullableAmountFormat,
|
||||
sortable: false
|
||||
}, {
|
||||
name: 'position.PosiGrossPrice',
|
||||
@@ -266,12 +240,7 @@ var colModelGrid = [
|
||||
width: 150,
|
||||
align: 'center',
|
||||
sortable: false,
|
||||
formatter: function (cellValue, options, rowObject) {
|
||||
if (cellValue == null) {
|
||||
return "";
|
||||
}
|
||||
return otcformat.trading.premiumRateP(cellValue);
|
||||
}
|
||||
formatter: YieldRateFormat
|
||||
},
|
||||
{
|
||||
name: 'position.UnderlyingPrice',
|
||||
@@ -302,15 +271,7 @@ var colModelGrid = [
|
||||
index: 'InterestAmount',
|
||||
width: 120,
|
||||
align: 'center',
|
||||
formatter: StockEqvNotionalFormat,
|
||||
sortable: false,
|
||||
}, {
|
||||
name: 'position.PosiFeePending',
|
||||
label: '开仓交易费用',
|
||||
index: 'position.PosiFeePending',
|
||||
width: 120,
|
||||
align: 'center',
|
||||
formatter: StockEqvNotionalFormat,
|
||||
formatter: FixedAmountFormat,
|
||||
sortable: false,
|
||||
}, {
|
||||
name: 'position.PosiProfitSum',
|
||||
@@ -318,7 +279,47 @@ var colModelGrid = [
|
||||
index: 'position.PosiProfitSum',
|
||||
width: 120,
|
||||
align: 'center',
|
||||
formatter: StockEqvNotionalFormat,
|
||||
formatter: FixedAmountFormat,
|
||||
sortable: false,
|
||||
}, {
|
||||
name: 'position.PosiFeePending',
|
||||
label: '开平仓交易费用',
|
||||
index: 'position.PosiFeePending',
|
||||
width: 120,
|
||||
align: 'center',
|
||||
formatter: FixedAmountFormat,
|
||||
sortable: false,
|
||||
}, {
|
||||
name: 'OpenMarginRate',
|
||||
label: '预付金利率',
|
||||
index: 'OpenMarginRate',
|
||||
width: 120,
|
||||
align: 'center',
|
||||
formatter: TrimmedRateFormat,
|
||||
sortable: false,
|
||||
}, {
|
||||
name: 'MarginInterestAmount',
|
||||
label: '预付金利息',
|
||||
index: 'MarginInterestAmount',
|
||||
width: 120,
|
||||
align: 'center',
|
||||
formatter: FixedAmountFormat,
|
||||
sortable: false,
|
||||
}, {
|
||||
name: 'OpenMarginAmount',
|
||||
label: '期初预付金',
|
||||
index: 'OpenMarginAmount',
|
||||
width: 120,
|
||||
align: 'center',
|
||||
formatter: FixedAmountFormat,
|
||||
sortable: false,
|
||||
}, {
|
||||
name: 'AdditionalMarginAmount',
|
||||
label: '追加预付金',
|
||||
index: 'AdditionalMarginAmount',
|
||||
width: 120,
|
||||
align: 'center',
|
||||
formatter: FixedAmountFormat,
|
||||
sortable: false,
|
||||
}, {
|
||||
name: 'NetSettmentAmount',
|
||||
@@ -326,23 +327,23 @@ var colModelGrid = [
|
||||
index: 'NetSettmentAmount',
|
||||
width: 120,
|
||||
align: 'center',
|
||||
formatter: StockEqvNotionalFormat,
|
||||
formatter: FixedAmountFormat,
|
||||
sortable: false,
|
||||
}, {
|
||||
name: 'TrsValue',
|
||||
label: 'TRS估值',
|
||||
index: 'TrsValue',
|
||||
width: 120,
|
||||
align: 'center',
|
||||
formatter: FixedAmountFormat,
|
||||
sortable: false,
|
||||
}
|
||||
//TODO(估值模块V1-缺失字段): 以下需求字段本轮未实现,待后端确认数据来源后补充:
|
||||
// - 期间分红(ETF适用,利率与规模分组)
|
||||
// - 预付金利率、预付金利息、期初预付金、追加预付金(预付金分组)
|
||||
// - TRS估值(汇总分组)
|
||||
];
|
||||
|
||||
//互换估值分组配置(对应需求《估值模块V1》3.2.2 字段定义)
|
||||
//columns 使用 colModel.name;现有列顺序已天然满足分组连续性,无需重排
|
||||
var eodSwapValuationGroupConfig = [
|
||||
{ title: '基本信息', columns: ['TradeNumber', 'ConfrimNo', 'ClientName', 'position.PosiStartDate', 'position.ValueDate', 'position.UnderlyingCode'] },
|
||||
{ title: '利率与规模', columns: ['InterestRate', 'position.FloatRateUnderlyingCode', 'position.FloatRate', 'position.PosiNotionalValue', 'position.PosiQuantity', 'PeriodAmount'] },
|
||||
{ title: '价格', columns: ['position.PosiGrossPrice', 'InitYtm', 'position.UnderlyingPrice', 'DayCount'] },
|
||||
{ title: '收益', columns: ['FloatRateAbs', 'InterestAmount', 'position.PosiFeePending', 'position.PosiProfitSum', 'NetSettmentAmount'] }
|
||||
];
|
||||
// 页面字段顺序以《估值模块V1》第二部分为准;历史个人列配置只能控制显隐,不能打乱业务列顺序。
|
||||
var defaultVisibleColumnNames = colModelGrid
|
||||
.filter(function (column) { return column.hidden !== true; })
|
||||
.map(function (column) { return column.name; });
|
||||
var documentColumnOrder = colModelGrid.map(function (column) { return column.name; });
|
||||
|
||||
function formatter6(cellvalue, options, rowObject) {
|
||||
return main.formatNumber(cellvalue, 6);
|
||||
@@ -372,25 +373,37 @@ function gridComplete() {
|
||||
}
|
||||
}
|
||||
//jgrid.sortGrid(g_sort.name, g_sort.order);
|
||||
//在列设置应用完成后初始化可折叠分组表头(setcolumnChooser 会 remapColumns,须在其 done 回调后建表头)
|
||||
var defer = main.setcolumnChooser(jgrid, page.configcolumn);
|
||||
$.when(defer).done(function () {
|
||||
main.initCollapsibleGroupHeaders(jgrid, eodSwapValuationGroupConfig);
|
||||
$.when(main.setcolumnChooser(jgrid, page.configcolumn)).always(function () {
|
||||
restoreDocumentColumnOrder(jgrid);
|
||||
ensureBusinessColumnsVisible(jgrid);
|
||||
});
|
||||
$(".selftooltip").tooltip({ html: true, show: 50000, trigger: "hover" });
|
||||
$(window).off('resize.jqGrid');
|
||||
}
|
||||
|
||||
//导出互换估值当前可见列(需求《估值模块V1》3.2 + 4.2)
|
||||
//与现有 DownLoadReport(整份PDF/Excel报告)独立,互不影响
|
||||
function exportSwapValuationVisibleColumns() {
|
||||
var jgrid = jQuery('#listGrid');
|
||||
var clientName = $("#ClientId :selected").text() || '';
|
||||
var dateStr = $("#ValueDate").val() || '';
|
||||
var fileName = (clientName ? clientName + '_' : '') + '每日估值报告_互换估值' + (dateStr ? '_' + dateStr : '');
|
||||
main.exportVisibleColumnsToExcel(jgrid, fileName);
|
||||
function restoreDocumentColumnOrder(jgrid) {
|
||||
var colModel = jgrid.jqGrid('getGridParam', 'colModel') || [];
|
||||
var currentNames = colModel.map(function (column) { return column.name; });
|
||||
var targetNames = currentNames
|
||||
.filter(function (name) { return documentColumnOrder.indexOf(name) < 0; })
|
||||
.concat(documentColumnOrder);
|
||||
var permutation = targetNames.map(function (name) { return currentNames.indexOf(name); });
|
||||
if (permutation.length === currentNames.length
|
||||
&& permutation.every(function (index) { return index >= 0; })
|
||||
&& permutation.some(function (index, targetIndex) { return index !== targetIndex; })) {
|
||||
jgrid.jqGrid('remapColumns', permutation, true);
|
||||
}
|
||||
}
|
||||
|
||||
function ensureBusinessColumnsVisible(jgrid) {
|
||||
var colModel = jgrid.jqGrid('getGridParam', 'colModel') || [];
|
||||
var hasVisibleBusinessColumn = colModel.some(function (column) {
|
||||
return defaultVisibleColumnNames.indexOf(column.name) >= 0 && column.hidden !== true;
|
||||
});
|
||||
if (!hasVisibleBusinessColumn) {
|
||||
jgrid.jqGrid('showCol', defaultVisibleColumnNames);
|
||||
}
|
||||
}
|
||||
|
||||
function reloadTradeMarketReport() {
|
||||
//重新加载
|
||||
@@ -430,10 +443,6 @@ function SendReport() {
|
||||
main.message("结束日期不能大于当前系统日期!");
|
||||
return;
|
||||
}
|
||||
if ($("#ValueDate").val() < $("#ValueDateFrom").val()) {
|
||||
main.message("起始日期不能大于结束日期!");
|
||||
return;
|
||||
}
|
||||
main.open("向{0}发送报告".template(clientName), "/clientbalance/TradeMarketClientSend?clientid=" + param.ClientId + "&ParentFlag=" + param.ParentFlag);
|
||||
}
|
||||
function DownLoadReport() {
|
||||
@@ -454,18 +463,16 @@ function DownLoadReport() {
|
||||
main.message("结束日期不能大于当前系统日期!");
|
||||
return;
|
||||
}
|
||||
if ($("#ValueDate").val() < $("#ValueDateFrom").val()) {
|
||||
main.message("起始日期不能大于结束日期!");
|
||||
return;
|
||||
}
|
||||
|
||||
main.post("/clientbalance/ViewTradeMarketFile", screenData()).done(function (res) {
|
||||
window.open(res.obj);
|
||||
});
|
||||
}
|
||||
function screenData() {
|
||||
var data = { From: $("#ValueDateFrom").val(), To: $("#ValueDate").val() };
|
||||
// 已移除起始日期控件,预览报告按单个估值日生成,From/To 保持同日。
|
||||
var data = { From: $("#ValueDate").val(), To: $("#ValueDate").val() };
|
||||
data.ClientId = $("#ClientId").val();
|
||||
// 报告下载和发送弹窗均从 screenData 取参数,必须保留当前簿记账户筛选。
|
||||
data.BookId = $("#BookId").val();
|
||||
if ($("#ParentFlag").prop("checked"))
|
||||
data.ParentFlag = true;
|
||||
else
|
||||
@@ -477,12 +484,35 @@ function showChiCang() {
|
||||
main.showcolumnChooser(jQuery('#listGrid'), page.configcolumn);
|
||||
}
|
||||
function PriceFormat(cellValue, options, rowObject) {
|
||||
return otcformat.trading.umprice(cellValue);
|
||||
return main.formatNumber(cellValue, 9, { grouping: true });
|
||||
}
|
||||
|
||||
function StockEqvNotionalFormat(cellValue, options, rowObject) {
|
||||
return otcformat.trading.StockEqvNotional(cellValue);
|
||||
}
|
||||
function AmountFormat(cellValue, options, rowObject) {
|
||||
return main.formatNumber(cellValue, 2, { trimTailZeros: true });
|
||||
}
|
||||
function NullableAmountFormat(cellValue, options, rowObject) {
|
||||
if (cellValue === null || cellValue === undefined || cellValue === '') {
|
||||
return '';
|
||||
}
|
||||
return AmountFormat(cellValue, options, rowObject);
|
||||
}
|
||||
function FixedAmountFormat(cellValue, options, rowObject) {
|
||||
return main.formatNumber(cellValue, 2);
|
||||
}
|
||||
function YieldRateFormat(cellValue, options, rowObject) {
|
||||
if (cellValue === null || cellValue === undefined || cellValue === '') {
|
||||
return '';
|
||||
}
|
||||
// percent 格式会在数字末尾添加 %,通用 trimTailZeros 无法识别其后的 0。
|
||||
// 先将小数收益率转为百分比数值,再格式化并追加 %,确保最多保留四位小数且去尾零。
|
||||
return main.formatNumber(cellValue * 100, 4, { trimTailZeros: true }) + '%';
|
||||
}
|
||||
function TrimmedRateFormat(cellValue, options, rowObject) {
|
||||
return main.formatNumber(cellValue, 4, { percent: true, trimTailZeros: true });
|
||||
}
|
||||
function RateFormat(cellValue, options, rowObject) {
|
||||
if (cellValue) {
|
||||
var num = new Number(cellValue) * 100;
|
||||
@@ -491,19 +521,23 @@ function RateFormat(cellValue, options, rowObject) {
|
||||
return "0.0000%";
|
||||
}
|
||||
}
|
||||
function SpreadRateFormat(cellValue, options, rowObject) {
|
||||
if (cellValue) {
|
||||
var num = new Number(cellValue) * 100;
|
||||
return num.toFixed(2) + "%";
|
||||
} else {
|
||||
return "0.00%";
|
||||
}
|
||||
}
|
||||
function locationChange(tab) {
|
||||
if ($("#ValueDate").val() > page.valueDate) {
|
||||
main.message("结束日期不能大于当前系统日期!");
|
||||
return;
|
||||
}
|
||||
if ($("#ValueDate").val() < $("#ValueDateFrom").val()) {
|
||||
main.message("起始日期不能大于结束日期!");
|
||||
return;
|
||||
}
|
||||
if ($("#ParentFlag").prop("checked"))
|
||||
ParentFlag = true;
|
||||
else
|
||||
ParentFlag = false;
|
||||
window.location.href = tab + "?clientId=" + $("#ClientId").val() + "&startTime=" + $("#ValueDateFrom").val() + "&endTime=" + $("#ValueDate").val() + "&ParentFlag=" + ParentFlag;
|
||||
window.location.href = tab + "?clientId=" + $("#ClientId").val() + "&startTime=" + $("#ValueDate").val() + "&endTime=" + $("#ValueDate").val() + "&ParentFlag=" + ParentFlag;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1209,21 +1209,41 @@ main.refreshCollapsibleGroupHeaders = function (jgrid) {
|
||||
*
|
||||
* @param {jQuery} jgrid jqGrid 容器
|
||||
* @param {string} fileName 导出文件名(不含扩展名)
|
||||
* @param {Array} groupConfig 可选,分组表头配置,每项 { title: string, columns: string[] }
|
||||
* @param {Array} exportRows 可选,后端返回的全部筛选结果;未传时导出当前页
|
||||
*/
|
||||
main.exportVisibleColumnsToExcel = function (jgrid, fileName) {
|
||||
main.exportVisibleColumnsToExcel = function (jgrid, fileName, groupConfig, exportRows) {
|
||||
var colModel = jgrid.jqGrid('getGridParam', 'colModel');
|
||||
// 只导出可见列(hidden !== true),与折叠状态联动:收起的列自动不可见
|
||||
var visibleCols = colModel.filter(function (c) { return c.hidden !== true && c.name !== 'cb' && c.name !== 'rn'; });
|
||||
if (!visibleCols.length) { main.message && main.message("没有可导出的列"); return; }
|
||||
|
||||
var rows = jgrid.jqGrid('getRowData'); // 获取当前页数据(已格式化)
|
||||
var rows = exportRows || jgrid.jqGrid('getRowData');
|
||||
if (exportRows) {
|
||||
var gridElement = jgrid[0];
|
||||
rows = exportRows.map(function (row, rowIndex) {
|
||||
var formattedRow = {};
|
||||
visibleCols.forEach(function (col) {
|
||||
var colIndex = colModel.indexOf(col);
|
||||
var rawValue = $.jgrid.getAccessor(row, col.name);
|
||||
var formattedValue = gridElement && gridElement.formatter
|
||||
? gridElement.formatter(rowIndex + 1, rawValue, colIndex, row, 'add')
|
||||
: rawValue;
|
||||
formattedRow[col.name] = $('<div>').html(formattedValue == null ? '' : String(formattedValue)).text().replace(/\u00a0/g, '');
|
||||
});
|
||||
return formattedRow;
|
||||
});
|
||||
}
|
||||
var headerLabels = visibleCols.map(function (c) { return c.label || c.name; });
|
||||
|
||||
// 构建 HTML table,用 style 保持 mso-number-format 让金额不被科学计数法破坏
|
||||
var html = '<html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel" xmlns="http://www.w3.org/TR/REC-html40">';
|
||||
html += '<head><meta charset="UTF-8"><!--[if gte mso 9]><xml><x:ExcelWorkbook><x:ExcelWorksheets><x:ExcelWorksheet><x:Name>Sheet1</x:Name><x:WorksheetOptions><x:DisplayGridlines/></x:WorksheetOptions></x:ExcelWorksheet></x:ExcelWorksheets></x:ExcelWorkbook></xml><![endif]--></head>';
|
||||
html += '<body><table border="1">';
|
||||
//表头
|
||||
//表头:有分组配置时导出两级表头(分类 + 字段),否则保持原单级表头。
|
||||
if (groupConfig && groupConfig.length) {
|
||||
html += buildGroupHeaderHtml(visibleCols, groupConfig);
|
||||
}
|
||||
html += '<tr>' + headerLabels.map(function (l) {
|
||||
return '<th style="background:#f0f0f0;font-weight:bold;">' + escapeXml(l) + '</th>';
|
||||
}).join('') + '</tr>';
|
||||
@@ -1252,5 +1272,25 @@ main.exportVisibleColumnsToExcel = function (jgrid, fileName) {
|
||||
function escapeXml(s) {
|
||||
return s.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
||||
}
|
||||
|
||||
function buildGroupHeaderHtml(cols, config) {
|
||||
var titleByColumn = {};
|
||||
config.forEach(function (group) {
|
||||
(group.columns || []).forEach(function (name) {
|
||||
titleByColumn[name] = group.title || '';
|
||||
});
|
||||
});
|
||||
|
||||
var cells = [];
|
||||
for (var i = 0; i < cols.length; i++) {
|
||||
var title = titleByColumn[cols[i].name] || '';
|
||||
var colspan = 1;
|
||||
while (i + colspan < cols.length && (titleByColumn[cols[i + colspan].name] || '') === title) {
|
||||
colspan++;
|
||||
}
|
||||
cells.push('<th colspan="' + colspan + '" style="background:#d9edf7;font-weight:bold;text-align:center;">' + escapeXml(title) + '</th>');
|
||||
i += colspan - 1;
|
||||
}
|
||||
return '<tr>' + cells.join('') + '</tr>';
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user