Skip to content

Commit

Permalink
CIK support added (#85)
Browse files Browse the repository at this point in the history
  • Loading branch information
wtn authored Sep 19, 2024
1 parent f130bdc commit c9d5183
Show file tree
Hide file tree
Showing 4 changed files with 187 additions and 1 deletion.
23 changes: 22 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Currently supported standards:
[ISIN](https://en.wikipedia.org/wiki/International_Securities_Identification_Number),
[CUSIP](https://en.wikipedia.org/wiki/CUSIP),
[SEDOL](https://en.wikipedia.org/wiki/SEDOL),
[FIGI](https://en.wikipedia.org/wiki/Financial_Instrument_Global_Identifier).
[FIGI](https://en.wikipedia.org/wiki/Financial_Instrument_Global_Identifier), [CIK](https://en.wikipedia.org/wiki/Central_Index_Key).

Work in progress:
[IBAN](https://en.wikipedia.org/wiki/International_Bank_Account_Number).
Expand Down Expand Up @@ -179,6 +179,27 @@ figi.restore! # => 'BBG000DMBXR2'
figi.calculate_check_digit # => 2
```

### SecId::CIK full example

```ruby
# class level
SecId::CIK.valid?('0001094517') # => true
SecId::CIK.valid_format?('0001094517') # => true
SecId::CIK.restore!('1094517') # => '0001094517'
SecId::CIK.check_digit('0001094517') # raises NotImplementedError

# instance level
cik = SecId::CIK.new('0001094517')
cik.full_number # => '0001094517'
cik.padding # => '000'
cik.identifier # => '1094517'
cik.valid? # => true
cik.valid_format? # => true
cik.restore! # => '0001094517'
cik.calculate_check_digit # raises NotImplementedError
cik.check_digit # => nil
```

## Development

After checking out the repo, run `bin/setup` to install dependencies.
Expand Down
1 change: 1 addition & 0 deletions lib/sec_id.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
require 'sec_id/cusip'
require 'sec_id/sedol'
require 'sec_id/figi'
require 'sec_id/cik'

module SecId
Error = Class.new(StandardError)
Expand Down
35 changes: 35 additions & 0 deletions lib/sec_id/cik.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# frozen_string_literal: true

module SecId
# https://en.wikipedia.org/wiki/Central_Index_Key
class CIK < Base
ID_REGEX = /\A
(?=\d{1,10}\z)(?<padding>0*)(?<identifier>[1-9]\d{0,9})
\z/x

attr_reader :padding

def initialize(cik)
cik_parts = parse cik
@padding = cik_parts[:padding]
@identifier = cik_parts[:identifier]
end

def valid?
valid_format?
end

def valid_format?
!identifier.nil?
end

def restore!
if valid_format?
@padding = '0' * (10 - @identifier.length)
return(@full_number = @identifier.rjust(10, '0'))
end

raise InvalidFormatError, "CIK '#{full_number}' is invalid and cannot be restored!"
end
end
end
129 changes: 129 additions & 0 deletions spec/sec_id/cik_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
# frozen_string_literal: true

RSpec.describe SecId::CIK do
let(:cik) { described_class.new(cik_number) }

context 'when CIK is valid' do
let(:cik_number) { '0001521365' }

it 'parses CIK correctly' do
expect(cik.padding).to eq('000')
expect(cik.identifier).to eq('1521365')
expect(cik.check_digit).to be_nil
end

describe '#valid?' do
it 'returns true' do
expect(cik.valid?).to be(true)
end
end

describe '#restore!' do
it 'returns full CIK number' do
expect(cik.restore!).to eq(cik_number)
expect(cik.full_number).to eq(cik_number)
end
end

describe '#calculate_check_digit' do
it 'raises an error' do
expect { cik.calculate_check_digit }.to raise_error(NotImplementedError)
end
end
end

context 'when CIK number is missing leading zeros' do
let(:cik_number) { '10624' }

it 'parses CIK number correctly' do
expect(cik.identifier).to eq(cik_number)
expect(cik.check_digit).to be_nil
end

describe '#valid?' do
it 'returns true' do
expect(cik.valid?).to be(true)
end
end

describe '#restore!' do
it 'returns full CIK number and sets padding' do
expect(cik.restore!).to eq('0000010624')
expect(cik.full_number).to eq('0000010624')
expect(cik.padding).to eq('00000')
end
end

describe '#calculate_check_digit' do
it 'raises an error' do
expect { cik.calculate_check_digit }.to raise_error(NotImplementedError)
end
end
end

describe '.valid?' do
context 'when CIK is malformed' do
it 'returns false' do
expect(described_class.valid?('X9')).to be(false)
expect(described_class.valid?('0000000000')).to be(false)
expect(described_class.valid?('01234567890')).to be(false)
end
end

context 'when CIK is valid' do
it 'returns true' do
%w[0000000003 0000089562 0000010624 0002035979].each do |cik_number|
expect(described_class.valid?(cik_number)).to be(true)
end
end
end
end

describe '.restore!' do
context 'when CIK is malformed' do
it 'raises an error' do
expect { described_class.restore!('X9') }.to raise_error(SecId::InvalidFormatError)
expect { described_class.restore!('0000000000') }.to raise_error(SecId::InvalidFormatError)
expect { described_class.restore!('09876543210') }.to raise_error(SecId::InvalidFormatError)
end
end

context 'when CIK is valid' do
it 'restores check-digit and returns full CIK number' do
expect(described_class.restore!('3')).to eq('0000000003')
expect(described_class.restore!('0000000003')).to eq('0000000003')
expect(described_class.restore!('1072424')).to eq('0001072424')
expect(described_class.restore!('001072424')).to eq('0001072424')
expect(described_class.restore!('0001072424')).to eq('0001072424')
end
end
end

describe '.valid_format?' do
context 'when CIK is malformed' do
it 'returns false' do
expect(described_class.valid_format?('X9')).to be(false)
expect(described_class.valid_format?('0000000000')).to be(false)
expect(described_class.valid_format?('01234567890')).to be(false)
end
end

context 'when CIK is valid or missing leading zeros' do
it 'returns true' do
expect(described_class.valid_format?('3')).to be(true)
expect(described_class.valid_format?('0000000003')).to be(true)
expect(described_class.valid_format?('1072424')).to be(true)
expect(described_class.valid_format?('001072424')).to be(true)
expect(described_class.valid_format?('0001072424')).to be(true)
end
end
end

describe '.check_digit' do
it 'raises an error' do
expect { described_class.check_digit('0000320193') }.to raise_error(NotImplementedError)
expect { described_class.check_digit('320193') }.to raise_error(NotImplementedError)
expect { described_class.check_digit('0') }.to raise_error(NotImplementedError)
end
end
end

0 comments on commit c9d5183

Please sign in to comment.