Team OPR

Is there a way to get a list of all teams and their OPR?
Not just at individual events.

Similar to has good data for overall team stats


If we were actually going to have a Championship, this is exactly what we were looking for!

Thank you!

The blue alliance provides OPR for each event. The top 15 OPR for each event is available on the “Insights” tab on the event page. I’m guessing there would be a way to extract this data, but that is above my pay grade.

Why the variance between TBA’s OPR and your data? For example, 987 TBA data shows 987 with a 93.04 and 4201 with 92.96 OPR… Assume other variance as well but for obvious reasons these two data points attracted my attention;)

You can use the TBA API to retrieve OPR, DPR, and CCWM values for all teams at all events. You can see the API documentation here, check out “/event/{event_key}/oprs”.

Just to mention it here as well, be aware that OPR is more useful to compare teams at the same event rather than teams at different events.

@Caleb_Sykes has a database that includes OPR:

I believe his OPR calculations are the same as those of TBA.

@lucas.kinion’s website says this right on top:

The OPR you see here WILL be different from as TBA only uses final scores for its’ OPR calculations, which has penalties baked in

This is true if you merely pull the data from TBA for each individual event. However, you could calculate OPR yourself and instead treat every single match as if it is part of a super event. This way, the resulting OPR will be more accurate as teams face more opponents.

This “World OPR” calculation can be found in many places, but to keep it simple the following Python program would work. It requires numpy and requests:

import numpy as np
import requests

api_key = "INSERT_KEY"

def get_tba_json_data(api_url):
    return requests.get(
        headers={"User-Agent": "OPRCalculator", "X-TBA-Auth-Key": api_key}

year = 2020
weeks = [0, 1]

all_teams = set()
all_matches = []

# Get team and match data
event_data = get_tba_json_data(f"events/{year}")
for event in event_data:
    if event["week"] in weeks:
        event_key = event["key"]
        event_matches = get_tba_json_data(f"event/{event_key}/matches")
        for match in event_matches:
            for team in match["alliances"]["red"]["team_keys"]:
            for team in match["alliances"]["blue"]["team_keys"]:

all_teams = sorted(all_teams)

# Construct Matrix A and vector b
A = np.zeros((len(all_matches) * 2, len(all_teams)))
b = np.zeros((len(all_matches) * 2, 1))
for i, match in enumerate(all_matches):
    if len(match["winning_alliance"]) == 0: continue
    for team in match["alliances"]["red"]["team_keys"]:
        A[i * 2, all_teams.index(int(team[3:]))] = 1
    for team in match["alliances"]["blue"]["team_keys"]:
        A[i * 2 + 1, all_teams.index(int(team[3:]))] = 1
    breakdown = match["score_breakdown"]
    b[i * 2, 0] = breakdown["red"]["totalPoints"] - breakdown["red"]["foulPoints"]
    b[i * 2 + 1, 0] = breakdown["blue"]["totalPoints"] - breakdown["blue"]["foulPoints"]

# Solve for x
x = np.linalg.lstsq(A, b, rcond=None)[0]
x = np.round(x, decimals=2)

# sort by top opr and print
team_opr_pairs = sorted(zip(all_teams, x.T.tolist()[0]),
                        key=lambda x: x[1], reverse=True)
for team, opr in team_opr_pairs:
    print(f"{team}, {opr}")

Note that for 2020 (at least until more events get played), since there has been essentially no mixing of teams between events, world OPR will be nearly equivalent to each team’s event OPR.


Just to clarify, it’s not my website, it was made by 6800 Viper Bots Valor who is an awesome team from the Austin TX area, and I just happen to use the site. Next time I get to see 6800 though, I already have some feedback I want to pass along, and maybe I’ll see if they could provide an option to toggle between penalties / no pentalties for OPR

This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.