From dd11ed7b9f4904ad036033a58906f50e7a267244 Mon Sep 17 00:00:00 2001 From: Florent Tapponnier <160007691+Flotapponnier@users.noreply.github.com> Date: Wed, 29 Jul 2026 21:18:35 +0200 Subject: [PATCH] fix: remove betfair venue (geo-restricted from FR + EU probe nodes) --- benchmarks/pm-api-latency.yml | 27 +-- benchmarks/pm-rate-limits.yml | 17 -- .../pm-rate-limits/cmd/script/betfair_auth.go | 160 ------------------ harnesses/pm-rate-limits/cmd/script/config.go | 21 +-- harnesses/pm-rate-limits/cmd/script/main.go | 4 - harnesses/pm-rate-limits/cmd/script/pin.go | 57 ------- harnesses/pm-rate-limits/cmd/script/venues.go | 50 ------ src/lib/pm-venue-context.ts | 1 - 8 files changed, 11 insertions(+), 326 deletions(-) delete mode 100644 harnesses/pm-rate-limits/cmd/script/betfair_auth.go diff --git a/benchmarks/pm-api-latency.yml b/benchmarks/pm-api-latency.yml index ef7d1e2b..6dea781f 100644 --- a/benchmarks/pm-api-latency.yml +++ b/benchmarks/pm-api-latency.yml @@ -108,17 +108,13 @@ per_chain_explainer: - slug: metaculus h2: Metaculus API latency body: "Metaculus is a public forecasting aggregator with no real-money markets, probed via its /api2/questions/{id}/ endpoint every 5 seconds from three regions. No authentication is required and no CDN cache layer was detected on the single-question endpoint. Metaculus has no order book and no WebSocket: all data access is REST. The uptime panel shows the live health gauge for the question API; the latency reflects the Django origin server without a CDN buffer. For builders querying aggregate community forecasts rather than market prices, Metaculus is the only venue in this cohort with a purely public read API at sub-second latency." - - slug: betfair - h2: Betfair API latency - body: "Betfair is the world's largest regulated betting exchange, probed via its Exchange API listMarketBook endpoint (POST with JSON body). Unlike every other venue in this cohort, all Betfair API calls are POST and require X-Application (developer AppKey) plus X-Authentication (session token). Betfair's infrastructure is UK-centric, so latency from us-east and sgp is higher than from eu-west. The latency shown is the full round trip of a listMarketBook call on a pinned binary market. For how Betfair behaves under rising request rates, see the pm-rate-limits bench." - source: https://github.com/ChainBench/OpenChainBench/tree/main/harnesses/pm-rate-limits prometheus: window: 24h expected_freshness_seconds: 300 -rank_matrix_query: 1000 * label_replace(histogram_quantile(0.50, sum by (venue, region, le) (rate(pmapi_request_duration_seconds_bucket{source="direct",venue=~"polymarket|kalshi|limitless|manifold|myriad|predictit|smarkets|metaculus|betfair",conn="warm",class="price",cache!="hit"}[24h]))), "provider", "$1", "venue", "(.+)") +rank_matrix_query: 1000 * label_replace(histogram_quantile(0.50, sum by (venue, region, le) (rate(pmapi_request_duration_seconds_bucket{source="direct",venue=~"polymarket|kalshi|limitless|manifold|myriad|predictit|smarkets|metaculus",conn="warm",class="price",cache!="hit"}[24h]))), "provider", "$1", "venue", "(.+)") dimensions: venue: @@ -131,7 +127,6 @@ dimensions: - { value: predictit, label: PredictIt } - { value: smarkets, label: Smarkets } - { value: metaculus, label: Metaculus } - - { value: betfair, label: Betfair } region: - { value: all, label: All regions } - { value: us-east, label: US East } @@ -142,21 +137,21 @@ metric_panels: - id: uptime_24h label: Uptime 24h description: "Share of probe cycles that succeeded over the last 24 hours, averaged across the three probe regions. 100 percent means every 5 second probe of the venue's API came back healthy." - metric: 100 * avg(avg_over_time(pmapi_health{source="direct",venue=~"polymarket|kalshi|limitless|manifold|myriad|predictit|smarkets|metaculus|betfair"}[24h])) + metric: 100 * avg(avg_over_time(pmapi_health{source="direct",venue=~"polymarket|kalshi|limitless|manifold|myriad|predictit|smarkets|metaculus"}[24h])) label_key: venue unit: pct higher_is_better: true - id: uptime_7d label: Uptime 7d description: "Same health gauge averaged over 7 days. Short outages that vanish from the 24h figure stay visible here for a week." - metric: 100 * avg(avg_over_time(pmapi_health{source="direct",venue=~"polymarket|kalshi|limitless|manifold|myriad|predictit|smarkets|metaculus|betfair"}[7d])) + metric: 100 * avg(avg_over_time(pmapi_health{source="direct",venue=~"polymarket|kalshi|limitless|manifold|myriad|predictit|smarkets|metaculus"}[7d])) label_key: venue unit: pct higher_is_better: true - id: uptime_30d label: Uptime 30d description: "Same health gauge averaged over 30 days. Feeds the ledger's 30d window toggle so a one-hour outage that's already aged out of the 24h panel still shows up in the table over a month." - metric: 100 * avg(avg_over_time(pmapi_health{source="direct",venue=~"polymarket|kalshi|limitless|manifold|myriad|predictit|smarkets|metaculus|betfair"}[30d])) + metric: 100 * avg(avg_over_time(pmapi_health{source="direct",venue=~"polymarket|kalshi|limitless|manifold|myriad|predictit|smarkets|metaculus"}[30d])) label_key: venue unit: pct higher_is_better: true @@ -164,7 +159,7 @@ metric_panels: - id: cold_connect_p50 label: Cold connect description: "TCP plus TLS handshake time on the once a minute cold probe with keep alives disabled. The startup cost a brand new client pays before its first request." - metric: 1000 * histogram_quantile(0.50, sum(rate(pmapi_request_connect_seconds_bucket{source="direct",venue=~"polymarket|kalshi|limitless|manifold|myriad|predictit|smarkets|metaculus|betfair"}[24h])) by (le)) + metric: 1000 * histogram_quantile(0.50, sum(rate(pmapi_request_connect_seconds_bucket{source="direct",venue=~"polymarket|kalshi|limitless|manifold|myriad|predictit|smarkets|metaculus"}[24h])) by (le)) label_key: venue unit: ms higher_is_better: false @@ -281,15 +276,3 @@ providers: sample_size: sum(increase(pmapi_requests_total{venue="metaculus",source="direct",class="price",conn="warm",outcome!="probe_invalid"}[24h])) series: 1000 * histogram_quantile(0.50, sum(rate(pmapi_request_duration_seconds_bucket{venue="metaculus",source="direct",class="price",conn="warm",cache!="hit"}[1h])) by (le)) - - slug: betfair - name: Betfair - tag: "UK FCA-regulated exchange, auth required, price endpoint is listMarketBook POST" - formula: "p50 of warm round trips against the Exchange API listMarketBook endpoint (POST, pinned binary market), successful requests only, 24h window. Requires BETFAIR_APP_KEY + credentials to run." - queries: - p50: 1000 * histogram_quantile(0.50, sum(rate(pmapi_request_duration_seconds_bucket{venue="betfair",source="direct",class="price",conn="warm",cache!="hit"}[24h])) by (le)) - p90: 1000 * histogram_quantile(0.90, sum(rate(pmapi_request_duration_seconds_bucket{venue="betfair",source="direct",class="price",conn="warm",cache!="hit"}[24h])) by (le)) - p99: 1000 * histogram_quantile(0.99, sum(rate(pmapi_request_duration_seconds_bucket{venue="betfair",source="direct",class="price",conn="warm",cache!="hit"}[24h])) by (le)) - mean: 1000 * sum(rate(pmapi_request_duration_seconds_sum{venue="betfair",source="direct",class="price",conn="warm",cache!="hit"}[24h])) / sum(rate(pmapi_request_duration_seconds_count{venue="betfair",source="direct",class="price",conn="warm",cache!="hit"}[24h])) - success: clamp_max(sum(rate(pmapi_requests_total{venue="betfair",source="direct",class="price",conn="warm",outcome="ok"}[24h])) / sum(rate(pmapi_requests_total{venue="betfair",source="direct",class="price",conn="warm",outcome!="probe_invalid"}[24h])), 1) - sample_size: sum(increase(pmapi_requests_total{venue="betfair",source="direct",class="price",conn="warm",outcome!="probe_invalid"}[24h])) - series: 1000 * histogram_quantile(0.50, sum(rate(pmapi_request_duration_seconds_bucket{venue="betfair",source="direct",class="price",conn="warm",cache!="hit"}[1h])) by (le)) diff --git a/benchmarks/pm-rate-limits.yml b/benchmarks/pm-rate-limits.yml index e0451a35..5ee0d3d7 100644 --- a/benchmarks/pm-rate-limits.yml +++ b/benchmarks/pm-rate-limits.yml @@ -114,10 +114,6 @@ per_chain_explainer: - slug: metaculus h2: Metaculus rate limits body: "Metaculus does not document API rate limits. The harness uses the most conservative ramp in the cohort (5/10/20 requests per 10 seconds) with the standard abort guard. There is no order book endpoint and no WebSocket. Because Metaculus is a forecasting aggregator rather than a betting exchange, its API is designed for low-frequency access; polling faster than once per 5 seconds on a single question likely hits undocumented limits. Warm price endpoint p50: {{p50:metaculus}}." - - slug: betfair - h2: Betfair rate limits - body: "Betfair's Exchange API documents a rate limit of 5 requests per second per AppKey for the listMarketBook endpoint, enforced with 429 responses. The daily ramp uses tiers of 10/25/50 requests per 10 seconds and stops at the first 429, consistent with the documented contract. Unlike Polymarket, Betfair does not queue under Cloudflare — it applies a hard token bucket. All API calls are POST with JSON body and require X-Application (AppKey) plus X-Authentication (session token). Betfair is the only venue in this cohort where the price probe is a POST request. Warm book p50: {{p50:betfair}}." - source: https://github.com/ChainBench/OpenChainBench/tree/main/harnesses/pm-rate-limits prometheus: @@ -137,7 +133,6 @@ dimensions: - { value: predictit, label: PredictIt } - { value: smarkets, label: Smarkets } - { value: metaculus, label: Metaculus } - - { value: betfair, label: Betfair } region: - { value: all, label: All regions } - { value: us-east, label: US East } @@ -289,15 +284,3 @@ providers: sample_size: sum(increase(pmapi_requests_total{venue="metaculus",class="price",conn="warm",outcome!="probe_invalid"}[24h])) series: 1000 * histogram_quantile(0.50, sum(rate(pmapi_request_duration_seconds_bucket{cache!="hit",venue="metaculus",class="price",conn="warm"}[1h])) by (le)) - - slug: betfair - name: Betfair - tag: "UK FCA-regulated exchange, auth required, listMarketBook POST, token bucket rate limiting" - formula: "p50 of warm listMarketBook POST requests against the Exchange API for the pinned binary market, successful requests only, 24h window. Requires BETFAIR_APP_KEY + credentials. Ramp at 10/25/50 per 10s, stops on first 429." - queries: - p50: 1000 * histogram_quantile(0.50, sum(rate(pmapi_request_duration_seconds_bucket{cache!="hit",venue="betfair",class="book",conn="warm"}[24h])) by (le)) - p90: 1000 * histogram_quantile(0.90, sum(rate(pmapi_request_duration_seconds_bucket{cache!="hit",venue="betfair",class="book",conn="warm"}[24h])) by (le)) - p99: 1000 * histogram_quantile(0.99, sum(rate(pmapi_request_duration_seconds_bucket{cache!="hit",venue="betfair",class="book",conn="warm"}[24h])) by (le)) - mean: 1000 * sum(rate(pmapi_request_duration_seconds_sum{cache!="hit",venue="betfair",class="book",conn="warm"}[24h])) / sum(rate(pmapi_request_duration_seconds_count{cache!="hit",venue="betfair",class="book",conn="warm"}[24h])) - success: clamp_max(sum(rate(pmapi_requests_total{venue="betfair",class="book",conn="warm",outcome="ok"}[24h])) / sum(rate(pmapi_requests_total{venue="betfair",class="book",conn="warm",outcome!="probe_invalid"}[24h])), 1) - sample_size: sum(increase(pmapi_requests_total{venue="betfair",class="book",conn="warm",outcome!="probe_invalid"}[24h])) - series: 1000 * histogram_quantile(0.50, sum(rate(pmapi_request_duration_seconds_bucket{cache!="hit",venue="betfair",class="book",conn="warm"}[1h])) by (le)) diff --git a/harnesses/pm-rate-limits/cmd/script/betfair_auth.go b/harnesses/pm-rate-limits/cmd/script/betfair_auth.go deleted file mode 100644 index 98882669..00000000 --- a/harnesses/pm-rate-limits/cmd/script/betfair_auth.go +++ /dev/null @@ -1,160 +0,0 @@ -package main - -import ( - "encoding/json" - "fmt" - "log" - "net/http" - "net/url" - "strings" - "sync" - "time" -) - -// Package-level Betfair session state. Managed by initBetfairAuth. -// betfairRequestMutator() reads from these; pinBetfair reads from these. -var ( - betfairMu sync.RWMutex - betfairToken string - betfairAppKey__ string -) - -// initBetfairAuth starts the background goroutine that keeps the Betfair -// session token alive. Call once at startup when credentials are present. -func initBetfairAuth(cfg Config) { - betfairMu.Lock() - betfairAppKey__ = cfg.BetfairAppKey - if cfg.BetfairSessionToken != "" { - betfairToken = cfg.BetfairSessionToken - } - betfairMu.Unlock() - - if cfg.BetfairSessionToken != "" { - // Pre-supplied token: keepalive only - go betfairKeepaliveLoop(cfg) - return - } - if cfg.BetfairUsername != "" && cfg.BetfairPassword != "" { - go betfairLoginLoop(cfg) - } -} - -func betfairLoginLoop(cfg Config) { - for { - token, err := betfairLogin(cfg.BetfairUsername, cfg.BetfairPassword, cfg.BetfairAppKey) - if err != nil { - log.Printf("[betfair-auth] login failed: %v (retry in 60s)", err) - time.Sleep(60 * time.Second) - continue - } - betfairMu.Lock() - betfairToken = token - betfairMu.Unlock() - log.Printf("[betfair-auth] session token refreshed") - time.Sleep(20 * time.Hour) // tokens valid 24h; refresh after 20h - } -} - -func betfairKeepaliveLoop(cfg Config) { - t := time.NewTicker(15 * time.Minute) - defer t.Stop() - for range t.C { - if err := betfairKeepalive(cfg.BetfairAppKey); err != nil { - log.Printf("[betfair-auth] keepalive failed: %v", err) - if cfg.BetfairUsername != "" && cfg.BetfairPassword != "" { - if token, err2 := betfairLogin(cfg.BetfairUsername, cfg.BetfairPassword, cfg.BetfairAppKey); err2 == nil { - betfairMu.Lock() - betfairToken = token - betfairMu.Unlock() - log.Printf("[betfair-auth] re-logged in after keepalive failure") - } - } - } - } -} - -func betfairLogin(username, password, appKey string) (string, error) { - data := url.Values{} - data.Set("username", username) - data.Set("password", password) - - req, err := http.NewRequest(http.MethodPost, - "https://identitysso.betfair.com/api/login", - strings.NewReader(data.Encode())) - if err != nil { - return "", err - } - req.Header.Set("X-Application", appKey) - req.Header.Set("Content-Type", "application/x-www-form-urlencoded") - req.Header.Set("Accept", "application/json") - - client := &http.Client{Timeout: 15 * time.Second} - resp, err := client.Do(req) - if err != nil { - return "", err - } - defer resp.Body.Close() - - var result struct { - Token string `json:"token"` - Status string `json:"status"` - Error string `json:"error"` - } - if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { - return "", fmt.Errorf("decode: %w", err) - } - if result.Status != "SUCCESS" { - return "", fmt.Errorf("status=%q error=%q", result.Status, result.Error) - } - return result.Token, nil -} - -func betfairKeepalive(appKey string) error { - betfairMu.RLock() - token := betfairToken - betfairMu.RUnlock() - - req, err := http.NewRequest(http.MethodGet, - "https://identitysso.betfair.com/api/keepAlive", nil) - if err != nil { - return err - } - req.Header.Set("X-Application", appKey) - req.Header.Set("X-Authentication", token) - req.Header.Set("Accept", "application/json") - - client := &http.Client{Timeout: 10 * time.Second} - resp, err := client.Do(req) - if err != nil { - return err - } - defer resp.Body.Close() - - var result struct { - Status string `json:"status"` - } - if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { - return fmt.Errorf("decode: %w", err) - } - if result.Status != "SUCCESS" { - return fmt.Errorf("keepalive status=%q", result.Status) - } - return nil -} - -// betfairRequestMutator returns a function that stamps the current Betfair -// AppKey and session token onto outgoing requests. -func betfairRequestMutator() func(*http.Request) { - return func(req *http.Request) { - betfairMu.RLock() - appKey := betfairAppKey__ - token := betfairToken - betfairMu.RUnlock() - if appKey != "" { - req.Header.Set("X-Application", appKey) - } - if token != "" { - req.Header.Set("X-Authentication", token) - } - } -} diff --git a/harnesses/pm-rate-limits/cmd/script/config.go b/harnesses/pm-rate-limits/cmd/script/config.go index fd679571..107293db 100644 --- a/harnesses/pm-rate-limits/cmd/script/config.go +++ b/harnesses/pm-rate-limits/cmd/script/config.go @@ -17,12 +17,8 @@ type Config struct { MobulaAPIKey string // Authorization: on api.mobula.io PredexonAPIKey string // x-api-key on api.predexon.com DefinedSessionCookie string // 7-day cookie used to mint Codex JWT - KalshiAPIKeyID string // KALSHI-ACCESS-KEY header for WS auth - KalshiPrivateKeyPEM string // RSA private key PEM for WS signature - BetfairAppKey string // BETFAIR_APP_KEY — developer application key - BetfairUsername string // BETFAIR_USERNAME — login credential for session token - BetfairPassword string // BETFAIR_PASSWORD — login credential for session token - BetfairSessionToken string // BETFAIR_SESSION_TOKEN — pre-obtained token (alternative to user/pass) + KalshiAPIKeyID string // KALSHI-ACCESS-KEY header for WS auth + KalshiPrivateKeyPEM string // RSA private key PEM for WS signature } func loadConfig() Config { @@ -30,17 +26,12 @@ func loadConfig() Config { MobulaAPIKey: strings.TrimSpace(os.Getenv("MOBULA_API_KEY")), PredexonAPIKey: strings.TrimSpace(os.Getenv("PREDEXON_API_KEY")), DefinedSessionCookie: strings.TrimSpace(os.Getenv("DEFINED_SESSION_COOKIE")), - KalshiAPIKeyID: strings.TrimSpace(os.Getenv("KALSHI_API_KEY_ID")), - KalshiPrivateKeyPEM: strings.TrimSpace(os.Getenv("KALSHI_PRIVATE_KEY_PEM")), - BetfairAppKey: strings.TrimSpace(os.Getenv("BETFAIR_APP_KEY")), - BetfairUsername: strings.TrimSpace(os.Getenv("BETFAIR_USERNAME")), - BetfairPassword: strings.TrimSpace(os.Getenv("BETFAIR_PASSWORD")), - BetfairSessionToken: strings.TrimSpace(os.Getenv("BETFAIR_SESSION_TOKEN")), + KalshiAPIKeyID: strings.TrimSpace(os.Getenv("KALSHI_API_KEY_ID")), + KalshiPrivateKeyPEM: strings.TrimSpace(os.Getenv("KALSHI_PRIVATE_KEY_PEM")), } - fmt.Printf("[providers] mobula=%v predexon=%v codex=%v kalshi_ws=%v betfair=%v\n", + fmt.Printf("[providers] mobula=%v predexon=%v codex=%v kalshi_ws=%v\n", cfg.MobulaAPIKey != "", cfg.PredexonAPIKey != "", cfg.DefinedSessionCookie != "", - cfg.KalshiAPIKeyID != "" && cfg.KalshiPrivateKeyPEM != "", - cfg.BetfairAppKey != "") + cfg.KalshiAPIKeyID != "" && cfg.KalshiPrivateKeyPEM != "") return cfg } diff --git a/harnesses/pm-rate-limits/cmd/script/main.go b/harnesses/pm-rate-limits/cmd/script/main.go index 8ed7b5ee..9a540c9a 100644 --- a/harnesses/pm-rate-limits/cmd/script/main.go +++ b/harnesses/pm-rate-limits/cmd/script/main.go @@ -32,10 +32,6 @@ func main() { ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) defer stop() - if cfg.BetfairAppKey != "" { - initBetfairAuth(cfg) - } - pinClient := &http.Client{Timeout: 20 * time.Second} vs := venues(cfg) stateByVenue := map[string]*venueState{} diff --git a/harnesses/pm-rate-limits/cmd/script/pin.go b/harnesses/pm-rate-limits/cmd/script/pin.go index c1998c31..3fc63c43 100644 --- a/harnesses/pm-rate-limits/cmd/script/pin.go +++ b/harnesses/pm-rate-limits/cmd/script/pin.go @@ -1,7 +1,6 @@ package main import ( - "bytes" "context" "encoding/json" "errors" @@ -370,62 +369,6 @@ func pinMetaculus(ctx context.Context, c *http.Client, avoid string) (Pin, error return Pin{}, errors.New("metaculus: no open binary question with >24h close horizon") } -func pinBetfair(ctx context.Context, c *http.Client, avoid string) (Pin, error) { - const catalogueURL = "https://api.betfair.com/exchange/betting/rest/v1.0/listMarketCatalogue/" - body := []byte(`{"filter":{"inPlayOnly":false},"sort":"FIRST_TO_START","maxResults":"200","marketProjection":["MARKET_START_TIME","RUNNER_DESCRIPTION"]}`) - - var markets []struct { - MarketID string `json:"marketId"` - MarketStartTime string `json:"marketStartTime"` - Runners []json.RawMessage `json:"runners"` - } - if err := postJSON(ctx, c, catalogueURL, body, &markets, betfairRequestMutator()); err != nil { - return Pin{}, fmt.Errorf("betfair catalogue: %w", err) - } - for _, m := range markets { - if m.MarketID == avoid || len(m.Runners) != 2 { - continue - } - // Betfair uses "2006-01-02T15:04:05.000Z" format - start, err := time.Parse("2006-01-02T15:04:05.000Z", m.MarketStartTime) - if err != nil { - start, err = time.Parse(time.RFC3339, m.MarketStartTime) - if err != nil { - continue - } - } - if time.Until(start) < 48*time.Hour { - continue - } - return Pin{Market: m.MarketID, Expiry: start}, nil - } - return Pin{}, errors.New("betfair: no binary market with >48h horizon in first 200 results") -} - -// postJSON sends a POST request with a JSON body and decodes the JSON response. -func postJSON(ctx context.Context, c *http.Client, url string, body []byte, out any, mutator func(*http.Request)) error { - req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewReader(body)) - if err != nil { - return err - } - req.Header.Set("User-Agent", userAgent) - req.Header.Set("Accept", "application/json") - req.Header.Set("Content-Type", "application/json") - if mutator != nil { - mutator(req) - } - resp, err := c.Do(req) - if err != nil { - return err - } - defer resp.Body.Close() - if resp.StatusCode != http.StatusOK { - b, _ := io.ReadAll(io.LimitReader(resp.Body, 1<<10)) - return fmt.Errorf("status %d: %s", resp.StatusCode, b) - } - return json.NewDecoder(io.LimitReader(resp.Body, 8<<20)).Decode(out) -} - // stalenessPolymarket reads the ms timestamp Polymarket embeds in every book // response (string in the docs, tolerate a bare number). func stalenessPolymarket(class string, body []byte) (int64, bool) { diff --git a/harnesses/pm-rate-limits/cmd/script/venues.go b/harnesses/pm-rate-limits/cmd/script/venues.go index 0696061c..a75cf8e9 100644 --- a/harnesses/pm-rate-limits/cmd/script/venues.go +++ b/harnesses/pm-rate-limits/cmd/script/venues.go @@ -195,56 +195,6 @@ func venues(cfg Config) []*Venue { RampRates: []int{5, 10, 20}, }, } - // Betfair: UK-regulated exchange, auth required. All Exchange API calls are - // POST with JSON body, so we use Class.Method="POST" + Class.BodyFn. - // Only wired when BETFAIR_APP_KEY is set. - if cfg.BetfairAppKey != "" && (cfg.BetfairUsername != "" || cfg.BetfairSessionToken != "") { - vs = append(vs, &Venue{ - Slug: "betfair", - Classes: []Class{ - { - Name: "book", Method: "POST", - Interval: 5 * time.Second, Timeout: 8 * time.Second, - URL: func(Pin) string { - return "https://api.betfair.com/exchange/betting/rest/v1.0/listMarketBook/" - }, - BodyFn: func(p Pin) []byte { - if p.Market == "" { - return nil - } - return []byte(`{"marketIds":["` + p.Market + `"],"priceProjection":{"priceData":["EX_BEST_OFFERS"]}}`) - }, - }, - { - Name: "price", Method: "POST", - Interval: 5 * time.Second, Timeout: 8 * time.Second, - URL: func(Pin) string { - return "https://api.betfair.com/exchange/betting/rest/v1.0/listMarketBook/" - }, - BodyFn: func(p Pin) []byte { - if p.Market == "" { - return nil - } - return []byte(`{"marketIds":["` + p.Market + `"]}`) - }, - }, - { - Name: "list", Method: "POST", - Interval: 30 * time.Second, Timeout: 15 * time.Second, - URL: func(Pin) string { - return "https://api.betfair.com/exchange/betting/rest/v1.0/listMarketCatalogue/" - }, - BodyFn: func(Pin) []byte { - return []byte(`{"filter":{"inPlayOnly":false},"sort":"FIRST_TO_START","maxResults":"20","marketProjection":["MARKET_START_TIME"]}`) - }, - }, - }, - PinFunc: pinBetfair, - RequestMutator: betfairRequestMutator(), - RampRates: []int{10, 25, 50}, - StopOn429: true, - }) - } return vs } diff --git a/src/lib/pm-venue-context.ts b/src/lib/pm-venue-context.ts index ed170239..cdec7fb6 100644 --- a/src/lib/pm-venue-context.ts +++ b/src/lib/pm-venue-context.ts @@ -26,7 +26,6 @@ export const PM_VENUE_META: Record myriad: { url: "https://myriad.markets", chainLabel: "Abstract L2" }, manifold: { url: "https://manifold.markets", chainLabel: "Offchain" }, metaculus: { url: "https://www.metaculus.com", chainLabel: "Offchain" }, - betfair: { url: "https://www.betfair.com", chainLabel: "Offchain UK" }, }; export const PM_FEED_META: Record = {