In general, concurrency is difficult. Especially with 200 statements (i'm assuming you don't only query = SELECT) or even transactions (actually every single statement issued is wrapped into a transaction if it's not in a transaction already).
The general solution are these:
To be aware that deadlocks can happen, catch them in the application, check the Error Codes for class 40 or 40P01 and retry the transaction.
Reserve locks. Use SELECT ... FOR UPDATE. Evade explicit locks as long as possible. Locks will force other transactions to wait for lock release, which harms concurrency, but can prevent transactions running into deadlocks. Check the example for deadlocks in chapter 13. Especially the one in which transaction A waits for B and B waits for A (the bank account thingy).
Choose a different Isolation Level, for example a weaker one like READ COMMITED, if possible. Be aware of LOST UPDATEs in READ COMMITED mode. Prevent them with REPEATABLE READ.
Write your statements with locks in the same order in EVERY transaction, for example by table name alphabetically.
Also, you should realise that a statement involving multiple rows is not executed atomically in a concurrent situation. In other words, if you have two statements A and B involving multiple rows, then thethey can be executed in this order:
a1 b1 a2 a3 a4 b2 b3
but notNOT as a block of a's followed by b's.
The same applies to a statement with a sub-query.
Have you looked at the query plans using EXPLAIN ?
In your case, you can try
UPDATE BALANCES WHERE ID IN (
SELECT ID FROM some_function() FOR UPDATE -- LOCK using FOR UPDATE
-- other transactions will WAIT / BLOCK temporarily on conc. write access
);
If possible by what you want to do, you can also use SELECT ... FOR UPDATE SKIP LOCK, which will skip already locked data to get back concurrency, which is lost by WAITing for another transaction to release a lock (FOR UPDATE). But this will not apply an UPDATE to locked rows, which your application logic might require. So run that later on (see point 1).
Also read LOST UPDATE about the LOST UPDATE and
SKIP LOCKED about SKIP LOCKED. A queue might be an idea in your case, which is explained perfectly in the SKIP LOCKED reference, although relational DBMS are not meant to be queues.
HTH