class CoinTools::Cryptowatch

Constants

BASE_URL
DAYS_FOR_PERIODS

we expect this many days worth of data for a given period precision (in seconds); NOT guaranteed by the API

DataPoint

Public Instance Methods

exchanges() click to toggle source
# File lib/cointools/cryptowatch.rb, line 22
def exchanges
  @exchanges ||= get_exchanges
end
get_current_price(exchange, market) click to toggle source
# File lib/cointools/cryptowatch.rb, line 96
def get_current_price(exchange, market)
  raise InvalidExchangeError if exchange.to_s.empty?
  raise InvalidSymbolError if market.to_s.empty?

  url = URI("#{BASE_URL}/markets/#{exchange}/#{market}/price")

  response = Request.get(url)

  case response
  when Net::HTTPSuccess
    json = Utils.parse_json(response.body)
    raise JSONError.new(response) unless json.is_a?(Hash)

    data = json['result']
    allowance = json['allowance']
    raise JSONError.new(response) unless data.is_a?(Hash) && allowance.is_a?(Hash)

    price = data['price']
    raise NoDataError.new(response) unless price

    return DataPoint.new(
      price: price,
      time: nil,
      api_time_spent: allowance['cost'],
      api_time_remaining: allowance['remaining']
    )
  when Net::HTTPNotFound
    raise UnknownCoinError.new(response)
  when Net::HTTPClientError
    raise BadRequestError.new(response)
  else
    raise ServiceUnavailableError.new(response)
  end
end
get_markets(exchange) click to toggle source
# File lib/cointools/cryptowatch.rb, line 26
def get_markets(exchange)
  raise InvalidExchangeError if exchange.to_s.empty?

  url = URI("#{BASE_URL}/markets/#{exchange}")

  response = Request.get(url)

  case response
  when Net::HTTPSuccess
    json = Utils.parse_json(response.body)
    raise JSONError.new(response) unless json.is_a?(Hash) && json['result'].is_a?(Array)

    return json['result'].select { |m| m['active'] == true }.map { |m| m['pair'] }.sort
  when Net::HTTPNotFound
    raise UnknownExchangeError.new(response)
  when Net::HTTPClientError
    raise BadRequestError.new(response)
  else
    raise ServiceUnavailableError.new(response)
  end
end
get_price(exchange, market, time = nil) click to toggle source
# File lib/cointools/cryptowatch.rb, line 48
def get_price(exchange, market, time = nil)
  raise InvalidExchangeError if exchange.to_s.empty?
  raise InvalidSymbolError if market.to_s.empty?

  if time.nil?
    return get_current_price(exchange, market)
  elsif time.is_a?(String)
    time = Utils.parse_time(time)
  end

  (time <= Time.now) or raise InvalidDateError.new('Future date was passed')
  (time.year >= 2009) or raise InvalidDateError.new('Too early date was passed')

  unixtime = time.to_i
  current_time = Time.now.to_i
  url = URI("#{BASE_URL}/markets/#{exchange}/#{market}/ohlc?after=#{unixtime}")

  response = Request.get(url)

  case response
  when Net::HTTPSuccess
    json = Utils.parse_json(response.body)
    raise JSONError.new(response) unless json.is_a?(Hash)

    data = json['result']
    allowance = json['allowance']
    raise JSONError.new(response) unless data.is_a?(Hash) && allowance.is_a?(Hash)

    timestamp, o, h, l, c, volume = best_matching_record(data, unixtime, current_time)
    raise NoDataError.new(response, 'No price data returned') unless timestamp && o

    actual_time = Time.at(timestamp)

    return DataPoint.new(
      price: o,
      time: actual_time,
      api_time_spent: allowance['cost'],
      api_time_remaining: allowance['remaining']
    )
  when Net::HTTPNotFound
    raise UnknownCoinError.new(response)
  when Net::HTTPClientError
    raise BadRequestError.new(response)
  else
    raise ServiceUnavailableError.new(response)
  end
end
get_price_fast(exchange, market, time = nil) click to toggle source
# File lib/cointools/cryptowatch.rb, line 131
def get_price_fast(exchange, market, time = nil)
  raise InvalidExchangeError if exchange.to_s.empty?
  raise InvalidSymbolError if market.to_s.empty?

  if time.nil?
    return get_current_price(exchange, market)
  elsif time.is_a?(String)
    time = Utils.parse_time(time)
  end

  (time <= Time.now) or raise InvalidDateError.new('Future date was passed')
  (time.year >= 2009) or raise InvalidDateError.new('Too early date was passed')

  period = precision_for_time(time)

  if period.nil?
    return get_price(exchange, market, time)
  end

  unixtime = time.to_i
  current_time = Time.now.to_i
  url = URI("#{BASE_URL}/markets/#{exchange}/#{market}/ohlc?after=#{unixtime}&periods=#{period}")

  response = Request.get(url)

  case response
  when Net::HTTPSuccess
    json = Utils.parse_json(response.body)
    raise JSONError.new(response) unless json.is_a?(Hash)

    data = json['result']
    allowance = json['allowance']
    raise JSONError.new(response) unless data.is_a?(Hash) && allowance.is_a?(Hash)

    timestamp, o, h, l, c, volume = best_matching_record(data, unixtime, current_time)
    raise NoDataError.new(response, 'No price data returned') unless timestamp && o

    actual_time = Time.at(timestamp)

    return DataPoint.new(
      price: o,
      time: actual_time,
      api_time_spent: allowance['cost'],
      api_time_remaining: allowance['remaining']
    )
  when Net::HTTPNotFound
    raise UnknownCoinError.new(response)
  when Net::HTTPClientError
    raise BadRequestError.new(response)
  else
    raise ServiceUnavailableError.new(response)
  end
end

Private Instance Methods

best_matching_record(data, unixtime, request_time) click to toggle source
# File lib/cointools/cryptowatch.rb, line 188
def best_matching_record(data, unixtime, request_time)
  candidates = []

  data.keys.sort_by { |k| k.to_i }.each do |k|
    records = data[k] || []
    previous = nil

    records.each do |record|
      timestamp, o, h, l, c, volume = record

      if timestamp >= unixtime
        candidates.push(record) unless timestamp > request_time
        break
      else
        previous = record
      end
    end

    candidates.push(previous) if previous
  end

  candidates.sort_by { |record| (record[0] - unixtime).abs }.first
end
get_exchanges() click to toggle source
# File lib/cointools/cryptowatch.rb, line 227
def get_exchanges
  url = URI("#{BASE_URL}/exchanges")

  response = Request.get(url)

  case response
  when Net::HTTPSuccess
    json = Utils.parse_json(response.body)
    raise JSONError.new(response) unless json.is_a?(Hash) && json['result'].is_a?(Array)

    return json['result'].select { |e| e['active'] == true }.map { |e| e['symbol'] }.sort
  when Net::HTTPClientError
    raise BadRequestError.new(response)
  else
    raise ServiceUnavailableError.new(response)
  end
end
precision_for_time(time) click to toggle source
# File lib/cointools/cryptowatch.rb, line 212
def precision_for_time(time)
  now = Time.now

  DAYS_FOR_PERIODS.keys.sort.each do |period|
    days = DAYS_FOR_PERIODS[period]
    earliest_date = now - days * 86400

    if earliest_date < time
      return period
    end
  end

  nil
end