The document.ready event won't be the problem. All that means is that when the document is "ready" it runs the contained code, which in this case means it assigns the handlers as soon as the document is ready.
I think the following might actually be the problem:
You're attaching the change event handlers to the source fields, but you're looking at the value of the calculated field.
The problem with that is the change on the source fields occurs as soon as the field loses focus, but if you have a calculation filling the .need field, then it doesn't fire until after the change event on the first field.
Basically, when your function executes hbportion hasn't been updated yet.
A good way to test is to add console.log(hbportion) before the if, and then open the browser dev tools so you can see every time it gets triggered.
Since you have a field with the calculated value, the change event should be attached to that instead. Also, you don't need the "else" branch since you're setting default values.
$(document).ready(function(){
$('.need input').on('change',function(){
var hhportion = $(this).val();
var under = 'No';
var warn = 'red';
if(hbportion < 150){
under = 'Yes';
warn = 'green';
}
$('.underlimit select').val(under);
$('#q16 select').css('background-color',warn);
});
});