sec_id 4.4.1 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +49 -3
- data/MIGRATION.md +257 -0
- data/README.md +234 -142
- data/lib/sec_id/base.rb +14 -38
- data/lib/sec_id/cei.rb +7 -2
- data/lib/sec_id/cfi.rb +38 -12
- data/lib/sec_id/cik.rb +21 -18
- data/lib/sec_id/concerns/checkable.rb +56 -14
- data/lib/sec_id/concerns/identifier_metadata.rb +56 -0
- data/lib/sec_id/concerns/normalizable.rb +42 -16
- data/lib/sec_id/concerns/validatable.rb +158 -0
- data/lib/sec_id/cusip.rb +10 -8
- data/lib/sec_id/detector.rb +156 -0
- data/lib/sec_id/errors.rb +67 -0
- data/lib/sec_id/figi.rb +30 -10
- data/lib/sec_id/fisn.rb +10 -4
- data/lib/sec_id/iban/country_rules.rb +4 -2
- data/lib/sec_id/iban.rb +44 -12
- data/lib/sec_id/isin.rb +11 -6
- data/lib/sec_id/lei.rb +12 -9
- data/lib/sec_id/occ.rb +40 -25
- data/lib/sec_id/sedol.rb +9 -7
- data/lib/sec_id/valoren.rb +22 -22
- data/lib/sec_id/version.rb +2 -2
- data/lib/sec_id/wkn.rb +10 -7
- data/lib/sec_id.rb +127 -6
- data/sec_id.gemspec +6 -3
- metadata +11 -3
data/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#
|
|
1
|
+
# SecID [](https://rubygems.org/gems/sec_id) [](https://app.codecov.io/gh/svyatov/sec_id) [](https://github.com/svyatov/sec_id/actions?query=workflow%3ACI)
|
|
2
2
|
|
|
3
3
|
> Validate securities identification numbers with ease!
|
|
4
4
|
|
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
- [Supported Ruby Versions](#supported-ruby-versions)
|
|
8
8
|
- [Installation](#installation)
|
|
9
9
|
- [Supported Standards and Usage](#supported-standards-and-usage)
|
|
10
|
+
- [Metadata Registry](#metadata-registry) - enumerate, filter, look up, and detect identifier types
|
|
11
|
+
- [Structured Validation](#structured-validation) - detailed error codes and messages
|
|
10
12
|
- [ISIN](#isin) - International Securities Identification Number
|
|
11
13
|
- [CUSIP](#cusip) - Committee on Uniform Securities Identification Procedures
|
|
12
14
|
- [CEI](#cei) - CUSIP Entity Identifier
|
|
@@ -28,14 +30,14 @@
|
|
|
28
30
|
|
|
29
31
|
## Supported Ruby Versions
|
|
30
32
|
|
|
31
|
-
Ruby 3.
|
|
33
|
+
Ruby 3.2+ is required.
|
|
32
34
|
|
|
33
35
|
## Installation
|
|
34
36
|
|
|
35
37
|
Add this line to your application's Gemfile:
|
|
36
38
|
|
|
37
39
|
```ruby
|
|
38
|
-
gem 'sec_id', '~>
|
|
40
|
+
gem 'sec_id', '~> 5.0'
|
|
39
41
|
```
|
|
40
42
|
|
|
41
43
|
And then execute:
|
|
@@ -50,16 +52,124 @@ Or install it yourself:
|
|
|
50
52
|
gem install sec_id
|
|
51
53
|
```
|
|
52
54
|
|
|
55
|
+
**Upgrading from v4?** See [MIGRATION.md](MIGRATION.md) for a step-by-step guide.
|
|
56
|
+
|
|
53
57
|
## Supported Standards and Usage
|
|
54
58
|
|
|
55
|
-
All identifier classes provide `valid
|
|
59
|
+
All identifier classes provide `valid?`, `errors`, `validate`, `validate!` methods at both class and instance levels.
|
|
60
|
+
|
|
61
|
+
**All identifiers** support normalization:
|
|
62
|
+
- `.normalize(id)` - strips separators, upcases, validates, and returns the canonical string
|
|
63
|
+
- `#normalized` / `#normalize` - returns the canonical string for a valid instance
|
|
64
|
+
- `#normalize!` - mutates `full_id` to canonical form, returns `self`
|
|
56
65
|
|
|
57
66
|
**Check-digit based identifiers** (ISIN, CUSIP, CEI, SEDOL, FIGI, LEI, IBAN) also provide:
|
|
58
|
-
- `restore
|
|
67
|
+
- `restore` / `.restore` - returns the full identifier string with correct check-digit (no mutation)
|
|
68
|
+
- `restore!` / `.restore!` - restores check-digit in place and returns `self` / instance
|
|
59
69
|
- `check_digit` / `calculate_check_digit` - calculates and returns the check-digit
|
|
60
70
|
|
|
61
|
-
|
|
62
|
-
|
|
71
|
+
### Metadata Registry
|
|
72
|
+
|
|
73
|
+
All identifier classes are registered automatically and can be enumerated, filtered, and looked up by symbol key:
|
|
74
|
+
|
|
75
|
+
```ruby
|
|
76
|
+
# Look up by symbol key
|
|
77
|
+
SecID[:isin] # => SecID::ISIN
|
|
78
|
+
SecID[:cusip] # => SecID::CUSIP
|
|
79
|
+
|
|
80
|
+
# Enumerate all identifier classes
|
|
81
|
+
SecID.identifiers # => [SecID::ISIN, SecID::CUSIP, ...]
|
|
82
|
+
SecID.identifiers.map(&:short_name) # => ["ISIN", "CUSIP", "SEDOL", ...]
|
|
83
|
+
|
|
84
|
+
# Query metadata
|
|
85
|
+
SecID::ISIN.short_name # => "ISIN"
|
|
86
|
+
SecID::ISIN.full_name # => "International Securities Identification Number"
|
|
87
|
+
SecID::ISIN.id_length # => 12
|
|
88
|
+
SecID::ISIN.example # => "US5949181045"
|
|
89
|
+
SecID::ISIN.has_check_digit? # => true
|
|
90
|
+
|
|
91
|
+
# Filter with standard Ruby
|
|
92
|
+
SecID.identifiers.select(&:has_check_digit?).map(&:short_name)
|
|
93
|
+
# => ["ISIN", "CUSIP", "SEDOL", "FIGI", "LEI", "IBAN", "CEI"]
|
|
94
|
+
|
|
95
|
+
# Detect identifier type from an unknown string
|
|
96
|
+
# Results are sorted by specificity: check-digit types first, then by length precision
|
|
97
|
+
SecID.detect('US5949181045') # => [:isin]
|
|
98
|
+
SecID.detect('037833100') # => [:cusip, :valoren, :cik]
|
|
99
|
+
SecID.detect('APPLE INC/SH') # => [:fisn]
|
|
100
|
+
SecID.detect('INVALID') # => []
|
|
101
|
+
|
|
102
|
+
# Quick boolean validation
|
|
103
|
+
SecID.valid?('US5949181045') # => true (any type)
|
|
104
|
+
SecID.valid?('INVALID') # => false
|
|
105
|
+
SecID.valid?('US5949181045', types: [:isin]) # => true
|
|
106
|
+
SecID.valid?('594918104', types: %i[cusip sedol]) # => true
|
|
107
|
+
SecID.valid?('US5949181045', types: [:cusip]) # => false
|
|
108
|
+
|
|
109
|
+
# Parse into a typed instance (returns the most specific match)
|
|
110
|
+
SecID.parse('US5949181045') # => #<SecID::ISIN>
|
|
111
|
+
SecID.parse('594918104') # => #<SecID::CUSIP>
|
|
112
|
+
SecID.parse('unknown') # => nil
|
|
113
|
+
SecID.parse('594918104', types: [:cusip]) # => #<SecID::CUSIP>
|
|
114
|
+
|
|
115
|
+
# Bang version raises on failure
|
|
116
|
+
SecID.parse!('US5949181045') # => #<SecID::ISIN>
|
|
117
|
+
SecID.parse!('unknown') # raises SecID::InvalidFormatError
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Structured Validation
|
|
121
|
+
|
|
122
|
+
All identifier classes provide a Rails-like `#errors` API for detailed error reporting:
|
|
123
|
+
|
|
124
|
+
```ruby
|
|
125
|
+
isin = SecID::ISIN.new('US5949181040')
|
|
126
|
+
isin.errors.none? # => false
|
|
127
|
+
isin.errors.messages # => ["Check digit '0' is invalid, expected '5'"]
|
|
128
|
+
isin.errors.details # => [{ error: :invalid_check_digit, message: "Check digit '0' is invalid, expected '5'" }]
|
|
129
|
+
isin.errors.any? # => true
|
|
130
|
+
isin.errors.empty? # => false
|
|
131
|
+
isin.errors.size # => 1
|
|
132
|
+
isin.errors.to_a # => same as messages
|
|
133
|
+
|
|
134
|
+
# Class-level convenience method (returns the instance with errors cached)
|
|
135
|
+
SecID::ISIN.validate('US5949181040') # => #<SecID::ISIN>
|
|
136
|
+
SecID::ISIN.validate('US5949181040').errors # => #<SecID::Errors>
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
**Common error codes** (all identifier types):
|
|
140
|
+
- `:invalid_length` - wrong number of characters
|
|
141
|
+
- `:invalid_characters` - contains characters not allowed for this type
|
|
142
|
+
- `:invalid_format` - correct length and characters but wrong structure
|
|
143
|
+
|
|
144
|
+
**Type-specific error codes:**
|
|
145
|
+
- `:invalid_check_digit` - check digit mismatch (ISIN, CUSIP, SEDOL, FIGI, LEI, IBAN, CEI)
|
|
146
|
+
- `:invalid_prefix` - restricted FIGI prefix (FIGI)
|
|
147
|
+
- `:invalid_category` - unknown CFI category code (CFI)
|
|
148
|
+
- `:invalid_group` - unknown CFI group code for category (CFI)
|
|
149
|
+
- `:invalid_bban` - BBAN format invalid for country (IBAN)
|
|
150
|
+
- `:invalid_date` - unparseable expiration date (OCC)
|
|
151
|
+
|
|
152
|
+
#### Fail-fast validation with `validate!`
|
|
153
|
+
|
|
154
|
+
Use `validate!` when you want to raise an exception on invalid input instead of inspecting errors:
|
|
155
|
+
|
|
156
|
+
```ruby
|
|
157
|
+
# Returns self when valid (enables chaining)
|
|
158
|
+
SecID::ISIN.new('US5949181045').validate! # => #<SecID::ISIN>
|
|
159
|
+
|
|
160
|
+
# Raises with a descriptive message when invalid
|
|
161
|
+
SecID::ISIN.new('INVALID').validate!
|
|
162
|
+
# => SecID::InvalidFormatError: Expected 12 characters, got 7
|
|
163
|
+
|
|
164
|
+
SecID::ISIN.new('US5949181040').validate!
|
|
165
|
+
# => SecID::InvalidCheckDigitError: Check digit '0' is invalid, expected '5'
|
|
166
|
+
|
|
167
|
+
SecID::FIGI.new('BSG000BLNNH6').validate!
|
|
168
|
+
# => SecID::InvalidStructureError: Prefix 'BS' is restricted
|
|
169
|
+
|
|
170
|
+
# Class-level convenience method (returns the instance)
|
|
171
|
+
isin = SecID::ISIN.validate!('US5949181045') # => #<SecID::ISIN>
|
|
172
|
+
```
|
|
63
173
|
|
|
64
174
|
### ISIN
|
|
65
175
|
|
|
@@ -67,42 +177,42 @@ All identifier classes provide `valid?` and `valid_format?` methods at both clas
|
|
|
67
177
|
|
|
68
178
|
```ruby
|
|
69
179
|
# class level
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
180
|
+
SecID::ISIN.valid?('US5949181045') # => true
|
|
181
|
+
SecID::ISIN.restore('US594918104') # => 'US5949181045'
|
|
182
|
+
SecID::ISIN.restore!('US594918104') # => #<SecID::ISIN>
|
|
183
|
+
SecID::ISIN.check_digit('US594918104') # => 5
|
|
74
184
|
|
|
75
185
|
# instance level
|
|
76
|
-
isin =
|
|
77
|
-
isin.
|
|
186
|
+
isin = SecID::ISIN.new('US5949181045')
|
|
187
|
+
isin.full_id # => 'US5949181045'
|
|
78
188
|
isin.country_code # => 'US'
|
|
79
189
|
isin.nsin # => '594918104'
|
|
80
190
|
isin.check_digit # => 5
|
|
81
191
|
isin.valid? # => true
|
|
82
|
-
isin.
|
|
83
|
-
isin.restore! # =>
|
|
192
|
+
isin.restore # => 'US5949181045'
|
|
193
|
+
isin.restore! # => #<SecID::ISIN> (mutates instance)
|
|
84
194
|
isin.calculate_check_digit # => 5
|
|
85
|
-
isin.to_cusip # => #<
|
|
195
|
+
isin.to_cusip # => #<SecID::CUSIP>
|
|
86
196
|
isin.nsin_type # => :cusip
|
|
87
|
-
isin.to_nsin # => #<
|
|
197
|
+
isin.to_nsin # => #<SecID::CUSIP>
|
|
88
198
|
|
|
89
199
|
# NSIN extraction for different countries
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
200
|
+
SecID::ISIN.new('GB00B02H2F76').nsin_type # => :sedol
|
|
201
|
+
SecID::ISIN.new('GB00B02H2F76').to_nsin # => #<SecID::SEDOL>
|
|
202
|
+
SecID::ISIN.new('DE0007164600').nsin_type # => :wkn
|
|
203
|
+
SecID::ISIN.new('DE0007164600').to_nsin # => #<SecID::WKN>
|
|
204
|
+
SecID::ISIN.new('CH0012221716').nsin_type # => :valoren
|
|
205
|
+
SecID::ISIN.new('CH0012221716').to_nsin # => #<SecID::Valoren>
|
|
206
|
+
SecID::ISIN.new('FR0000120271').nsin_type # => :generic
|
|
207
|
+
SecID::ISIN.new('FR0000120271').to_nsin # => '000012027' (raw NSIN string)
|
|
98
208
|
|
|
99
209
|
# Type-specific conversion methods with validation
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
210
|
+
SecID::ISIN.new('GB00B02H2F76').sedol? # => true
|
|
211
|
+
SecID::ISIN.new('GB00B02H2F76').to_sedol # => #<SecID::SEDOL>
|
|
212
|
+
SecID::ISIN.new('DE0007164600').wkn? # => true
|
|
213
|
+
SecID::ISIN.new('DE0007164600').to_wkn # => #<SecID::WKN>
|
|
214
|
+
SecID::ISIN.new('CH0012221716').valoren? # => true
|
|
215
|
+
SecID::ISIN.new('CH0012221716').to_valoren # => #<SecID::Valoren>
|
|
106
216
|
```
|
|
107
217
|
|
|
108
218
|
### CUSIP
|
|
@@ -111,22 +221,22 @@ SecId::ISIN.new('CH0012221716').to_valoren # => #<SecId::Valoren>
|
|
|
111
221
|
|
|
112
222
|
```ruby
|
|
113
223
|
# class level
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
224
|
+
SecID::CUSIP.valid?('594918104') # => true
|
|
225
|
+
SecID::CUSIP.restore('59491810') # => '594918104'
|
|
226
|
+
SecID::CUSIP.restore!('59491810') # => #<SecID::CUSIP>
|
|
227
|
+
SecID::CUSIP.check_digit('59491810') # => 4
|
|
118
228
|
|
|
119
229
|
# instance level
|
|
120
|
-
cusip =
|
|
121
|
-
cusip.
|
|
230
|
+
cusip = SecID::CUSIP.new('594918104')
|
|
231
|
+
cusip.full_id # => '594918104'
|
|
122
232
|
cusip.cusip6 # => '594918'
|
|
123
233
|
cusip.issue # => '10'
|
|
124
234
|
cusip.check_digit # => 4
|
|
125
235
|
cusip.valid? # => true
|
|
126
|
-
cusip.
|
|
127
|
-
cusip.restore! # =>
|
|
236
|
+
cusip.restore # => '594918104'
|
|
237
|
+
cusip.restore! # => #<SecID::CUSIP> (mutates instance)
|
|
128
238
|
cusip.calculate_check_digit # => 4
|
|
129
|
-
cusip.to_isin('US') # => #<
|
|
239
|
+
cusip.to_isin('US') # => #<SecID::ISIN>
|
|
130
240
|
cusip.cins? # => false
|
|
131
241
|
```
|
|
132
242
|
|
|
@@ -136,21 +246,21 @@ cusip.cins? # => false
|
|
|
136
246
|
|
|
137
247
|
```ruby
|
|
138
248
|
# class level
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
249
|
+
SecID::CEI.valid?('A0BCDEFGH1') # => true
|
|
250
|
+
SecID::CEI.restore('A0BCDEFGH') # => 'A0BCDEFGH1'
|
|
251
|
+
SecID::CEI.restore!('A0BCDEFGH') # => #<SecID::CEI>
|
|
252
|
+
SecID::CEI.check_digit('A0BCDEFGH') # => 1
|
|
143
253
|
|
|
144
254
|
# instance level
|
|
145
|
-
cei =
|
|
146
|
-
cei.
|
|
255
|
+
cei = SecID::CEI.new('A0BCDEFGH1')
|
|
256
|
+
cei.full_id # => 'A0BCDEFGH1'
|
|
147
257
|
cei.prefix # => 'A'
|
|
148
258
|
cei.numeric # => '0'
|
|
149
259
|
cei.entity_id # => 'BCDEFGH'
|
|
150
260
|
cei.check_digit # => 1
|
|
151
261
|
cei.valid? # => true
|
|
152
|
-
cei.
|
|
153
|
-
cei.restore! # =>
|
|
262
|
+
cei.restore # => 'A0BCDEFGH1'
|
|
263
|
+
cei.restore! # => #<SecID::CEI> (mutates instance)
|
|
154
264
|
cei.calculate_check_digit # => 1
|
|
155
265
|
```
|
|
156
266
|
|
|
@@ -160,21 +270,21 @@ cei.calculate_check_digit # => 1
|
|
|
160
270
|
|
|
161
271
|
```ruby
|
|
162
272
|
# class level
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
273
|
+
SecID::SEDOL.valid?('B0Z52W5') # => true
|
|
274
|
+
SecID::SEDOL.restore('B0Z52W') # => 'B0Z52W5'
|
|
275
|
+
SecID::SEDOL.restore!('B0Z52W') # => #<SecID::SEDOL>
|
|
276
|
+
SecID::SEDOL.check_digit('B0Z52W') # => 5
|
|
167
277
|
|
|
168
278
|
# instance level
|
|
169
|
-
sedol =
|
|
170
|
-
sedol.
|
|
279
|
+
sedol = SecID::SEDOL.new('B0Z52W5')
|
|
280
|
+
sedol.full_id # => 'B0Z52W5'
|
|
171
281
|
sedol.check_digit # => 5
|
|
172
282
|
sedol.valid? # => true
|
|
173
|
-
sedol.
|
|
174
|
-
sedol.restore! # =>
|
|
283
|
+
sedol.restore # => 'B0Z52W5'
|
|
284
|
+
sedol.restore! # => #<SecID::SEDOL> (mutates instance)
|
|
175
285
|
sedol.calculate_check_digit # => 5
|
|
176
|
-
sedol.to_isin # => #<
|
|
177
|
-
sedol.to_isin('IE') # => #<
|
|
286
|
+
sedol.to_isin # => #<SecID::ISIN> (GB ISIN by default)
|
|
287
|
+
sedol.to_isin('IE') # => #<SecID::ISIN> (IE ISIN)
|
|
178
288
|
```
|
|
179
289
|
|
|
180
290
|
### FIGI
|
|
@@ -183,20 +293,20 @@ sedol.to_isin('IE') # => #<SecId::ISIN> (IE ISIN)
|
|
|
183
293
|
|
|
184
294
|
```ruby
|
|
185
295
|
# class level
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
296
|
+
SecID::FIGI.valid?('BBG000DMBXR2') # => true
|
|
297
|
+
SecID::FIGI.restore('BBG000DMBXR') # => 'BBG000DMBXR2'
|
|
298
|
+
SecID::FIGI.restore!('BBG000DMBXR') # => #<SecID::FIGI>
|
|
299
|
+
SecID::FIGI.check_digit('BBG000DMBXR') # => 2
|
|
190
300
|
|
|
191
301
|
# instance level
|
|
192
|
-
figi =
|
|
193
|
-
figi.
|
|
302
|
+
figi = SecID::FIGI.new('BBG000DMBXR2')
|
|
303
|
+
figi.full_id # => 'BBG000DMBXR2'
|
|
194
304
|
figi.prefix # => 'BB'
|
|
195
305
|
figi.random_part # => '000DMBXR'
|
|
196
306
|
figi.check_digit # => 2
|
|
197
307
|
figi.valid? # => true
|
|
198
|
-
figi.
|
|
199
|
-
figi.restore! # =>
|
|
308
|
+
figi.restore # => 'BBG000DMBXR2'
|
|
309
|
+
figi.restore! # => #<SecID::FIGI> (mutates instance)
|
|
200
310
|
figi.calculate_check_digit # => 2
|
|
201
311
|
```
|
|
202
312
|
|
|
@@ -206,21 +316,21 @@ figi.calculate_check_digit # => 2
|
|
|
206
316
|
|
|
207
317
|
```ruby
|
|
208
318
|
# class level
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
319
|
+
SecID::LEI.valid?('5493006MHB84DD0ZWV18') # => true
|
|
320
|
+
SecID::LEI.restore('5493006MHB84DD0ZWV') # => '5493006MHB84DD0ZWV18'
|
|
321
|
+
SecID::LEI.restore!('5493006MHB84DD0ZWV') # => #<SecID::LEI>
|
|
322
|
+
SecID::LEI.check_digit('5493006MHB84DD0ZWV') # => 18
|
|
213
323
|
|
|
214
324
|
# instance level
|
|
215
|
-
lei =
|
|
216
|
-
lei.
|
|
325
|
+
lei = SecID::LEI.new('5493006MHB84DD0ZWV18')
|
|
326
|
+
lei.full_id # => '5493006MHB84DD0ZWV18'
|
|
217
327
|
lei.lou_id # => '5493'
|
|
218
328
|
lei.reserved # => '00'
|
|
219
329
|
lei.entity_id # => '6MHB84DD0ZWV'
|
|
220
330
|
lei.check_digit # => 18
|
|
221
331
|
lei.valid? # => true
|
|
222
|
-
lei.
|
|
223
|
-
lei.restore! # =>
|
|
332
|
+
lei.restore # => '5493006MHB84DD0ZWV18'
|
|
333
|
+
lei.restore! # => #<SecID::LEI> (mutates instance)
|
|
224
334
|
lei.calculate_check_digit # => 18
|
|
225
335
|
```
|
|
226
336
|
|
|
@@ -230,22 +340,22 @@ lei.calculate_check_digit # => 18
|
|
|
230
340
|
|
|
231
341
|
```ruby
|
|
232
342
|
# class level
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
343
|
+
SecID::IBAN.valid?('DE89370400440532013000') # => true
|
|
344
|
+
SecID::IBAN.restore('DE370400440532013000') # => 'DE89370400440532013000'
|
|
345
|
+
SecID::IBAN.restore!('DE370400440532013000') # => #<SecID::IBAN>
|
|
346
|
+
SecID::IBAN.check_digit('DE370400440532013000') # => 89
|
|
237
347
|
|
|
238
348
|
# instance level
|
|
239
|
-
iban =
|
|
240
|
-
iban.
|
|
349
|
+
iban = SecID::IBAN.new('DE89370400440532013000')
|
|
350
|
+
iban.full_id # => 'DE89370400440532013000'
|
|
241
351
|
iban.country_code # => 'DE'
|
|
242
352
|
iban.bban # => '370400440532013000'
|
|
243
353
|
iban.bank_code # => '37040044'
|
|
244
354
|
iban.account_number # => '0532013000'
|
|
245
355
|
iban.check_digit # => 89
|
|
246
356
|
iban.valid? # => true
|
|
247
|
-
iban.
|
|
248
|
-
iban.restore! # =>
|
|
357
|
+
iban.restore # => 'DE89370400440532013000'
|
|
358
|
+
iban.restore! # => #<SecID::IBAN> (mutates instance)
|
|
249
359
|
iban.calculate_check_digit # => 89
|
|
250
360
|
iban.known_country? # => true
|
|
251
361
|
```
|
|
@@ -258,58 +368,50 @@ Full BBAN structural validation is supported for EU/EEA countries. Other countri
|
|
|
258
368
|
|
|
259
369
|
```ruby
|
|
260
370
|
# class level
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
SecId::CIK.normalize!('1094517') # => '0001094517'
|
|
371
|
+
SecID::CIK.valid?('0001094517') # => true
|
|
372
|
+
SecID::CIK.normalize('1094517') # => '0001094517'
|
|
264
373
|
|
|
265
374
|
# instance level
|
|
266
|
-
cik =
|
|
267
|
-
cik.
|
|
375
|
+
cik = SecID::CIK.new('0001094517')
|
|
376
|
+
cik.full_id # => '0001094517'
|
|
268
377
|
cik.padding # => '000'
|
|
269
378
|
cik.identifier # => '1094517'
|
|
270
379
|
cik.valid? # => true
|
|
271
|
-
cik.
|
|
272
|
-
cik.normalize! # =>
|
|
273
|
-
cik.to_s # => '0001094517'
|
|
380
|
+
cik.normalized # => '0001094517'
|
|
381
|
+
cik.normalize! # => #<SecID::CIK> (mutates full_id, returns self)
|
|
274
382
|
```
|
|
275
383
|
|
|
276
384
|
### OCC
|
|
277
385
|
|
|
278
|
-
> [Options Clearing Corporation Symbol](https://en.wikipedia.org/wiki/Option_symbol#The_OCC_Option_Symbol) - a 21-character code used to identify equity options contracts.
|
|
386
|
+
> [Options Clearing Corporation Symbol](https://en.wikipedia.org/wiki/Option_symbol#The_OCC_Option_Symbol) - a 16-to-21-character code used to identify equity options contracts.
|
|
279
387
|
|
|
280
388
|
```ruby
|
|
281
389
|
# class level
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
SecId::OCC.build(
|
|
390
|
+
SecID::OCC.valid?('BRKB 100417C00090000') # => true
|
|
391
|
+
SecID::OCC.normalize('BRKB100417C00090000') # => 'BRKB 100417C00090000'
|
|
392
|
+
SecID::OCC.build(
|
|
286
393
|
underlying: 'BRKB',
|
|
287
394
|
date: Date.new(2010, 4, 17),
|
|
288
395
|
type: 'C',
|
|
289
396
|
strike: 90,
|
|
290
|
-
) # => #<
|
|
397
|
+
) # => #<SecID::OCC>
|
|
291
398
|
|
|
292
399
|
# instance level
|
|
293
|
-
occ =
|
|
294
|
-
occ.
|
|
400
|
+
occ = SecID::OCC.new('BRKB 100417C00090000')
|
|
401
|
+
occ.full_id # => 'BRKB 100417C00090000'
|
|
295
402
|
occ.underlying # => 'BRKB'
|
|
296
403
|
occ.date_str # => '100417'
|
|
297
404
|
occ.date_obj # => #<Date: 2010-04-17>
|
|
298
405
|
occ.type # => 'C'
|
|
299
406
|
occ.strike # => 90.0
|
|
300
407
|
occ.valid? # => true
|
|
301
|
-
occ.
|
|
302
|
-
occ.normalize! # => 'BRKB 100417C00090000'
|
|
408
|
+
occ.normalized # => 'BRKB 100417C00090000'
|
|
303
409
|
|
|
304
|
-
occ =
|
|
305
|
-
occ.
|
|
306
|
-
occ.normalize! # raises SecId::InvalidFormatError
|
|
307
|
-
|
|
308
|
-
occ = SecId::OCC.new('X 250620C00050000')
|
|
309
|
-
occ.full_symbol # => 'X 250620C00050000'
|
|
410
|
+
occ = SecID::OCC.new('X 250620C00050000')
|
|
411
|
+
occ.full_id # => 'X 250620C00050000'
|
|
310
412
|
occ.valid? # => true
|
|
311
|
-
occ.normalize! # =>
|
|
312
|
-
occ.
|
|
413
|
+
occ.normalize! # => #<SecID::OCC> (mutates full_id, returns self)
|
|
414
|
+
occ.full_id # => 'X 250620C00050000'
|
|
313
415
|
```
|
|
314
416
|
|
|
315
417
|
### WKN
|
|
@@ -318,18 +420,16 @@ occ.full_symbol # => 'X 250620C00050000'
|
|
|
318
420
|
|
|
319
421
|
```ruby
|
|
320
422
|
# class level
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
SecId::WKN.valid_format?('514000') # => true
|
|
423
|
+
SecID::WKN.valid?('514000') # => true
|
|
424
|
+
SecID::WKN.valid?('CBK100') # => true
|
|
324
425
|
|
|
325
426
|
# instance level
|
|
326
|
-
wkn =
|
|
327
|
-
wkn.
|
|
427
|
+
wkn = SecID::WKN.new('514000')
|
|
428
|
+
wkn.full_id # => '514000'
|
|
328
429
|
wkn.identifier # => '514000'
|
|
329
430
|
wkn.valid? # => true
|
|
330
|
-
wkn.valid_format? # => true
|
|
331
431
|
wkn.to_s # => '514000'
|
|
332
|
-
wkn.to_isin # => #<
|
|
432
|
+
wkn.to_isin # => #<SecID::ISIN> (DE ISIN)
|
|
333
433
|
```
|
|
334
434
|
|
|
335
435
|
WKN excludes letters I and O to avoid confusion with digits 1 and 0.
|
|
@@ -340,24 +440,22 @@ WKN excludes letters I and O to avoid confusion with digits 1 and 0.
|
|
|
340
440
|
|
|
341
441
|
```ruby
|
|
342
442
|
# class level
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
SecId::Valoren.normalize!('3886335') # => '003886335'
|
|
443
|
+
SecID::Valoren.valid?('3886335') # => true
|
|
444
|
+
SecID::Valoren.valid?('24476758') # => true
|
|
445
|
+
SecID::Valoren.valid?('35514757') # => true
|
|
446
|
+
SecID::Valoren.valid?('97429325') # => true
|
|
447
|
+
SecID::Valoren.normalize('3886335') # => '003886335'
|
|
349
448
|
|
|
350
449
|
# instance level
|
|
351
|
-
valoren =
|
|
352
|
-
valoren.
|
|
450
|
+
valoren = SecID::Valoren.new('3886335')
|
|
451
|
+
valoren.full_id # => '3886335'
|
|
353
452
|
valoren.padding # => ''
|
|
354
453
|
valoren.identifier # => '3886335'
|
|
355
454
|
valoren.valid? # => true
|
|
356
|
-
valoren.
|
|
357
|
-
valoren.normalize! # =>
|
|
358
|
-
valoren.
|
|
359
|
-
valoren.to_isin
|
|
360
|
-
valoren.to_isin('LI') # => #<SecId::ISIN> (LI ISIN)
|
|
455
|
+
valoren.normalized # => '003886335'
|
|
456
|
+
valoren.normalize! # => #<SecID::Valoren> (mutates full_id, returns self)
|
|
457
|
+
valoren.to_isin # => #<SecID::ISIN> (CH ISIN by default)
|
|
458
|
+
valoren.to_isin('LI') # => #<SecID::ISIN> (LI ISIN)
|
|
361
459
|
```
|
|
362
460
|
|
|
363
461
|
### CFI
|
|
@@ -366,20 +464,17 @@ valoren.to_isin('LI') # => #<SecId::ISIN> (LI ISIN)
|
|
|
366
464
|
|
|
367
465
|
```ruby
|
|
368
466
|
# class level
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
SecId::CFI.valid_format?('ESXXXX') # => true
|
|
372
|
-
|
|
467
|
+
SecID::CFI.valid?('ESXXXX') # => true
|
|
468
|
+
SecID::CFI.valid?('ESVUFR') # => true
|
|
373
469
|
# instance level
|
|
374
|
-
cfi =
|
|
375
|
-
cfi.
|
|
470
|
+
cfi = SecID::CFI.new('ESVUFR')
|
|
471
|
+
cfi.full_id # => 'ESVUFR'
|
|
376
472
|
cfi.identifier # => 'ESVUFR'
|
|
377
473
|
cfi.category_code # => 'E'
|
|
378
474
|
cfi.group_code # => 'S'
|
|
379
475
|
cfi.category # => :equity
|
|
380
476
|
cfi.group # => :common_shares
|
|
381
477
|
cfi.valid? # => true
|
|
382
|
-
cfi.valid_format? # => true
|
|
383
478
|
|
|
384
479
|
# Equity-specific predicates
|
|
385
480
|
cfi.equity? # => true
|
|
@@ -397,18 +492,15 @@ CFI validates the category code (position 1) against 14 valid values and the gro
|
|
|
397
492
|
|
|
398
493
|
```ruby
|
|
399
494
|
# class level
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
SecId::FISN.valid_format?('APPLE INC/SH') # => true
|
|
403
|
-
|
|
495
|
+
SecID::FISN.valid?('APPLE INC/SH') # => true
|
|
496
|
+
SecID::FISN.valid?('apple inc/sh') # => true (normalized to uppercase)
|
|
404
497
|
# instance level
|
|
405
|
-
fisn =
|
|
406
|
-
fisn.
|
|
498
|
+
fisn = SecID::FISN.new('APPLE INC/SH')
|
|
499
|
+
fisn.full_id # => 'APPLE INC/SH'
|
|
407
500
|
fisn.identifier # => 'APPLE INC/SH'
|
|
408
501
|
fisn.issuer # => 'APPLE INC'
|
|
409
502
|
fisn.description # => 'SH'
|
|
410
503
|
fisn.valid? # => true
|
|
411
|
-
fisn.valid_format? # => true
|
|
412
504
|
fisn.to_s # => 'APPLE INC/SH'
|
|
413
505
|
```
|
|
414
506
|
|