It was yesterday that Nathan Oyler asked me on Twitter if I could rewrite my Perl code to calculate offensive SRS and defensive SRS. Nathan, I believe, is working on a game or a simulation and wanted to be able to calculate these values. I replied, “Do you know how to calculate these?” and, after playing around a little, I can only conclude that the best way to handle this calculation is going to be a matter of debate.

That said, I have a way to calculate these numbers, but first we need a little theory. It starts with Chase Stuart’s comment on the Smart Football blog that these values are related to points for and points against. Given that, and the definition of margin of victory:

MOV(team) = ( “points for” – “points against” ) / games_played(team) = point spread/games_played(team)

We now need to define an average score. This works:

AVG_SCORE = points_for(all teams)/ games_played(all teams)

From these definitions and the hint Chase dropped, we define offensive MOV and defensive MOV this way.

OMOV(team) = ( points_for(team) – games_played(team)*AVG_SCORE ) / games_played(team)

DMOV(team) = ( games_played(team)*AVG_SCORE – points_against(team) ) / games_played(team)

So, rather than plugging in MOV to a SRS linear equation solver, you can plug in offensive MOV and defensive MOV and then you can get numbers that will help you calculate an OSRS and a DSRS.

I say will get you numbers because there a  gotcha, in that whenever you have OSOS and DSOS and they are of opposite sign, then there is no unique solution to the equation


as I can choose any constant c and the result

SOS = (OSOS + c) + (DSOS – c)

is also a solution. This kind of linear wandering around, the solver adding arbitrary constants to OSOS and DSOS, happens when you attempt to solve for these equations. The issue is, there is no one obvious solution to this problem, unlike regular SRS where the constraint “sum of all SRS must equal zero” applies.  Now if someone uncovers a constraint, let me know and I’ll be happy to code it. In the absence of such a rule so far, I’ve used this folk rule.

Reduce the magnitude of the OSOS and DSOS terms until the smaller of the two, in terms of absolute magnitude, is zero.

This is straightforward to code. That my solution is not the same as the one in Pro Football  Reference is easy enough to show. If I go to this page, I get these values for the 2007 New England Patriots. If I calculate OMOV and DMOV using my code, we can extract the DSOS and OSOS values for this calculation.

2007 New England Patriots
20.1 15.9 4.2 15.1 4.6 0.8 -0.4


and while my code uses 0.4 and 0 for OSOS and DSOS respectively, the evident values that Pro Football Reference uses are 0.8 and -0.4. All that clear now?

I’m pretty sure my SOS calculation isn’t the same as PFR’s either, as I seen differences in OSRS/DSRS that amount to a point or two. In some cases this occurs when my calc yields same signed OSOS and DSOS values, and in that case, I don’t modify them at all.

The source code I’ve used to do these calculations is given here, as a Perl module. A “snapshot” of the code fragment I use to feed the Perl module source is:


typical output is, for the 2007 season:


And yes, there are plenty of unknowns at this point. PFR has never really given any details of their OSOS/DSOS calculations, or the normalization routines they use. DSRS and OSRS as implemented by them is a “black box”. This implementation may not, in the long run, be the best of them, but it is reasonably well documented.

Update: corrected DMOV definition. Rewritten slightly for clarity.