blob: a3e91885fe2ef834bdda34fc06ac107c81508390 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
|
namespace FundLab.Domain
open System
module RedemptionPolicy =
let unitsMaximum = 99999999999999999999.99999999m
let cashMaximum = 999999999999999999.99m
let roundDown (scale: int) (value: decimal) : decimal =
let factor = decimal (pown 10 scale)
Decimal.Truncate(value * factor) / factor
let validateRequest
(availableUnits: decimal)
(requestedUnits: decimal)
(feeAmount: decimal)
: Result<unit, string> =
if requestedUnits <= 0m then
Error "redemption units must be positive"
elif requestedUnits > unitsMaximum then
Error "redemption units exceed supported precision"
elif requestedUnits > availableUnits then
Error "requested units exceed available holdings"
elif feeAmount < 0m then
Error "redemption fee cannot be negative"
elif feeAmount > cashMaximum then
Error "redemption fee exceeds supported precision"
else
Ok ()
type RedemptionComputation =
{
GrossCash: decimal
Fee: decimal
Proceeds: decimal
RedeemedCost: decimal
}
let compute
(requestedUnits: decimal)
(unitNav: decimal)
(feeAmount: decimal)
(positionCost: decimal)
(positionUnits: decimal)
: Result<RedemptionComputation, string> =
if unitNav <= 0m then
Error "unit nav must be positive"
elif unitNav < 0.00000001m then
Error "unit nav is below database precision"
elif requestedUnits <= 0m then
Error "redemption units must be positive"
elif positionUnits <= 0m then
Error "position units must be positive"
elif requestedUnits > positionUnits then
Error "redemption units exceed position units"
elif feeAmount < 0m then
Error "redemption fee cannot be negative"
else
let gross = requestedUnits * unitNav
let grossRounded = roundDown 2 gross
if grossRounded <= 0m then
Error "redemption cash rounds to zero"
elif feeAmount > grossRounded then
Error "redemption fee exceeds redemption value"
else
let proceeds = grossRounded - feeAmount
if proceeds <= 0m then
Error "redemption proceeds rounds to zero"
else
let redeemedCost =
if requestedUnits = positionUnits then
positionCost
else
roundDown 2 (positionCost * requestedUnits / positionUnits)
Ok
{
GrossCash = grossRounded
Fee = feeAmount
Proceeds = proceeds
RedeemedCost = redeemedCost
}
|