-
Notifications
You must be signed in to change notification settings - Fork 0
feat: adds matching markets for sports by date, ticker, or slug #7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
7229ce8
cbffc30
b7bf65f
a923c7a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -28,6 +28,10 @@ def polymarket | |
| @polymarket ||= Polymarket::Client.new(clone) | ||
| end | ||
|
|
||
| def matching_markets | ||
| @matching_markets ||= MatchingMarkets.new(self) | ||
| end | ||
|
Comment on lines
+31
to
+33
|
||
|
|
||
| private | ||
|
|
||
| def full_url(path) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,96 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| module Rubyists | ||
| module Domeapi | ||
| # Matching Markets API endpoints | ||
| class MatchingMarkets | ||
| SPORTS = %w[nfl mlb cfb nba nhl cbb].freeze | ||
|
|
||
| # Filter for matching markets sports | ||
| class SportsFilter < Contract | ||
| propertize(%i[sport date]) | ||
|
|
||
| validation do | ||
| # :nocov: | ||
| params do | ||
| required(:sport).filled(:string, included_in?: SPORTS) | ||
| required(:date).filled(:string, format?: /\A\d{4}-\d{2}-\d{2}\z/) | ||
| end | ||
| # :nocov: | ||
| end | ||
| end | ||
|
|
||
| # Filter for matching markets | ||
| class Filter < Contract | ||
| propertize(%i[polymarket_market_slug kalshi_event_ticker]) | ||
|
|
||
| validation do | ||
| # :nocov: | ||
| params do | ||
| optional(:polymarket_market_slug).maybe(Types::PolymarketMarketSlug) | ||
| optional(:kalshi_event_ticker).maybe(Types::KalshiEventTicker) | ||
| end | ||
|
|
||
| rule(:polymarket_market_slug, :kalshi_event_ticker) do | ||
| if !values[:polymarket_market_slug] && !values[:kalshi_event_ticker] | ||
| key.failure('Either polymarket_market_slug or kalshi_event_ticker must be provided') | ||
| end | ||
| end | ||
| # :nocov: | ||
| end | ||
| end | ||
|
|
||
| attr_reader :client | ||
|
|
||
| # @param client [Rubyists::Domeapi::Client] | ||
| # | ||
| # @return [void] | ||
| def initialize(client = Rubyists::Domeapi::Client.new) | ||
| @client = client | ||
| end | ||
|
|
||
| # List matching markets | ||
| # | ||
| # @param filter [Filter|Hash] Filter options | ||
| # | ||
| # @return [Hash|Array] resource data | ||
| def sports(filter = Filter.new(Filter::Properties.new)) | ||
| filter = Filter.new(Filter::Properties.new(**filter)) if filter.is_a?(Hash) | ||
| raise ArgumentError, filter.errors.full_messages.join(', ') unless filter.validate({}) | ||
|
|
||
| client.get('matching-markets/sports', params: filter.to_h) | ||
| end | ||
|
|
||
| # List matching markets for a specific sport and date | ||
| # | ||
| # @param filter [SportsFilter|Hash] Filter options | ||
| # | ||
| # @return [Hash|Array] resource data | ||
| def sports_by_date(filter = SportsFilter.new(SportsFilter::Properties.new)) | ||
| filter = prepare_sports_filter(filter) | ||
| raise ArgumentError, filter.errors.full_messages.join(', ') unless filter.validate({}) | ||
|
|
||
| client.get("matching-markets/sports/#{filter.sport}", params: { date: filter.date }) | ||
| end | ||
|
|
||
| SPORTS.each do |sport| | ||
| define_method("#{sport}_on") do |date| | ||
| sports_by_date(sport: sport, date: date) | ||
| end | ||
|
|
||
| define_singleton_method("#{sport}_on") do |date| | ||
| new.send("#{sport}_on", date) | ||
| end | ||
| end | ||
|
|
||
| private | ||
|
|
||
| def prepare_sports_filter(filter) | ||
| return filter unless filter.is_a?(Hash) | ||
|
|
||
| filter[:date] = filter[:date].strftime('%Y-%m-%d') if filter[:date].respond_to?(:strftime) | ||
| SportsFilter.new(SportsFilter::Properties.new(**filter)) | ||
| end | ||
| end | ||
| end | ||
| end |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| require 'dry-types' | ||
|
|
||
| module Rubyists | ||
| module Domeapi | ||
| # Custom types for Domeapi | ||
| module Types | ||
| include Dry.Types() | ||
|
|
||
| # Polymarket slugs are typically kebab-case strings | ||
| PolymarketMarketSlug = String.constrained(format: /\A[a-z0-9]+(?:-[a-z0-9]+)+\z/) | ||
|
|
||
| # Kalshi event tickers are typically uppercase alphanumeric strings, potentially with dashes | ||
| KalshiEventTicker = String.constrained(format: /\A[A-Z0-9]+(?:-[A-Z0-9]+)+\z/) | ||
|
bougyman marked this conversation as resolved.
bougyman marked this conversation as resolved.
|
||
| end | ||
| end | ||
| end | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,112 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| require_relative '../helper' | ||
|
|
||
| describe Rubyists::Domeapi::MatchingMarkets do | ||
| let(:client) { Rubyists::Domeapi::Client.new } | ||
| let(:matching_markets) { client.matching_markets } | ||
|
|
||
| before do | ||
| Rubyists::Domeapi.configure do |config| | ||
| config.api_key = 'test_api_key' | ||
| end | ||
| end | ||
|
|
||
| describe 'Sports' do | ||
| it 'lists matching markets for sports' do | ||
| stub_request(:get, 'https://api.domeapi.io/v1/matching-markets/sports') | ||
| .with(query: { polymarket_market_slug: 'valid-slug' }) | ||
| .to_return(status: 200, body: '[]') | ||
|
|
||
| response = matching_markets.sports(polymarket_market_slug: 'valid-slug') | ||
|
|
||
| _(response).must_equal [] | ||
| end | ||
|
|
||
| it 'lists matching markets for sports with Filter object' do | ||
| stub_request(:get, 'https://api.domeapi.io/v1/matching-markets/sports') | ||
| .with(query: { polymarket_market_slug: 'valid-slug' }) | ||
| .to_return(status: 200, body: '[]') | ||
|
|
||
| filter = Rubyists::Domeapi::MatchingMarkets::Filter.new( | ||
| Rubyists::Domeapi::MatchingMarkets::Filter::Properties.new(polymarket_market_slug: 'valid-slug') | ||
| ) | ||
| response = matching_markets.sports(filter) | ||
|
|
||
| _(response).must_equal [] | ||
| end | ||
|
|
||
| it 'validates filter' do | ||
| error = _ { matching_markets.sports({}) }.must_raise ArgumentError | ||
| _(error.message).must_include 'Either polymarket_market_slug or kalshi_event_ticker must be provided' | ||
| end | ||
| end | ||
|
|
||
| describe 'Sports by Date' do | ||
| it 'lists matching markets for sport and date' do | ||
| stub_request(:get, 'https://api.domeapi.io/v1/matching-markets/sports/nfl') | ||
| .with(query: { date: '2023-01-01' }) | ||
| .to_return(status: 200, body: '[]') | ||
|
|
||
| response = matching_markets.sports_by_date(sport: 'nfl', date: '2023-01-01') | ||
|
|
||
| _(response).must_equal [] | ||
| end | ||
|
|
||
| it 'accepts Date object' do | ||
| stub_request(:get, 'https://api.domeapi.io/v1/matching-markets/sports/nfl') | ||
| .with(query: { date: '2023-01-01' }) | ||
| .to_return(status: 200, body: '[]') | ||
|
|
||
| require 'date' | ||
| response = matching_markets.sports_by_date(sport: 'nfl', date: Date.parse('2023-01-01')) | ||
|
|
||
| _(response).must_equal [] | ||
| end | ||
|
|
||
| it 'accepts SportsFilter object' do | ||
| stub_request(:get, 'https://api.domeapi.io/v1/matching-markets/sports/nfl') | ||
| .with(query: { date: '2023-01-01' }) | ||
| .to_return(status: 200, body: '[]') | ||
|
|
||
| filter = Rubyists::Domeapi::MatchingMarkets::SportsFilter.new( | ||
| Rubyists::Domeapi::MatchingMarkets::SportsFilter::Properties.new(sport: 'nfl', date: '2023-01-01') | ||
| ) | ||
| response = matching_markets.sports_by_date(filter) | ||
|
|
||
| _(response).must_equal [] | ||
| end | ||
|
|
||
| it 'validates sport' do | ||
| error = _ { matching_markets.sports_by_date(sport: 'invalid', date: '2023-01-01') }.must_raise ArgumentError | ||
| _(error.message).must_include 'Sport must be one of: nfl, mlb, cfb, nba, nhl, cbb' | ||
| end | ||
|
|
||
| it 'validates date format' do | ||
| error = _ { matching_markets.sports_by_date(sport: 'nfl', date: 'invalid') }.must_raise ArgumentError | ||
| _(error.message).must_include 'Date is in invalid format' | ||
| end | ||
| end | ||
|
|
||
| describe 'Dynamic methods' do | ||
| it 'defines methods for each sport' do | ||
| stub_request(:get, 'https://api.domeapi.io/v1/matching-markets/sports/nfl') | ||
| .with(query: { date: '2023-01-01' }) | ||
| .to_return(status: 200, body: '[]') | ||
|
|
||
| response = matching_markets.nfl_on('2023-01-01') | ||
|
|
||
| _(response).must_equal [] | ||
| end | ||
|
|
||
| it 'defines class methods for each sport' do | ||
| stub_request(:get, 'https://api.domeapi.io/v1/matching-markets/sports/nfl') | ||
| .with(query: { date: '2023-01-01' }) | ||
| .to_return(status: 200, body: '[]') | ||
|
|
||
| response = Rubyists::Domeapi::MatchingMarkets.nfl_on('2023-01-01') | ||
|
|
||
| _(response).must_equal [] | ||
| end | ||
| end | ||
| end |
Uh oh!
There was an error while loading. Please reload this page.