2013-04-22 14 views

risposta

7

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

La procedura per il calcolo cifre di controllo ISIN è simile alla tecnica del "Modulo 10 Doppia Aggiungi doppia" utilizzato in CUSIPs. Per calcolare la cifra di controllo, convertire prima tutte le lettere in numeri aggiungendo la loro posizione ordinale in alfabeto a 9, tale che A = 10 e M = 22. A partire dalla cifra più a destra, ogni altra cifra viene moltiplicata per due. (Per le cifre di controllo CUSIP, questi due passaggi sono invertiti.) La stringa risultante di cifre (numeri maggiori di 9 che diventano due cifre separate) viene sommata. Sottrai questa somma dal numero più piccolo che termina con zero che è maggiore o uguale a questo: questo dà la cifra di controllo, che è anche nota come complemento a dieci della somma modulo 10. Cioè, la somma risultante, incluso il controllo- cifra, è un multiplo di 10.

Hanno anche un good example.

+0

Tale descrizione ha alcuni errori: "... ogni altra cifra viene moltiplicata per due". Come ho potuto trovare in altri riferimenti, e controllando gli esempi: non ogni altro numero, ma cifre alternative sono moltiplicate per due. –

+0

@ PabloFranciscoPérezHidalgo: è perfettamente ragionevole interpretare "ogni altra cifra" per indicare "cifre alternative". Non è chiaro quale pensi siano gli errori nella voce di Wikipedia. –

2

Sulla base degli esempi pubblicati in Wikipedia, il metodo è:

  1. sostituire ogni lettera dal suo ordinale (A = 1, B = 2 e così via) più 9 ->enter image description here
  2. Per ogni digitare in una posizione pari a partire dalla posizione più a destra (enter image description here), sostituirlo con le cifre del suo doppio (due cifre in due voci vettoriali) ->enter image description here; Codice
  3. di verifica:

enter image description here

Una possibile implementazione 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: Per includere @queso aggiornamenti:

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 
} 
+1

Per il codice sopra, ISIN XS0977502110 non riesce a guardare qui: http://en.wikipedia.org/wiki/International_Securities_Identification_Number#External_links Sono riuscito ad aggiornare il codice qui http://jsfiddle.net/markbenda/nh2w1Lbh/16/. Grazie per aver fatto la parte difficile. – Queso

+0

@Queso Ho modificato la mia risposta per includere i suggerimenti del codice. Grazie per i miglioramenti! –

7

costruzione sugli esempi di altri, ecco un'implementazione in C# che valuterà sia gli ISIN che i CUSIP (e forse alcune altre varianti di Luhn).

Usage:

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); 
} 

Implementazione:

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; 
    } 
} 

Sarà probabilmente sensato utilizzare la convalida checksum in combinazione con un normale partita pattern di espressione. Questi sono l'espressione regolare che uso:

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]$

0

mi piacerebbe condividere la mia applicazione in R. Non richiede alcun pacchetto specifico.

mgsub è una funzione di supporto che consente la sostituzione di tutti i caratteri nel codice ISIN in un unico comando. Si è copiato da Replace multiple letters with accents with gsub

il iso3166alpha2$Code contiene l'elenco dei paesi che Grenade ha elencato

L'algoritmo è implementato nella funzione isIsin(x), che restituisce TRUE in caso di valido codice ISIN

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 

}