docs(swap): 排查SQL新增第2.5步-分红重复计算完整证明

4段查询还原证据链(含事件/价格/费用/付息明细):
段1事件拆解 MarkClosePnl=价差+费+分红;段2 EOD逐日盯市vs分红列;
段3累计对照;段4一句话结论。已连库实测:1875重复302400、1891重复-18.66。

修正段1列名: PosiNetPrice/PosiGrossPrice 在swap_flow_event是[NotMapped]
不入库,改用 TradingAmountNetAvg/TradingAmountNetFeeAvg。
This commit is contained in:
hjhan
2026-06-26 09:55:19 +08:00
parent dd305444dd
commit ee1e8487d7
@@ -1,3 +1,21 @@
SELECT sfe.SwapTradeId,
MAX(sfe.SwapTradeNo) AS SwapTradeNo,
SUM(sfe.DividendIn) AS ,
SUM(sfe.MarkClosePnl) AS ,
-- 关键:必须有 eod 归档才算"走完EOD的有效样本"
(SELECT COUNT(*) FROM eod_swap_position esp
WHERE esp.SwapTradeId = sfe.SwapTradeId) AS eod持仓快照数,
(SELECT COUNT(*) FROM eod_swap es
WHERE es.SwapTradeId = sfe.SwapTradeId) AS eod汇总数
FROM swap_flow_event sfe
WHERE sfe.EventType IN (3,4) AND sfe.DividendIn <> 0 AND sfe.DataState = 100
GROUP BY sfe.SwapTradeId
HAVING eod持仓快照数 > 0 AND eod汇总数 > 0 -- ← 这道过滤保证种子完整
ORDER BY ABS(SUM(sfe.DividendIn)) DESC, MAX(sfe.SwapTradeNo) DESC;
-- ============================================================================
-- 互换分红损益:数据导出与重复计算验证 SQL 工具箱
-- ----------------------------------------------------------------------------
@@ -235,6 +253,120 @@ WHERE sfe.SwapTradeId = @TargetTradeId
ORDER BY sfe.EventDate;
-- ============================================================================
-- 第 2.5 步:分红被计算两次的完整证明(含事件/价格/费用/付息明细)
-- ============================================================================
-- 本节用 4 个查询把"分红重复计算"的证据链完整还原:
-- 段1事件明细:每条平仓/互换事件,把 MarkClosePnl 拆成 价差+费+分红 三成分。
-- 若"价差"残差~0 且 DividendIn<>0,证明 MarkClosePnl 里装的就是分红。
-- 段2 EOD逐日:盯市列(TdCloseMtmPnl) 与 分红列(TdCloseDividend) 同日出现等额分红。
-- 段3 累计对照:盯市列累计 RealizedMtmPnL 含了分红成分,分红列累计 RealizedDividend 又含一份。
-- 段4 一句话结论:重复金额 = 盯市列里的分红成分(按 PositionId 汇总避免双向腿抵消)。
--
-- 已用 golden 数据(1875纯分红型重复302400、1891混合型重复-18.66)校验本 SQL 结果正确。
SET @TargetTradeId := 1875; -- 替换为实际样本(1875=纯分红型 / 1891=混合型)
-- 段1 事件明细拆解:MarkClosePnl = 价差 + 费(CloseFee) + 分红(DividendIn)
-- 价差残差 = MarkClosePnl - DividendIn - CloseFee
-- 纯分红型:价差残差~0MarkClosePnl 全是分红)
-- 混合型 :价差残差<>0MarkClosePnl 含真实价差 + 少量分红)
SELECT sfe.id,
sfe.EventDate,
CASE sfe.EventType WHEN 1 THEN '开仓' WHEN 2 THEN '平仓'
WHEN 3 THEN '互换' WHEN 4 THEN '自动互换' END AS ,
sfe.PositionId,
sfe.UnderlyingCode,
sfe.Quantity,
sfe.TradingAmountAvg AS ,
sfe.TradingAmountFeeAvg AS ,
sfe.TradingAmountNetAvg AS ,
sfe.TradingAmountNetFeeAvg AS ,
sfe.TradingAmount AS ,
sfe.TradingFee AS ,
sfe.TradingFeePending AS ,
sfe.CloseFee AS _费成分,
sfe.DividendPending AS ,
sfe.DividendIn AS _分红成分,
sfe.MarkClosePnl AS _总额,
(sfe.MarkClosePnl - sfe.DividendIn - sfe.CloseFee) AS ,
CASE WHEN sfe.EventType IN (2,3,4)
AND ABS(sfe.MarkClosePnl - sfe.DividendIn - sfe.CloseFee) < 0.01
AND sfe.DividendIn <> 0
THEN '根因:价差~0->MarkClosePnl全是分红'
WHEN sfe.EventType IN (2,3,4) AND sfe.DividendIn <> 0
THEN '混合:价差+分红(分红成分进盯市列)'
ELSE '' END AS
FROM swap_flow_event sfe
WHERE sfe.SwapTradeId = @TargetTradeId
AND sfe.DataState = 100
AND sfe.EventType IN (2,3,4)
ORDER BY sfe.PositionId, sfe.EventDate, sfe.EventType;
-- 段2 EOD逐日:盯市列 vs 分红列(同日同腿,分红同时出现在两列)
SELECT esp.ValueDate,
esp.PositionId,
esp.TdCloseQty,
esp.UnderlyingPrice AS ,
esp.PosiGrossPrice AS ,
esp.TdCloseMtmPnl AS ,
esp.RealizedMtmPnL AS ,
esp.TdCloseDividend AS ,
esp.RealizedDividend AS ,
esp.TdCloseFee AS ,
esp.TdCloseInterest AS ,
esp.RealizedPnl AS ,
CASE WHEN esp.TdCloseMtmPnl <> 0 AND esp.TdCloseDividend <> 0
THEN CONCAT('分红', esp.TdCloseDividend, '同时进盯市列和分红列')
ELSE '' END AS
FROM eod_swap_position esp
WHERE esp.SwapTradeId = @TargetTradeId
AND (esp.TdCloseMtmPnl <> 0 OR esp.TdCloseDividend <> 0)
ORDER BY esp.PositionId, esp.ValueDate;
-- 段3 累计对照:按 PositionId 取最终累计,算出盯市列里的分红成分
-- 盯市列里的分红成分 = 该腿所有平仓/互换事件的 DividendIn 之和
SELECT fin.PositionId,
fin.RealizedMtmPnL AS ,
fin.RealizedDividend AS ,
fin.RealizedFee AS ,
fin.RealizedInterest AS ,
fin.RealizedPnl AS ,
dv.,
(fin.RealizedMtmPnL - dv. - fin.RealizedFee) AS
FROM (SELECT PositionId, RealizedMtmPnL, RealizedDividend, RealizedFee, RealizedInterest, RealizedPnl
FROM eod_swap_position esp
WHERE esp.SwapTradeId = @TargetTradeId
AND esp.id IN (SELECT MAX(id) FROM eod_swap_position
WHERE SwapTradeId = @TargetTradeId GROUP BY PositionId)
) fin
LEFT JOIN (SELECT PositionId, SUM(DividendIn) AS
FROM swap_flow_event
WHERE SwapTradeId = @TargetTradeId AND DataState = 100 AND EventType IN (2,3,4)
GROUP BY PositionId) dv ON dv.PositionId = fin.PositionId
ORDER BY fin.PositionId;
-- 段4 一句话结论:重复金额合计 = 盯市列里的分红成分之和
SELECT @TargetTradeId AS SwapTradeId,
SUM() AS ,
(SELECT SUM(RealizedPnl) FROM eod_swap_position
WHERE SwapTradeId = @TargetTradeId
AND id IN (SELECT MAX(id) FROM eod_swap_position
WHERE SwapTradeId = @TargetTradeId GROUP BY PositionId)) AS RealizedPnl合计,
CASE WHEN ABS(SUM()) > 0.01
THEN CONCAT('坐实重复计算: 分红 ', SUM(),
' 既在 RealizedMtmPnL(盯市列) 又在 RealizedDividend(分红列)',
', 修复后 RealizedPnl 应减少 ', SUM())
ELSE '未检测到重复(可能已修复或无分红平仓/互换事件)' END AS
FROM (SELECT PositionId, SUM(DividendIn) AS
FROM swap_flow_event
WHERE SwapTradeId = @TargetTradeId AND DataState = 100 AND EventType IN (2,3,4)
GROUP BY PositionId) t;
-- 预期结果:
-- 1875(纯分红型) -> 重复计入 302400.00MarkClosePnl 全是分红,价差=0
-- 1891(混合型) -> 重复计入 -18.66 MarkClosePnl 含价差395604 + 分红-18.66
-- ============================================================================
-- 第 3 步:生命周期守恒校验(累计已实现分红 vs 理论应付分红)
-- ============================================================================