that would appear to be correct.
As far as a bug, mmm, how could such a thing be worked around in an automated fashion ? Attribute set events are just that. Either the backref events would not fire off at all, waiting for the database round trip to populate (just don't use back_populates to achieve that), or the backref events would be delayed until after constructor, but that is quite awkward and arbitrary, as the same kinds of events can happen in any other number of ways besides being set within the constructor.
There's another behavior, which is that a backref populates the incoming record into the so-called "pending" collection for an unloaded collection, which populates upon access of the collection after the database population has occurred. This behavior is to save on SQL calls for backref events, and it does not kick in on a transient or pending object since there's no collection to load. Some awkward mechanics could allow this behavior to occur on a pending/transient, but then the moment you access the actual dictionary for a read, its loaded, and then you'd have this undesirable behavior again for subsequent populations of Holding. As long as the dictionary we're dealing with is just a plain dict and not some dynamic view type of thing (which is in fact a feature here), I'm not seeing how this kind of issue is avoidable (don't take that to mean there's no solution though...I can't say for sure).
It would appear the simplest approach is to just populate the dictionaries directly:
class Holding(object):
def __init__(self, broker, stock):
stock.by_broker[broker] = self
broker.by_stock[stock] = self