module Moex

Moscow Exchange ISS

@see www.moex.com/a2920 Informational & Statistical Server (ISS)

Constants

CSV_PARSE_OPTS
ClientError

Raised when the ISS response is unsuccessful or invalid, or in case of any connection errors

MOEX_ISS_API_BASE_URL

rubocop:enable all

Public Class Methods

prices(date: nil) click to toggle source

List security prices

@param date [String, nil] the date, format YYYY-MM-DD @raise [Moex::ClientError] if the ISS response is unsuccessful or

invalid, or in case of any connection errors

@return [Array<Resources::Price>] the list of security prices

# File lib/moex.rb, line 93
def self.prices(date: nil)
  Enumerator.new do |yielder|
    start = 0
    price_table = load_prices(date: date, start: start)

    loop do
      break if price_table.empty?

      price_table.each do |row|
        yielder << Resources::Price.new(
          row.fetch(:tradedate),
          row.fetch(:secid),
          row.fetch(:close)
        )
      end

      price_table = load_prices(date: date, start: start += price_table.size)
    end
  end
end
securities(params) click to toggle source

List securities

@param params [Hash] @option params [String] security_group security group @option params [String] collection security collection @raise [Moex::ClientError] if the ISS response is unsuccessful or

invalid, or in case of any connection errors

@return [Array<Resources::Security>] the list of securities @see iss.moex.com/iss/securitygroups

List of security groups

@see iss.moex.com/iss/securitygroups/stock_shares/collections

List of stock collections

@see iss.moex.com/iss/securitygroups/stock_bonds/collections

List of bond collections
# File lib/moex.rb, line 32
def self.securities(params)
  Enumerator.new do |yielder|
    start = 0
    security_table = load_securities(params, start: start)

    loop do
      break if empty_table?(security_table)

      security_table.each do |row|
        yielder << Resources::Security.new(
          row.fetch(:isin),
          row.fetch(:secid),
          row.fetch(:shortname)
        )
      end

      start += security_table.size
      security_table = load_securities(params, start: start)
    end
  end
end

Private Class Methods

connection() click to toggle source
# File lib/moex.rb, line 154
def self.connection
  @connection ||= Faraday.new(MOEX_ISS_API_BASE_URL) do |connection|
    connection.response :raise_error
    connection.response :csv, CSV_PARSE_OPTS
    connection.adapter Faraday.default_adapter
  end
end
empty_table?(table) click to toggle source
# File lib/moex.rb, line 70
def self.empty_table?(table)
  return true if table.empty?

  # For some reason, if `start` is greater that a table size, then
  # the ISS responds with a single table row containing only null
  # values.
  if table.size == 1
    row = table[0]

    return row.all? { |_key, value| value.nil? }
  end

  false
end
load_prices(date:, start:) click to toggle source
# File lib/moex.rb, line 114
def self.load_prices(date:, start:)
  response = connection.get do |request|
    request.url '/iss/history/engines/stock/markets/shares/boards/tqbr/securities.csv'
    request.params['iss.only'] = 'history'
    request.params['iss.delimiter'] = ';'
    request.params['iss.dp'] = 'point'
    request.params['date'] = date if date
    request.params['start'] = start
  end

  response.body
end
load_securities(params, start:) click to toggle source
# File lib/moex.rb, line 54
def self.load_securities(params, start:)
  url = format('/iss/securitygroups/%<security_group>s/collections/%<collection>s/securities.csv', params)

  response = connection.get do |request|
    request.url(url)
    request.params['iss.only'] = 'securities'
    request.params['iss.delimiter'] = ';'
    request.params['iss.dp'] = 'point'
    request.params['start'] = start
  end

  response.body
end