I have a nested loop structure to generate a large number of backtests by varying the value of 4 variables, oq, aq, lev, and val. The idea is to execute every combination of the variables below within the ranges provided.
Without a constraint, this loop would therefore execute a total of 5 * 6 * 5 * 5 = 750 times which at ~5-10 seconds each would take several hours. However, there is a constraint, which is that all the weights must sum to exactly 1 (tot_wgt). By adding an if statement, I hoped to simply discard such cases.
if (tot_wgt != 1):
continue
Unfortunately, the code still seems to execute sometimes when tot_wgt does not have a value of 1. This seems to happen every time the val loop has completed a cycle (and presumably also happens when each of the other 3 loops have completed a cycle).
Problem solved: I had an indentation error: my needed to be at the level of the if statement. But see the excellent answer on recognition of floating point numbers.
mom = 0
for oq in [0.3, 0.4, 0.5, 0.6, 0.7]:
for aq in [0.05, 0.1, 0.15, 0.2, 0.25, 0.3]:
for lev in [0.0, 0.05, 0.1, 0.15, 0.2]:
for val in [0.0, 0.05, 0.1, 0.15, 0.2]:
tot_wgt = oq + aq + lev + val + mom
if (tot_wgt != 1): #we only want to backtest where the weights add up to 1. If <1 or >1, simply skip
continue
<MAIN BACKTEST CODE HERE>
This is caused by the limitations of representing floating-point numbers in computer hardware as base 2 (binary) fractions. Refer to Floating Point Arithmetic: Issues and Limitations for the detailed description.
For instance, in your case,
>>> 0.7 + 0.2 + 0.0 + 0.1 + 0
0.9999999999999999
# more specific
>>> from decimal import Decimal
>>> Decimal(0.7 + 0.2 + 0.0 + 0.1 + 0)
Decimal('0.99999999999999988897769753748434595763683319091796875')
As you can see, this is not equvelent to 1
. One simple way to resolve it is just to replace the line if (tot_wgt != 1):
with,
if abs(tot_wgt - 1) < 0.0001 :
Python 3.5 adds the math.isclose
for testing approximate equality. For an earlier version, the equivalent function is as follows.
def isclose(a, b, rel_tol=1e-09, abs_tol=0.0):
return abs(a-b) <= max(rel_tol * max(abs(a), abs(b)), abs_tol)
Use numpy.isclose
to test if two arrays are element-wise equal within a tolerance.
# Usage
numpy.isclose(a, b, rtol=1e-05, atol=1e-08, equal_nan=False)
# An example
>>> import numpy as np
>>> np.isclose([1e10,1e-7], [1.00001e10,1e-8])
array([True, False])