How to check the International Securities Identification Number (ISIN)

If I'm not mistaken, the last ISIN position is a check digit. What is the mathematical function that determines its value in the function of the first 11 digits?

+8
source share
7 answers

http://en.wikipedia.org/wiki/International_Securities_Identification_Number

The ISIN check digit calculation procedure is similar to the “Modular 10 Double Add Double” technique used in CUSIP. To calculate the check digit, first convert any letters to numbers by adding their ordinal position in alphabetical order to 9, so that A = 10 and M = 22. Starting with the rightmost digit, each other digit is multiplied by two. (For CUSIP check digits, these two steps are reversed.) The resulting string of digits (digits greater than 9, turning into two separate digits) are added. Subtract this amount from the smallest number ending in zero, which is greater than or equal to it: this gives a check digit, which is also known as ten additions to the sum modulo 10. That is, the sum received, the digit is a multiple of 10.

They have a good example .

+7
source

Based on examples from others, here is a C # implementation that will check both ISIN and CUSIP (and possibly some other Luhn options).

Using:

foreach (var isin in ValidIsins) { var calculatedChecksum = SecuritiesValidation.CalculateChecksum(isin.Substring(0, 11)); var actualChecksum = (isin.Last() - '0'); Assert.AreEqual(calculatedChecksum, actualChecksum); } foreach (var cusip in ValidCusips) { var calculatedChecksum = SecuritiesValidation.CalculateChecksum(cusip.Substring(0, 8), true, true); var actualChecksum = (cusip.Last() - '0'); Assert.AreEqual(calculatedChecksum, actualChecksum); } 

