Option FanaticOptions, stock, futures, and system trading, backtesting, money management, and much more!

Backtester Logic (Part 8)

Having tied up several loose ends in the last post, today I want to analyze logic for the find_short branch.

As with find_long (see here), find_short involves a multiple if statement:

The input variable (see key) from the top of the program is defined as follows:

      width = int(input(“Enter spread width (in monthly expirations): “))

L98 searches for a DTE between upper and lower bounds. The difference between bounds is 10 days. The upper bound is the long option expiration minus (28 * width). With 28 or 35 days between expiration cycles, (28 * width) is the highest value it should ever take. The extra 10 [days] should accommodate a 35-day cycle.

If the width covers two (or more) 35-day cycles then this may not work because 7 * 2 = 14 days is more than the 10 provided. Moving the 10 inside parentheses won’t work because width = 4 (the least required to get two 35-day cycles) would allow for a lower bound that is ((4 * 28) + 40) below the upper bound: enough to capture two potential options for the short leg (although the desired longer-dated option will match first if the data file is complete).

To better code L98, I should include the additional days for each three months of width:

      DTE > L_dte_orig – ((28 * width) + (10 * ((width + 2) // 3)))

Floor division ( // ) truncates the remainder for positive numbers. For one month, this is 3 // 3 = 1. For three months, this is
5 // 3 = 1. Only when I get to four months, which is 6 // 3 = 2, will 20 additional days be included.

I will make this modification, but for a couple reasons it’s not something I will use anytime soon. First, I really worry about liquidity and option availability for width > 3 because the long will then be very far from expiration and probably lightly traded. Practically speaking, I would only consider a max width of two months. Second, the lower bound seems unnecessary. Any option available in the long expiration should be available in the short as long as the data file is complete.*

If I want to clean the data files, which includes assessing completeness by searching for omitted and erroneous data, then I can create some simple Python scripts. That’s a topic for another post.

L99 looks to match strike price with the long option. One option per expiration cycle should match. I am okay with this line coming after L98 because the more restrictive if statement (see Part 4) may depend on how many expirations are available: a variable number over the years.

L99 also looks to match date with long option purchase. This is redundant because the previous if statement (not shown) checks for this and makes an entry in missing_s_s_p_dict if short not found.

I will continue next time.

*—This brings to mind another problem with 10-point multiples in find_long (see fourth paragraph Part 7).
     Far from expiration, I have sometimes noticed that only 25-point strikes are available (and only 50-point
     strikes going back many years). Requiring 10-multiple is actually constrained to 50-point strikes in that
     case. The difference between one 50-point strike and the next can be much more than 1% of the
     underlying in those earlier years, which is enough to make for a clearly directional trade.