summaryrefslogtreecommitdiff
path: root/tests/FundLab.Domain.Tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests/FundLab.Domain.Tests')
-rw-r--r--tests/FundLab.Domain.Tests/BondRulesTests.fs70
-rw-r--r--tests/FundLab.Domain.Tests/FundLab.Domain.Tests.fsproj1
2 files changed, 71 insertions, 0 deletions
diff --git a/tests/FundLab.Domain.Tests/BondRulesTests.fs b/tests/FundLab.Domain.Tests/BondRulesTests.fs
new file mode 100644
index 0000000..31069f1
--- /dev/null
+++ b/tests/FundLab.Domain.Tests/BondRulesTests.fs
@@ -0,0 +1,70 @@
+namespace FundLab.Domain.Tests
+
+module BondRulesTests =
+
+ open System
+ open Xunit
+ open FundLab.Domain
+
+ let private annualCb =
+ BondTerms.create
+ 100m
+ 0.02m
+ 1
+ (DateOnly(2020, 10, 15))
+ (DateOnly(2026, 10, 15))
+ 10m
+ 0
+ 0m
+
+ [<Fact>]
+ let ``coupon schedule runs from value date through maturity inclusive`` () =
+ let schedule = BondRules.couponSchedule annualCb
+
+ Assert.Equal(7, List.length schedule)
+ Assert.Equal(DateOnly(2020, 10, 15), List.head schedule)
+ Assert.Equal(DateOnly(2026, 10, 15), List.last schedule)
+ Assert.Equal(DateOnly(2025, 10, 15), List.item 5 schedule)
+
+ [<Fact>]
+ let ``accrued interest uses actual over actual days in the current period`` () =
+ // Period 2025-10-15 .. 2026-10-15 is 365 days; 2026-04-15 is 182 days in.
+ let accrued = BondRules.accruedInterest annualCb (DateOnly(2026, 4, 15))
+ Assert.Equal(0.997260m, accrued)
+
+ [<Fact>]
+ let ``accrued interest is zero before the value date and after maturity`` () =
+ Assert.Equal(0m, BondRules.accruedInterest annualCb (DateOnly(2020, 10, 14)))
+ Assert.Equal(0m, BondRules.accruedInterest annualCb (DateOnly(2027, 1, 1)))
+
+ [<Fact>]
+ let ``dirty price is clean price plus accrued interest`` () =
+ Assert.Equal(106.368m, BondRules.dirtyPrice 106.368m 0m)
+ Assert.Equal(107.368m, BondRules.dirtyPrice 106.368m 1.0m)
+
+ [<Fact>]
+ let ``trade cost is quantity times price for a 100 par bond`` () =
+ // 10 张 * 106.368 = 1063.68, matching the existing bond-trade contract.
+ Assert.Equal(1063.68m, BondRules.tradeCost annualCb 10m 106.368m)
+
+ [<Fact>]
+ let ``trade cost applies the instrument fee rate`` () =
+ let terms = { annualCb with FeeRate = 0.0002m }
+ // 1063.68 gross + 0.212736 -> 0.21 fee.
+ Assert.Equal(1063.89m, BondRules.tradeCost terms 10m 106.368m)
+
+ [<Fact>]
+ let ``settlement date is T+0 for exchange bonds and skips weekends otherwise`` () =
+ Assert.Equal(DateOnly(2026, 9, 18), BondRules.settlementDate annualCb (DateOnly(2026, 9, 18)))
+ let t1 = { annualCb with SettlementDays = 1 }
+ Assert.Equal(DateOnly(2026, 9, 21), BondRules.settlementDate t1 (DateOnly(2026, 9, 18)))
+
+ [<Fact>]
+ let ``quantity must be positive and a whole multiple of the minimum unit`` () =
+ match BondRules.validateQuantity annualCb 10m with
+ | Ok value -> Assert.Equal(10m, value)
+ | Error message -> failwith message
+
+ Assert.True(BondRules.validateQuantity annualCb 15m |> Result.isError)
+ Assert.True(BondRules.validateQuantity annualCb 0m |> Result.isError)
+ Assert.True(BondRules.validateQuantity annualCb -10m |> Result.isError)
diff --git a/tests/FundLab.Domain.Tests/FundLab.Domain.Tests.fsproj b/tests/FundLab.Domain.Tests/FundLab.Domain.Tests.fsproj
index a9c6fc0..468ee16 100644
--- a/tests/FundLab.Domain.Tests/FundLab.Domain.Tests.fsproj
+++ b/tests/FundLab.Domain.Tests/FundLab.Domain.Tests.fsproj
@@ -21,6 +21,7 @@
<ItemGroup>
<Compile Include="DomainTests.fs" />
<Compile Include="InvestmentPlanTests.fs" />
+ <Compile Include="BondRulesTests.fs" />
<Compile Include="Program.fs" />
</ItemGroup>
</Project>