fix: 兼容TRS日终推送历史利息腿类别为空
This commit is contained in:
@@ -142,6 +142,88 @@ namespace YLErp.Modules.EodModuleTests
|
||||
Assert.AreEqual(0m, item.InitMarginLoss);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void BuildContract_历史类别为空_仍推送全部TRS合约()
|
||||
{
|
||||
var valueDate = new DateTime(2026, 2, 10);
|
||||
var eodSwap = new eod_swap { ValueDate = valueDate, SwapTradeId = 1645, SwapTradeNo = "ZSZQ-IS-202602090001" };
|
||||
var positions = new List<eod_swap_position>
|
||||
{
|
||||
new() { SwapTradeId = 1645, PositionId = 33306, UnderlyingCode = "220208.IB", PositionType = 1 },
|
||||
new() { SwapTradeId = 1645, PositionId = 33307, InterestMode = (int)InterestModeEnum.标的期初全价, InterestRateDefault = 0.001m, InterestDirection = 1 }
|
||||
};
|
||||
var swapPositions = new Dictionary<long, swap_position>
|
||||
{
|
||||
[33306] = new() { id = 33306, category_tag = null },
|
||||
[33307] = new() { id = 33307, category_tag = null }
|
||||
};
|
||||
|
||||
var item = TrsContractKafkaPushService.BuildContract(
|
||||
eodSwap,
|
||||
new Dictionary<int, trade> { [1645] = new() { id = 1645, UnderlyingCode = "220208.IB" } },
|
||||
positions,
|
||||
swapPositions);
|
||||
|
||||
Assert.AreEqual(0m, item.FixedRate);
|
||||
Assert.AreEqual(1, item.InterestDirection);
|
||||
Assert.AreEqual(1, item.FloatingDirection);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void BuildContract_增强收益腿不参与固定利率取值()
|
||||
{
|
||||
var eodSwap = new eod_swap { ValueDate = new DateTime(2026, 8, 24), SwapTradeId = 7 };
|
||||
var positions = new List<eod_swap_position>
|
||||
{
|
||||
new() { SwapTradeId = 7, PositionId = 101, UnderlyingCode = "600000.SH", PositionType = 2 },
|
||||
new() { SwapTradeId = 7, PositionId = 102, InterestMode = (int)InterestModeEnum.固定值, InterestRateDefault = 0.0123m, InterestDirection = 1 },
|
||||
new() { SwapTradeId = 7, PositionId = 103, InterestMode = (int)InterestModeEnum.固定值, InterestRateDefault = 0.0999m, InterestDirection = 2 }
|
||||
};
|
||||
var swapPositions = new Dictionary<long, swap_position>
|
||||
{
|
||||
[101] = new() { id = 101, category_tag = null },
|
||||
[102] = new() { id = 102, category_tag = "互换利率" },
|
||||
[103] = new() { id = 103, category_tag = "增强收益" }
|
||||
};
|
||||
|
||||
var item = TrsContractKafkaPushService.BuildContract(
|
||||
eodSwap,
|
||||
new Dictionary<int, trade> { [7] = new() { id = 7, UnderlyingCode = "600000.SH" } },
|
||||
positions,
|
||||
swapPositions);
|
||||
|
||||
Assert.AreEqual(0.0123m, item.FixedRate);
|
||||
Assert.AreEqual(1, item.InterestDirection);
|
||||
Assert.AreEqual(2, item.FloatingDirection);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void BuildContract_多条互换利率腿_取第一条()
|
||||
{
|
||||
var eodSwap = new eod_swap { ValueDate = new DateTime(2026, 8, 24), SwapTradeId = 7 };
|
||||
var positions = new List<eod_swap_position>
|
||||
{
|
||||
new() { SwapTradeId = 7, PositionId = 101, UnderlyingCode = "600000.SH", PositionType = 1 },
|
||||
new() { SwapTradeId = 7, PositionId = 102, InterestMode = (int)InterestModeEnum.固定值, InterestRateDefault = 0.0123m, InterestDirection = 1 },
|
||||
new() { SwapTradeId = 7, PositionId = 103, InterestMode = (int)InterestModeEnum.固定值, InterestRateDefault = 0.0456m, InterestDirection = 2 }
|
||||
};
|
||||
var swapPositions = new Dictionary<long, swap_position>
|
||||
{
|
||||
[101] = new() { id = 101, category_tag = null },
|
||||
[102] = new() { id = 102, category_tag = "互换利率" },
|
||||
[103] = new() { id = 103, category_tag = "互换利率" }
|
||||
};
|
||||
|
||||
var item = TrsContractKafkaPushService.BuildContract(
|
||||
eodSwap,
|
||||
new Dictionary<int, trade> { [7] = new() { id = 7, UnderlyingCode = "600000.SH" } },
|
||||
positions,
|
||||
swapPositions);
|
||||
|
||||
Assert.AreEqual(0.0123m, item.FixedRate);
|
||||
Assert.AreEqual(1, item.InterestDirection);
|
||||
}
|
||||
|
||||
private static TestableTrsContractKafkaPushService CreateService(RecordingKafkaProducer producer, DateTime valueDate)
|
||||
{
|
||||
return new TestableTrsContractKafkaPushService(
|
||||
|
||||
@@ -140,7 +140,7 @@ namespace YLErp.Modules.EodModule
|
||||
.ToList();
|
||||
var positionIds = eodPositions.Select(x => x.PositionId).Distinct().ToList();
|
||||
var swapPositions = _dbContext.swap_position
|
||||
.Where(x => positionIds.Contains(x.id) && !x.Invalid && x.category_tag == InterestCategory)
|
||||
.Where(x => positionIds.Contains(x.id) && !x.Invalid)
|
||||
.AsNoTracking()
|
||||
.ToDictionary(x => x.id);
|
||||
|
||||
@@ -167,18 +167,27 @@ namespace YLErp.Modules.EodModule
|
||||
}
|
||||
|
||||
var positions = eodPositions.Where(x => x.SwapTradeId == eodSwap.SwapTradeId).ToList();
|
||||
var floating = positions.Where(x => !string.IsNullOrWhiteSpace(x.UnderlyingCode) && swapPositions.ContainsKey(x.PositionId)).ToList();
|
||||
var interest = positions.Where(x => string.IsNullOrWhiteSpace(x.UnderlyingCode)
|
||||
var floating = positions.Where(x => !string.IsNullOrWhiteSpace(x.UnderlyingCode)).ToList();
|
||||
var interestCandidates = positions.Where(x => string.IsNullOrWhiteSpace(x.UnderlyingCode)
|
||||
&& ConsTrade.InterestModels.Contains(x.InterestMode)
|
||||
&& swapPositions.TryGetValue(x.PositionId, out var swapPosition)
|
||||
&& swapPosition.category_tag == InterestCategory).ToList();
|
||||
&& (swapPosition.category_tag == InterestCategory || string.IsNullOrWhiteSpace(swapPosition.category_tag)))
|
||||
.ToList();
|
||||
|
||||
if (floating.Count != 1 || interest.Count != 1)
|
||||
// 互换利率腿优先;同类别多腿按当前查询顺序取第一条。历史类别为空时保留利息方向,
|
||||
// 但 fixedRate 按约定置 0,避免把未标注类别的历史值当作已确认利率。
|
||||
var interest = interestCandidates.FirstOrDefault(x =>
|
||||
swapPositions.TryGetValue(x.PositionId, out var swapPosition)
|
||||
&& swapPosition.category_tag == InterestCategory);
|
||||
var isUncategorizedInterest = interest == null && interestCandidates.Count > 0;
|
||||
interest ??= interestCandidates.FirstOrDefault();
|
||||
|
||||
if (floating.Count != 1 || interest == null)
|
||||
{
|
||||
throw new InvalidOperationException($"TRS legs invalid, swapTradeId:{eodSwap.SwapTradeId}, floating:{floating.Count}, interest:{interest.Count}");
|
||||
throw new InvalidOperationException($"TRS legs invalid, swapTradeId:{eodSwap.SwapTradeId}, floating:{floating.Count}, interest:{(interest == null ? 0 : 1)}");
|
||||
}
|
||||
|
||||
var interestLeg = interest[0];
|
||||
var interestLeg = interest;
|
||||
var floatingLeg = floating[0];
|
||||
return new TrsContractSnapshotItem
|
||||
{
|
||||
@@ -193,7 +202,7 @@ namespace YLErp.Modules.EodModule
|
||||
Dv01 = eodSwap.dv01 ?? 0,
|
||||
StartDate = trade.StartDate?.ToString(DateFormat),
|
||||
MaturityDate = trade.ExerciseDate?.ToString(DateFormat),
|
||||
FixedRate = interestLeg.InterestRateDefault,
|
||||
FixedRate = isUncategorizedInterest ? 0 : interestLeg.InterestRateDefault,
|
||||
InterestDirection = interestLeg.InterestDirection,
|
||||
FloatingDirection = floatingLeg.PositionType,
|
||||
InitMarginGain = eodSwap.InitMarginGain,
|
||||
|
||||
Reference in New Issue
Block a user