Potential infinite loop in `allocate` due to high precision requirement for non-integer positions
0xEljh opened this issue · 0 comments
Description:
I have identified an issue with the allocate
method of SecurityBase
in core.py
. When integer_positions
is set to False
and the method attempts to find an optimal q
such that the total outlay is as close as possible to the amount
, it potentially triggers the infinite loop error condition due to the high precision requirements (rtol
is set to 0.0
):
while not np.isclose(full_outlay, amount, rtol=0.0) and q != 0:
dq_wout_considering_tx_costs = (full_outlay - amount) / (
self._price * self.multiplier
)
This results in the following error being thrown in a setup that involves no commissions:
if i > 1e4:
raise Exception(
"Potentially infinite loop detected. This occurred "
"while trying to reduce the amount of shares purchased"
" to respect the outlay <= amount rule. This is most "
"likely due to a commission function that outputs a "
"commission that is greater than the amount of cash "
"a short sale can raise."
)
Reproduction
This issue is hard to reproduce without providing data, but is an issue I frequently encounter when backtesting cryptocurrencies, whereby it makes sense to have integer_positions
set to False
and where price decimal precision is high, causing this error to sometimes emerge when allocating into crypto assets.
def setup_backtest(name: str, algos: list, prices: pd.DataFrame):
strat = bt.Strategy(name, algos)
return bt.Backtest(strat, prices, integer_positions=False, commissions=None)
total_count = 25_000
batch_size = 10
for i in tqdm(range(0, total_count, batch_size)):
backtests = []
for x in range(batch_size):
backtests.append(setup_backtest(f"pick_1_{i + x}", [
bt.algos.RunMonthly(),
bt.algos.SelectRandomly(n=1),
bt.algos.WeighEqually(),
bt.algos.Rebalance(),
],
price_df
))
results = bt.run(*backtests)
Expected behavior
The allocate
method should find q
without entering an infinite loop since no commissions are involved. Otherwise, the error thrown should be more informative of potential causes.
Solutions
I've found that relaxing the precision requirement from rtol=0.0
to rtol=1e-8
in the np.isclose
function seems to mitigate the issue. However, I would like to open up a discussion on the potential impacts of this change.
- Could relaxing this condition have any unexpected negative impact?
- Perhaps this can be constrained to when
integer_positions=False
. - Should
rtol
be set as an argument tobacktest
? - Is there a better way to handle this issue?