Я, наверно, переусложнил, но пока так получилось (СУБД - Postgres):
select client_id, st, en, avg(bal)
from (
select *,
max(day) filter (where l = 1) over (partition by client_id order by day) st,
min(day) filter (where r = 1) over (partition by client_id order by day range between current row and unbounded following) en
from (
select *,
case when (lag(bal) over w) > 0 then 0 else 1 end as l,
case when (lead(bal) over w) > 0 then 0 else 1 end as r
from balance
window w as (partition by client_id order by day)
) t
where bal > 0
) t1 group by client_id, st, en
order by 1, 2
Самый внутренний запрос определяет пограничные строки (начало/конец периода ненулевого баланса). Промежуточный - вычисляет даты границ, внешний - группирует и считает среднее
https://www.db-fiddle.com/f/495cu1zp5BSmZDPwqXJ9Ev/0