fix(swap): 修复复利计算和部分平仓处理中的多个问题(算头算尾合约复利平仓)
- 修复合约名义本金规模模式下部分平仓时名义本金计算错误 - 修复最终全平时复利利息计算中历史平仓尾差处理问题 - 修复复利重置日处理中利息重复注入历史本金的错误 - 修复部分平仓后日终待实现利息计算中本金比例应用问题 - 修复全平且实际金额覆盖应结利息后待实现利息清零逻辑 - 新增复合复利交换服务用于单元测试验证 - 添加多个测试用例覆盖复利计算边界场景
This commit is contained in:
@@ -1295,10 +1295,14 @@ namespace YLErp.Modules.SwapModule
|
||||
// 首次日终结算可能包含当日收盘,因此尚无先前的日终利息持仓。
|
||||
// 部分平仓仍要续接上一日日终:CalcUnwindInterest 会将 InterestProfitSum
|
||||
// 加入本次待实现,已实现字段也必须按日累计,不能从新建的临时对象重新开始。
|
||||
var hasPreviousEod = eodPayPosition != null && eodPayPosition.id != 0;
|
||||
var lastInterestIncomeSum = eodPayPosition?.InterestIncomeSum ?? 0m;
|
||||
var lastInterestFeeSum = eodPayPosition?.InterestFeeSum ?? 0m;
|
||||
var lastRealizedInterest = eodPayPosition?.RealizedInterest ?? 0m;
|
||||
var lastRealizedInterestFee = eodPayPosition?.RealizedInterestFee ?? 0m;
|
||||
// 先保留平仓前的复利本金;后面 interests.First().InterestPrincipal 是本次已平部分,
|
||||
// 不能用它代表平仓前全额本金计算当日总利息。
|
||||
var lastTdInterestPrincipal = eodPayPosition?.TdInterestPrincipal ?? 0m;
|
||||
// 保留上一日日终标识和计息上下文,部分平仓只从 ValueDate 之后续算,不能重置到交易起始日。
|
||||
eodPayPosition = eodPayPosition?.Clone() ?? new eod_swap_position();
|
||||
eodPayPosition.ClientId = td.ClientId;
|
||||
@@ -1401,28 +1405,55 @@ namespace YLErp.Modules.SwapModule
|
||||
{
|
||||
intersetAcmount /= tradeExtend.AnnualDays;
|
||||
}
|
||||
newEodPayPosition.TdInterestIncome = !autoSwap && calcLast
|
||||
? TdInterestAmount - lastInterestIncomeSum
|
||||
: intersetAcmount;
|
||||
newEodPayPosition.TdInterestIncome = autoSwap
|
||||
? intersetAcmount
|
||||
: !hasPreviousEod
|
||||
? interestAmountBeforeSettlement
|
||||
: posiNotionalValue == 0m
|
||||
? interestAmountBeforeSettlement - lastInterestIncomeSum
|
||||
: lastRealizedInterest != 0m || lastRealizedInterestFee != 0m || !calcLast
|
||||
? intersetAcmount
|
||||
: TdInterestAmount - lastInterestIncomeSum;
|
||||
if (!autoSwap
|
||||
&& calcLast
|
||||
&& closePercent > 0m && closePercent < 1m
|
||||
&& posiNotionalValue > 0m
|
||||
&& position.InterestType == (int)InterestTypeEnum.复利
|
||||
&& position.InterestMode == (int)InterestModeEnum.合约名义本金规模)
|
||||
{
|
||||
// 算尾的复利部分平仓:平仓金额只结算“上日待实现 * 平仓比例
|
||||
// + 已平本金当日利息”,但日终待实现必须按“上日待实现
|
||||
// + 平仓前全额本金当日利息 - 实际平仓结算”递推。
|
||||
// 通用路径的 intersetAcmount 此时基于已平本金:0007 只得到 30% 的
|
||||
// 4,088.64,会漏记剩余 70% 的 9,540.16;因此改用上日终全额复利本金,
|
||||
// 得到当日总利息 13,628.81,剩余部分才能继续参与后续复利。
|
||||
var fullPrincipal = lastTdInterestPrincipal > 0m
|
||||
? lastTdInterestPrincipal
|
||||
: oriPosiNotionalValue;
|
||||
newEodPayPosition.TdInterestIncome = fullPrincipal
|
||||
* (newEodPayPosition.TdInterestRate + newEodPayPosition.FloatRate);
|
||||
if (position.IsAnnualized)
|
||||
{
|
||||
newEodPayPosition.TdInterestIncome /= tradeExtend.AnnualDays;
|
||||
}
|
||||
}
|
||||
Log.Info($"InterestIncomeSum is {lastInterestIncomeSum},TdInterestIncome is {newEodPayPosition.TdInterestIncome}" +
|
||||
$",TdCloseInterest is {newEodPayPosition.TdCloseInterest}");
|
||||
Log.Info($"InterestFeeSum is {eodPayPosition.InterestFeeSum},TdInterestFee is {newEodPayPosition.TdInterestFee}" +
|
||||
$",TdCloseInterestFee is {newEodPayPosition.TdCloseInterestFee}");
|
||||
if (closePercent == 1)
|
||||
{
|
||||
// 全量平仓后不应把待实现利息或费用带入下一交易日。
|
||||
newEodPayPosition.InterestIncomeSum = 0;
|
||||
newEodPayPosition.InterestFeeSum = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
var pendingInterestBeforeSettlement = autoSwap
|
||||
? interestAmountBeforeSettlement
|
||||
: lastInterestIncomeSum + newEodPayPosition.TdInterestIncome;
|
||||
newEodPayPosition.InterestIncomeSum = RoundEodInterest(
|
||||
pendingInterestBeforeSettlement - newEodPayPosition.TdCloseInterest);
|
||||
newEodPayPosition.InterestFeeSum = eodPayPosition.InterestFeeSum + newEodPayPosition.TdInterestFee - newEodPayPosition.TdCloseInterestFee;
|
||||
}
|
||||
var pendingInterestBeforeSettlement = autoSwap
|
||||
? interestAmountBeforeSettlement
|
||||
: lastInterestIncomeSum + newEodPayPosition.TdInterestIncome;
|
||||
var pendingInterestFeeBeforeSettlement = eodPayPosition.InterestFeeSum
|
||||
+ newEodPayPosition.TdInterestFee;
|
||||
newEodPayPosition.InterestIncomeSum = closePercent == 1
|
||||
&& RoundMoney(pendingInterestBeforeSettlement) == RoundMoney(newEodPayPosition.TdCloseInterest)
|
||||
? 0m
|
||||
: RoundEodInterest(pendingInterestBeforeSettlement - newEodPayPosition.TdCloseInterest);
|
||||
newEodPayPosition.InterestFeeSum = closePercent == 1
|
||||
&& RoundMoney(pendingInterestFeeBeforeSettlement) == RoundMoney(newEodPayPosition.TdCloseInterestFee)
|
||||
? 0m
|
||||
: RoundEodInterest(pendingInterestFeeBeforeSettlement - newEodPayPosition.TdCloseInterestFee);
|
||||
//持仓内容-利息腿-损益统计(本方视角)
|
||||
newEodPayPosition.InterestProfitSum = newEodPayPosition.InterestIncomeSum + newEodPayPosition.InterestFeeSum;
|
||||
//持仓价值
|
||||
|
||||
Reference in New Issue
Block a user