refactor(return-leg): 新增 DividendCalc 分红增值税后计算 + 修复 MtmCalc 参数类型

DividendCalc: 增值税后票息 = payment/(1+tax)*(1-tax),
原 SwapEodPositionService 4处重复此公式(1804/1894/1906/2095)。
本次先建纯函数+测试, 4处inline替换下次做。

附带修复: MtmCalc shortRatio 参数从 decimal 改为 int
(原代码6处声明中5处是int, 仅1696是decimal, 统一为int)。
1696行 shortRatio 声明同步改为 int。

验证: 编译0错误, DividendCalc 4个测试全过,
全量507测试7失败(基线一致)。
This commit is contained in:
hjhan
2026-08-11 11:04:08 +08:00
parent 946cbbb66d
commit 963f7a04e5
4 changed files with 59 additions and 2 deletions
@@ -0,0 +1,37 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using YLErp.Modules.SwapModule.ReturnLegs;
namespace UnitTestProject.Modules.SwapModule.ReturnLegs
{
[TestClass]
public class DividendCalcTest
{
[TestMethod]
public void AfterTax_零税率_等于原值()
{
Assert.AreEqual(1000m, DividendCalc.AfterTax(1000m, 0m));
}
[TestMethod]
public void AfterTax_6pct增值税()
{
// 1000 / 1.06 * 0.94 = 886.79...
Assert.AreEqual(886.79m, DividendCalc.AfterTax(1000m, 0.06m));
}
[TestMethod]
public void AfterTax_负票息()
{
// -500 / 1.06 * 0.94 = -443.40
Assert.AreEqual(-443.40m, DividendCalc.AfterTax(-500m, 0.06m));
}
[TestMethod]
public void AfterTaxRaw_不四舍五入()
{
var raw = DividendCalc.AfterTaxRaw(1000m, 0.06m);
Assert.AreNotEqual(886.79m, raw, "Raw 版本不四舍五入");
Assert.IsTrue(raw > 886.79m && raw < 886.80m);
}
}
}
@@ -0,0 +1,18 @@
namespace YLErp.Modules.SwapModule.ReturnLegs;
/// <summary>
/// 标的端分红(票息)计算。
///
/// 增值税后票息 = 税前票息 / (1+税率) × (1-税率)
/// 原代码在 SwapEodPositionService 4处重复此公式(1804/1894/1906/2095)。
/// </summary>
public static class DividendCalc
{
/// <summary>增值税后票息,四舍五入到 2 位。</summary>
public static decimal AfterTax(decimal payment, decimal tax)
=> Math.Round(payment / (1 + tax) * (1 - tax), 2);
/// <summary>增值税后票息(不四舍五入,供中间计算用)。</summary>
public static decimal AfterTaxRaw(decimal payment, decimal tax)
=> payment / (1 + tax) * (1 - tax);
}
@@ -10,11 +10,13 @@ namespace YLErp.Modules.SwapModule.ReturnLegs;
/// </summary>
public static class MtmCalc
{
/// <summary>标的市值。多头为正、空头为负。</summary>
/// <summary>标的市值。多头为正、空头为负。shortRatio: 多头=1, 空头=-1。</summary>
public static decimal MarketValue(decimal price, decimal qty, decimal contractSize, int shortRatio)
=> price * qty * contractSize * shortRatio;
/// <summary>盯市未实现盈亏 = (标的价 - 成本全价) × 数量 × 合约乘数 × 多空 × 收付。</summary>
/// <param name="shortRatio">多头=1, 空头=-1。</param>
/// <param name="ratio">收取=1, 支付=-1。</param>
public static decimal UnrealizedPnl(decimal price, decimal costGrossPrice, decimal qty, decimal contractSize, int shortRatio, decimal ratio)
=> (price - costGrossPrice) * qty * contractSize * shortRatio * ratio;
}
@@ -1693,7 +1693,7 @@ namespace YLErp.Modules.SwapModule
{
payQty = Math.Abs(payQty);
decimal ratio = eventFlow.PayDirection == (int)SwapDirectionEnum. ? 1 : -1;//收取为正,支付为负
decimal shortRatio = newEodPayPosition.PositionType == (int)PositionTypeFlag.Long ? 1 : -1;//多空方向
int shortRatio = newEodPayPosition.PositionType == (int)PositionTypeFlag.Long ? 1 : -1;//多空方向
newEodPayPosition.ValueDate = eventFlow.PayDate.Value;
newEodPayPosition.PositionId = eventFlow.PositionId;
newEodPayPosition.ClientId = td.ClientId;