Module: IdentifiersValid

Defined in:
lib/identifiers_valid.rb

Constant Summary collapse

ISIN_FORMAT =
'^[A-Z]{2}[A-Z0-9]{9}[0-9]$'.freeze
SEDOL_FORMAT =
'^[A-Z0-9]{6}\d{1}$'.freeze
CUSIP_FORMAT =
'^[A-Z0-9]{8}\d{1}$'.freeze
ISIN_REGEX =
Regexp.new(ISIN_FORMAT).freeze
LETTER_TO_CODE_VALUE =

Ascii code for ‘A’ == 65 && ‘A’ == 10 in isin calculation code

55
CUSIP_SPECIAL_CHAR =
{ '*' => 36, '@' => 37, "\#" => 38 }.freeze
SEDOL_VALID_CHAR =
'0123456789BCDFGHJKLMNPQRSTVWXYZ'.freeze
SEDOL_WEIGHT =
[1, 3, 1, 7, 3, 9].freeze

Class Method Summary collapse

Class Method Details

.check_cusip_char(char, index) ⇒ Object



41
42
43
44
45
46
47
48
49
50
# File 'lib/identifiers_valid.rb', line 41

def self.check_cusip_char(char, index)
  v = if char.match?(/\d/)
        char.to_i
      elsif CUSIP_SPECIAL_CHAR[char]
        CUSIP_SPECIAL_CHAR[char]
      else
        char.ord - LETTER_TO_CODE_VALUE + 9
      end
  (index % 2).zero? ? v : v * 2
end

.cusip_invalid?(cusip) ⇒ Boolean

Returns:

  • (Boolean)


52
53
54
# File 'lib/identifiers_valid.rb', line 52

def self.cusip_invalid?(cusip)
  !cusip_valid?(cusip)
end

.cusip_valid?(cusip) ⇒ Boolean

Returns:

  • (Boolean)


28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/identifiers_valid.rb', line 28

def self.cusip_valid?(cusip)
  return nil if cusip.nil? || cusip.size != 9

  proc_cusip = cusip.split(//)
  key = proc_cusip.pop
  sum = proc_cusip.each_with_index.map do |char, i|
    v = check_cusip_char(char, i)
    (v / 10).to_i + (v % 10)
  end.inject(:+)

  valid?(sum, key) ? cusip : nil
end

.identifier_invalid?(value, field_key) ⇒ Boolean

Returns:

  • (Boolean)


24
25
26
# File 'lib/identifiers_valid.rb', line 24

def self.identifier_invalid?(value, field_key)
  !identifier_valid?(value, field_key)
end

.identifier_valid?(value, field_key) ⇒ Boolean

Returns:

  • (Boolean)


13
14
15
16
17
18
19
20
21
22
# File 'lib/identifiers_valid.rb', line 13

def self.identifier_valid?(value, field_key)
  case field_key.to_s.upcase
  when 'ISIN'
    isin_valid?(value)
  when 'SEDOL'
    sedol_valid?(value)
  when 'CUSIP'
    cusip_valid?(value)
  end
end

.isin_invalid?(isin) ⇒ Boolean

Returns:

  • (Boolean)


85
86
87
# File 'lib/identifiers_valid.rb', line 85

def self.isin_invalid?(isin)
  !isin_valid?(isin)
end

.isin_valid?(isin) ⇒ Boolean

Returns:

  • (Boolean)


71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/identifiers_valid.rb', line 71

def self.isin_valid?(isin)
  return nil if isin.nil? || !isin.match?(ISIN_REGEX)

  proc_isin = isin.split(//)
  key = proc_isin.pop.to_i
  proc_isin = proc_isin.map do |e|
    e.match?(/[A-Z]/) ? (e.ord - LETTER_TO_CODE_VALUE).to_s.split(//) : e
  end.flatten.reverse
  proc_isin.each_index do |i|
    proc_isin[i] = (proc_isin[i].to_i * 2).to_s.split(//) if (i % 2).zero?
  end.flatten!
  ((key + proc_isin.map(&:to_i).inject(:+)) % 10).zero? ? isin : nil
end

.sedol_invalid?(sedol) ⇒ Boolean

Returns:

  • (Boolean)


67
68
69
# File 'lib/identifiers_valid.rb', line 67

def self.sedol_invalid?(sedol)
  !sedol_valid?(sedol)
end

.sedol_valid?(sedol) ⇒ Boolean

Returns:

  • (Boolean)


56
57
58
59
60
61
62
63
64
65
# File 'lib/identifiers_valid.rb', line 56

def self.sedol_valid?(sedol)
  return nil if sedol.nil? || sedol.size != 7

  proc_sedol = sedol.split(//)
  proc_sedol.each { |char| return nil unless SEDOL_VALID_CHAR.include?(char) }
  key = proc_sedol.pop
  sum = proc_sedol.zip(SEDOL_WEIGHT)
                  .map { |ch, weight| ch.to_i(36) * weight }.inject(:+)
  valid?(sum, key) ? sedol : nil
end

.valid?(sum, key) ⇒ Boolean

Returns:

  • (Boolean)


89
90
91
# File 'lib/identifiers_valid.rb', line 89

def self.valid?(sum, key)
  ((10 - (sum % 10)) % 10).to_s == key
end