The main problem you are encountering is due to the fact that you are using Euler integration. It is very innacurate and assumes a constant acceleration over the whole timestep (in your case an implicit dt = 1), which is wrong in general, but in the case of a bounce, very very wrong indeed.
Also, there is no damping. In the real world, energy is always lost due to friction and collisions. In fact, your code is actually giving free energy to the ball: at the moment of impact, you teleport the ball upwards a short distance, while giving it an impossibly perfect eleastic collision, thereby giving the ball free potential energy. The only reason your ball's bouncing becomes smaller is because of the innacurate Euler integration, not something you want to rely on.
To make your code better, I would first introduce an explicit timestep, then you can call your integration function multiple times per frame with smaller timesteps to get better accuracy, or have an adaptive timestep.
You could also look into higher order integration functions. 4th Order Runge-Kutta is very accurate, but overkill for what you want, but perhaps a second order leapfrog method.
You could also look into Verlet integration. While it is still not very accurate, it is easy to control and very stable.
You should also add some damping. The easiest way is to multiply the velocity by a number slightly smaller than 1 (which is equivalent to adding a small force proportional to velocity, in the opposite direction):
vel *= 0.9999; <- energy loss every frame, air resistance
if ( ballPos.y >= 590 )
ballPos.y = 590;
vel *= -0.95; <-larger energy loss in collision
So, to sum up, here is a list of keywords to research to improve your simulation:
rigid body stacking,
You can easily find lots of easy to understand tutorials, samples and papers relating to this. That's probably a lot deeper than you need/desire to go. But writing a simple, but solid, physics simulation is a great learning experience and a whole lot of fun.