In response to my previous post, Maciej Pichocki from the IFRS Foundation posted a question:

I would be really curious to see “trivial” EPS check in Sphinx syntax (just to get started with more realistic examples of business rules application).

EarningsPerShare(reported in currency per per share) equals ProfitLoss (duration in currency) div (NoOfShares (instant beginning of period reported in shares) + NoOfShares (instant end of period reported in shares) ) div 2

It goes without saying that in a single instance I got various periods and various units.

Maciej picks an interesting example.  To be fair, a “trivial” EPS check would look more like this:

us-gaap:EarningsPerShareBasic[] == 
  us-gaap:NetIncomeLoss[] / us-gaap:WeightedAverageNumberOfSharesOutstandingBasic[]

This is simpler (and, if you’ve got a “weighted number of shares outstanding concept”, better) as it involves three concepts in the same period, so the normal lining up of periods does what we want.

The interesting bit is the units, as we’ve got three different units:

  • Currency per xbrli:shares
  • Currency
  • xbrli:shares

Sphinx’s “lining up” behaviour automatically takes into account the division operator and applies that same division to the units, so the above example really does just work: EPS in Euros/share will be calculated from Profit in Euros, and EPS in Dollars/share will be calculated from Profit in Dollars.  This is a nice benefit of the way that units are defined in XBRL, as it allows a processor to understand the relationship between the different units.

Anyway, that’s not what was asked for.  Maciej’s example uses the average of the opening and closing balances of the shares. Here it is in Sphinx:

let
  d = foreach set(values ProfitLoss[]::period)
  bop = $d::start-date  
  eop = $d::end-date
in
  EarningsPerShare[period = $d] := 
    ProfitLoss[period = $d] /
    ((NumberOfShares[period = $bop] + NumberOfShares[period = $eop]) / 2)

As you can see, the bit after the “in” maps very closely to how Maciej wrote the rule in English.

The first bit simply gets me a set of the periods for which “ProfitLoss” has been reported, and then assigns the dates at the beginning and end of that period to the variables $bop and $eop respectively.  Those variables aren’t necessary, I just used them for clarity.

Maciej requested a rule that runs on an instance with multiple periods and multiple currencies:

The above screenshot shows the result of running the rule on some sample data in our Magnify review tool.  As you can see, it includes two different currencies and two different reporting periods.  The red crosses show where the rule has flagged a failure because the reported value does not match the calculated value.

Just to take this one step further, suppose I wanted to use WeightedAverageNumberOfShares if it’s reported, but fall back on the calculated unweighted average if not.  I can introduce a function to give me the “best” option for the average number of shares:

function ANS(d) 
  if(exists(WeightedAverageNumberOfShares[period=$d])) then
    WeightedAverageNumberOfShares[period=$d]
  else
    ((NumberOfShares[period = $d::start-date] + NumberOfShares[period = $d::end-date]) / 2)

and then use that in my expression:

let 
  d = foreach set(values ProfitLoss[]::period) 
in
  EarningsPerShare[period = $d] := ProfitLoss[period = $d] / ANS($d)