1"""Finance."""
2
3from .utils import validator
4
5
6def _cusip_checksum(cusip: str):
7 check, val = 0, None
8
9 for idx in range(9):
10 c = cusip[idx]
11 if c >= "0" and c <= "9":
12 val = ord(c) - ord("0")
13 elif c >= "A" and c <= "Z":
14 val = 10 + ord(c) - ord("A")
15 elif c >= "a" and c <= "z":
16 val = 10 + ord(c) - ord("a")
17 elif c == "*":
18 val = 36
19 elif c == "@":
20 val = 37
21 elif c == "#":
22 val = 38
23 else:
24 return False
25
26 if idx & 1:
27 val += val
28
29 check = check + (val // 10) + (val % 10)
30
31 return (check % 10) == 0
32
33
34def _isin_checksum(value: str):
35 check, val = 0, None
36
37 for idx in range(12):
38 c = value[idx]
39 if c >= "0" and c <= "9" and idx > 1:
40 val = ord(c) - ord("0")
41 elif c >= "A" and c <= "Z":
42 val = 10 + ord(c) - ord("A")
43 elif c >= "a" and c <= "z":
44 val = 10 + ord(c) - ord("a")
45 else:
46 return False
47
48 if idx & 1:
49 val += val
50
51 return (check % 10) == 0
52
53
54@validator
55def cusip(value: str):
56 """Return whether or not given value is a valid CUSIP.
57
58 Checks if the value is a valid [CUSIP][1].
59 [1]: https://en.wikipedia.org/wiki/CUSIP
60
61 Examples:
62 >>> cusip('037833DP2')
63 True
64 >>> cusip('037833DP3')
65 ValidationError(func=cusip, args={'value': '037833DP3'})
66
67 Args:
68 value: CUSIP string to validate.
69
70 Returns:
71 (Literal[True]): If `value` is a valid CUSIP string.
72 (ValidationError): If `value` is an invalid CUSIP string.
73 """
74 return len(value) == 9 and _cusip_checksum(value)
75
76
77@validator
78def isin(value: str):
79 """Return whether or not given value is a valid ISIN.
80
81 Checks if the value is a valid [ISIN][1].
82 [1]: https://en.wikipedia.org/wiki/International_Securities_Identification_Number
83
84 Examples:
85 >>> isin('037833DP2')
86 ValidationError(func=isin, args={'value': '037833DP2'})
87 >>> isin('037833DP3')
88 ValidationError(func=isin, args={'value': '037833DP3'})
89
90 Args:
91 value: ISIN string to validate.
92
93 Returns:
94 (Literal[True]): If `value` is a valid ISIN string.
95 (ValidationError): If `value` is an invalid ISIN string.
96 """
97 return len(value) == 12 and _isin_checksum(value)
98
99
100@validator
101def sedol(value: str):
102 """Return whether or not given value is a valid SEDOL.
103
104 Checks if the value is a valid [SEDOL][1].
105 [1]: https://en.wikipedia.org/wiki/SEDOL
106
107 Examples:
108 >>> sedol('2936921')
109 True
110 >>> sedol('29A6922')
111 ValidationError(func=sedol, args={'value': '29A6922'})
112
113 Args:
114 value: SEDOL string to validate.
115
116 Returns:
117 (Literal[True]): If `value` is a valid SEDOL string.
118 (ValidationError): If `value` is an invalid SEDOL string.
119 """
120 if len(value) != 7:
121 return False
122
123 weights = [1, 3, 1, 7, 3, 9, 1]
124 check = 0
125 for idx in range(7):
126 c = value[idx]
127 if c in "AEIOU":
128 return False
129
130 val = None
131 if c >= "0" and c <= "9":
132 val = ord(c) - ord("0")
133 elif c >= "A" and c <= "Z":
134 val = 10 + ord(c) - ord("A")
135 else:
136 return False
137 check += val * weights[idx]
138
139 return (check % 10) == 0