Implementation:

 public static class SecuritiesValidation { public static int CalculateChecksum(IEnumerable<char> codeWithoutChecksum, bool reverseLuhn = false, bool allowSymbols = false) { return reverseLuhn ? codeWithoutChecksum .Select((c, i) => c.OrdinalPosition(allowSymbols).ConditionalMultiplyByTwo(i.IsOdd()).SumDigits()) .Sum() .TensComplement() : codeWithoutChecksum .ToArray() .ToDigits(allowSymbols) .Select((d, i) => d.ConditionalMultiplyByTwo(i.IsEven()).SumDigits()) .Sum() .TensComplement(); } public static bool IsChecksumCorrect(string code, bool reverseLuhn = false, bool allowSymbols = false) { try { var checksum = code.Last().ToInt(); return checksum == CalculateChecksum(code.Take(code.Length - 1), reverseLuhn, allowSymbols); } catch { return false; } } /* Be careful here. This method is probably inapropriate for anything other than its designed purpose of Luhn-algorithm based validation. * Specifically: * - numbers are assigned a value equal to the number ('0' == 0, '1' == 1). * - letters are assigned a value indicating the number 9 plus the letters ordinal position in the English alphabet ('A' == 10, 'B' == 11). * - if symbols are allowed (eg: for CUSIP validation), they are assigned values beginning from 36 ('*' == 36, '@' == 37). */ private static int OrdinalPosition(this char c, bool allowSymbols = false) { if (char.IsLower(c)) return char.ToUpper(c) - 'A' + 10; if (char.IsUpper(c)) return c - 'A' + 10; if (char.IsDigit(c)) return c.ToInt(); if (allowSymbols) switch (c) { case '*': return 36; case '@': return 37; case '#': return 38; } throw new ArgumentOutOfRangeException("Specified character is not a letter, digit or allowed symbol."); } private static bool IsEven(this int x) { return (x % 2 == 0); } private static bool IsOdd(this int x) { return !IsEven(x); } private static int ToInt(this char digit) { if (char.IsDigit(digit)) return digit - '0'; throw new ArgumentOutOfRangeException("Specified character is not a digit."); } private static IEnumerable<int> ToDigits(this char[] s, bool allowSymbols = false) { var digits = new List<int>(); for (var i = s.Length - 1; i >= 0; i--) { var ordinalPosition = s[i].OrdinalPosition(allowSymbols); digits.Add(ordinalPosition % 10); if (ordinalPosition > 9) digits.Add(ordinalPosition / 10); } return digits; } private static int SumDigits(this int value) { //return value > 9 ? ((value / 10) + (value % 10)) : value; return ((value / 10) + (value % 10)); } private static int ConditionalMultiplyByTwo(this int value, bool condition) { return condition ? value * 2 : value; } private static int TensComplement(this int value) { return (10 - (value % 10)) % 10; } } 

It probably makes sense to use a checksum check in conjunction with a regular expression pattern match. This is the regex that I use:

ISIN: ^(XS|AD|AE|AF|AG|AI|AL|AM|AO|AQ|AR|AS|AT|AU|AW|AX|AZ|BA|BB|BD|BE|BF|BG|BH|BI|BJ|BL|BM|BN|BO|BQ|BR|BS|BT|BV|BW|BY|BZ|CA|CC|CD|CF|CG|CH|CI|CK|CL|CM|CN|CO|CR|CU|CV|CW|CX|CY|CZ|DE|DJ|DK|DM|DO|DZ|EC|EE|EG|EH|ER|ES|ET|FI|FJ|FK|FM|FO|FR|GA|GB|GD|GE|GF|GG|GH|GI|GL|GM|GN|GP|GQ|GR|GS|GT|GU|GW|GY|HK|HM|HN|HR|HT|HU|ID|IE|IL|IM|IN|IO|IQ|IR|IS|IT|JE|JM|JO|JP|KE|KG|KH|KI|KM|KN|KP|KR|KW|KY|KZ|LA|LB|LC|LI|LK|LR|LS|LT|LU|LV|LY|MA|MC|MD|ME|MF|MG|MH|MK|ML|MM|MN|MO|MP|MQ|MR|MS|MT|MU|MV|MW|MX|MY|MZ|NA|NC|NE|NF|NG|NI|NL|NO|NP|NR|NU|NZ|OM|PA|PE|PF|PG|PH|PK|PL|PM|PN|PR|PS|PT|PW|PY|QA|RE|RO|RS|RU|RW|SA|SB|SC|SD|SE|SG|SH|SI|SJ|SK|SL|SM|SN|SO|SR|SS|ST|SV|SX|SY|SZ|TC|TD|TF|TG|TH|TJ|TK|TL|TM|TN|TO|TR|TT|TV|TW|TZ|UA|UG|UM|US|UY|UZ|VA|VC|VE|VG|VI|VN|VU|WF|WS|YE|YT|ZA|ZM|ZW)([0-9A-Z]{9})([0-9]{1})$

CUSIP: ^[A-Z0-9]{8}[0-9]$

+8
source

Based on examples published on Wikipedia, the method:

  • Replace each letter with its serial number (A = 1, B = 2, etc.) plus 9 → enter image description here
  • For each digit in an even position, starting with the rightmost position ( enter image description here ), replace it with the digits of its double (two digits in two vector inputs) → enter image description here ;
  • Confirmation code:

enter image description here

Possible implementation in JavaScript:

 function getVerificationCode(isin) { if(isin.length != 12) return null; var v = []; for(var i = isin.length-2; i >= 0; i--) { var c = isin.charAt(i); if(isNaN(c)) //Not a digit { var letterCode = isin.charCodeAt(i)-55; //Char ordinal + 9 v.push(letterCode % 10); if(letterCode > 9) v.push(Math.floor(letterCode/10)); } else v.push(Number(c)); } var sum = 0; var l = v.length; for(var i = 0; i < l; i++) if(i % 2 == 0) { var d = v[i]*2; sum += Math.floor(d/10); sum += d % 10; } else sum += v[i]; return 10 - (sum % 10); } 

EDIT: To enable @queso updates:

 function getVerificationCode(isin) { if (isin.length != 12) return false; var v = []; for (var i = isin.length - 2; i >= 0; i--) { var c = isin.charAt(i); if (isNaN(c)) { //not a digit var letterCode = isin.charCodeAt(i) - 55; //Char ordinal + 9 v.push(letterCode % 10); if (letterCode > 9) { v.push(Math.floor(letterCode / 10)); } } else { v.push(Number(c)); } } var sum = 0; var l = v.length; for (var i = 0; i < l; i++) { if (i % 2 == 0) { var d = v[i] * 2; sum += Math.floor(d / 10); sum += d % 10; } else { sum += v[i]; } } return (10 - (sum % 10)) % 10 } 
+2
source

I shared a feature with you in Matlab, thanks to @pablo and @queso .

 function isISIN = checkISINCode(Isin) % % % % see: % - source:https://en.wikipedia.org/wiki/International_Securities_Identification_Number % - source: https://stackoverflow.com/questions/16140753/how-to-validate-a-international-securities-identification-number-isin-number % % isISIN = 0; if length(Isin) ~= 12 return; end v = []; for i = (length(Isin)-1):-1:1 c = Isin(i); if isnan(str2double(Isin(i))) % from ASCII letterCode = double(upper(Isin(i))) - 64 + 9; v = [mod(letterCode, 10), v]; if letterCode > 9 v = [floor(letterCode/10),v]; end else v = [int8(str2double(Isin(i))), v]; end end sum_ = 0; l = length(v); for i=1:l if(mod(i-1,2) == 0) d = v(i) * 2.; sum_ = sum_ + floor( double(d) / 10.0); sum_ = sum_ + mod(d, 10); else sum_ = sum_ + v(i); end end checkValue = mod((10 - mod(sum_, 10)),10); % Check Computed value with last digit isISIN = int8(str2double(Isin(end))) == checkValue; end 
+1
source

I want to share my implementation in R. It does not require any specific package.

mgsub is a helper function that allows you to replace all characters in the ISIN code with a single command. It is copied from Replace a few letters with accents with gsub

iso3166alpha2$Code contains a list of countries that Grenade indicated

The algorithm is implemented in the function isIsin(x) , which returns TRUE in case of a valid ISIN code

 mgsub <- function(pattern, replacement, x, ...) { if (length(pattern)!=length(replacement)) { stop("pattern and replacement do not have the same length.") } result <- x for (i in 1:length(pattern)) { result <- gsub(pattern[i], replacement[i], result, ...) } result } isIsin <- function (identifier) { correctPrefix <- substr(identifier, 1, 2) %in% c(iso3166alpha2$Code, "XS") correctLength <- nchar(identifier) == 12 correctCharset <- !grepl('[[:punct:]]', identifier) if(!correctPrefix | !correctLength | !correctCharset) { return(FALSE) } # replace all character with its equivalent number identifierOnlyNumbers <- mgsub(LETTERS, seq(10, 35), substr(identifier, 1, 11)) # split the identifier in single digits and reverse its order characterVector <- rev(unlist(strsplit(identifierOnlyNumbers, ""))) # Double every second digit of the group of digits with the rightmost character characterVector[seq(1, nchar(identifierOnlyNumbers), 2)] <- as.character(as.numeric(characterVector[seq(1, nchar(identifierOnlyNumbers), 2)]) * 2) # Subtract 9 if > 9 (can apply to all since no digit can be greater than 9 before doubling) # Add up the digits summation <- sum(ifelse(as.numeric(characterVector) > 9, as.numeric(characterVector) - 9, as.numeric(characterVector))) # Take the 10s modulus of the sum, subtract it from 10 and take the 10s modulus of the result # this final step is important in the instance where the modulus of the sum is 0, as the resulting check digit would be 10 correctCheckDigit <- (10 - (summation %% 10)) %% 10 == as.numeric(substr(identifier, 12, 12)) correctCheckDigit } 
0
source
 <?php function cusipToIsin($CUSIP, $Country) { if (strlen($CUSIP) == 9) { $string = charToCusipBinary($Country) . charToCusipBinary($CUSIP); //Convert any letters to numbers $arrayString = str_split($string); //check wether string length is even or odd if (strlen($string) % 2 != 0) { $num = 0; foreach ($arrayString as $key => $value) { //Collect odd and even characters if ($key % 2 != 0) { $values = $value; } else { $values = $value * 2; //The key is in odd position, so Multiply by 2 } $sumValue = array_sum(str_split($values)); //Add up the individual digits $num += $sumValue; } $isinCheckDigit = (10 - ($num % 10)) % 10; $result1 = strtoupper($Country . $CUSIP . $isinCheckDigit); } else { $num = 0; foreach ($arrayString as $key => $value) { //Collect odd and even characters if ($key % 2 != 0) { $values = $value * 2; //The key is in even position, so Multiply by 2 } else { $values = $value; } $sumValue = array_sum(str_split($values)); //Add up the individual digits $num += $sumValue; } $isinCheckDigit = (10 - ($num % 10)) % 10; $result1 = strtoupper($Country . $CUSIP . $isinCheckDigit); } $Validate = isinValidate($result1); if ($Validate == true) { $result = $result1; } else { $result = 'Please check the CUSIP'; } } else { $result = 'Please check the CUSIP'; } return $result; } function charToCusipBinary($string) { return strtr(strtoupper($string), ['A' => '10', 'B' => '11', 'C' => '12', 'D' => '13', 'E' => '14', 'F' => '15', 'G' => '16', 'H' => '17', 'I' => '18', 'J' => '19', 'K' => '20', 'L' => '21', 'M' => '22', 'N' => '23', 'O' => '24', 'P' => '25', 'Q' => '26', 'R' => '27', 'S' => '28', 'T' => '29', 'U' => '30', 'V' => '31', 'W' => '32', 'X' => '33', 'Y' => '34', 'Z' => '35']); } function isinValidate($isin) { if (!preg_match('/^[AZ]{2}[A-Z0-9]{9}[0-9]$/i', $isin)) { return false; } $base10 = ''; for ($i = 0; $i <= 11; $i++) { $base10 .= base_convert($isin{$i}, 36, 10); } $checksum = 0; $len = strlen($base10) - 1; $parity = $len % 2; for ($i = $len; $i >= 0; $i--) { $weighted = $base10{$i} << (($i - $parity) & 1); $checksum += $weighted % 10 + (int) ($weighted / 10); } return !(bool) ($checksum % 10); } echo cusipToIsin('78012KD61', 'US'); //ISIN: US78012KD617 ?> 
0
source

This is an approach in Swift.

First check the requirement of 2 letters + 10 alphanumeric characters with regular expression

 func validateISIN(_ isin : String) -> Bool { guard isin.range(of: "^[AZ]{2}[A-Z0-9]{10}$", options: .regularExpression) != nil, let checksum = Int(isin.suffix(1)) else { return false } let digits = isin.dropLast().map{Int(String($0), radix: 36)!}.map(String.init).joined() var sum = 0 var evenFlag = true digits.reversed().forEach { character in var integer = Int(String(character))! if evenFlag { integer *= 2 } sum += integer / 10 sum += integer % 10 evenFlag.toggle() } return (10 - (sum % 10)) % 10 == checksum } 
0
source

All Articles