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):
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
<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)
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])