brndnmtthws / thetagang

ThetaGang is an IBKR bot for collecting money
GNU Affero General Public License v3.0
1.89k stars 248 forks source link

ValueError: cannot convert float NaN to integer - after Start of calculating target positions. #368

Closed robby28-11 closed 4 months ago

robby28-11 commented 4 months ago

Thetagang gets portfolio positions but the it kind of crashes. Was working wonderful, haven't changed anything. Wonder what might have influenced this. Already switched back to 1.9.1 and 1.9.2 with the same issue.

Any ideas?

`│ T │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ P │ -1 │ $0… │ $0… │ $-0 │ $-2 │ $2 │ 90.… │ $1… │ 202… │ 2 │ │ │ │ P │ -5 │ $0… │ $0… │ $-… │ $-98 │ $41 │ 41.… │ $1… │ 202… │ 65 │ │ └─────┴───┴─────┴─────┴─────┴─────┴──────┴─────┴──────┴─────┴──────┴─────┴─────┘ Calculating target positions... ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 0% -:--:-- ╭───────────────────── Traceback (most recent call last) ──────────────────────╮ │ /usr/local/lib/python3.10/dist-packages/thetagang/portfolio_manager.py:591 │ │ in manage │ │ │ │ 588 │ │ │ │ positions_table, │ │ 589 │ │ │ │ put_actions_table, │ │ 590 │ │ │ │ puts_to_write, │ │ ❱ 591 │ │ │ ) = self.check_if_can_write_puts(account_summary, portfol │ │ 592 │ │ │ │ │ 593 │ │ │ # Look for lots of stock that don't have covered calls │ │ 594 │ │ │ (call_actions_table, calls_to_write) = self.check_for_unc │ │ │ │ /usr/local/lib/python3.10/dist-packages/thetagang/portfolio_manager.py:1005 │ │ in check_if_can_write_puts │ │ │ │ 1002 │ │ │ targets[symbol] = round( │ │ 1003 │ │ │ │ self.config["symbols"][symbol]["weight"] * total_buyi │ │ 1004 │ │ │ ) │ │ ❱ 1005 │ │ │ self.target_quantities[symbol] = math.floor( │ │ 1006 │ │ │ │ targets[symbol] / ticker.marketPrice() │ │ 1007 │ │ │ ) │ │ 1008 │ ╰──────────────────────────────────────────────────────────────────────────────╯ ValueError: cannot convert float NaN to integer

Press ANY KEY to close this window `

brndnmtthws commented 4 months ago

Was the market open when you ran this? The ticker is not returning a price, hence the error.

robby28-11 commented 4 months ago

Yes. Also at this moment same error. Even with 1.8.1

brndnmtthws commented 4 months ago

Odd. Does IBKR return a price for the same symbol? There are some weird edge cases (like things with very low liquidity) where sometimes it doesn't return data. It should normally return the midpoint price, or the last trade price if that's not available. This is a tricky case because it's not safe to keep going if you can't get a valid price for the symbol, it will lead to undefined behaviour.

See here: https://ib-insync.readthedocs.io/api.html#ib_insync.ticker.Ticker.marketPrice

robby28-11 commented 4 months ago

Argh, yes there was one option with a price of 0.00. Just bought it for 0.01 to close that position. Fixed the issue instantly. Thanks for the hint.

brndnmtthws commented 4 months ago

That's odd, I guess there must have been no bids or asks, and no trades. Maybe I will have it handle that one particular case more gracefully by skipping over contracts with no price, but I want to think about it for a bit to make sure I don't inadvertently introduce a footgun. I think it's probably safe to just ignore this specific case, but that means it won't be able to roll the contract (ideally it would expire worthless).

robby28-11 commented 4 months ago

The put option (T Feb16 14Put) was sold with a very low commission. Unfortunately, I hadn't used minimum_credit yet, which could probably have prevented it (which is btw just working in the latest tag, not main). As a result, the option was too far out of the money.

Of course, the option would have simply expired - that's not a problem. The problem, however, is that all other options were set to "pause" because the script was always interrupted as a result.

So maybe it's not such a bad idea to distinguish whether the option price is 0.00 or does not return a value at all?

Screenshot 2024-02-15 081029
brndnmtthws commented 4 months ago

What's especially confusing to me is that it's actually failing on getting the market price for the stock, not the option itself. That should certainly not be returning 0 or NaN. It should already gracefully handle cases where the option has no price, usually it'll print an error and continue anyway.

So, I'm either failing to understand the problem, or I've missed some key piece of information.

Was there any other detail in the stack trace from before? I'm thinking that stack trace is actually a red herring and the error is somewhere else.

If I were to guess, it's failing after this function (which tries to handle this as gracefully as possible, but could still return a 0 or NaN in some cases): https://github.com/brndnmtthws/thetagang/blob/42af12345fc7bf77ae0dfc0db57c1f89c5cf6089/thetagang/util.py#L207-L222

The error itself is caused by a failed type conversion from a float to an integer, which doesn't quite make sense because the prices are handled as floats. I will make a tiny correction so that in the case that ticker.marketPrice() is NaN, it'll return 0.0 instead.

robby28-11 commented 4 months ago

Something odd is happening here:

Log from yesterday attached. It just happens in my paper account. The live account does not have any issues. I'm using the cash management in the paper only. Currently the paper only have SGOV in account. It somehow fails to caluclate target positions.

Maybe this helps to find whats happening. Error.txt

brndnmtthws commented 4 months ago

One of those two values (either the market price, or the weights) has to be NaN. It's not the weight, because it prints that earlier (showing 20%), so it must be the market price.

What do you have this setting set to: https://github.com/brndnmtthws/thetagang/blob/dfd37f43a4ee789c6e7afe8771d2f91a18db016f/thetagang.toml#L386

The only thing I can think of, is that if ib_insync.api_response_wait_time is too small, it won't wait long enough for the API to get the market prices. Anything less than 5 is probably going to frequently cause problems.

In any case, I'll make one more tweak to this code, which will hopefully handle this situation better.

robby28-11 commented 4 months ago

api_response_wait_time = 60

for both paper/live

robby28-11 commented 4 months ago

Now it is just working. Haven't changed anything. But thanks for digging into this rare issue.