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
{
///
/// 单条前后端盈亏差异(纯数据,便于单测断言)。
/// 原内嵌于 SwapDealService,因 SwapDealService 已属超大文件(2000+ 行),
/// 将其与本校验逻辑一并抽离,降低对超大文件的改动面。
///
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;
}
///
/// 前端盈亏只读校验:用 FrontendCalcReference 公式重算盈亏,与前端传来的 unwindData 逐字段比对。
/// 纯函数(无副作用、无 DB/日志依赖),便于无库单测(见 SwapFrontendPnlValidateTest)。
/// 返回 null 表示前置条件不满足(无浮动腿或 PosiGrossPrice=0),调用方应跳过。
///
/// 从 SwapDealService.ValidateFrontendPnL 抽出,原方法仅保留调用 + 日志。
///
public static class SwapFrontendPnlValidator
{
/// 前端算好传入的结算数据
/// true=结息页(income公式),false=平仓页(unwind公式)
/// 差异阈值,默认 0.01
public static List? 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(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 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
});
}
}
}
}