-
Notifications
You must be signed in to change notification settings - Fork 158
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #359 from yozachar/workshop
feat: adds `finance` validator
- Loading branch information
Showing
6 changed files
with
208 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
# finance | ||
|
||
::: validators.finance.cusip | ||
::: validators.finance.isin | ||
::: validators.finance.sedol |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
finance | ||
------- | ||
|
||
.. module:: validators.finance | ||
.. autofunction:: cusip | ||
.. autofunction:: isin | ||
.. autofunction:: sedol |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
"""Finance.""" | ||
|
||
from .utils import validator | ||
|
||
|
||
def _cusip_checksum(cusip: str): | ||
check, val = 0, None | ||
|
||
for idx in range(9): | ||
c = cusip[idx] | ||
if c >= "0" and c <= "9": | ||
val = ord(c) - ord("0") | ||
elif c >= "A" and c <= "Z": | ||
val = 10 + ord(c) - ord("A") | ||
elif c >= "a" and c <= "z": | ||
val = 10 + ord(c) - ord("a") | ||
elif c == "*": | ||
val = 36 | ||
elif c == "@": | ||
val = 37 | ||
elif c == "#": | ||
val = 38 | ||
else: | ||
return False | ||
|
||
if idx & 1: | ||
val += val | ||
|
||
check = check + (val // 10) + (val % 10) | ||
|
||
return (check % 10) == 0 | ||
|
||
|
||
def _isin_checksum(value: str): | ||
check, val = 0, None | ||
|
||
for idx in range(12): | ||
c = value[idx] | ||
if c >= "0" and c <= "9" and idx > 1: | ||
val = ord(c) - ord("0") | ||
elif c >= "A" and c <= "Z": | ||
val = 10 + ord(c) - ord("A") | ||
elif c >= "a" and c <= "z": | ||
val = 10 + ord(c) - ord("a") | ||
else: | ||
return False | ||
|
||
if idx & 1: | ||
val += val | ||
|
||
return (check % 10) == 0 | ||
|
||
|
||
@validator | ||
def cusip(value: str): | ||
"""Return whether or not given value is a valid CUSIP. | ||
Checks if the value is a valid [CUSIP][1]. | ||
[1]: https://en.wikipedia.org/wiki/CUSIP | ||
Examples: | ||
>>> cusip('037833DP2') | ||
True | ||
>>> cusip('037833DP3') | ||
ValidationFailure(func=cusip, ...) | ||
Args: | ||
value: CUSIP string to validate. | ||
Returns: | ||
(Literal[True]): If `value` is a valid CUSIP string. | ||
(ValidationError): If `value` is an invalid CUSIP string. | ||
""" | ||
return len(value) == 9 and _cusip_checksum(value) | ||
|
||
|
||
@validator | ||
def isin(value: str): | ||
"""Return whether or not given value is a valid ISIN. | ||
Checks if the value is a valid [ISIN][1]. | ||
[1]: https://en.wikipedia.org/wiki/International_Securities_Identification_Number | ||
Examples: | ||
>>> isin('037833DP2') | ||
True | ||
>>> isin('037833DP3') | ||
ValidationFailure(func=isin, ...) | ||
Args: | ||
value: ISIN string to validate. | ||
Returns: | ||
(Literal[True]): If `value` is a valid ISIN string. | ||
(ValidationError): If `value` is an invalid ISIN string. | ||
""" | ||
return len(value) == 12 and _isin_checksum(value) | ||
|
||
|
||
@validator | ||
def sedol(value: str): | ||
"""Return whether or not given value is a valid SEDOL. | ||
Checks if the value is a valid [SEDOL][1]. | ||
[1]: https://en.wikipedia.org/wiki/SEDOL | ||
Examples: | ||
>>> sedol('2936921') | ||
True | ||
>>> sedol('29A6922') | ||
ValidationFailure(func=sedol, ...) | ||
Args: | ||
value: SEDOL string to validate. | ||
Returns: | ||
(Literal[True]): If `value` is a valid SEDOL string. | ||
(ValidationError): If `value` is an invalid SEDOL string. | ||
""" | ||
if len(value) != 7: | ||
return False | ||
|
||
weights = [1, 3, 1, 7, 3, 9, 1] | ||
check = 0 | ||
for idx in range(7): | ||
c = value[idx] | ||
if c in "AEIOU": | ||
return False | ||
|
||
val = None | ||
if c >= "0" and c <= "9": | ||
val = ord(c) - ord("0") | ||
elif c >= "A" and c <= "Z": | ||
val = 10 + ord(c) - ord("A") | ||
else: | ||
return False | ||
check += val * weights[idx] | ||
|
||
return (check % 10) == 0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
"""Test Finance.""" | ||
|
||
# external | ||
import pytest | ||
|
||
# local | ||
from validators import ValidationError, cusip, isin, sedol | ||
|
||
# ==> CUSIP <== # | ||
|
||
|
||
@pytest.mark.parametrize("value", ["912796X38", "912796X20", "912796x20"]) | ||
def test_returns_true_on_valid_cusip(value: str): | ||
"""Test returns true on valid cusip.""" | ||
assert cusip(value) | ||
|
||
|
||
@pytest.mark.parametrize("value", ["912796T67", "912796T68", "XCVF", "00^^^1234"]) | ||
def test_returns_failed_validation_on_invalid_cusip(value: str): | ||
"""Test returns failed validation on invalid cusip.""" | ||
assert isinstance(cusip(value), ValidationError) | ||
|
||
|
||
# ==> ISIN <== # | ||
|
||
|
||
@pytest.mark.parametrize("value", ["US0004026250", "JP000K0VF054", "US0378331005"]) | ||
def test_returns_true_on_valid_isin(value: str): | ||
"""Test returns true on valid isin.""" | ||
assert isin(value) | ||
|
||
|
||
@pytest.mark.parametrize("value", ["010378331005" "XCVF", "00^^^1234", "A000009"]) | ||
def test_returns_failed_validation_on_invalid_isin(value: str): | ||
"""Test returns failed validation on invalid isin.""" | ||
assert isinstance(isin(value), ValidationError) | ||
|
||
|
||
# ==> SEDOL <== # | ||
|
||
|
||
@pytest.mark.parametrize("value", ["0263494", "0540528", "B000009"]) | ||
def test_returns_true_on_valid_sedol(value: str): | ||
"""Test returns true on valid sedol.""" | ||
assert sedol(value) | ||
|
||
|
||
@pytest.mark.parametrize("value", ["0540526", "XCVF", "00^^^1234", "A000009"]) | ||
def test_returns_failed_validation_on_invalid_sedol(value: str): | ||
"""Test returns failed validation on invalid sedol.""" | ||
assert isinstance(sedol(value), ValidationError) |