What causes lost updates being observed in my SQL Azure database?
The problem was because of "other interpretation" lost update - when data is first read into local memory and then the table is updated with that data. There was another code that was intended for resetting balances of some users very rarely. It was something like this:

DECLARE @BalancesToReset TABLE
(
   UserId uniqueidentifier,
   PriorBalance int,
   NewBalance int
)

INSERT INTO @BalancesToReset( UserId, PriorBalance, NewBalance )
    SELECT UserId, AvailableMoney, dbo.ufn_ComputeNewBalance(some params)
    FROM UserBalances WHERE SomeIncorrectConditionHere

UPDATE UserBalances SET 
    AvailableMoney = BR.NewBalance FROM
       @BalancesToReset BR INNER JOIN UserBalances
       ON UserBalances.UserId = BR.UserId;

 INSERT INTO UserBalanceChanges( PriorBalance, NewBalance )
    SELECT PriorBalance, NewBalance
    FROM @BalancesToReset WHERE NewBalance &lt; PriorBalance;


and this code would run concurrently with the code in question. The code was intended to INSERT from SELECT only a small fraction of user balances and only occasionally but because of an incorrect condition in the SELECT part it would select a huge portion of balances and very often. The balances that were unintentionally selected had NewBalance equal to PriorBalance and so the final INSERT from SELECT would not log the changes.

The UPDATE in the middle of this code would forcibly reset the AvailableMoney for many users to a precomputed value and this would introduce a "second interpretation" lost update which is possible in SQL Server.