BuildFrontendValidationDiffs/FrontendPnlDiff/AddDiffIfOverThreshold 从2043行 SwapDealService 移至新文件,本文件仅留调用+日志。纯位置迁移,零行为变更。
111 lines
5.2 KiB
C#
111 lines
5.2 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using MoreLinq.Extensions;
|
||
using Newtonsoft.Json;
|
||
using Qdp.Pricing.Library.Base.Utilities;
|
||
using System.Linq.Expressions;
|
||
using YLErp.BLL;
|
||
using YLErp.BLL.Eod;
|
||
using YLErp.DBModels.Enums;
|
||
using YLErp.Helpers;
|
||
using YLErp.Modules.DataProviderModule;
|
||
using YLErp.Modules.EodModule;
|
||
using YLErp.Modules.TradeModule;
|
||
using YLErp.Modules.TradeModule.DealModule;
|
||
using YLErp.QdpModule;
|
||
|
||
namespace YLErp.Modules.SwapModule
|
||
{
|
||
/// <summary>
|
||
/// 单条前后端盈亏差异(纯数据,便于单测断言)。
|
||
/// 原内嵌于 SwapDealService,因 SwapDealService 已属超大文件(2000+ 行),
|
||
/// 将其与本校验逻辑一并抽离,降低对超大文件的改动面。
|
||
/// </summary>
|
||
public sealed class FrontendPnlDiff
|
||
{
|
||
public string Field { get; init; } = string.Empty;
|
||
public decimal FrontendValue { get; init; }
|
||
public decimal BackendValue { get; init; }
|
||
public decimal Delta => FrontendValue - BackendValue;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 前端盈亏只读校验:用 FrontendCalcReference 公式重算盈亏,与前端传来的 unwindData 逐字段比对。
|
||
/// 纯函数(无副作用、无 DB/日志依赖),便于无库单测(见 SwapFrontendPnlValidateTest)。
|
||
/// 返回 null 表示前置条件不满足(无浮动腿或 PosiGrossPrice=0),调用方应跳过。
|
||
///
|
||
/// 从 SwapDealService.ValidateFrontendPnL 抽出,原方法仅保留调用 + 日志。
|
||
/// </summary>
|
||
public static class SwapFrontendPnlValidator
|
||
{
|
||
/// <param name="unwindData">前端算好传入的结算数据</param>
|
||
/// <param name="isIncome">true=结息页(income公式),false=平仓页(unwind公式)</param>
|
||
/// <param name="threshold">差异阈值,默认 0.01</param>
|
||
public static List<FrontendPnlDiff>? BuildFrontendValidationDiffs(
|
||
UnwindData unwindData, bool isIncome, decimal threshold = 0.01m)
|
||
{
|
||
// 取浮动腿(有 UnderlyingCode 的),与前端 initDeal 取法一致
|
||
var floatLeg = unwindData.FlowEvents?.FirstOrDefault(x => !string.IsNullOrEmpty(x.UnderlyingCode));
|
||
// PosiGrossPrice 是 [NotMapped],前端可能没传;为空/0 时跳过(避免误报)
|
||
if (floatLeg == null || floatLeg.PosiGrossPrice == 0)
|
||
{
|
||
return null;
|
||
}
|
||
|
||
// 用 UnderlyingInstrumentType 推 Multiplier(债券=100,否则1)
|
||
bool isBond = ConsGlobal.InstrumentType.IsBond(floatLeg.UnderlyingInstrumentType);
|
||
int multiplier = isBond ? 100 : 1;
|
||
|
||
// 分类利息腿/预付金腿(InterestMode 初始预付金/追加预付金→Margin,否则→Interest)
|
||
var input = new UnwindInput
|
||
{
|
||
Multiplier = multiplier,
|
||
PosiGrossPrice = floatLeg.PosiGrossPrice, // EntryDirtyPrice
|
||
TradingAmountAvg = floatLeg.TradingAmountAvg, // ExitDirtyPrice(界面×multiplier形态)
|
||
CloseQty = unwindData.CloseQty,
|
||
PositionQty = unwindData.PositionQty,
|
||
ContractSize = floatLeg.ContractSize,
|
||
CloseNotionalValue = unwindData.CloseNotionalValue,
|
||
PayDirection = floatLeg.PayDirection,
|
||
PositionType = floatLeg.PositionType,
|
||
TradingFee = floatLeg.TradingFee.ToString(),
|
||
TradingFeePending = floatLeg.TradingFeePending.ToString(),
|
||
DividendIn = floatLeg.DividendIn.ToString(),
|
||
};
|
||
foreach (var leg in unwindData.FlowEvents.Where(x => string.IsNullOrEmpty(x.UnderlyingCode)))
|
||
{
|
||
var target = (leg.InterestMode == (int)InterestModeEnum.初始预付金
|
||
|| leg.InterestMode == (int)InterestModeEnum.追加预付金)
|
||
? input.MarginLegs : input.InterestLegs;
|
||
target.Add(new LegInput { InterestClosePnL = leg.InterestClosePnL });
|
||
}
|
||
|
||
var recalc = isIncome
|
||
? FrontendCalcReference.CalcIncome(input)
|
||
: FrontendCalcReference.CalcUnwind(input);
|
||
|
||
var diffs = new List<FrontendPnlDiff>(3);
|
||
AddDiffIfOverThreshold(diffs, nameof(recalc.SwapRealizedPnL), unwindData.SwapRealizedPnL, recalc.SwapRealizedPnL, threshold);
|
||
AddDiffIfOverThreshold(diffs, nameof(recalc.SwapCloseAmount), unwindData.SwapCloseAmount, recalc.SwapCloseAmount, threshold);
|
||
AddDiffIfOverThreshold(diffs, "MarkClosePnl", floatLeg.MarkClosePnl, recalc.MarkClosePnl, threshold);
|
||
return diffs;
|
||
}
|
||
|
||
private static void AddDiffIfOverThreshold(
|
||
List<FrontendPnlDiff> diffs, string field, decimal frontendVal, decimal backendVal, decimal threshold)
|
||
{
|
||
decimal diff = frontendVal - backendVal;
|
||
if (Math.Abs(diff) > threshold)
|
||
{
|
||
diffs.Add(new FrontendPnlDiff
|
||
{
|
||
Field = field,
|
||
FrontendValue = frontendVal,
|
||
BackendValue = backendVal
|
||
});
|
||
}
|
||
}
|
||
}
|
||
}
|