Class: SecID::ISIN

Inherits:
Base
  • Object
show all
Includes:
Checkable
Defined in:
lib/sec_id/isin.rb

Overview

International Securities Identification Number (ISIN) - a 12-character alphanumeric code that uniquely identifies a security globally.

Format: 2-letter country code + 9-character NSIN + 1-digit check digit

Examples:

Validate an ISIN

SecID::ISIN.valid?('US5949181045')  #=> true

Restore check digit

SecID::ISIN.restore!('US594918104')  #=> #<SecID::ISIN>

See Also:

Constant Summary collapse

FULL_NAME =

Human-readable name of the standard.

'International Securities Identification Number'
ID_LENGTH =

Valid length(s) of a normalized identifier.

12
EXAMPLE =

A representative valid identifier.

'US5949181045'
VALID_CHARS_REGEX =

Pattern matching the identifier's permitted character set.

/\A[A-Z0-9]+\z/
ID_REGEX =

Regular expression for parsing ISIN components.

/\A
  (?<identifier>
    (?<country_code>[A-Z]{2})
    (?<nsin>[A-Z0-9]{9}))
  (?<check_digit>\d)?
\z/x
CGS_COUNTRY_CODES =

Country codes that use CUSIP Global Services (CGS) for NSIN assignment.

Set.new(
  %w[
    US CA AG AI AN AR AS AW BB BL BM BO BQ BS BZ CL CO CR CW DM DO EC FM GD
    GS GU GY HN HT JM KN KY LC MF MH MP MX NI PA PE PH PR PW PY SR SV SX TT
    UM UY VC VE VG VI YT
  ]
).freeze
NSIN_COUNTRY_TYPES =

