Class: TradingviewScreener::ScreenData::FieldContract

Inherits:
Object
  • Object
show all
Defined in:
lib/tradingview_screener/screen_data.rb

Overview

Field names used in filter and sort expressions are validated against the scanner's server-side schema. Projection-only aliases such as ticker-view are intentionally excluded because TradingView does not publish them in /metainfo even though /scan accepts them as columns.

Constant Summary collapse

METAINFO_URL =
"https://scanner.tradingview.com/%<market>s/metainfo"
MARKET_RE =
/\A[a-z0-9_-]+\z/i
PROJECTION_ALIASES =
Set.new(%w[ticker-view sector.tr AnalystRating AnalystRating.tr]).freeze

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(market_fields) ⇒ FieldContract

Returns a new instance of FieldContract.



124
125
126
# File 'lib/tradingview_screener/screen_data.rb', line 124

def initialize(market_fields)
  @market_fields = market_fields.transform_keys(&:to_s).transform_values { |fields| Set.new(fields.map(&:to_s)) }
end

Class Method Details

.fetch(markets:, client:) ⇒ Object



100
101
102
103
104
105
106
107
108
109
110
# File 'lib/tradingview_screener/screen_data.rb', line 100

def self.fetch(markets:, client:)
  market_fields = Array(markets).map(&:to_s).uniq.to_h do |market|
    unless market.match?(MARKET_RE)
      raise FieldContractError, "invalid scanner market for metainfo: #{market.inspect}"
    end

    body = client.get(format(METAINFO_URL, market: market))
    [market, parse_fields(body, market)]
  end
  new(market_fields)
end

.parse_fields(body, market) ⇒ Object



112
113
114
115
116
117
118
119
120
121
122
# File 'lib/tradingview_screener/screen_data.rb', line 112

def self.parse_fields(body, market)
  data = JSON.parse(body)
  fields = data["fields"]
  unless fields.is_a?(Array)
    raise FieldContractError, "invalid #{market} metainfo: fields must be an array"
  end

  fields.filter_map { |field| field["n"] if field.is_a?(Hash) }.to_set
rescue JSON::ParserError => e
  raise FieldContractError, "invalid #{market} metainfo JSON: #{e.message}"
end

Instance Method Details

#validate!(field, projection: false) ⇒ Object

Raises:



128
129
130
131
132
133
134
135
136
# File 'lib/tradingview_screener/screen_data.rb', line 128

def validate!(field, projection: false)
  return field if projection && PROJECTION_ALIASES.include?(field.to_s)

  missing_markets = @market_fields.filter_map { |market, fields| market unless fields.include?(field.to_s) }
  return field if missing_markets.empty?

  raise FieldContractError,
        "scanner field #{field.inspect} is absent from metainfo for: #{missing_markets.join(', ')}"
end