How to validate a International Securities Identif

2020-05-23 11:25发布

If I am not wrong, ISIN numbers last position is a verification digit. What is the mathematical function that determines its value in function of the first 11 digits?

7条回答
ゆ 、 Hurt°
2楼-- · 2020-05-23 11:58

Based on the examples published in Wikipedia, the method is:

  1. Replace each letter by its ordinal (A=1, B=2 and so on) plus 9 -> enter image description here
  2. For each digit at an even position starting from the rightmost position (enter image description here), replace it by the digits of its double (two digits in two vector entries) -> enter image description here;
  3. Verification code:

enter image description here

A possible implementation in JavaScript is:

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 include @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
}
查看更多
放我归山
3楼-- · 2020-05-23 12:04

I'd like to share my implementation in R. It does not require any specific package.

The mgsub is a support function that allows the substitution of all the characters in the ISIN code in one single command. It is copied from Replace multiple letters with accents with gsub

the iso3166alpha2$Code contains the list of countries that Grenade has listed

The algorithm is implemented in the isIsin(x) function, which returns TRUE in case of 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 

}
查看更多
beautiful°
4楼-- · 2020-05-23 12:08

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

The procedure for calculating ISIN check digits is similar to the "Modulus 10 Double Add Double" technique used in CUSIPs. To calculate the check digit, first convert any letters to numbers by adding their ordinal position in the alphabet to 9, such that A = 10 and M = 22. Starting with the right most digit, every other digit is multiplied by two. (For CUSIP check digits, these two steps are reversed.) The resulting string of digits (numbers greater than 9 becoming two separate digits) are added up. Subtract this sum from the smallest number ending with zero that is greater than or equal to it: this gives the check digit, which is also known as the ten's complement of the sum modulo 10. That is, the resulting sum, including the check-digit, is a multiple of 10.

They have a good example too.

查看更多
时光不老,我们不散
5楼-- · 2020-05-23 12:10

This is an approach in Swift.

It checks first for the requirement 2 letters + 10 alphanumeric characters with Regular Expression

func validateISIN(_ isin : String) -> Bool {
    guard isin.range(of: "^[A-Z]{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
}
查看更多
一纸荒年 Trace。
6楼-- · 2020-05-23 12:16
<?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('/^[A-Z]{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
?>
查看更多
等我变得足够好
7楼-- · 2020-05-23 12:22

I share with you a function 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
查看更多
登录 后发表回答