Neither do I as I don't use Python. I can't comment on Python specific issues, which is why I focused on the IB calls in the program.
app.run is designed to never return. You can't put it inside a for loop. That's why only the first request is processed. In this case you can probably get by moving it after the loop, but that won't work for more complicated code. Message loops and threading are tricky. Unless you understand them, I'd strongly suggest using ib_insync. It has more example code than there is for IB's api and a mailing list to ask questions. @nooby_mcnoob, do you use ib_insync in Spartan? I've been using it for flagging opportunities that I then trade manually, but I have some concerns about its internals that make me hesitant to use it as I move to automating the trades.
My main gripe is that instead of building on top of IB's api they've reverse engineered everything and do the socket communication directly. I get that it makes the message loop cleaner, but it seems fragile and I've seen a couple minor reports in the issue tracker related to it. Maybe I'm being paranoid, but in the worst case I could imagine IB makes a change and size and price get switched or something like that.
Is this true? Edit: looks like you're right. I must have reviewed the code before that change or something. Could have sworn they used ibapi
If you want to learn more about the IB API (and its often unexpected behavior) you may want to sign up at this group: https://groups.io/g/twsapi I'm pretty sure that your question has been asked there previously.