Maps country codes to their NSIN identifier types. Countries not in this map return :generic (CGS countries handled via #cgs?).

{
  # SEDOL countries (UK, Ireland, Crown Dependencies, and Overseas Territories)
  'GB' => :sedol,
  'IE' => :sedol,
  'GG' => :sedol,
  'IM' => :sedol,
  'JE' => :sedol,
  'FK' => :sedol,
  # WKN country (Germany)
  'DE' => :wkn,
  # Valoren countries (Switzerland and Liechtenstein)
  'CH' => :valoren,
  'LI' => :valoren
}.freeze

Constants included from Checkable

Checkable::CHAR_TO_DIGIT, Checkable::CHAR_TO_DIGITS

Constants included from Generatable

Generatable::ALPHA, Generatable::ALPHANUMERIC, Generatable::DIGITS

Constants included from Validatable

Validatable::ERROR_MAP

Constants included from Normalizable

Normalizable::SEPARATORS

Instance Attribute Summary collapse

Attributes inherited from Base

#full_id, #identifier

Instance Method Summary collapse

Methods included from Checkable

#restore, #restore!, #to_s, #valid?

Methods inherited from Base

#==, #as_json, example, full_name, has_check_digit?, #hash, id_length, length_specificity, length_values, short_name, #to_h

Methods included from Validatable

#errors, #valid?, #validate, #validate!

Methods included from Normalizable

#normalize!, #normalized, #to_s, #to_str

Constructor Details

#initialize(isin) ⇒ ISIN

Returns a new instance of ISIN.

Parameters:

  • isin (String)

    the ISIN string to parse



69
70
71
72
73
74
75
# File 'lib/sec_id/isin.rb', line 69

def initialize(isin)
  isin_parts = parse isin
  @identifier = isin_parts[:identifier]
  @country_code = isin_parts[:country_code]
  @nsin = isin_parts[:nsin]
  @check_digit = isin_parts[:check_digit]&.to_i
end

Instance Attribute Details

#country_codeString? (readonly)

Returns the ISO 3166-1 alpha-2 country code.

Returns:

  • (String, nil)

    the ISO 3166-1 alpha-2 country code



63
64
65
# File 'lib/sec_id/isin.rb', line 63

def country_code
  @country_code
end

#nsinString? (readonly)

Returns the National Securities Identifying Number (9 characters).

Returns:

  • (String, nil)

    the National Securities Identifying Number (9 characters)



66
67
68
# File 'lib/sec_id/isin.rb', line 66

def nsin
  @nsin
end

Instance Method Details

#calculate_check_digitInteger

Returns the calculated check digit (0-9).

Returns:

  • (Integer)

    the calculated check digit (0-9)

Raises:



86
87
88
89
# File 'lib/sec_id/isin.rb', line 86

def calculate_check_digit
  validate_format_for_calculation!
  mod10(luhn_sum_standard(reversed_digits_multi(identifier)))
end

#cgs?Boolean

Returns true if the country code is a CGS country.

Returns:

  • (Boolean)

    true if the country code is a CGS country



109
110
111
# File 'lib/sec_id/isin.rb', line 109

def cgs?
  CGS_COUNTRY_CODES.include?(country_code)
end

#nsin_typeSymbol

Returns the type of NSIN embedded in this ISIN.

Returns:

  • (Symbol)

    :cusip, :sedol, :wkn, :valoren, or :generic



155
156
157
158
159
# File 'lib/sec_id/isin.rb', line 155

def nsin_type
  return :cusip if cgs?

  NSIN_COUNTRY_TYPES.fetch(country_code, :generic)
end

#sedol?Boolean

Returns true if the country code uses SEDOL.

Returns:

  • (Boolean)

    true if the country code uses SEDOL



114
115
116
# File 'lib/sec_id/isin.rb', line 114

def sedol?
  NSIN_COUNTRY_TYPES[country_code] == :sedol
end

#to_cusipCUSIP

Returns a new CUSIP instance.

Returns:

  • (CUSIP)

    a new CUSIP instance

Raises:



102
103
104
105
106
# File 'lib/sec_id/isin.rb', line 102

def to_cusip
  raise InvalidFormatError, "'#{country_code}' is not a CGS country code!" unless cgs?

  CUSIP.new(nsin)
end

#to_nsinCUSIP, ...

Extracts the national identifier from this ISIN.

Returns:

Raises:



165
166
167
168
169
170
171
172
173
174
175
# File 'lib/sec_id/isin.rb', line 165

def to_nsin
  raise InvalidFormatError, 'Invalid ISIN format' unless valid_format?

  case nsin_type
  when :cusip   then to_cusip
  when :sedol   then to_sedol
  when :wkn     then to_wkn
  when :valoren then to_valoren
  else nsin # :generic - return raw string
  end
end

#to_pretty_sString?

Returns:

  • (String, nil)


78
79
80
81
82
# File 'lib/sec_id/isin.rb', line 78

def to_pretty_s
  return nil unless valid?

  "#{country_code} #{nsin} #{check_digit}"
end

#to_sedolSEDOL

Returns a new SEDOL instance.

Returns:

  • (SEDOL)

    a new SEDOL instance

Raises:



130
131
132
133
134
# File 'lib/sec_id/isin.rb', line 130

def to_sedol
  raise InvalidFormatError, "'#{country_code}' is not a SEDOL country code!" unless sedol?

  SEDOL.new(nsin[2..])
end

#to_valorenValoren

Returns a new Valoren instance.

Returns:

  • (Valoren)

    a new Valoren instance

Raises:



146
147
148
149
150
# File 'lib/sec_id/isin.rb', line 146

def to_valoren
  raise InvalidFormatError, "'#{country_code}' is not a Valoren country code!" unless valoren?

  Valoren.new(nsin)
end

#to_wknWKN

Returns a new WKN instance.

Returns:

  • (WKN)

    a new WKN instance

Raises:



138
139
140
141
142
# File 'lib/sec_id/isin.rb', line 138

def to_wkn
  raise InvalidFormatError, "'#{country_code}' is not a WKN country code!" unless wkn?

  WKN.new(nsin[3..])
end

#valoren?Boolean

Returns true if the country code uses Valoren.

Returns:

  • (Boolean)

    true if the country code uses Valoren



124
125
126
# File 'lib/sec_id/isin.rb', line 124

def valoren?
  NSIN_COUNTRY_TYPES[country_code] == :valoren
end

#wkn?Boolean

Returns true if the country code uses WKN.

Returns:

  • (Boolean)

    true if the country code uses WKN



119
120
121
# File 'lib/sec_id/isin.rb', line 119

def wkn?
  country_code == 'DE'
end